]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
authorJohn W. Linville <linville@tuxdriver.com>
Wed, 15 Aug 2012 18:29:37 +0000 (14:29 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 15 Aug 2012 18:29:37 +0000 (14:29 -0400)
1816 files changed:
Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads [new file with mode: 0644]
Documentation/ABI/stable/sysfs-bus-firewire
Documentation/ABI/testing/sysfs-bus-rbd
Documentation/ABI/testing/sysfs-devices-edac [new file with mode: 0644]
Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb [new file with mode: 0644]
Documentation/ABI/testing/sysfs-platform-asus-wmi
Documentation/DMA-attributes.txt
Documentation/DocBook/media/v4l/biblio.xml
Documentation/DocBook/media/v4l/common.xml
Documentation/DocBook/media/v4l/compat.xml
Documentation/DocBook/media/v4l/controls.xml
Documentation/DocBook/media/v4l/dev-subdev.xml
Documentation/DocBook/media/v4l/io.xml
Documentation/DocBook/media/v4l/selection-api.xml
Documentation/DocBook/media/v4l/selections-common.xml [new file with mode: 0644]
Documentation/DocBook/media/v4l/v4l2.xml
Documentation/DocBook/media/v4l/vidioc-create-bufs.xml
Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml
Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml [new file with mode: 0644]
Documentation/DocBook/media/v4l/vidioc-g-frequency.xml
Documentation/DocBook/media/v4l/vidioc-g-selection.xml
Documentation/DocBook/media/v4l/vidioc-g-tuner.xml
Documentation/DocBook/media/v4l/vidioc-qbuf.xml
Documentation/DocBook/media/v4l/vidioc-querycap.xml
Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml
Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml
Documentation/IRQ-domain.txt
Documentation/block/queue-sysfs.txt
Documentation/cgroups/hugetlb.txt [new file with mode: 0644]
Documentation/cgroups/memory.txt
Documentation/device-mapper/dm-raid.txt
Documentation/device-mapper/striped.txt
Documentation/device-mapper/thin-provisioning.txt
Documentation/devicetree/bindings/arm/calxeda/l2ecc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/arm/calxeda/mem-ctrlr.txt [new file with mode: 0644]
Documentation/devicetree/bindings/ata/cavium-compact-flash.txt [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/cavium-octeon-gpio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/gpio_i2c.txt [deleted file]
Documentation/devicetree/bindings/i2c/cavium-i2c.txt [new file with mode: 0644]
Documentation/devicetree/bindings/i2c/gpio-i2c.txt [new file with mode: 0644]
Documentation/devicetree/bindings/i2c/i2c-mxs.txt
Documentation/devicetree/bindings/i2c/i2c-ocores.txt [new file with mode: 0644]
Documentation/devicetree/bindings/i2c/mrvl-i2c.txt
Documentation/devicetree/bindings/mfd/ab8500.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/max77686.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/tps65910.txt
Documentation/devicetree/bindings/mfd/twl6040.txt
Documentation/devicetree/bindings/mips/cavium/bootbus.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mips/cavium/ciu.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mips/cavium/ciu2.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mips/cavium/dma-engine.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mips/cavium/uctl.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/cavium-mdio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/cavium-mix.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/cavium-pip.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/cpsw.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/davinci-mdio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pwm/mxs-pwm.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pwm/pwm.txt [new file with mode: 0644]
Documentation/devicetree/bindings/serial/cavium-uart.txt [new file with mode: 0644]
Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt [new file with mode: 0644]
Documentation/dontdiff
Documentation/dvb/get_dvb_firmware
Documentation/edac.txt
Documentation/fault-injection/fault-injection.txt
Documentation/fault-injection/notifier-error-inject.txt [new file with mode: 0644]
Documentation/feature-removal-schedule.txt
Documentation/filesystems/Locking
Documentation/filesystems/vfs.txt
Documentation/input/edt-ft5x06.txt [new file with mode: 0644]
Documentation/ioctl/ioctl-number.txt
Documentation/kernel-parameters.txt
Documentation/power/power_supply_class.txt
Documentation/printk-formats.txt
Documentation/pwm.txt [new file with mode: 0644]
Documentation/sound/alsa/HD-Audio-Models.txt
Documentation/sysctl/fs.txt
Documentation/sysctl/vm.txt
Documentation/vfio.txt [new file with mode: 0644]
Documentation/video4linux/CARDLIST.au0828
Documentation/video4linux/CARDLIST.bttv
Documentation/video4linux/CARDLIST.cx23885
Documentation/video4linux/CARDLIST.saa7134
Documentation/video4linux/v4l2-framework.txt
MAINTAINERS
Makefile
arch/Kconfig
arch/alpha/Kconfig
arch/alpha/include/asm/unistd.h
arch/alpha/kernel/smc37c669.c
arch/arm/Kconfig
arch/arm/boot/dts/highbank.dts
arch/arm/boot/dts/imx28.dtsi
arch/arm/boot/dts/r8a7740.dtsi [new file with mode: 0644]
arch/arm/boot/dts/sh7377.dtsi [new file with mode: 0644]
arch/arm/boot/dts/tegra20.dtsi
arch/arm/boot/dts/tegra30.dtsi
arch/arm/common/dmabounce.c
arch/arm/configs/armadillo800eva_defconfig
arch/arm/configs/kzm9d_defconfig [new file with mode: 0644]
arch/arm/configs/kzm9g_defconfig
arch/arm/configs/omap2plus_defconfig
arch/arm/configs/tegra_defconfig
arch/arm/include/asm/cacheflush.h
arch/arm/include/asm/dma-mapping.h
arch/arm/include/asm/mutex.h
arch/arm/include/asm/setup.h
arch/arm/include/asm/unistd.h
arch/arm/kernel/entry-armv.S
arch/arm/kernel/entry-common.S
arch/arm/kernel/ftrace.c
arch/arm/kernel/process.c
arch/arm/kernel/ptrace.c
arch/arm/kernel/setup.c
arch/arm/kernel/signal.c
arch/arm/kernel/signal.h
arch/arm/kernel/smp.c
arch/arm/kernel/traps.c
arch/arm/lib/io-acorn.S
arch/arm/mach-davinci/devices-da8xx.c
arch/arm/mach-exynos/clock-exynos4.c
arch/arm/mach-exynos/clock-exynos4.h
arch/arm/mach-exynos/clock-exynos4210.c
arch/arm/mach-exynos/clock-exynos4212.c
arch/arm/mach-exynos/mach-nuri.c
arch/arm/mach-exynos/mach-origen.c
arch/arm/mach-netx/fb.c
arch/arm/mach-omap1/board-h2-mmc.c
arch/arm/mach-omap1/board-h3-mmc.c
arch/arm/mach-omap1/board-nokia770.c
arch/arm/mach-omap1/board-palmz71.c
arch/arm/mach-omap2/board-n8x0.c
arch/arm/mach-omap2/display.c
arch/arm/mach-omap2/hsmmc.c
arch/arm/mach-pxa/eseries.h [deleted file]
arch/arm/mach-pxa/hx4700.c
arch/arm/mach-pxa/lubbock.c
arch/arm/mach-pxa/magician.c
arch/arm/mach-pxa/trizeps4.c
arch/arm/mach-s3c64xx/include/mach/pm-core.h
arch/arm/mach-shmobile/Kconfig
arch/arm/mach-shmobile/Makefile
arch/arm/mach-shmobile/board-ag5evm.c
arch/arm/mach-shmobile/board-ap4evb.c
arch/arm/mach-shmobile/board-armadillo800eva.c
arch/arm/mach-shmobile/board-bonito.c
arch/arm/mach-shmobile/board-g4evm.c
arch/arm/mach-shmobile/board-kota2.c
arch/arm/mach-shmobile/board-kzm9d.c
arch/arm/mach-shmobile/board-kzm9g.c
arch/arm/mach-shmobile/board-mackerel.c
arch/arm/mach-shmobile/board-marzen.c
arch/arm/mach-shmobile/clock-r8a7740.c
arch/arm/mach-shmobile/clock-r8a7779.c
arch/arm/mach-shmobile/clock-sh7367.c
arch/arm/mach-shmobile/clock-sh7372.c
arch/arm/mach-shmobile/clock-sh7377.c
arch/arm/mach-shmobile/clock-sh73a0.c
arch/arm/mach-shmobile/include/mach/common.h
arch/arm/mach-shmobile/include/mach/dma-register.h [new file with mode: 0644]
arch/arm/mach-shmobile/include/mach/gpio.h
arch/arm/mach-shmobile/include/mach/pm-rmobile.h [new file with mode: 0644]
arch/arm/mach-shmobile/include/mach/r8a7740.h
arch/arm/mach-shmobile/include/mach/sh7372.h
arch/arm/mach-shmobile/include/mach/sh73a0.h
arch/arm/mach-shmobile/intc-r8a7740.c
arch/arm/mach-shmobile/pfc-r8a7740.c
arch/arm/mach-shmobile/pm-r8a7740.c [new file with mode: 0644]
arch/arm/mach-shmobile/pm-rmobile.c [new file with mode: 0644]
arch/arm/mach-shmobile/pm-sh7372.c
arch/arm/mach-shmobile/setup-r8a7740.c
arch/arm/mach-shmobile/setup-sh7372.c
arch/arm/mach-shmobile/setup-sh7377.c
arch/arm/mach-shmobile/setup-sh73a0.c
arch/arm/mach-spear3xx/spear300.c
arch/arm/mach-spear3xx/spear310.c
arch/arm/mach-spear3xx/spear320.c
arch/arm/mach-spear3xx/spear3xx.c
arch/arm/mach-spear6xx/spear6xx.c
arch/arm/mach-tegra/board-dt-tegra20.c
arch/arm/mach-tegra/board-dt-tegra30.c
arch/arm/mach-ux500/board-mop500.c
arch/arm/mach-ux500/cpu-db8500.c
arch/arm/mach-ux500/devices-common.h
arch/arm/mach-ux500/include/mach/setup.h
arch/arm/mach-vt8500/Makefile
arch/arm/mach-vt8500/pwm.c [deleted file]
arch/arm/mm/dma-mapping.c
arch/arm/mm/mm.h
arch/arm/mm/tlb-v7.S
arch/arm/plat-mxc/Kconfig
arch/arm/plat-mxc/Makefile
arch/arm/plat-mxc/include/mach/i2c.h
arch/arm/plat-mxc/pwm.c [deleted file]
arch/arm/plat-nomadik/include/plat/i2c.h [deleted file]
arch/arm/plat-omap/include/plat/mmc.h
arch/arm/plat-pxa/Makefile
arch/arm/plat-pxa/pwm.c [deleted file]
arch/arm/plat-samsung/Makefile
arch/arm/plat-samsung/pwm.c [deleted file]
arch/arm/plat-spear/include/plat/pl080.h
arch/arm/plat-spear/pl080.c
arch/arm/vfp/entry.S
arch/arm/vfp/vfphw.S
arch/arm/vfp/vfpmodule.c
arch/avr32/Kconfig
arch/avr32/boards/atstk1000/atstk1002.c
arch/avr32/include/asm/unistd.h
arch/avr32/mm/fault.c
arch/blackfin/Kconfig
arch/blackfin/include/asm/unistd.h
arch/blackfin/kernel/Makefile
arch/blackfin/kernel/pwm.c [deleted file]
arch/cris/Kconfig
arch/cris/include/asm/unistd.h
arch/frv/Kconfig
arch/frv/include/asm/cpumask.h [deleted file]
arch/frv/include/asm/unistd.h
arch/frv/kernel/kernel_thread.S
arch/h8300/Kconfig
arch/h8300/include/asm/unistd.h
arch/hexagon/include/asm/Kbuild
arch/ia64/Kconfig
arch/ia64/include/asm/atomic.h
arch/ia64/include/asm/machvec.h
arch/ia64/include/asm/machvec_dig.h
arch/ia64/include/asm/machvec_dig_vtd.h
arch/ia64/include/asm/machvec_hpsim.h
arch/ia64/include/asm/machvec_hpzx1.h
arch/ia64/include/asm/machvec_hpzx1_swiotlb.h
arch/ia64/include/asm/machvec_sn2.h
arch/ia64/include/asm/machvec_uv.h
arch/ia64/include/asm/machvec_xen.h
arch/ia64/include/asm/processor.h
arch/ia64/kernel/irq_ia64.c
arch/ia64/kernel/perfmon.c
arch/ia64/kvm/Kconfig
arch/ia64/pci/fixup.c
arch/m32r/Kconfig
arch/m32r/include/asm/unistd.h
arch/m68k/Kconfig
arch/m68k/include/asm/unistd.h
arch/microblaze/Kconfig
arch/microblaze/include/asm/unistd.h
arch/mips/Kbuild.platforms
arch/mips/Kconfig
arch/mips/alchemy/board-mtx1.c
arch/mips/alchemy/common/platform.c
arch/mips/alchemy/devboards/Makefile
arch/mips/alchemy/devboards/bcsr.c
arch/mips/alchemy/devboards/pb1100.c
arch/mips/alchemy/devboards/pb1500.c
arch/mips/alchemy/devboards/platform.c
arch/mips/alchemy/devboards/prom.c [deleted file]
arch/mips/bcm63xx/Kconfig
arch/mips/bcm63xx/Makefile
arch/mips/bcm63xx/boards/board_bcm963xx.c
arch/mips/bcm63xx/clk.c
arch/mips/bcm63xx/cpu.c
arch/mips/bcm63xx/dev-dsp.c
arch/mips/bcm63xx/dev-flash.c [new file with mode: 0644]
arch/mips/bcm63xx/dev-rng.c [new file with mode: 0644]
arch/mips/bcm63xx/dev-spi.c [new file with mode: 0644]
arch/mips/bcm63xx/dev-wdt.c
arch/mips/bcm63xx/irq.c
arch/mips/bcm63xx/prom.c
arch/mips/bcm63xx/setup.c
arch/mips/boot/compressed/Makefile
arch/mips/boot/compressed/uart-16550.c
arch/mips/cavium-octeon/.gitignore [new file with mode: 0644]
arch/mips/cavium-octeon/Makefile
arch/mips/cavium-octeon/executive/cvmx-fpa.c [deleted file]
arch/mips/cavium-octeon/executive/cvmx-helper-fpa.c [deleted file]
arch/mips/cavium-octeon/octeon-irq.c
arch/mips/cavium-octeon/octeon-memcpy.S
arch/mips/cavium-octeon/octeon-platform.c
arch/mips/cavium-octeon/octeon_3xxx.dts [new file with mode: 0644]
arch/mips/cavium-octeon/octeon_68xx.dts [new file with mode: 0644]
arch/mips/cavium-octeon/serial.c
arch/mips/cavium-octeon/setup.c
arch/mips/configs/ls1b_defconfig [new file with mode: 0644]
arch/mips/configs/nlm_xlr_defconfig
arch/mips/dec/prom/memory.c
arch/mips/include/asm/clock.h
arch/mips/include/asm/cpu.h
arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_flash.h [new file with mode: 0644]
arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h [new file with mode: 0644]
arch/mips/include/asm/mach-bcm63xx/bcm63xx_gpio.h
arch/mips/include/asm/mach-bcm63xx/bcm63xx_io.h
arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
arch/mips/include/asm/mach-bcm63xx/ioremap.h
arch/mips/include/asm/mach-cavium-octeon/irq.h
arch/mips/include/asm/mach-jz4740/jz4740_nand.h
arch/mips/include/asm/mach-loongson/loongson.h
arch/mips/include/asm/mach-loongson1/irq.h [new file with mode: 0644]
arch/mips/include/asm/mach-loongson1/loongson1.h [new file with mode: 0644]
arch/mips/include/asm/mach-loongson1/platform.h [new file with mode: 0644]
arch/mips/include/asm/mach-loongson1/prom.h [new file with mode: 0644]
arch/mips/include/asm/mach-loongson1/regs-clk.h [new file with mode: 0644]
arch/mips/include/asm/mach-loongson1/regs-wdt.h [new file with mode: 0644]
arch/mips/include/asm/mach-loongson1/war.h [new file with mode: 0644]
arch/mips/include/asm/mach-netlogic/cpu-feature-overrides.h
arch/mips/include/asm/mach-tx49xx/mangle-port.h
arch/mips/include/asm/mipsmtregs.h
arch/mips/include/asm/module.h
arch/mips/include/asm/netlogic/xlp-hal/cpucontrol.h
arch/mips/include/asm/netlogic/xlp-hal/iomap.h
arch/mips/include/asm/netlogic/xlp-hal/pcibus.h [new file with mode: 0644]
arch/mips/include/asm/netlogic/xlp-hal/pic.h
arch/mips/include/asm/netlogic/xlp-hal/usb.h [new file with mode: 0644]
arch/mips/include/asm/netlogic/xlp-hal/xlp.h
arch/mips/include/asm/netlogic/xlr/bridge.h [new file with mode: 0644]
arch/mips/include/asm/netlogic/xlr/flash.h [new file with mode: 0644]
arch/mips/include/asm/netlogic/xlr/gpio.h
arch/mips/include/asm/octeon/cvmx-helper-fpa.h [deleted file]
arch/mips/include/asm/octeon/cvmx-helper.h
arch/mips/include/asm/octeon/octeon.h
arch/mips/include/asm/prom.h
arch/mips/include/asm/smtc.h
arch/mips/include/asm/uaccess.h
arch/mips/include/asm/uasm.h
arch/mips/include/asm/unistd.h
arch/mips/jz4740/board-qi_lb60.c
arch/mips/jz4740/platform.c
arch/mips/jz4740/reset.c
arch/mips/kernel/cpu-probe.c
arch/mips/kernel/cpufreq/Makefile
arch/mips/kernel/cpufreq/loongson2_clock.c [deleted file]
arch/mips/kernel/cpufreq/loongson2_cpufreq.c
arch/mips/kernel/perf_event_mipsxx.c
arch/mips/kernel/prom.c
arch/mips/kernel/smp.c
arch/mips/kernel/smtc.c
arch/mips/kernel/traps.c
arch/mips/lantiq/clk.c
arch/mips/lantiq/prom.c
arch/mips/lantiq/xway/sysctrl.c
arch/mips/lib/Makefile
arch/mips/lib/memcpy-inatomic.S [deleted file]
arch/mips/lib/memcpy.S
arch/mips/loongson/Kconfig
arch/mips/loongson/lemote-2f/Makefile
arch/mips/loongson/lemote-2f/clock.c [new file with mode: 0644]
arch/mips/loongson1/Kconfig [new file with mode: 0644]
arch/mips/loongson1/Makefile [new file with mode: 0644]
arch/mips/loongson1/Platform [new file with mode: 0644]
arch/mips/loongson1/common/Makefile [new file with mode: 0644]
arch/mips/loongson1/common/clock.c [new file with mode: 0644]
arch/mips/loongson1/common/irq.c [new file with mode: 0644]
arch/mips/loongson1/common/platform.c [new file with mode: 0644]
arch/mips/loongson1/common/prom.c [new file with mode: 0644]
arch/mips/loongson1/common/reset.c [new file with mode: 0644]
arch/mips/loongson1/common/setup.c [new file with mode: 0644]
arch/mips/loongson1/ls1b/Makefile [new file with mode: 0644]
arch/mips/loongson1/ls1b/board.c [new file with mode: 0644]
arch/mips/mm/uasm.c
arch/mips/netlogic/common/earlycons.c
arch/mips/netlogic/common/smpboot.S
arch/mips/netlogic/xlp/Makefile
arch/mips/netlogic/xlp/nlm_hal.c
arch/mips/netlogic/xlp/of.c [new file with mode: 0644]
arch/mips/netlogic/xlp/platform.c
arch/mips/netlogic/xlp/setup.c
arch/mips/netlogic/xlp/usb-init.c [new file with mode: 0644]
arch/mips/netlogic/xlr/Makefile
arch/mips/netlogic/xlr/platform-flash.c [new file with mode: 0644]
arch/mips/netlogic/xlr/platform.c
arch/mips/netlogic/xlr/setup.c
arch/mips/oprofile/common.c
arch/mips/oprofile/op_model_mipsxx.c
arch/mips/pci/Makefile
arch/mips/pci/fixup-cobalt.c
arch/mips/pci/fixup-malta.c
arch/mips/pci/fixup-rc32434.c
arch/mips/pci/ops-bcm63xx.c
arch/mips/pci/pci-bcm63xx.c
arch/mips/pci/pci-bcm63xx.h
arch/mips/pci/pci-xlp.c [new file with mode: 0644]
arch/mips/pci/pci-xlr.c
arch/mips/pnx833x/stb22x/board.c
arch/mips/sgi-ip27/ip27-memory.c
arch/mips/txx9/Kconfig
arch/mips/txx9/generic/pci.c
arch/mips/txx9/generic/setup.c
arch/mips/txx9/generic/setup_tx4939.c
arch/mips/txx9/rbtx4939/setup.c
arch/mn10300/Kconfig
arch/mn10300/include/asm/ipc.h [deleted file]
arch/mn10300/include/asm/unistd.h
arch/openrisc/include/asm/Kbuild
arch/powerpc/Kconfig
arch/powerpc/boot/dts/p2020rdb-pc_32b.dts
arch/powerpc/boot/dts/p2020rdb-pc_36b.dts
arch/powerpc/boot/dts/p3041ds.dts
arch/powerpc/configs/chroma_defconfig
arch/powerpc/include/asm/dma-mapping.h
arch/powerpc/include/asm/unistd.h
arch/powerpc/kernel/dma-iommu.c
arch/powerpc/kernel/dma-swiotlb.c
arch/powerpc/kernel/dma.c
arch/powerpc/kernel/vio.c
arch/powerpc/kvm/book3s_rmhandlers.S
arch/powerpc/platforms/85xx/p1022_ds.c
arch/powerpc/platforms/cell/spufs/inode.c
arch/powerpc/platforms/cell/spufs/syscalls.c
arch/powerpc/sysdev/fsl_85xx_cache_ctlr.h
arch/powerpc/sysdev/fsl_85xx_l2ctlr.c
arch/powerpc/sysdev/xics/icp-hv.c
arch/powerpc/sysdev/xics/icp-native.c
arch/powerpc/sysdev/xics/xics-common.c
arch/s390/Kconfig
arch/s390/defconfig
arch/s390/include/asm/mmu_context.h
arch/s390/include/asm/processor.h
arch/s390/include/asm/setup.h
arch/s390/include/asm/unistd.h
arch/s390/kernel/debug.c
arch/s390/kernel/dis.c
arch/s390/kernel/early.c
arch/s390/kernel/ipl.c
arch/s390/kernel/setup.c
arch/s390/kernel/traps.c
arch/s390/kernel/vdso.c
arch/s390/kernel/vmlinux.lds.S
arch/s390/mm/fault.c
arch/s390/mm/mmap.c
arch/s390/mm/pgtable.c
arch/s390/oprofile/backtrace.c
arch/sh/Kconfig
arch/sh/configs/apsh4ad0a_defconfig
arch/sh/configs/sdk7786_defconfig
arch/sh/configs/se7206_defconfig
arch/sh/configs/shx3_defconfig
arch/sh/configs/urquell_defconfig
arch/sh/include/asm/unistd.h
arch/sparc/Kconfig
arch/sparc/include/asm/unistd.h
arch/sparc/kernel/ldc.c
arch/sparc/kernel/sys_sparc_64.c
arch/tile/configs/tilegx_defconfig
arch/tile/configs/tilepro_defconfig
arch/tile/include/asm/Kbuild
arch/um/defconfig
arch/um/drivers/chan_kern.c
arch/um/drivers/line.c
arch/um/drivers/line.h
arch/um/drivers/mconsole_kern.c
arch/um/drivers/port_kern.c
arch/um/drivers/random.c
arch/um/drivers/ssl.c
arch/um/drivers/stdio_console.c
arch/um/drivers/ubd_kern.c
arch/um/drivers/xterm_kern.c
arch/um/include/asm/ptrace-generic.h
arch/um/include/shared/as-layout.h
arch/um/include/shared/irq_user.h
arch/um/include/shared/kern_util.h
arch/um/kernel/irq.c
arch/um/kernel/process.c
arch/um/kernel/ptrace.c
arch/um/kernel/sigio.c
arch/um/kernel/skas/syscall.c
arch/um/kernel/time.c
arch/um/kernel/trap.c
arch/um/os-Linux/internal.h
arch/um/os-Linux/signal.c
arch/um/os-Linux/skas/process.c
arch/um/os-Linux/time.c
arch/x86/Kconfig
arch/x86/include/asm/perf_event.h
arch/x86/include/asm/unistd.h
arch/x86/kernel/cpu/perf_event.h
arch/x86/kernel/cpu/perf_event_intel.c
arch/x86/kernel/cpu/perf_event_intel_uncore.c
arch/x86/kernel/cpu/perf_event_intel_uncore.h
arch/x86/kernel/e820.c
arch/x86/um/asm/ptrace.h
arch/xtensa/Kconfig
arch/xtensa/include/asm/cpumask.h [deleted file]
arch/xtensa/include/asm/rmap.h [deleted file]
arch/xtensa/kernel/syscall.c
arch/xtensa/mm/fault.c
block/blk-cgroup.c
block/blk-cgroup.h
block/blk-core.c
block/blk-ioc.c
block/blk-settings.c
block/blk-sysfs.c
block/blk-throttle.c
block/blk.h
block/bsg-lib.c
block/genhd.c
block/ioctl.c
block/partition-generic.c
drivers/Kconfig
drivers/Makefile
drivers/acpi/video_detect.c
drivers/ata/pata_arasan_cf.c
drivers/base/Kconfig
drivers/base/devtmpfs.c
drivers/base/dma-mapping.c
drivers/bcma/host_pci.c
drivers/bcma/sprom.c
drivers/block/drbd/drbd_actlog.c
drivers/block/drbd/drbd_bitmap.c
drivers/block/drbd/drbd_int.h
drivers/block/drbd/drbd_main.c
drivers/block/drbd/drbd_nl.c
drivers/block/drbd/drbd_proc.c
drivers/block/drbd/drbd_receiver.c
drivers/block/drbd/drbd_req.c
drivers/block/drbd/drbd_worker.c
drivers/block/floppy.c
drivers/block/nbd.c
drivers/block/rbd.c
drivers/block/rbd_types.h
drivers/block/umem.c
drivers/block/virtio_blk.c
drivers/block/xen-blkfront.c
drivers/char/hw_random/Kconfig
drivers/char/hw_random/Makefile
drivers/char/hw_random/bcm63xx-rng.c [new file with mode: 0644]
drivers/char/hw_random/virtio-rng.c
drivers/char/mspec.c
drivers/char/random.c
drivers/clk/Kconfig
drivers/clk/clk.c
drivers/cpufreq/exynos5250-cpufreq.c
drivers/crypto/n2_core.c
drivers/dma/Kconfig
drivers/dma/Makefile
drivers/dma/amba-pl08x.c
drivers/dma/omap-dma.c [new file with mode: 0644]
drivers/dma/sa11x0-dma.c
drivers/dma/virt-dma.c [new file with mode: 0644]
drivers/dma/virt-dma.h [new file with mode: 0644]
drivers/edac/Kconfig
drivers/edac/Makefile
drivers/edac/amd64_edac.c
drivers/edac/amd64_edac.h
drivers/edac/amd64_edac_dbg.c
drivers/edac/amd64_edac_inj.c
drivers/edac/amd76x_edac.c
drivers/edac/cell_edac.c
drivers/edac/cpc925_edac.c
drivers/edac/e752x_edac.c
drivers/edac/e7xxx_edac.c
drivers/edac/edac_core.h
drivers/edac/edac_device.c
drivers/edac/edac_device_sysfs.c
drivers/edac/edac_mc.c
drivers/edac/edac_mc_sysfs.c
drivers/edac/edac_module.c
drivers/edac/edac_module.h
drivers/edac/edac_pci.c
drivers/edac/edac_pci_sysfs.c
drivers/edac/highbank_l2_edac.c [new file with mode: 0644]
drivers/edac/highbank_mc_edac.c [new file with mode: 0644]
drivers/edac/i3000_edac.c
drivers/edac/i3200_edac.c
drivers/edac/i5000_edac.c
drivers/edac/i5100_edac.c
drivers/edac/i5400_edac.c
drivers/edac/i7300_edac.c
drivers/edac/i7core_edac.c
drivers/edac/i82443bxgx_edac.c
drivers/edac/i82860_edac.c
drivers/edac/i82875p_edac.c
drivers/edac/i82975x_edac.c
drivers/edac/mpc85xx_edac.c
drivers/edac/mv64x60_edac.c
drivers/edac/pasemi_edac.c
drivers/edac/ppc4xx_edac.c
drivers/edac/r82600_edac.c
drivers/edac/sb_edac.c
drivers/edac/tile_edac.c
drivers/edac/x38_edac.c
drivers/extcon/Kconfig
drivers/extcon/extcon-max8997.c
drivers/firewire/core-device.c
drivers/firewire/core-iso.c
drivers/firewire/core-transaction.c
drivers/firewire/ohci.c
drivers/firmware/dmi_scan.c
drivers/firmware/memmap.c
drivers/firmware/pcdp.c
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-tps6586x.c [new file with mode: 0644]
drivers/gpu/drm/exynos/exynos_drm_connector.c
drivers/gpu/drm/exynos/exynos_drm_core.c
drivers/gpu/drm/exynos/exynos_drm_crtc.c
drivers/gpu/drm/exynos/exynos_drm_crtc.h
drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
drivers/gpu/drm/exynos/exynos_drm_drv.c
drivers/gpu/drm/exynos/exynos_drm_drv.h
drivers/gpu/drm/exynos/exynos_drm_encoder.c
drivers/gpu/drm/exynos/exynos_drm_encoder.h
drivers/gpu/drm/exynos/exynos_drm_fimd.c
drivers/gpu/drm/exynos/exynos_drm_gem.c
drivers/gpu/drm/exynos/exynos_drm_gem.h
drivers/gpu/drm/exynos/exynos_drm_plane.c
drivers/gpu/drm/exynos/exynos_drm_plane.h
drivers/gpu/drm/exynos/exynos_drm_vidi.c
drivers/gpu/drm/exynos/exynos_hdmi.c
drivers/gpu/drm/exynos/exynos_mixer.c
drivers/hid/hid-core.c
drivers/hid/hid-ids.h
drivers/hv/vmbus_drv.c
drivers/hwmon/acpi_power_meter.c
drivers/hwmon/applesmc.c
drivers/hwmon/coretemp.c
drivers/hwmon/jc42.c
drivers/hwmon/via-cputemp.c
drivers/i2c/busses/Kconfig
drivers/i2c/busses/i2c-at91.c
drivers/i2c/busses/i2c-bfin-twi.c
drivers/i2c/busses/i2c-imx.c
drivers/i2c/busses/i2c-mv64xxx.c
drivers/i2c/busses/i2c-mxs.c
drivers/i2c/busses/i2c-nomadik.c
drivers/i2c/busses/i2c-ocores.c
drivers/i2c/busses/i2c-octeon.c
drivers/i2c/busses/i2c-omap.c
drivers/i2c/busses/i2c-pmcmsp.c
drivers/i2c/busses/i2c-pnx.c
drivers/i2c/busses/i2c-puv3.c
drivers/i2c/busses/i2c-pxa.c
drivers/i2c/busses/i2c-s3c2410.c
drivers/i2c/busses/i2c-stu300.c
drivers/i2c/busses/i2c-tegra.c
drivers/i2c/i2c-core.c
drivers/infiniband/core/cma.c
drivers/infiniband/core/ucma.c
drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
drivers/infiniband/hw/qib/qib.h
drivers/infiniband/ulp/ipoib/ipoib.h
drivers/infiniband/ulp/ipoib/ipoib_cm.c
drivers/infiniband/ulp/ipoib/ipoib_main.c
drivers/infiniband/ulp/ipoib/ipoib_multicast.c
drivers/input/misc/88pm80x_onkey.c [new file with mode: 0644]
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/ab8500-ponkey.c
drivers/input/mouse/synaptics.c
drivers/input/serio/hp_sdc.c
drivers/input/tablet/wacom_wac.c
drivers/input/tablet/wacom_wac.h
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/edt-ft5x06.c [new file with mode: 0644]
drivers/isdn/isdnloop/isdnloop.c
drivers/md/Kconfig
drivers/md/dm-crypt.c
drivers/md/dm-delay.c
drivers/md/dm-exception-store.c
drivers/md/dm-flakey.c
drivers/md/dm-ioctl.c
drivers/md/dm-linear.c
drivers/md/dm-log.c
drivers/md/dm-mpath.c
drivers/md/dm-raid.c
drivers/md/dm-raid1.c
drivers/md/dm-snap.c
drivers/md/dm-stripe.c
drivers/md/dm-table.c
drivers/md/dm-thin-metadata.c
drivers/md/dm-thin-metadata.h
drivers/md/dm-thin.c
drivers/md/dm-verity.c
drivers/md/dm.c
drivers/md/dm.h
drivers/md/md.c
drivers/md/md.h
drivers/md/persistent-data/Makefile
drivers/md/persistent-data/dm-block-manager.c
drivers/md/persistent-data/dm-block-manager.h
drivers/md/persistent-data/dm-space-map-checker.c [deleted file]
drivers/md/persistent-data/dm-space-map-checker.h [deleted file]
drivers/md/persistent-data/dm-space-map-common.c
drivers/md/persistent-data/dm-space-map-common.h
drivers/md/persistent-data/dm-space-map-disk.c
drivers/md/persistent-data/dm-transaction-manager.c
drivers/md/persistent-data/dm-transaction-manager.h
drivers/md/raid1.c
drivers/md/raid1.h
drivers/md/raid10.c
drivers/md/raid10.h
drivers/md/raid5.c
drivers/md/raid5.h
drivers/media/Kconfig
drivers/media/common/tuners/Kconfig
drivers/media/common/tuners/tuner-xc2028.c
drivers/media/common/tuners/xc5000.c
drivers/media/dvb/ddbridge/ddbridge-core.c
drivers/media/dvb/dvb-core/dvb_frontend.h
drivers/media/dvb/dvb-usb/Kconfig
drivers/media/dvb/dvb-usb/az6007.c
drivers/media/dvb/dvb-usb/dvb-usb-ids.h
drivers/media/dvb/dvb-usb/rtl28xxu.c
drivers/media/dvb/frontends/Kconfig
drivers/media/dvb/frontends/Makefile
drivers/media/dvb/frontends/a8293.c
drivers/media/dvb/frontends/dib8000.c
drivers/media/dvb/frontends/drxk.h
drivers/media/dvb/frontends/drxk_hard.c
drivers/media/dvb/frontends/drxk_hard.h
drivers/media/dvb/frontends/lgs8gxx.c
drivers/media/dvb/frontends/rtl2832.c [new file with mode: 0644]
drivers/media/dvb/frontends/rtl2832.h [new file with mode: 0644]
drivers/media/dvb/frontends/rtl2832_priv.h [new file with mode: 0644]
drivers/media/dvb/frontends/s5h1420.c
drivers/media/dvb/frontends/stb0899_drv.c
drivers/media/dvb/frontends/stv0367.c
drivers/media/dvb/frontends/stv090x.c
drivers/media/dvb/frontends/tda10071.c
drivers/media/dvb/frontends/tda10071_priv.h
drivers/media/dvb/ngene/ngene-cards.c
drivers/media/dvb/siano/smscoreapi.c
drivers/media/radio/Kconfig
drivers/media/radio/Makefile
drivers/media/radio/lm7000.h [new file with mode: 0644]
drivers/media/radio/radio-aimslab.c
drivers/media/radio/radio-cadet.c
drivers/media/radio/radio-mr800.c
drivers/media/radio/radio-sf16fmi.c
drivers/media/radio/radio-shark.c [new file with mode: 0644]
drivers/media/radio/radio-shark2.c [new file with mode: 0644]
drivers/media/radio/radio-tea5777.c [new file with mode: 0644]
drivers/media/radio/radio-tea5777.h [new file with mode: 0644]
drivers/media/radio/radio-wl1273.c
drivers/media/radio/si470x/radio-si470x-common.c
drivers/media/radio/si470x/radio-si470x-i2c.c
drivers/media/radio/si470x/radio-si470x-usb.c
drivers/media/radio/si470x/radio-si470x.h
drivers/media/radio/wl128x/fmdrv_rx.c
drivers/media/radio/wl128x/fmdrv_v4l2.c
drivers/media/rc/Kconfig
drivers/media/rc/Makefile
drivers/media/rc/ati_remote.c
drivers/media/rc/ene_ir.c
drivers/media/rc/fintek-cir.c
drivers/media/rc/gpio-ir-recv.c
drivers/media/rc/iguanair.c [new file with mode: 0644]
drivers/media/rc/mceusb.c
drivers/media/rc/nuvoton-cir.c
drivers/media/rc/rc-main.c
drivers/media/video/Kconfig
drivers/media/video/Makefile
drivers/media/video/adv7180.c
drivers/media/video/adv7393.c [new file with mode: 0644]
drivers/media/video/adv7393_regs.h [new file with mode: 0644]
drivers/media/video/bt8xx/bttv-cards.c
drivers/media/video/bt8xx/bttv-driver.c
drivers/media/video/bt8xx/bttv.h
drivers/media/video/cpia2/cpia2_v4l.c
drivers/media/video/cs8420.h [deleted file]
drivers/media/video/cx18/cx18-ioctl.c
drivers/media/video/cx18/cx18-ioctl.h
drivers/media/video/cx18/cx18-streams.c
drivers/media/video/cx231xx/cx231xx-avcore.c
drivers/media/video/cx231xx/cx231xx-cards.c
drivers/media/video/cx231xx/cx231xx-i2c.c
drivers/media/video/cx231xx/cx231xx.h
drivers/media/video/cx23885/cx23885-i2c.c
drivers/media/video/cx23885/cx23885.h
drivers/media/video/cx25821/cx25821-i2c.c
drivers/media/video/cx25821/cx25821-medusa-video.c
drivers/media/video/cx25821/cx25821.h
drivers/media/video/cx88/cx88-alsa.c
drivers/media/video/cx88/cx88-blackbird.c
drivers/media/video/cx88/cx88-cards.c
drivers/media/video/cx88/cx88-core.c
drivers/media/video/cx88/cx88-video.c
drivers/media/video/cx88/cx88.h
drivers/media/video/davinci/Kconfig
drivers/media/video/davinci/Makefile
drivers/media/video/davinci/vpbe_display.c
drivers/media/video/davinci/vpif.c
drivers/media/video/davinci/vpif.h
drivers/media/video/davinci/vpif_capture.c
drivers/media/video/davinci/vpif_capture.h
drivers/media/video/davinci/vpif_display.c
drivers/media/video/davinci/vpif_display.h
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-i2c.c
drivers/media/video/em28xx/em28xx-input.c
drivers/media/video/em28xx/em28xx-reg.h
drivers/media/video/gspca/benq.c
drivers/media/video/gspca/conex.c
drivers/media/video/gspca/cpia1.c
drivers/media/video/gspca/etoms.c
drivers/media/video/gspca/finepix.c
drivers/media/video/gspca/gl860/gl860.c
drivers/media/video/gspca/gspca.c
drivers/media/video/gspca/jeilinj.c
drivers/media/video/gspca/jl2005bcd.c
drivers/media/video/gspca/kinect.c
drivers/media/video/gspca/konica.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/nw80x.c
drivers/media/video/gspca/ov519.c
drivers/media/video/gspca/ov534.c
drivers/media/video/gspca/ov534_9.c
drivers/media/video/gspca/pac207.c
drivers/media/video/gspca/pac7302.c
drivers/media/video/gspca/pac7311.c
drivers/media/video/gspca/se401.c
drivers/media/video/gspca/sn9c2028.c
drivers/media/video/gspca/sonixb.c
drivers/media/video/gspca/sonixj.c
drivers/media/video/gspca/spca1528.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/sq930x.c
drivers/media/video/gspca/stk014.c
drivers/media/video/gspca/stv0680.c
drivers/media/video/gspca/sunplus.c
drivers/media/video/gspca/t613.c
drivers/media/video/gspca/topro.c
drivers/media/video/gspca/tv8532.c
drivers/media/video/gspca/vc032x.c
drivers/media/video/gspca/vicam.c
drivers/media/video/gspca/w996Xcf.c
drivers/media/video/gspca/xirlink_cit.c
drivers/media/video/ibmmpeg2.h [deleted file]
drivers/media/video/ivtv/ivtv-ioctl.c
drivers/media/video/ivtv/ivtv-ioctl.h
drivers/media/video/ivtv/ivtv-streams.c
drivers/media/video/m5mols/Kconfig
drivers/media/video/m5mols/m5mols_controls.c
drivers/media/video/mem2mem_testdev.c
drivers/media/video/mt9m001.c
drivers/media/video/mt9m032.c
drivers/media/video/mt9m111.c
drivers/media/video/mt9p031.c
drivers/media/video/mt9t001.c
drivers/media/video/mt9v022.c
drivers/media/video/mx2_emmaprp.c
drivers/media/video/omap3isp/ispccdc.c
drivers/media/video/omap3isp/isppreview.c
drivers/media/video/omap3isp/ispresizer.c
drivers/media/video/ov2640.c
drivers/media/video/ov772x.c
drivers/media/video/ov9640.c
drivers/media/video/pms.c
drivers/media/video/pvrusb2/Kconfig
drivers/media/video/pvrusb2/pvrusb2-v4l2.c
drivers/media/video/pwc/pwc-if.c
drivers/media/video/pwc/pwc-v4l.c
drivers/media/video/pwc/pwc.h
drivers/media/video/s2255drv.c
drivers/media/video/s5p-fimc/fimc-capture.c
drivers/media/video/s5p-fimc/fimc-core.c
drivers/media/video/s5p-fimc/fimc-core.h
drivers/media/video/s5p-fimc/fimc-lite-reg.c
drivers/media/video/s5p-fimc/fimc-lite.c
drivers/media/video/s5p-fimc/fimc-m2m.c
drivers/media/video/s5p-fimc/fimc-mdevice.c
drivers/media/video/s5p-fimc/fimc-reg.c
drivers/media/video/s5p-g2d/g2d.c
drivers/media/video/s5p-jpeg/jpeg-core.c
drivers/media/video/s5p-mfc/s5p_mfc_dec.c
drivers/media/video/s5p-mfc/s5p_mfc_enc.c
drivers/media/video/s5p-tv/mixer_video.c
drivers/media/video/s5p-tv/sii9234_drv.c
drivers/media/video/saa7121.h [deleted file]
drivers/media/video/saa7134/saa7134-dvb.c
drivers/media/video/saa7146.h [deleted file]
drivers/media/video/saa7146reg.h [deleted file]
drivers/media/video/saa7164/saa7164-api.c
drivers/media/video/saa7164/saa7164-i2c.c
drivers/media/video/saa7164/saa7164.h
drivers/media/video/smiapp/Kconfig
drivers/media/video/smiapp/smiapp-core.c
drivers/media/video/sn9c102/sn9c102.h
drivers/media/video/soc_camera.c
drivers/media/video/tlg2300/pd-main.c
drivers/media/video/tuner-core.c
drivers/media/video/tvaudio.c
drivers/media/video/tvp5150.c
drivers/media/video/tw9910.c
drivers/media/video/uvc/Kconfig
drivers/media/video/uvc/uvc_ctrl.c
drivers/media/video/uvc/uvc_v4l2.c
drivers/media/video/uvc/uvc_video.c
drivers/media/video/v4l2-compat-ioctl32.c
drivers/media/video/v4l2-ctrls.c
drivers/media/video/v4l2-dev.c
drivers/media/video/v4l2-ioctl.c
drivers/media/video/v4l2-mem2mem.c
drivers/media/video/v4l2-subdev.c
drivers/media/video/via-camera.c
drivers/media/video/videobuf-core.c
drivers/media/video/videobuf-dma-contig.c
drivers/media/video/videobuf2-core.c
drivers/media/video/vivi.c
drivers/media/video/zr364xx.c
drivers/message/i2o/i2o_config.c
drivers/message/i2o/i2o_proc.c
drivers/mfd/88pm800.c [new file with mode: 0644]
drivers/mfd/88pm805.c [new file with mode: 0644]
drivers/mfd/88pm80x.c [new file with mode: 0644]
drivers/mfd/88pm860x-core.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/ab3100-core.c
drivers/mfd/ab8500-core.c
drivers/mfd/ab8500-debugfs.c
drivers/mfd/ab8500-gpadc.c
drivers/mfd/ab8500-sysctrl.c
drivers/mfd/adp5520.c
drivers/mfd/anatop-mfd.c
drivers/mfd/arizona-core.c [new file with mode: 0644]
drivers/mfd/arizona-i2c.c [new file with mode: 0644]
drivers/mfd/arizona-irq.c [new file with mode: 0644]
drivers/mfd/arizona-spi.c [new file with mode: 0644]
drivers/mfd/arizona.h [new file with mode: 0644]
drivers/mfd/da9052-core.c
drivers/mfd/db8500-prcmu.c
drivers/mfd/dbx500-prcmu-regs.h
drivers/mfd/max77686-irq.c [new file with mode: 0644]
drivers/mfd/max77686.c [new file with mode: 0644]
drivers/mfd/max77693.c
drivers/mfd/max8925-core.c
drivers/mfd/max8997-irq.c
drivers/mfd/max8997.c
drivers/mfd/mc13xxx-core.c
drivers/mfd/mc13xxx-i2c.c
drivers/mfd/mc13xxx-spi.c
drivers/mfd/mfd-core.c
drivers/mfd/pcf50633-core.c
drivers/mfd/s5m-core.c [deleted file]
drivers/mfd/s5m-irq.c [deleted file]
drivers/mfd/sec-core.c [new file with mode: 0644]
drivers/mfd/sec-irq.c [new file with mode: 0644]
drivers/mfd/tc3589x.c
drivers/mfd/timberdale.c
drivers/mfd/tps65010.c
drivers/mfd/tps65090.c
drivers/mfd/tps6586x.c
drivers/mfd/tps65910.c
drivers/mfd/twl-core.c
drivers/mfd/twl6040-core.c
drivers/mfd/wm5102-tables.c [new file with mode: 0644]
drivers/mfd/wm5110-tables.c [new file with mode: 0644]
drivers/mfd/wm831x-otp.c
drivers/mfd/wm8350-core.c
drivers/mfd/wm8350-i2c.c
drivers/mfd/wm8350-irq.c
drivers/mfd/wm8350-regmap.c
drivers/mfd/wm8994-core.c
drivers/mfd/wm8994-irq.c
drivers/misc/Kconfig
drivers/misc/ab8500-pwm.c
drivers/misc/lkdtm.c
drivers/misc/ti-st/st_core.c
drivers/mmc/host/omap.c
drivers/mmc/host/omap_hsmmc.c
drivers/mtd/nand/jz4740_nand.c
drivers/mtd/nand/omap2.c
drivers/net/bonding/bond_main.c
drivers/net/can/c_can/c_can_platform.c
drivers/net/cris/eth_v10.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
drivers/net/ethernet/chelsio/cxgb4/sge.c
drivers/net/ethernet/chelsio/cxgb4vf/sge.c
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
drivers/net/ethernet/mellanox/mlx4/en_rx.c
drivers/net/ethernet/mellanox/mlx4/en_tx.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
drivers/net/ethernet/mellanox/mlx4/sense.c
drivers/net/ethernet/octeon/octeon_mgmt.c
drivers/net/ethernet/sfc/efx.c
drivers/net/ethernet/sfc/efx.h
drivers/net/ethernet/sfc/ethtool.c
drivers/net/ethernet/sfc/tx.c
drivers/net/ethernet/stmicro/stmmac/stmmac.h
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/ti/Kconfig
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/davinci_mdio.c
drivers/net/hyperv/netvsc.c
drivers/net/hyperv/netvsc_drv.c
drivers/net/hyperv/rndis_filter.c
drivers/net/loopback.c
drivers/net/phy/mdio-octeon.c
drivers/net/ppp/ppp_generic.c
drivers/net/team/team.c
drivers/net/usb/cdc-phonet.c
drivers/net/usb/cdc_ncm.c
drivers/net/veth.c
drivers/net/virtio_net.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/b43/main.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmsmac/channel.c
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/libertas/cfg.c
drivers/net/wireless/libertas/if_sdio.c
drivers/net/wireless/libertas/main.c
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/xen-netfront.c
drivers/platform/x86/acer-wmi.c
drivers/platform/x86/apple-gmux.c
drivers/platform/x86/asus-nb-wmi.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/asus-wmi.h
drivers/platform/x86/classmate-laptop.c
drivers/platform/x86/dell-laptop.c
drivers/platform/x86/eeepc-wmi.c
drivers/platform/x86/samsung-laptop.c
drivers/platform/x86/thinkpad_acpi.c
drivers/power/Kconfig
drivers/power/bq27x00_battery.c
drivers/power/charger-manager.c
drivers/power/ds2781_battery.c
drivers/power/gpio-charger.c
drivers/power/lp8727_charger.c
drivers/power/max17042_battery.c
drivers/power/olpc_battery.c
drivers/power/pda_power.c
drivers/power/power_supply_core.c
drivers/power/power_supply_sysfs.c
drivers/power/sbs-battery.c
drivers/power/smb347-charger.c
drivers/power/test_power.c
drivers/power/twl4030_charger.c
drivers/pps/pps.c
drivers/pwm/Kconfig [new file with mode: 0644]
drivers/pwm/Makefile [new file with mode: 0644]
drivers/pwm/core.c [new file with mode: 0644]
drivers/pwm/pwm-bfin.c [new file with mode: 0644]
drivers/pwm/pwm-imx.c [new file with mode: 0644]
drivers/pwm/pwm-lpc32xx.c [new file with mode: 0644]
drivers/pwm/pwm-mxs.c [new file with mode: 0644]
drivers/pwm/pwm-pxa.c [new file with mode: 0644]
drivers/pwm/pwm-samsung.c [new file with mode: 0644]
drivers/pwm/pwm-tegra.c [new file with mode: 0644]
drivers/pwm/pwm-tiecap.c [new file with mode: 0644]
drivers/pwm/pwm-tiehrpwm.c [new file with mode: 0644]
drivers/pwm/pwm-vt8500.c [new file with mode: 0644]
drivers/regulator/Kconfig
drivers/regulator/ab8500.c
drivers/regulator/db8500-prcmu.c
drivers/regulator/s5m8767.c
drivers/rtc/Kconfig
drivers/rtc/Makefile
drivers/rtc/rtc-88pm80x.c [new file with mode: 0644]
drivers/rtc/rtc-ab8500.c
drivers/rtc/rtc-coh901331.c
drivers/rtc/rtc-da9052.c
drivers/rtc/rtc-max8925.c
drivers/rtc/rtc-mc13xxx.c
drivers/rtc/rtc-pcf8563.c
drivers/rtc/rtc-pl031.c
drivers/rtc/rtc-r9701.c
drivers/rtc/rtc-s3c.c
drivers/rtc/rtc-wm831x.c
drivers/scsi/scsi_transport_fc.c
drivers/scsi/scsi_transport_iscsi.c
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/spi-falcon.c [new file with mode: 0644]
drivers/spi/spi-omap2-mcspi.c
drivers/staging/bcm/Misc.c
drivers/staging/gdm72xx/sdio_boot.c
drivers/staging/gdm72xx/usb_boot.c
drivers/staging/media/dt3155v4l/dt3155v4l.c
drivers/staging/media/easycap/easycap_main.c
drivers/staging/media/lirc/lirc_bt829.c
drivers/staging/media/lirc/lirc_sir.c
drivers/staging/media/solo6x10/TODO
drivers/staging/media/solo6x10/core.c
drivers/staging/media/solo6x10/i2c.c
drivers/staging/octeon/ethernet-mdio.c
drivers/staging/octeon/ethernet.c
drivers/staging/octeon/octeon-ethernet.h
drivers/target/target_core_file.c
drivers/thermal/thermal_sys.c
drivers/tty/serial/uartlite.c
drivers/usb/core/hub.c
drivers/usb/gadget/f_phonet.c
drivers/usb/gadget/goku_udc.c
drivers/usb/gadget/m66592-udc.c
drivers/usb/gadget/m66592-udc.h
drivers/usb/gadget/pxa25x_udc.c
drivers/usb/gadget/r8a66597-udc.c
drivers/usb/gadget/r8a66597-udc.h
drivers/usb/gadget/storage_common.c
drivers/usb/gadget/u_uac1.c
drivers/usb/host/ehci-omap.c
drivers/usb/host/r8a66597-hcd.c
drivers/usb/host/r8a66597.h
drivers/usb/musb/musb_core.h
drivers/usb/otg/isp1301_omap.c
drivers/vfio/Kconfig [new file with mode: 0644]
drivers/vfio/Makefile [new file with mode: 0644]
drivers/vfio/pci/Kconfig [new file with mode: 0644]
drivers/vfio/pci/Makefile [new file with mode: 0644]
drivers/vfio/pci/vfio_pci.c [new file with mode: 0644]
drivers/vfio/pci/vfio_pci_config.c [new file with mode: 0644]
drivers/vfio/pci/vfio_pci_intrs.c [new file with mode: 0644]
drivers/vfio/pci/vfio_pci_private.h [new file with mode: 0644]
drivers/vfio/pci/vfio_pci_rdwr.c [new file with mode: 0644]
drivers/vfio/vfio.c [new file with mode: 0644]
drivers/vfio/vfio_iommu_type1.c [new file with mode: 0644]
drivers/video/aty/aty128fb.c
drivers/video/backlight/Kconfig
drivers/video/backlight/atmel-pwm-bl.c
drivers/video/backlight/corgi_lcd.c
drivers/video/backlight/l4f00242t03.c
drivers/video/backlight/lm3533_bl.c
drivers/video/backlight/lms283gf05.c
drivers/video/backlight/lp855x_bl.c
drivers/video/backlight/ot200_bl.c
drivers/video/backlight/pwm_bl.c
drivers/video/backlight/tosa_bl.c
drivers/video/backlight/tosa_lcd.c
drivers/video/da8xx-fb.c
drivers/video/epson1355fb.c
drivers/video/exynos/exynos_dp_core.c
drivers/video/exynos/exynos_dp_core.h
drivers/video/exynos/exynos_dp_reg.c
drivers/video/exynos/exynos_mipi_dsi.c
drivers/video/exynos/s6e8ax0.h [deleted file]
drivers/video/fb_defio.c
drivers/video/fb_draw.h
drivers/video/grvga.c
drivers/video/mx3fb.c
drivers/video/omap2/displays/panel-acx565akm.c
drivers/video/omap2/displays/panel-generic-dpi.c
drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
drivers/video/omap2/displays/panel-n8x0.c
drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c
drivers/video/omap2/displays/panel-picodlp.c
drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
drivers/video/omap2/displays/panel-taal.c
drivers/video/omap2/displays/panel-tfp410.c
drivers/video/omap2/displays/panel-tpo-td043mtea1.c
drivers/video/omap2/dss/Kconfig
drivers/video/omap2/dss/apply.c
drivers/video/omap2/dss/dispc.c
drivers/video/omap2/dss/dispc.h
drivers/video/omap2/dss/display.c
drivers/video/omap2/dss/dpi.c
drivers/video/omap2/dss/dsi.c
drivers/video/omap2/dss/dss.c
drivers/video/omap2/dss/dss.h
drivers/video/omap2/dss/dss_features.h
drivers/video/omap2/dss/hdmi.c
drivers/video/omap2/dss/hdmi_panel.c
drivers/video/omap2/dss/manager.c
drivers/video/omap2/dss/overlay.c
drivers/video/omap2/dss/rfbi.c
drivers/video/omap2/dss/sdi.c
drivers/video/omap2/dss/ti_hdmi.h
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
drivers/video/omap2/dss/venc.c
drivers/video/omap2/omapfb/omapfb-main.c
drivers/video/s3fb.c
drivers/video/sh_mipi_dsi.c
drivers/video/sh_mobile_lcdcfb.c
drivers/video/sh_mobile_lcdcfb.h
drivers/video/sh_mobile_meram.c
drivers/video/smscufx.c
drivers/video/w100fb.c
firmware/Makefile
firmware/cxgb3/t3fw-7.10.0.bin.ihex [deleted file]
fs/9p/vfs_file.c
fs/affs/bitmap.c
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/file.c
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/relocation.c
fs/btrfs/super.c
fs/btrfs/transaction.c
fs/buffer.c
fs/cachefiles/rdwr.c
fs/ceph/addr.c
fs/ceph/dir.c
fs/ceph/mds_client.c
fs/ceph/snap.c
fs/ceph/super.c
fs/ceph/super.h
fs/ceph/xattr.c
fs/ecryptfs/inode.c
fs/exec.c
fs/ext2/balloc.c
fs/ext2/ialloc.c
fs/ext2/inode.c
fs/ext2/super.c
fs/ext3/balloc.c
fs/ext3/bitmap.c
fs/ext4/bitmap.c
fs/ext4/inode.c
fs/ext4/mmp.c
fs/ext4/super.c
fs/fat/dir.c
fs/fat/fat.h
fs/fat/file.c
fs/fat/inode.c
fs/fat/namei_msdos.c
fs/fat/namei_vfat.c
fs/fcntl.c
fs/file_table.c
fs/fs-writeback.c
fs/fuse/file.c
fs/gfs2/file.c
fs/gfs2/trans.c
fs/hfsplus/super.c
fs/hugetlbfs/inode.c
fs/inode.c
fs/internal.h
fs/lockd/clntproc.c
fs/lockd/grace.c
fs/lockd/host.c
fs/lockd/netns.h
fs/lockd/svc.c
fs/lockd/svc4proc.c
fs/lockd/svclock.c
fs/lockd/svcproc.c
fs/lockd/svcsubs.c
fs/locks.c
fs/minix/itree_v2.c
fs/namei.c
fs/namespace.c
fs/nfs/Kconfig
fs/nfs/Makefile
fs/nfs/blocklayout/blocklayout.c
fs/nfs/callback.c
fs/nfs/callback.h
fs/nfs/callback_xdr.c
fs/nfs/client.c
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/dir.c
fs/nfs/direct.c
fs/nfs/dns_resolve.c
fs/nfs/file.c
fs/nfs/getroot.c
fs/nfs/idmap.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/namespace.c
fs/nfs/netns.h
fs/nfs/nfs.h [new file with mode: 0644]
fs/nfs/nfs2super.c [new file with mode: 0644]
fs/nfs/nfs2xdr.c
fs/nfs/nfs3client.c [new file with mode: 0644]
fs/nfs/nfs3proc.c
fs/nfs/nfs3super.c [new file with mode: 0644]
fs/nfs/nfs3xdr.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4client.c [new file with mode: 0644]
fs/nfs/nfs4file.c [new file with mode: 0644]
fs/nfs/nfs4filelayout.c
fs/nfs/nfs4filelayoutdev.c
fs/nfs/nfs4getroot.c [new file with mode: 0644]
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c
fs/nfs/nfs4super.c [new file with mode: 0644]
fs/nfs/nfs4sysctl.c [new file with mode: 0644]
fs/nfs/nfs4xdr.c
fs/nfs/pagelist.c
fs/nfs/pnfs.c
fs/nfs/pnfs.h
fs/nfs/proc.c
fs/nfs/read.c
fs/nfs/super.c
fs/nfs/sysctl.c
fs/nfs/unlink.c
fs/nfs/write.c
fs/nfsd/export.c
fs/nfsd/netns.h
fs/nfsd/nfs4callback.c
fs/nfsd/nfs4idmap.c
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4recover.c
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/nfsctl.c
fs/nfsd/nfsd.h
fs/nfsd/nfsfh.c
fs/nfsd/nfsproc.c
fs/nfsd/nfssvc.c
fs/nfsd/state.h
fs/nfsd/vfs.c
fs/nfsd/vfs.h
fs/nilfs2/alloc.h
fs/nilfs2/bmap.h
fs/nilfs2/btnode.h
fs/nilfs2/cpfile.c
fs/nilfs2/dat.c
fs/nilfs2/export.h
fs/nilfs2/file.c
fs/nilfs2/ifile.c
fs/nilfs2/inode.c
fs/nilfs2/ioctl.c
fs/nilfs2/mdt.h
fs/nilfs2/nilfs.h
fs/nilfs2/segment.c
fs/nilfs2/sufile.c
fs/nilfs2/super.c
fs/nilfs2/the_nilfs.c
fs/nilfs2/the_nilfs.h
fs/ntfs/file.c
fs/ntfs/super.c
fs/ocfs2/file.c
fs/ocfs2/ioctl.c
fs/ocfs2/journal.c
fs/ocfs2/localalloc.c
fs/ocfs2/mmap.c
fs/ocfs2/refcounttree.c
fs/open.c
fs/pipe.c
fs/proc/base.c
fs/qnx4/bitmap.c
fs/splice.c
fs/super.c
fs/sysfs/bin.c
fs/xattr.c
fs/xfs/xfs_alloc_btree.h
fs/xfs/xfs_aops.c
fs/xfs/xfs_aops.h
fs/xfs/xfs_attr.c
fs/xfs/xfs_attr_leaf.c
fs/xfs/xfs_attr_leaf.h
fs/xfs/xfs_bmap.c
fs/xfs/xfs_buf.c
fs/xfs/xfs_buf.h
fs/xfs/xfs_buf_item.c
fs/xfs/xfs_buf_item.h
fs/xfs/xfs_da_btree.c
fs/xfs/xfs_da_btree.h
fs/xfs/xfs_dinode.h
fs/xfs/xfs_dir2.c
fs/xfs/xfs_dir2_block.c
fs/xfs/xfs_dir2_data.c
fs/xfs/xfs_dir2_leaf.c
fs/xfs/xfs_dir2_node.c
fs/xfs/xfs_dir2_priv.h
fs/xfs/xfs_dir2_sf.c
fs/xfs/xfs_file.c
fs/xfs/xfs_ialloc.c
fs/xfs/xfs_ialloc.h
fs/xfs/xfs_iget.c
fs/xfs/xfs_inode.c
fs/xfs/xfs_inode.h
fs/xfs/xfs_ioctl.c
fs/xfs/xfs_ioctl32.c
fs/xfs/xfs_iomap.c
fs/xfs/xfs_iops.c
fs/xfs/xfs_itable.c
fs/xfs/xfs_log.c
fs/xfs/xfs_log_priv.h
fs/xfs/xfs_log_recover.c
fs/xfs/xfs_mount.c
fs/xfs/xfs_mount.h
fs/xfs/xfs_qm.c
fs/xfs/xfs_super.c
fs/xfs/xfs_sync.c
fs/xfs/xfs_trace.h
fs/xfs/xfs_trans.c
fs/xfs/xfs_trans.h
fs/xfs/xfs_trans_ail.c
fs/xfs/xfs_trans_buf.c
fs/xfs/xfs_trans_priv.h
fs/xfs/xfs_types.h
fs/xfs/xfs_utils.c
fs/xfs/xfs_vnodeops.c
include/asm-generic/dma-coherent.h
include/asm-generic/dma-mapping-common.h
include/asm-generic/fcntl.h
include/drm/exynos_drm.h
include/linux/Kbuild
include/linux/acpi.h
include/linux/aio.h
include/linux/amba/pl08x.h
include/linux/audit.h
include/linux/backing-dev.h
include/linux/bcma/bcma_driver_chipcommon.h
include/linux/blk_types.h
include/linux/blkdev.h
include/linux/blkpg.h
include/linux/bsg-lib.h
include/linux/ceph/ceph_features.h [new file with mode: 0644]
include/linux/ceph/ceph_fs.h
include/linux/ceph/decode.h
include/linux/ceph/libceph.h
include/linux/ceph/messenger.h
include/linux/ceph/mon_client.h
include/linux/ceph/msgpool.h
include/linux/cgroup_subsys.h
include/linux/clk.h
include/linux/compaction.h
include/linux/compat.h
include/linux/crush/crush.h
include/linux/device-mapper.h
include/linux/dm-ioctl.h
include/linux/dma-attrs.h
include/linux/dma-mapping.h
include/linux/edac.h
include/linux/firewire.h
include/linux/flex_proportions.h [new file with mode: 0644]
include/linux/fs.h
include/linux/genhd.h
include/linux/gfp.h
include/linux/hash.h
include/linux/highmem.h
include/linux/hugetlb.h
include/linux/hugetlb_cgroup.h [new file with mode: 0644]
include/linux/i2c-ocores.h
include/linux/i2c.h
include/linux/i2c/twl.h
include/linux/if_arp.h
include/linux/if_team.h
include/linux/if_tunnel.h
include/linux/inetdevice.h
include/linux/input/edt-ft5x06.h [new file with mode: 0644]
include/linux/interrupt.h
include/linux/ip6_tunnel.h
include/linux/irqdesc.h
include/linux/irqdomain.h
include/linux/jiffies.h
include/linux/kern_levels.h [new file with mode: 0644]
include/linux/key-type.h
include/linux/libfdt.h [new file with mode: 0644]
include/linux/libfdt_env.h [new file with mode: 0644]
include/linux/lockd/lockd.h
include/linux/lp855x.h [deleted file]
include/linux/lp8727.h [deleted file]
include/linux/memcontrol.h
include/linux/mempolicy.h
include/linux/mempool.h
include/linux/mfd/88pm80x.h [new file with mode: 0644]
include/linux/mfd/88pm860x.h
include/linux/mfd/abx500/ab8500.h
include/linux/mfd/arizona/core.h [new file with mode: 0644]
include/linux/mfd/arizona/pdata.h [new file with mode: 0644]
include/linux/mfd/arizona/registers.h [new file with mode: 0644]
include/linux/mfd/core.h
include/linux/mfd/db8500-prcmu.h
include/linux/mfd/dbx500-prcmu.h
include/linux/mfd/max77686-private.h [new file with mode: 0644]
include/linux/mfd/max77686.h [new file with mode: 0644]
include/linux/mfd/max77693-private.h
include/linux/mfd/max8997-private.h
include/linux/mfd/max8997.h
include/linux/mfd/s5m87xx/s5m-core.h [deleted file]
include/linux/mfd/s5m87xx/s5m-pmic.h [deleted file]
include/linux/mfd/s5m87xx/s5m-rtc.h [deleted file]
include/linux/mfd/samsung/core.h [new file with mode: 0644]
include/linux/mfd/samsung/irq.h [new file with mode: 0644]
include/linux/mfd/samsung/rtc.h [new file with mode: 0644]
include/linux/mfd/samsung/s2mps11.h [new file with mode: 0644]
include/linux/mfd/samsung/s5m8763.h [new file with mode: 0644]
include/linux/mfd/samsung/s5m8767.h [new file with mode: 0644]
include/linux/mfd/tps65910.h
include/linux/mfd/twl6040.h
include/linux/mfd/wm8350/core.h
include/linux/mfd/wm8994/pdata.h
include/linux/migrate.h
include/linux/mm.h
include/linux/mm_types.h
include/linux/mmzone.h
include/linux/namei.h
include/linux/netdevice.h
include/linux/nfs_fs.h
include/linux/nfs_fs_sb.h
include/linux/nfs_idmap.h
include/linux/nfs_page.h
include/linux/nfs_xdr.h
include/linux/nfsd/nfsfh.h
include/linux/nilfs2_fs.h
include/linux/of.h
include/linux/omap-dma.h [new file with mode: 0644]
include/linux/oom.h
include/linux/packet_diag.h [new file with mode: 0644]
include/linux/page-flags.h
include/linux/page-isolation.h
include/linux/page_cgroup.h
include/linux/pagemap.h
include/linux/pipe_fs_i.h
include/linux/platform_data/i2c-nomadik.h [new file with mode: 0644]
include/linux/platform_data/lp855x.h [new file with mode: 0644]
include/linux/platform_data/lp8727.h [new file with mode: 0644]
include/linux/platform_data/mv_usb.h
include/linux/power/charger-manager.h
include/linux/power_supply.h
include/linux/printk.h
include/linux/pwm.h
include/linux/pwm_backlight.h
include/linux/random.h
include/linux/scatterlist.h
include/linux/sched.h
include/linux/shm.h
include/linux/shrinker.h
include/linux/skbuff.h
include/linux/slab.h
include/linux/slab_def.h
include/linux/slub_def.h
include/linux/snmp.h
include/linux/string.h
include/linux/sunrpc/auth.h
include/linux/sunrpc/cache.h
include/linux/sunrpc/gss_api.h
include/linux/sunrpc/svc.h
include/linux/sunrpc/xdr.h
include/linux/sunrpc/xprt.h
include/linux/swap.h
include/linux/thermal.h
include/linux/uvcvideo.h
include/linux/v4l2-common.h [new file with mode: 0644]
include/linux/v4l2-subdev.h
include/linux/vfio.h [new file with mode: 0644]
include/linux/videodev2.h
include/linux/virtio_blk.h
include/linux/virtio_ids.h
include/linux/vm_event_item.h
include/linux/vmalloc.h
include/linux/vmstat.h
include/linux/writeback.h
include/media/adv7393.h [new file with mode: 0644]
include/media/davinci/vpif_types.h
include/media/gpio-ir-recv.h
include/media/mt9t001.h
include/media/v4l2-chip-ident.h
include/media/v4l2-dev.h
include/media/v4l2-ioctl.h
include/media/videobuf-core.h
include/media/videobuf2-core.h
include/media/videobuf2-dma-contig.h
include/net/arp.h
include/net/cfg80211.h
include/net/dst.h
include/net/ip6_tunnel.h
include/net/ipv6.h
include/net/ndisc.h
include/net/neighbour.h
include/net/net_namespace.h
include/net/netlink.h
include/net/netns/sctp.h [new file with mode: 0644]
include/net/sctp/sctp.h
include/net/sctp/sm.h
include/net/sctp/structs.h
include/net/snmp.h
include/net/sock.h
include/net/xfrm.h
include/ras/ras_event.h [new file with mode: 0644]
include/sound/es1688.h
include/sound/tea575x-tuner.h
include/trace/events/gfpflags.h
include/trace/events/random.h [new file with mode: 0644]
include/video/da8xx-fb.h
include/video/omapdss.h
include/video/sh_mobile_lcdc.h
include/video/sh_mobile_meram.h
init/Kconfig
init/main.c
ipc/compat.c
ipc/shm.c
ipc/syscall.c
ipc/util.c
ipc/util.h
kernel/audit.c
kernel/cpu.c
kernel/events/uprobes.c
kernel/fork.c
kernel/irq/handle.c
kernel/irq/irqdomain.c
kernel/irq/manage.c
kernel/kexec.c
kernel/kmod.c
kernel/panic.c
kernel/power/suspend.c
kernel/printk.c
kernel/resource.c
kernel/sched/core.c
kernel/softirq.c
kernel/sys.c
kernel/sysctl.c
kernel/sysctl_binary.c
kernel/taskstats.c
kernel/watchdog.c
lib/Kconfig
lib/Kconfig.debug
lib/Makefile
lib/atomic64_test.c
lib/cpu-notifier-error-inject.c
lib/crc32.c
lib/fdt.c [new file with mode: 0644]
lib/fdt_ro.c [new file with mode: 0644]
lib/fdt_rw.c [new file with mode: 0644]
lib/fdt_strerror.c [new file with mode: 0644]
lib/fdt_sw.c [new file with mode: 0644]
lib/fdt_wip.c [new file with mode: 0644]
lib/flex_proportions.c [new file with mode: 0644]
lib/memory-notifier-error-inject.c [new file with mode: 0644]
lib/memweight.c [new file with mode: 0644]
lib/notifier-error-inject.c [new file with mode: 0644]
lib/notifier-error-inject.h [new file with mode: 0644]
lib/pSeries-reconfig-notifier-error-inject.c [new file with mode: 0644]
lib/percpu_counter.c
lib/pm-notifier-error-inject.c [new file with mode: 0644]
lib/scatterlist.c
lib/spinlock_debug.c
lib/vsprintf.c
mm/Kconfig
mm/Makefile
mm/backing-dev.c
mm/compaction.c
mm/fadvise.c
mm/filemap.c
mm/filemap_xip.c
mm/highmem.c
mm/hugetlb.c
mm/hugetlb_cgroup.c [new file with mode: 0644]
mm/hwpoison-inject.c
mm/internal.h
mm/memblock.c
mm/memcontrol.c
mm/memory-failure.c
mm/memory.c
mm/memory_hotplug.c
mm/mempolicy.c
mm/mempool.c
mm/migrate.c
mm/mmap.c
mm/mmu_notifier.c
mm/mmzone.c
mm/mremap.c
mm/oom_kill.c
mm/page-writeback.c
mm/page_alloc.c
mm/page_cgroup.c
mm/page_io.c
mm/page_isolation.c
mm/shmem.c
mm/slab.c
mm/slab.h [new file with mode: 0644]
mm/slab_common.c [new file with mode: 0644]
mm/slob.c
mm/slub.c
mm/sparse.c
mm/swap.c
mm/swap_state.c
mm/swapfile.c
mm/vmalloc.c
mm/vmscan.c
mm/vmstat.c
net/bridge/br_fdb.c
net/bridge/br_stp_timer.c
net/caif/caif_socket.c
net/ceph/ceph_common.c
net/ceph/crush/mapper.c
net/ceph/messenger.c
net/ceph/mon_client.c
net/ceph/msgpool.c
net/ceph/osd_client.c
net/ceph/osdmap.c
net/core/dev.c
net/core/filter.c
net/core/rtnetlink.c
net/core/skbuff.c
net/core/sock.c
net/decnet/dn_route.c
net/ipv4/Makefile
net/ipv4/af_inet.c
net/ipv4/devinet.c
net/ipv4/fib_frontend.c
net/ipv4/fib_trie.c
net/ipv4/igmp.c
net/ipv4/ipmr.c
net/ipv4/netfilter/ipt_rpfilter.c
net/ipv4/route.c
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp.c
net/ipv4/tcp_cong.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_output.c
net/ipv6/Kconfig
net/ipv6/Makefile
net/ipv6/ip6_gre.c [new file with mode: 0644]
net/ipv6/ip6_tunnel.c
net/ipv6/route.c
net/ipv6/tcp_ipv6.c
net/mac80211/mesh.c
net/mac80211/mlme.c
net/mac80211/scan.c
net/packet/Kconfig
net/packet/Makefile
net/packet/af_packet.c
net/packet/diag.c [new file with mode: 0644]
net/packet/internal.h [new file with mode: 0644]
net/sched/act_gact.c
net/sched/sch_generic.c
net/sctp/associola.c
net/sctp/auth.c
net/sctp/bind_addr.c
net/sctp/chunk.c
net/sctp/endpointola.c
net/sctp/input.c
net/sctp/ipv6.c
net/sctp/objcnt.c
net/sctp/output.c
net/sctp/outqueue.c
net/sctp/primitive.c
net/sctp/proc.c
net/sctp/protocol.c
net/sctp/sm_make_chunk.c
net/sctp/sm_sideeffect.c
net/sctp/sm_statefuns.c
net/sctp/sm_statetable.c
net/sctp/socket.c
net/sctp/sysctl.c
net/sctp/transport.c
net/sctp/ulpevent.c
net/sctp/ulpqueue.c
net/sunrpc/Kconfig
net/sunrpc/auth.c
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/auth_gss/gss_mech_switch.c
net/sunrpc/cache.c
net/sunrpc/clnt.c
net/sunrpc/rpcb_clnt.c
net/sunrpc/sched.c
net/sunrpc/xdr.c
net/sunrpc/xprtrdma/transport.c
net/sunrpc/xprtsock.c
net/unix/af_unix.c
net/wireless/reg.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_state.c
scripts/checkpatch.pl
scripts/coccinelle/iterators/use_after_iter.cocci [new file with mode: 0644]
scripts/coccinelle/misc/irqf_oneshot.cocci [new file with mode: 0644]
scripts/config
scripts/kconfig/.gitignore
scripts/kconfig/Makefile
scripts/kconfig/confdata.c
scripts/kconfig/lxdialog/check-lxdialog.sh
scripts/kconfig/lxdialog/textbox.c
scripts/kconfig/mconf.c
scripts/kconfig/nconf.c
scripts/kconfig/nconf.gui.c
scripts/kconfig/streamline_config.pl
scripts/link-vmlinux.sh
scripts/package/builddeb
scripts/sortextable.c
scripts/tags.sh
security/selinux/avc.c
security/selinux/hooks.c
security/smack/smackfs.c
sound/core/misc.c
sound/drivers/mpu401/mpu401_uart.c
sound/i2c/other/tea575x-tuner.c
sound/isa/es1688/es1688_lib.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c
sound/soc/codecs/twl6040.c
sound/sound_firmware.c
sound/usb/clock.c
tools/lib/traceevent/.gitignore [new file with mode: 0644]
tools/lib/traceevent/Makefile
tools/perf/Makefile
tools/perf/builtin-test.c
tools/perf/builtin-top.c
tools/perf/ui/browsers/hists.c
tools/perf/util/annotate.c
tools/perf/util/dso-test-data.c [new file with mode: 0644]
tools/perf/util/evlist.c
tools/perf/util/header.c
tools/perf/util/hist.c
tools/perf/util/map.c
tools/perf/util/map.h
tools/perf/util/parse-events.c
tools/perf/util/session.c
tools/perf/util/symbol.c
tools/perf/util/symbol.h
tools/perf/util/target.c
tools/testing/fault-injection/failcmd.sh [new file with mode: 0644]
tools/testing/ktest/ktest.pl
tools/testing/ktest/sample.conf
tools/testing/selftests/Makefile
tools/testing/selftests/cpu-hotplug/Makefile [new file with mode: 0644]
tools/testing/selftests/cpu-hotplug/on-off-test.sh [new file with mode: 0644]
tools/testing/selftests/memory-hotplug/Makefile [new file with mode: 0644]
tools/testing/selftests/memory-hotplug/on-off-test.sh [new file with mode: 0644]
tools/vm/slabinfo.c

diff --git a/Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads b/Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads
new file mode 100644 (file)
index 0000000..b0b0eeb
--- /dev/null
@@ -0,0 +1,5 @@
+What:          /proc/sys/vm/nr_pdflush_threads
+Date:          June 2012
+Contact:       Wanpeng Li <liwp@linux.vnet.ibm.com>
+Description: Since pdflush is replaced by per-BDI flusher, the interface of old pdflush
+             exported in /proc/sys/vm/ should be removed.
index 3d484e5dc846ed9b7625c83d948322f825c323df..41e5a0cd1e3ed334234c4f3e9e3db1e2fa021dfc 100644 (file)
@@ -39,6 +39,17 @@ Users:               udev rules to set ownership and access permissions or ACLs of
                /dev/fw[0-9]+ character device files
 
 
+What:          /sys/bus/firewire/devices/fw[0-9]+/is_local
+Date:          July 2012
+KernelVersion: 3.6
+Contact:       linux1394-devel@lists.sourceforge.net
+Description:
+               IEEE 1394 node device attribute.
+               Read-only and immutable.
+Values:                1: The sysfs entry represents a local node (a controller card).
+               0: The sysfs entry represents a remote node.
+
+
 What:          /sys/bus/firewire/devices/fw[0-9]+[.][0-9]+/
 Date:          May 2007
 KernelVersion: 2.6.22
index bcd88eb7ebcd240abcdbe0ef34f4a517f0f404bd..3c17b62899f688c5955a3f065e966c9fc72231f7 100644 (file)
@@ -35,8 +35,14 @@ name
 
 pool
 
-       The pool where this rbd image resides. The pool-name pair is unique
-       per rados system.
+       The name of the storage pool where this rbd image resides.
+       An rbd image name is unique within its pool.
+
+pool_id
+
+       The unique identifier for the rbd image's pool.  This is
+       a permanent attribute of the pool.  A pool's id will never
+       change.
 
 size
 
diff --git a/Documentation/ABI/testing/sysfs-devices-edac b/Documentation/ABI/testing/sysfs-devices-edac
new file mode 100644 (file)
index 0000000..30ee78a
--- /dev/null
@@ -0,0 +1,140 @@
+What:          /sys/devices/system/edac/mc/mc*/reset_counters
+Date:          January 2006
+Contact:       linux-edac@vger.kernel.org
+Description:   This write-only control file will zero all the statistical
+               counters for UE and CE errors on the given memory controller.
+               Zeroing the counters will also reset the timer indicating how
+               long since the last counter were reset. This is useful for
+               computing errors/time.  Since the counters are always reset
+               at driver initialization time, no module/kernel parameter
+               is available.
+
+What:          /sys/devices/system/edac/mc/mc*/seconds_since_reset
+Date:          January 2006
+Contact:       linux-edac@vger.kernel.org
+Description:   This attribute file displays how many seconds have elapsed
+               since the last counter reset. This can be used with the error
+               counters to measure error rates.
+
+What:          /sys/devices/system/edac/mc/mc*/mc_name
+Date:          January 2006
+Contact:       linux-edac@vger.kernel.org
+Description:   This attribute file displays the type of memory controller
+               that is being utilized.
+
+What:          /sys/devices/system/edac/mc/mc*/size_mb
+Date:          January 2006
+Contact:       linux-edac@vger.kernel.org
+Description:   This attribute file displays, in count of megabytes, of memory
+               that this memory controller manages.
+
+What:          /sys/devices/system/edac/mc/mc*/ue_count
+Date:          January 2006
+Contact:       linux-edac@vger.kernel.org
+Description:   This attribute file displays the total count of uncorrectable
+               errors that have occurred on this memory controller. If
+               panic_on_ue is set, this counter will not have a chance to
+               increment, since EDAC will panic the system
+
+What:          /sys/devices/system/edac/mc/mc*/ue_noinfo_count
+Date:          January 2006
+Contact:       linux-edac@vger.kernel.org
+Description:   This attribute file displays the number of UEs that have
+               occurred on this memory controller with no information as to
+               which DIMM slot is having errors.
+
+What:          /sys/devices/system/edac/mc/mc*/ce_count
+Date:          January 2006
+Contact:       linux-edac@vger.kernel.org
+Description:   This attribute file displays the total count of correctable
+               errors that have occurred on this memory controller. This
+               count is very important to examine. CEs provide early
+               indications that a DIMM is beginning to fail. This count
+               field should be monitored for non-zero values and report
+               such information to the system administrator.
+
+What:          /sys/devices/system/edac/mc/mc*/ce_noinfo_count
+Date:          January 2006
+Contact:       linux-edac@vger.kernel.org
+Description:   This attribute file displays the number of CEs that
+               have occurred on this memory controller wherewith no
+               information as to which DIMM slot is having errors. Memory is
+               handicapped, but operational, yet no information is available
+               to indicate which slot the failing memory is in. This count
+               field should be also be monitored for non-zero values.
+
+What:          /sys/devices/system/edac/mc/mc*/sdram_scrub_rate
+Date:          February 2007
+Contact:       linux-edac@vger.kernel.org
+Description:   Read/Write attribute file that controls memory scrubbing.
+               The scrubbing rate used by the memory controller is set by
+               writing a minimum bandwidth in bytes/sec to the attribute file.
+               The rate will be translated to an internal value that gives at
+               least the specified rate.
+               Reading the file will return the actual scrubbing rate employed.
+               If configuration fails or memory scrubbing is not implemented,
+               the value of the attribute file will be -1.
+
+What:          /sys/devices/system/edac/mc/mc*/max_location
+Date:          April 2012
+Contact:       Mauro Carvalho Chehab <mchehab@redhat.com>
+               linux-edac@vger.kernel.org
+Description:   This attribute file displays the information about the last
+               available memory slot in this memory controller. It is used by
+               userspace tools in order to display the memory filling layout.
+
+What:          /sys/devices/system/edac/mc/mc*/(dimm|rank)*/size
+Date:          April 2012
+Contact:       Mauro Carvalho Chehab <mchehab@redhat.com>
+               linux-edac@vger.kernel.org
+Description:   This attribute file will display the size of dimm or rank.
+               For dimm*/size, this is the size, in MB of the DIMM memory
+               stick. For rank*/size, this is the size, in MB for one rank
+               of the DIMM memory stick. On single rank memories (1R), this
+               is also the total size of the dimm. On dual rank (2R) memories,
+               this is half the size of the total DIMM memories.
+
+What:          /sys/devices/system/edac/mc/mc*/(dimm|rank)*/dimm_dev_type
+Date:          April 2012
+Contact:       Mauro Carvalho Chehab <mchehab@redhat.com>
+               linux-edac@vger.kernel.org
+Description:   This attribute file will display what type of DRAM device is
+               being utilized on this DIMM (x1, x2, x4, x8, ...).
+
+What:          /sys/devices/system/edac/mc/mc*/(dimm|rank)*/dimm_edac_mode
+Date:          April 2012
+Contact:       Mauro Carvalho Chehab <mchehab@redhat.com>
+               linux-edac@vger.kernel.org
+Description:   This attribute file will display what type of Error detection
+               and correction is being utilized. For example: S4ECD4ED would
+               mean a Chipkill with x4 DRAM.
+
+What:          /sys/devices/system/edac/mc/mc*/(dimm|rank)*/dimm_label
+Date:          April 2012
+Contact:       Mauro Carvalho Chehab <mchehab@redhat.com>
+               linux-edac@vger.kernel.org
+Description:   This control file allows this DIMM to have a label assigned
+               to it. With this label in the module, when errors occur
+               the output can provide the DIMM label in the system log.
+               This becomes vital for panic events to isolate the
+               cause of the UE event.
+               DIMM Labels must be assigned after booting, with information
+               that correctly identifies the physical slot with its
+               silk screen label. This information is currently very
+               motherboard specific and determination of this information
+               must occur in userland at this time.
+
+What:          /sys/devices/system/edac/mc/mc*/(dimm|rank)*/dimm_location
+Date:          April 2012
+Contact:       Mauro Carvalho Chehab <mchehab@redhat.com>
+               linux-edac@vger.kernel.org
+Description:   This attribute file will display the location (csrow/channel,
+               branch/channel/slot or channel/slot) of the dimm or rank.
+
+What:          /sys/devices/system/edac/mc/mc*/(dimm|rank)*/dimm_mem_type
+Date:          April 2012
+Contact:       Mauro Carvalho Chehab <mchehab@redhat.com>
+               linux-edac@vger.kernel.org
+Description:   This attribute file will display what type of memory is
+               currently on this csrow. Normally, either buffered or
+               unbuffered memory (for example, Unbuffered-DDR3).
diff --git a/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb b/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb
new file mode 100644 (file)
index 0000000..2107082
--- /dev/null
@@ -0,0 +1,44 @@
+What:          /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_alpha
+Date:          May 2012
+Contact:       Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Description:
+               This file is only available on fb[0-9] devices corresponding
+               to overlay planes.
+
+               Stores the alpha blending value for the overlay. Values range
+               from 0 (transparent) to 255 (opaque). The value is ignored if
+               the mode is not set to Alpha Blending.
+
+What:          /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_mode
+Date:          May 2012
+Contact:       Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Description:
+               This file is only available on fb[0-9] devices corresponding
+               to overlay planes.
+
+               Selects the composition mode for the overlay. Possible values
+               are
+
+               0 - Alpha Blending
+               1 - ROP3
+
+What:          /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_position
+Date:          May 2012
+Contact:       Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Description:
+               This file is only available on fb[0-9] devices corresponding
+               to overlay planes.
+
+               Stores the x,y overlay position on the display in pixels. The
+               position format is `[0-9]+,[0-9]+'.
+
+What:          /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_rop3
+Date:          May 2012
+Contact:       Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Description:
+               This file is only available on fb[0-9] devices corresponding
+               to overlay planes.
+
+               Stores the raster operation (ROP3) for the overlay. Values
+               range from 0 to 255. The value is ignored if the mode is not
+               set to ROP3.
index 2e7df91620de46ce9e473298f7a8d7fa8216f0db..019e1e29370e73b96aff40903bc49551206caa77 100644 (file)
@@ -29,3 +29,10 @@ KernelVersion:       2.6.39
 Contact:       "Corentin Chary" <corentincj@iksaif.net>
 Description:
                Control the card touchpad. 1 means on, 0 means off.
+
+What:          /sys/devices/platform/<platform>/lid_resume
+Date:          May 2012
+KernelVersion: 3.5
+Contact:       "AceLan Kao" <acelan.kao@canonical.com>
+Description:
+               Resume on lid open. 1 means on, 0 means off.
index 5c72eed89563083250217336c79ab1d7f350a7c5..f50309081ac78e0900c3ef5e5646abf998079657 100644 (file)
@@ -49,3 +49,45 @@ DMA_ATTR_NON_CONSISTENT lets the platform to choose to return either
 consistent or non-consistent memory as it sees fit.  By using this API,
 you are guaranteeing to the platform that you have all the correct and
 necessary sync points for this memory in the driver.
+
+DMA_ATTR_NO_KERNEL_MAPPING
+--------------------------
+
+DMA_ATTR_NO_KERNEL_MAPPING lets the platform to avoid creating a kernel
+virtual mapping for the allocated buffer. On some architectures creating
+such mapping is non-trivial task and consumes very limited resources
+(like kernel virtual address space or dma consistent address space).
+Buffers allocated with this attribute can be only passed to user space
+by calling dma_mmap_attrs(). By using this API, you are guaranteeing
+that you won't dereference the pointer returned by dma_alloc_attr(). You
+can threat it as a cookie that must be passed to dma_mmap_attrs() and
+dma_free_attrs(). Make sure that both of these also get this attribute
+set on each call.
+
+Since it is optional for platforms to implement
+DMA_ATTR_NO_KERNEL_MAPPING, those that do not will simply ignore the
+attribute and exhibit default behavior.
+
+DMA_ATTR_SKIP_CPU_SYNC
+----------------------
+
+By default dma_map_{single,page,sg} functions family transfer a given
+buffer from CPU domain to device domain. Some advanced use cases might
+require sharing a buffer between more than one device. This requires
+having a mapping created separately for each device and is usually
+performed by calling dma_map_{single,page,sg} function more than once
+for the given buffer with device pointer to each device taking part in
+the buffer sharing. The first call transfers a buffer from 'CPU' domain
+to 'device' domain, what synchronizes CPU caches for the given region
+(usually it means that the cache has been flushed or invalidated
+depending on the dma direction). However, next calls to
+dma_map_{single,page,sg}() for other devices will perform exactly the
+same sychronization operation on the CPU cache. CPU cache sychronization
+might be a time consuming operation, especially if the buffers are
+large, so it is highly recommended to avoid it if possible.
+DMA_ATTR_SKIP_CPU_SYNC allows platform code to skip synchronization of
+the CPU cache for the given buffer assuming that it has been already
+transferred to 'device' domain. This attribute can be also used for
+dma_unmap_{single,page,sg} functions family to force buffer to stay in
+device domain after releasing a mapping for it. Use this attribute with
+care!
index 7c49facecd25a8ee7cbe293cb4af906d0728d116..1078e45f189f0630c6c6ea50f74b3764dc1cea43 100644 (file)
@@ -194,7 +194,7 @@ in the frequency range from 87,5 to 108,0 MHz</title>
        <corpauthor>National Radio Systems Committee
 (<ulink url="http://www.nrscstandards.org">http://www.nrscstandards.org</ulink>)</corpauthor>
       </authorgroup>
-      <title>NTSC-4: United States RBDS Standard</title>
+      <title>NRSC-4: United States RBDS Standard</title>
     </biblioentry>
 
     <biblioentry id="iso12232">
index 4101aeb565406b6ed08219feff76bdd735f1fd33..b91d25313b631eb25fec06bf42877131af31c61e 100644 (file)
@@ -464,14 +464,14 @@ The <structfield>type</structfield> field of the respective
 <structfield>tuner</structfield> field contains the index number of
 the tuner.</para>
 
-      <para>Radio devices have exactly one tuner with index zero, no
+      <para>Radio input devices have exactly one tuner with index zero, no
 video inputs.</para>
 
       <para>To query and change tuner properties applications use the
 &VIDIOC-G-TUNER; and &VIDIOC-S-TUNER; ioctl, respectively. The
 &v4l2-tuner; returned by <constant>VIDIOC_G_TUNER</constant> also
 contains signal status information applicable when the tuner of the
-current video input, or a radio tuner is queried. Note that
+current video or radio input is queried. Note that
 <constant>VIDIOC_S_TUNER</constant> does not switch the current tuner,
 when there is more than one at all. The tuner is solely determined by
 the current video input. Drivers must support both ioctls and set the
@@ -491,8 +491,17 @@ the modulator. The <structfield>type</structfield> field of the
 respective &v4l2-output; returned by the &VIDIOC-ENUMOUTPUT; ioctl is
 set to <constant>V4L2_OUTPUT_TYPE_MODULATOR</constant> and its
 <structfield>modulator</structfield> field contains the index number
-of the modulator. This specification does not define radio output
-devices.</para>
+of the modulator.</para>
+
+      <para>Radio output devices have exactly one modulator with index
+zero, no video outputs.</para>
+
+      <para>A video or radio device cannot support both a tuner and a
+modulator. Two separate device nodes will have to be used for such
+hardware, one that supports the tuner functionality and one that supports
+the modulator functionality. The reason is a limitation with the
+&VIDIOC-S-FREQUENCY; ioctl where you cannot specify whether the frequency
+is for a tuner or a modulator.</para>
 
       <para>To query and change modulator properties applications use
 the &VIDIOC-G-MODULATOR; and &VIDIOC-S-MODULATOR; ioctl. Note that
index ea42ef824948cd6392189328faa65725c22013f8..faa0fd14666a54bc2688ca176c9dee531cc5eccf 100644 (file)
@@ -2377,10 +2377,11 @@ that used it. It was originally scheduled for removal in 2.6.35.
          <para>V4L2_CTRL_FLAG_VOLATILE was added to signal volatile controls to userspace.</para>
         </listitem>
         <listitem>
-         <para>Add selection API for extended control over cropping and
-composing. Does not affect the compatibility of current drivers and
-applications.  See <link linkend="selection-api"> selection API </link> for
-details.</para>
+         <para>Add selection API for extended control over cropping
+         and composing. Does not affect the compatibility of current
+         drivers and applications. See <link
+         linkend="selection-api"> selection API </link> for
+         details.</para>
         </listitem>
       </orderedlist>
     </section>
@@ -2458,6 +2459,36 @@ details.</para>
       </orderedlist>
     </section>
 
+    <section>
+      <title>V4L2 in Linux 3.6</title>
+      <orderedlist>
+       <listitem>
+         <para>Replaced <structfield>input</structfield> in
+         <structname>v4l2_buffer</structname> by
+         <structfield>reserved2</structfield> and removed
+         <constant>V4L2_BUF_FLAG_INPUT</constant>.</para>
+       </listitem>
+      </orderedlist>
+    </section>
+
+    <section>
+      <title>V4L2 in Linux 3.6</title>
+      <orderedlist>
+        <listitem>
+         <para>Added V4L2_CAP_VIDEO_M2M and V4L2_CAP_VIDEO_M2M_MPLANE capabilities.</para>
+        </listitem>
+      </orderedlist>
+    </section>
+
+    <section>
+      <title>V4L2 in Linux 3.6</title>
+      <orderedlist>
+        <listitem>
+         <para>Added support for frequency band enumerations: &VIDIOC-ENUM-FREQ-BANDS;.</para>
+        </listitem>
+      </orderedlist>
+    </section>
+
     <section id="other">
       <title>Relation of V4L2 to other Linux multimedia APIs</title>
 
@@ -2587,6 +2618,9 @@ ioctls.</para>
          <para><link linkend="v4l2-auto-focus-area"><constant>
          V4L2_CID_AUTO_FOCUS_AREA</constant></link> control.</para>
         </listitem>
+        <listitem>
+         <para>Support for frequency band enumeration: &VIDIOC-ENUM-FREQ-BANDS; ioctl.</para>
+        </listitem>
       </itemizedlist>
     </section>
 
index cda0dfb6769aee5d0fcadb8a30b4d19658259bc7..b0964fb4e8348853619efd3296b7821d4e6b6803 100644 (file)
@@ -372,6 +372,11 @@ minimum value disables backlight compensation.</entry>
            Cr component, bits [15:8] as Cb component and bits [31:16] must be zero.
          </entry>
          </row>
+         <row>
+           <entry><constant>V4L2_CID_AUTOBRIGHTNESS</constant></entry>
+           <entry>boolean</entry>
+           <entry>Enable Automatic Brightness.</entry>
+         </row>
          <row>
            <entry><constant>V4L2_CID_ROTATE</constant></entry>
            <entry>integer</entry>
index 4afcbbec5eda6c7146a497646ec36345ce4ef2bf..a3d9dd093268747de3751dcc41001e1fba638b11 100644 (file)
       </para>
     </section>
 
-    <section>
+    <section id="v4l2-subdev-selections">
       <title>Selections: cropping, scaling and composition</title>
 
       <para>Many sub-devices support cropping frames on their input or output
       size. Both the coordinates and sizes are expressed in pixels.</para>
 
       <para>As for pad formats, drivers store try and active
-      rectangles for the selection targets of ACTUAL type <xref
-      linkend="v4l2-subdev-selection-targets">.</xref></para>
+      rectangles for the selection targets <xref
+      linkend="v4l2-selections-common" />.</para>
 
       <para>On sink pads, cropping is applied relative to the
       current pad format. The pad format represents the image size as
       <para>Scaling support is optional. When supported by a subdev,
       the crop rectangle on the subdev's sink pad is scaled to the
       size configured using the &VIDIOC-SUBDEV-S-SELECTION; IOCTL
-      using <constant>V4L2_SUBDEV_SEL_COMPOSE_ACTUAL</constant>
+      using <constant>V4L2_SEL_TGT_COMPOSE</constant>
       selection target on the same pad. If the subdev supports scaling
       but not composing, the top and left values are not used and must
       always be set to zero.</para>
       <para>The drivers should always use the closest possible
       rectangle the user requests on all selection targets, unless
       specifically told otherwise.
-      <constant>V4L2_SUBDEV_SEL_FLAG_SIZE_GE</constant> and
-      <constant>V4L2_SUBDEV_SEL_FLAG_SIZE_LE</constant> flags may be
+      <constant>V4L2_SEL_FLAG_GE</constant> and
+      <constant>V4L2_SEL_FLAG_LE</constant> flags may be
       used to round the image size either up or down. <xref
-      linkend="v4l2-subdev-selection-flags"></xref></para>
+      linkend="v4l2-selection-flags" /></para>
     </section>
 
     <section>
       <title>Types of selection targets</title>
 
       <section>
-       <title>ACTUAL targets</title>
+       <title>Actual targets</title>
 
-       <para>ACTUAL targets reflect the actual hardware configuration
-       at any point of time. There is a BOUNDS target
-       corresponding to every ACTUAL.</para>
+       <para>Actual targets (without a postfix) reflect the actual
+       hardware configuration at any point of time. There is a BOUNDS
+       target corresponding to every actual target.</para>
       </section>
 
       <section>
        <title>BOUNDS targets</title>
 
-       <para>BOUNDS targets is the smallest rectangle that contains
-       all valid ACTUAL rectangles. It may not be possible to set the
-       ACTUAL rectangle as large as the BOUNDS rectangle, however.
-       This may be because e.g. a sensor's pixel array is not
-       rectangular but cross-shaped or round. The maximum size may
-       also be smaller than the BOUNDS rectangle.</para>
+       <para>BOUNDS targets is the smallest rectangle that contains all
+       valid actual rectangles. It may not be possible to set the actual
+       rectangle as large as the BOUNDS rectangle, however. This may be
+       because e.g. a sensor's pixel array is not rectangular but
+       cross-shaped or round. The maximum size may also be smaller than the
+       BOUNDS rectangle.</para>
       </section>
 
     </section>
       performed by the user: the changes made will be propagated to
       any subsequent stages. If this behaviour is not desired, the
       user must set
-      <constant>V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG</constant> flag. This
+      <constant>V4L2_SEL_FLAG_KEEP_CONFIG</constant> flag. This
       flag causes no propagation of the changes are allowed in any
       circumstances. This may also cause the accessed rectangle to be
       adjusted by the driver, depending on the properties of the
index fd6aca2922b64994f283f2d3f0820f7f10541824..1885cc0755cb48a438aadfe686857f46ee541172 100644 (file)
@@ -683,14 +683,12 @@ memory, set by the application. See <xref linkend="userp" /> for details.
          </row>
          <row>
            <entry>__u32</entry>
-           <entry><structfield>input</structfield></entry>
+           <entry><structfield>reserved2</structfield></entry>
            <entry></entry>
-           <entry>Some video capture drivers support rapid and
-synchronous video input changes, a function useful for example in
-video surveillance applications. For this purpose applications set the
-<constant>V4L2_BUF_FLAG_INPUT</constant> flag, and this field to the
-number of a video input as in &v4l2-input; field
-<structfield>index</structfield>.</entry>
+           <entry>A place holder for future extensions and custom
+(driver defined) buffer types
+<constant>V4L2_BUF_TYPE_PRIVATE</constant> and higher. Applications
+should set this to 0.</entry>
          </row>
          <row>
            <entry>__u32</entry>
@@ -921,13 +919,6 @@ previous key frame.</entry>
            <entry>The <structfield>timecode</structfield> field is valid.
 Drivers set or clear this flag when the <constant>VIDIOC_DQBUF</constant>
 ioctl is called.</entry>
-         </row>
-         <row>
-           <entry><constant>V4L2_BUF_FLAG_INPUT</constant></entry>
-           <entry>0x0200</entry>
-           <entry>The <structfield>input</structfield> field is valid.
-Applications set or clear this flag before calling the
-<constant>VIDIOC_QBUF</constant> ioctl.</entry>
          </row>
          <row>
            <entry><constant>V4L2_BUF_FLAG_PREPARED</constant></entry>
index b299e4779354afac2ba9a2891ad1699fcfd313b0..e7ed5077834deaa027fe1a202a555631338dba15 100644 (file)
@@ -53,11 +53,11 @@ cropping and composing rectangles have the same size.</para>
        </mediaobject>
       </figure>
 
-For complete list of the available selection targets see table <xref
-linkend="v4l2-sel-target"/>
-
     </section>
 
+    See <xref linkend="v4l2-selection-targets" /> for more
+    information.
+
   <section>
 
   <title>Configuration</title>
@@ -74,7 +74,7 @@ cropping/composing rectangles may have to be aligned, and both the source and
 the sink may have arbitrary upper and lower size limits. Therefore, as usual,
 drivers are expected to adjust the requested parameters and return the actual
 values selected. An application can control the rounding behaviour using <link
-linkend="v4l2-sel-flags"> constraint flags </link>.</para>
+linkend="v4l2-selection-flags"> constraint flags </link>.</para>
 
    <section>
 
@@ -91,7 +91,7 @@ top/left corner at position <constant> (0,0) </constant>.  The rectangle's
 coordinates are expressed in pixels.</para>
 
 <para>The top left corner, width and height of the source rectangle, that is
-the area actually sampled, is given by the <constant> V4L2_SEL_TGT_CROP_ACTIVE
+the area actually sampled, is given by the <constant> V4L2_SEL_TGT_CROP
 </constant> target. It uses the same coordinate system as <constant>
 V4L2_SEL_TGT_CROP_BOUNDS </constant>. The active cropping area must lie
 completely inside the capture boundaries. The driver may further adjust the
@@ -111,13 +111,13 @@ height are equal to the image size set by <constant> VIDIOC_S_FMT </constant>.
 </para>
 
 <para>The part of a buffer into which the image is inserted by the hardware is
-controlled by the <constant> V4L2_SEL_TGT_COMPOSE_ACTIVE </constant> target.
+controlled by the <constant> V4L2_SEL_TGT_COMPOSE </constant> target.
 The rectangle's coordinates are also expressed in the same coordinate system as
 the bounds rectangle. The composing rectangle must lie completely inside bounds
 rectangle. The driver must adjust the composing rectangle to fit to the
 bounding limits. Moreover, the driver can perform other adjustments according
 to hardware limitations. The application can control rounding behaviour using
-<link linkend="v4l2-sel-flags"> constraint flags </link>.</para>
+<link linkend="v4l2-selection-flags"> constraint flags </link>.</para>
 
 <para>For capture devices the default composing rectangle is queried using
 <constant> V4L2_SEL_TGT_COMPOSE_DEFAULT </constant>. It is usually equal to the
@@ -125,7 +125,7 @@ bounding rectangle.</para>
 
 <para>The part of a buffer that is modified by the hardware is given by
 <constant> V4L2_SEL_TGT_COMPOSE_PADDED </constant>. It contains all pixels
-defined using <constant> V4L2_SEL_TGT_COMPOSE_ACTIVE </constant> plus all
+defined using <constant> V4L2_SEL_TGT_COMPOSE </constant> plus all
 padding data modified by hardware during insertion process. All pixels outside
 this rectangle <emphasis>must not</emphasis> be changed by the hardware. The
 content of pixels that lie inside the padded area but outside active area is
@@ -153,7 +153,7 @@ specified using <constant> VIDIOC_S_FMT </constant> ioctl.</para>
 
 <para>The top left corner, width and height of the source rectangle, that is
 the area from which image date are processed by the hardware, is given by the
-<constant> V4L2_SEL_TGT_CROP_ACTIVE </constant>. Its coordinates are expressed
+<constant> V4L2_SEL_TGT_CROP </constant>. Its coordinates are expressed
 in in the same coordinate system as the bounds rectangle. The active cropping
 area must lie completely inside the crop boundaries and the driver may further
 adjust the requested size and/or position according to hardware
@@ -165,7 +165,7 @@ bounding rectangle.</para>
 
 <para>The part of a video signal or graphics display where the image is
 inserted by the hardware is controlled by <constant>
-V4L2_SEL_TGT_COMPOSE_ACTIVE </constant> target.  The rectangle's coordinates
+V4L2_SEL_TGT_COMPOSE </constant> target.  The rectangle's coordinates
 are expressed in pixels. The composing rectangle must lie completely inside the
 bounds rectangle.  The driver must adjust the area to fit to the bounding
 limits.  Moreover, the driver can perform other adjustments according to
@@ -184,7 +184,7 @@ such a padded area is driver-dependent feature not covered by this document.
 Driver developers are encouraged to keep padded rectangle equal to active one.
 The padded target is accessed by the <constant> V4L2_SEL_TGT_COMPOSE_PADDED
 </constant> identifier.  It must contain all pixels from the <constant>
-V4L2_SEL_TGT_COMPOSE_ACTIVE </constant> target.</para>
+V4L2_SEL_TGT_COMPOSE </constant> target.</para>
 
    </section>
 
@@ -193,8 +193,8 @@ V4L2_SEL_TGT_COMPOSE_ACTIVE </constant> target.</para>
      <title>Scaling control</title>
 
 <para>An application can detect if scaling is performed by comparing the width
-and the height of rectangles obtained using <constant> V4L2_SEL_TGT_CROP_ACTIVE
-</constant> and <constant> V4L2_SEL_TGT_COMPOSE_ACTIVE </constant> targets. If
+and the height of rectangles obtained using <constant> V4L2_SEL_TGT_CROP
+</constant> and <constant> V4L2_SEL_TGT_COMPOSE </constant> targets. If
 these are not equal then the scaling is applied. The application can compute
 the scaling ratios using these values.</para>
 
@@ -252,7 +252,7 @@ area)</para>
        ret = ioctl(fd, &VIDIOC-G-SELECTION;, &amp;sel);
        if (ret)
                exit(-1);
-       sel.target = V4L2_SEL_TGT_CROP_ACTIVE;
+       sel.target = V4L2_SEL_TGT_CROP;
        ret = ioctl(fd, &VIDIOC-S-SELECTION;, &amp;sel);
        if (ret)
                exit(-1);
@@ -281,7 +281,7 @@ area)</para>
        r.left = sel.r.width / 4;
        r.top = sel.r.height / 4;
        sel.r = r;
-       sel.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
+       sel.target = V4L2_SEL_TGT_COMPOSE;
        sel.flags = V4L2_SEL_FLAG_LE;
        ret = ioctl(fd, &VIDIOC-S-SELECTION;, &amp;sel);
        if (ret)
@@ -298,11 +298,11 @@ V4L2_BUF_TYPE_VIDEO_OUTPUT </constant> for other devices</para>
 
        &v4l2-selection; compose = {
                .type = V4L2_BUF_TYPE_VIDEO_OUTPUT,
-               .target = V4L2_SEL_TGT_COMPOSE_ACTIVE,
+               .target = V4L2_SEL_TGT_COMPOSE,
        };
        &v4l2-selection; crop = {
                .type = V4L2_BUF_TYPE_VIDEO_OUTPUT,
-               .target = V4L2_SEL_TGT_CROP_ACTIVE,
+               .target = V4L2_SEL_TGT_CROP,
        };
        double hscale, vscale;
 
diff --git a/Documentation/DocBook/media/v4l/selections-common.xml b/Documentation/DocBook/media/v4l/selections-common.xml
new file mode 100644 (file)
index 0000000..7502f78
--- /dev/null
@@ -0,0 +1,164 @@
+<section id="v4l2-selections-common">
+
+  <title>Common selection definitions</title>
+
+  <para>While the <link linkend="selection-api">V4L2 selection
+  API</link> and <link linkend="v4l2-subdev-selections">V4L2 subdev
+  selection APIs</link> are very similar, there's one fundamental
+  difference between the two. On sub-device API, the selection
+  rectangle refers to the media bus format, and is bound to a
+  sub-device's pad. On the V4L2 interface the selection rectangles
+  refer to the in-memory pixel format.</para>
+
+  <para>This section defines the common definitions of the
+  selection interfaces on the two APIs.</para>
+
+  <section id="v4l2-selection-targets">
+
+    <title>Selection targets</title>
+
+    <para>The precise meaning of the selection targets may be
+    dependent on which of the two interfaces they are used.</para>
+
+    <table pgwide="1" frame="none" id="v4l2-selection-targets-table">
+    <title>Selection target definitions</title>
+      <tgroup cols="5">
+       <colspec colname="c1" />
+       <colspec colname="c2" />
+       <colspec colname="c3" />
+       <colspec colname="c4" />
+       <colspec colname="c5" />
+       &cs-def;
+       <thead>
+         <row rowsep="1">
+           <entry align="left">Target name</entry>
+           <entry align="left">id</entry>
+           <entry align="left">Definition</entry>
+           <entry align="left">Valid for V4L2</entry>
+           <entry align="left">Valid for V4L2 subdev</entry>
+         </row>
+       </thead>
+       <tbody valign="top">
+         <row>
+           <entry><constant>V4L2_SEL_TGT_CROP</constant></entry>
+           <entry>0x0000</entry>
+           <entry>Crop rectangle. Defines the cropped area.</entry>
+           <entry>Yes</entry>
+           <entry>Yes</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_SEL_TGT_CROP_DEFAULT</constant></entry>
+           <entry>0x0001</entry>
+           <entry>Suggested cropping rectangle that covers the "whole picture".</entry>
+           <entry>Yes</entry>
+           <entry>No</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_SEL_TGT_CROP_BOUNDS</constant></entry>
+           <entry>0x0002</entry>
+           <entry>Bounds of the crop rectangle. All valid crop
+           rectangles fit inside the crop bounds rectangle.
+           </entry>
+           <entry>Yes</entry>
+           <entry>Yes</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_SEL_TGT_COMPOSE</constant></entry>
+           <entry>0x0100</entry>
+           <entry>Compose rectangle. Used to configure scaling
+           and composition.</entry>
+           <entry>Yes</entry>
+           <entry>Yes</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_SEL_TGT_COMPOSE_DEFAULT</constant></entry>
+           <entry>0x0101</entry>
+           <entry>Suggested composition rectangle that covers the "whole picture".</entry>
+           <entry>Yes</entry>
+           <entry>No</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_SEL_TGT_COMPOSE_BOUNDS</constant></entry>
+           <entry>0x0102</entry>
+           <entry>Bounds of the compose rectangle. All valid compose
+           rectangles fit inside the compose bounds rectangle.</entry>
+           <entry>Yes</entry>
+           <entry>Yes</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_SEL_TGT_COMPOSE_PADDED</constant></entry>
+           <entry>0x0103</entry>
+           <entry>The active area and all padding pixels that are inserted or
+           modified by hardware.</entry>
+           <entry>Yes</entry>
+           <entry>No</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+  </section>
+
+  <section id="v4l2-selection-flags">
+
+    <title>Selection flags</title>
+
+    <table pgwide="1" frame="none" id="v4l2-selection-flags-table">
+    <title>Selection flag definitions</title>
+      <tgroup cols="5">
+       <colspec colname="c1" />
+       <colspec colname="c2" />
+       <colspec colname="c3" />
+       <colspec colname="c4" />
+       <colspec colname="c5" />
+       &cs-def;
+       <thead>
+       <row rowsep="1">
+           <entry align="left">Flag name</entry>
+           <entry align="left">id</entry>
+           <entry align="left">Definition</entry>
+           <entry align="left">Valid for V4L2</entry>
+           <entry align="left">Valid for V4L2 subdev</entry>
+       </row>
+       </thead>
+       <tbody valign="top">
+         <row>
+           <entry><constant>V4L2_SEL_FLAG_GE</constant></entry>
+           <entry>(1 &lt;&lt; 0)</entry>
+           <entry>Suggest the driver it should choose greater or
+           equal rectangle (in size) than was requested. Albeit the
+           driver may choose a lesser size, it will only do so due to
+           hardware limitations. Without this flag (and
+           <constant>V4L2_SEL_FLAG_LE</constant>) the
+           behaviour is to choose the closest possible
+           rectangle.</entry>
+           <entry>Yes</entry>
+           <entry>Yes</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_SEL_FLAG_LE</constant></entry>
+           <entry>(1 &lt;&lt; 1)</entry>
+           <entry>Suggest the driver it
+           should choose lesser or equal rectangle (in size) than was
+           requested. Albeit the driver may choose a greater size, it
+           will only do so due to hardware limitations.</entry>
+           <entry>Yes</entry>
+           <entry>Yes</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_SEL_FLAG_KEEP_CONFIG</constant></entry>
+           <entry>(1 &lt;&lt; 2)</entry>
+           <entry>The configuration must not be propagated to any
+           further processing steps. If this flag is not given, the
+           configuration is propagated inside the subdevice to all
+           further processing steps.</entry>
+           <entry>No</entry>
+           <entry>Yes</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+  </section>
+
+</section>
index 008c2d73a484622d8343752391fd5312c3ff2793..eee6908c749fd3b6fb66e0e9ab84c9775c09509b 100644 (file)
@@ -140,6 +140,11 @@ structs, ioctls) must be noted in more detail in the history chapter
 applications. -->
 
       <revision>
+       <revnumber>3.6</revnumber>
+       <date>2012-07-02</date>
+       <authorinitials>hv</authorinitials>
+       <revremark>Added VIDIOC_ENUM_FREQ_BANDS.
+       </revremark>
        <revnumber>3.5</revnumber>
        <date>2012-05-07</date>
        <authorinitials>sa, sn</authorinitials>
@@ -534,6 +539,7 @@ and discussions on the V4L mailing list.</revremark>
     &sub-enum-fmt;
     &sub-enum-framesizes;
     &sub-enum-frameintervals;
+    &sub-enum-freq-bands;
     &sub-enuminput;
     &sub-enumoutput;
     &sub-enumstd;
@@ -589,6 +595,11 @@ and discussions on the V4L mailing list.</revremark>
     &sub-write;
   </appendix>
 
+  <appendix>
+    <title>Common definitions for V4L2 and V4L2 subdev interfaces</title>
+      &sub-selections-common;
+  </appendix>
+
   <appendix id="videodev">
     <title>Video For Linux Two Header File</title>
     &sub-videodev2-h;
index a2474ecb574acd06c533f7a22bd4c5fbccacc4c6..a8cda1acacd9a129b47d15451bfe054d6fcfd9c8 100644 (file)
@@ -64,7 +64,7 @@ different sizes.</para>
     <para>To allocate device buffers applications initialize relevant fields of
 the <structname>v4l2_create_buffers</structname> structure. They set the
 <structfield>type</structfield> field in the
-<structname>v4l2_format</structname> structure, embedded in this
+&v4l2-format; structure, embedded in this
 structure, to the respective stream or buffer type.
 <structfield>count</structfield> must be set to the number of required buffers.
 <structfield>memory</structfield> specifies the required I/O method. The
@@ -97,7 +97,13 @@ information.</para>
          <row>
            <entry>__u32</entry>
            <entry><structfield>count</structfield></entry>
-           <entry>The number of buffers requested or granted.</entry>
+           <entry>The number of buffers requested or granted. If count == 0, then
+           <constant>VIDIOC_CREATE_BUFS</constant> will set <structfield>index</structfield>
+           to the current number of created buffers, and it will check the validity of
+           <structfield>memory</structfield> and <structfield>format.type</structfield>.
+           If those are invalid -1 is returned and errno is set to &EINVAL;,
+           otherwise <constant>VIDIOC_CREATE_BUFS</constant> returns 0. It will
+           never set errno to &EBUSY; in this particular case.</entry>
          </row>
          <row>
            <entry>__u32</entry>
@@ -108,7 +114,7 @@ information.</para>
 /></entry>
          </row>
          <row>
-           <entry>struct&nbsp;v4l2_format</entry>
+           <entry>&v4l2-format;</entry>
            <entry><structfield>format</structfield></entry>
            <entry>Filled in by the application, preserved by the driver.</entry>
          </row>
index 6673ce582050d244e4a6fb28f88a65a49c0eb018..cd7720d404eaf337acb6b6fef9cff431903c05cb 100644 (file)
       interface and may change in the future.</para>
     </note>
 
-    <para>To query the available timings, applications initialize the
-<structfield>index</structfield> field and zero the reserved array of &v4l2-dv-timings-cap;
-and call the <constant>VIDIOC_DV_TIMINGS_CAP</constant> ioctl with a pointer to this
-structure. Drivers fill the rest of the structure or return an
-&EINVAL; when the index is out of bounds. To enumerate all supported DV timings,
-applications shall begin at index zero, incrementing by one until the
-driver returns <errorcode>EINVAL</errorcode>. Note that drivers may enumerate a
-different set of DV timings after switching the video input or
-output.</para>
+    <para>To query the capabilities of the DV receiver/transmitter applications can call
+this ioctl and the driver will fill in the structure. Note that drivers may return
+different values after switching the video input or output.</para>
 
     <table pgwide="1" frame="none" id="v4l2-bt-timings-cap">
       <title>struct <structname>v4l2_bt_timings_cap</structname></title>
@@ -115,7 +109,7 @@ output.</para>
          <row>
            <entry>__u32</entry>
            <entry><structfield>reserved</structfield>[16]</entry>
-           <entry></entry>
+           <entry>Reserved for future extensions. Drivers must set the array to zero.</entry>
          </row>
        </tbody>
       </tgroup>
diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml b/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml
new file mode 100644 (file)
index 0000000..6541ba0
--- /dev/null
@@ -0,0 +1,179 @@
+<refentry id="vidioc-enum-freq-bands">
+  <refmeta>
+    <refentrytitle>ioctl VIDIOC_ENUM_FREQ_BANDS</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>VIDIOC_ENUM_FREQ_BANDS</refname>
+    <refpurpose>Enumerate supported frequency bands</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+       <funcdef>int <function>ioctl</function></funcdef>
+       <paramdef>int <parameter>fd</parameter></paramdef>
+       <paramdef>int <parameter>request</parameter></paramdef>
+       <paramdef>struct v4l2_frequency_band
+*<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+       <term><parameter>fd</parameter></term>
+       <listitem>
+         <para>&fd;</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>request</parameter></term>
+       <listitem>
+         <para>VIDIOC_ENUM_FREQ_BANDS</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>argp</parameter></term>
+       <listitem>
+         <para></para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <note>
+      <title>Experimental</title>
+      <para>This is an <link linkend="experimental"> experimental </link>
+      interface and may change in the future.</para>
+    </note>
+
+    <para>Enumerates the frequency bands that a tuner or modulator supports.
+To do this applications initialize the <structfield>tuner</structfield>,
+<structfield>type</structfield> and <structfield>index</structfield> fields,
+and zero out the <structfield>reserved</structfield> array of a &v4l2-frequency-band; and
+call the <constant>VIDIOC_ENUM_FREQ_BANDS</constant> ioctl with a pointer
+to this structure.</para>
+
+    <para>This ioctl is supported if the <constant>V4L2_TUNER_CAP_FREQ_BANDS</constant> capability
+    of the corresponding tuner/modulator is set.</para>
+
+    <table pgwide="1" frame="none" id="v4l2-frequency-band">
+      <title>struct <structname>v4l2_frequency_band</structname></title>
+      <tgroup cols="3">
+       &cs-str;
+       <tbody valign="top">
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>tuner</structfield></entry>
+           <entry>The tuner or modulator index number. This is the
+same value as in the &v4l2-input; <structfield>tuner</structfield>
+field and the &v4l2-tuner; <structfield>index</structfield> field, or
+the &v4l2-output; <structfield>modulator</structfield> field and the
+&v4l2-modulator; <structfield>index</structfield> field.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>type</structfield></entry>
+           <entry>The tuner type. This is the same value as in the
+&v4l2-tuner; <structfield>type</structfield> field. The type must be set
+to <constant>V4L2_TUNER_RADIO</constant> for <filename>/dev/radioX</filename>
+device nodes, and to <constant>V4L2_TUNER_ANALOG_TV</constant>
+for all others. Set this field to <constant>V4L2_TUNER_RADIO</constant> for
+modulators (currently only radio modulators are supported).
+See <xref linkend="v4l2-tuner-type" /></entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>index</structfield></entry>
+           <entry>Identifies the frequency band, set by the application.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>capability</structfield></entry>
+           <entry spanname="hspan">The tuner/modulator capability flags for
+this frequency band, see <xref linkend="tuner-capability" />. The <constant>V4L2_TUNER_CAP_LOW</constant>
+capability must be the same for all frequency bands of the selected tuner/modulator.
+So either all bands have that capability set, or none of them have that capability.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>rangelow</structfield></entry>
+           <entry spanname="hspan">The lowest tunable frequency in
+units of 62.5 kHz, or if the <structfield>capability</structfield>
+flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
+Hz, for this frequency band.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>rangehigh</structfield></entry>
+           <entry spanname="hspan">The highest tunable frequency in
+units of 62.5 kHz, or if the <structfield>capability</structfield>
+flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
+Hz, for this frequency band.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>modulation</structfield></entry>
+           <entry spanname="hspan">The supported modulation systems of this frequency band.
+           See <xref linkend="band-modulation" />. Note that currently only one
+           modulation system per frequency band is supported. More work will need to
+           be done if multiple modulation systems are possible. Contact the
+           linux-media mailing list (&v4l-ml;) if you need that functionality.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>reserved</structfield>[9]</entry>
+           <entry>Reserved for future extensions. Applications and drivers
+           must set the array to zero.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="band-modulation">
+      <title>Band Modulation Systems</title>
+      <tgroup cols="3">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>V4L2_BAND_MODULATION_VSB</constant></entry>
+           <entry>0x02</entry>
+           <entry>Vestigial Sideband modulation, used for analog TV.</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_BAND_MODULATION_FM</constant></entry>
+           <entry>0x04</entry>
+           <entry>Frequency Modulation, commonly used for analog radio.</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_BAND_MODULATION_AM</constant></entry>
+           <entry>0x08</entry>
+           <entry>Amplitude Modulation, commonly used for analog radio.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+
+    <variablelist>
+      <varlistentry>
+       <term><errorcode>EINVAL</errorcode></term>
+       <listitem>
+         <para>The <structfield>tuner</structfield> or <structfield>index</structfield>
+is out of bounds or the <structfield>type</structfield> field is wrong.</para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+</refentry>
index 69c178a4d20546d5c54f767df0c32158f7e222df..c7a1c462e7243fe665dab4b5e4fbcb762765f874 100644 (file)
@@ -98,11 +98,12 @@ the &v4l2-output; <structfield>modulator</structfield> field and the
            <entry>__u32</entry>
            <entry><structfield>type</structfield></entry>
            <entry>The tuner type. This is the same value as in the
-&v4l2-tuner; <structfield>type</structfield> field. See The type must be set
+&v4l2-tuner; <structfield>type</structfield> field. The type must be set
 to <constant>V4L2_TUNER_RADIO</constant> for <filename>/dev/radioX</filename>
 device nodes, and to <constant>V4L2_TUNER_ANALOG_TV</constant>
-for all others. The field is not applicable to modulators, &ie; ignored
-by drivers. See <xref linkend="v4l2-tuner-type" /></entry>
+for all others. Set this field to <constant>V4L2_TUNER_RADIO</constant> for
+modulators (currently only radio modulators are supported).
+See <xref linkend="v4l2-tuner-type" /></entry>
          </row>
          <row>
            <entry>__u32</entry>
@@ -135,6 +136,12 @@ bounds or the value in the <structfield>type</structfield> field is
 wrong.</para>
        </listitem>
       </varlistentry>
+      <varlistentry>
+       <term><errorcode>EBUSY</errorcode></term>
+       <listitem>
+         <para>A hardware seek is in progress.</para>
+       </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 </refentry>
index bb04eff75f45440fa338d316911b7d8b7a79e75c..f76d8a6d9b92df2133babc08456ac3bfc4d9c171 100644 (file)
@@ -65,9 +65,9 @@ Do not use multiplanar buffers.  Use <constant> V4L2_BUF_TYPE_VIDEO_CAPTURE
 </constant>.  Use <constant> V4L2_BUF_TYPE_VIDEO_OUTPUT </constant> instead of
 <constant> V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE </constant>.  The next step is
 setting the value of &v4l2-selection; <structfield>target</structfield> field
-to <constant> V4L2_SEL_TGT_CROP_ACTIVE </constant> (<constant>
-V4L2_SEL_TGT_COMPOSE_ACTIVE </constant>).  Please refer to table <xref
-linkend="v4l2-sel-target" /> or <xref linkend="selection-api" /> for additional
+to <constant> V4L2_SEL_TGT_CROP </constant> (<constant>
+V4L2_SEL_TGT_COMPOSE </constant>).  Please refer to table <xref
+linkend="v4l2-selections-common" /> or <xref linkend="selection-api" /> for additional
 targets.  The <structfield>flags</structfield> and <structfield>reserved
 </structfield> fields of &v4l2-selection; are ignored and they must be filled
 with zeros.  The driver fills the rest of the structure or
@@ -86,9 +86,9 @@ use multiplanar buffers.  Use <constant> V4L2_BUF_TYPE_VIDEO_CAPTURE
 </constant>.  Use <constant> V4L2_BUF_TYPE_VIDEO_OUTPUT </constant> instead of
 <constant> V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE </constant>.  The next step is
 setting the value of &v4l2-selection; <structfield>target</structfield> to
-<constant>V4L2_SEL_TGT_CROP_ACTIVE</constant> (<constant>
-V4L2_SEL_TGT_COMPOSE_ACTIVE </constant>). Please refer to table <xref
-linkend="v4l2-sel-target" /> or <xref linkend="selection-api" /> for additional
+<constant>V4L2_SEL_TGT_CROP</constant> (<constant>
+V4L2_SEL_TGT_COMPOSE </constant>). Please refer to table <xref
+linkend="v4l2-selections-common" /> or <xref linkend="selection-api" /> for additional
 targets.  The &v4l2-rect; <structfield>r</structfield> rectangle need to be
 set to the desired active area. Field &v4l2-selection; <structfield> reserved
 </structfield> is ignored and must be filled with zeros.  The driver may adjust
@@ -154,74 +154,8 @@ exist no rectangle </emphasis> that satisfies the constraints.</para>
 
   </refsect1>
 
-  <refsect1>
-    <table frame="none" pgwide="1" id="v4l2-sel-target">
-      <title>Selection targets.</title>
-      <tgroup cols="3">
-       &cs-def;
-       <tbody valign="top">
-         <row>
-            <entry><constant>V4L2_SEL_TGT_CROP_ACTIVE</constant></entry>
-            <entry>0x0000</entry>
-            <entry>The area that is currently cropped by hardware.</entry>
-         </row>
-         <row>
-            <entry><constant>V4L2_SEL_TGT_CROP_DEFAULT</constant></entry>
-            <entry>0x0001</entry>
-            <entry>Suggested cropping rectangle that covers the "whole picture".</entry>
-         </row>
-         <row>
-            <entry><constant>V4L2_SEL_TGT_CROP_BOUNDS</constant></entry>
-            <entry>0x0002</entry>
-            <entry>Limits for the cropping rectangle.</entry>
-         </row>
-         <row>
-            <entry><constant>V4L2_SEL_TGT_COMPOSE_ACTIVE</constant></entry>
-            <entry>0x0100</entry>
-            <entry>The area to which data is composed by hardware.</entry>
-         </row>
-         <row>
-            <entry><constant>V4L2_SEL_TGT_COMPOSE_DEFAULT</constant></entry>
-            <entry>0x0101</entry>
-            <entry>Suggested composing rectangle that covers the "whole picture".</entry>
-         </row>
-         <row>
-            <entry><constant>V4L2_SEL_TGT_COMPOSE_BOUNDS</constant></entry>
-            <entry>0x0102</entry>
-            <entry>Limits for the composing rectangle.</entry>
-         </row>
-         <row>
-            <entry><constant>V4L2_SEL_TGT_COMPOSE_PADDED</constant></entry>
-            <entry>0x0103</entry>
-            <entry>The active area and all padding pixels that are inserted or modified by hardware.</entry>
-         </row>
-       </tbody>
-      </tgroup>
-    </table>
-  </refsect1>
-
-  <refsect1>
-    <table frame="none" pgwide="1" id="v4l2-sel-flags">
-      <title>Selection constraint flags</title>
-      <tgroup cols="3">
-       &cs-def;
-       <tbody valign="top">
-         <row>
-            <entry><constant>V4L2_SEL_FLAG_GE</constant></entry>
-            <entry>0x00000001</entry>
-            <entry>Indicates that the adjusted rectangle must contain the original
-           &v4l2-selection; <structfield>r</structfield> rectangle.</entry>
-         </row>
-         <row>
-            <entry><constant>V4L2_SEL_FLAG_LE</constant></entry>
-            <entry>0x00000002</entry>
-            <entry>Indicates that the adjusted rectangle must be inside the original
-           &v4l2-rect; <structfield>r</structfield> rectangle.</entry>
-         </row>
-       </tbody>
-      </tgroup>
-    </table>
-  </refsect1>
+  <para>Selection targets and flags are documented in <xref
+  linkend="v4l2-selections-common"/>.</para>
 
     <section>
       <figure id="sel-const-adjust">
@@ -252,14 +186,14 @@ exist no rectangle </emphasis> that satisfies the constraints.</para>
          <row>
            <entry>__u32</entry>
            <entry><structfield>target</structfield></entry>
-            <entry>Used to select between <link linkend="v4l2-sel-target"> cropping
+            <entry>Used to select between <link linkend="v4l2-selections-common"> cropping
            and composing rectangles</link>.</entry>
          </row>
          <row>
            <entry>__u32</entry>
            <entry><structfield>flags</structfield></entry>
             <entry>Flags controlling the selection rectangle adjustments, refer to
-           <link linkend="v4l2-sel-flags">selection flags</link>.</entry>
+           <link linkend="v4l2-selection-flags">selection flags</link>.</entry>
          </row>
          <row>
            <entry>&v4l2-rect;</entry>
index 62a1aa200a36ccfd5dd59fe132de8ddfb57e2724..720395127904574b69db6dd2b042f46b3d14901b 100644 (file)
@@ -119,10 +119,14 @@ field is not quite clear.--></para></entry>
 <xref linkend="tuner-capability" />. Audio flags indicate the ability
 to decode audio subprograms. They will <emphasis>not</emphasis>
 change, for example with the current video standard.</para><para>When
-the structure refers to a radio tuner only the
-<constant>V4L2_TUNER_CAP_LOW</constant>,
-<constant>V4L2_TUNER_CAP_STEREO</constant> and
-<constant>V4L2_TUNER_CAP_RDS</constant> flags can be set.</para></entry>
+the structure refers to a radio tuner the
+<constant>V4L2_TUNER_CAP_LANG1</constant>,
+<constant>V4L2_TUNER_CAP_LANG2</constant> and
+<constant>V4L2_TUNER_CAP_NORM</constant> flags can't be used.</para>
+<para>If multiple frequency bands are supported, then
+<structfield>capability</structfield> is the union of all
+<structfield>capability></structfield> fields of each &v4l2-frequency-band;.
+</para></entry>
          </row>
          <row>
            <entry>__u32</entry>
@@ -130,7 +134,9 @@ the structure refers to a radio tuner only the
            <entry spanname="hspan">The lowest tunable frequency in
 units of 62.5 kHz, or if the <structfield>capability</structfield>
 flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
-Hz.</entry>
+Hz. If multiple frequency bands are supported, then
+<structfield>rangelow</structfield> is the lowest frequency
+of all the frequency bands.</entry>
          </row>
          <row>
            <entry>__u32</entry>
@@ -138,7 +144,9 @@ Hz.</entry>
            <entry spanname="hspan">The highest tunable frequency in
 units of 62.5 kHz, or if the <structfield>capability</structfield>
 flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
-Hz.</entry>
+Hz. If multiple frequency bands are supported, then
+<structfield>rangehigh</structfield> is the highest frequency
+of all the frequency bands.</entry>
          </row>
          <row>
            <entry>__u32</entry>
@@ -275,6 +283,18 @@ can or must be switched. (B/G PAL tuners for example are typically not
       see the description of ioctl &VIDIOC-ENUMINPUT; for details. Only
       <constant>V4L2_TUNER_ANALOG_TV</constant> tuners can have this capability.</entry>
          </row>
+         <row>
+           <entry><constant>V4L2_TUNER_CAP_HWSEEK_BOUNDED</constant></entry>
+           <entry>0x0004</entry>
+           <entry>If set, then this tuner supports the hardware seek functionality
+           where the seek stops when it reaches the end of the frequency range.</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_TUNER_CAP_HWSEEK_WRAP</constant></entry>
+           <entry>0x0008</entry>
+           <entry>If set, then this tuner supports the hardware seek functionality
+           where the seek wraps around when it reaches the end of the frequency range.</entry>
+         </row>
          <row>
        <entry><constant>V4L2_TUNER_CAP_STEREO</constant></entry>
        <entry>0x0010</entry>
@@ -328,6 +348,12 @@ radio tuners.</entry>
        <entry>0x0200</entry>
        <entry>The RDS data is parsed by the hardware and set via controls.</entry>
          </row>
+         <row>
+       <entry><constant>V4L2_TUNER_CAP_FREQ_BANDS</constant></entry>
+       <entry>0x0400</entry>
+       <entry>The &VIDIOC-ENUM-FREQ-BANDS; ioctl can be used to enumerate
+       the available frequency bands.</entry>
+         </row>
        </tbody>
       </tgroup>
     </table>
index 9caa49af580fe2a2ae5607a30f1a5d2141ff072a..77ff5be0809d13ef530ff07718e243aab98358fc 100644 (file)
@@ -71,12 +71,9 @@ initialize the <structfield>bytesused</structfield>,
 <structfield>field</structfield> and
 <structfield>timestamp</structfield> fields, see <xref
 linkend="buffer" /> for details.
-Applications must also set <structfield>flags</structfield> to 0. If a driver
-supports capturing from specific video inputs and you want to specify a video
-input, then <structfield>flags</structfield> should be set to
-<constant>V4L2_BUF_FLAG_INPUT</constant> and the field
-<structfield>input</structfield> must be initialized to the desired input.
-The <structfield>reserved</structfield> field must be set to 0. When using
+Applications must also set <structfield>flags</structfield> to 0.
+The <structfield>reserved2</structfield> and
+<structfield>reserved</structfield> fields must be set to 0. When using
 the <link linkend="planar-apis">multi-planar API</link>, the
 <structfield>m.planes</structfield> field must contain a userspace pointer
 to a filled-in array of &v4l2-plane; and the <structfield>length</structfield>
index 4643505cd4ca575df1d6d584c03159fda60349f2..f33dd746b66b8177b60f5a431aefc937973e5eea 100644 (file)
@@ -191,6 +191,19 @@ linkend="output">Video Output</link> interface.</entry>
            <link linkend="planar-apis">multi-planar API</link> through the
            <link linkend="output">Video Output</link> interface.</entry>
          </row>
+         <row>
+           <entry><constant>V4L2_CAP_VIDEO_M2M</constant></entry>
+           <entry>0x00004000</entry>
+           <entry>The device supports the single-planar API through the
+           Video Memory-To-Memory interface.</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_CAP_VIDEO_M2M_MPLANE</constant></entry>
+           <entry>0x00008000</entry>
+           <entry>The device supports the
+           <link linkend="planar-apis">multi-planar API</link> through the
+           Video Memory-To-Memory  interface.</entry>
+         </row>
          <row>
            <entry><constant>V4L2_CAP_VIDEO_OVERLAY</constant></entry>
            <entry>0x00000004</entry>
index 407dfceb71f0c29fcc8cfd7d2a29cabc2e305ba1..3dd1bec6d3c74c1a6619364aaae41d85d615d17f 100644 (file)
     <para>Start a hardware frequency seek from the current frequency.
 To do this applications initialize the <structfield>tuner</structfield>,
 <structfield>type</structfield>, <structfield>seek_upward</structfield>,
-<structfield>spacing</structfield> and
-<structfield>wrap_around</structfield> fields, and zero out the
-<structfield>reserved</structfield> array of a &v4l2-hw-freq-seek; and
-call the <constant>VIDIOC_S_HW_FREQ_SEEK</constant> ioctl with a pointer
-to this structure.</para>
+<structfield>wrap_around</structfield>, <structfield>spacing</structfield>,
+<structfield>rangelow</structfield> and <structfield>rangehigh</structfield>
+fields, and zero out the <structfield>reserved</structfield> array of a
+&v4l2-hw-freq-seek; and call the <constant>VIDIOC_S_HW_FREQ_SEEK</constant>
+ioctl with a pointer to this structure.</para>
+
+    <para>The <structfield>rangelow</structfield> and
+<structfield>rangehigh</structfield> fields can be set to a non-zero value to
+tell the driver to search a specific band. If the &v4l2-tuner;
+<structfield>capability</structfield> field has the
+<constant>V4L2_TUNER_CAP_HWSEEK_PROG_LIM</constant> flag set, these values
+must fall within one of the bands returned by &VIDIOC-ENUM-FREQ-BANDS;. If
+the <constant>V4L2_TUNER_CAP_HWSEEK_PROG_LIM</constant> flag is not set,
+then these values must exactly match those of one of the bands returned by
+&VIDIOC-ENUM-FREQ-BANDS;. If the current frequency of the tuner does not fall
+within the selected band it will be clamped to fit in the band before the
+seek is started.</para>
+
+    <para>If an error is returned, then the original frequency will
+    be restored.</para>
 
     <para>This ioctl is supported if the <constant>V4L2_CAP_HW_FREQ_SEEK</constant> capability is set.</para>
 
@@ -87,7 +102,10 @@ field and the &v4l2-tuner; <structfield>index</structfield> field.</entry>
          <row>
            <entry>__u32</entry>
            <entry><structfield>wrap_around</structfield></entry>
-           <entry>If non-zero, wrap around when at the end of the frequency range, else stop seeking.</entry>
+           <entry>If non-zero, wrap around when at the end of the frequency range, else stop seeking.
+           The &v4l2-tuner; <structfield>capability</structfield> field will tell you what the
+           hardware supports.
+           </entry>
          </row>
          <row>
            <entry>__u32</entry>
@@ -96,7 +114,27 @@ field and the &v4l2-tuner; <structfield>index</structfield> field.</entry>
          </row>
          <row>
            <entry>__u32</entry>
-           <entry><structfield>reserved</structfield>[7]</entry>
+           <entry><structfield>rangelow</structfield></entry>
+           <entry>If non-zero, the lowest tunable frequency of the band to
+search in units of 62.5 kHz, or if the &v4l2-tuner;
+<structfield>capability</structfield> field has the
+<constant>V4L2_TUNER_CAP_LOW</constant> flag set, in units of 62.5 Hz.
+If <structfield>rangelow</structfield> is zero a reasonable default value
+is used.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>rangehigh</structfield></entry>
+           <entry>If non-zero, the highest tunable frequency of the band to
+search in units of 62.5 kHz, or if the &v4l2-tuner;
+<structfield>capability</structfield> field has the
+<constant>V4L2_TUNER_CAP_LOW</constant> flag set, in units of 62.5 Hz.
+If <structfield>rangehigh</structfield> is zero a reasonable default value
+is used.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>reserved</structfield>[5]</entry>
            <entry>Reserved for future extensions. Applications
            must set the array to zero.</entry>
          </row>
@@ -113,14 +151,22 @@ field and the &v4l2-tuner; <structfield>index</structfield> field.</entry>
        <term><errorcode>EINVAL</errorcode></term>
        <listitem>
          <para>The <structfield>tuner</structfield> index is out of
-bounds, the wrap_around value is not supported or the value in the <structfield>type</structfield> field is
-wrong.</para>
+bounds, the <structfield>wrap_around</structfield> value is not supported or
+one of the values in the <structfield>type</structfield>,
+<structfield>rangelow</structfield> or <structfield>rangehigh</structfield>
+fields is wrong.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><errorcode>ENODATA</errorcode></term>
+       <listitem>
+         <para>The hardware seek found no channels.</para>
        </listitem>
       </varlistentry>
       <varlistentry>
-       <term><errorcode>EAGAIN</errorcode></term>
+       <term><errorcode>EBUSY</errorcode></term>
        <listitem>
-         <para>The ioctl timed-out. Try again.</para>
+         <para>Another hardware seek is already in progress.</para>
        </listitem>
       </varlistentry>
     </variablelist>
index 208e9f0da3f30440e67d0c744b5ab92bdd51f582..f33cc814a01d14d07483a04051f4513ace88876c 100644 (file)
     <section>
       <title>Types of selection targets</title>
 
-      <para>There are two types of selection targets: actual and bounds.
-      The ACTUAL targets are the targets which configure the hardware.
-      The BOUNDS target will return a rectangle that contain all
-      possible ACTUAL rectangles.</para>
+      <para>There are two types of selection targets: actual and bounds. The
+      actual targets are the targets which configure the hardware. The BOUNDS
+      target will return a rectangle that contain all possible actual
+      rectangles.</para>
     </section>
 
     <section>
       <constant>EINVAL</constant>.</para>
     </section>
 
-    <table pgwide="1" frame="none" id="v4l2-subdev-selection-targets">
-      <title>V4L2 subdev selection targets</title>
-      <tgroup cols="3">
-        &cs-def;
-       <tbody valign="top">
-         <row>
-           <entry><constant>V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL</constant></entry>
-           <entry>0x0000</entry>
-           <entry>Actual crop. Defines the cropping
-           performed by the processing step.</entry>
-         </row>
-         <row>
-           <entry><constant>V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS</constant></entry>
-           <entry>0x0002</entry>
-           <entry>Bounds of the crop rectangle.</entry>
-         </row>
-         <row>
-           <entry><constant>V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL</constant></entry>
-           <entry>0x0100</entry>
-           <entry>Actual compose rectangle. Used to configure scaling
-           on sink pads and composition on source pads.</entry>
-         </row>
-         <row>
-           <entry><constant>V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS</constant></entry>
-           <entry>0x0102</entry>
-           <entry>Bounds of the compose rectangle.</entry>
-         </row>
-       </tbody>
-      </tgroup>
-    </table>
-
-    <table pgwide="1" frame="none" id="v4l2-subdev-selection-flags">
-      <title>V4L2 subdev selection flags</title>
-      <tgroup cols="3">
-        &cs-def;
-       <tbody valign="top">
-         <row>
-           <entry><constant>V4L2_SUBDEV_SEL_FLAG_SIZE_GE</constant></entry>
-           <entry>(1 &lt;&lt; 0)</entry> <entry>Suggest the driver it
-           should choose greater or equal rectangle (in size) than
-           was requested. Albeit the driver may choose a lesser size,
-           it will only do so due to hardware limitations. Without
-           this flag (and
-           <constant>V4L2_SUBDEV_SEL_FLAG_SIZE_LE</constant>) the
-           behaviour is to choose the closest possible
-           rectangle.</entry>
-         </row>
-         <row>
-           <entry><constant>V4L2_SUBDEV_SEL_FLAG_SIZE_LE</constant></entry>
-           <entry>(1 &lt;&lt; 1)</entry> <entry>Suggest the driver it
-           should choose lesser or equal rectangle (in size) than was
-           requested. Albeit the driver may choose a greater size, it
-           will only do so due to hardware limitations.</entry>
-         </row>
-         <row>
-           <entry><constant>V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG</constant></entry>
-           <entry>(1 &lt;&lt; 2)</entry>
-           <entry>The configuration should not be propagated to any
-           further processing steps. If this flag is not given, the
-           configuration is propagated inside the subdevice to all
-           further processing steps.</entry>
-         </row>
-       </tbody>
-      </tgroup>
-    </table>
+    <para>Selection targets and flags are documented in <xref
+    linkend="v4l2-selections-common"/>.</para>
 
     <table pgwide="1" frame="none" id="v4l2-subdev-selection">
       <title>struct <structname>v4l2_subdev_selection</structname></title>
            <entry>__u32</entry>
            <entry><structfield>target</structfield></entry>
            <entry>Target selection rectangle. See
-           <xref linkend="v4l2-subdev-selection-targets">.</xref>.</entry>
+           <xref linkend="v4l2-selections-common" />.</entry>
          </row>
          <row>
            <entry>__u32</entry>
            <entry><structfield>flags</structfield></entry>
            <entry>Flags. See
-           <xref linkend="v4l2-subdev-selection-flags">.</xref></entry>
+           <xref linkend="v4l2-selection-flags" />.</entry>
          </row>
          <row>
            <entry>&v4l2-rect;</entry>
index 27dcaabfb4db2f186bb6df8c5fb016a049c2d2d4..1401cece745ae46ce74fcb8c6a319aa05be7a318 100644 (file)
@@ -93,6 +93,7 @@ Linux IRQ number into the hardware.
 Most drivers cannot use this mapping.
 
 ==== Legacy ====
+irq_domain_add_simple()
 irq_domain_add_legacy()
 irq_domain_add_legacy_isa()
 
@@ -115,3 +116,7 @@ The legacy map should only be used if fixed IRQ mappings must be
 supported.  For example, ISA controllers would use the legacy map for
 mapping Linux IRQs 0-15 so that existing ISA drivers get the correct IRQ
 numbers.
+
+Most users of legacy mappings should use irq_domain_add_simple() which
+will use a legacy domain only if an IRQ range is supplied by the
+system and will otherwise use a linear domain mapping.
index d8147b336c354e203addd40bb85bcc0abbeeded6..6518a55273e7094f62f84a5d83467fd96b26fd26 100644 (file)
@@ -38,6 +38,13 @@ read or write requests. Note that the total allocated number may be twice
 this amount, since it applies only to reads or writes (not the accumulated
 sum).
 
+To avoid priority inversion through request starvation, a request
+queue maintains a separate request pool per each cgroup when
+CONFIG_BLK_CGROUP is enabled, and this parameter applies to each such
+per-block-cgroup request pool.  IOW, if there are N block cgroups,
+each request queue may have upto N request pools, each independently
+regulated by nr_requests.
+
 read_ahead_kb (RW)
 ------------------
 Maximum number of kilobytes to read-ahead for filesystems on this block
diff --git a/Documentation/cgroups/hugetlb.txt b/Documentation/cgroups/hugetlb.txt
new file mode 100644 (file)
index 0000000..a9faaca
--- /dev/null
@@ -0,0 +1,45 @@
+HugeTLB Controller
+-------------------
+
+The HugeTLB controller allows to limit the HugeTLB usage per control group and
+enforces the controller limit during page fault. Since HugeTLB doesn't
+support page reclaim, enforcing the limit at page fault time implies that,
+the application will get SIGBUS signal if it tries to access HugeTLB pages
+beyond its limit. This requires the application to know beforehand how much
+HugeTLB pages it would require for its use.
+
+HugeTLB controller can be created by first mounting the cgroup filesystem.
+
+# mount -t cgroup -o hugetlb none /sys/fs/cgroup
+
+With the above step, the initial or the parent HugeTLB group becomes
+visible at /sys/fs/cgroup. At bootup, this group includes all the tasks in
+the system. /sys/fs/cgroup/tasks lists the tasks in this cgroup.
+
+New groups can be created under the parent group /sys/fs/cgroup.
+
+# cd /sys/fs/cgroup
+# mkdir g1
+# echo $$ > g1/tasks
+
+The above steps create a new group g1 and move the current shell
+process (bash) into it.
+
+Brief summary of control files
+
+ hugetlb.<hugepagesize>.limit_in_bytes     # set/show limit of "hugepagesize" hugetlb usage
+ hugetlb.<hugepagesize>.max_usage_in_bytes # show max "hugepagesize" hugetlb  usage recorded
+ hugetlb.<hugepagesize>.usage_in_bytes     # show current res_counter usage for "hugepagesize" hugetlb
+ hugetlb.<hugepagesize>.failcnt                   # show the number of allocation failure due to HugeTLB limit
+
+For a system supporting two hugepage size (16M and 16G) the control
+files include:
+
+hugetlb.16GB.limit_in_bytes
+hugetlb.16GB.max_usage_in_bytes
+hugetlb.16GB.usage_in_bytes
+hugetlb.16GB.failcnt
+hugetlb.16MB.limit_in_bytes
+hugetlb.16MB.max_usage_in_bytes
+hugetlb.16MB.usage_in_bytes
+hugetlb.16MB.failcnt
index dd88540bb995e88dab868e1f42cbd218b888d7bd..4372e6b8a353f3dce002ac32b84b6dd3b53a8e31 100644 (file)
@@ -73,6 +73,8 @@ Brief summary of control files.
 
  memory.kmem.tcp.limit_in_bytes  # set/show hard limit for tcp buf memory
  memory.kmem.tcp.usage_in_bytes  # show current tcp buf memory allocation
+ memory.kmem.tcp.failcnt            # show the number of tcp buf memory usage hits limits
+ memory.kmem.tcp.max_usage_in_bytes # show max tcp buf memory usage recorded
 
 1. History
 
@@ -187,12 +189,12 @@ the cgroup that brought it in -- this will happen on memory pressure).
 But see section 8.2: when moving a task to another cgroup, its pages may
 be recharged to the new cgroup, if move_charge_at_immigrate has been chosen.
 
-Exception: If CONFIG_CGROUP_CGROUP_MEM_RES_CTLR_SWAP is not used.
+Exception: If CONFIG_CGROUP_CGROUP_MEMCG_SWAP is not used.
 When you do swapoff and make swapped-out pages of shmem(tmpfs) to
 be backed into memory in force, charges for pages are accounted against the
 caller of swapoff rather than the users of shmem.
 
-2.4 Swap Extension (CONFIG_CGROUP_MEM_RES_CTLR_SWAP)
+2.4 Swap Extension (CONFIG_MEMCG_SWAP)
 
 Swap Extension allows you to record charge for swap. A swapped-in page is
 charged back to original page allocator if possible.
@@ -259,7 +261,7 @@ When oom event notifier is registered, event will be delivered.
   per-zone-per-cgroup LRU (cgroup's private LRU) is just guarded by
   zone->lru_lock, it has no lock of its own.
 
-2.7 Kernel Memory Extension (CONFIG_CGROUP_MEM_RES_CTLR_KMEM)
+2.7 Kernel Memory Extension (CONFIG_MEMCG_KMEM)
 
 With the Kernel memory extension, the Memory Controller is able to limit
 the amount of kernel memory used by the system. Kernel memory is fundamentally
@@ -286,8 +288,8 @@ per cgroup, instead of globally.
 
 a. Enable CONFIG_CGROUPS
 b. Enable CONFIG_RESOURCE_COUNTERS
-c. Enable CONFIG_CGROUP_MEM_RES_CTLR
-d. Enable CONFIG_CGROUP_MEM_RES_CTLR_SWAP (to use swap extension)
+c. Enable CONFIG_MEMCG
+d. Enable CONFIG_MEMCG_SWAP (to use swap extension)
 
 1. Prepare the cgroups (see cgroups.txt, Why are cgroups needed?)
 # mount -t tmpfs none /sys/fs/cgroup
index 946c73342cdea1c4afb23cb889828856a5e6308d..1c184495716697610d4bb41e1dde3ab60dce9e51 100644 (file)
@@ -27,6 +27,10 @@ The target is named "raid" and it accepts the following parameters:
                - rotating parity N (right-to-left) with data restart
   raid6_nc     RAID6 N continue
                - rotating parity N (right-to-left) with data continuation
+  raid10        Various RAID10 inspired algorithms chosen by additional params
+               - RAID10: Striped Mirrors (aka 'Striping on top of mirrors')
+               - RAID1E: Integrated Adjacent Stripe Mirroring
+               -  and other similar RAID10 variants
 
   Reference: Chapter 4 of
   http://www.snia.org/sites/default/files/SNIA_DDF_Technical_Position_v2.0.pdf
@@ -59,6 +63,28 @@ The target is named "raid" and it accepts the following parameters:
                logical size of the array.  The bitmap records the device
                synchronisation state for each region.
 
+        [raid10_copies   <# copies>]
+        [raid10_format   near]
+               These two options are used to alter the default layout of
+               a RAID10 configuration.  The number of copies is can be
+               specified, but the default is 2.  There are other variations
+               to how the copies are laid down - the default and only current
+               option is "near".  Near copies are what most people think of
+               with respect to mirroring.  If these options are left
+               unspecified, or 'raid10_copies 2' and/or 'raid10_format near'
+               are given, then the layouts for 2, 3 and 4 devices are:
+               2 drives         3 drives          4 drives
+               --------         ----------        --------------
+               A1  A1           A1  A1  A2        A1  A1  A2  A2
+               A2  A2           A2  A3  A3        A3  A3  A4  A4
+               A3  A3           A4  A4  A5        A5  A5  A6  A6
+               A4  A4           A5  A6  A6        A7  A7  A8  A8
+               ..  ..           ..  ..  ..        ..  ..  ..  ..
+               The 2-device layout is equivalent 2-way RAID1.  The 4-device
+               layout is what a traditional RAID10 would look like.  The
+               3-device layout is what might be called a 'RAID1E - Integrated
+               Adjacent Stripe Mirroring'.
+
 <#raid_devs>: The number of devices composing the array.
        Each device consists of two entries.  The first is the device
        containing the metadata (if any); the second is the one containing the
index f34d3236b9da838e0a3e81516e135b914f5577ee..45f3b91ea4c3259ba19388b40935d60f276ff6ec 100644 (file)
@@ -9,15 +9,14 @@ devices in parallel.
 
 Parameters: <num devs> <chunk size> [<dev path> <offset>]+
     <num devs>: Number of underlying devices.
-    <chunk size>: Size of each chunk of data. Must be a power-of-2 and at
-                  least as large as the system's PAGE_SIZE.
+    <chunk size>: Size of each chunk of data. Must be at least as
+                  large as the system's PAGE_SIZE.
     <dev path>: Full pathname to the underlying block-device, or a
                 "major:minor" device-number.
     <offset>: Starting sector within the device.
 
 One or more underlying devices can be specified. The striped device size must
-be a multiple of the chunk size and a multiple of the number of underlying
-devices.
+be a multiple of the chunk size multiplied by the number of underlying devices.
 
 
 Example scripts
index f5cfc62b7ad3fa2bfbb50fb3f33339e1d402a575..30b8b83bd333401a2cc1138d664d6086b4d47aef 100644 (file)
@@ -231,6 +231,9 @@ i) Constructor
       no_discard_passdown: Don't pass discards down to the underlying
                           data device, but just remove the mapping.
 
+      read_only: Don't allow any changes to be made to the pool
+                metadata.
+
     Data block size must be between 64KB (128 sectors) and 1GB
     (2097152 sectors) inclusive.
 
@@ -239,7 +242,7 @@ ii) Status
 
     <transaction id> <used metadata blocks>/<total metadata blocks>
     <used data blocks>/<total data blocks> <held metadata root>
-
+    [no_]discard_passdown ro|rw
 
     transaction id:
        A 64-bit number used by userspace to help synchronise with metadata
@@ -257,6 +260,21 @@ ii) Status
        held root.  This feature is not yet implemented so '-' is
        always returned.
 
+    discard_passdown|no_discard_passdown
+       Whether or not discards are actually being passed down to the
+       underlying device.  When this is enabled when loading the table,
+       it can get disabled if the underlying device doesn't support it.
+
+    ro|rw
+       If the pool encounters certain types of device failures it will
+       drop into a read-only metadata mode in which no changes to
+       the pool metadata (like allocating new blocks) are permitted.
+
+       In serious cases where even a read-only mode is deemed unsafe
+       no further I/O will be permitted and the status will just
+       contain the string 'Fail'.  The userspace recovery tools
+       should then be used.
+
 iii) Messages
 
     create_thin <dev id>
@@ -329,3 +347,7 @@ regain some space then send the 'trim' message to the pool.
 ii) Status
 
      <nr mapped sectors> <highest mapped sector>
+
+       If the pool has encountered device errors and failed, the status
+       will just contain the string 'Fail'.  The userspace recovery
+       tools should then be used.
diff --git a/Documentation/devicetree/bindings/arm/calxeda/l2ecc.txt b/Documentation/devicetree/bindings/arm/calxeda/l2ecc.txt
new file mode 100644 (file)
index 0000000..94e642a
--- /dev/null
@@ -0,0 +1,15 @@
+Calxeda Highbank L2 cache ECC
+
+Properties:
+- compatible : Should be "calxeda,hb-sregs-l2-ecc"
+- reg : Address and size for ECC error interrupt clear registers.
+- interrupts : Should be single bit error interrupt, then double bit error
+       interrupt.
+
+Example:
+
+       sregs@fff3c200 {
+               compatible = "calxeda,hb-sregs-l2-ecc";
+               reg = <0xfff3c200 0x100>;
+               interrupts = <0 71 4  0 72 4>;
+       };
diff --git a/Documentation/devicetree/bindings/arm/calxeda/mem-ctrlr.txt b/Documentation/devicetree/bindings/arm/calxeda/mem-ctrlr.txt
new file mode 100644 (file)
index 0000000..f770ac0
--- /dev/null
@@ -0,0 +1,14 @@
+Calxeda DDR memory controller
+
+Properties:
+- compatible : Should be "calxeda,hb-ddr-ctrl"
+- reg : Address and size for DDR controller registers.
+- interrupts : Interrupt for DDR controller.
+
+Example:
+
+       memory-controller@fff00000 {
+               compatible = "calxeda,hb-ddr-ctrl";
+               reg = <0xfff00000 0x1000>;
+               interrupts = <0 91 4>;
+       };
diff --git a/Documentation/devicetree/bindings/ata/cavium-compact-flash.txt b/Documentation/devicetree/bindings/ata/cavium-compact-flash.txt
new file mode 100644 (file)
index 0000000..93986a5
--- /dev/null
@@ -0,0 +1,30 @@
+* Compact Flash
+
+The Cavium Compact Flash device is connected to the Octeon Boot Bus,
+and is thus a child of the Boot Bus device.  It can read and write
+industry standard compact flash devices.
+
+Properties:
+- compatible: "cavium,ebt3000-compact-flash";
+
+  Compatibility with many Cavium evaluation boards.
+
+- reg: The base address of the the CF chip select banks.  Depending on
+  the device configuration, there may be one or two banks.
+
+- cavium,bus-width: The width of the connection to the CF devices.  Valid
+  values are 8 and 16.
+
+- cavium,true-ide: Optional, if present the CF connection is in True IDE mode.
+
+- cavium,dma-engine-handle: Optional, a phandle for the DMA Engine connected
+  to this device.
+
+Example:
+       compact-flash@5,0 {
+               compatible = "cavium,ebt3000-compact-flash";
+               reg = <5 0 0x10000>, <6 0 0x10000>;
+               cavium,bus-width = <16>;
+               cavium,true-ide;
+               cavium,dma-engine-handle = <&dma0>;
+       };
diff --git a/Documentation/devicetree/bindings/gpio/cavium-octeon-gpio.txt b/Documentation/devicetree/bindings/gpio/cavium-octeon-gpio.txt
new file mode 100644 (file)
index 0000000..9d6dcd3
--- /dev/null
@@ -0,0 +1,49 @@
+* General Purpose Input Output (GPIO) bus.
+
+Properties:
+- compatible: "cavium,octeon-3860-gpio"
+
+  Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs.
+
+- reg: The base address of the GPIO unit's register bank.
+
+- gpio-controller: This is a GPIO controller.
+
+- #gpio-cells: Must be <2>.  The first cell is the GPIO pin.
+
+- interrupt-controller: The GPIO controller is also an interrupt
+  controller, many of its pins may be configured as an interrupt
+  source.
+
+- #interrupt-cells: Must be <2>.  The first cell is the GPIO pin
+   connected to the interrupt source.  The second cell is the interrupt
+   triggering protocol and may have one of four values:
+   1 - edge triggered on the rising edge.
+   2 - edge triggered on the falling edge
+   4 - level triggered active high.
+   8 - level triggered active low.
+
+- interrupts: Interrupt routing for each pin.
+
+Example:
+
+       gpio-controller@1070000000800 {
+               #gpio-cells = <2>;
+               compatible = "cavium,octeon-3860-gpio";
+               reg = <0x10700 0x00000800 0x0 0x100>;
+               gpio-controller;
+               /* Interrupts are specified by two parts:
+                * 1) GPIO pin number (0..15)
+                * 2) Triggering (1 - edge rising
+                *                2 - edge falling
+                *                4 - level active high
+                *                8 - level active low)
+                */
+               interrupt-controller;
+               #interrupt-cells = <2>;
+               /* The GPIO pin connect to 16 consecutive CUI bits */
+               interrupts = <0 16>, <0 17>, <0 18>, <0 19>,
+                            <0 20>, <0 21>, <0 22>, <0 23>,
+                            <0 24>, <0 25>, <0 26>, <0 27>,
+                            <0 28>, <0 29>, <0 30>, <0 31>;
+       };
diff --git a/Documentation/devicetree/bindings/gpio/gpio_i2c.txt b/Documentation/devicetree/bindings/gpio/gpio_i2c.txt
deleted file mode 100644 (file)
index 4f8ec94..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-Device-Tree bindings for i2c gpio driver
-
-Required properties:
-       - compatible = "i2c-gpio";
-       - gpios: sda and scl gpio
-
-
-Optional properties:
-       - i2c-gpio,sda-open-drain: sda as open drain
-       - i2c-gpio,scl-open-drain: scl as open drain
-       - i2c-gpio,scl-output-only: scl as output only
-       - i2c-gpio,delay-us: delay between GPIO operations (may depend on each platform)
-       - i2c-gpio,timeout-ms: timeout to get data
-
-Example nodes:
-
-i2c@0 {
-       compatible = "i2c-gpio";
-       gpios = <&pioA 23 0 /* sda */
-                &pioA 24 0 /* scl */
-               >;
-       i2c-gpio,sda-open-drain;
-       i2c-gpio,scl-open-drain;
-       i2c-gpio,delay-us = <2>;        /* ~100 kHz */
-       #address-cells = <1>;
-       #size-cells = <0>;
-
-       rv3029c2@56 {
-               compatible = "rv3029c2";
-               reg = <0x56>;
-       };
-};
diff --git a/Documentation/devicetree/bindings/i2c/cavium-i2c.txt b/Documentation/devicetree/bindings/i2c/cavium-i2c.txt
new file mode 100644 (file)
index 0000000..dced82e
--- /dev/null
@@ -0,0 +1,34 @@
+* Two Wire Serial Interface (TWSI) / I2C
+
+- compatible: "cavium,octeon-3860-twsi"
+
+  Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs.
+
+- reg: The base address of the TWSI/I2C bus controller register bank.
+
+- #address-cells: Must be <1>.
+
+- #size-cells: Must be <0>.  I2C addresses have no size component.
+
+- interrupts: A single interrupt specifier.
+
+- clock-frequency: The I2C bus clock rate in Hz.
+
+Example:
+       twsi0: i2c@1180000001000 {
+               #address-cells = <1>;
+               #size-cells = <0>;
+               compatible = "cavium,octeon-3860-twsi";
+               reg = <0x11800 0x00001000 0x0 0x200>;
+               interrupts = <0 45>;
+               clock-frequency = <100000>;
+
+               rtc@68 {
+                       compatible = "dallas,ds1337";
+                       reg = <0x68>;
+               };
+               tmp@4c {
+                       compatible = "ti,tmp421";
+                       reg = <0x4c>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/i2c/gpio-i2c.txt b/Documentation/devicetree/bindings/i2c/gpio-i2c.txt
new file mode 100644 (file)
index 0000000..4f8ec94
--- /dev/null
@@ -0,0 +1,32 @@
+Device-Tree bindings for i2c gpio driver
+
+Required properties:
+       - compatible = "i2c-gpio";
+       - gpios: sda and scl gpio
+
+
+Optional properties:
+       - i2c-gpio,sda-open-drain: sda as open drain
+       - i2c-gpio,scl-open-drain: scl as open drain
+       - i2c-gpio,scl-output-only: scl as output only
+       - i2c-gpio,delay-us: delay between GPIO operations (may depend on each platform)
+       - i2c-gpio,timeout-ms: timeout to get data
+
+Example nodes:
+
+i2c@0 {
+       compatible = "i2c-gpio";
+       gpios = <&pioA 23 0 /* sda */
+                &pioA 24 0 /* scl */
+               >;
+       i2c-gpio,sda-open-drain;
+       i2c-gpio,scl-open-drain;
+       i2c-gpio,delay-us = <2>;        /* ~100 kHz */
+       #address-cells = <1>;
+       #size-cells = <0>;
+
+       rv3029c2@56 {
+               compatible = "rv3029c2";
+               reg = <0x56>;
+       };
+};
index 1bfc02de1b0cc9900827be41386429638c0548d7..30ac3a0557f7d8c99a3660cbaf6ef2d718f3f3a9 100644 (file)
@@ -4,6 +4,8 @@ Required properties:
 - compatible: Should be "fsl,<chip>-i2c"
 - reg: Should contain registers location and length
 - interrupts: Should contain ERROR and DMA interrupts
+- clock-frequency: Desired I2C bus clock frequency in Hz.
+                   Only 100000Hz and 400000Hz modes are supported.
 
 Examples:
 
@@ -13,4 +15,5 @@ i2c0: i2c@80058000 {
        compatible = "fsl,imx28-i2c";
        reg = <0x80058000 2000>;
        interrupts = <111 68>;
+       clock-frequency = <100000>;
 };
diff --git a/Documentation/devicetree/bindings/i2c/i2c-ocores.txt b/Documentation/devicetree/bindings/i2c/i2c-ocores.txt
new file mode 100644 (file)
index 0000000..c15781f
--- /dev/null
@@ -0,0 +1,33 @@
+Device tree configuration for i2c-ocores
+
+Required properties:
+- compatible      : "opencores,i2c-ocores"
+- reg             : bus address start and address range size of device
+- interrupts      : interrupt number
+- clock-frequency : frequency of bus clock in Hz
+- #address-cells  : should be <1>
+- #size-cells     : should be <0>
+
+Optional properties:
+- reg-shift       : device register offsets are shifted by this value
+- reg-io-width    : io register width in bytes (1, 2 or 4)
+- regstep         : deprecated, use reg-shift above
+
+Example:
+
+       i2c0: ocores@a0000000 {
+               #address-cells = <1>;
+               #size-cells = <0>;
+               compatible = "opencores,i2c-ocores";
+               reg = <0xa0000000 0x8>;
+               interrupts = <10>;
+               clock-frequency = <20000000>;
+
+               reg-shift = <0>;        /* 8 bit registers */
+               reg-io-width = <1>;     /* 8 bit read/write */
+
+               dummy@60 {
+                       compatible = "dummy";
+                       reg = <0x60>;
+               };
+       };
index b891ee218354014e6bb9eca531b01750f6bb54c7..0f7945019f6fb0351140bb376465cb09ae302b5a 100644 (file)
@@ -1,4 +1,4 @@
-* I2C
+* Marvell MMP I2C controller
 
 Required properties :
 
@@ -32,3 +32,20 @@ Examples:
                interrupts = <58>;
        };
 
+* Marvell MV64XXX I2C controller
+
+Required properties :
+
+ - reg             : Offset and length of the register set for the device
+ - compatible      : Should be "marvell,mv64xxx-i2c"
+ - interrupts      : The interrupt number
+ - clock-frequency : Desired I2C bus clock frequency in Hz.
+
+Examples:
+
+       i2c@11000 {
+               compatible = "marvell,mv64xxx-i2c";
+               reg = <0x11000 0x20>;
+               interrupts = <29>;
+               clock-frequency = <100000>;
+       };
diff --git a/Documentation/devicetree/bindings/mfd/ab8500.txt b/Documentation/devicetree/bindings/mfd/ab8500.txt
new file mode 100644 (file)
index 0000000..69e757a
--- /dev/null
@@ -0,0 +1,123 @@
+* AB8500 Multi-Functional Device (MFD)
+
+Required parent device properties:
+- compatible             : contains "stericsson,ab8500";
+- interrupts             : contains the IRQ line for the AB8500
+- interrupt-controller   : describes the AB8500 as an Interrupt Controller (has its own domain)
+- #interrupt-cells       : should be 2, for 2-cell format
+                            - The first cell is the AB8500 local IRQ number
+                            - The second cell is used to specify optional parameters
+                              - bits[3:0] trigger type and level flags:
+                                  1 = low-to-high edge triggered
+                                  2 = high-to-low edge triggered
+                                  4 = active high level-sensitive
+                                  8 = active low level-sensitive
+
+Optional parent device properties:
+- reg                    : contains the PRCMU mailbox address for the AB8500 i2c port
+
+The AB8500 consists of a large and varied group of sub-devices:
+
+Device                     IRQ Names              Supply Names   Description
+------                     ---------              ------------   -----------
+ab8500-bm                :                      :              : Battery Manager
+ab8500-btemp             :                      :              : Battery Temperature
+ab8500-charger           :                      :              : Battery Charger
+ab8500-fg                :                      :              : Fuel Gauge
+ab8500-gpadc             : HW_CONV_END          : vddadc       : Analogue to Digital Converter
+                           SW_CONV_END          :              :
+ab8500-gpio              :                      :              : GPIO Controller
+ab8500-ponkey            : ONKEY_DBF            :              : Power-on Key
+                           ONKEY_DBR            :              :
+ab8500-pwm               :                      :              : Pulse Width Modulator
+ab8500-regulator         :                      :              : Regulators
+ab8500-rtc               : 60S                  :              : Real Time Clock
+                         : ALARM                :              :
+ab8500-sysctrl           :                      :              : System Control
+ab8500-usb               : ID_WAKEUP_R          : vddulpivio18 : Universal Serial Bus
+                         : ID_WAKEUP_F          : v-ape        :
+                         : VBUS_DET_F           : musb_1v8     :
+                         : VBUS_DET_R           :              :
+                         : USB_LINK_STATUS      :              :
+                         : USB_ADP_PROBE_PLUG   :              :
+                         : USB_ADP_PROBE_UNPLUG :              :
+
+Required child device properties:
+- compatible             : "stericsson,ab8500-[bm|btemp|charger|fg|gpadc|gpio|ponkey|
+                                               pwm|regulator|rtc|sysctrl|usb]";
+
+Optional child device properties:
+- interrupts             : contains the device IRQ(s) using the 2-cell format (see above)
+- interrupt-names        : contains names of IRQ resource in the order in which they were
+                           supplied in the interrupts property
+- <supply_name>-supply   : contains a phandle to the regulator supply node in Device Tree
+
+ab8500@5 {
+         compatible = "stericsson,ab8500";
+         reg = <5>; /* mailbox 5 is i2c */
+         interrupts = <0 40 0x4>;
+         interrupt-controller;
+         #interrupt-cells = <2>;
+
+         ab8500-rtc {
+                 compatible = "stericsson,ab8500-rtc";
+                 interrupts = <17 0x4
+                               18 0x4>;
+                 interrupt-names = "60S", "ALARM";
+         };
+
+        ab8500-gpadc {
+                compatible = "stericsson,ab8500-gpadc";
+                interrupts = <32 0x4
+                              39 0x4>;
+                interrupt-names = "HW_CONV_END", "SW_CONV_END";
+                vddadc-supply = <&ab8500_ldo_tvout_reg>;
+        };
+
+        ab8500-usb {
+                compatible = "stericsson,ab8500-usb";
+                interrupts = < 90 0x4
+                               96 0x4
+                               14 0x4
+                               15 0x4
+                               79 0x4
+                               74 0x4
+                               75 0x4>;
+                interrupt-names = "ID_WAKEUP_R",
+                                  "ID_WAKEUP_F",
+                                  "VBUS_DET_F",
+                                  "VBUS_DET_R",
+                                  "USB_LINK_STATUS",
+                                  "USB_ADP_PROBE_PLUG",
+                                  "USB_ADP_PROBE_UNPLUG";
+                vddulpivio18-supply = <&ab8500_ldo_initcore_reg>;
+                v-ape-supply = <&db8500_vape_reg>;
+                musb_1v8-supply = <&db8500_vsmps2_reg>;
+        };
+
+        ab8500-ponkey {
+                compatible = "stericsson,ab8500-ponkey";
+                interrupts = <6 0x4
+                              7 0x4>;
+                interrupt-names = "ONKEY_DBF", "ONKEY_DBR";
+        };
+
+        ab8500-sysctrl {
+                compatible = "stericsson,ab8500-sysctrl";
+        };
+
+        ab8500-pwm {
+                compatible = "stericsson,ab8500-pwm";
+        };
+
+        ab8500-regulators {
+                compatible = "stericsson,ab8500-regulator";
+
+                ab8500_ldo_aux1_reg: ab8500_ldo_aux1 {
+                        /*
+                         * See: Documentation/devicetree/bindings/regulator/regulator.txt
+                         * for more information on regulators
+                         */
+                };
+        };
+};
diff --git a/Documentation/devicetree/bindings/mfd/max77686.txt b/Documentation/devicetree/bindings/mfd/max77686.txt
new file mode 100644 (file)
index 0000000..c6a3469
--- /dev/null
@@ -0,0 +1,59 @@
+Maxim MAX77686 multi-function device
+
+MAX77686 is a Mulitifunction device with PMIC, RTC and Charger on chip. It is
+interfaced to host controller using i2c interface. PMIC and Charger submodules
+are addressed using same i2c slave address whereas RTC submodule uses
+different i2c slave address,presently for which we are statically creating i2c
+client while probing.This document describes the binding for mfd device and
+PMIC submodule.
+
+Required properties:
+- compatible : Must be "maxim,max77686";
+- reg : Specifies the i2c slave address of PMIC block.
+- interrupts : This i2c device has an IRQ line connected to the main SoC.
+- interrupt-parent : The parent interrupt controller.
+
+Optional node:
+- voltage-regulators : The regulators of max77686 have to be instantiated
+  under subnode named "voltage-regulators" using the following format.
+
+       regulator_name {
+               regulator-compatible = LDOn/BUCKn
+               standard regulator constraints....
+       };
+       refer Documentation/devicetree/bindings/regulator/regulator.txt
+
+  The regulator-compatible property of regulator should initialized with string
+to get matched with their hardware counterparts as follow:
+
+       -LDOn   :       for LDOs, where n can lie in range 1 to 26.
+                       example: LDO1, LDO2, LDO26.
+       -BUCKn  :       for BUCKs, where n can lie in range 1 to 9.
+                       example: BUCK1, BUCK5, BUCK9.
+
+Example:
+
+       max77686@09 {
+               compatible = "maxim,max77686";
+               interrupt-parent = <&wakeup_eint>;
+               interrupts = <26 0>;
+               reg = <0x09>;
+
+               voltage-regulators {
+                       ldo11_reg {
+                               regulator-compatible = "LDO11";
+                               regulator-name = "vdd_ldo11";
+                               regulator-min-microvolt = <1900000>;
+                               regulator-max-microvolt = <1900000>;
+                               regulator-always-on;
+                       };
+
+                       buck1_reg {
+                               regulator-compatible = "BUCK1";
+                               regulator-name = "vdd_mif";
+                               regulator-min-microvolt = <950000>;
+                               regulator-max-microvolt = <1300000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                       };
+       }
index d2802d4717bcfe70c146c95a1bc430807feae514..db03599ae4dcf268f0410d0b1373c3369f67351d 100644 (file)
@@ -81,7 +81,7 @@ Example:
 
                ti,vmbch-threshold = 0;
                ti,vmbch2-threshold = 0;
-
+               ti,en-ck32k-xtal;
                ti,en-gpio-sleep = <0 0 1 0 0 0 0 0 0>;
 
                vcc1-supply = <&reg_parent>;
index bc67c6f424aacc05ae894ecc7f684d7443d9ac95..c855240f3a0e0afec8fa3e6870f82dddde00ebcc 100644 (file)
@@ -6,7 +6,7 @@ They are connected ot the host processor via i2c for commands, McPDM for audio
 data and commands.
 
 Required properties:
-- compatible : Must be "ti,twl6040";
+- compatible : "ti,twl6040" for twl6040, "ti,twl6041" for twl6041
 - reg: must be 0x4b for i2c address
 - interrupts: twl6040 has one interrupt line connecteded to the main SoC
 - interrupt-parent: The parent interrupt controller
diff --git a/Documentation/devicetree/bindings/mips/cavium/bootbus.txt b/Documentation/devicetree/bindings/mips/cavium/bootbus.txt
new file mode 100644 (file)
index 0000000..6581478
--- /dev/null
@@ -0,0 +1,126 @@
+* Boot Bus
+
+The Octeon Boot Bus is a configurable parallel bus with 8 chip
+selects.  Each chip select is independently configurable.
+
+Properties:
+- compatible: "cavium,octeon-3860-bootbus"
+
+  Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs.
+
+- reg: The base address of the Boot Bus' register bank.
+
+- #address-cells: Must be <2>.  The first cell is the chip select
+   within the bootbus.  The second cell is the offset from the chip select.
+
+- #size-cells: Must be <1>.
+
+- ranges: There must be one one triplet of (child-bus-address,
+  parent-bus-address, length) for each active chip select.  If the
+  length element for any triplet is zero, the chip select is disabled,
+  making it inactive.
+
+The configuration parameters for each chip select are stored in child
+nodes.
+
+Configuration Properties:
+- compatible:  "cavium,octeon-3860-bootbus-config"
+
+- cavium,cs-index: A single cell indicating the chip select that
+  corresponds to this configuration.
+
+- cavium,t-adr: A cell specifying the ADR timing (in nS).
+
+- cavium,t-ce: A cell specifying the CE timing (in nS).
+
+- cavium,t-oe: A cell specifying the OE timing (in nS).
+
+- cavium,t-we: A cell specifying the WE timing (in nS).
+
+- cavium,t-rd-hld: A cell specifying the RD_HLD timing (in nS).
+
+- cavium,t-wr-hld: A cell specifying the WR_HLD timing (in nS).
+
+- cavium,t-pause: A cell specifying the PAUSE timing (in nS).
+
+- cavium,t-wait: A cell specifying the WAIT timing (in nS).
+
+- cavium,t-page: A cell specifying the PAGE timing (in nS).
+
+- cavium,t-rd-dly: A cell specifying the RD_DLY timing (in nS).
+
+- cavium,pages: A cell specifying the PAGES parameter (0 = 8 bytes, 1
+  = 2 bytes, 2 = 4 bytes, 3 = 8 bytes).
+
+- cavium,wait-mode: Optional.  If present, wait mode (WAITM) is selected.
+
+- cavium,page-mode: Optional.  If present, page mode (PAGEM) is selected.
+
+- cavium,bus-width: A cell specifying the WIDTH parameter (in bits) of
+  the bus for this chip select.
+
+- cavium,ale-mode: Optional.  If present, ALE mode is selected.
+
+- cavium,sam-mode: Optional.  If present, SAM mode is selected.
+
+- cavium,or-mode: Optional.  If present, OR mode is selected.
+
+Example:
+       bootbus: bootbus@1180000000000 {
+               compatible = "cavium,octeon-3860-bootbus";
+               reg = <0x11800 0x00000000 0x0 0x200>;
+               /* The chip select number and offset */
+               #address-cells = <2>;
+               /* The size of the chip select region */
+               #size-cells = <1>;
+               ranges = <0 0  0x0 0x1f400000  0xc00000>,
+                        <1 0  0x10000 0x30000000  0>,
+                        <2 0  0x10000 0x40000000  0>,
+                        <3 0  0x10000 0x50000000  0>,
+                        <4 0  0x0 0x1d020000  0x10000>,
+                        <5 0  0x0 0x1d040000  0x10000>,
+                        <6 0  0x0 0x1d050000  0x10000>,
+                        <7 0  0x10000 0x90000000  0>;
+
+                       cavium,cs-config@0 {
+                       compatible = "cavium,octeon-3860-bootbus-config";
+                       cavium,cs-index = <0>;
+                       cavium,t-adr  = <20>;
+                       cavium,t-ce   = <60>;
+                       cavium,t-oe   = <60>;
+                       cavium,t-we   = <45>;
+                       cavium,t-rd-hld = <35>;
+                       cavium,t-wr-hld = <45>;
+                       cavium,t-pause  = <0>;
+                       cavium,t-wait   = <0>;
+                       cavium,t-page   = <35>;
+                       cavium,t-rd-dly = <0>;
+
+                       cavium,pages     = <0>;
+                       cavium,bus-width = <8>;
+               };
+               .
+               .
+               .
+               cavium,cs-config@6 {
+                       compatible = "cavium,octeon-3860-bootbus-config";
+                       cavium,cs-index = <6>;
+                       cavium,t-adr  = <5>;
+                       cavium,t-ce   = <300>;
+                       cavium,t-oe   = <270>;
+                       cavium,t-we   = <150>;
+                       cavium,t-rd-hld = <100>;
+                       cavium,t-wr-hld = <70>;
+                       cavium,t-pause  = <0>;
+                       cavium,t-wait   = <0>;
+                       cavium,t-page   = <320>;
+                       cavium,t-rd-dly = <0>;
+
+                       cavium,pages     = <0>;
+                       cavium,wait-mode;
+                       cavium,bus-width = <16>;
+               };
+               .
+               .
+               .
+       };
diff --git a/Documentation/devicetree/bindings/mips/cavium/ciu.txt b/Documentation/devicetree/bindings/mips/cavium/ciu.txt
new file mode 100644 (file)
index 0000000..2c2d074
--- /dev/null
@@ -0,0 +1,26 @@
+* Central Interrupt Unit
+
+Properties:
+- compatible: "cavium,octeon-3860-ciu"
+
+  Compatibility with all cn3XXX, cn5XXX and cn63XX SOCs.
+
+- interrupt-controller:  This is an interrupt controller.
+
+- reg: The base address of the CIU's register bank.
+
+- #interrupt-cells: Must be <2>.  The first cell is the bank within
+   the CIU and may have a value of 0 or 1.  The second cell is the bit
+   within the bank and may have a value between 0 and 63.
+
+Example:
+       interrupt-controller@1070000000000 {
+               compatible = "cavium,octeon-3860-ciu";
+               interrupt-controller;
+               /* Interrupts are specified by two parts:
+                * 1) Controller register (0 or 1)
+                * 2) Bit within the register (0..63)
+                */
+               #interrupt-cells = <2>;
+               reg = <0x10700 0x00000000 0x0 0x7000>;
+       };
diff --git a/Documentation/devicetree/bindings/mips/cavium/ciu2.txt b/Documentation/devicetree/bindings/mips/cavium/ciu2.txt
new file mode 100644 (file)
index 0000000..0ec7ba8
--- /dev/null
@@ -0,0 +1,27 @@
+* Central Interrupt Unit
+
+Properties:
+- compatible: "cavium,octeon-6880-ciu2"
+
+  Compatibility with 68XX SOCs.
+
+- interrupt-controller:  This is an interrupt controller.
+
+- reg: The base address of the CIU's register bank.
+
+- #interrupt-cells: Must be <2>.  The first cell is the bank within
+  the CIU and may have a value between 0 and 63.  The second cell is
+  the bit within the bank and may also have a value between 0 and 63.
+
+Example:
+       interrupt-controller@1070100000000 {
+               compatible = "cavium,octeon-6880-ciu2";
+               interrupt-controller;
+               /* Interrupts are specified by two parts:
+                * 1) Controller register (0..63)
+                * 2) Bit within the register (0..63)
+                */
+               #address-cells = <0>;
+               #interrupt-cells = <2>;
+               reg = <0x10701 0x00000000 0x0 0x4000000>;
+       };
diff --git a/Documentation/devicetree/bindings/mips/cavium/dma-engine.txt b/Documentation/devicetree/bindings/mips/cavium/dma-engine.txt
new file mode 100644 (file)
index 0000000..cb4291e
--- /dev/null
@@ -0,0 +1,21 @@
+* DMA Engine.
+
+The Octeon DMA Engine transfers between the Boot Bus and main memory.
+The DMA Engine will be refered to by phandle by any device that is
+connected to it.
+
+Properties:
+- compatible: "cavium,octeon-5750-bootbus-dma"
+
+  Compatibility with all cn52XX, cn56XX and cn6XXX SOCs.
+
+- reg: The base address of the DMA Engine's register bank.
+
+- interrupts: A single interrupt specifier.
+
+Example:
+       dma0: dma-engine@1180000000100 {
+               compatible = "cavium,octeon-5750-bootbus-dma";
+               reg = <0x11800 0x00000100 0x0 0x8>;
+               interrupts = <0 63>;
+       };
diff --git a/Documentation/devicetree/bindings/mips/cavium/uctl.txt b/Documentation/devicetree/bindings/mips/cavium/uctl.txt
new file mode 100644 (file)
index 0000000..aa66b9b
--- /dev/null
@@ -0,0 +1,46 @@
+* UCTL USB controller glue
+
+Properties:
+- compatible: "cavium,octeon-6335-uctl"
+
+  Compatibility with all cn6XXX SOCs.
+
+- reg: The base address of the UCTL register bank.
+
+- #address-cells: Must be <2>.
+
+- #size-cells: Must be <2>.
+
+- ranges: Empty to signify direct mapping of the children.
+
+- refclk-frequency: A single cell containing the reference clock
+  frequency in Hz.
+
+- refclk-type: A string describing the reference clock connection
+  either "crystal" or "external".
+
+Example:
+       uctl@118006f000000 {
+               compatible = "cavium,octeon-6335-uctl";
+               reg = <0x11800 0x6f000000 0x0 0x100>;
+               ranges; /* Direct mapping */
+               #address-cells = <2>;
+               #size-cells = <2>;
+               /* 12MHz, 24MHz and 48MHz allowed */
+               refclk-frequency = <24000000>;
+               /* Either "crystal" or "external" */
+               refclk-type = "crystal";
+
+               ehci@16f0000000000 {
+                       compatible = "cavium,octeon-6335-ehci","usb-ehci";
+                       reg = <0x16f00 0x00000000 0x0 0x100>;
+                       interrupts = <0 56>;
+                       big-endian-regs;
+               };
+               ohci@16f0000000400 {
+                       compatible = "cavium,octeon-6335-ohci","usb-ohci";
+                       reg = <0x16f00 0x00000400 0x0 0x100>;
+                       interrupts = <0 56>;
+                       big-endian-regs;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/net/cavium-mdio.txt b/Documentation/devicetree/bindings/net/cavium-mdio.txt
new file mode 100644 (file)
index 0000000..04cb749
--- /dev/null
@@ -0,0 +1,27 @@
+* System Management Interface (SMI) / MDIO
+
+Properties:
+- compatible: "cavium,octeon-3860-mdio"
+
+  Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs.
+
+- reg: The base address of the MDIO bus controller register bank.
+
+- #address-cells: Must be <1>.
+
+- #size-cells: Must be <0>.  MDIO addresses have no size component.
+
+Typically an MDIO bus might have several children.
+
+Example:
+       mdio@1180000001800 {
+               compatible = "cavium,octeon-3860-mdio";
+               #address-cells = <1>;
+               #size-cells = <0>;
+               reg = <0x11800 0x00001800 0x0 0x40>;
+
+               ethernet-phy@0 {
+                       ...
+                       reg = <0>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/net/cavium-mix.txt b/Documentation/devicetree/bindings/net/cavium-mix.txt
new file mode 100644 (file)
index 0000000..5da628d
--- /dev/null
@@ -0,0 +1,39 @@
+* MIX Ethernet controller.
+
+Properties:
+- compatible: "cavium,octeon-5750-mix"
+
+  Compatibility with all cn5XXX and cn6XXX SOCs populated with MIX
+  devices.
+
+- reg: The base addresses of four separate register banks.  The first
+  bank contains the MIX registers.  The second bank the corresponding
+  AGL registers.  The third bank are the AGL registers shared by all
+  MIX devices present.  The fourth bank is the AGL_PRT_CTL shared by
+  all MIX devices present.
+
+- cell-index: A single cell specifying which portion of the shared
+  register banks corresponds to this MIX device.
+
+- interrupts: Two interrupt specifiers.  The first is the MIX
+  interrupt routing and the second the routing for the AGL interrupts.
+
+- mac-address: Optional, the MAC address to assign to the device.
+
+- local-mac-address: Optional, the MAC address to assign to the device
+  if mac-address is not specified.
+
+- phy-handle: Optional, a phandle for the PHY device connected to this device.
+
+Example:
+       ethernet@1070000100800 {
+               compatible = "cavium,octeon-5750-mix";
+               reg = <0x10700 0x00100800 0x0 0x100>, /* MIX */
+                     <0x11800 0xE0000800 0x0 0x300>, /* AGL */
+                     <0x11800 0xE0000400 0x0 0x400>, /* AGL_SHARED  */
+                     <0x11800 0xE0002008 0x0 0x8>;   /* AGL_PRT_CTL */
+               cell-index = <1>;
+               interrupts = <1 18>, < 1 46>;
+               local-mac-address = [ 00 0f b7 10 63 54 ];
+               phy-handle = <&phy1>;
+       };
diff --git a/Documentation/devicetree/bindings/net/cavium-pip.txt b/Documentation/devicetree/bindings/net/cavium-pip.txt
new file mode 100644 (file)
index 0000000..d4c53ba
--- /dev/null
@@ -0,0 +1,98 @@
+* PIP Ethernet nexus.
+
+The PIP Ethernet nexus can control several data packet input/output
+devices.  The devices have a two level grouping scheme.  There may be
+several interfaces, and each interface may have several ports.  These
+ports might be an individual Ethernet PHY.
+
+
+Properties for the PIP nexus:
+- compatible: "cavium,octeon-3860-pip"
+
+  Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs.
+
+- reg: The base address of the PIP's register bank.
+
+- #address-cells: Must be <1>.
+
+- #size-cells: Must be <0>.
+
+Properties for PIP interfaces which is a child the PIP nexus:
+- compatible: "cavium,octeon-3860-pip-interface"
+
+  Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs.
+
+- reg: The interface number.
+
+- #address-cells: Must be <1>.
+
+- #size-cells: Must be <0>.
+
+Properties for PIP port which is a child the PIP interface:
+- compatible: "cavium,octeon-3860-pip-port"
+
+  Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs.
+
+- reg: The port number within the interface group.
+
+- mac-address: Optional, the MAC address to assign to the device.
+
+- local-mac-address: Optional, the MAC address to assign to the device
+  if mac-address is not specified.
+
+- phy-handle: Optional, a phandle for the PHY device connected to this device.
+
+Example:
+
+       pip@11800a0000000 {
+               compatible = "cavium,octeon-3860-pip";
+               #address-cells = <1>;
+               #size-cells = <0>;
+               reg = <0x11800 0xa0000000 0x0 0x2000>;
+
+               interface@0 {
+                       compatible = "cavium,octeon-3860-pip-interface";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg = <0>; /* interface */
+
+                       ethernet@0 {
+                               compatible = "cavium,octeon-3860-pip-port";
+                               reg = <0x0>; /* Port */
+                               local-mac-address = [ 00 0f b7 10 63 60 ];
+                               phy-handle = <&phy2>;
+                       };
+                       ethernet@1 {
+                               compatible = "cavium,octeon-3860-pip-port";
+                               reg = <0x1>; /* Port */
+                               local-mac-address = [ 00 0f b7 10 63 61 ];
+                               phy-handle = <&phy3>;
+                       };
+                       ethernet@2 {
+                               compatible = "cavium,octeon-3860-pip-port";
+                               reg = <0x2>; /* Port */
+                               local-mac-address = [ 00 0f b7 10 63 62 ];
+                               phy-handle = <&phy4>;
+                       };
+                       ethernet@3 {
+                               compatible = "cavium,octeon-3860-pip-port";
+                               reg = <0x3>; /* Port */
+                               local-mac-address = [ 00 0f b7 10 63 63 ];
+                               phy-handle = <&phy5>;
+                       };
+               };
+
+               interface@1 {
+                       compatible = "cavium,octeon-3860-pip-interface";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg = <1>; /* interface */
+
+                       ethernet@0 {
+                               compatible = "cavium,octeon-3860-pip-port";
+                               reg = <0x0>; /* Port */
+                               local-mac-address = [ 00 0f b7 10 63 64 ];
+                               phy-handle = <&phy6>;
+                       };
+               };
+       };
diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt
new file mode 100644 (file)
index 0000000..dcaabe9
--- /dev/null
@@ -0,0 +1,109 @@
+TI SoC Ethernet Switch Controller Device Tree Bindings
+------------------------------------------------------
+
+Required properties:
+- compatible           : Should be "ti,cpsw"
+- reg                  : physical base address and size of the cpsw
+                         registers map
+- interrupts           : property with a value describing the interrupt
+                         number
+- interrupt-parent     : The parent interrupt controller
+- cpdma_channels       : Specifies number of channels in CPDMA
+- host_port_no         : Specifies host port shift
+- cpdma_reg_ofs                : Specifies CPDMA submodule register offset
+- cpdma_sram_ofs       : Specifies CPDMA SRAM offset
+- ale_reg_ofs          : Specifies ALE submodule register offset
+- ale_entries          : Specifies No of entries ALE can hold
+- host_port_reg_ofs    : Specifies host port register offset
+- hw_stats_reg_ofs     : Specifies hardware statistics register offset
+- bd_ram_ofs           : Specifies internal desciptor RAM offset
+- bd_ram_size          : Specifies internal descriptor RAM size
+- rx_descs             : Specifies number of Rx descriptors
+- mac_control          : Specifies Default MAC control register content
+                         for the specific platform
+- slaves               : Specifies number for slaves
+- slave_reg_ofs                : Specifies slave register offset
+- sliver_reg_ofs       : Specifies slave sliver register offset
+- phy_id               : Specifies slave phy id
+- mac-address          : Specifies slave MAC address
+
+Optional properties:
+- ti,hwmods            : Must be "cpgmac0"
+- no_bd_ram            : Must be 0 or 1
+
+Note: "ti,hwmods" field is used to fetch the base address and irq
+resources from TI, omap hwmod data base during device registration.
+Future plan is to migrate hwmod data base contents into device tree
+blob so that, all the required data will be used from device tree dts
+file.
+
+Examples:
+
+       mac: ethernet@4A100000 {
+               compatible = "ti,cpsw";
+               reg = <0x4A100000 0x1000>;
+               interrupts = <55 0x4>;
+               interrupt-parent = <&intc>;
+               cpdma_channels = <8>;
+               host_port_no = <0>;
+               cpdma_reg_ofs = <0x800>;
+               cpdma_sram_ofs = <0xa00>;
+               ale_reg_ofs = <0xd00>;
+               ale_entries = <1024>;
+               host_port_reg_ofs = <0x108>;
+               hw_stats_reg_ofs = <0x900>;
+               bd_ram_ofs = <0x2000>;
+               bd_ram_size = <0x2000>;
+               no_bd_ram = <0>;
+               rx_descs = <64>;
+               mac_control = <0x20>;
+               slaves = <2>;
+               cpsw_emac0: slave@0 {
+                       slave_reg_ofs = <0x208>;
+                       sliver_reg_ofs = <0xd80>;
+                       phy_id = "davinci_mdio.16:00";
+                       /* Filled in by U-Boot */
+                       mac-address = [ 00 00 00 00 00 00 ];
+               };
+               cpsw_emac1: slave@1 {
+                       slave_reg_ofs = <0x308>;
+                       sliver_reg_ofs = <0xdc0>;
+                       phy_id = "davinci_mdio.16:01";
+                       /* Filled in by U-Boot */
+                       mac-address = [ 00 00 00 00 00 00 ];
+               };
+       };
+
+(or)
+       mac: ethernet@4A100000 {
+               compatible = "ti,cpsw";
+               ti,hwmods = "cpgmac0";
+               cpdma_channels = <8>;
+               host_port_no = <0>;
+               cpdma_reg_ofs = <0x800>;
+               cpdma_sram_ofs = <0xa00>;
+               ale_reg_ofs = <0xd00>;
+               ale_entries = <1024>;
+               host_port_reg_ofs = <0x108>;
+               hw_stats_reg_ofs = <0x900>;
+               bd_ram_ofs = <0x2000>;
+               bd_ram_size = <0x2000>;
+               no_bd_ram = <0>;
+               rx_descs = <64>;
+               mac_control = <0x20>;
+               slaves = <2>;
+               cpsw_emac0: slave@0 {
+                       slave_reg_ofs = <0x208>;
+                       sliver_reg_ofs = <0xd80>;
+                       phy_id = "davinci_mdio.16:00";
+                       /* Filled in by U-Boot */
+                       mac-address = [ 00 00 00 00 00 00 ];
+               };
+               cpsw_emac1: slave@1 {
+                       slave_reg_ofs = <0x308>;
+                       sliver_reg_ofs = <0xdc0>;
+                       phy_id = "davinci_mdio.16:01";
+                       /* Filled in by U-Boot */
+                       mac-address = [ 00 00 00 00 00 00 ];
+               };
+       };
diff --git a/Documentation/devicetree/bindings/net/davinci-mdio.txt b/Documentation/devicetree/bindings/net/davinci-mdio.txt
new file mode 100644 (file)
index 0000000..72efaaf
--- /dev/null
@@ -0,0 +1,33 @@
+TI SoC Davinci MDIO Controller Device Tree Bindings
+---------------------------------------------------
+
+Required properties:
+- compatible           : Should be "ti,davinci_mdio"
+- reg                  : physical base address and size of the davinci mdio
+                         registers map
+- bus_freq             : Mdio Bus frequency
+
+Optional properties:
+- ti,hwmods            : Must be "davinci_mdio"
+
+Note: "ti,hwmods" field is used to fetch the base address and irq
+resources from TI, omap hwmod data base during device registration.
+Future plan is to migrate hwmod data base contents into device tree
+blob so that, all the required data will be used from device tree dts
+file.
+
+Examples:
+
+       mdio: davinci_mdio@4A101000 {
+               compatible = "ti,cpsw";
+               reg = <0x4A101000 0x1000>;
+               bus_freq = <1000000>;
+       };
+
+(or)
+
+       mdio: davinci_mdio@4A101000 {
+               compatible = "ti,cpsw";
+               ti,hwmods = "davinci_mdio";
+               bus_freq = <1000000>;
+       };
diff --git a/Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt b/Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt
new file mode 100644 (file)
index 0000000..cfe1db3
--- /dev/null
@@ -0,0 +1,12 @@
+LPC32XX PWM controller
+
+Required properties:
+- compatible: should be "nxp,lpc3220-pwm"
+- reg: physical base address and length of the controller's registers
+
+Examples:
+
+pwm@0x4005C000 {
+       compatible = "nxp,lpc3220-pwm";
+       reg = <0x4005C000 0x8>;
+};
diff --git a/Documentation/devicetree/bindings/pwm/mxs-pwm.txt b/Documentation/devicetree/bindings/pwm/mxs-pwm.txt
new file mode 100644 (file)
index 0000000..b16f4a5
--- /dev/null
@@ -0,0 +1,17 @@
+Freescale MXS PWM controller
+
+Required properties:
+- compatible: should be "fsl,imx23-pwm"
+- reg: physical base address and length of the controller's registers
+- #pwm-cells: should be 2.  The first cell specifies the per-chip index
+  of the PWM to use and the second cell is the duty cycle in nanoseconds.
+- fsl,pwm-number: the number of PWM devices
+
+Example:
+
+pwm: pwm@80064000 {
+       compatible = "fsl,imx28-pwm", "fsl,imx23-pwm";
+       reg = <0x80064000 2000>;
+       #pwm-cells = <2>;
+       fsl,pwm-number = <8>;
+};
diff --git a/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt b/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt
new file mode 100644 (file)
index 0000000..bbbeedb
--- /dev/null
@@ -0,0 +1,18 @@
+Tegra SoC PWFM controller
+
+Required properties:
+- compatible: should be one of:
+  - "nvidia,tegra20-pwm"
+  - "nvidia,tegra30-pwm"
+- reg: physical base address and length of the controller's registers
+- #pwm-cells: On Tegra the number of cells used to specify a PWM is 2. The
+  first cell specifies the per-chip index of the PWM to use and the second
+  cell is the duty cycle in nanoseconds.
+
+Example:
+
+       pwm: pwm@7000a000 {
+               compatible = "nvidia,tegra20-pwm";
+               reg = <0x7000a000 0x100>;
+               #pwm-cells = <2>;
+       };
diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
new file mode 100644 (file)
index 0000000..73ec962
--- /dev/null
@@ -0,0 +1,57 @@
+Specifying PWM information for devices
+======================================
+
+1) PWM user nodes
+-----------------
+
+PWM users should specify a list of PWM devices that they want to use
+with a property containing a 'pwm-list':
+
+       pwm-list ::= <single-pwm> [pwm-list]
+       single-pwm ::= <pwm-phandle> <pwm-specifier>
+       pwm-phandle : phandle to PWM controller node
+       pwm-specifier : array of #pwm-cells specifying the given PWM
+                       (controller specific)
+
+PWM properties should be named "pwms". The exact meaning of each pwms
+property must be documented in the device tree binding for each device.
+An optional property "pwm-names" may contain a list of strings to label
+each of the PWM devices listed in the "pwms" property. If no "pwm-names"
+property is given, the name of the user node will be used as fallback.
+
+Drivers for devices that use more than a single PWM device can use the
+"pwm-names" property to map the name of the PWM device requested by the
+pwm_get() call to an index into the list given by the "pwms" property.
+
+The following example could be used to describe a PWM-based backlight
+device:
+
+       pwm: pwm {
+               #pwm-cells = <2>;
+       };
+
+       [...]
+
+       bl: backlight {
+               pwms = <&pwm 0 5000000>;
+               pwm-names = "backlight";
+       };
+
+pwm-specifier typically encodes the chip-relative PWM number and the PWM
+period in nanoseconds. Note that in the example above, specifying the
+"pwm-names" is redundant because the name "backlight" would be used as
+fallback anyway.
+
+2) PWM controller nodes
+-----------------------
+
+PWM controller nodes must specify the number of cells used for the
+specifier using the '#pwm-cells' property.
+
+An example PWM controller might look like this:
+
+       pwm: pwm@7000a000 {
+               compatible = "nvidia,tegra20-pwm";
+               reg = <0x7000a000 0x100>;
+               #pwm-cells = <2>;
+       };
diff --git a/Documentation/devicetree/bindings/serial/cavium-uart.txt b/Documentation/devicetree/bindings/serial/cavium-uart.txt
new file mode 100644 (file)
index 0000000..87a6c37
--- /dev/null
@@ -0,0 +1,19 @@
+* Universal Asynchronous Receiver/Transmitter (UART)
+
+- compatible: "cavium,octeon-3860-uart"
+
+  Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs.
+
+- reg: The base address of the UART register bank.
+
+- interrupts: A single interrupt specifier.
+
+- current-speed: Optional, the current bit rate in bits per second.
+
+Example:
+       uart1: serial@1180000000c00 {
+               compatible = "cavium,octeon-3860-uart","ns16550";
+               reg = <0x11800 0x00000c00 0x0 0x400>;
+               current-speed = <115200>;
+               interrupts = <0 35>;
+       };
diff --git a/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
new file mode 100644 (file)
index 0000000..1e4fc72
--- /dev/null
@@ -0,0 +1,28 @@
+pwm-backlight bindings
+
+Required properties:
+  - compatible: "pwm-backlight"
+  - pwms: OF device-tree PWM specification (see PWM binding[0])
+  - brightness-levels: Array of distinct brightness levels. Typically these
+      are in the range from 0 to 255, but any range starting at 0 will do.
+      The actual brightness level (PWM duty cycle) will be interpolated
+      from these values. 0 means a 0% duty cycle (darkest/off), while the
+      last value in the array represents a 100% duty cycle (brightest).
+  - default-brightness-level: the default brightness level (index into the
+      array defined by the "brightness-levels" property)
+
+Optional properties:
+  - pwm-names: a list of names for the PWM devices specified in the
+               "pwms" property (see PWM binding[0])
+
+[0]: Documentation/devicetree/bindings/pwm/pwm.txt
+
+Example:
+
+       backlight {
+               compatible = "pwm-backlight";
+               pwms = <&pwm 0 5000000>;
+
+               brightness-levels = <0 4 8 16 32 64 128 255>;
+               default-brightness-level = <6>;
+       };
index b4a898f43c37e72d728c17013520e44f56c87a5d..39462cf35cd4fe946dd0f0f5668defddde60d3cf 100644 (file)
@@ -150,7 +150,6 @@ keywords.c
 ksym.c*
 ksym.h*
 kxgettext
-lkc_defs.h
 lex.c
 lex.*.c
 linux
index fbb2411744864dba27ca6d2821c640f71643a89b..12d3952e83d5b0305c26afba116047aabf3c2850 100755 (executable)
@@ -29,7 +29,7 @@ use IO::Handle;
                "af9015", "ngene", "az6027", "lme2510_lg", "lme2510c_s7395",
                "lme2510c_s7395_old", "drxk", "drxk_terratec_h5",
                "drxk_hauppauge_hvr930c", "tda10071", "it9135", "it9137",
-               "drxk_pctv");
+               "drxk_pctv", "drxk_terratec_htc_stick", "sms1xxx_hcw");
 
 # Check args
 syntax() if (scalar(@ARGV) != 1);
@@ -676,6 +676,24 @@ sub drxk_terratec_h5 {
     "$fwfile"
 }
 
+sub drxk_terratec_htc_stick {
+    my $url = "http://ftp.terratec.de/Receiver/Cinergy_HTC_Stick/Updates/";
+    my $zipfile = "Cinergy_HTC_Stick_Drv_5.09.1202.00_XP_Vista_7.exe";
+    my $hash = "6722a2442a05423b781721fbc069ed5e";
+    my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 0);
+    my $drvfile = "Cinergy HTC Stick/BDA Driver 5.09.1202.00/Windows 32 Bit/emOEM.sys";
+    my $fwfile = "dvb-usb-terratec-htc-stick-drxk.fw";
+
+    checkstandard();
+
+    wgetfile($zipfile, $url . $zipfile);
+    verify($zipfile, $hash);
+    unzip($zipfile, $tmpdir);
+    extract("$tmpdir/$drvfile", 0x4e5c0, 42692, "$fwfile");
+
+    "$fwfile"
+}
+
 sub it9135 {
        my $sourcefile = "dvb-usb-it9135.zip";
        my $url = "http://www.ite.com.tw/uploads/firmware/v3.6.0.0/$sourcefile";
@@ -748,6 +766,28 @@ sub drxk_pctv {
     "$fwfile";
 }
 
+sub sms1xxx_hcw {
+    my $url = "http://steventoth.net/linux/sms1xxx/";
+    my %files = (
+       'sms1xxx-hcw-55xxx-dvbt-01.fw'  => "afb6f9fb9a71d64392e8564ef9577e5a",
+       'sms1xxx-hcw-55xxx-dvbt-02.fw'  => "b44807098ba26e52cbedeadc052ba58f",
+       'sms1xxx-hcw-55xxx-isdbt-02.fw' => "dae934eeea85225acbd63ce6cfe1c9e4",
+    );
+
+    checkstandard();
+
+    my $allfiles;
+    foreach my $fwfile (keys %files) {
+       wgetfile($fwfile, "$url/$fwfile");
+       verify($fwfile, $files{$fwfile});
+       $allfiles .= " $fwfile";
+    }
+
+    $allfiles =~ s/^\s//;
+
+    $allfiles;
+}
+
 # ---------------------------------------------------------------
 # Utilities
 
index 03df2b0203320d3451fad0765bb11e95661392f8..56c7e936430f172e135ea8d180682167ce907754 100644 (file)
@@ -232,116 +232,20 @@ EDAC control and attribute files.
 
 
 In 'mcX' directories are EDAC control and attribute files for
-this 'X' instance of the memory controllers:
-
-
-Counter reset control file:
-
-       'reset_counters'
-
-       This write-only control file will zero all the statistical counters
-       for UE and CE errors.  Zeroing the counters will also reset the timer
-       indicating how long since the last counter zero.  This is useful
-       for computing errors/time.  Since the counters are always reset at
-       driver initialization time, no module/kernel parameter is available.
-
-       RUN TIME: echo "anything" >/sys/devices/system/edac/mc/mc0/counter_reset
-
-               This resets the counters on memory controller 0
-
-
-Seconds since last counter reset control file:
-
-       'seconds_since_reset'
-
-       This attribute file displays how many seconds have elapsed since the
-       last counter reset. This can be used with the error counters to
-       measure error rates.
-
-
-
-Memory Controller name attribute file:
-
-       'mc_name'
-
-       This attribute file displays the type of memory controller
-       that is being utilized.
-
-
-Total memory managed by this memory controller attribute file:
-
-       'size_mb'
-
-       This attribute file displays, in count of megabytes, of memory
-       that this instance of memory controller manages.
-
-
-Total Uncorrectable Errors count attribute file:
-
-       'ue_count'
-
-       This attribute file displays the total count of uncorrectable
-       errors that have occurred on this memory controller. If panic_on_ue
-       is set this counter will not have a chance to increment,
-       since EDAC will panic the system.
-
-
-Total UE count that had no information attribute fileY:
-
-       'ue_noinfo_count'
-
-       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:
-
-       'ce_count'
-
-       This attribute file displays the total count of correctable
-       errors that have occurred on this memory controller. This
-       count is very important to examine. CEs provide early
-       indications that a DIMM is beginning to fail. This count
-       field should be monitored for non-zero values and report
-       such information to the system administrator.
-
-
-Total Correctable Errors count attribute file:
-
-       'ce_noinfo_count'
-
-       This attribute file displays the number of CEs that
-       have occurred wherewith no information as to which DIMM slot
-       is having errors. Memory is handicapped, but operational,
-       yet no information is available to indicate which slot
-       the failing memory is in. This count field should be also
-       be monitored for non-zero values.
-
-Device Symlink:
-
-       'device'
-
-       Symlink to the memory controller device.
-
-Sdram memory scrubbing rate:
-
-       'sdram_scrub_rate'
-
-       Read/Write attribute file that controls memory scrubbing. The scrubbing
-       rate is set by writing a minimum bandwidth in bytes/sec to the attribute
-       file. The rate will be translated to an internal value that gives at
-       least the specified rate.
-
-       Reading the file will return the actual scrubbing rate employed.
-
-       If configuration fails or memory scrubbing is not implemented, accessing
-       that attribute will fail.
+this 'X' instance of the memory controllers.
 
+For a description of the sysfs API, please see:
+       Documentation/ABI/testing/sysfs/devices-edac
 
 
 ============================================================================
 'csrowX' DIRECTORIES
 
+When CONFIG_EDAC_LEGACY_SYSFS is enabled, the sysfs will contain the
+csrowX directories. As this API doesn't work properly for Rambus, FB-DIMMs
+and modern Intel Memory Controllers, this is being deprecated in favor
+of dimmX directories.
+
 In the 'csrowX' directories are EDAC control and attribute files for
 this 'X' instance of csrow:
 
index ba4be8b77093f2ea0399ae7c6662471e987a98e0..4cf1a2a6bd7257125e824f930d46c0e70b8fb8e6 100644 (file)
@@ -240,3 +240,30 @@ trap "echo 0 > /sys/kernel/debug/$FAILTYPE/probability" SIGINT SIGTERM EXIT
 echo "Injecting errors into the module $module... (interrupt to stop)"
 sleep 1000000
 
+Tool to run command with failslab or fail_page_alloc
+----------------------------------------------------
+In order to make it easier to accomplish the tasks mentioned above, we can use
+tools/testing/fault-injection/failcmd.sh.  Please run a command
+"./tools/testing/fault-injection/failcmd.sh --help" for more information and
+see the following examples.
+
+Examples:
+
+Run a command "make -C tools/testing/selftests/ run_tests" with injecting slab
+allocation failure.
+
+       # ./tools/testing/fault-injection/failcmd.sh \
+               -- make -C tools/testing/selftests/ run_tests
+
+Same as above except to specify 100 times failures at most instead of one time
+at most by default.
+
+       # ./tools/testing/fault-injection/failcmd.sh --times=100 \
+               -- make -C tools/testing/selftests/ run_tests
+
+Same as above except to inject page allocation failure instead of slab
+allocation failure.
+
+       # env FAILCMD_TYPE=fail_page_alloc \
+               ./tools/testing/fault-injection/failcmd.sh --times=100 \
+                -- make -C tools/testing/selftests/ run_tests
diff --git a/Documentation/fault-injection/notifier-error-inject.txt b/Documentation/fault-injection/notifier-error-inject.txt
new file mode 100644 (file)
index 0000000..c83526c
--- /dev/null
@@ -0,0 +1,99 @@
+Notifier error injection
+========================
+
+Notifier error injection provides the ability to inject artifical errors to
+specified notifier chain callbacks. It is useful to test the error handling of
+notifier call chain failures which is rarely executed.  There are kernel
+modules that can be used to test the following notifiers.
+
+ * CPU notifier
+ * PM notifier
+ * Memory hotplug notifier
+ * powerpc pSeries reconfig notifier
+
+CPU notifier error injection module
+-----------------------------------
+This feature can be used to test the error handling of the CPU notifiers by
+injecting artifical errors to CPU notifier chain callbacks.
+
+If the notifier call chain should be failed with some events notified, write
+the error code to debugfs interface
+/sys/kernel/debug/notifier-error-inject/cpu/actions/<notifier event>/error
+
+Possible CPU notifier events to be failed are:
+
+ * CPU_UP_PREPARE
+ * CPU_UP_PREPARE_FROZEN
+ * CPU_DOWN_PREPARE
+ * CPU_DOWN_PREPARE_FROZEN
+
+Example1: Inject CPU offline error (-1 == -EPERM)
+
+       # cd /sys/kernel/debug/notifier-error-inject/cpu
+       # echo -1 > actions/CPU_DOWN_PREPARE/error
+       # echo 0 > /sys/devices/system/cpu/cpu1/online
+       bash: echo: write error: Operation not permitted
+
+Example2: inject CPU online error (-2 == -ENOENT)
+
+       # echo -2 > actions/CPU_UP_PREPARE/error
+       # echo 1 > /sys/devices/system/cpu/cpu1/online
+       bash: echo: write error: No such file or directory
+
+PM notifier error injection module
+----------------------------------
+This feature is controlled through debugfs interface
+/sys/kernel/debug/notifier-error-inject/pm/actions/<notifier event>/error
+
+Possible PM notifier events to be failed are:
+
+ * PM_HIBERNATION_PREPARE
+ * PM_SUSPEND_PREPARE
+ * PM_RESTORE_PREPARE
+
+Example: Inject PM suspend error (-12 = -ENOMEM)
+
+       # cd /sys/kernel/debug/notifier-error-inject/pm/
+       # echo -12 > actions/PM_SUSPEND_PREPARE/error
+       # echo mem > /sys/power/state
+       bash: echo: write error: Cannot allocate memory
+
+Memory hotplug notifier error injection module
+----------------------------------------------
+This feature is controlled through debugfs interface
+/sys/kernel/debug/notifier-error-inject/memory/actions/<notifier event>/error
+
+Possible memory notifier events to be failed are:
+
+ * MEM_GOING_ONLINE
+ * MEM_GOING_OFFLINE
+
+Example: Inject memory hotplug offline error (-12 == -ENOMEM)
+
+       # cd /sys/kernel/debug/notifier-error-inject/memory
+       # echo -12 > actions/MEM_GOING_OFFLINE/error
+       # echo offline > /sys/devices/system/memory/memoryXXX/state
+       bash: echo: write error: Cannot allocate memory
+
+powerpc pSeries reconfig notifier error injection module
+--------------------------------------------------------
+This feature is controlled through debugfs interface
+/sys/kernel/debug/notifier-error-inject/pSeries-reconfig/actions/<notifier event>/error
+
+Possible pSeries reconfig notifier events to be failed are:
+
+ * PSERIES_RECONFIG_ADD
+ * PSERIES_RECONFIG_REMOVE
+ * PSERIES_DRCONF_MEM_ADD
+ * PSERIES_DRCONF_MEM_REMOVE
+
+For more usage examples
+-----------------------
+There are tools/testing/selftests using the notifier error injection features
+for CPU and memory notifiers.
+
+ * tools/testing/selftests/cpu-hotplug/on-off-test.sh
+ * tools/testing/selftests/memory-hotplug/on-off-test.sh
+
+These scripts first do simple online and offline tests and then do fault
+injection tests if notifier error injection module is available.
index 76112dac76592f4454fbdb20d43437079aef6c92..afaff312bf415acb59449aeb9bee4c848c197ff2 100644 (file)
@@ -13,6 +13,14 @@ Who: Jim Cromie <jim.cromie@gmail.com>, Jason Baron <jbaron@redhat.com>
 
 ---------------------------
 
+What: /proc/sys/vm/nr_pdflush_threads
+When: 2012
+Why: Since pdflush is deprecated, the interface exported in /proc/sys/vm/
+     should be removed.
+Who: Wanpeng Li <liwp@linux.vnet.ibm.com>
+
+---------------------------
+
 What:  CONFIG_APM_CPU_IDLE, and its ability to call APM BIOS in idle
 When:  2012
 Why:   This optional sub-feature of APM is of dubious reliability,
@@ -70,20 +78,6 @@ Who: Luis R. Rodriguez <lrodriguez@atheros.com>
 
 ---------------------------
 
-What:  IRQF_SAMPLE_RANDOM
-Check: IRQF_SAMPLE_RANDOM
-When:  July 2009
-
-Why:   Many of IRQF_SAMPLE_RANDOM users are technically bogus as entropy
-       sources in the kernel's current entropy model. To resolve this, every
-       input point to the kernel's entropy pool needs to better document the
-       type of entropy source it actually is. This will be replaced with
-       additional add_*_randomness functions in drivers/char/random.c
-
-Who:   Robin Getz <rgetz@blackfin.uclinux.org> & Matt Mackall <mpm@selenic.com>
-
----------------------------
-
 What:  The ieee80211_regdom module parameter
 When:  March 2010 / desktop catchup
 
@@ -600,3 +594,46 @@ When:      June 2013
 Why:   Unsupported/unmaintained/unused since 2.6
 
 ----------------------------
+
+What:  V4L2 selections API target rectangle and flags unification, the
+       following definitions will be removed: V4L2_SEL_TGT_CROP_ACTIVE,
+       V4L2_SEL_TGT_COMPOSE_ACTIVE, V4L2_SUBDEV_SEL_*, V4L2_SUBDEV_SEL_FLAG_*
+       in favor of common V4L2_SEL_TGT_* and V4L2_SEL_FLAG_* definitions.
+       For more details see include/linux/v4l2-common.h.
+When:  3.8
+Why:   The regular V4L2 selections and the subdev selection API originally
+       defined distinct names for the target rectangles and flags - V4L2_SEL_*
+       and V4L2_SUBDEV_SEL_*. Although, it turned out that the meaning of these
+       target rectangles is virtually identical and the APIs were consolidated
+       to use single set of names - V4L2_SEL_*. This didn't involve any ABI
+       changes. Alias definitions were created for the original ones to avoid
+       any instabilities in the user space interface. After few cycles these
+       backward compatibility definitions will be removed.
+Who:   Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
+
+----------------------------
+
+What:  Using V4L2_CAP_VIDEO_CAPTURE and V4L2_CAP_VIDEO_OUTPUT flags
+       to indicate a V4L2 memory-to-memory device capability
+When:  3.8
+Why:   New drivers should use new V4L2_CAP_VIDEO_M2M capability flag
+       to indicate a V4L2 video memory-to-memory (M2M) device and
+       applications can now identify a M2M video device by checking
+       for V4L2_CAP_VIDEO_M2M, with VIDIOC_QUERYCAP ioctl. Using ORed
+       V4L2_CAP_VIDEO_CAPTURE and V4L2_CAP_VIDEO_OUTPUT flags for M2M
+       devices is ambiguous and may lead, for example, to identifying
+       a M2M device as a video capture or output device.
+Who:   Sylwester Nawrocki <s.nawrocki@samsung.com>
+
+----------------------------
+
+What:  OMAP private DMA implementation
+When:  2013
+Why:   We have a DMA engine implementation; all users should be updated
+       to use this rather than persisting with the old APIs.  The old APIs
+       block merging the old DMA engine implementation into the DMA
+       engine driver.
+Who:   Russell King <linux@arm.linux.org.uk>,
+       Santosh Shilimkar <santosh.shilimkar@ti.com>
+
+----------------------------
index e0cce2a5f820a6327f9ea1558717434c490e8aae..0f103e39b4f6a025cb486cbdfb21126c78992738 100644 (file)
@@ -138,8 +138,8 @@ evict_inode:
 put_super:             write
 write_super:           read
 sync_fs:               read
-freeze_fs:             read
-unfreeze_fs:           read
+freeze_fs:             write
+unfreeze_fs:           write
 statfs:                        maybe(read)     (see below)
 remount_fs:            write
 umount_begin:          no
@@ -206,6 +206,8 @@ prototypes:
        int (*launder_page)(struct page *);
        int (*is_partially_uptodate)(struct page *, read_descriptor_t *, unsigned long);
        int (*error_remove_page)(struct address_space *, struct page *);
+       int (*swap_activate)(struct file *);
+       int (*swap_deactivate)(struct file *);
 
 locking rules:
        All except set_page_dirty and freepage may block
@@ -229,6 +231,8 @@ migratepage:                yes (both)
 launder_page:          yes
 is_partially_uptodate: yes
 error_remove_page:     yes
+swap_activate:         no
+swap_deactivate:       no
 
        ->write_begin(), ->write_end(), ->sync_page() and ->readpage()
 may be called from the request handler (/dev/loop).
@@ -330,6 +334,15 @@ cleaned, or an error value if not. Note that in order to prevent the page
 getting mapped back in and redirtied, it needs to be kept locked
 across the entire operation.
 
+       ->swap_activate will be called with a non-zero argument on
+files backing (non block device backed) swapfiles. A return value
+of zero indicates success, in which case this file can be used for
+backing swapspace. The swapspace operations will be proxied to the
+address space operations.
+
+       ->swap_deactivate() will be called in the sys_swapoff()
+path after ->swap_activate() returned success.
+
 ----------------------- file_lock_operations ------------------------------
 prototypes:
        void (*fl_copy_lock)(struct file_lock *, struct file_lock *);
@@ -346,7 +359,6 @@ prototypes:
        int (*lm_compare_owner)(struct file_lock *, struct file_lock *);
        void (*lm_notify)(struct file_lock *);  /* unblock callback */
        int (*lm_grant)(struct file_lock *, struct file_lock *, int);
-       void (*lm_release_private)(struct file_lock *);
        void (*lm_break)(struct file_lock *); /* break_lease callback */
        int (*lm_change)(struct file_lock **, int);
 
@@ -355,7 +367,6 @@ locking rules:
 lm_compare_owner:      yes             no
 lm_notify:             yes             no
 lm_grant:              no              no
-lm_release_private:    maybe           no
 lm_break:              yes             no
 lm_change              yes             no
 
index aa754e01464e1c71f55869da9d5222ef763518d7..065aa2dc083538dcf05a9e2863e8b14fddb9b514 100644 (file)
@@ -592,6 +592,8 @@ struct address_space_operations {
        int (*migratepage) (struct page *, struct page *);
        int (*launder_page) (struct page *);
        int (*error_remove_page) (struct mapping *mapping, struct page *page);
+       int (*swap_activate)(struct file *);
+       int (*swap_deactivate)(struct file *);
 };
 
   writepage: called by the VM to write a dirty page to backing store.
@@ -760,6 +762,16 @@ struct address_space_operations {
        Setting this implies you deal with pages going away under you,
        unless you have them locked or reference counts increased.
 
+  swap_activate: Called when swapon is used on a file to allocate
+       space if necessary and pin the block lookup information in
+       memory. A return value of zero indicates success,
+       in which case this file can be used to back swapspace. The
+       swapspace operations will be proxied to this address space's
+       ->swap_{out,in} methods.
+
+  swap_deactivate: Called during swapoff on files where swap_activate
+       was successful.
+
 
 The File Object
 ===============
diff --git a/Documentation/input/edt-ft5x06.txt b/Documentation/input/edt-ft5x06.txt
new file mode 100644 (file)
index 0000000..2032f0b
--- /dev/null
@@ -0,0 +1,54 @@
+EDT ft5x06 based Polytouch devices
+----------------------------------
+
+The edt-ft5x06 driver is useful for the EDT "Polytouch" family of capacitive
+touch screens. Note that it is *not* suitable for other devices based on the
+focaltec ft5x06 devices, since they contain vendor-specific firmware. In
+particular this driver is not suitable for the Nook tablet.
+
+It has been tested with the following devices:
+  * EP0350M06
+  * EP0430M06
+  * EP0570M06
+  * EP0700M06
+
+The driver allows configuration of the touch screen via a set of sysfs files:
+
+/sys/class/input/eventX/device/device/threshold:
+    allows setting the "click"-threshold in the range from 20 to 80.
+
+/sys/class/input/eventX/device/device/gain:
+    allows setting the sensitivity in the range from 0 to 31. Note that
+    lower values indicate higher sensitivity.
+
+/sys/class/input/eventX/device/device/offset:
+    allows setting the edge compensation in the range from 0 to 31.
+
+/sys/class/input/eventX/device/device/report_rate:
+    allows setting the report rate in the range from 3 to 14.
+
+
+For debugging purposes the driver provides a few files in the debug
+filesystem (if available in the kernel). In /sys/kernel/debug/edt_ft5x06
+you'll find the following files:
+
+num_x, num_y:
+    (readonly) contains the number of sensor fields in X- and
+    Y-direction.
+
+mode:
+    allows switching the sensor between "factory mode" and "operation
+    mode" by writing "1" or "0" to it. In factory mode (1) it is
+    possible to get the raw data from the sensor. Note that in factory
+    mode regular events don't get delivered and the options described
+    above are unavailable.
+
+raw_data:
+    contains num_x * num_y big endian 16 bit values describing the raw
+    values for each sensor field. Note that each read() call on this
+    files triggers a new readout. It is recommended to provide a buffer
+    big enough to contain num_x * num_y * 2 bytes.
+
+Note that reading raw_data gives a I/O error when the device is not in factory
+mode. The same happens when reading/writing to the parameter files when the
+device is not in regular operation mode.
index 915f28c470e94644f24ef842abb68ba7cbed97af..849b771c5e03620799cbdce625e16bab7ffa275b 100644 (file)
@@ -88,6 +88,7 @@ Code  Seq#(hex)       Include File            Comments
                and kernel/power/user.c
 '8'    all                             SNP8023 advanced NIC card
                                        <mailto:mcr@solidum.com>
+';'    64-7F   linux/vfio.h
 '@'    00-0F   linux/radeonfb.h        conflict!
 '@'    00-0F   drivers/video/aty/aty128fb.c    conflict!
 'A'    00-1F   linux/apm_bios.h        conflict!
index c2619ef44a720d14bf23d9c842692b92d7b7a9ba..ad7e2e5088c126ce48e0362d1f3296f3e8b55d2b 100644 (file)
@@ -526,7 +526,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 
        coherent_pool=nn[KMG]   [ARM,KNL]
                        Sets the size of memory pool for coherent, atomic dma
-                       allocations if Contiguous Memory Allocator (CMA) is used.
+                       allocations, by default set to 256K.
 
        code_bytes      [X86] How many bytes of object code to print
                        in an oops report.
index 211831d4095fef63eacb13a2dbde8d647b5499a6..2f0ddc15b5ac3621a4caaa7933cfbc656b47016b 100644 (file)
@@ -112,14 +112,24 @@ CHARGE_COUNTER - the current charge counter (in ÂµAh).  This could easily
 be negative; there is no empty or full value.  It is only useful for
 relative, time-based measurements.
 
+CONSTANT_CHARGE_CURRENT - constant charge current programmed by charger.
+
+CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger.
+
 ENERGY_FULL, ENERGY_EMPTY - same as above but for energy.
 
 CAPACITY - capacity in percents.
+CAPACITY_ALERT_MIN - minimum capacity alert value in percents.
+CAPACITY_ALERT_MAX - maximum capacity alert value in percents.
 CAPACITY_LEVEL - capacity level. This corresponds to
 POWER_SUPPLY_CAPACITY_LEVEL_*.
 
 TEMP - temperature of the power supply.
+TEMP_ALERT_MIN - minimum battery temperature alert value in milli centigrade.
+TEMP_ALERT_MAX - maximum battery temperature alert value in milli centigrade.
 TEMP_AMBIENT - ambient temperature.
+TEMP_AMBIENT_ALERT_MIN - minimum ambient temperature alert value in milli centigrade.
+TEMP_AMBIENT_ALERT_MAX - maximum ambient temperature alert value in milli centigrade.
 
 TIME_TO_EMPTY - seconds left for battery to be considered empty (i.e.
 while battery powers a load)
index 5df176ed59b826e8cbeaca7c96d6ed6d06759fa8..7561d7ed8e11ef25a9a1fb479492148b6f038d5d 100644 (file)
@@ -53,9 +53,20 @@ Struct Resources:
        For printing struct resources. The 'R' and 'r' specifiers result in a
        printed resource with ('R') or without ('r') a decoded flags member.
 
+Raw buffer as a hex string:
+       %*ph    00 01 02  ...  3f
+       %*phC   00:01:02: ... :3f
+       %*phD   00-01-02- ... -3f
+       %*phN   000102 ... 3f
+
+       For printing a small buffers (up to 64 bytes long) as a hex string with
+       certain separator. For the larger buffers consider to use
+       print_hex_dump().
+
 MAC/FDDI addresses:
 
        %pM     00:01:02:03:04:05
+       %pMR    05:04:03:02:01:00
        %pMF    00-01-02-03-04-05
        %pm     000102030405
 
@@ -67,6 +78,10 @@ MAC/FDDI addresses:
        the 'M' specifier to use dash ('-') separators instead of the default
        separator.
 
+       For Bluetooth addresses the 'R' specifier shall be used after the 'M'
+       specifier to use reversed byte order suitable for visual interpretation
+       of Bluetooth addresses which are in the little endian order.
+
 IPv4 addresses:
 
        %pI4    1.2.3.4
diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
new file mode 100644 (file)
index 0000000..554290e
--- /dev/null
@@ -0,0 +1,76 @@
+Pulse Width Modulation (PWM) interface
+
+This provides an overview about the Linux PWM interface
+
+PWMs are commonly used for controlling LEDs, fans or vibrators in
+cell phones. PWMs with a fixed purpose have no need implementing
+the Linux PWM API (although they could). However, PWMs are often
+found as discrete devices on SoCs which have no fixed purpose. It's
+up to the board designer to connect them to LEDs or fans. To provide
+this kind of flexibility the generic PWM API exists.
+
+Identifying PWMs
+----------------
+
+Users of the legacy PWM API use unique IDs to refer to PWM devices.
+
+Instead of referring to a PWM device via its unique ID, board setup code
+should instead register a static mapping that can be used to match PWM
+consumers to providers, as given in the following example:
+
+       static struct pwm_lookup board_pwm_lookup[] = {
+               PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight", NULL),
+       };
+
+       static void __init board_init(void)
+       {
+               ...
+               pwm_add_table(board_pwm_lookup, ARRAY_SIZE(board_pwm_lookup));
+               ...
+       }
+
+Using PWMs
+----------
+
+Legacy users can request a PWM device using pwm_request() and free it
+after usage with pwm_free().
+
+New users should use the pwm_get() function and pass to it the consumer
+device or a consumer name. pwm_put() is used to free the PWM device.
+
+After being requested a PWM has to be configured using:
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
+
+To start/stop toggling the PWM output use pwm_enable()/pwm_disable().
+
+Implementing a PWM driver
+-------------------------
+
+Currently there are two ways to implement pwm drivers. Traditionally
+there only has been the barebone API meaning that each driver has
+to implement the pwm_*() functions itself. This means that it's impossible
+to have multiple PWM drivers in the system. For this reason it's mandatory
+for new drivers to use the generic PWM framework.
+
+A new PWM controller/chip can be added using pwmchip_add() and removed
+again with pwmchip_remove(). pwmchip_add() takes a filled in struct
+pwm_chip as argument which provides a description of the PWM chip, the
+number of PWM devices provider by the chip and the chip-specific
+implementation of the supported PWM operations to the framework.
+
+Locking
+-------
+
+The PWM core list manipulations are protected by a mutex, so pwm_request()
+and pwm_free() may not be called from an atomic context. Currently the
+PWM core does not enforce any locking to pwm_enable(), pwm_disable() and
+pwm_config(), so the calling context is currently driver specific. This
+is an issue derived from the former barebone API and should be fixed soon.
+
+Helpers
+-------
+
+Currently a PWM can only be configured with period_ns and duty_ns. For several
+use cases freq_hz and duty_percent might be better. Instead of calculating
+this in your driver please consider adding appropriate helpers to the framework.
index 7456360e161cbe2962eb4850f05ef0cf45c30657..a92bba8168435bc731ce8db34530d55639fa8def 100644 (file)
@@ -53,6 +53,7 @@ ALC882/883/885/888/889
   acer-aspire-8930g    Acer Aspire 8330G/6935G
   acer-aspire          Acer Aspire others
   inv-dmic     Inverted internal mic workaround
+  no-primary-hp                VAIO Z workaround (for fixed speaker DAC)
 
 ALC861/660
 ==========
@@ -273,6 +274,10 @@ STAC92HD83*
   dell-s14     Dell laptop
   dell-vostro-3500     Dell Vostro 3500 laptop
   hp-dv7-4000  HP dv-7 4000
+  hp_cNB11_intquad HP CNB models with 4 speakers
+  hp-zephyr    HP Zephyr
+  hp-led       HP with broken BIOS for mute LED
+  hp-inv-led   HP with broken BIOS for inverted mute LED
   auto         BIOS setup (default)
 
 STAC9872
index 13d6166d7a2798fbd54b39a90b533ad5ddebe9eb..88152f214f48cb69c643d4bf2ff2ac9a61ad2eb0 100644 (file)
@@ -32,6 +32,8 @@ Currently, these files are in /proc/sys/fs:
 - nr_open
 - overflowuid
 - overflowgid
+- protected_hardlinks
+- protected_symlinks
 - suid_dumpable
 - super-max
 - super-nr
@@ -157,22 +159,68 @@ The default is 65534.
 
 ==============================================================
 
+protected_hardlinks:
+
+A long-standing class of security issues is the hardlink-based
+time-of-check-time-of-use race, most commonly seen in world-writable
+directories like /tmp. The common method of exploitation of this flaw
+is to cross privilege boundaries when following a given hardlink (i.e. a
+root process follows a hardlink created by another user). Additionally,
+on systems without separated partitions, this stops unauthorized users
+from "pinning" vulnerable setuid/setgid files against being upgraded by
+the administrator, or linking to special files.
+
+When set to "0", hardlink creation behavior is unrestricted.
+
+When set to "1" hardlinks cannot be created by users if they do not
+already own the source file, or do not have read/write access to it.
+
+This protection is based on the restrictions in Openwall and grsecurity.
+
+==============================================================
+
+protected_symlinks:
+
+A long-standing class of security issues is the symlink-based
+time-of-check-time-of-use race, most commonly seen in world-writable
+directories like /tmp. The common method of exploitation of this flaw
+is to cross privilege boundaries when following a given symlink (i.e. a
+root process follows a symlink belonging to another user). For a likely
+incomplete list of hundreds of examples across the years, please see:
+http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=/tmp
+
+When set to "0", symlink following behavior is unrestricted.
+
+When set to "1" symlinks are permitted to be followed only when outside
+a sticky world-writable directory, or when the uid of the symlink and
+follower match, or when the directory owner matches the symlink's owner.
+
+This protection is based on the restrictions in Openwall and grsecurity.
+
+==============================================================
+
 suid_dumpable:
 
 This value can be used to query and set the core dump mode for setuid
 or otherwise protected/tainted binaries. The modes are
 
 0 - (default) - traditional behaviour. Any process which has changed
-       privilege levels or is execute only will not be dumped
+       privilege levels or is execute only will not be dumped.
 1 - (debug) - all processes dump core when possible. The core dump is
        owned by the current user and no security is applied. This is
        intended for system debugging situations only. Ptrace is unchecked.
+       This is insecure as it allows regular users to examine the memory
+       contents of privileged processes.
 2 - (suidsafe) - any binary which normally would not be dumped is dumped
-       readable by root only. This allows the end user to remove
-       such a dump but not access it directly. For security reasons
-       core dumps in this mode will not overwrite one another or
-       other files. This mode is appropriate when administrators are
-       attempting to debug problems in a normal environment.
+       anyway, but only if the "core_pattern" kernel sysctl is set to
+       either a pipe handler or a fully qualified path. (For more details
+       on this limitation, see CVE-2006-2451.) This mode is appropriate
+       when administrators are attempting to debug problems in a normal
+       environment, and either have a core dump pipe handler that knows
+       to treat privileged core dumps with care, or specific directory
+       defined for catching core dumps. If a core dump happens without
+       a pipe handler or fully qualifid path, a message will be emitted
+       to syslog warning about the lack of a correct setting.
 
 ==============================================================
 
index 96f0ee825bed3e71fe9758156ee8b575389d7bba..dcc2a94ae34e7d4a976c8fd2f5613236c05968f3 100644 (file)
@@ -42,7 +42,6 @@ Currently, these files are in /proc/sys/vm:
 - mmap_min_addr
 - nr_hugepages
 - nr_overcommit_hugepages
-- nr_pdflush_threads
 - nr_trim_pages         (only if CONFIG_MMU=n)
 - numa_zonelist_order
 - oom_dump_tasks
@@ -426,16 +425,6 @@ See Documentation/vm/hugetlbpage.txt
 
 ==============================================================
 
-nr_pdflush_threads
-
-The current number of pdflush threads.  This value is read-only.
-The value changes according to the number of dirty pages in the system.
-
-When necessary, additional pdflush threads are created, one per second, up to
-nr_pdflush_threads_max.
-
-==============================================================
-
 nr_trim_pages
 
 This is available only on NOMMU kernels.
@@ -502,9 +491,10 @@ oom_dump_tasks
 
 Enables a system-wide task dump (excluding kernel threads) to be
 produced when the kernel performs an OOM-killing and includes such
-information as pid, uid, tgid, vm size, rss, cpu, oom_adj score, and
-name.  This is helpful to determine why the OOM killer was invoked
-and to identify the rogue task that caused it.
+information as pid, uid, tgid, vm size, rss, nr_ptes, swapents,
+oom_score_adj score, and name.  This is helpful to determine why the
+OOM killer was invoked, to identify the rogue task that caused it,
+and to determine why the OOM killer chose the task it did to kill.
 
 If this is set to zero, this information is suppressed.  On very
 large systems with thousands of tasks it may not be feasible to dump
@@ -574,16 +564,24 @@ of physical RAM.  See above.
 
 page-cluster
 
-page-cluster controls the number of pages which are written to swap in
-a single attempt.  The swap I/O size.
+page-cluster controls the number of pages up to which consecutive pages
+are read in from swap in a single attempt. This is the swap counterpart
+to page cache readahead.
+The mentioned consecutivity is not in terms of virtual/physical addresses,
+but consecutive on swap space - that means they were swapped out together.
 
 It is a logarithmic value - setting it to zero means "1 page", setting
 it to 1 means "2 pages", setting it to 2 means "4 pages", etc.
+Zero disables swap readahead completely.
 
 The default value is three (eight pages at a time).  There may be some
 small benefits in tuning this to a different value if your workload is
 swap-intensive.
 
+Lower values mean lower latencies for initial faults, but at the same time
+extra faults and I/O delays for following faults if they would have been part of
+that consecutive pages readahead would have brought in.
+
 =============================================================
 
 panic_on_oom
diff --git a/Documentation/vfio.txt b/Documentation/vfio.txt
new file mode 100644 (file)
index 0000000..0cb6685
--- /dev/null
@@ -0,0 +1,314 @@
+VFIO - "Virtual Function I/O"[1]
+-------------------------------------------------------------------------------
+Many modern system now provide DMA and interrupt remapping facilities
+to help ensure I/O devices behave within the boundaries they've been
+allotted.  This includes x86 hardware with AMD-Vi and Intel VT-d,
+POWER systems with Partitionable Endpoints (PEs) and embedded PowerPC
+systems such as Freescale PAMU.  The VFIO driver is an IOMMU/device
+agnostic framework for exposing direct device access to userspace, in
+a secure, IOMMU protected environment.  In other words, this allows
+safe[2], non-privileged, userspace drivers.
+
+Why do we want that?  Virtual machines often make use of direct device
+access ("device assignment") when configured for the highest possible
+I/O performance.  From a device and host perspective, this simply
+turns the VM into a userspace driver, with the benefits of
+significantly reduced latency, higher bandwidth, and direct use of
+bare-metal device drivers[3].
+
+Some applications, particularly in the high performance computing
+field, also benefit from low-overhead, direct device access from
+userspace.  Examples include network adapters (often non-TCP/IP based)
+and compute accelerators.  Prior to VFIO, these drivers had to either
+go through the full development cycle to become proper upstream
+driver, be maintained out of tree, or make use of the UIO framework,
+which has no notion of IOMMU protection, limited interrupt support,
+and requires root privileges to access things like PCI configuration
+space.
+
+The VFIO driver framework intends to unify these, replacing both the
+KVM PCI specific device assignment code as well as provide a more
+secure, more featureful userspace driver environment than UIO.
+
+Groups, Devices, and IOMMUs
+-------------------------------------------------------------------------------
+
+Devices are the main target of any I/O driver.  Devices typically
+create a programming interface made up of I/O access, interrupts,
+and DMA.  Without going into the details of each of these, DMA is
+by far the most critical aspect for maintaining a secure environment
+as allowing a device read-write access to system memory imposes the
+greatest risk to the overall system integrity.
+
+To help mitigate this risk, many modern IOMMUs now incorporate
+isolation properties into what was, in many cases, an interface only
+meant for translation (ie. solving the addressing problems of devices
+with limited address spaces).  With this, devices can now be isolated
+from each other and from arbitrary memory access, thus allowing
+things like secure direct assignment of devices into virtual machines.
+
+This isolation is not always at the granularity of a single device
+though.  Even when an IOMMU is capable of this, properties of devices,
+interconnects, and IOMMU topologies can each reduce this isolation.
+For instance, an individual device may be part of a larger multi-
+function enclosure.  While the IOMMU may be able to distinguish
+between devices within the enclosure, the enclosure may not require
+transactions between devices to reach the IOMMU.  Examples of this
+could be anything from a multi-function PCI device with backdoors
+between functions to a non-PCI-ACS (Access Control Services) capable
+bridge allowing redirection without reaching the IOMMU.  Topology
+can also play a factor in terms of hiding devices.  A PCIe-to-PCI
+bridge masks the devices behind it, making transaction appear as if
+from the bridge itself.  Obviously IOMMU design plays a major factor
+as well.
+
+Therefore, while for the most part an IOMMU may have device level
+granularity, any system is susceptible to reduced granularity.  The
+IOMMU API therefore supports a notion of IOMMU groups.  A group is
+a set of devices which is isolatable from all other devices in the
+system.  Groups are therefore the unit of ownership used by VFIO.
+
+While the group is the minimum granularity that must be used to
+ensure secure user access, it's not necessarily the preferred
+granularity.  In IOMMUs which make use of page tables, it may be
+possible to share a set of page tables between different groups,
+reducing the overhead both to the platform (reduced TLB thrashing,
+reduced duplicate page tables), and to the user (programming only
+a single set of translations).  For this reason, VFIO makes use of
+a container class, which may hold one or more groups.  A container
+is created by simply opening the /dev/vfio/vfio character device.
+
+On its own, the container provides little functionality, with all
+but a couple version and extension query interfaces locked away.
+The user needs to add a group into the container for the next level
+of functionality.  To do this, the user first needs to identify the
+group associated with the desired device.  This can be done using
+the sysfs links described in the example below.  By unbinding the
+device from the host driver and binding it to a VFIO driver, a new
+VFIO group will appear for the group as /dev/vfio/$GROUP, where
+$GROUP is the IOMMU group number of which the device is a member.
+If the IOMMU group contains multiple devices, each will need to
+be bound to a VFIO driver before operations on the VFIO group
+are allowed (it's also sufficient to only unbind the device from
+host drivers if a VFIO driver is unavailable; this will make the
+group available, but not that particular device).  TBD - interface
+for disabling driver probing/locking a device.
+
+Once the group is ready, it may be added to the container by opening
+the VFIO group character device (/dev/vfio/$GROUP) and using the
+VFIO_GROUP_SET_CONTAINER ioctl, passing the file descriptor of the
+previously opened container file.  If desired and if the IOMMU driver
+supports sharing the IOMMU context between groups, multiple groups may
+be set to the same container.  If a group fails to set to a container
+with existing groups, a new empty container will need to be used
+instead.
+
+With a group (or groups) attached to a container, the remaining
+ioctls become available, enabling access to the VFIO IOMMU interfaces.
+Additionally, it now becomes possible to get file descriptors for each
+device within a group using an ioctl on the VFIO group file descriptor.
+
+The VFIO device API includes ioctls for describing the device, the I/O
+regions and their read/write/mmap offsets on the device descriptor, as
+well as mechanisms for describing and registering interrupt
+notifications.
+
+VFIO Usage Example
+-------------------------------------------------------------------------------
+
+Assume user wants to access PCI device 0000:06:0d.0
+
+$ readlink /sys/bus/pci/devices/0000:06:0d.0/iommu_group
+../../../../kernel/iommu_groups/26
+
+This device is therefore in IOMMU group 26.  This device is on the
+pci bus, therefore the user will make use of vfio-pci to manage the
+group:
+
+# modprobe vfio-pci
+
+Binding this device to the vfio-pci driver creates the VFIO group
+character devices for this group:
+
+$ lspci -n -s 0000:06:0d.0
+06:0d.0 0401: 1102:0002 (rev 08)
+# echo 0000:06:0d.0 > /sys/bus/pci/devices/0000:06:0d.0/driver/unbind
+# echo 1102 0002 > /sys/bus/pci/drivers/vfio/new_id
+
+Now we need to look at what other devices are in the group to free
+it for use by VFIO:
+
+$ ls -l /sys/bus/pci/devices/0000:06:0d.0/iommu_group/devices
+total 0
+lrwxrwxrwx. 1 root root 0 Apr 23 16:13 0000:00:1e.0 ->
+       ../../../../devices/pci0000:00/0000:00:1e.0
+lrwxrwxrwx. 1 root root 0 Apr 23 16:13 0000:06:0d.0 ->
+       ../../../../devices/pci0000:00/0000:00:1e.0/0000:06:0d.0
+lrwxrwxrwx. 1 root root 0 Apr 23 16:13 0000:06:0d.1 ->
+       ../../../../devices/pci0000:00/0000:00:1e.0/0000:06:0d.1
+
+This device is behind a PCIe-to-PCI bridge[4], therefore we also
+need to add device 0000:06:0d.1 to the group following the same
+procedure as above.  Device 0000:00:1e.0 is a bridge that does
+not currently have a host driver, therefore it's not required to
+bind this device to the vfio-pci driver (vfio-pci does not currently
+support PCI bridges).
+
+The final step is to provide the user with access to the group if
+unprivileged operation is desired (note that /dev/vfio/vfio provides
+no capabilities on its own and is therefore expected to be set to
+mode 0666 by the system).
+
+# chown user:user /dev/vfio/26
+
+The user now has full access to all the devices and the iommu for this
+group and can access them as follows:
+
+       int container, group, device, i;
+       struct vfio_group_status group_status =
+                                       { .argsz = sizeof(group_status) };
+       struct vfio_iommu_x86_info iommu_info = { .argsz = sizeof(iommu_info) };
+       struct vfio_iommu_x86_dma_map dma_map = { .argsz = sizeof(dma_map) };
+       struct vfio_device_info device_info = { .argsz = sizeof(device_info) };
+
+       /* Create a new container */
+       container = open("/dev/vfio/vfio, O_RDWR);
+
+       if (ioctl(container, VFIO_GET_API_VERSION) != VFIO_API_VERSION)
+               /* Unknown API version */
+
+       if (!ioctl(container, VFIO_CHECK_EXTENSION, VFIO_X86_IOMMU))
+               /* Doesn't support the IOMMU driver we want. */
+
+       /* Open the group */
+       group = open("/dev/vfio/26", O_RDWR);
+
+       /* Test the group is viable and available */
+       ioctl(group, VFIO_GROUP_GET_STATUS, &group_status);
+
+       if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE))
+               /* Group is not viable (ie, not all devices bound for vfio) */
+
+       /* Add the group to the container */
+       ioctl(group, VFIO_GROUP_SET_CONTAINER, &container);
+
+       /* Enable the IOMMU model we want */
+       ioctl(container, VFIO_SET_IOMMU, VFIO_X86_IOMMU)
+
+       /* Get addition IOMMU info */
+       ioctl(container, VFIO_IOMMU_GET_INFO, &iommu_info);
+
+       /* Allocate some space and setup a DMA mapping */
+       dma_map.vaddr = mmap(0, 1024 * 1024, PROT_READ | PROT_WRITE,
+                            MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+       dma_map.size = 1024 * 1024;
+       dma_map.iova = 0; /* 1MB starting at 0x0 from device view */
+       dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE;
+
+       ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map);
+
+       /* Get a file descriptor for the device */
+       device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, "0000:06:0d.0");
+
+       /* Test and setup the device */
+       ioctl(device, VFIO_DEVICE_GET_INFO, &device_info);
+
+       for (i = 0; i < device_info.num_regions; i++) {
+               struct vfio_region_info reg = { .argsz = sizeof(reg) };
+
+               reg.index = i;
+
+               ioctl(device, VFIO_DEVICE_GET_REGION_INFO, &reg);
+
+               /* Setup mappings... read/write offsets, mmaps
+                * For PCI devices, config space is a region */
+       }
+
+       for (i = 0; i < device_info.num_irqs; i++) {
+               struct vfio_irq_info irq = { .argsz = sizeof(irq) };
+
+               irq.index = i;
+
+               ioctl(device, VFIO_DEVICE_GET_IRQ_INFO, &reg);
+
+               /* Setup IRQs... eventfds, VFIO_DEVICE_SET_IRQS */
+       }
+
+       /* Gratuitous device reset and go... */
+       ioctl(device, VFIO_DEVICE_RESET);
+
+VFIO User API
+-------------------------------------------------------------------------------
+
+Please see include/linux/vfio.h for complete API documentation.
+
+VFIO bus driver API
+-------------------------------------------------------------------------------
+
+VFIO bus drivers, such as vfio-pci make use of only a few interfaces
+into VFIO core.  When devices are bound and unbound to the driver,
+the driver should call vfio_add_group_dev() and vfio_del_group_dev()
+respectively:
+
+extern int vfio_add_group_dev(struct iommu_group *iommu_group,
+                              struct device *dev,
+                              const struct vfio_device_ops *ops,
+                              void *device_data);
+
+extern void *vfio_del_group_dev(struct device *dev);
+
+vfio_add_group_dev() indicates to the core to begin tracking the
+specified iommu_group and register the specified dev as owned by
+a VFIO bus driver.  The driver provides an ops structure for callbacks
+similar to a file operations structure:
+
+struct vfio_device_ops {
+       int     (*open)(void *device_data);
+       void    (*release)(void *device_data);
+       ssize_t (*read)(void *device_data, char __user *buf,
+                       size_t count, loff_t *ppos);
+       ssize_t (*write)(void *device_data, const char __user *buf,
+                        size_t size, loff_t *ppos);
+       long    (*ioctl)(void *device_data, unsigned int cmd,
+                        unsigned long arg);
+       int     (*mmap)(void *device_data, struct vm_area_struct *vma);
+};
+
+Each function is passed the device_data that was originally registered
+in the vfio_add_group_dev() call above.  This allows the bus driver
+an easy place to store its opaque, private data.  The open/release
+callbacks are issued when a new file descriptor is created for a
+device (via VFIO_GROUP_GET_DEVICE_FD).  The ioctl interface provides
+a direct pass through for VFIO_DEVICE_* ioctls.  The read/write/mmap
+interfaces implement the device region access defined by the device's
+own VFIO_DEVICE_GET_REGION_INFO ioctl.
+
+-------------------------------------------------------------------------------
+
+[1] VFIO was originally an acronym for "Virtual Function I/O" in its
+initial implementation by Tom Lyon while as Cisco.  We've since
+outgrown the acronym, but it's catchy.
+
+[2] "safe" also depends upon a device being "well behaved".  It's
+possible for multi-function devices to have backdoors between
+functions and even for single function devices to have alternative
+access to things like PCI config space through MMIO registers.  To
+guard against the former we can include additional precautions in the
+IOMMU driver to group multi-function PCI devices together
+(iommu=group_mf).  The latter we can't prevent, but the IOMMU should
+still provide isolation.  For PCI, SR-IOV Virtual Functions are the
+best indicator of "well behaved", as these are designed for
+virtualization usage models.
+
+[3] As always there are trade-offs to virtual machine device
+assignment that are beyond the scope of VFIO.  It's expected that
+future IOMMU technologies will reduce some, but maybe not all, of
+these trade-offs.
+
+[4] In this case the device is below a PCI bridge, so transactions
+from either function of the device are indistinguishable to the iommu:
+
+-[0000:00]-+-1e.0-[06]--+-0d.0
+                        \-0d.1
+
+00:1e.0 PCI bridge: Intel Corporation 82801 PCI Bridge (rev 90)
index 7b59e953c4bfe8673b7d65314fe9e65feb3676ef..a8a65753e544c7df7b4098cba8394f96f605a4ab 100644 (file)
@@ -3,4 +3,4 @@
   2 -> Hauppauge HVR850                         (au0828)        [2040:7240]
   3 -> DViCO FusionHDTV USB                     (au0828)        [0fe9:d620]
   4 -> Hauppauge HVR950Q rev xxF8               (au0828)        [2040:7201,2040:7211,2040:7281]
-  5 -> Hauppauge Woodbury                       (au0828)        [2040:8200]
+  5 -> Hauppauge Woodbury                       (au0828)        [05e1:0480,2040:8200]
index b753906c71830b892d95b623c857a767ba885571..581f666a76cfc9b4200053f347b257756ee1a6c6 100644 (file)
 158 -> Geovision GV-800(S) (slave)                         [800b:763d,800c:763d,800d:763d]
 159 -> ProVideo PV183                                      [1830:1540,1831:1540,1832:1540,1833:1540,1834:1540,1835:1540,1836:1540,1837:1540]
 160 -> Tongwei Video Technology TD-3116                    [f200:3116]
+161 -> Aposonic W-DVR                                      [0279:0228]
index f316d1816fcd2265acb9a0b9efdb3f1e56160ac3..652aecd131998a7d2958a6a7cc2978e0eeeaa9ae 100644 (file)
@@ -18,7 +18,7 @@
  17 -> NetUP Dual DVB-S2 CI                                [1b55:2a2c]
  18 -> Hauppauge WinTV-HVR1270                             [0070:2211]
  19 -> Hauppauge WinTV-HVR1275                             [0070:2215,0070:221d,0070:22f2]
- 20 -> Hauppauge WinTV-HVR1255                             [0070:2251,0070:2259,0070:22f1]
+ 20 -> Hauppauge WinTV-HVR1255                             [0070:2251,0070:22f1]
  21 -> Hauppauge WinTV-HVR1210                             [0070:2291,0070:2295,0070:2299,0070:229d,0070:22f0,0070:22f3,0070:22f4,0070:22f5]
  22 -> Mygica X8506 DMB-TH                                 [14f1:8651]
  23 -> Magic-Pro ProHDTV Extreme 2                         [14f1:8657]
@@ -33,3 +33,5 @@
  32 -> MPX-885
  33 -> Mygica X8507                                        [14f1:8502]
  34 -> TerraTec Cinergy T PCIe Dual                        [153b:117e]
+ 35 -> TeVii S471                                          [d471:9022]
+ 36 -> Hauppauge WinTV-HVR1255                             [0070:2259]
index 34f3b330e5f40033c128364c068b6895f10e307a..94d9025aa82d97306bd44f79368d48cb8cfa4acc 100644 (file)
 187 -> Beholder BeholdTV 503 FM                 [5ace:5030]
 188 -> Sensoray 811/911                         [6000:0811,6000:0911]
 189 -> Kworld PC150-U                           [17de:a134]
+190 -> Asus My Cinema PS3-100                   [1043:48cd]
index 1f590527005064b677830745ad10eac22c1aa92f..89318be6c1d2117581b0dfbb5fc478370c8e0f5a 100644 (file)
@@ -594,6 +594,15 @@ You should also set these fields:
   unlocked_ioctl file operation is called this lock will be taken by the
   core and released afterwards. See the next section for more details.
 
+- queue: a pointer to the struct vb2_queue associated with this device node.
+  If queue is non-NULL, and queue->lock is non-NULL, then queue->lock is
+  used for the queuing ioctls (VIDIOC_REQBUFS, CREATE_BUFS, QBUF, DQBUF,
+  QUERYBUF, PREPARE_BUF, STREAMON and STREAMOFF) instead of the lock above.
+  That way the vb2 queuing framework does not have to wait for other ioctls.
+  This queue pointer is also used by the vb2 helper functions to check for
+  queuing ownership (i.e. is the filehandle calling it allowed to do the
+  operation).
+
 - prio: keeps track of the priorities. Used to implement VIDIOC_G/S_PRIORITY.
   If left to NULL, then it will use the struct v4l2_prio_state in v4l2_device.
   If you want to have a separate priority state per (group of) device node(s),
@@ -647,47 +656,43 @@ manually set the struct media_entity type and name fields.
 A reference to the entity will be automatically acquired/released when the
 video device is opened/closed.
 
-v4l2_file_operations and locking
---------------------------------
-
-You can set a pointer to a mutex_lock in struct video_device. Usually this
-will be either a top-level mutex or a mutex per device node. By default this
-lock will be used for unlocked_ioctl, but you can disable locking for
-selected ioctls by calling:
-
-       void v4l2_disable_ioctl_locking(struct video_device *vdev, unsigned int cmd);
-
-E.g.: v4l2_disable_ioctl_locking(vdev, VIDIOC_DQBUF);
+ioctls and locking
+------------------
 
-You have to call this before you register the video_device.
+The V4L core provides optional locking services. The main service is the
+lock field in struct video_device, which is a pointer to a mutex. If you set
+this pointer, then that will be used by unlocked_ioctl to serialize all ioctls.
 
-Particularly with USB drivers where certain commands such as setting controls
-can take a long time you may want to do your own locking for the buffer queuing
-ioctls.
+If you are using the videobuf2 framework, then there is a second lock that you
+can set: video_device->queue->lock. If set, then this lock will be used instead
+of video_device->lock to serialize all queuing ioctls (see the previous section
+for the full list of those ioctls).
 
-If you want still finer-grained locking then you have to set mutex_lock to NULL
-and do you own locking completely.
+The advantage of using a different lock for the queuing ioctls is that for some
+drivers (particularly USB drivers) certain commands such as setting controls
+can take a long time, so you want to use a separate lock for the buffer queuing
+ioctls. That way your VIDIOC_DQBUF doesn't stall because the driver is busy
+changing the e.g. exposure of the webcam.
 
-It is up to the driver developer to decide which method to use. However, if
-your driver has high-latency operations (for example, changing the exposure
-of a USB webcam might take a long time), then you might be better off with
-doing your own locking if you want to allow the user to do other things with
-the device while waiting for the high-latency command to finish.
+Of course, you can always do all the locking yourself by leaving both lock
+pointers at NULL.
 
-If a lock is specified then all ioctl commands will be serialized on that
-lock. If you use videobuf then you must pass the same lock to the videobuf
-queue initialize function: if videobuf has to wait for a frame to arrive, then
-it will temporarily unlock the lock and relock it afterwards. If your driver
-also waits in the code, then you should do the same to allow other processes
-to access the device node while the first process is waiting for something.
+If you use the old videobuf then you must pass the video_device lock to the
+videobuf queue initialize function: if videobuf has to wait for a frame to
+arrive, then it will temporarily unlock the lock and relock it afterwards. If
+your driver also waits in the code, then you should do the same to allow other
+processes to access the device node while the first process is waiting for
+something.
 
 In the case of videobuf2 you will need to implement the wait_prepare and
-wait_finish callbacks to unlock/lock if applicable. In particular, if you use
-the lock in struct video_device then you must unlock/lock this mutex in
-wait_prepare and wait_finish.
-
-The implementation of a hotplug disconnect should also take the lock before
-calling v4l2_device_disconnect.
+wait_finish callbacks to unlock/lock if applicable. If you use the queue->lock
+pointer, then you can use the helper functions vb2_ops_wait_prepare/finish.
+
+The implementation of a hotplug disconnect should also take the lock from
+video_device before calling v4l2_device_disconnect. If you are also using
+video_device->queue->lock, then you have to first lock video_device->queue->lock
+followed by video_device->lock. That way you can be sure no ioctl is running
+when you call v4l2_device_disconnect.
 
 video_device registration
 -------------------------
index bd451649f13a2ee9527bda181cfa002c77c526f4..94b823f71e944d1f8805815c2b16b5bca437d3b7 100644 (file)
@@ -1789,15 +1789,16 @@ F:      arch/powerpc/oprofile/*cell*
 F:     arch/powerpc/platforms/cell/
 
 CEPH DISTRIBUTED FILE SYSTEM CLIENT
-M:     Sage Weil <sage@newdream.net>
+M:     Sage Weil <sage@inktank.com>
 L:     ceph-devel@vger.kernel.org
-W:     http://ceph.newdream.net/
+W:     http://ceph.com/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git
 S:     Supported
 F:     Documentation/filesystems/ceph.txt
 F:     fs/ceph
 F:     net/ceph
 F:     include/linux/ceph
+F:     include/linux/crush
 
 CERTIFIED WIRELESS USB (WUSB) SUBSYSTEM:
 L:     linux-usb@vger.kernel.org
@@ -2750,6 +2751,7 @@ M:        Jingoo Han <jg1.han@samsung.com>
 L:     linux-fbdev@vger.kernel.org
 S:     Maintained
 F:     drivers/video/exynos/exynos_dp*
+F:     include/video/exynos_dp*
 
 EXYNOS MIPI DISPLAY DRIVERS
 M:     Inki Dae <inki.dae@samsung.com>
@@ -3155,8 +3157,7 @@ S:        Maintained
 F:     drivers/media/video/gspca/t613.c
 
 GSPCA USB WEBCAM DRIVER
-M:     Jean-Francois Moine <moinejf@free.fr>
-W:     http://moinejf.free.fr
+M:     Hans de Goede <hdegoede@redhat.com>
 L:     linux-media@vger.kernel.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
 S:     Maintained
@@ -5526,6 +5527,18 @@ S:       Maintained
 F:     Documentation/video4linux/README.pvrusb2
 F:     drivers/media/video/pvrusb2/
 
+PWM SUBSYSTEM
+M:     Thierry Reding <thierry.reding@avionic-design.de>
+L:     linux-kernel@vger.kernel.org
+S:     Maintained
+W:     http://gitorious.org/linux-pwm
+T:     git git://gitorious.org/linux-pwm/linux-pwm.git
+F:     Documentation/pwm.txt
+F:     Documentation/devicetree/bindings/pwm/
+F:     include/linux/pwm.h
+F:     include/linux/of_pwm.h
+F:     drivers/pwm/
+
 PXA2xx/PXA3xx SUPPORT
 M:     Eric Miao <eric.y.miao@gmail.com>
 M:     Russell King <linux@arm.linux.org.uk>
@@ -5627,10 +5640,12 @@ S:      Supported
 F:     arch/hexagon/
 
 RADOS BLOCK DEVICE (RBD)
-F:     include/linux/qnxtypes.h
-M:     Yehuda Sadeh <yehuda@hq.newdream.net>
-M:     Sage Weil <sage@newdream.net>
+M:     Yehuda Sadeh <yehuda@inktank.com>
+M:     Sage Weil <sage@inktank.com>
+M:     Alex Elder <elder@inktank.com>
 M:     ceph-devel@vger.kernel.org
+W:     http://ceph.com/
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git
 S:     Supported
 F:     drivers/block/rbd.c
 F:     drivers/block/rbd_types.h
@@ -5667,7 +5682,7 @@ F:        Documentation/blockdev/ramdisk.txt
 F:     drivers/block/brd.c
 
 RANDOM NUMBER DRIVER
-M:     Matt Mackall <mpm@selenic.com>
+M:     Theodore Ts'o" <tytso@mit.edu>
 S:     Maintained
 F:     drivers/char/random.c
 
@@ -5900,6 +5915,16 @@ L:       linux-fbdev@vger.kernel.org
 S:     Maintained
 F:     drivers/video/s3c-fb.c
 
+SAMSUNG MULTIFUNCTION DEVICE DRIVERS
+M:     Sangbeom Kim <sbkim73@samsung.com>
+L:     linux-kernel@vger.kernel.org
+S:     Supported
+F:     drivers/mfd/sec*.c
+F:     drivers/regulator/s2m*.c
+F:     drivers/regulator/s5m*.c
+F:     drivers/rtc/rtc-sec.c
+F:     include/linux/mfd/samsung/
+
 SERIAL DRIVERS
 M:     Alan Cox <alan@linux.intel.com>
 L:     linux-serial@vger.kernel.org
@@ -7357,6 +7382,7 @@ W:        http://user-mode-linux.sourceforge.net
 S:     Maintained
 F:     Documentation/virtual/uml/
 F:     arch/um/
+F:     arch/x86/um/
 F:     fs/hostfs/
 F:     fs/hppfs/
 
@@ -7389,6 +7415,14 @@ S:       Maintained
 F:     Documentation/filesystems/vfat.txt
 F:     fs/fat/
 
+VFIO DRIVER
+M:     Alex Williamson <alex.williamson@redhat.com>
+L:     kvm@vger.kernel.org
+S:     Maintained
+F:     Documentation/vfio.txt
+F:     drivers/vfio/
+F:     include/linux/vfio.h
+
 VIDEOBUF2 FRAMEWORK
 M:     Pawel Osciak <pawel@osciak.com>
 M:     Marek Szyprowski <m.szyprowski@samsung.com>
index 4bb09e1b1230d33d9328c2a67ba32aff22ebde7d..8e4c0a7d402b02376263611a10fc26053e0ba9fa 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -535,11 +535,11 @@ PHONY += include/config/auto.conf
 
 include/config/auto.conf:
        $(Q)test -e include/generated/autoconf.h -a -e $@ || (          \
-       echo;                                                           \
-       echo "  ERROR: Kernel configuration is invalid.";               \
-       echo "         include/generated/autoconf.h or $@ are missing.";\
-       echo "         Run 'make oldconfig && make prepare' on kernel src to fix it.";  \
-       echo;                                                           \
+       echo >&2;                                                       \
+       echo >&2 "  ERROR: Kernel configuration is invalid.";           \
+       echo >&2 "         include/generated/autoconf.h or $@ are missing.";\
+       echo >&2 "         Run 'make oldconfig && make prepare' on kernel src to fix it.";      \
+       echo >&2 ;                                                      \
        /bin/false)
 
 endif # KBUILD_EXTMOD
@@ -796,8 +796,8 @@ prepare3: include/config/kernel.release
 ifneq ($(KBUILD_SRC),)
        @$(kecho) '  Using $(srctree) as source for kernel'
        $(Q)if [ -f $(srctree)/.config -o -d $(srctree)/include/config ]; then \
-               echo "  $(srctree) is not clean, please run 'make mrproper'"; \
-               echo "  in the '$(srctree)' directory.";\
+               echo >&2 "  $(srctree) is not clean, please run 'make mrproper'"; \
+               echo >&2 "  in the '$(srctree)' directory.";\
                /bin/false; \
        fi;
 endif
@@ -971,11 +971,11 @@ else # CONFIG_MODULES
 # ---------------------------------------------------------------------------
 
 modules modules_install: FORCE
-       @echo
-       @echo "The present kernel configuration has modules disabled."
-       @echo "Type 'make config' and enable loadable module support."
-       @echo "Then build a kernel with module support enabled."
-       @echo
+       @echo >&2
+       @echo >&2 "The present kernel configuration has modules disabled."
+       @echo >&2 "Type 'make config' and enable loadable module support."
+       @echo >&2 "Then build a kernel with module support enabled."
+       @echo >&2
        @exit 1
 
 endif # CONFIG_MODULES
index 8c3d957fa8e2f6b7794223791136c75d6a66b412..72f2fa189cc5200bb98143c6b35de8ffc9e0aaee 100644 (file)
@@ -248,7 +248,14 @@ config HAVE_CMPXCHG_LOCAL
 config HAVE_CMPXCHG_DOUBLE
        bool
 
+config ARCH_WANT_IPC_PARSE_VERSION
+       bool
+
+config ARCH_WANT_COMPAT_IPC_PARSE_VERSION
+       bool
+
 config ARCH_WANT_OLD_COMPAT_IPC
+       select ARCH_WANT_COMPAT_IPC_PARSE_VERSION
        bool
 
 config HAVE_ARCH_SECCOMP_FILTER
index 3de74c9f961093ebbb7d87162ed6a6c3c96053d8..d5b9b5e645cc7ad6ee66984d78434b306bc1aa13 100644 (file)
@@ -14,6 +14,7 @@ config ALPHA
        select AUTO_IRQ_AFFINITY if SMP
        select GENERIC_IRQ_SHOW
        select ARCH_WANT_OPTIONAL_GPIOLIB
+       select ARCH_WANT_IPC_PARSE_VERSION
        select ARCH_HAVE_NMI_SAFE_CMPXCHG
        select GENERIC_SMP_IDLE_THREAD
        select GENERIC_CMOS_UPDATE
index d1f23b722df4feb84d7978c4984efb5ccb6a0c36..633b23b0664ab2a4a075d648d26547dbc911a65d 100644 (file)
 
 #define NR_SYSCALLS                    504
 
-#define __ARCH_WANT_IPC_PARSE_VERSION
 #define __ARCH_WANT_OLD_READDIR
 #define __ARCH_WANT_STAT64
 #define __ARCH_WANT_SYS_GETHOSTNAME
index 0435921d41c6be15370b58210d8aaaaa2866ea9a..c803fc76ae4f40a04665e5624ee8eaf823334d7b 100644 (file)
@@ -933,18 +933,6 @@ void SMC37c669_display_device_info(
  *
  *--
  */
-#if 0
-/* $INCLUDE_OPTIONS$ */
-#include    "cp$inc:platform_io.h"
-/* $INCLUDE_OPTIONS_END$ */
-#include    "cp$src:common.h"
-#include    "cp$inc:prototypes.h"
-#include    "cp$src:kernel_def.h"
-#include    "cp$src:msg_def.h"
-#include    "cp$src:smcc669_def.h"
-/* Platform-specific includes */
-#include    "cp$src:platform.h"
-#endif
 
 #ifndef TRUE
 #define TRUE 1
index fbdd8533c05daeda38181127c077cd62430972ef..7980873525b264c5b8fe327387483d241662f3a1 100644 (file)
@@ -11,6 +11,7 @@ config ARM
        select RTC_LIB
        select SYS_SUPPORTS_APM_EMULATION
        select GENERIC_ATOMIC64 if (CPU_V6 || !CPU_32v6K || !AEABI)
+       select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
        select HAVE_OPROFILE if (HAVE_PERF_EVENTS)
        select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL
        select HAVE_ARCH_KGDB
@@ -38,6 +39,7 @@ config ARM
        select GENERIC_IRQ_PROBE
        select GENERIC_IRQ_SHOW
        select GENERIC_IRQ_PROBE
+       select ARCH_WANT_IPC_PARSE_VERSION
        select HARDIRQS_SW_RESEND
        select CPU_PM if (SUSPEND || CPU_IDLE)
        select GENERIC_PCI_IOMAP
@@ -1009,7 +1011,6 @@ config ARCH_VT8500
        select ARCH_HAS_CPUFREQ
        select GENERIC_CLOCKEVENTS
        select ARCH_REQUIRE_GPIOLIB
-       select HAVE_PWM
        help
          Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
 
index 2e1cfa00c25b4af5bd81f328f775471f9ed2bee9..9fecf1ae777bac14b2770c0cf09dd601e85b73b8 100644 (file)
                        clocks = <&eclk>;
                };
 
+               memory-controller@fff00000 {
+                       compatible = "calxeda,hb-ddr-ctrl";
+                       reg = <0xfff00000 0x1000>;
+                       interrupts = <0 91 4>;
+               };
+
                ipc@fff20000 {
                        compatible = "arm,pl320", "arm,primecell";
                        reg = <0xfff20000 0x1000>;
                        };
                };
 
+               sregs@fff3c200 {
+                       compatible = "calxeda,hb-sregs-l2-ecc";
+                       reg = <0xfff3c200 0x100>;
+                       interrupts = <0 71 4  0 72 4>;
+               };
+
                dma@fff3d000 {
                        compatible = "arm,pl330", "arm,primecell";
                        reg = <0xfff3d000 0x1000>;
index 915db89e364431450347e69e93c9e73bb6b59108..787efac68da81b81f79318a0ac55817eb92c8879 100644 (file)
                                compatible = "fsl,imx28-i2c";
                                reg = <0x80058000 2000>;
                                interrupts = <111 68>;
+                               clock-frequency = <100000>;
                                status = "disabled";
                        };
 
                                compatible = "fsl,imx28-i2c";
                                reg = <0x8005a000 2000>;
                                interrupts = <110 69>;
+                               clock-frequency = <100000>;
                                status = "disabled";
                        };
 
diff --git a/arch/arm/boot/dts/r8a7740.dtsi b/arch/arm/boot/dts/r8a7740.dtsi
new file mode 100644 (file)
index 0000000..798fa35
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Device Tree Source for the r8a7740 SoC
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * 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/ "skeleton.dtsi"
+
+/ {
+       compatible = "renesas,r8a7740";
+
+       cpus {
+               cpu@0 {
+                       compatible = "arm,cortex-a9";
+               };
+       };
+};
diff --git a/arch/arm/boot/dts/sh7377.dtsi b/arch/arm/boot/dts/sh7377.dtsi
new file mode 100644 (file)
index 0000000..767ee07
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Device Tree Source for the sh7377 SoC
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * 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/ "skeleton.dtsi"
+
+/ {
+       compatible = "renesas,sh7377";
+
+       cpus {
+               cpu@0 {
+                       compatible = "arm,cortex-a8";
+               };
+       };
+};
index 9f1921634eb7b8ab19c671fc2de17f0d44a50fc4..405d1673904e53805a11551693446b55cfa1831c 100644 (file)
                status = "disabled";
        };
 
+       pwm {
+               compatible = "nvidia,tegra20-pwm";
+               reg = <0x7000a000 0x100>;
+               #pwm-cells = <2>;
+       };
+
        i2c@7000c000 {
                compatible = "nvidia,tegra20-i2c";
                reg = <0x7000c000 0x100>;
index da740191771f8493f1734636a88244869be5abff..3e4334d14efb4d70bdd88e46c44dc4b6af1e7cf2 100644 (file)
                status = "disabled";
        };
 
+       pwm {
+               compatible = "nvidia,tegra30-pwm", "nvidia,tegra20-pwm";
+               reg = <0x7000a000 0x100>;
+               #pwm-cells = <2>;
+       };
+
        i2c@7000c000 {
                compatible =  "nvidia,tegra30-i2c", "nvidia,tegra20-i2c";
                reg = <0x7000c000 0x100>;
index aa07f5938f05cfac42414a79f475135ff827c260..1143c4d5c56730e12221a6944acc6dc505465be1 100644 (file)
@@ -452,6 +452,7 @@ static struct dma_map_ops dmabounce_ops = {
        .alloc                  = arm_dma_alloc,
        .free                   = arm_dma_free,
        .mmap                   = arm_dma_mmap,
+       .get_sgtable            = arm_dma_get_sgtable,
        .map_page               = dmabounce_map_page,
        .unmap_page             = dmabounce_unmap_page,
        .sync_single_for_cpu    = dmabounce_sync_for_cpu,
index ddc9fe6a78acca672bc7cf2a91209eb18eca622d..7d8718468e0dff1ec28b63014f6c56ec62650d10 100644 (file)
@@ -5,10 +5,7 @@ CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_UTS_NS is not set
 # CONFIG_IPC_NS is not set
-# CONFIG_USER_NS is not set
 # CONFIG_PID_NS is not set
-CONFIG_SYSFS_DEPRECATED=y
-CONFIG_SYSFS_DEPRECATED_V2=y
 CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -21,7 +18,7 @@ CONFIG_ARCH_SHMOBILE=y
 CONFIG_ARCH_R8A7740=y
 CONFIG_MACH_ARMADILLO800EVA=y
 # CONFIG_SH_TIMER_TMU is not set
-# CONFIG_ARM_THUMB is not set
+CONFIG_ARM_THUMB=y
 CONFIG_CPU_BPREDICT_DISABLE=y
 # CONFIG_CACHE_L2X0 is not set
 CONFIG_ARM_ERRATA_430973=y
@@ -39,6 +36,7 @@ CONFIG_ZBOOT_ROM_BSS=0x0
 CONFIG_CMDLINE="console=tty0 console=ttySC1,115200 earlyprintk=sh-sci.1,115200 ignore_loglevel root=/dev/nfs ip=dhcp nfsroot=,rsize=4096,wsize=4096"
 CONFIG_CMDLINE_FORCE=y
 CONFIG_KEXEC=y
+CONFIG_VFP=y
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
 # CONFIG_SUSPEND is not set
 CONFIG_NET=y
@@ -89,26 +87,32 @@ CONFIG_SERIAL_SH_SCI_CONSOLE=y
 CONFIG_I2C=y
 CONFIG_I2C_SH_MOBILE=y
 # CONFIG_HWMON is not set
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+# CONFIG_RC_CORE is not set
+# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set
+# CONFIG_V4L_USB_DRIVERS is not set
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_SOC_CAMERA=y
+CONFIG_SOC_CAMERA_MT9T112=y
+CONFIG_VIDEO_SH_MOBILE_CEU=y
+# CONFIG_RADIO_ADAPTERS is not set
 CONFIG_FB=y
-CONFIG_FB_MODE_HELPERS=y
 CONFIG_FB_SH_MOBILE_LCDC=y
+CONFIG_FB_SH_MOBILE_HDMI=y
 CONFIG_LCD_CLASS_DEVICE=y
 CONFIG_FRAMEBUFFER_CONSOLE=y
 CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
 CONFIG_LOGO=y
 # CONFIG_LOGO_LINUX_MONO is not set
 # CONFIG_LOGO_LINUX_VGA16 is not set
-CONFIG_SOUND=y
-CONFIG_SND=y
 # CONFIG_SND_SUPPORT_OLD_API is not set
 # CONFIG_SND_VERBOSE_PROCFS is not set
 # CONFIG_SND_DRIVERS is not set
 # CONFIG_SND_ARM is not set
-CONFIG_SND_SOC=y
 CONFIG_SND_SOC_SH4_FSI=y
 # CONFIG_HID_SUPPORT is not set
 CONFIG_USB=y
-# CONFIG_USB_DEVICE_CLASS is not set
 CONFIG_USB_RENESAS_USBHS=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_RENESAS_USBHS_UDC=y
@@ -116,6 +120,8 @@ CONFIG_USB_ETH=m
 CONFIG_MMC=y
 CONFIG_MMC_SDHI=y
 CONFIG_MMC_SH_MMCIF=y
+CONFIG_DMADEVICES=y
+CONFIG_SH_DMAE=y
 CONFIG_UIO=y
 CONFIG_UIO_PDRV_GENIRQ=y
 # CONFIG_DNOTIFY is not set
@@ -124,7 +130,6 @@ CONFIG_VFAT_FS=y
 CONFIG_TMPFS=y
 # CONFIG_MISC_FILESYSTEMS is not set
 CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
 CONFIG_NFS_V3_ACL=y
 CONFIG_NFS_V4=y
 CONFIG_NFS_V4_1=y
diff --git a/arch/arm/configs/kzm9d_defconfig b/arch/arm/configs/kzm9d_defconfig
new file mode 100644 (file)
index 0000000..26146ff
--- /dev/null
@@ -0,0 +1,89 @@
+# CONFIG_ARM_PATCH_PHYS_VIRT is not set
+CONFIG_EXPERIMENTAL=y
+CONFIG_SYSVIPC=y
+CONFIG_NO_HZ=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_EMBEDDED=y
+CONFIG_SLAB=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_SHMOBILE=y
+CONFIG_ARCH_EMEV2=y
+CONFIG_MACH_KZM9D=y
+CONFIG_MEMORY_START=0x40000000
+CONFIG_MEMORY_SIZE=0x10000000
+# CONFIG_SH_TIMER_TMU is not set
+# CONFIG_SWP_EMULATE is not set
+# CONFIG_CACHE_L2X0 is not set
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
+CONFIG_HOTPLUG_CPU=y
+# CONFIG_LOCAL_TIMERS is not set
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_CROSS_MEMORY_ATTACH is not set
+CONFIG_FORCE_MAX_ZONEORDER=13
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_CMDLINE="console=tty0 console=ttyS1,115200n81 earlyprintk=serial8250-em.1,115200n81 mem=128M@0x40000000 ignore_loglevel root=/dev/nfs ip=dhcp nfsroot=,rsize=4096,wsize=4096"
+CONFIG_CMDLINE_FORCE=y
+CONFIG_VFP=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_SUSPEND is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_WIRELESS is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_BLK_DEV is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CHELSIO is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+CONFIG_SMSC911X=y
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_EM=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_EM=y
+# CONFIG_HWMON is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_IOMMU_SUPPORT is not set
+# CONFIG_DNOTIFY is not set
+CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+# CONFIG_FTRACE is not set
index e3ebc20ed0a78a9a40ed76d44cfe907b01535ed3..2388c86106277dccb08e0c24820a6e4a0a733630 100644 (file)
@@ -100,7 +100,12 @@ CONFIG_SND_SOC_SH4_FSI=y
 CONFIG_USB=y
 CONFIG_USB_DEVICEFS=y
 CONFIG_USB_R8A66597_HCD=y
+CONFIG_USB_RENESAS_USBHS=y
 CONFIG_USB_STORAGE=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_RENESAS_USBHS_UDC=y
+CONFIG_USB_ETH=m
+CONFIG_USB_MASS_STORAGE=m
 CONFIG_MMC=y
 # CONFIG_MMC_BLOCK_BOUNCE is not set
 CONFIG_MMC_SDHI=y
@@ -108,12 +113,13 @@ CONFIG_MMC_SH_MMCIF=y
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
 CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_RS5C372=y
 CONFIG_DMADEVICES=y
 CONFIG_SH_DMAE=y
 CONFIG_ASYNC_TX_DMA=y
 CONFIG_STAGING=y
 # CONFIG_DNOTIFY is not set
-# CONFIG_INOTIFY_USER is not set
+CONFIG_INOTIFY_USER=y
 CONFIG_VFAT_FS=y
 CONFIG_TMPFS=y
 # CONFIG_MISC_FILESYSTEMS is not set
index b152de79fd95373e2f6fd8e34e66a4c360463c3b..e58edc36b4066bdba9dcdd4112d5ae29c15c8f5e 100644 (file)
@@ -193,6 +193,8 @@ CONFIG_MMC_OMAP_HS=y
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_TWL92330=y
 CONFIG_RTC_DRV_TWL4030=y
+CONFIG_DMADEVICES=y
+CONFIG_DMA_OMAP=y
 CONFIG_EXT2_FS=y
 CONFIG_EXT3_FS=y
 # CONFIG_EXT3_FS_XATTR is not set
index 4be9c1e80ee63aac2c5d8239222ec1dcf147e683..db2245353f0f936aee5b86614e496aaafa044694 100644 (file)
@@ -106,6 +106,7 @@ CONFIG_I2C_TEGRA=y
 CONFIG_SPI=y
 CONFIG_SPI_TEGRA=y
 CONFIG_GPIO_TPS65910=y
+CONFIG_GPIO_TPS6586X=y
 CONFIG_POWER_SUPPLY=y
 CONFIG_BATTERY_SBS=y
 CONFIG_SENSORS_LM90=y
index 004c1bc95d2b9082de58e0626134a779705620b6..e4448e16046dd32ab69ca17ef65a6b21745a3e0b 100644 (file)
@@ -215,7 +215,9 @@ static inline void vivt_flush_cache_mm(struct mm_struct *mm)
 static inline void
 vivt_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
 {
-       if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm)))
+       struct mm_struct *mm = vma->vm_mm;
+
+       if (!mm || cpumask_test_cpu(smp_processor_id(), mm_cpumask(mm)))
                __cpuc_flush_user_range(start & PAGE_MASK, PAGE_ALIGN(end),
                                        vma->vm_flags);
 }
@@ -223,7 +225,9 @@ vivt_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned
 static inline void
 vivt_flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsigned long pfn)
 {
-       if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) {
+       struct mm_struct *mm = vma->vm_mm;
+
+       if (!mm || cpumask_test_cpu(smp_processor_id(), mm_cpumask(mm))) {
                unsigned long addr = user_addr & PAGE_MASK;
                __cpuc_flush_user_range(addr, addr + PAGE_SIZE, vma->vm_flags);
        }
index bbef15d04890b7c1ef4a9d4afec77867de1fc72d..2ae842df455180d3bcd445d339ecd332d3b36c4e 100644 (file)
@@ -186,17 +186,6 @@ extern int arm_dma_mmap(struct device *dev, struct vm_area_struct *vma,
                        void *cpu_addr, dma_addr_t dma_addr, size_t size,
                        struct dma_attrs *attrs);
 
-#define dma_mmap_coherent(d, v, c, h, s) dma_mmap_attrs(d, v, c, h, s, NULL)
-
-static inline int dma_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
-                                 void *cpu_addr, dma_addr_t dma_addr,
-                                 size_t size, struct dma_attrs *attrs)
-{
-       struct dma_map_ops *ops = get_dma_ops(dev);
-       BUG_ON(!ops);
-       return ops->mmap(dev, vma, cpu_addr, dma_addr, size, attrs);
-}
-
 static inline void *dma_alloc_writecombine(struct device *dev, size_t size,
                                       dma_addr_t *dma_handle, gfp_t flag)
 {
@@ -213,20 +202,12 @@ static inline void dma_free_writecombine(struct device *dev, size_t size,
        return dma_free_attrs(dev, size, cpu_addr, dma_handle, &attrs);
 }
 
-static inline int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma,
-                     void *cpu_addr, dma_addr_t dma_addr, size_t size)
-{
-       DEFINE_DMA_ATTRS(attrs);
-       dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
-       return dma_mmap_attrs(dev, vma, cpu_addr, dma_addr, size, &attrs);
-}
-
 /*
  * This can be called during boot to increase the size of the consistent
  * DMA region above it's default value of 2MB. It must be called before the
  * memory allocator is initialised, i.e. before any core_initcall.
  */
-extern void __init init_consistent_dma_size(unsigned long size);
+static inline void init_consistent_dma_size(unsigned long size) { }
 
 /*
  * For SA-1111, IXP425, and ADI systems  the dma-mapping functions are "magic"
@@ -280,6 +261,9 @@ extern void arm_dma_sync_sg_for_cpu(struct device *, struct scatterlist *, int,
                enum dma_data_direction);
 extern void arm_dma_sync_sg_for_device(struct device *, struct scatterlist *, int,
                enum dma_data_direction);
+extern int arm_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
+               void *cpu_addr, dma_addr_t dma_addr, size_t size,
+               struct dma_attrs *attrs);
 
 #endif /* __KERNEL__ */
 #endif
index 93226cf23ae0a838e192be1d675ee886be9dd52e..b1479fd04a951f1d82d55f77fd01cb9f0b072f38 100644 (file)
  */
 #ifndef _ASM_MUTEX_H
 #define _ASM_MUTEX_H
-
-#if __LINUX_ARM_ARCH__ < 6
-/* On pre-ARMv6 hardware the swp based implementation is the most efficient. */
-# include <asm-generic/mutex-xchg.h>
-#else
-
 /*
- * Attempting to lock a mutex on ARMv6+ can be done with a bastardized
- * atomic decrement (it is not a reliable atomic decrement but it satisfies
- * the defined semantics for our purpose, while being smaller and faster
- * than a real atomic decrement or atomic swap.  The idea is to attempt
- * decrementing the lock value only once.  If once decremented it isn't zero,
- * or if its store-back fails due to a dispute on the exclusive store, we
- * simply bail out immediately through the slow path where the lock will be
- * reattempted until it succeeds.
+ * On pre-ARMv6 hardware this results in a swp-based implementation,
+ * which is the most efficient. For ARMv6+, we emit a pair of exclusive
+ * accesses instead.
  */
-static inline void
-__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
-{
-       int __ex_flag, __res;
-
-       __asm__ (
-
-               "ldrex  %0, [%2]        \n\t"
-               "sub    %0, %0, #1      \n\t"
-               "strex  %1, %0, [%2]    "
-
-               : "=&r" (__res), "=&r" (__ex_flag)
-               : "r" (&(count)->counter)
-               : "cc","memory" );
-
-       __res |= __ex_flag;
-       if (unlikely(__res != 0))
-               fail_fn(count);
-}
-
-static inline int
-__mutex_fastpath_lock_retval(atomic_t *count, int (*fail_fn)(atomic_t *))
-{
-       int __ex_flag, __res;
-
-       __asm__ (
-
-               "ldrex  %0, [%2]        \n\t"
-               "sub    %0, %0, #1      \n\t"
-               "strex  %1, %0, [%2]    "
-
-               : "=&r" (__res), "=&r" (__ex_flag)
-               : "r" (&(count)->counter)
-               : "cc","memory" );
-
-       __res |= __ex_flag;
-       if (unlikely(__res != 0))
-               __res = fail_fn(count);
-       return __res;
-}
-
-/*
- * Same trick is used for the unlock fast path. However the original value,
- * rather than the result, is used to test for success in order to have
- * better generated assembly.
- */
-static inline void
-__mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *))
-{
-       int __ex_flag, __res, __orig;
-
-       __asm__ (
-
-               "ldrex  %0, [%3]        \n\t"
-               "add    %1, %0, #1      \n\t"
-               "strex  %2, %1, [%3]    "
-
-               : "=&r" (__orig), "=&r" (__res), "=&r" (__ex_flag)
-               : "r" (&(count)->counter)
-               : "cc","memory" );
-
-       __orig |= __ex_flag;
-       if (unlikely(__orig != 0))
-               fail_fn(count);
-}
-
-/*
- * If the unlock was done on a contended lock, or if the unlock simply fails
- * then the mutex remains locked.
- */
-#define __mutex_slowpath_needs_to_unlock()     1
-
-/*
- * For __mutex_fastpath_trylock we use another construct which could be
- * described as a "single value cmpxchg".
- *
- * This provides the needed trylock semantics like cmpxchg would, but it is
- * lighter and less generic than a true cmpxchg implementation.
- */
-static inline int
-__mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *))
-{
-       int __ex_flag, __res, __orig;
-
-       __asm__ (
-
-               "1: ldrex       %0, [%3]        \n\t"
-               "subs           %1, %0, #1      \n\t"
-               "strexeq        %2, %1, [%3]    \n\t"
-               "movlt          %0, #0          \n\t"
-               "cmpeq          %2, #0          \n\t"
-               "bgt            1b              "
-
-               : "=&r" (__orig), "=&r" (__res), "=&r" (__ex_flag)
-               : "r" (&count->counter)
-               : "cc", "memory" );
-
-       return __orig;
-}
-
-#endif
+#include <asm-generic/mutex-xchg.h>
 #endif
index 23ebc0c82a3975ae5c455dd39598e93ab33922e7..24d284a1bfc75faa0ed90fb7882a67f00128f336 100644 (file)
@@ -196,7 +196,7 @@ static const struct tagtable __tagtable_##fn __tag = { tag, fn }
 
 struct membank {
        phys_addr_t start;
-       unsigned long size;
+       phys_addr_t size;
        unsigned int highmem;
 };
 
@@ -217,7 +217,7 @@ extern struct meminfo meminfo;
 #define bank_phys_end(bank)    ((bank)->start + (bank)->size)
 #define bank_phys_size(bank)   (bank)->size
 
-extern int arm_add_memory(phys_addr_t start, unsigned long size);
+extern int arm_add_memory(phys_addr_t start, phys_addr_t size);
 extern void early_print(const char *str, ...);
 extern void dump_machine_table(void);
 
index 512cd1473454c9ba26eaf51fbb659a313aad96b0..0cab47d4a83ff97a23c7a9d2975cfb855a687e3e 100644 (file)
 
 #ifdef __KERNEL__
 
-#define __ARCH_WANT_IPC_PARSE_VERSION
 #define __ARCH_WANT_STAT64
 #define __ARCH_WANT_SYS_GETHOSTNAME
 #define __ARCH_WANT_SYS_PAUSE
index 0d1851ca6eb993a628f623c257487200fe529604..0f82098c9bfe3618115dcc4e8b2d74d95c7ddcf6 100644 (file)
@@ -244,6 +244,19 @@ svc_preempt:
        b       1b
 #endif
 
+__und_fault:
+       @ Correct the PC such that it is pointing at the instruction
+       @ which caused the fault.  If the faulting instruction was ARM
+       @ the PC will be pointing at the next instruction, and have to
+       @ subtract 4.  Otherwise, it is Thumb, and the PC will be
+       @ pointing at the second half of the Thumb instruction.  We
+       @ have to subtract 2.
+       ldr     r2, [r0, #S_PC]
+       sub     r2, r2, r1
+       str     r2, [r0, #S_PC]
+       b       do_undefinstr
+ENDPROC(__und_fault)
+
        .align  5
 __und_svc:
 #ifdef CONFIG_KPROBES
@@ -261,25 +274,32 @@ __und_svc:
        @
        @  r0 - instruction
        @
-#ifndef        CONFIG_THUMB2_KERNEL
+#ifndef CONFIG_THUMB2_KERNEL
        ldr     r0, [r4, #-4]
 #else
+       mov     r1, #2
        ldrh    r0, [r4, #-2]                   @ Thumb instruction at LR - 2
        cmp     r0, #0xe800                     @ 32-bit instruction if xx >= 0
-       ldrhhs  r9, [r4]                        @ bottom 16 bits
-       orrhs   r0, r9, r0, lsl #16
+       blo     __und_svc_fault
+       ldrh    r9, [r4]                        @ bottom 16 bits
+       add     r4, r4, #2
+       str     r4, [sp, #S_PC]
+       orr     r0, r9, r0, lsl #16
 #endif
-       adr     r9, BSYM(1f)
+       adr     r9, BSYM(__und_svc_finish)
        mov     r2, r4
        bl      call_fpe
 
+       mov     r1, #4                          @ PC correction to apply
+__und_svc_fault:
        mov     r0, sp                          @ struct pt_regs *regs
-       bl      do_undefinstr
+       bl      __und_fault
 
        @
        @ IRQs off again before pulling preserved data off the stack
        @
-1:     disable_irq_notrace
+__und_svc_finish:
+       disable_irq_notrace
 
        @
        @ restore SPSR and restart the instruction
@@ -423,25 +443,33 @@ __und_usr:
        mov     r2, r4
        mov     r3, r5
 
+       @ r2 = regs->ARM_pc, which is either 2 or 4 bytes ahead of the
+       @      faulting instruction depending on Thumb mode.
+       @ r3 = regs->ARM_cpsr
        @
-       @ fall through to the emulation code, which returns using r9 if
-       @ it has emulated the instruction, or the more conventional lr
-       @ if we are to treat this as a real undefined instruction
-       @
-       @  r0 - instruction
+       @ The emulation code returns using r9 if it has emulated the
+       @ instruction, or the more conventional lr if we are to treat
+       @ this as a real undefined instruction
        @
        adr     r9, BSYM(ret_from_exception)
-       adr     lr, BSYM(__und_usr_unknown)
+
        tst     r3, #PSR_T_BIT                  @ Thumb mode?
-       itet    eq                              @ explicit IT needed for the 1f label
-       subeq   r4, r2, #4                      @ ARM instr at LR - 4
-       subne   r4, r2, #2                      @ Thumb instr at LR - 2
-1:     ldreqt  r0, [r4]
+       bne     __und_usr_thumb
+       sub     r4, r2, #4                      @ ARM instr at LR - 4
+1:     ldrt    r0, [r4]
 #ifdef CONFIG_CPU_ENDIAN_BE8
-       reveq   r0, r0                          @ little endian instruction
+       rev     r0, r0                          @ little endian instruction
 #endif
-       beq     call_fpe
+       @ r0 = 32-bit ARM instruction which caused the exception
+       @ r2 = PC value for the following instruction (:= regs->ARM_pc)
+       @ r4 = PC value for the faulting instruction
+       @ lr = 32-bit undefined instruction function
+       adr     lr, BSYM(__und_usr_fault_32)
+       b       call_fpe
+
+__und_usr_thumb:
        @ Thumb instruction
+       sub     r4, r2, #2                      @ First half of thumb instr at LR - 2
 #if CONFIG_ARM_THUMB && __LINUX_ARM_ARCH__ >= 6 && CONFIG_CPU_V7
 /*
  * Thumb-2 instruction handling.  Note that because pre-v6 and >= v6 platforms
@@ -455,7 +483,7 @@ __und_usr:
        ldr     r5, .LCcpu_architecture
        ldr     r5, [r5]
        cmp     r5, #CPU_ARCH_ARMv7
-       blo     __und_usr_unknown
+       blo     __und_usr_fault_16              @ 16bit undefined instruction
 /*
  * The following code won't get run unless the running CPU really is v7, so
  * coding round the lack of ldrht on older arches is pointless.  Temporarily
@@ -463,15 +491,18 @@ __und_usr:
  */
        .arch   armv6t2
 #endif
-2:
- ARM(  ldrht   r5, [r4], #2    )
- THUMB(        ldrht   r5, [r4]        )
- THUMB(        add     r4, r4, #2      )
+2:     ldrht   r5, [r4]
        cmp     r5, #0xe800                     @ 32bit instruction if xx != 0
-       blo     __und_usr_unknown
-3:     ldrht   r0, [r4]
+       blo     __und_usr_fault_16              @ 16bit undefined instruction
+3:     ldrht   r0, [r2]
        add     r2, r2, #2                      @ r2 is PC + 2, make it PC + 4
+       str     r2, [sp, #S_PC]                 @ it's a 2x16bit instr, update
        orr     r0, r0, r5, lsl #16
+       adr     lr, BSYM(__und_usr_fault_32)
+       @ r0 = the two 16-bit Thumb instructions which caused the exception
+       @ r2 = PC value for the following Thumb instruction (:= regs->ARM_pc)
+       @ r4 = PC value for the first 16-bit Thumb instruction
+       @ lr = 32bit undefined instruction function
 
 #if __LINUX_ARM_ARCH__ < 7
 /* If the target arch was overridden, change it back: */
@@ -482,17 +513,13 @@ __und_usr:
 #endif
 #endif /* __LINUX_ARM_ARCH__ < 7 */
 #else /* !(CONFIG_ARM_THUMB && __LINUX_ARM_ARCH__ >= 6 && CONFIG_CPU_V7) */
-       b       __und_usr_unknown
+       b       __und_usr_fault_16
 #endif
- UNWIND(.fnend         )
+ UNWIND(.fnend)
 ENDPROC(__und_usr)
 
-       @
-       @ fallthrough to call_fpe
-       @
-
 /*
- * The out of line fixup for the ldrt above.
+ * The out of line fixup for the ldrt instructions above.
  */
        .pushsection .fixup, "ax"
        .align  2
@@ -524,11 +551,12 @@ ENDPROC(__und_usr)
  * NEON handler code.
  *
  * Emulators may wish to make use of the following registers:
- *  r0  = instruction opcode.
- *  r2  = PC+4
+ *  r0  = instruction opcode (32-bit ARM or two 16-bit Thumb)
+ *  r2  = PC value to resume execution after successful emulation
  *  r9  = normal "successful" return address
- *  r10 = this threads thread_info structure.
+ *  r10 = this threads thread_info structure
  *  lr  = unrecognised instruction return address
+ * IRQs disabled, FIQs enabled.
  */
        @
        @ Fall-through from Thumb-2 __und_usr
@@ -659,12 +687,17 @@ ENTRY(no_fp)
        mov     pc, lr
 ENDPROC(no_fp)
 
-__und_usr_unknown:
-       enable_irq
+__und_usr_fault_32:
+       mov     r1, #4
+       b       1f
+__und_usr_fault_16:
+       mov     r1, #2
+1:     enable_irq
        mov     r0, sp
        adr     lr, BSYM(ret_from_exception)
-       b       do_undefinstr
-ENDPROC(__und_usr_unknown)
+       b       __und_fault
+ENDPROC(__und_usr_fault_32)
+ENDPROC(__und_usr_fault_16)
 
        .align  5
 __pabt_usr:
index 49d9f93052476b51e0136b760312b7d829cf4899..978eac57e04a783f8901a16986f6d88f96f125ba 100644 (file)
@@ -51,23 +51,15 @@ ret_fast_syscall:
 fast_work_pending:
        str     r0, [sp, #S_R0+S_OFF]!          @ returned r0
 work_pending:
-       tst     r1, #_TIF_NEED_RESCHED
-       bne     work_resched
-       /*
-        * TIF_SIGPENDING or TIF_NOTIFY_RESUME must've been set if we got here
-        */
-       ldr     r2, [sp, #S_PSR]
        mov     r0, sp                          @ 'regs'
-       tst     r2, #15                         @ are we returning to user mode?
-       bne     no_work_pending                 @ no?  just leave, then...
        mov     r2, why                         @ 'syscall'
-       tst     r1, #_TIF_SIGPENDING            @ delivering a signal?
-       movne   why, #0                         @ prevent further restarts
-       bl      do_notify_resume
-       b       ret_slow_syscall                @ Check work again
+       bl      do_work_pending
+       cmp     r0, #0
+       beq     no_work_pending
+       movlt   scno, #(__NR_restart_syscall - __NR_SYSCALL_BASE)
+       ldmia   sp, {r0 - r6}                   @ have to reload r0 - r6
+       b       local_restart                   @ ... and off we go
 
-work_resched:
-       bl      schedule
 /*
  * "slow" syscall return path.  "why" tells us if this was a real syscall.
  */
@@ -409,6 +401,7 @@ ENTRY(vector_swi)
        eor     scno, scno, #__NR_SYSCALL_BASE  @ check OS number
 #endif
 
+local_restart:
        ldr     r10, [tsk, #TI_FLAGS]           @ check for syscall tracing
        stmdb   sp!, {r4, r5}                   @ push fifth and sixth args
 
@@ -450,7 +443,8 @@ __sys_trace:
        mov     scno, r0                        @ syscall number (possibly new)
        add     r1, sp, #S_R0 + S_OFF           @ pointer to regs
        cmp     scno, #NR_syscalls              @ check upper syscall limit
-       ldmccia r1, {r0 - r3}                   @ have to reload r0 - r3
+       ldmccia r1, {r0 - r6}                   @ have to reload r0 - r6
+       stmccia sp, {r4, r5}                    @ and update the stack args
        ldrcc   pc, [tbl, scno, lsl #2]         @ call sys_* routine
        b       2b
 
index df0bf0c8cb790a5ee501c55a204a45ddbcb48aab..34e56647dceeee88d99f65d5fd0a6e00fb46a0fd 100644 (file)
@@ -179,19 +179,20 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
        old = *parent;
        *parent = return_hooker;
 
-       err = ftrace_push_return_trace(old, self_addr, &trace.depth,
-                                      frame_pointer);
-       if (err == -EBUSY) {
-               *parent = old;
-               return;
-       }
-
        trace.func = self_addr;
+       trace.depth = current->curr_ret_stack + 1;
 
        /* Only trace if the calling function expects to */
        if (!ftrace_graph_entry(&trace)) {
-               current->curr_ret_stack--;
                *parent = old;
+               return;
+       }
+
+       err = ftrace_push_return_trace(old, self_addr, &trace.depth,
+                                      frame_pointer);
+       if (err == -EBUSY) {
+               *parent = old;
+               return;
        }
 }
 
index 19c95ea65b2f25c25371734a7a03f147e929978e..693b744fd572f163f053c9fef42d563e246e04d3 100644 (file)
@@ -247,6 +247,7 @@ void machine_shutdown(void)
 void machine_halt(void)
 {
        machine_shutdown();
+       local_irq_disable();
        while (1);
 }
 
@@ -268,6 +269,7 @@ void machine_restart(char *cmd)
 
        /* Whoops - the platform was unable to reboot. Tell the user! */
        printk("Reboot failed -- System halted\n");
+       local_irq_disable();
        while (1);
 }
 
index dab711e6e1ca444c30f960177c12149add9ca2e6..3e0fc5f7ed4b05cb09df7c58f7098492176a568b 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/regset.h>
 #include <linux/audit.h>
 #include <linux/tracehook.h>
+#include <linux/unistd.h>
 
 #include <asm/pgtable.h>
 #include <asm/traps.h>
index e15d83bb4ea378c1316db6a7d705eab30248a98b..a81dcecc734388f7745e399e38504645f4e0e758 100644 (file)
@@ -508,7 +508,7 @@ void __init dump_machine_table(void)
                /* can't use cpu_relax() here as it may require MMU setup */;
 }
 
-int __init arm_add_memory(phys_addr_t start, unsigned long size)
+int __init arm_add_memory(phys_addr_t start, phys_addr_t size)
 {
        struct membank *bank = &meminfo.bank[meminfo.nr_banks];
 
@@ -538,7 +538,7 @@ int __init arm_add_memory(phys_addr_t start, unsigned long size)
        }
 #endif
 
-       bank->size = size & PAGE_MASK;
+       bank->size = size & ~(phys_addr_t)(PAGE_SIZE - 1);
 
        /*
         * Check whether this memory region has non-zero size or
@@ -558,7 +558,7 @@ int __init arm_add_memory(phys_addr_t start, unsigned long size)
 static int __init early_mem(char *p)
 {
        static int usermem __initdata = 0;
-       unsigned long size;
+       phys_addr_t size;
        phys_addr_t start;
        char *endp;
 
index 536c5d6b340b7fca2aee9c770000372c38be98bb..f27789e4e38aaad749e05426d042cffe7912b5c7 100644 (file)
@@ -27,7 +27,6 @@
  */
 #define SWI_SYS_SIGRETURN      (0xef000000|(__NR_sigreturn)|(__NR_OABI_SYSCALL_BASE))
 #define SWI_SYS_RT_SIGRETURN   (0xef000000|(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE))
-#define SWI_SYS_RESTART                (0xef000000|__NR_restart_syscall|__NR_OABI_SYSCALL_BASE)
 
 /*
  * With EABI, the syscall number has to be loaded into r7.
@@ -47,18 +46,6 @@ const unsigned long sigreturn_codes[7] = {
        MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN,
 };
 
-/*
- * Either we support OABI only, or we have EABI with the OABI
- * compat layer enabled.  In the later case we don't know if
- * user space is EABI or not, and if not we must not clobber r7.
- * Always using the OABI syscall solves that issue and works for
- * all those cases.
- */
-const unsigned long syscall_restart_code[2] = {
-       SWI_SYS_RESTART,        /* swi  __NR_restart_syscall */
-       0xe49df004,             /* ldr  pc, [sp], #4 */
-};
-
 /*
  * atomically swap in the new signal mask, and wait for a signal.
  */
@@ -582,12 +569,13 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
  * the kernel can handle, and then we build all the user-level signal handling
  * stack-frames in one go after that.
  */
-static void do_signal(struct pt_regs *regs, int syscall)
+static int do_signal(struct pt_regs *regs, int syscall)
 {
        unsigned int retval = 0, continue_addr = 0, restart_addr = 0;
        struct k_sigaction ka;
        siginfo_t info;
        int signr;
+       int restart = 0;
 
        /*
         * If we were from a system call, check for system call restarting...
@@ -602,15 +590,15 @@ static void do_signal(struct pt_regs *regs, int syscall)
                 * debugger will see the already changed PSW.
                 */
                switch (retval) {
+               case -ERESTART_RESTARTBLOCK:
+                       restart -= 2;
                case -ERESTARTNOHAND:
                case -ERESTARTSYS:
                case -ERESTARTNOINTR:
+                       restart++;
                        regs->ARM_r0 = regs->ARM_ORIG_r0;
                        regs->ARM_pc = restart_addr;
                        break;
-               case -ERESTART_RESTARTBLOCK:
-                       regs->ARM_r0 = -EINTR;
-                       break;
                }
        }
 
@@ -619,14 +607,17 @@ static void do_signal(struct pt_regs *regs, int syscall)
         * point the debugger may change all our registers ...
         */
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+       /*
+        * Depending on the signal settings we may need to revert the
+        * decision to restart the system call.  But skip this if a
+        * debugger has chosen to restart at a different PC.
+        */
+       if (regs->ARM_pc != restart_addr)
+               restart = 0;
        if (signr > 0) {
-               /*
-                * Depending on the signal settings we may need to revert the
-                * decision to restart the system call.  But skip this if a
-                * debugger has chosen to restart at a different PC.
-                */
-               if (regs->ARM_pc == restart_addr) {
-                       if (retval == -ERESTARTNOHAND
+               if (unlikely(restart)) {
+                       if (retval == -ERESTARTNOHAND ||
+                           retval == -ERESTART_RESTARTBLOCK
                            || (retval == -ERESTARTSYS
                                && !(ka.sa.sa_flags & SA_RESTART))) {
                                regs->ARM_r0 = -EINTR;
@@ -635,52 +626,43 @@ static void do_signal(struct pt_regs *regs, int syscall)
                }
 
                handle_signal(signr, &ka, &info, regs);
-               return;
-       }
-
-       if (syscall) {
-               /*
-                * Handle restarting a different system call.  As above,
-                * if a debugger has chosen to restart at a different PC,
-                * ignore the restart.
-                */
-               if (retval == -ERESTART_RESTARTBLOCK
-                   && regs->ARM_pc == continue_addr) {
-                       if (thumb_mode(regs)) {
-                               regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE;
-                               regs->ARM_pc -= 2;
-                       } else {
-#if defined(CONFIG_AEABI) && !defined(CONFIG_OABI_COMPAT)
-                               regs->ARM_r7 = __NR_restart_syscall;
-                               regs->ARM_pc -= 4;
-#else
-                               u32 __user *usp;
-
-                               regs->ARM_sp -= 4;
-                               usp = (u32 __user *)regs->ARM_sp;
-
-                               if (put_user(regs->ARM_pc, usp) == 0) {
-                                       regs->ARM_pc = KERN_RESTART_CODE;
-                               } else {
-                                       regs->ARM_sp += 4;
-                                       force_sigsegv(0, current);
-                               }
-#endif
-                       }
-               }
+               return 0;
        }
 
        restore_saved_sigmask();
+       if (unlikely(restart))
+               regs->ARM_pc = continue_addr;
+       return restart;
 }
 
-asmlinkage void
-do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall)
+asmlinkage int
+do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
 {
-       if (thread_flags & _TIF_SIGPENDING)
-               do_signal(regs, syscall);
-
-       if (thread_flags & _TIF_NOTIFY_RESUME) {
-               clear_thread_flag(TIF_NOTIFY_RESUME);
-               tracehook_notify_resume(regs);
-       }
+       do {
+               if (likely(thread_flags & _TIF_NEED_RESCHED)) {
+                       schedule();
+               } else {
+                       if (unlikely(!user_mode(regs)))
+                               return 0;
+                       local_irq_enable();
+                       if (thread_flags & _TIF_SIGPENDING) {
+                               int restart = do_signal(regs, syscall);
+                               if (unlikely(restart)) {
+                                       /*
+                                        * Restart without handlers.
+                                        * Deal with it without leaving
+                                        * the kernel space.
+                                        */
+                                       return restart;
+                               }
+                               syscall = 0;
+                       } else {
+                               clear_thread_flag(TIF_NOTIFY_RESUME);
+                               tracehook_notify_resume(regs);
+                       }
+               }
+               local_irq_disable();
+               thread_flags = current_thread_info()->flags;
+       } while (thread_flags & _TIF_WORK_MASK);
+       return 0;
 }
index 6fcfe8398aa473051fbf72396986a84dc700978f..5ff067b7c7522f428b4343832ecb759b7ccf16de 100644 (file)
@@ -8,7 +8,5 @@
  * published by the Free Software Foundation.
  */
 #define KERN_SIGRETURN_CODE    (CONFIG_VECTORS_BASE + 0x00000500)
-#define KERN_RESTART_CODE      (KERN_SIGRETURN_CODE + sizeof(sigreturn_codes))
 
 extern const unsigned long sigreturn_codes[7];
-extern const unsigned long syscall_restart_code[2];
index aea74f5bc34abdcefbd90570e3c1b7e534b29763..ebd8ad274d76bb82488240e9543d7a1d99b5c674 100644 (file)
@@ -563,7 +563,8 @@ void smp_send_stop(void)
 
        cpumask_copy(&mask, cpu_online_mask);
        cpumask_clear_cpu(smp_processor_id(), &mask);
-       smp_cross_call(&mask, IPI_CPU_STOP);
+       if (!cpumask_empty(&mask))
+               smp_cross_call(&mask, IPI_CPU_STOP);
 
        /* Wait up to one second for other CPUs to stop */
        timeout = USEC_PER_SEC;
index 8b97d739b17b1040f4f3e4bb7fe069d84808fa72..f7945218b8c63a722cf08badc229345d7083b052 100644 (file)
@@ -402,18 +402,10 @@ static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
 
 asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
 {
-       unsigned int correction = thumb_mode(regs) ? 2 : 4;
        unsigned int instr;
        siginfo_t info;
        void __user *pc;
 
-       /*
-        * According to the ARM ARM, PC is 2 or 4 bytes ahead,
-        * depending whether we're in Thumb mode or not.
-        * Correct this offset.
-        */
-       regs->ARM_pc -= correction;
-
        pc = (void __user *)instruction_pointer(regs);
 
        if (processor_mode(regs) == SVC_MODE) {
@@ -852,8 +844,6 @@ void __init early_trap_init(void *vectors_base)
         */
        memcpy((void *)(vectors + KERN_SIGRETURN_CODE - CONFIG_VECTORS_BASE),
               sigreturn_codes, sizeof(sigreturn_codes));
-       memcpy((void *)(vectors + KERN_RESTART_CODE - CONFIG_VECTORS_BASE),
-              syscall_restart_code, sizeof(syscall_restart_code));
 
        flush_icache_range(vectors, vectors + PAGE_SIZE);
        modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
index 1b197ea7aab3a7a074624f1f9b5e0acafc4cac50..69719bad674ddc8bce50dc3e4880b0ac7d138217 100644 (file)
  *
  */
 #include <linux/linkage.h>
+#include <linux/kern_levels.h>
 #include <asm/assembler.h>
 
                .text
                .align
 
 .Liosl_warning:
-               .ascii  "<4>insl/outsl not implemented, called from %08lX\0"
+               .ascii  KERN_WARNING "insl/outsl not implemented, called from %08lX\0"
                .align
 
 /*
index d1624a315c9a89d97664fedcdc7f9f3ba8ba52de..783eab6845c4ea6d56e0a9575b9e9cb89e5eaf84 100644 (file)
@@ -546,6 +546,7 @@ static struct lcd_ctrl_config lcd_cfg = {
        .sync_edge              = 0,
        .sync_ctrl              = 1,
        .raster_order           = 0,
+       .fifo_th                = 6,
 };
 
 struct da8xx_lcdc_platform_data sharp_lcd035q3dg01_pdata = {
index 26fe9de35ecb8ca715f73a4cee3955591b1e2b2a..2f51293c18757712621bb26723f10d1ced8099dd 100644 (file)
@@ -619,10 +619,6 @@ static struct clk exynos4_init_clocks_off[] = {
                .devname        = "samsung-ac97",
                .enable         = exynos4_clk_ip_peril_ctrl,
                .ctrlbit        = (1 << 27),
-       }, {
-               .name           = "fimg2d",
-               .enable         = exynos4_clk_ip_image_ctrl,
-               .ctrlbit        = (1 << 0),
        }, {
                .name           = "mfc",
                .devname        = "s5p-mfc",
@@ -819,47 +815,21 @@ static struct clk *exynos4_clkset_mout_g2d0_list[] = {
        [1] = &exynos4_clk_sclk_apll.clk,
 };
 
-static struct clksrc_sources exynos4_clkset_mout_g2d0 = {
+struct clksrc_sources exynos4_clkset_mout_g2d0 = {
        .sources        = exynos4_clkset_mout_g2d0_list,
        .nr_sources     = ARRAY_SIZE(exynos4_clkset_mout_g2d0_list),
 };
 
-static struct clksrc_clk exynos4_clk_mout_g2d0 = {
-       .clk    = {
-               .name           = "mout_g2d0",
-       },
-       .sources = &exynos4_clkset_mout_g2d0,
-       .reg_src = { .reg = EXYNOS4_CLKSRC_IMAGE, .shift = 0, .size = 1 },
-};
-
 static struct clk *exynos4_clkset_mout_g2d1_list[] = {
        [0] = &exynos4_clk_mout_epll.clk,
        [1] = &exynos4_clk_sclk_vpll.clk,
 };
 
-static struct clksrc_sources exynos4_clkset_mout_g2d1 = {
+struct clksrc_sources exynos4_clkset_mout_g2d1 = {
        .sources        = exynos4_clkset_mout_g2d1_list,
        .nr_sources     = ARRAY_SIZE(exynos4_clkset_mout_g2d1_list),
 };
 
-static struct clksrc_clk exynos4_clk_mout_g2d1 = {
-       .clk    = {
-               .name           = "mout_g2d1",
-       },
-       .sources = &exynos4_clkset_mout_g2d1,
-       .reg_src = { .reg = EXYNOS4_CLKSRC_IMAGE, .shift = 4, .size = 1 },
-};
-
-static struct clk *exynos4_clkset_mout_g2d_list[] = {
-       [0] = &exynos4_clk_mout_g2d0.clk,
-       [1] = &exynos4_clk_mout_g2d1.clk,
-};
-
-static struct clksrc_sources exynos4_clkset_mout_g2d = {
-       .sources        = exynos4_clkset_mout_g2d_list,
-       .nr_sources     = ARRAY_SIZE(exynos4_clkset_mout_g2d_list),
-};
-
 static struct clk *exynos4_clkset_mout_mfc0_list[] = {
        [0] = &exynos4_clk_mout_mpll.clk,
        [1] = &exynos4_clk_sclk_apll.clk,
@@ -1124,13 +1094,6 @@ static struct clksrc_clk exynos4_clksrcs[] = {
                .sources = &exynos4_clkset_group,
                .reg_src = { .reg = EXYNOS4_CLKSRC_LCD0, .shift = 0, .size = 4 },
                .reg_div = { .reg = EXYNOS4_CLKDIV_LCD0, .shift = 0, .size = 4 },
-       }, {
-               .clk    = {
-                       .name           = "sclk_fimg2d",
-               },
-               .sources = &exynos4_clkset_mout_g2d,
-               .reg_src = { .reg = EXYNOS4_CLKSRC_IMAGE, .shift = 8, .size = 1 },
-               .reg_div = { .reg = EXYNOS4_CLKDIV_IMAGE, .shift = 0, .size = 4 },
        }, {
                .clk    = {
                        .name           = "sclk_mfc",
index 28a1197011823b132c10542db7795d3b3eddb588..bd12d5f8b63d3824d9b5578844c84f4732ff5f40 100644 (file)
@@ -23,6 +23,9 @@ extern struct clksrc_sources exynos4_clkset_group;
 extern struct clk *exynos4_clkset_aclk_top_list[];
 extern struct clk *exynos4_clkset_group_list[];
 
+extern struct clksrc_sources exynos4_clkset_mout_g2d0;
+extern struct clksrc_sources exynos4_clkset_mout_g2d1;
+
 extern int exynos4_clksrc_mask_fsys_ctrl(struct clk *clk, int enable);
 extern int exynos4_clk_ip_fsys_ctrl(struct clk *clk, int enable);
 extern int exynos4_clk_ip_lcd1_ctrl(struct clk *clk, int enable);
index b8689ff60baf5cb721a5d1d5caef45393fa60743..fed4c26e9dad6b2ca8b10dd71e3423c9cb428d1a 100644 (file)
@@ -48,6 +48,32 @@ static struct clksrc_clk *sysclks[] = {
        /* nothing here yet */
 };
 
+static struct clksrc_clk exynos4210_clk_mout_g2d0 = {
+       .clk    = {
+               .name           = "mout_g2d0",
+       },
+       .sources = &exynos4_clkset_mout_g2d0,
+       .reg_src = { .reg = EXYNOS4_CLKSRC_IMAGE, .shift = 0, .size = 1 },
+};
+
+static struct clksrc_clk exynos4210_clk_mout_g2d1 = {
+       .clk    = {
+               .name           = "mout_g2d1",
+       },
+       .sources = &exynos4_clkset_mout_g2d1,
+       .reg_src = { .reg = EXYNOS4_CLKSRC_IMAGE, .shift = 4, .size = 1 },
+};
+
+static struct clk *exynos4210_clkset_mout_g2d_list[] = {
+       [0] = &exynos4210_clk_mout_g2d0.clk,
+       [1] = &exynos4210_clk_mout_g2d1.clk,
+};
+
+static struct clksrc_sources exynos4210_clkset_mout_g2d = {
+       .sources        = exynos4210_clkset_mout_g2d_list,
+       .nr_sources     = ARRAY_SIZE(exynos4210_clkset_mout_g2d_list),
+};
+
 static int exynos4_clksrc_mask_lcd1_ctrl(struct clk *clk, int enable)
 {
        return s5p_gatectrl(EXYNOS4210_CLKSRC_MASK_LCD1, clk, enable);
@@ -74,6 +100,13 @@ static struct clksrc_clk clksrcs[] = {
                .sources = &exynos4_clkset_group,
                .reg_src = { .reg = EXYNOS4210_CLKSRC_LCD1, .shift = 0, .size = 4 },
                .reg_div = { .reg = EXYNOS4210_CLKDIV_LCD1, .shift = 0, .size = 4 },
+       }, {
+               .clk    = {
+                       .name           = "sclk_fimg2d",
+               },
+               .sources = &exynos4210_clkset_mout_g2d,
+               .reg_src = { .reg = EXYNOS4_CLKSRC_IMAGE, .shift = 8, .size = 1 },
+               .reg_div = { .reg = EXYNOS4_CLKDIV_IMAGE, .shift = 0, .size = 4 },
        },
 };
 
@@ -105,6 +138,10 @@ static struct clk init_clocks_off[] = {
                .devname        = SYSMMU_CLOCK_DEVNAME(fimd1, 11),
                .enable         = exynos4_clk_ip_lcd1_ctrl,
                .ctrlbit        = (1 << 4),
+       }, {
+               .name           = "fimg2d",
+               .enable         = exynos4_clk_ip_image_ctrl,
+               .ctrlbit        = (1 << 0),
        },
 };
 
index da397d21bbcf438cb81f73fe28adbfc7d365668f..8fba0b5fb8ab895d6ef8c65b0aec576a6ad650df 100644 (file)
@@ -68,12 +68,45 @@ static struct clksrc_clk clk_mout_mpll_user = {
        .reg_src        = { .reg = EXYNOS4_CLKSRC_CPU, .shift = 24, .size = 1 },
 };
 
+static struct clksrc_clk exynos4x12_clk_mout_g2d0 = {
+       .clk    = {
+               .name           = "mout_g2d0",
+       },
+       .sources = &exynos4_clkset_mout_g2d0,
+       .reg_src = { .reg = EXYNOS4_CLKSRC_DMC, .shift = 20, .size = 1 },
+};
+
+static struct clksrc_clk exynos4x12_clk_mout_g2d1 = {
+       .clk    = {
+               .name           = "mout_g2d1",
+       },
+       .sources = &exynos4_clkset_mout_g2d1,
+       .reg_src = { .reg = EXYNOS4_CLKSRC_DMC, .shift = 24, .size = 1 },
+};
+
+static struct clk *exynos4x12_clkset_mout_g2d_list[] = {
+       [0] = &exynos4x12_clk_mout_g2d0.clk,
+       [1] = &exynos4x12_clk_mout_g2d1.clk,
+};
+
+static struct clksrc_sources exynos4x12_clkset_mout_g2d = {
+       .sources        = exynos4x12_clkset_mout_g2d_list,
+       .nr_sources     = ARRAY_SIZE(exynos4x12_clkset_mout_g2d_list),
+};
+
 static struct clksrc_clk *sysclks[] = {
        &clk_mout_mpll_user,
 };
 
 static struct clksrc_clk clksrcs[] = {
-       /* nothing here yet */
+       {
+               .clk    = {
+                       .name           = "sclk_fimg2d",
+               },
+               .sources = &exynos4x12_clkset_mout_g2d,
+               .reg_src = { .reg = EXYNOS4_CLKSRC_DMC, .shift = 28, .size = 1 },
+               .reg_div = { .reg = EXYNOS4_CLKDIV_DMC1, .shift = 0, .size = 4 },
+       },
 };
 
 static struct clk init_clocks_off[] = {
@@ -102,7 +135,11 @@ static struct clk init_clocks_off[] = {
                .devname        = "exynos-fimc-lite.1",
                .enable         = exynos4212_clk_ip_isp0_ctrl,
                .ctrlbit        = (1 << 3),
-       }
+       }, {
+               .name           = "fimg2d",
+               .enable         = exynos4_clk_ip_dmc_ctrl,
+               .ctrlbit        = (1 << 23),
+       },
 };
 
 #ifdef CONFIG_PM_SLEEP
index f98a83a81ce73ed0cd89f364bfa87956802e7be8..ea785fcaf6c3262892f26dc6a3a7264d22a2e3d3 100644 (file)
@@ -1066,12 +1066,8 @@ static struct platform_device nuri_max8903_device = {
 static void __init nuri_power_init(void)
 {
        int gpio;
-       int irq_base = IRQ_GPIO_END + 1;
        int ta_en = 0;
 
-       nuri_max8997_pdata.irq_base = irq_base;
-       irq_base += MAX8997_IRQ_NR;
-
        gpio = EXYNOS4_GPX0(7);
        gpio_request(gpio, "AP_PMIC_IRQ");
        s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0xf));
index 5a12dc26f496a7e2ef3552f1728d66ea11f09ef8..5ca80307d6d7789c9fe1781dd6d56658f64f72e5 100644 (file)
@@ -426,7 +426,6 @@ static struct max8997_platform_data __initdata origen_max8997_pdata = {
        .buck1_gpiodvs  = false,
        .buck2_gpiodvs  = false,
        .buck5_gpiodvs  = false,
-       .irq_base       = IRQ_GPIO_END + 1,
 
        .ignore_gpiodvs_side_effect = true,
        .buck125_default_idx = 0x0,
index 2cdf6ef69beea89fa97c444e9ffa00473dd0e793..d122ee6ab9913aa8e8f2896ea0fee44a01832068 100644 (file)
@@ -69,29 +69,6 @@ void netx_clcd_remove(struct clcd_fb *fb)
                              fb->fb.screen_base, fb->fb.fix.smem_start);
 }
 
-void clk_disable(struct clk *clk)
-{
-}
-
-int clk_set_rate(struct clk *clk, unsigned long rate)
-{
-       return 0;
-}
-
-int clk_enable(struct clk *clk)
-{
-       return 0;
-}
-
-struct clk *clk_get(struct device *dev, const char *id)
-{
-       return dev && strcmp(dev_name(dev), "fb") == 0 ? NULL : ERR_PTR(-ENOENT);
-}
-
-void clk_put(struct clk *clk)
-{
-}
-
 static AMBA_AHB_DEVICE(fb, "fb", 0, 0x00104000, { NETX_IRQ_LCD }, NULL);
 
 int netx_fb_init(struct clcd_board *board, struct clcd_panel *panel)
index da0e37d408237076ae8dda8255fa00e085f325e5..e1362ce48497e53bc60ef3d81e9ee309f0601bfe 100644 (file)
@@ -54,7 +54,6 @@ static struct omap_mmc_platform_data mmc1_data = {
        .nr_slots                       = 1,
        .init                           = mmc_late_init,
        .cleanup                        = mmc_cleanup,
-       .dma_mask                       = 0xffffffff,
        .slots[0]       = {
                .set_power              = mmc_set_power,
                .ocr_mask               = MMC_VDD_32_33 | MMC_VDD_33_34,
index f8242aa9b76321229f400ccf2b0bcb4a3f28c1f0..c74daace8cd686482d3c440eb6a4d1a0472947c7 100644 (file)
@@ -36,7 +36,6 @@ static int mmc_set_power(struct device *dev, int slot, int power_on,
  */
 static struct omap_mmc_platform_data mmc1_data = {
        .nr_slots                       = 1,
-       .dma_mask                       = 0xffffffff,
        .slots[0]       = {
                .set_power              = mmc_set_power,
                .ocr_mask               = MMC_VDD_32_33 | MMC_VDD_33_34,
index 4007a372481b9a9bfac27c9fa8d55d2ed672fa27..2c0ca8fc3380092380b6e2e683dfa9a17460fadf 100644 (file)
@@ -185,7 +185,6 @@ static int nokia770_mmc_get_cover_state(struct device *dev, int slot)
 
 static struct omap_mmc_platform_data nokia770_mmc2_data = {
        .nr_slots                       = 1,
-       .dma_mask                       = 0xffffffff,
        .max_freq                       = 12000000,
        .slots[0]       = {
                .set_power              = nokia770_mmc_set_power,
index cc71a26723efb0937294bfe777b10aa9a59ed746..355980321c2d19f91ecb55c2dd5b7b1a9c7f67dd 100644 (file)
@@ -288,8 +288,7 @@ palmz71_gpio_setup(int early)
                }
                gpio_direction_input(PALMZ71_USBDETECT_GPIO);
                if (request_irq(gpio_to_irq(PALMZ71_USBDETECT_GPIO),
-                               palmz71_powercable, IRQF_SAMPLE_RANDOM,
-                               "palmz71-cable", NULL))
+                               palmz71_powercable, 0, "palmz71-cable", NULL))
                        printk(KERN_ERR
                                        "IRQ request for power cable failed!\n");
                palmz71_powercable(gpio_to_irq(PALMZ71_USBDETECT_GPIO), NULL);
index 2c5d0ed75285153d8126e60a030f08630a277635..677357ff61aca74d0c399ae889c7496cbc7e9ffa 100644 (file)
@@ -468,7 +468,6 @@ static struct omap_mmc_platform_data mmc1_data = {
        .cleanup                        = n8x0_mmc_cleanup,
        .shutdown                       = n8x0_mmc_shutdown,
        .max_freq                       = 24000000,
-       .dma_mask                       = 0xffffffff,
        .slots[0] = {
                .wires                  = 4,
                .set_power              = n8x0_mmc_set_power,
index 5fb47a14f4ba85211010afa34c439a3f1c73b30c..af1ed7d24a1fbb20fd7e1fffba8ebb1f12bbcca5 100644 (file)
@@ -37,6 +37,7 @@
 
 #define DISPC_CONTROL          0x0040
 #define DISPC_CONTROL2         0x0238
+#define DISPC_CONTROL3         0x0848
 #define DISPC_IRQSTATUS                0x0018
 
 #define DSS_SYSCONFIG          0x10
@@ -52,6 +53,7 @@
 #define EVSYNC_EVEN_IRQ_SHIFT  2
 #define EVSYNC_ODD_IRQ_SHIFT   3
 #define FRAMEDONE2_IRQ_SHIFT   22
+#define FRAMEDONE3_IRQ_SHIFT   30
 #define FRAMEDONETV_IRQ_SHIFT  24
 
 /*
@@ -376,7 +378,7 @@ int __init omap_display_init(struct omap_dss_board_info *board_data)
 static void dispc_disable_outputs(void)
 {
        u32 v, irq_mask = 0;
-       bool lcd_en, digit_en, lcd2_en = false;
+       bool lcd_en, digit_en, lcd2_en = false, lcd3_en = false;
        int i;
        struct omap_dss_dispc_dev_attr *da;
        struct omap_hwmod *oh;
@@ -405,7 +407,13 @@ static void dispc_disable_outputs(void)
                lcd2_en = v & LCD_EN_MASK;
        }
 
-       if (!(lcd_en | digit_en | lcd2_en))
+       /* store value of LCDENABLE for LCD3 */
+       if (da->manager_count > 3) {
+               v = omap_hwmod_read(oh, DISPC_CONTROL3);
+               lcd3_en = v & LCD_EN_MASK;
+       }
+
+       if (!(lcd_en | digit_en | lcd2_en | lcd3_en))
                return; /* no managers currently enabled */
 
        /*
@@ -426,10 +434,12 @@ static void dispc_disable_outputs(void)
 
        if (lcd2_en)
                irq_mask |= 1 << FRAMEDONE2_IRQ_SHIFT;
+       if (lcd3_en)
+               irq_mask |= 1 << FRAMEDONE3_IRQ_SHIFT;
 
        /*
         * clear any previous FRAMEDONE, FRAMEDONETV,
-        * EVSYNC_EVEN/ODD or FRAMEDONE2 interrupts
+        * EVSYNC_EVEN/ODD, FRAMEDONE2 or FRAMEDONE3 interrupts
         */
        omap_hwmod_write(irq_mask, oh, DISPC_IRQSTATUS);
 
@@ -445,12 +455,19 @@ static void dispc_disable_outputs(void)
                omap_hwmod_write(v, oh, DISPC_CONTROL2);
        }
 
+       /* disable LCD3 manager */
+       if (da->manager_count > 3) {
+               v = omap_hwmod_read(oh, DISPC_CONTROL3);
+               v &= ~LCD_EN_MASK;
+               omap_hwmod_write(v, oh, DISPC_CONTROL3);
+       }
+
        i = 0;
        while ((omap_hwmod_read(oh, DISPC_IRQSTATUS) & irq_mask) !=
               irq_mask) {
                i++;
                if (i > FRAMEDONE_IRQ_TIMEOUT) {
-                       pr_err("didn't get FRAMEDONE1/2 or TV interrupt\n");
+                       pr_err("didn't get FRAMEDONE1/2/3 or TV interrupt\n");
                        break;
                }
                mdelay(1);
index be697d4e084357e3c2a4938a60f2caab03605fbb..a9675d8d182254744327415ecf42e45a88fbdcb2 100644 (file)
@@ -315,7 +315,6 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
        mmc->slots[0].caps = c->caps;
        mmc->slots[0].pm_caps = c->pm_caps;
        mmc->slots[0].internal_clock = !c->ext_clock;
-       mmc->dma_mask = 0xffffffff;
        mmc->max_freq = c->max_freq;
        if (cpu_is_omap44xx())
                mmc->reg_offset = OMAP4_MMC_REG_OFFSET;
diff --git a/arch/arm/mach-pxa/eseries.h b/arch/arm/mach-pxa/eseries.h
deleted file mode 100644 (file)
index b96949d..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-void __init eseries_fixup(struct tag *tags, char **cmdline, struct meminfo *mi);
-
-extern struct pxa2xx_udc_mach_info e7xx_udc_mach_info;
-extern struct pxaficp_platform_data e7xx_ficp_platform_data;
-extern int e7xx_irda_init(void);
-
-extern int eseries_tmio_enable(struct platform_device *dev);
-extern int eseries_tmio_disable(struct platform_device *dev);
-extern int eseries_tmio_suspend(struct platform_device *dev);
-extern int eseries_tmio_resume(struct platform_device *dev);
-extern void eseries_get_tmio_gpios(void);
-extern struct resource eseries_tmio_resources[];
-extern struct platform_device e300_tc6387xb_device;
-
index d3de84b0dcbed280a27d12092509f6a9df4e181e..e6311988add2425be3e53324fa3b2f16fa40862e 100644 (file)
@@ -296,27 +296,11 @@ static struct asic3_led asic3_leds[ASIC3_NUM_LEDS] = {
 
 static struct resource asic3_resources[] = {
        /* GPIO part */
-       [0] = {
-               .start  = ASIC3_PHYS,
-               .end    = ASIC3_PHYS + ASIC3_MAP_SIZE_16BIT - 1,
-               .flags  = IORESOURCE_MEM,
-       },
-       [1] = {
-               .start  = PXA_GPIO_TO_IRQ(GPIO12_HX4700_ASIC3_IRQ),
-               .end    = PXA_GPIO_TO_IRQ(GPIO12_HX4700_ASIC3_IRQ),
-               .flags  = IORESOURCE_IRQ,
-       },
+       [0] = DEFINE_RES_MEM(ASIC3_PHYS, ASIC3_MAP_SIZE_16BIT),
+       [1] = DEFINE_RES_IRQ(PXA_GPIO_TO_IRQ(GPIO12_HX4700_ASIC3_IRQ)),
        /* SD part */
-       [2] = {
-               .start  = ASIC3_SD_PHYS,
-               .end    = ASIC3_SD_PHYS + ASIC3_MAP_SIZE_16BIT - 1,
-               .flags  = IORESOURCE_MEM,
-       },
-       [3] = {
-               .start  = PXA_GPIO_TO_IRQ(GPIO66_HX4700_ASIC3_nSDIO_IRQ),
-               .end    = PXA_GPIO_TO_IRQ(GPIO66_HX4700_ASIC3_nSDIO_IRQ),
-               .flags  = IORESOURCE_IRQ,
-       },
+       [2] = DEFINE_RES_MEM(ASIC3_SD_PHYS, ASIC3_MAP_SIZE_16BIT),
+       [3] = DEFINE_RES_IRQ(PXA_GPIO_TO_IRQ(GPIO66_HX4700_ASIC3_nSDIO_IRQ)),
 };
 
 static struct asic3_platform_data asic3_platform_data = {
@@ -343,11 +327,7 @@ static struct platform_device asic3 = {
  */
 
 static struct resource egpio_resources[] = {
-       [0] = {
-               .start = PXA_CS5_PHYS,
-               .end   = PXA_CS5_PHYS + 0x4 - 1,
-               .flags = IORESOURCE_MEM,
-       },
+       [0] = DEFINE_RES_MEM(PXA_CS5_PHYS, 0x4),
 };
 
 static struct htc_egpio_chip egpio_chips[] = {
@@ -537,11 +517,7 @@ static struct w100fb_mach_info w3220_info = {
 };
 
 static struct resource w3220_resources[] = {
-       [0] = {
-               .start  = ATI_W3220_PHYS,
-               .end    = ATI_W3220_PHYS + 0x00ffffff,
-               .flags  = IORESOURCE_MEM,
-       },
+       [0] = DEFINE_RES_MEM(ATI_W3220_PHYS, SZ_16M),
 };
 
 static struct platform_device w3220 = {
@@ -683,20 +659,12 @@ static struct pda_power_pdata power_supply_info = {
 };
 
 static struct resource power_supply_resources[] = {
-       [0] = {
-               .name  = "ac",
-               .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE |
-                        IORESOURCE_IRQ_LOWEDGE,
-               .start = PXA_GPIO_TO_IRQ(GPIOD9_nAC_IN),
-               .end   = PXA_GPIO_TO_IRQ(GPIOD9_nAC_IN),
-       },
-       [1] = {
-               .name  = "usb",
-               .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE |
-                        IORESOURCE_IRQ_LOWEDGE,
-               .start = PXA_GPIO_TO_IRQ(GPIOD14_nUSBC_DETECT),
-               .end   = PXA_GPIO_TO_IRQ(GPIOD14_nUSBC_DETECT),
-       },
+       [0] = DEFINE_RES_NAMED(PXA_GPIO_TO_IRQ(GPIOD9_nAC_IN), 1, "ac",
+               IORESOURCE_IRQ |
+               IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE),
+       [1] = DEFINE_RES_NAMED(PXA_GPIO_TO_IRQ(GPIOD14_nUSBC_DETECT), 1, "usb",
+               IORESOURCE_IRQ |
+               IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE),
 };
 
 static struct platform_device power_supply = {
index 6bb3f47b1f14e7070cfeb5c3dbaff57949adac10..0ca0db787903ab104ef9c0f216148cc3c32acee6 100644 (file)
@@ -456,7 +456,7 @@ static int lubbock_mci_init(struct device *dev,
        init_timer(&mmc_timer);
        mmc_timer.data = (unsigned long) data;
        return request_irq(LUBBOCK_SD_IRQ, lubbock_detect_int,
-                       IRQF_SAMPLE_RANDOM, "lubbock-sd-detect", data);
+                          0, "lubbock-sd-detect", data);
 }
 
 static int lubbock_mci_get_ro(struct device *dev)
index 2db697cd2b4ecc9fcb3be872aa0f82cd276b0577..39561dcf65f2668aa585a710fd09cda64c6249e6 100644 (file)
@@ -633,9 +633,8 @@ static struct platform_device bq24022 = {
 static int magician_mci_init(struct device *dev,
                                irq_handler_t detect_irq, void *data)
 {
-       return request_irq(IRQ_MAGICIAN_SD, detect_irq,
-                               IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
-                               "mmc card detect", data);
+       return request_irq(IRQ_MAGICIAN_SD, detect_irq, IRQF_DISABLED,
+                          "mmc card detect", data);
 }
 
 static void magician_mci_exit(struct device *dev, void *data)
index 2b6ac00b2cd98ce24e3e5e34bf56d36698d54c87..166dd32cc1d359e9c8299ea7a07146438d944020 100644 (file)
@@ -332,8 +332,8 @@ static int trizeps4_mci_init(struct device *dev, irq_handler_t mci_detect_int,
        int err;
 
        err = request_irq(TRIZEPS4_MMC_IRQ, mci_detect_int,
-               IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_SAMPLE_RANDOM,
-               "MMC card detect", data);
+                         IRQF_DISABLED | IRQF_TRIGGER_RISING,
+                         "MMC card detect", data);
        if (err) {
                printk(KERN_ERR "trizeps4_mci_init: MMC/SD: can't request"
                                                "MMC card detect IRQ\n");
index fcf3dcabb6944678e9dfabd5cbc67a3e490deade..c0537f40a3d859205fb7609ad912fa8ecf64c053 100644 (file)
@@ -12,6 +12,9 @@
  * published by the Free Software Foundation.
  */
 
+#ifndef __MACH_S3C64XX_PM_CORE_H
+#define __MACH_S3C64XX_PM_CORE_H __FILE__
+
 #include <mach/regs-gpio.h>
 
 static inline void s3c_pm_debug_init_uart(void)
@@ -113,3 +116,4 @@ static inline void samsung_pm_saved_gpios(void)
 
        __raw_writel(S3C64XX_SLPEN_USE_xSLP, S3C64XX_SLPEN);
 }
+#endif /* __MACH_S3C64XX_PM_CORE_H */
index df33909205e2ce6155553a8485512bdd4817422b..4cacc2d22fbeb78b483d4510f4e0eb7a880ef757 100644 (file)
@@ -19,6 +19,7 @@ config ARCH_SH7372
        select CPU_V7
        select SH_CLK_CPG
        select ARCH_WANT_OPTIONAL_GPIOLIB
+       select ARM_CPU_SUSPEND if PM || CPU_IDLE
 
 config ARCH_SH73A0
        bool "SH-Mobile AG5 (R8A73A00)"
@@ -58,6 +59,7 @@ config MACH_G4EVM
        bool "G4EVM board"
        depends on ARCH_SH7377
        select ARCH_REQUIRE_GPIOLIB
+       select REGULATOR_FIXED_VOLTAGE if REGULATOR
 
 config MACH_AP4EVB
        bool "AP4EVB board"
@@ -65,6 +67,7 @@ config MACH_AP4EVB
        select ARCH_REQUIRE_GPIOLIB
        select SH_LCD_MIPI_DSI
        select SND_SOC_AK4642 if SND_SIMPLE_CARD
+       select REGULATOR_FIXED_VOLTAGE if REGULATOR
 
 choice
        prompt "AP4EVB LCD panel selection"
@@ -83,6 +86,7 @@ config MACH_AG5EVM
        bool "AG5EVM board"
        select ARCH_REQUIRE_GPIOLIB
        select SH_LCD_MIPI_DSI
+       select REGULATOR_FIXED_VOLTAGE if REGULATOR
        depends on ARCH_SH73A0
 
 config MACH_MACKEREL
@@ -90,15 +94,18 @@ config MACH_MACKEREL
        depends on ARCH_SH7372
        select ARCH_REQUIRE_GPIOLIB
        select SND_SOC_AK4642 if SND_SIMPLE_CARD
+       select REGULATOR_FIXED_VOLTAGE if REGULATOR
 
 config MACH_KOTA2
        bool "KOTA2 board"
        select ARCH_REQUIRE_GPIOLIB
+       select REGULATOR_FIXED_VOLTAGE if REGULATOR
        depends on ARCH_SH73A0
 
 config MACH_BONITO
        bool "bonito board"
        select ARCH_REQUIRE_GPIOLIB
+       select REGULATOR_FIXED_VOLTAGE if REGULATOR
        depends on ARCH_R8A7740
 
 config MACH_ARMADILLO800EVA
@@ -106,22 +113,28 @@ config MACH_ARMADILLO800EVA
        depends on ARCH_R8A7740
        select ARCH_REQUIRE_GPIOLIB
        select USE_OF
+       select REGULATOR_FIXED_VOLTAGE if REGULATOR
+       select SND_SOC_WM8978 if SND_SIMPLE_CARD
 
 config MACH_MARZEN
        bool "MARZEN board"
        depends on ARCH_R8A7779
        select ARCH_REQUIRE_GPIOLIB
+       select REGULATOR_FIXED_VOLTAGE if REGULATOR
 
 config MACH_KZM9D
        bool "KZM9D board"
        depends on ARCH_EMEV2
        select USE_OF
+       select REGULATOR_FIXED_VOLTAGE if REGULATOR
 
 config MACH_KZM9G
        bool "KZM-A9-GT board"
        depends on ARCH_SH73A0
        select ARCH_REQUIRE_GPIOLIB
        select USE_OF
+       select SND_SOC_AK4642 if SND_SIMPLE_CARD
+       select REGULATOR_FIXED_VOLTAGE if REGULATOR
 
 comment "SH-Mobile System Configuration"
 
index 8aa1962c22a278dd233022b0886b9a77bba39af4..0df5ae6740c6e491895cc20adfda5096437beb74 100644 (file)
@@ -39,7 +39,9 @@ obj-$(CONFIG_ARCH_R8A7740)    += entry-intc.o
 # PM objects
 obj-$(CONFIG_SUSPEND)          += suspend.o
 obj-$(CONFIG_CPU_IDLE)         += cpuidle.o
+obj-$(CONFIG_ARCH_SHMOBILE)    += pm-rmobile.o
 obj-$(CONFIG_ARCH_SH7372)      += pm-sh7372.o sleep-sh7372.o
+obj-$(CONFIG_ARCH_R8A7740)     += pm-r8a7740.o
 obj-$(CONFIG_ARCH_R8A7779)     += pm-r8a7779.o
 
 # Board objects
index 5a6f22f05e99bc3a166a92d968f48e3d43c56e5b..d82c010fdfc6f7682f2c4774db3038656a96a3a6 100644 (file)
@@ -27,6 +27,8 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/dma-mapping.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
 #include <linux/serial_sci.h>
 #include <linux/smsc911x.h>
 #include <linux/gpio.h>
 #include <asm/hardware/cache-l2x0.h>
 #include <asm/traps.h>
 
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+       REGULATOR_SUPPLY("vddvario", "smsc911x"),
+       REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
+
 static struct resource smsc9220_resources[] = {
        [0] = {
                .start          = 0x14000000,
@@ -142,6 +150,13 @@ static struct platform_device fsi_device = {
        .resource       = fsi_resources,
 };
 
+/* Fixed 1.8V regulator to be used by MMCIF */
+static struct regulator_consumer_supply fixed1v8_power_consumers[] =
+{
+       REGULATOR_SUPPLY("vmmc", "sh_mmcif.0"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mmcif.0"),
+};
+
 static struct resource sh_mmcif_resources[] = {
        [0] = {
                .name   = "MMCIF",
@@ -364,6 +379,13 @@ static struct platform_device mipidsi0_device = {
        },
 };
 
+/* Fixed 2.8V regulators to be used by SDHI0 */
+static struct regulator_consumer_supply fixed2v8_power_consumers[] =
+{
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"),
+};
+
 /* SDHI0 */
 static struct sh_mobile_sdhi_info sdhi0_info = {
        .dma_slave_tx   = SHDMA_SLAVE_SDHI0_TX,
@@ -408,8 +430,57 @@ static struct platform_device sdhi0_device = {
        },
 };
 
-void ag5evm_sdhi1_set_pwr(struct platform_device *pdev, int state)
+/* Fixed 3.3V regulator to be used by SDHI1 */
+static struct regulator_consumer_supply cn4_power_consumers[] =
 {
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.1"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.1"),
+};
+
+static struct regulator_init_data cn4_power_init_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .num_consumer_supplies  = ARRAY_SIZE(cn4_power_consumers),
+       .consumer_supplies      = cn4_power_consumers,
+};
+
+static struct fixed_voltage_config cn4_power_info = {
+       .supply_name = "CN4 SD/MMC Vdd",
+       .microvolts = 3300000,
+       .gpio = GPIO_PORT114,
+       .enable_high = 1,
+       .init_data = &cn4_power_init_data,
+};
+
+static struct platform_device cn4_power = {
+       .name = "reg-fixed-voltage",
+       .id   = 2,
+       .dev  = {
+               .platform_data = &cn4_power_info,
+       },
+};
+
+static void ag5evm_sdhi1_set_pwr(struct platform_device *pdev, int state)
+{
+       static int power_gpio = -EINVAL;
+
+       if (power_gpio < 0) {
+               int ret = gpio_request(GPIO_PORT114, "sdhi1_power");
+               if (!ret) {
+                       power_gpio = GPIO_PORT114;
+                       gpio_direction_output(power_gpio, 0);
+               }
+       }
+
+       /*
+        * If requesting the GPIO above failed, it means, that the regulator got
+        * probed and grabbed the GPIO, but we don't know, whether the sdhi
+        * driver already uses the regulator. If it doesn't, we have to toggle
+        * the GPIO ourselves, even though it is now owned by the fixed
+        * regulator driver. We have to live with the race in case the driver
+        * gets unloaded and the GPIO freed between these two steps.
+        */
        gpio_set_value(GPIO_PORT114, state);
 }
 
@@ -455,6 +526,7 @@ static struct platform_device sdhi1_device = {
 };
 
 static struct platform_device *ag5evm_devices[] __initdata = {
+       &cn4_power,
        &eth_device,
        &keysc_device,
        &fsi_device,
@@ -468,6 +540,12 @@ static struct platform_device *ag5evm_devices[] __initdata = {
 
 static void __init ag5evm_init(void)
 {
+       regulator_register_always_on(0, "fixed-1.8V", fixed1v8_power_consumers,
+                                    ARRAY_SIZE(fixed1v8_power_consumers), 1800000);
+       regulator_register_always_on(1, "fixed-2.8V", fixed2v8_power_consumers,
+                                    ARRAY_SIZE(fixed2v8_power_consumers), 3300000);
+       regulator_register_fixed(3, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
        sh73a0_pinmux_init();
 
        /* enable SCIFA2 */
@@ -562,8 +640,6 @@ static void __init ag5evm_init(void)
        gpio_request(GPIO_FN_SDHID1_2_PU, NULL);
        gpio_request(GPIO_FN_SDHID1_1_PU, NULL);
        gpio_request(GPIO_FN_SDHID1_0_PU, NULL);
-       gpio_request(GPIO_PORT114, "sdhi1_power");
-       gpio_direction_output(GPIO_PORT114, 0);
 
 #ifdef CONFIG_CACHE_L2X0
        /* Shared attribute override enable, 64K*8way */
index ace60246a5dfd429d3ffd98c778c6910cd839d39..f172ca85905cdf68d114d046ea987e43b0570af9 100644 (file)
@@ -34,6 +34,8 @@
 #include <linux/i2c.h>
 #include <linux/i2c/tsc2007.h>
 #include <linux/io.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
 #include <linux/smsc911x.h>
 #include <linux/sh_intc.h>
 #include <linux/sh_clk.h>
  * CN12: 3.3v
  */
 
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply fixed1v8_power_consumers[] =
+{
+       /* J22 default position: 1.8V */
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.1"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.1"),
+       REGULATOR_SUPPLY("vmmc", "sh_mmcif.0"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mmcif.0"),
+};
+
+static struct regulator_consumer_supply fixed3v3_power_consumers[] =
+{
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"),
+};
+
+static struct regulator_consumer_supply dummy_supplies[] = {
+       REGULATOR_SUPPLY("vddvario", "smsc911x"),
+       REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
+
 /* MTD */
 static struct mtd_partition nor_flash_partitions[] = {
        {
@@ -1138,21 +1161,6 @@ static void __init fsi_init_pm_clock(void)
        clk_put(fsia_ick);
 }
 
-/*
- * FIXME !!
- *
- * gpio_no_direction
- * are quick_hack.
- *
- * current gpio frame work doesn't have
- * the method to control only pull up/down/free.
- * this function should be replaced by correct gpio function
- */
-static void __init gpio_no_direction(u32 addr)
-{
-       __raw_writeb(0x00, addr);
-}
-
 /* TouchScreen */
 #ifdef CONFIG_AP4EVB_QHD
 # define GPIO_TSC_IRQ  GPIO_FN_IRQ28_123
@@ -1224,6 +1232,12 @@ static void __init ap4evb_init(void)
        u32 srcr4;
        struct clk *clk;
 
+       regulator_register_always_on(0, "fixed-1.8V", fixed1v8_power_consumers,
+                                    ARRAY_SIZE(fixed1v8_power_consumers), 1800000);
+       regulator_register_always_on(1, "fixed-3.3V", fixed3v3_power_consumers,
+                                    ARRAY_SIZE(fixed3v3_power_consumers), 3300000);
+       regulator_register_fixed(2, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
        /* External clock source */
        clk_set_rate(&sh7372_dv_clki_clk, 27000000);
 
@@ -1302,8 +1316,8 @@ static void __init ap4evb_init(void)
 
        gpio_request(GPIO_PORT9, NULL);
        gpio_request(GPIO_PORT10, NULL);
-       gpio_no_direction(GPIO_PORT9CR);  /* FSIAOBT needs no direction */
-       gpio_no_direction(GPIO_PORT10CR); /* FSIAOLR needs no direction */
+       gpio_direction_none(GPIO_PORT9CR);  /* FSIAOBT needs no direction */
+       gpio_direction_none(GPIO_PORT10CR); /* FSIAOLR needs no direction */
 
        /* card detect pin for MMC slot (CN7) */
        gpio_request(GPIO_PORT41, NULL);
@@ -1447,14 +1461,14 @@ static void __init ap4evb_init(void)
 
        platform_add_devices(ap4evb_devices, ARRAY_SIZE(ap4evb_devices));
 
-       sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc1_device);
-       sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device);
-       sh7372_add_device_to_domain(&sh7372_a4mp, &fsi_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4lc, &lcdc1_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4lc, &lcdc_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4mp, &fsi_device);
 
-       sh7372_add_device_to_domain(&sh7372_a3sp, &sh_mmcif_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &sdhi0_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &sdhi1_device);
-       sh7372_add_device_to_domain(&sh7372_a4r, &ceu_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &sh_mmcif_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &sdhi0_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &sdhi1_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4r, &ceu_device);
 
        hdmi_init_pm_clock();
        fsi_init_pm_clock();
index 9bd135531d76118feecbc2a02647344ce21b25c5..cf10f92856dcbb905712024b1d6a00e55c77cf51 100644 (file)
@@ -28,6 +28,8 @@
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
 #include <linux/gpio_keys.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
 #include <linux/sh_eth.h>
 #include <linux/videodev2.h>
 #include <linux/usb/renesas_usbhs.h>
 #include <linux/mmc/sh_mobile_sdhi.h>
 #include <mach/common.h>
 #include <mach/irqs.h>
+#include <mach/r8a7740.h>
+#include <media/mt9t112.h>
+#include <media/sh_mobile_ceu.h>
+#include <media/soc_camera.h>
 #include <asm/page.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 #include <asm/mach/time.h>
 #include <asm/hardware/cache-l2x0.h>
-#include <mach/r8a7740.h>
 #include <video/sh_mobile_lcdc.h>
+#include <video/sh_mobile_hdmi.h>
+#include <sound/sh_fsi.h>
+#include <sound/simple_card.h>
 
 /*
  * CON1                Camera Module
  *-----------+---------------+----------------------------
  */
 
+/*
+ * FSI-WM8978
+ *
+ * this command is required when playback.
+ *
+ * # amixer set "Headphone" 50
+ */
+
 /*
  * USB function
  *
  * These are a little bit complex.
  * see
  *     usbhsf_power_ctrl()
- *
- * CAUTION
- *
- * It uses autonomy mode for USB hotplug at this point
- * (= usbhs_private.platform_callback.get_vbus is NULL),
- * since we don't know what's happen on PM control
- * on this workaround.
  */
+#define IRQ7           evt2irq(0x02e0)
 #define USBCR1         0xe605810a
 #define USBH           0xC6700000
 #define USBH_USBCTR    0x10834
@@ -204,6 +214,20 @@ static void usbhsf_power_ctrl(struct platform_device *pdev,
        }
 }
 
+static int usbhsf_get_vbus(struct platform_device *pdev)
+{
+       return gpio_get_value(GPIO_PORT209);
+}
+
+static irqreturn_t usbhsf_interrupt(int irq, void *data)
+{
+       struct platform_device *pdev = data;
+
+       renesas_usbhs_call_notify_hotplug(pdev);
+
+       return IRQ_HANDLED;
+}
+
 static void usbhsf_hardware_exit(struct platform_device *pdev)
 {
        struct usbhsf_private *priv = usbhsf_get_priv(pdev);
@@ -227,11 +251,14 @@ static void usbhsf_hardware_exit(struct platform_device *pdev)
        priv->host      = NULL;
        priv->func      = NULL;
        priv->usbh_base = NULL;
+
+       free_irq(IRQ7, pdev);
 }
 
 static int usbhsf_hardware_init(struct platform_device *pdev)
 {
        struct usbhsf_private *priv = usbhsf_get_priv(pdev);
+       int ret;
 
        priv->phy       = clk_get(&pdev->dev, "phy");
        priv->usb24     = clk_get(&pdev->dev, "usb24");
@@ -251,6 +278,14 @@ static int usbhsf_hardware_init(struct platform_device *pdev)
                return -EIO;
        }
 
+       ret = request_irq(IRQ7, usbhsf_interrupt, IRQF_TRIGGER_NONE,
+                         dev_name(&pdev->dev), pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "request_irq err\n");
+               return ret;
+       }
+       irq_set_irq_type(IRQ7, IRQ_TYPE_EDGE_BOTH);
+
        /* usb24 use 1/1 of parent clock (= usb24s = 24MHz) */
        clk_set_rate(priv->usb24,
                     clk_get_rate(clk_get_parent(priv->usb24)));
@@ -262,6 +297,7 @@ static struct usbhsf_private usbhsf_private = {
        .info = {
                .platform_callback = {
                        .get_id         = usbhsf_get_id,
+                       .get_vbus       = usbhsf_get_vbus,
                        .hardware_init  = usbhsf_hardware_init,
                        .hardware_exit  = usbhsf_hardware_exit,
                        .power_ctrl     = usbhsf_power_ctrl,
@@ -269,6 +305,8 @@ static struct usbhsf_private usbhsf_private = {
                .driver_param = {
                        .buswait_bwait          = 5,
                        .detection_delay        = 5,
+                       .d0_rx_id       = SHDMA_SLAVE_USBHS_RX,
+                       .d1_tx_id       = SHDMA_SLAVE_USBHS_TX,
                },
        }
 };
@@ -384,6 +422,103 @@ static struct platform_device lcdc0_device = {
        },
 };
 
+/*
+ * LCDC1/HDMI
+ */
+static struct sh_mobile_hdmi_info hdmi_info = {
+       .flags          = HDMI_OUTPUT_PUSH_PULL |
+                         HDMI_OUTPUT_POLARITY_HI |
+                         HDMI_32BIT_REG |
+                         HDMI_HAS_HTOP1 |
+                         HDMI_SND_SRC_SPDIF,
+};
+
+static struct resource hdmi_resources[] = {
+       [0] = {
+               .name   = "HDMI",
+               .start  = 0xe6be0000,
+               .end    = 0xe6be03ff,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = evt2irq(0x1700),
+               .flags  = IORESOURCE_IRQ,
+       },
+       [2] = {
+               .name   = "HDMI emma3pf",
+               .start  = 0xe6be4000,
+               .end    = 0xe6be43ff,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device hdmi_device = {
+       .name           = "sh-mobile-hdmi",
+       .num_resources  = ARRAY_SIZE(hdmi_resources),
+       .resource       = hdmi_resources,
+       .id             = -1,
+       .dev    = {
+               .platform_data  = &hdmi_info,
+       },
+};
+
+static const struct fb_videomode lcdc1_mode = {
+       .name           = "HDMI 720p",
+       .xres           = 1280,
+       .yres           = 720,
+       .pixclock       = 13468,
+       .left_margin    = 220,
+       .right_margin   = 110,
+       .hsync_len      = 40,
+       .upper_margin   = 20,
+       .lower_margin   = 5,
+       .vsync_len      = 5,
+       .refresh        = 60,
+       .sync           = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT,
+};
+
+static struct sh_mobile_lcdc_info hdmi_lcdc_info = {
+       .clock_source   = LCDC_CLK_PERIPHERAL, /* HDMI clock */
+       .ch[0] = {
+               .chan                   = LCDC_CHAN_MAINLCD,
+               .fourcc                 = V4L2_PIX_FMT_RGB565,
+               .interface_type         = RGB24,
+               .clock_divider          = 1,
+               .flags                  = LCDC_FLAGS_DWPOL,
+               .lcd_modes              = &lcdc1_mode,
+               .num_modes              = 1,
+               .tx_dev                 = &hdmi_device,
+               .panel_cfg = {
+                       .width  = 1280,
+                       .height = 720,
+               },
+       },
+};
+
+static struct resource hdmi_lcdc_resources[] = {
+       [0] = {
+               .name   = "LCDC1",
+               .start  = 0xfe944000,
+               .end    = 0xfe948000 - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = intcs_evt2irq(0x1780),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device hdmi_lcdc_device = {
+       .name           = "sh_mobile_lcdc_fb",
+       .num_resources  = ARRAY_SIZE(hdmi_lcdc_resources),
+       .resource       = hdmi_lcdc_resources,
+       .id             = 1,
+       .dev    = {
+               .platform_data  = &hdmi_lcdc_info,
+               .coherent_dma_mask = ~0,
+       },
+};
+
 /* GPIO KEY */
 #define GPIO_KEY(c, g, d) { .code = c, .gpio = g, .desc = d, .active_low = 1 }
 
@@ -407,6 +542,17 @@ static struct platform_device gpio_keys_device = {
        },
 };
 
+/* Fixed 3.3V regulator to be used by SDHI0, SDHI1, MMCIF */
+static struct regulator_consumer_supply fixed3v3_power_consumers[] =
+{
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"),
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.1"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.1"),
+       REGULATOR_SUPPLY("vmmc", "sh_mmcif"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mmcif"),
+};
+
 /* SDHI0 */
 /*
  * FIXME
@@ -418,6 +564,8 @@ static struct platform_device gpio_keys_device = {
  */
 #define IRQ31  evt2irq(0x33E0)
 static struct sh_mobile_sdhi_info sdhi0_info = {
+       .dma_slave_tx   = SHDMA_SLAVE_SDHI0_TX,
+       .dma_slave_rx   = SHDMA_SLAVE_SDHI0_RX,
        .tmio_caps      = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |\
                          MMC_CAP_NEEDS_POLL,
        .tmio_ocr_mask  = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34,
@@ -458,6 +606,8 @@ static struct platform_device sdhi0_device = {
 
 /* SDHI1 */
 static struct sh_mobile_sdhi_info sdhi1_info = {
+       .dma_slave_tx   = SHDMA_SLAVE_SDHI1_TX,
+       .dma_slave_rx   = SHDMA_SLAVE_SDHI1_RX,
        .tmio_caps      = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
        .tmio_ocr_mask  = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34,
        .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT,
@@ -532,12 +682,209 @@ static struct platform_device sh_mmcif_device = {
        .resource       = sh_mmcif_resources,
 };
 
+/* Camera */
+static int mt9t111_power(struct device *dev, int mode)
+{
+       struct clk *mclk = clk_get(NULL, "video1");
+
+       if (IS_ERR(mclk)) {
+               dev_err(dev, "can't get video1 clock\n");
+               return -EINVAL;
+       }
+
+       if (mode) {
+               /* video1 (= CON1 camera) expect 24MHz */
+               clk_set_rate(mclk, clk_round_rate(mclk, 24000000));
+               clk_enable(mclk);
+               gpio_direction_output(GPIO_PORT158, 1);
+       } else {
+               gpio_direction_output(GPIO_PORT158, 0);
+               clk_disable(mclk);
+       }
+
+       clk_put(mclk);
+
+       return 0;
+}
+
+static struct i2c_board_info i2c_camera_mt9t111 = {
+       I2C_BOARD_INFO("mt9t112", 0x3d),
+};
+
+static struct mt9t112_camera_info mt9t111_info = {
+       .divider = { 16, 0, 0, 7, 0, 10, 14, 7, 7 },
+};
+
+static struct soc_camera_link mt9t111_link = {
+       .i2c_adapter_id = 0,
+       .bus_id         = 0,
+       .board_info     = &i2c_camera_mt9t111,
+       .power          = mt9t111_power,
+       .priv           = &mt9t111_info,
+};
+
+static struct platform_device camera_device = {
+       .name   = "soc-camera-pdrv",
+       .id     = 0,
+       .dev    = {
+               .platform_data = &mt9t111_link,
+       },
+};
+
+/* CEU0 */
+static struct sh_mobile_ceu_info sh_mobile_ceu0_info = {
+       .flags = SH_CEU_FLAG_LOWER_8BIT,
+};
+
+static struct resource ceu0_resources[] = {
+       [0] = {
+               .name   = "CEU",
+               .start  = 0xfe910000,
+               .end    = 0xfe91009f,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = intcs_evt2irq(0x0500),
+               .flags  = IORESOURCE_IRQ,
+       },
+       [2] = {
+               /* place holder for contiguous memory */
+       },
+};
+
+static struct platform_device ceu0_device = {
+       .name           = "sh_mobile_ceu",
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(ceu0_resources),
+       .resource       = ceu0_resources,
+       .dev    = {
+               .platform_data          = &sh_mobile_ceu0_info,
+               .coherent_dma_mask      = 0xffffffff,
+       },
+};
+
+/* FSI */
+static int fsi_hdmi_set_rate(struct device *dev, int rate, int enable)
+{
+       struct clk *fsib;
+       int ret;
+
+       /* it support 48KHz only */
+       if (48000 != rate)
+               return -EINVAL;
+
+       fsib = clk_get(dev, "ickb");
+       if (IS_ERR(fsib))
+               return -EINVAL;
+
+       if (enable) {
+               ret = SH_FSI_ACKMD_256 | SH_FSI_BPFMD_64;
+               clk_enable(fsib);
+       } else {
+               ret = 0;
+               clk_disable(fsib);
+       }
+
+       clk_put(fsib);
+
+       return ret;
+}
+
+static struct sh_fsi_platform_info fsi_info = {
+       /* FSI-WM8978 */
+       .port_a = {
+               .tx_id = SHDMA_SLAVE_FSIA_TX,
+       },
+       /* FSI-HDMI */
+       .port_b = {
+               .flags          = SH_FSI_FMT_SPDIF |
+                                 SH_FSI_ENABLE_STREAM_MODE,
+               .set_rate       = fsi_hdmi_set_rate,
+               .tx_id          = SHDMA_SLAVE_FSIB_TX,
+       }
+};
+
+static struct resource fsi_resources[] = {
+       [0] = {
+               .name   = "FSI",
+               .start  = 0xfe1f0000,
+               .end    = 0xfe1f8400 - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = evt2irq(0x1840),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device fsi_device = {
+       .name           = "sh_fsi2",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(fsi_resources),
+       .resource       = fsi_resources,
+       .dev    = {
+               .platform_data  = &fsi_info,
+       },
+};
+
+/* FSI-WM8978 */
+static struct asoc_simple_dai_init_info fsi_wm8978_init_info = {
+       .fmt            = SND_SOC_DAIFMT_I2S,
+       .codec_daifmt   = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_NB_NF,
+       .cpu_daifmt     = SND_SOC_DAIFMT_CBS_CFS,
+       .sysclk         = 12288000,
+};
+
+static struct asoc_simple_card_info fsi_wm8978_info = {
+       .name           = "wm8978",
+       .card           = "FSI2A-WM8978",
+       .cpu_dai        = "fsia-dai",
+       .codec          = "wm8978.0-001a",
+       .platform       = "sh_fsi2",
+       .codec_dai      = "wm8978-hifi",
+       .init           = &fsi_wm8978_init_info,
+};
+
+static struct platform_device fsi_wm8978_device = {
+       .name   = "asoc-simple-card",
+       .id     = 0,
+       .dev    = {
+               .platform_data  = &fsi_wm8978_info,
+       },
+};
+
+/* FSI-HDMI */
+static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = {
+       .cpu_daifmt     = SND_SOC_DAIFMT_CBM_CFM,
+};
+
+static struct asoc_simple_card_info fsi2_hdmi_info = {
+       .name           = "HDMI",
+       .card           = "FSI2B-HDMI",
+       .cpu_dai        = "fsib-dai",
+       .codec          = "sh-mobile-hdmi",
+       .platform       = "sh_fsi2",
+       .codec_dai      = "sh_mobile_hdmi-hifi",
+       .init           = &fsi2_hdmi_init_info,
+};
+
+static struct platform_device fsi_hdmi_device = {
+       .name   = "asoc-simple-card",
+       .id     = 1,
+       .dev    = {
+               .platform_data  = &fsi2_hdmi_info,
+       },
+};
+
 /* I2C */
 static struct i2c_board_info i2c0_devices[] = {
        {
                I2C_BOARD_INFO("st1232-ts", 0x55),
                .irq = evt2irq(0x0340),
        },
+       {
+               I2C_BOARD_INFO("wm8978", 0x1a),
+       },
 };
 
 /*
@@ -549,6 +896,13 @@ static struct platform_device *eva_devices[] __initdata = {
        &sh_eth_device,
        &sdhi0_device,
        &sh_mmcif_device,
+       &hdmi_device,
+       &hdmi_lcdc_device,
+       &camera_device,
+       &ceu0_device,
+       &fsi_device,
+       &fsi_hdmi_device,
+       &fsi_wm8978_device,
 };
 
 static void __init eva_clock_init(void)
@@ -556,10 +910,14 @@ static void __init eva_clock_init(void)
        struct clk *system      = clk_get(NULL, "system_clk");
        struct clk *xtal1       = clk_get(NULL, "extal1");
        struct clk *usb24s      = clk_get(NULL, "usb24s");
+       struct clk *fsibck      = clk_get(NULL, "fsibck");
+       struct clk *fsib        = clk_get(&fsi_device.dev, "ickb");
 
        if (IS_ERR(system)      ||
            IS_ERR(xtal1)       ||
-           IS_ERR(usb24s)) {
+           IS_ERR(usb24s)      ||
+           IS_ERR(fsibck)      ||
+           IS_ERR(fsib)) {
                pr_err("armadillo800eva board clock init failed\n");
                goto clock_error;
        }
@@ -570,6 +928,11 @@ static void __init eva_clock_init(void)
        /* usb24s use extal1 (= system) clock (= 24MHz) */
        clk_set_parent(usb24s, system);
 
+       /* FSIBCK is 12.288MHz, and it is parent of FSI-B */
+       clk_set_parent(fsib, fsibck);
+       clk_set_rate(fsibck, 12288000);
+       clk_set_rate(fsib,   12288000);
+
 clock_error:
        if (!IS_ERR(system))
                clk_put(system);
@@ -577,16 +940,26 @@ clock_error:
                clk_put(xtal1);
        if (!IS_ERR(usb24s))
                clk_put(usb24s);
+       if (!IS_ERR(fsibck))
+               clk_put(fsibck);
+       if (!IS_ERR(fsib))
+               clk_put(fsib);
 }
 
 /*
  * board init
  */
+#define GPIO_PORT7CR   0xe6050007
+#define GPIO_PORT8CR   0xe6050008
 static void __init eva_init(void)
 {
-       eva_clock_init();
+       struct platform_device *usb = NULL;
+
+       regulator_register_always_on(0, "fixed-3.3V", fixed3v3_power_consumers,
+                                    ARRAY_SIZE(fixed3v3_power_consumers), 3300000);
 
        r8a7740_pinmux_init();
+       r8a7740_meram_workaround();
 
        /* SCIFA1 */
        gpio_request(GPIO_FN_SCIFA1_RXD, NULL);
@@ -667,8 +1040,19 @@ static void __init eva_init(void)
                /* USB Host */
        } else {
                /* USB Func */
-               gpio_request(GPIO_FN_VBUS, NULL);
+               /*
+                * A1 chip has 2 IRQ7 pin and it was controled by MSEL register.
+                * OTOH, usbhs interrupt needs its value (HI/LOW) to decide
+                * USB connection/disconnection (usbhsf_get_vbus()).
+                * This means we needs to select GPIO_FN_IRQ7_PORT209 first,
+                * and select GPIO_PORT209 here
+                */
+               gpio_request(GPIO_FN_IRQ7_PORT209, NULL);
+               gpio_request(GPIO_PORT209, NULL);
+               gpio_direction_input(GPIO_PORT209);
+
                platform_device_register(&usbhsf_device);
+               usb = &usbhsf_device;
        }
 
        /* SDHI0 */
@@ -706,6 +1090,48 @@ static void __init eva_init(void)
        gpio_request(GPIO_FN_MMC1_D6_PORT143,   NULL);
        gpio_request(GPIO_FN_MMC1_D7_PORT142,   NULL);
 
+       /* CEU0 */
+       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);
+       gpio_request(GPIO_FN_VIO0_CLK,          NULL);
+       gpio_request(GPIO_FN_VIO0_HD,           NULL);
+       gpio_request(GPIO_FN_VIO0_VD,           NULL);
+       gpio_request(GPIO_FN_VIO0_FIELD,        NULL);
+       gpio_request(GPIO_FN_VIO_CKO,           NULL);
+
+       /* CON1/CON15 Camera */
+       gpio_request(GPIO_PORT173, NULL); /* STANDBY */
+       gpio_request(GPIO_PORT172, NULL); /* RST */
+       gpio_request(GPIO_PORT158, NULL); /* CAM_PON */
+       gpio_direction_output(GPIO_PORT173, 0);
+       gpio_direction_output(GPIO_PORT172, 1);
+       gpio_direction_output(GPIO_PORT158, 0); /* see mt9t111_power() */
+
+       /* FSI-WM8978 */
+       gpio_request(GPIO_FN_FSIAIBT,           NULL);
+       gpio_request(GPIO_FN_FSIAILR,           NULL);
+       gpio_request(GPIO_FN_FSIAOMC,           NULL);
+       gpio_request(GPIO_FN_FSIAOSLD,          NULL);
+       gpio_request(GPIO_FN_FSIAISLD_PORT5,    NULL);
+
+       gpio_request(GPIO_PORT7, NULL);
+       gpio_request(GPIO_PORT8, NULL);
+       gpio_direction_none(GPIO_PORT7CR); /* FSIAOBT needs no direction */
+       gpio_direction_none(GPIO_PORT8CR); /* FSIAOLR needs no direction */
+
+       /* FSI-HDMI */
+       gpio_request(GPIO_FN_FSIBCK,            NULL);
+
+       /* HDMI */
+       gpio_request(GPIO_FN_HDMI_HPD,          NULL);
+       gpio_request(GPIO_FN_HDMI_CEC,          NULL);
+
        /*
         * CAUTION
         *
@@ -752,6 +1178,13 @@ static void __init eva_init(void)
 
        platform_add_devices(eva_devices,
                             ARRAY_SIZE(eva_devices));
+
+       eva_clock_init();
+
+       rmobile_add_device_to_domain(&r8a7740_pd_a4lc, &lcdc0_device);
+       rmobile_add_device_to_domain(&r8a7740_pd_a4lc, &hdmi_lcdc_device);
+       if (usb)
+               rmobile_add_device_to_domain(&r8a7740_pd_a3sp, usb);
 }
 
 static void __init eva_earlytimer_init(void)
index e9b32cfbf741061f378fedf05b556553cdd6ceb3..4129008eae290d445a274ca6c1a9ed27c91c37bc 100644 (file)
@@ -26,6 +26,8 @@
 #include <linux/irq.h>
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
 #include <linux/smsc911x.h>
 #include <linux/videodev2.h>
 #include <mach/common.h>
  * S38.2 = OFF
  */
 
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+       REGULATOR_SUPPLY("vddvario", "smsc911x"),
+       REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
+
 /*
  * FPGA
  */
@@ -360,6 +368,8 @@ static void __init bonito_init(void)
 {
        u16 val;
 
+       regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
        r8a7740_pinmux_init();
        bonito_fpga_init();
 
index f1257321999a3ca585b15706d413077339552f46..fa5dfc5c8ed6ecd91966e178db9d010e499111c7 100644 (file)
@@ -26,6 +26,8 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/physmap.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
 #include <linux/usb/r8a66597.h>
 #include <linux/io.h>
 #include <linux/input.h>
@@ -196,6 +198,15 @@ static struct platform_device keysc_device = {
        },
 };
 
+/* Fixed 3.3V regulator to be used by SDHI0 and SDHI1 */
+static struct regulator_consumer_supply fixed3v3_power_consumers[] =
+{
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"),
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.1"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.1"),
+};
+
 /* SDHI */
 static struct sh_mobile_sdhi_info sdhi0_info = {
        .tmio_caps      = MMC_CAP_SDIO_IRQ,
@@ -271,26 +282,11 @@ static struct platform_device *g4evm_devices[] __initdata = {
 #define GPIO_SDHID1_D3 0xe6052106
 #define GPIO_SDHICMD1  0xe6052107
 
-/*
- * FIXME !!
- *
- * gpio_pull_up is quick_hack.
- *
- * current gpio frame work doesn't have
- * the method to control only pull up/down/free.
- * this function should be replaced by correct gpio function
- */
-static void __init gpio_pull_up(u32 addr)
-{
-       u8 data = __raw_readb(addr);
-
-       data &= 0x0F;
-       data |= 0xC0;
-       __raw_writeb(data, addr);
-}
-
 static void __init g4evm_init(void)
 {
+       regulator_register_always_on(0, "fixed-3.3V", fixed3v3_power_consumers,
+                                    ARRAY_SIZE(fixed3v3_power_consumers), 3300000);
+
        sh7377_pinmux_init();
 
        /* Lit DS14 LED */
@@ -351,11 +347,11 @@ static void __init g4evm_init(void)
        gpio_request(GPIO_FN_SDHID0_3, NULL);
        gpio_request(GPIO_FN_SDHICMD0, NULL);
        gpio_request(GPIO_FN_SDHIWP0, NULL);
-       gpio_pull_up(GPIO_SDHID0_D0);
-       gpio_pull_up(GPIO_SDHID0_D1);
-       gpio_pull_up(GPIO_SDHID0_D2);
-       gpio_pull_up(GPIO_SDHID0_D3);
-       gpio_pull_up(GPIO_SDHICMD0);
+       gpio_request_pullup(GPIO_SDHID0_D0);
+       gpio_request_pullup(GPIO_SDHID0_D1);
+       gpio_request_pullup(GPIO_SDHID0_D2);
+       gpio_request_pullup(GPIO_SDHID0_D3);
+       gpio_request_pullup(GPIO_SDHICMD0);
 
        /* SDHI1 */
        gpio_request(GPIO_FN_SDHICLK1, NULL);
@@ -364,11 +360,11 @@ static void __init g4evm_init(void)
        gpio_request(GPIO_FN_SDHID1_2, NULL);
        gpio_request(GPIO_FN_SDHID1_3, NULL);
        gpio_request(GPIO_FN_SDHICMD1, NULL);
-       gpio_pull_up(GPIO_SDHID1_D0);
-       gpio_pull_up(GPIO_SDHID1_D1);
-       gpio_pull_up(GPIO_SDHID1_D2);
-       gpio_pull_up(GPIO_SDHID1_D3);
-       gpio_pull_up(GPIO_SDHICMD1);
+       gpio_request_pullup(GPIO_SDHID1_D0);
+       gpio_request_pullup(GPIO_SDHID1_D1);
+       gpio_request_pullup(GPIO_SDHID1_D2);
+       gpio_request_pullup(GPIO_SDHID1_D3);
+       gpio_request_pullup(GPIO_SDHICMD1);
 
        sh7377_add_standard_devices();
 
index f60f1b281cc46117c2cd2a03585ac8b21a8359dd..21dbe54304d5b9f82cf346a00c620264b0e83c11 100644 (file)
@@ -27,6 +27,8 @@
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/io.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
 #include <linux/smsc911x.h>
 #include <linux/gpio.h>
 #include <linux/input.h>
 #include <asm/hardware/cache-l2x0.h>
 #include <asm/traps.h>
 
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+       REGULATOR_SUPPLY("vddvario", "smsc911x"),
+       REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
+
 /* SMSC 9220 */
 static struct resource smsc9220_resources[] = {
        [0] = {
@@ -288,6 +296,13 @@ static struct platform_device leds_tpu30_device = {
        .resource       = tpu30_resources,
 };
 
+/* Fixed 1.8V regulator to be used by MMCIF */
+static struct regulator_consumer_supply fixed1v8_power_consumers[] =
+{
+       REGULATOR_SUPPLY("vmmc", "sh_mmcif.0"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mmcif.0"),
+};
+
 /* MMCIF */
 static struct resource mmcif_resources[] = {
        [0] = {
@@ -321,6 +336,15 @@ static struct platform_device mmcif_device = {
        .resource       = mmcif_resources,
 };
 
+/* Fixed 3.3V regulator to be used by SDHI0 and SDHI1 */
+static struct regulator_consumer_supply fixed3v3_power_consumers[] =
+{
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"),
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.1"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.1"),
+};
+
 /* SDHI0 */
 static struct sh_mobile_sdhi_info sdhi0_info = {
        .tmio_caps      = MMC_CAP_SD_HIGHSPEED,
@@ -411,6 +435,12 @@ static struct platform_device *kota2_devices[] __initdata = {
 
 static void __init kota2_init(void)
 {
+       regulator_register_always_on(0, "fixed-1.8V", fixed1v8_power_consumers,
+                                    ARRAY_SIZE(fixed1v8_power_consumers), 1800000);
+       regulator_register_always_on(1, "fixed-3.3V", fixed3v3_power_consumers,
+                                    ARRAY_SIZE(fixed3v3_power_consumers), 3300000);
+       regulator_register_fixed(2, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
        sh73a0_pinmux_init();
 
        /* SCIFA2 (UART2) */
index 6a33cf393428f730d9cc2b44633af997e4c7d3df..2c986eaae7b4d5ec77e4394dd2826f8ec3f67354 100644 (file)
@@ -21,6 +21,8 @@
 #include <linux/kernel.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
 #include <linux/smsc911x.h>
 #include <mach/common.h>
 #include <mach/emev2.h>
 #include <asm/mach/arch.h>
 #include <asm/hardware/gic.h>
 
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+       REGULATOR_SUPPLY("vddvario", "smsc911x"),
+       REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
+
 /* Ether */
 static struct resource smsc911x_resources[] = {
        [0] = {
@@ -63,6 +71,8 @@ static struct platform_device *kzm9d_devices[] __initdata = {
 
 void __init kzm9d_add_standard_devices(void)
 {
+       regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
        emev2_add_standard_devices();
 
        platform_add_devices(kzm9d_devices, ARRAY_SIZE(kzm9d_devices));
index c0ae815e7beb18a9be40be4bb892bfb725af7b78..53b7ea92c32c119bcfc6d44a96b92fac915363da 100644 (file)
 #include <linux/mmc/sh_mobile_sdhi.h>
 #include <linux/mfd/tmio.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
 #include <linux/smsc911x.h>
 #include <linux/usb/r8a66597.h>
+#include <linux/usb/renesas_usbhs.h>
 #include <linux/videodev2.h>
+#include <sound/sh_fsi.h>
+#include <sound/simple_card.h>
 #include <mach/irqs.h>
 #include <mach/sh73a0.h>
 #include <mach/common.h>
 #define GPIO_PCF8575_PORT15    (GPIO_NR + 13)
 #define GPIO_PCF8575_PORT16    (GPIO_NR + 14)
 
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+       REGULATOR_SUPPLY("vddvario", "smsc911x"),
+       REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
+
+/*
+ * FSI-AK4648
+ *
+ * this command is required when playback.
+ *
+ * # amixer set "LINEOUT Mixer DACL" on
+ */
+
 /* SMSC 9221 */
 static struct resource smsc9221_resources[] = {
        [0] = {
@@ -112,6 +131,151 @@ static struct platform_device usb_host_device = {
        .resource       = usb_resources,
 };
 
+/* USB Func CN17 */
+struct usbhs_private {
+       unsigned int phy;
+       unsigned int cr2;
+       struct renesas_usbhs_platform_info info;
+};
+
+#define IRQ15                  intcs_evt2irq(0x03e0)
+#define USB_PHY_MODE           (1 << 4)
+#define USB_PHY_INT_EN         ((1 << 3) | (1 << 2))
+#define USB_PHY_ON             (1 << 1)
+#define USB_PHY_OFF            (1 << 0)
+#define USB_PHY_INT_CLR                (USB_PHY_ON | USB_PHY_OFF)
+
+#define usbhs_get_priv(pdev) \
+       container_of(renesas_usbhs_get_info(pdev), struct usbhs_private, info)
+
+static int usbhs_get_vbus(struct platform_device *pdev)
+{
+       struct usbhs_private *priv = usbhs_get_priv(pdev);
+
+       return !((1 << 7) & __raw_readw(priv->cr2));
+}
+
+static void usbhs_phy_reset(struct platform_device *pdev)
+{
+       struct usbhs_private *priv = usbhs_get_priv(pdev);
+
+       /* init phy */
+       __raw_writew(0x8a0a, priv->cr2);
+}
+
+static int usbhs_get_id(struct platform_device *pdev)
+{
+       return USBHS_GADGET;
+}
+
+static irqreturn_t usbhs_interrupt(int irq, void *data)
+{
+       struct platform_device *pdev = data;
+       struct usbhs_private *priv = usbhs_get_priv(pdev);
+
+       renesas_usbhs_call_notify_hotplug(pdev);
+
+       /* clear status */
+       __raw_writew(__raw_readw(priv->phy) | USB_PHY_INT_CLR, priv->phy);
+
+       return IRQ_HANDLED;
+}
+
+static int usbhs_hardware_init(struct platform_device *pdev)
+{
+       struct usbhs_private *priv = usbhs_get_priv(pdev);
+       int ret;
+
+       /* clear interrupt status */
+       __raw_writew(USB_PHY_MODE | USB_PHY_INT_CLR, priv->phy);
+
+       ret = request_irq(IRQ15, usbhs_interrupt, IRQF_TRIGGER_HIGH,
+                         dev_name(&pdev->dev), pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "request_irq err\n");
+               return ret;
+       }
+
+       /* enable USB phy interrupt */
+       __raw_writew(USB_PHY_MODE | USB_PHY_INT_EN, priv->phy);
+
+       return 0;
+}
+
+static void usbhs_hardware_exit(struct platform_device *pdev)
+{
+       struct usbhs_private *priv = usbhs_get_priv(pdev);
+
+       /* clear interrupt status */
+       __raw_writew(USB_PHY_MODE | USB_PHY_INT_CLR, priv->phy);
+
+       free_irq(IRQ15, pdev);
+}
+
+static u32 usbhs_pipe_cfg[] = {
+       USB_ENDPOINT_XFER_CONTROL,
+       USB_ENDPOINT_XFER_ISOC,
+       USB_ENDPOINT_XFER_ISOC,
+       USB_ENDPOINT_XFER_BULK,
+       USB_ENDPOINT_XFER_BULK,
+       USB_ENDPOINT_XFER_BULK,
+       USB_ENDPOINT_XFER_INT,
+       USB_ENDPOINT_XFER_INT,
+       USB_ENDPOINT_XFER_INT,
+       USB_ENDPOINT_XFER_BULK,
+       USB_ENDPOINT_XFER_BULK,
+       USB_ENDPOINT_XFER_BULK,
+       USB_ENDPOINT_XFER_BULK,
+       USB_ENDPOINT_XFER_BULK,
+       USB_ENDPOINT_XFER_BULK,
+       USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usbhs_private usbhs_private = {
+       .phy    = 0xe60781e0,           /* USBPHYINT */
+       .cr2    = 0xe605810c,           /* USBCR2 */
+       .info = {
+               .platform_callback = {
+                       .hardware_init  = usbhs_hardware_init,
+                       .hardware_exit  = usbhs_hardware_exit,
+                       .get_id         = usbhs_get_id,
+                       .phy_reset      = usbhs_phy_reset,
+                       .get_vbus       = usbhs_get_vbus,
+               },
+               .driver_param = {
+                       .buswait_bwait  = 4,
+                       .has_otg        = 1,
+                       .pipe_type      = usbhs_pipe_cfg,
+                       .pipe_size      = ARRAY_SIZE(usbhs_pipe_cfg),
+               },
+       },
+};
+
+static struct resource usbhs_resources[] = {
+       [0] = {
+               .start  = 0xE6890000,
+               .end    = 0xE68900e6 - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = gic_spi(62),
+               .end    = gic_spi(62),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device usbhs_device = {
+       .name   = "renesas_usbhs",
+       .id     = -1,
+       .dev = {
+               .dma_mask               = NULL,
+               .coherent_dma_mask      = 0xffffffff,
+               .platform_data          = &usbhs_private.info,
+       },
+       .num_resources  = ARRAY_SIZE(usbhs_resources),
+       .resource       = usbhs_resources,
+};
+
 /* LCDC */
 static struct fb_videomode kzm_lcdc_mode = {
        .name           = "WVGA Panel",
@@ -166,6 +330,13 @@ static struct platform_device lcdc_device = {
        },
 };
 
+/* Fixed 1.8V regulator to be used by MMCIF */
+static struct regulator_consumer_supply fixed1v8_power_consumers[] =
+{
+       REGULATOR_SUPPLY("vmmc", "sh_mmcif.0"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mmcif.0"),
+};
+
 /* MMCIF */
 static struct resource sh_mmcif_resources[] = {
        [0] = {
@@ -187,6 +358,8 @@ static struct resource sh_mmcif_resources[] = {
 static struct sh_mmcif_plat_data sh_mmcif_platdata = {
        .ocr            = MMC_VDD_165_195,
        .caps           = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE,
+       .slave_id_tx    = SHDMA_SLAVE_MMCIF_TX,
+       .slave_id_rx    = SHDMA_SLAVE_MMCIF_RX,
 };
 
 static struct platform_device mmc_device = {
@@ -200,6 +373,15 @@ static struct platform_device mmc_device = {
        .resource       = sh_mmcif_resources,
 };
 
+/* Fixed 2.8V regulators to be used by SDHI0 and SDHI2 */
+static struct regulator_consumer_supply fixed2v8_power_consumers[] =
+{
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"),
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.2"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.2"),
+};
+
 /* SDHI */
 static struct sh_mobile_sdhi_info sdhi0_info = {
        .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT,
@@ -240,6 +422,50 @@ static struct platform_device sdhi0_device = {
        },
 };
 
+/* Micro SD */
+static struct sh_mobile_sdhi_info sdhi2_info = {
+       .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT |
+                         TMIO_MMC_USE_GPIO_CD |
+                         TMIO_MMC_WRPROTECT_DISABLE,
+       .tmio_caps      = MMC_CAP_SD_HIGHSPEED,
+       .tmio_ocr_mask  = MMC_VDD_27_28 | MMC_VDD_28_29,
+       .cd_gpio        = GPIO_PORT13,
+};
+
+static struct resource sdhi2_resources[] = {
+       [0] = {
+               .name   = "SDHI2",
+               .start  = 0xee140000,
+               .end    = 0xee1400ff,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .name   = SH_MOBILE_SDHI_IRQ_CARD_DETECT,
+               .start  = gic_spi(103),
+               .flags  = IORESOURCE_IRQ,
+       },
+       [2] = {
+               .name   = SH_MOBILE_SDHI_IRQ_SDCARD,
+               .start  = gic_spi(104),
+               .flags  = IORESOURCE_IRQ,
+       },
+       [3] = {
+               .name   = SH_MOBILE_SDHI_IRQ_SDIO,
+               .start  = gic_spi(105),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device sdhi2_device = {
+       .name           = "sh_mobile_sdhi",
+       .id             = 2,
+       .num_resources  = ARRAY_SIZE(sdhi2_resources),
+       .resource       = sdhi2_resources,
+       .dev    = {
+               .platform_data  = &sdhi2_info,
+       },
+};
+
 /* KEY */
 #define GPIO_KEY(c, g, d) { .code = c, .gpio = g, .desc = d, .active_low = 1 }
 
@@ -267,11 +493,74 @@ static struct platform_device gpio_keys_device = {
        },
 };
 
+/* FSI-AK4648 */
+static struct sh_fsi_platform_info fsi_info = {
+       .port_a = {
+               .tx_id = SHDMA_SLAVE_FSI2A_TX,
+       },
+};
+
+static struct resource fsi_resources[] = {
+       [0] = {
+               .name   = "FSI",
+               .start  = 0xEC230000,
+               .end    = 0xEC230400 - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = gic_spi(146),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device fsi_device = {
+       .name           = "sh_fsi2",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(fsi_resources),
+       .resource       = fsi_resources,
+       .dev    = {
+               .platform_data  = &fsi_info,
+       },
+};
+
+static struct asoc_simple_dai_init_info fsi2_ak4648_init_info = {
+       .fmt            = SND_SOC_DAIFMT_LEFT_J,
+       .codec_daifmt   = SND_SOC_DAIFMT_CBM_CFM,
+       .cpu_daifmt     = SND_SOC_DAIFMT_CBS_CFS,
+       .sysclk         = 11289600,
+};
+
+static struct asoc_simple_card_info fsi2_ak4648_info = {
+       .name           = "AK4648",
+       .card           = "FSI2A-AK4648",
+       .cpu_dai        = "fsia-dai",
+       .codec          = "ak4642-codec.0-0012",
+       .platform       = "sh_fsi2",
+       .codec_dai      = "ak4642-hifi",
+       .init           = &fsi2_ak4648_init_info,
+};
+
+static struct platform_device fsi_ak4648_device = {
+       .name   = "asoc-simple-card",
+       .dev    = {
+               .platform_data  = &fsi2_ak4648_info,
+       },
+};
+
 /* I2C */
 static struct pcf857x_platform_data pcf8575_pdata = {
        .gpio_base      = GPIO_PCF8575_BASE,
 };
 
+static struct i2c_board_info i2c0_devices[] = {
+       {
+               I2C_BOARD_INFO("ak4648", 0x12),
+       },
+       {
+               I2C_BOARD_INFO("r2025sd", 0x32),
+       }
+};
+
 static struct i2c_board_info i2c1_devices[] = {
        {
                I2C_BOARD_INFO("st1232-ts", 0x55),
@@ -289,10 +578,14 @@ static struct i2c_board_info i2c3_devices[] = {
 static struct platform_device *kzm_devices[] __initdata = {
        &smsc_device,
        &usb_host_device,
+       &usbhs_device,
        &lcdc_device,
        &mmc_device,
        &sdhi0_device,
+       &sdhi2_device,
        &gpio_keys_device,
+       &fsi_device,
+       &fsi_ak4648_device,
 };
 
 /*
@@ -350,6 +643,12 @@ device_initcall(as3711_enable_lcdc_backlight);
 
 static void __init kzm_init(void)
 {
+       regulator_register_always_on(0, "fixed-1.8V", fixed1v8_power_consumers,
+                                    ARRAY_SIZE(fixed1v8_power_consumers), 1800000);
+       regulator_register_always_on(1, "fixed-2.8V", fixed2v8_power_consumers,
+                                    ARRAY_SIZE(fixed2v8_power_consumers), 2800000);
+       regulator_register_fixed(2, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
        sh73a0_pinmux_init();
 
        /* enable SCIFA4 */
@@ -427,15 +726,36 @@ static void __init kzm_init(void)
        gpio_request(GPIO_PORT15, NULL);
        gpio_direction_output(GPIO_PORT15, 1); /* power */
 
+       /* enable Micro SD */
+       gpio_request(GPIO_FN_SDHID2_0,          NULL);
+       gpio_request(GPIO_FN_SDHID2_1,          NULL);
+       gpio_request(GPIO_FN_SDHID2_2,          NULL);
+       gpio_request(GPIO_FN_SDHID2_3,          NULL);
+       gpio_request(GPIO_FN_SDHICMD2,          NULL);
+       gpio_request(GPIO_FN_SDHICLK2,          NULL);
+       gpio_request(GPIO_PORT14, NULL);
+       gpio_direction_output(GPIO_PORT14, 1); /* power */
+
        /* I2C 3 */
        gpio_request(GPIO_FN_PORT27_I2C_SCL3, NULL);
        gpio_request(GPIO_FN_PORT28_I2C_SDA3, NULL);
 
+       /* enable FSI2 port A (ak4648) */
+       gpio_request(GPIO_FN_FSIACK,    NULL);
+       gpio_request(GPIO_FN_FSIAILR,   NULL);
+       gpio_request(GPIO_FN_FSIAIBT,   NULL);
+       gpio_request(GPIO_FN_FSIAISLD,  NULL);
+       gpio_request(GPIO_FN_FSIAOSLD,  NULL);
+
+       /* enable USB */
+       gpio_request(GPIO_FN_VBUS_0,    NULL);
+
 #ifdef CONFIG_CACHE_L2X0
        /* Early BRESP enable, Shared attribute override enable, 64K*8way */
        l2x0_init(IOMEM(0xf0100000), 0x40460000, 0x82000fff);
 #endif
 
+       i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices));
        i2c_register_board_info(1, i2c1_devices, ARRAY_SIZE(i2c1_devices));
        i2c_register_board_info(3, i2c3_devices, ARRAY_SIZE(i2c3_devices));
 
index 150122a446304071d23595c291b906b7b537707c..7ea2b31e31991355cb9304dfcafcc4ab6db6f628 100644 (file)
@@ -41,6 +41,8 @@
 #include <linux/mtd/physmap.h>
 #include <linux/mtd/sh_flctl.h>
 #include <linux/pm_clock.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
 #include <linux/smsc911x.h>
 #include <linux/sh_intc.h>
 #include <linux/tca6416_keypad.h>
  * amixer set "HPOUTR Mixer DACH" on
  */
 
-/*
- * FIXME !!
- *
- * gpio_no_direction
- * gpio_pull_down
- * are quick_hack.
- *
- * current gpio frame work doesn't have
- * the method to control only pull up/down/free.
- * this function should be replaced by correct gpio function
- */
-static void __init gpio_no_direction(u32 addr)
+/* Fixed 3.3V and 1.8V regulators to be used by multiple devices */
+static struct regulator_consumer_supply fixed1v8_power_consumers[] =
 {
-       __raw_writeb(0x00, addr);
-}
+       /*
+        * J22 on mackerel switches mmcif.0 and sdhi.1 between 1.8V and 3.3V
+        * Since we cannot support both voltages, we support the default 1.8V
+        */
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.1"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.1"),
+       REGULATOR_SUPPLY("vmmc", "sh_mmcif.0"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mmcif.0"),
+};
 
-static void __init gpio_pull_down(u32 addr)
+static struct regulator_consumer_supply fixed3v3_power_consumers[] =
 {
-       u8 data = __raw_readb(addr);
-
-       data &= 0x0F;
-       data |= 0xA0;
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"),
+       REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.2"),
+       REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.2"),
+};
 
-       __raw_writeb(data, addr);
-}
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+       REGULATOR_SUPPLY("vddvario", "smsc911x"),
+       REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
 
 /* MTD */
 static struct mtd_partition nor_flash_partitions[] = {
@@ -1409,6 +1412,12 @@ static void __init mackerel_init(void)
        u32 srcr4;
        struct clk *clk;
 
+       regulator_register_always_on(0, "fixed-1.8V", fixed1v8_power_consumers,
+                                    ARRAY_SIZE(fixed1v8_power_consumers), 1800000);
+       regulator_register_always_on(1, "fixed-3.3V", fixed3v3_power_consumers,
+                                    ARRAY_SIZE(fixed3v3_power_consumers), 3300000);
+       regulator_register_fixed(2, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
        /* External clock source */
        clk_set_rate(&sh7372_dv_clki_clk, 27000000);
 
@@ -1458,11 +1467,11 @@ static void __init mackerel_init(void)
 
        /* USBHS0 */
        gpio_request(GPIO_FN_VBUS0_0, NULL);
-       gpio_pull_down(GPIO_PORT168CR); /* VBUS0_0 pull down */
+       gpio_request_pulldown(GPIO_PORT168CR); /* VBUS0_0 pull down */
 
        /* USBHS1 */
        gpio_request(GPIO_FN_VBUS0_1, NULL);
-       gpio_pull_down(GPIO_PORT167CR); /* VBUS0_1 pull down */
+       gpio_request_pulldown(GPIO_PORT167CR); /* VBUS0_1 pull down */
        gpio_request(GPIO_FN_IDIN_1_113, NULL);
 
        /* enable FSI2 port A (ak4643) */
@@ -1475,8 +1484,8 @@ static void __init mackerel_init(void)
 
        gpio_request(GPIO_PORT9,  NULL);
        gpio_request(GPIO_PORT10, NULL);
-       gpio_no_direction(GPIO_PORT9CR);  /* FSIAOBT needs no direction */
-       gpio_no_direction(GPIO_PORT10CR); /* FSIAOLR needs no direction */
+       gpio_direction_none(GPIO_PORT9CR);  /* FSIAOBT needs no direction */
+       gpio_direction_none(GPIO_PORT10CR); /* FSIAOLR needs no direction */
 
        intc_set_priority(IRQ_FSI, 3); /* irq priority FSI(3) > SMSC911X(2) */
 
@@ -1614,20 +1623,20 @@ static void __init mackerel_init(void)
 
        platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
-       sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device);
-       sh7372_add_device_to_domain(&sh7372_a4lc, &hdmi_lcdc_device);
-       sh7372_add_device_to_domain(&sh7372_a4lc, &meram_device);
-       sh7372_add_device_to_domain(&sh7372_a4mp, &fsi_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &usbhs0_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &usbhs1_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &nand_flash_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &sh_mmcif_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &sdhi0_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4lc, &lcdc_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4lc, &hdmi_lcdc_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4lc, &meram_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4mp, &fsi_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &usbhs0_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &usbhs1_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &nand_flash_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &sh_mmcif_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &sdhi0_device);
 #if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE)
-       sh7372_add_device_to_domain(&sh7372_a3sp, &sdhi1_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &sdhi1_device);
 #endif
-       sh7372_add_device_to_domain(&sh7372_a3sp, &sdhi2_device);
-       sh7372_add_device_to_domain(&sh7372_a4r, &ceu_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &sdhi2_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4r, &ceu_device);
 
        hdmi_init_pm_clock();
        sh7372_pm_init();
index 14de3787cafcc7193abefe6e4c1c589988a76acc..3a528cf4366cb6addff63fb9e7032948f71f49e2 100644 (file)
@@ -27,6 +27,8 @@
 #include <linux/io.h>
 #include <linux/gpio.h>
 #include <linux/dma-mapping.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
 #include <linux/smsc911x.h>
 #include <mach/hardware.h>
 #include <mach/r8a7779.h>
 #include <asm/hardware/gic.h>
 #include <asm/traps.h>
 
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+       REGULATOR_SUPPLY("vddvario", "smsc911x"),
+       REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
+
 /* SMSC LAN89218 */
 static struct resource smsc911x_resources[] = {
        [0] = {
@@ -73,6 +81,8 @@ static struct platform_device *marzen_devices[] __initdata = {
 
 static void __init marzen_init(void)
 {
+       regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
        r8a7779_pinmux_init();
 
        /* SCIF2 (CN18: DEBUG0) */
index 26eea5f2105420116bb5332633d4a1385ed949d7..ad5fccc7b5e711e68f6b48f743b20ac21279c700 100644 (file)
 /* CPG registers */
 #define FRQCRA         0xe6150000
 #define FRQCRB         0xe6150004
+#define VCLKCR1                0xE6150008
+#define VCLKCR2                0xE615000c
 #define FRQCRC         0xe61500e0
+#define FSIACKCR       0xe6150018
 #define PLLC01CR       0xe6150028
 
 #define SUBCKCR                0xe6150080
@@ -54,6 +57,8 @@
 #define MSTPSR2                0xe6150040
 #define MSTPSR3                0xe6150048
 #define MSTPSR4                0xe615004c
+#define FSIBCKCR       0xe6150090
+#define HDMICKCR       0xe6150094
 #define SMSTPCR0       0xe6150130
 #define SMSTPCR1       0xe6150134
 #define SMSTPCR2       0xe6150138
@@ -271,6 +276,13 @@ static struct clk usb24_clk = {
        .parent         = &usb24s_clk,
 };
 
+/* External FSIACK/FSIBCK clock */
+static struct clk fsiack_clk = {
+};
+
+static struct clk fsibck_clk = {
+};
+
 struct clk *main_clks[] = {
        &extalr_clk,
        &extal1_clk,
@@ -288,6 +300,8 @@ struct clk *main_clks[] = {
        &pllc1_div2_clk,
        &usb24s_clk,
        &usb24_clk,
+       &fsiack_clk,
+       &fsibck_clk,
 };
 
 static void div4_kick(struct clk *clk)
@@ -313,6 +327,107 @@ static struct clk_div4_table div4_table = {
        .kick = div4_kick,
 };
 
+/* DIV6 reparent */
+enum {
+       DIV6_HDMI,
+       DIV6_VCLK1, DIV6_VCLK2,
+       DIV6_FSIA, DIV6_FSIB,
+       DIV6_REPARENT_NR,
+};
+
+static struct clk *hdmi_parent[] = {
+       [0] = &pllc1_div2_clk,
+       [1] = &system_clk,
+       [2] = &dv_clk
+};
+
+static struct clk *vclk_parents[8] = {
+       [0] = &pllc1_div2_clk,
+       [2] = &dv_clk,
+       [3] = &usb24s_clk,
+       [4] = &extal1_div2_clk,
+       [5] = &extalr_clk,
+};
+
+static struct clk *fsia_parents[] = {
+       [0] = &pllc1_div2_clk,
+       [1] = &fsiack_clk, /* external clock */
+};
+
+static struct clk *fsib_parents[] = {
+       [0] = &pllc1_div2_clk,
+       [1] = &fsibck_clk, /* external clock */
+};
+
+static struct clk div6_reparent_clks[DIV6_REPARENT_NR] = {
+       [DIV6_HDMI] = SH_CLK_DIV6_EXT(HDMICKCR, 0,
+                                     hdmi_parent, ARRAY_SIZE(hdmi_parent), 6, 2),
+       [DIV6_VCLK1] = SH_CLK_DIV6_EXT(VCLKCR1, 0,
+                                      vclk_parents, ARRAY_SIZE(vclk_parents), 12, 3),
+       [DIV6_VCLK2] = SH_CLK_DIV6_EXT(VCLKCR2, 0,
+                                      vclk_parents, ARRAY_SIZE(vclk_parents), 12, 3),
+       [DIV6_FSIA] = SH_CLK_DIV6_EXT(FSIACKCR, 0,
+                                     fsia_parents, ARRAY_SIZE(fsia_parents), 6, 2),
+       [DIV6_FSIB] = SH_CLK_DIV6_EXT(FSIBCKCR, 0,
+                                     fsib_parents, ARRAY_SIZE(fsib_parents), 6, 2),
+};
+
+/* HDMI1/2 clock */
+static unsigned long hdmi12_recalc(struct clk *clk)
+{
+       u32 val = __raw_readl(HDMICKCR);
+       int shift = (int)clk->priv;
+
+       val >>= shift;
+       val &= 0x3;
+
+       return clk->parent->rate / (1 << val);
+};
+
+static int hdmi12_set_rate(struct clk *clk, unsigned long rate)
+{
+       u32 val, mask;
+       int i, shift;
+
+       for (i = 0; i < 3; i++)
+               if (rate == clk->parent->rate / (1 << i))
+                       goto find;
+       return -ENODEV;
+
+find:
+       shift = (int)clk->priv;
+
+       val = __raw_readl(HDMICKCR);
+       mask = ~(0x3 << shift);
+       val = (val & mask) | i << shift;
+       __raw_writel(val, HDMICKCR);
+
+       return 0;
+};
+
+static struct sh_clk_ops hdmi12_clk_ops = {
+       .recalc         = hdmi12_recalc,
+       .set_rate       = hdmi12_set_rate,
+};
+
+static struct clk hdmi1_clk = {
+       .ops            = &hdmi12_clk_ops,
+       .priv           = (void *)9,
+       .parent         = &div6_reparent_clks[DIV6_HDMI],  /* late install */
+};
+
+static struct clk hdmi2_clk = {
+       .ops            = &hdmi12_clk_ops,
+       .priv           = (void *)11,
+       .parent         = &div6_reparent_clks[DIV6_HDMI], /* late install */
+};
+
+static struct clk *late_main_clks[] = {
+       &hdmi1_clk,
+       &hdmi2_clk,
+};
+
+/* MSTP */
 enum {
        DIV4_I, DIV4_ZG, DIV4_B, DIV4_M1, DIV4_HP,
        DIV4_HPP, DIV4_USBP, DIV4_S, DIV4_ZB, DIV4_M3, DIV4_CP,
@@ -343,11 +458,12 @@ static struct clk div6_clks[DIV6_NR] = {
 };
 
 enum {
-       MSTP125,
+       MSTP128, MSTP127, MSTP125,
        MSTP116, MSTP111, MSTP100, MSTP117,
 
        MSTP230,
        MSTP222,
+       MSTP218, MSTP217, MSTP216, MSTP214,
        MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200,
 
        MSTP329, MSTP328, MSTP323, MSTP320,
@@ -360,6 +476,8 @@ enum {
 };
 
 static struct clk mstp_clks[MSTP_NR] = {
+       [MSTP128] = SH_CLK_MSTP32(&div4_clks[DIV4_S],   SMSTPCR1, 28, 0), /* CEU21 */
+       [MSTP127] = SH_CLK_MSTP32(&div4_clks[DIV4_S],   SMSTPCR1, 27, 0), /* CEU20 */
        [MSTP125] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 25, 0), /* TMU0 */
        [MSTP117] = SH_CLK_MSTP32(&div4_clks[DIV4_B],   SMSTPCR1, 17, 0), /* LCDC1 */
        [MSTP116] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 16, 0), /* IIC0 */
@@ -368,6 +486,10 @@ static struct clk mstp_clks[MSTP_NR] = {
 
        [MSTP230] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 30, 0), /* SCIFA6 */
        [MSTP222] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 22, 0), /* SCIFA7 */
+       [MSTP218] = SH_CLK_MSTP32(&div4_clks[DIV4_HP],  SMSTPCR2, 18, 0), /* DMAC1 */
+       [MSTP217] = SH_CLK_MSTP32(&div4_clks[DIV4_HP],  SMSTPCR2, 17, 0), /* DMAC2 */
+       [MSTP216] = SH_CLK_MSTP32(&div4_clks[DIV4_HP],  SMSTPCR2, 16, 0), /* DMAC3 */
+       [MSTP214] = SH_CLK_MSTP32(&div4_clks[DIV4_HP],  SMSTPCR2, 14, 0), /* USBDMAC */
        [MSTP207] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2,  7, 0), /* SCIFA5 */
        [MSTP206] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2,  6, 0), /* SCIFB */
        [MSTP204] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2,  4, 0), /* SCIFA0 */
@@ -408,6 +530,12 @@ static struct clk_lookup lookups[] = {
        CLKDEV_CON_ID("pllc1_clk",              &pllc1_clk),
        CLKDEV_CON_ID("pllc1_div2_clk",         &pllc1_div2_clk),
        CLKDEV_CON_ID("usb24s",                 &usb24s_clk),
+       CLKDEV_CON_ID("hdmi1",                  &hdmi1_clk),
+       CLKDEV_CON_ID("hdmi2",                  &hdmi2_clk),
+       CLKDEV_CON_ID("video1",                 &div6_reparent_clks[DIV6_VCLK1]),
+       CLKDEV_CON_ID("video2",                 &div6_reparent_clks[DIV6_VCLK2]),
+       CLKDEV_CON_ID("fsiack",                 &fsiack_clk),
+       CLKDEV_CON_ID("fsibck",                 &fsibck_clk),
 
        /* DIV4 clocks */
        CLKDEV_CON_ID("i_clk",                  &div4_clks[DIV4_I]),
@@ -430,6 +558,8 @@ static struct clk_lookup lookups[] = {
        CLKDEV_DEV_ID("i2c-sh_mobile.0",        &mstp_clks[MSTP116]),
        CLKDEV_DEV_ID("sh_mobile_lcdc_fb.1",    &mstp_clks[MSTP117]),
        CLKDEV_DEV_ID("sh_tmu.0",               &mstp_clks[MSTP125]),
+       CLKDEV_DEV_ID("sh_mobile_ceu.0",        &mstp_clks[MSTP127]),
+       CLKDEV_DEV_ID("sh_mobile_ceu.1",        &mstp_clks[MSTP128]),
 
        CLKDEV_DEV_ID("sh-sci.4",               &mstp_clks[MSTP200]),
        CLKDEV_DEV_ID("sh-sci.3",               &mstp_clks[MSTP201]),
@@ -438,7 +568,10 @@ static struct clk_lookup lookups[] = {
        CLKDEV_DEV_ID("sh-sci.0",               &mstp_clks[MSTP204]),
        CLKDEV_DEV_ID("sh-sci.8",               &mstp_clks[MSTP206]),
        CLKDEV_DEV_ID("sh-sci.5",               &mstp_clks[MSTP207]),
-
+       CLKDEV_DEV_ID("sh-dma-engine.3",        &mstp_clks[MSTP214]),
+       CLKDEV_DEV_ID("sh-dma-engine.2",        &mstp_clks[MSTP216]),
+       CLKDEV_DEV_ID("sh-dma-engine.1",        &mstp_clks[MSTP217]),
+       CLKDEV_DEV_ID("sh-dma-engine.0",        &mstp_clks[MSTP218]),
        CLKDEV_DEV_ID("sh-sci.7",               &mstp_clks[MSTP222]),
        CLKDEV_DEV_ID("sh-sci.6",               &mstp_clks[MSTP230]),
 
@@ -459,6 +592,10 @@ static struct clk_lookup lookups[] = {
        CLKDEV_ICK_ID("phy",    "renesas_usbhs",        &mstp_clks[MSTP406]),
        CLKDEV_ICK_ID("pci",    "renesas_usbhs",        &div4_clks[DIV4_USBP]),
        CLKDEV_ICK_ID("usb24",  "renesas_usbhs",        &usb24_clk),
+       CLKDEV_ICK_ID("ick",    "sh-mobile-hdmi",       &div6_reparent_clks[DIV6_HDMI]),
+
+       CLKDEV_ICK_ID("icka", "sh_fsi2",        &div6_reparent_clks[DIV6_FSIA]),
+       CLKDEV_ICK_ID("ickb", "sh_fsi2",        &div6_reparent_clks[DIV6_FSIB]),
 };
 
 void __init r8a7740_clock_init(u8 md_ck)
@@ -495,7 +632,14 @@ void __init r8a7740_clock_init(u8 md_ck)
                ret = sh_clk_div6_register(div6_clks, DIV6_NR);
 
        if (!ret)
-               ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR);
+               ret = sh_clk_div6_reparent_register(div6_reparent_clks,
+                                                   DIV6_REPARENT_NR);
+
+       if (!ret)
+               ret = sh_clk_mstp_register(mstp_clks, MSTP_NR);
+
+       for (k = 0; !ret && (k < ARRAY_SIZE(late_main_clks)); k++)
+               ret = clk_register(late_main_clks[k]);
 
        clkdev_add_table(lookups, ARRAY_SIZE(lookups));
 
index 7d6e9fe47b56a812e7fbb7c1343da1eb9d045141..339c62c824d5178ba1a22ef1fac082edd65cd9ba 100644 (file)
@@ -162,7 +162,7 @@ void __init r8a7779_clock_init(void)
                ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table);
 
        if (!ret)
-               ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR);
+               ret = sh_clk_mstp_register(mstp_clks, MSTP_NR);
 
        for (k = 0; !ret && (k < ARRAY_SIZE(late_main_clks)); k++)
                ret = clk_register(late_main_clks[k]);
index 006e7b5d304c36207780ebbcde9f34a833ea2605..162b791b89847a5ec3f0b06c3af3c12ca05e2173 100644 (file)
@@ -344,7 +344,7 @@ void __init sh7367_clock_init(void)
                ret = sh_clk_div6_register(div6_clks, DIV6_NR);
 
        if (!ret)
-               ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR);
+               ret = sh_clk_mstp_register(mstp_clks, MSTP_NR);
 
        clkdev_add_table(lookups, ARRAY_SIZE(lookups));
 
index 94d1f88246d3fc780267ad698b9d43e73c339eaa..5a2894b1c96553976b6e4f089f832cb4a2e6a05d 100644 (file)
@@ -704,7 +704,7 @@ void __init sh7372_clock_init(void)
                ret = sh_clk_div6_reparent_register(div6_reparent_clks, DIV6_REPARENT_NR);
 
        if (!ret)
-               ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR);
+               ret = sh_clk_mstp_register(mstp_clks, MSTP_NR);
 
        for (k = 0; !ret && (k < ARRAY_SIZE(late_main_clks)); k++)
                ret = clk_register(late_main_clks[k]);
index 0798a15936c3cc41a6d51c70e909503d3f02afd8..85f2a3ec2c4431d324400bf52bcb628decc1fcf5 100644 (file)
@@ -355,7 +355,7 @@ void __init sh7377_clock_init(void)
                ret = sh_clk_div6_register(div6_clks, DIV6_NR);
 
        if (!ret)
-               ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR);
+               ret = sh_clk_mstp_register(mstp_clks, MSTP_NR);
 
        clkdev_add_table(lookups, ARRAY_SIZE(lookups));
 
index 3946c4ba2aa813f0cea841b5c66b622c5f0d3950..7f8da18a8580a234a0becb3bc8ec360b39d98202 100644 (file)
@@ -475,9 +475,9 @@ static struct clk *late_main_clks[] = {
 
 enum { MSTP001,
        MSTP129, MSTP128, MSTP127, MSTP126, MSTP125, MSTP118, MSTP116, MSTP100,
-       MSTP219, MSTP218,
+       MSTP219, MSTP218, MSTP217,
        MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200,
-       MSTP331, MSTP329, MSTP325, MSTP323,
+       MSTP331, MSTP329, MSTP328, MSTP325, MSTP323, MSTP322,
        MSTP314, MSTP313, MSTP312, MSTP311,
        MSTP303, MSTP302, MSTP301, MSTP300,
        MSTP411, MSTP410, MSTP403,
@@ -498,6 +498,7 @@ static struct clk mstp_clks[MSTP_NR] = {
        [MSTP100] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 0, 0), /* LCDC0 */
        [MSTP219] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 19, 0), /* SCIFA7 */
        [MSTP218] = MSTP(&div4_clks[DIV4_HP], SMSTPCR2, 18, 0), /* SY-DMAC */
+       [MSTP217] = MSTP(&div4_clks[DIV4_HP], SMSTPCR2, 17, 0), /* MP-DMAC */
        [MSTP207] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 7, 0), /* SCIFA5 */
        [MSTP206] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 6, 0), /* SCIFB */
        [MSTP204] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 4, 0), /* SCIFA0 */
@@ -507,8 +508,10 @@ static struct clk mstp_clks[MSTP_NR] = {
        [MSTP200] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 0, 0), /* SCIFA4 */
        [MSTP331] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR3, 31, 0), /* SCIFA6 */
        [MSTP329] = MSTP(&r_clk, SMSTPCR3, 29, 0), /* CMT10 */
+       [MSTP328] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 28, 0), /*FSI*/
        [MSTP325] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR3, 25, 0), /* IrDA */
        [MSTP323] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 23, 0), /* IIC1 */
+       [MSTP322] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 22, 0), /* USB */
        [MSTP314] = MSTP(&div6_clks[DIV6_SDHI0], SMSTPCR3, 14, 0), /* SDHI0 */
        [MSTP313] = MSTP(&div6_clks[DIV6_SDHI1], SMSTPCR3, 13, 0), /* SDHI1 */
        [MSTP312] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 12, 0), /* MMCIF0 */
@@ -553,6 +556,7 @@ static struct clk_lookup lookups[] = {
        CLKDEV_DEV_ID("sh_mobile_lcdc_fb.0", &mstp_clks[MSTP100]), /* LCDC0 */
        CLKDEV_DEV_ID("sh-sci.7", &mstp_clks[MSTP219]), /* SCIFA7 */
        CLKDEV_DEV_ID("sh-dma-engine.0", &mstp_clks[MSTP218]), /* SY-DMAC */
+       CLKDEV_DEV_ID("sh-dma-engine.1", &mstp_clks[MSTP217]), /* MP-DMAC */
        CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[MSTP207]), /* SCIFA5 */
        CLKDEV_DEV_ID("sh-sci.8", &mstp_clks[MSTP206]), /* SCIFB */
        CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP204]), /* SCIFA0 */
@@ -562,8 +566,10 @@ static struct clk_lookup lookups[] = {
        CLKDEV_DEV_ID("sh-sci.4", &mstp_clks[MSTP200]), /* SCIFA4 */
        CLKDEV_DEV_ID("sh-sci.6", &mstp_clks[MSTP331]), /* SCIFA6 */
        CLKDEV_DEV_ID("sh_cmt.10", &mstp_clks[MSTP329]), /* CMT10 */
+       CLKDEV_DEV_ID("sh_fsi2", &mstp_clks[MSTP328]), /* FSI */
        CLKDEV_DEV_ID("sh_irda.0", &mstp_clks[MSTP325]), /* IrDA */
        CLKDEV_DEV_ID("i2c-sh_mobile.1", &mstp_clks[MSTP323]), /* I2C1 */
+       CLKDEV_DEV_ID("renesas_usbhs", &mstp_clks[MSTP322]), /* USB */
        CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP314]), /* SDHI0 */
        CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP313]), /* SDHI1 */
        CLKDEV_DEV_ID("sh_mmcif.0", &mstp_clks[MSTP312]), /* MMCIF0 */
@@ -612,7 +618,7 @@ void __init sh73a0_clock_init(void)
                ret = sh_clk_div6_reparent_register(div6_clks, DIV6_NR);
 
        if (!ret)
-               ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR);
+               ret = sh_clk_mstp_register(mstp_clks, MSTP_NR);
 
        for (k = 0; !ret && (k < ARRAY_SIZE(late_main_clks)); k++)
                ret = clk_register(late_main_clks[k]);
index 01e2bc014f1501f41f2f2a9b18a7596bb686ac27..45e61dada030ba263fd721d4210d477e8664faba 100644 (file)
@@ -77,6 +77,7 @@ extern void r8a7779_add_standard_devices(void);
 extern void r8a7779_clock_init(void);
 extern void r8a7779_pinmux_init(void);
 extern void r8a7779_pm_init(void);
+extern void r8a7740_meram_workaround(void);
 
 extern unsigned int r8a7779_get_core_count(void);
 extern int r8a7779_platform_cpu_kill(unsigned int cpu);
diff --git a/arch/arm/mach-shmobile/include/mach/dma-register.h b/arch/arm/mach-shmobile/include/mach/dma-register.h
new file mode 100644 (file)
index 0000000..97c40bd
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * SH-ARM CPU-specific DMA definitions, used by both DMA drivers
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp
+ *
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * Based on arch/sh/include/cpu-sh4/cpu/dma-register.h
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef DMA_REGISTER_H
+#define DMA_REGISTER_H
+
+/*
+ *             Direct Memory Access Controller
+ */
+
+/* Transmit sizes and respective CHCR register values */
+enum {
+       XMIT_SZ_8BIT            = 0,
+       XMIT_SZ_16BIT           = 1,
+       XMIT_SZ_32BIT           = 2,
+       XMIT_SZ_64BIT           = 7,
+       XMIT_SZ_128BIT          = 3,
+       XMIT_SZ_256BIT          = 4,
+       XMIT_SZ_512BIT          = 5,
+};
+
+/* log2(size / 8) - used to calculate number of transfers */
+static const unsigned int dma_ts_shift[] = {
+       [XMIT_SZ_8BIT]          = 0,
+       [XMIT_SZ_16BIT]         = 1,
+       [XMIT_SZ_32BIT]         = 2,
+       [XMIT_SZ_64BIT]         = 3,
+       [XMIT_SZ_128BIT]        = 4,
+       [XMIT_SZ_256BIT]        = 5,
+       [XMIT_SZ_512BIT]        = 6,
+};
+
+#define TS_LOW_BIT     0x3 /* --xx */
+#define TS_HI_BIT      0xc /* xx-- */
+
+#define TS_LOW_SHIFT   (3)
+#define TS_HI_SHIFT    (20 - 2)        /* 2 bits for shifted low TS */
+
+#define TS_INDEX2VAL(i) \
+       ((((i) & TS_LOW_BIT) << TS_LOW_SHIFT) |\
+        (((i) & TS_HI_BIT)  << TS_HI_SHIFT))
+
+#define CHCR_TX(xmit_sz) (DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL((xmit_sz)))
+#define CHCR_RX(xmit_sz) (DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL((xmit_sz)))
+
+
+/*
+ *             USB High-Speed DMAC
+ */
+/* Transmit sizes and respective CHCR register values */
+enum {
+       USBTS_XMIT_SZ_8BYTE             = 0,
+       USBTS_XMIT_SZ_16BYTE            = 1,
+       USBTS_XMIT_SZ_32BYTE            = 2,
+};
+
+/* log2(size / 8) - used to calculate number of transfers */
+static const unsigned int dma_usbts_shift[] = {
+       [USBTS_XMIT_SZ_8BYTE]   = 3,
+       [USBTS_XMIT_SZ_16BYTE]  = 4,
+       [USBTS_XMIT_SZ_32BYTE]  = 5,
+};
+
+#define USBTS_LOW_BIT  0x3 /* --xx */
+#define USBTS_HI_BIT   0x0 /* ---- */
+
+#define USBTS_LOW_SHIFT        6
+#define USBTS_HI_SHIFT 0
+
+#define USBTS_INDEX2VAL(i) (((i) & 3) << 6)
+
+#endif /* DMA_REGISTER_H */
index de795b42232a8940720ed8e44ca3bc7da7431372..844507d937cbc2937f6489046bd0b9898d2d1f74 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/sh_pfc.h>
+#include <linux/io.h>
 
 #ifdef CONFIG_GPIOLIB
 
@@ -27,4 +28,35 @@ static inline int irq_to_gpio(unsigned int irq)
 
 #endif /* CONFIG_GPIOLIB */
 
+/*
+ * FIXME !!
+ *
+ * current gpio frame work doesn't have
+ * the method to control only pull up/down/free.
+ * this function should be replaced by correct gpio function
+ */
+static inline void __init gpio_direction_none(u32 addr)
+{
+       __raw_writeb(0x00, addr);
+}
+
+static inline void __init gpio_request_pullup(u32 addr)
+{
+       u8 data = __raw_readb(addr);
+
+       data &= 0x0F;
+       data |= 0xC0;
+       __raw_writeb(data, addr);
+}
+
+static inline void __init gpio_request_pulldown(u32 addr)
+{
+       u8 data = __raw_readb(addr);
+
+       data &= 0x0F;
+       data |= 0xA0;
+
+       __raw_writeb(data, addr);
+}
+
 #endif /* __ASM_ARCH_GPIO_H */
diff --git a/arch/arm/mach-shmobile/include/mach/pm-rmobile.h b/arch/arm/mach-shmobile/include/mach/pm-rmobile.h
new file mode 100644 (file)
index 0000000..5a40284
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#ifndef PM_RMOBILE_H
+#define PM_RMOBILE_H
+
+#include <linux/pm_domain.h>
+
+struct platform_device;
+
+struct rmobile_pm_domain {
+       struct generic_pm_domain genpd;
+       struct dev_power_governor *gov;
+       int (*suspend)(void);
+       void (*resume)(void);
+       unsigned int bit_shift;
+       bool no_debug;
+};
+
+static inline
+struct rmobile_pm_domain *to_rmobile_pd(struct generic_pm_domain *d)
+{
+       return container_of(d, struct rmobile_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd);
+extern void rmobile_add_device_to_domain(struct rmobile_pm_domain *rmobile_pd,
+                                       struct platform_device *pdev);
+extern void rmobile_pm_add_subdomain(struct rmobile_pm_domain *rmobile_pd,
+                                   struct rmobile_pm_domain *rmobile_sd);
+#else
+#define rmobile_init_pm_domain(pd) do { } while (0)
+#define rmobile_add_device_to_domain(pd, pdev) do { } while (0)
+#define rmobile_pm_add_subdomain(pd, sd) do { } while (0)
+#endif /* CONFIG_PM */
+
+#endif /* PM_RMOBILE_H */
index 9d447abb969c22de557005e6e595a836317abb12..7143147780df55f6e17a689e9cc67ed2a9cd17e9 100644 (file)
@@ -19,6 +19,8 @@
 #ifndef __ASM_R8A7740_H__
 #define __ASM_R8A7740_H__
 
+#include <mach/pm-rmobile.h>
+
 /*
  * MD_CKx pin
  */
@@ -139,7 +141,7 @@ enum {
        GPIO_FN_DBGMD10,        GPIO_FN_DBGMD11,        GPIO_FN_DBGMD20,
        GPIO_FN_DBGMD21,
 
-       /* FSI */
+       /* FSI-A */
        GPIO_FN_FSIAISLD_PORT0,         /* FSIAISLD Port 0/5 */
        GPIO_FN_FSIAISLD_PORT5,
        GPIO_FN_FSIASPDIF_PORT9,        /* FSIASPDIF Port 9/18 */
@@ -150,6 +152,9 @@ enum {
        GPIO_FN_FSIACK,         GPIO_FN_FSIAILR,
        GPIO_FN_FSIAIBT,
 
+       /* FSI-B */
+       GPIO_FN_FSIBCK,
+
        /* FMSI */
        GPIO_FN_FMSISLD_PORT1, /* FMSISLD Port 1/6 */
        GPIO_FN_FMSISLD_PORT6,
@@ -565,6 +570,10 @@ enum {
        GPIO_FN_RESETP_PULLUP,
        GPIO_FN_RESETP_PLAIN,
 
+       /* HDMI */
+       GPIO_FN_HDMI_HPD,
+       GPIO_FN_HDMI_CEC,
+
        /* SDENC */
        GPIO_FN_SDENC_CPG,
        GPIO_FN_SDENC_DV_CLKI,
@@ -581,4 +590,26 @@ enum {
        GPIO_FN_TRACEAUD_FROM_MEMC,
 };
 
+/* DMA slave IDs */
+enum {
+       SHDMA_SLAVE_INVALID,
+       SHDMA_SLAVE_SDHI0_RX,
+       SHDMA_SLAVE_SDHI0_TX,
+       SHDMA_SLAVE_SDHI1_RX,
+       SHDMA_SLAVE_SDHI1_TX,
+       SHDMA_SLAVE_SDHI2_RX,
+       SHDMA_SLAVE_SDHI2_TX,
+       SHDMA_SLAVE_FSIA_RX,
+       SHDMA_SLAVE_FSIA_TX,
+       SHDMA_SLAVE_FSIB_TX,
+       SHDMA_SLAVE_USBHS_TX,
+       SHDMA_SLAVE_USBHS_RX,
+};
+
+#ifdef CONFIG_PM
+extern struct rmobile_pm_domain r8a7740_pd_a4s;
+extern struct rmobile_pm_domain r8a7740_pd_a3sp;
+extern struct rmobile_pm_domain r8a7740_pd_a4lc;
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_R8A7740_H__ */
index 915d0093da0853280dc55eb06ba18d88cc1c3a8d..b59048e6d8fd7302717b9bb4f9359eae95ce10e5 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/sh_clk.h>
 #include <linux/pm_domain.h>
+#include <mach/pm-rmobile.h>
 
 /*
  * Pin Function Controller:
@@ -477,42 +478,16 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
-struct platform_device;
-
-struct sh7372_pm_domain {
-       struct generic_pm_domain genpd;
-       struct dev_power_governor *gov;
-       int (*suspend)(void);
-       void (*resume)(void);
-       unsigned int bit_shift;
-       bool no_debug;
-};
-
-static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
-{
-       return container_of(d, struct sh7372_pm_domain, genpd);
-}
-
 #ifdef CONFIG_PM
-extern struct sh7372_pm_domain sh7372_a4lc;
-extern struct sh7372_pm_domain sh7372_a4mp;
-extern struct sh7372_pm_domain sh7372_d4;
-extern struct sh7372_pm_domain sh7372_a4r;
-extern struct sh7372_pm_domain sh7372_a3rv;
-extern struct sh7372_pm_domain sh7372_a3ri;
-extern struct sh7372_pm_domain sh7372_a4s;
-extern struct sh7372_pm_domain sh7372_a3sp;
-extern struct sh7372_pm_domain sh7372_a3sg;
-
-extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
-extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
-                                       struct platform_device *pdev);
-extern void sh7372_pm_add_subdomain(struct sh7372_pm_domain *sh7372_pd,
-                                   struct sh7372_pm_domain *sh7372_sd);
-#else
-#define sh7372_init_pm_domain(pd) do { } while(0)
-#define sh7372_add_device_to_domain(pd, pdev) do { } while(0)
-#define sh7372_pm_add_subdomain(pd, sd) do { } while(0)
+extern struct rmobile_pm_domain sh7372_pd_a4lc;
+extern struct rmobile_pm_domain sh7372_pd_a4mp;
+extern struct rmobile_pm_domain sh7372_pd_d4;
+extern struct rmobile_pm_domain sh7372_pd_a4r;
+extern struct rmobile_pm_domain sh7372_pd_a3rv;
+extern struct rmobile_pm_domain sh7372_pd_a3ri;
+extern struct rmobile_pm_domain sh7372_pd_a4s;
+extern struct rmobile_pm_domain sh7372_pd_a3sp;
+extern struct rmobile_pm_domain sh7372_pd_a3sg;
 #endif /* CONFIG_PM */
 
 extern void sh7372_intcs_suspend(void);
index 398e2c10913bc3581b145c2a96192d66af011d1d..fe950f25d793966750f3e506034a831bee5d86ad 100644 (file)
@@ -516,6 +516,13 @@ enum {
        SHDMA_SLAVE_SDHI2_RX,
        SHDMA_SLAVE_MMCIF_TX,
        SHDMA_SLAVE_MMCIF_RX,
+       SHDMA_SLAVE_FSI2A_TX,
+       SHDMA_SLAVE_FSI2A_RX,
+       SHDMA_SLAVE_FSI2B_TX,
+       SHDMA_SLAVE_FSI2B_RX,
+       SHDMA_SLAVE_FSI2C_TX,
+       SHDMA_SLAVE_FSI2C_RX,
+       SHDMA_SLAVE_FSI2D_RX,
 };
 
 /*
index 09c42afcb22dae848551c1306b922866fc78a957..9a69a31918ba074d01501f210bfe162e0a216b56 100644 (file)
@@ -71,10 +71,12 @@ enum {
        DMAC3_1_DEI0, DMAC3_1_DEI1, DMAC3_1_DEI2, DMAC3_1_DEI3,
        DMAC3_2_DEI4, DMAC3_2_DEI5, DMAC3_2_DADERR,
        SHWYSTAT_RT, SHWYSTAT_HS, SHWYSTAT_COM,
+       HDMI,
        USBH_INT, USBH_OHCI, USBH_EHCI, USBH_PME, USBH_BIND,
        RSPI_OVRF, RSPI_SPTEF, RSPI_SPRF,
        SPU2_0, SPU2_1,
        FSI, FMSI,
+       HDMI_SSS, HDMI_KEY,
        IPMMU,
        AP_ARM_CTIIRQ, AP_ARM_PMURQ,
        MFIS2,
@@ -182,6 +184,7 @@ static struct intc_vect intca_vectors[] __initdata = {
        INTC_VECT(USBH_EHCI,            0x1580),
        INTC_VECT(USBH_PME,             0x15A0),
        INTC_VECT(USBH_BIND,            0x15C0),
+       INTC_VECT(HDMI,                 0x1700),
        INTC_VECT(RSPI_OVRF,            0x1780),
        INTC_VECT(RSPI_SPTEF,           0x17A0),
        INTC_VECT(RSPI_SPRF,            0x17C0),
@@ -189,6 +192,8 @@ static struct intc_vect intca_vectors[] __initdata = {
        INTC_VECT(SPU2_1,               0x1820),
        INTC_VECT(FSI,                  0x1840),
        INTC_VECT(FMSI,                 0x1860),
+       INTC_VECT(HDMI_SSS,             0x18A0),
+       INTC_VECT(HDMI_KEY,             0x18C0),
        INTC_VECT(IPMMU,                0x1920),
        INTC_VECT(AP_ARM_CTIIRQ,        0x1980),
        INTC_VECT(AP_ARM_PMURQ,         0x19A0),
@@ -304,11 +309,11 @@ static struct intc_mask_reg intca_mask_registers[] __initdata = {
            USBH_EHCI, USBH_PME, USBH_BIND, 0 } },
          /* IMR3A3 / IMCR3A3 */
        { /* IMR4A3 / IMCR4A3 */ 0xe6950090, 0xe69500d0, 8,
-         { 0, 0, 0, 0,
+         { HDMI, 0, 0, 0,
            RSPI_OVRF, RSPI_SPTEF, RSPI_SPRF, 0 } },
        { /* IMR5A3 / IMCR5A3 */ 0xe6950094, 0xe69500d4, 8,
          { SPU2_0, SPU2_1, FSI, FMSI,
-           0, 0, 0, 0 } },
+           0, HDMI_SSS, HDMI_KEY, 0 } },
        { /* IMR6A3 / IMCR6A3 */ 0xe6950098, 0xe69500d8, 8,
          { 0, IPMMU, 0, 0,
            AP_ARM_CTIIRQ, AP_ARM_PMURQ, 0, 0 } },
@@ -353,10 +358,10 @@ static struct intc_prio_reg intca_prio_registers[] __initdata = {
        { 0xe6950014, 0, 16, 4, /* IPRFA3 */ { USBH2, 0, 0, 0 } },
                                /* IPRGA3 */
                                /* IPRHA3 */
-                               /* IPRIA3 */
+       { 0xe6950020, 0, 16, 4, /* IPRIA3 */ { HDMI, 0, 0, 0 } },
        { 0xe6950024, 0, 16, 4, /* IPRJA3 */ { RSPI, 0, 0, 0 } },
        { 0xe6950028, 0, 16, 4, /* IPRKA3 */ { SPU2, 0, FSI, FMSI } },
-                               /* IPRLA3 */
+       { 0xe695002c, 0, 16, 4, /* IPRLA3 */ { 0, HDMI_SSS, HDMI_KEY, 0 } },
        { 0xe6950030, 0, 16, 4, /* IPRMA3 */ { IPMMU, 0, 0, 0 } },
        { 0xe6950034, 0, 16, 4, /* IPRNA3 */ { AP_ARM2, 0, 0, 0 } },
        { 0xe6950038, 0, 16, 4, /* IPROA3 */ { MFIS2, CPORTR2S,
index 670fe1869dbc4e9488651ab336f2da0167e41965..ce9e7fa5cc8a6f4c2c2cba6efa31014fa6303b9f 100644 (file)
@@ -169,7 +169,7 @@ enum {
        DBGMD10_MARK,   DBGMD11_MARK,   DBGMD20_MARK,
        DBGMD21_MARK,
 
-       /* FSI */
+       /* FSI-A */
        FSIAISLD_PORT0_MARK,    /* FSIAISLD Port 0/5 */
        FSIAISLD_PORT5_MARK,
        FSIASPDIF_PORT9_MARK,   /* FSIASPDIF Port 9/18 */
@@ -178,6 +178,9 @@ enum {
        FSIAOBT_MARK,   FSIAOSLD_MARK,  FSIAOMC_MARK,
        FSIACK_MARK,    FSIAILR_MARK,   FSIAIBT_MARK,
 
+       /* FSI-B */
+       FSIBCK_MARK,
+
        /* FMSI */
        FMSISLD_PORT1_MARK, /* FMSISLD Port 1/6 */
        FMSISLD_PORT6_MARK,
@@ -560,6 +563,9 @@ enum {
        /* SDENC */
        SDENC_CPG_MARK,         SDENC_DV_CLKI_MARK,
 
+       /* HDMI */
+       HDMI_HPD_MARK, HDMI_CEC_MARK,
+
        /* DEBUG */
        EDEBGREQ_PULLUP_MARK,   /* for JTAG */
        EDEBGREQ_PULLDOWN_MARK,
@@ -771,6 +777,7 @@ static pinmux_enum_t pinmux_data[] = {
 
        /* Port11 */
        PINMUX_DATA(FSIACK_MARK,                PORT11_FN1),
+       PINMUX_DATA(FSIBCK_MARK,                PORT11_FN2),
        PINMUX_DATA(IRQ2_PORT11_MARK,           PORT11_FN0,     MSEL1CR_2_0),
 
        /* Port12 */
@@ -1254,7 +1261,7 @@ static pinmux_enum_t pinmux_data[] = {
        PINMUX_DATA(A21_MARK,                   PORT120_FN1),
        PINMUX_DATA(MSIOF0_RSYNC_MARK,          PORT120_FN2),
        PINMUX_DATA(MSIOF1_TSYNC_PORT120_MARK,  PORT120_FN3,    MSEL4CR_10_0),
-       PINMUX_DATA(IRQ7_PORT120_MARK,          PORT120_FN0,    MSEL1CR_7_0),
+       PINMUX_DATA(IRQ7_PORT120_MARK,          PORT120_FN0,    MSEL1CR_7_1),
 
        /* Port121 */
        PINMUX_DATA(A20_MARK,                   PORT121_FN1),
@@ -1616,13 +1623,15 @@ static pinmux_enum_t pinmux_data[] = {
 
        /* Port209 */
        PINMUX_DATA(VBUS_MARK,                  PORT209_FN1),
-       PINMUX_DATA(IRQ7_PORT209_MARK,          PORT209_FN0,    MSEL1CR_7_1),
+       PINMUX_DATA(IRQ7_PORT209_MARK,          PORT209_FN0,    MSEL1CR_7_0),
 
        /* Port210 */
        PINMUX_DATA(IRQ9_PORT210_MARK,          PORT210_FN0,    MSEL1CR_9_1),
+       PINMUX_DATA(HDMI_HPD_MARK,              PORT210_FN1),
 
        /* Port211 */
        PINMUX_DATA(IRQ16_PORT211_MARK,         PORT211_FN0,    MSEL1CR_16_1),
+       PINMUX_DATA(HDMI_CEC_MARK,              PORT211_FN1),
 
        /* LCDC select */
        PINMUX_DATA(LCDC0_SELECT_MARK,                          MSEL3CR_6_0),
@@ -1691,7 +1700,7 @@ static struct pinmux_gpio pinmux_gpios[] = {
        GPIO_FN(DBGMD10),       GPIO_FN(DBGMD11),       GPIO_FN(DBGMD20),
        GPIO_FN(DBGMD21),
 
-       /* FSI */
+       /* FSI-A */
        GPIO_FN(FSIAISLD_PORT0),        /* FSIAISLD Port 0/5 */
        GPIO_FN(FSIAISLD_PORT5),
        GPIO_FN(FSIASPDIF_PORT9),       /* FSIASPDIF Port 9/18 */
@@ -1700,6 +1709,9 @@ static struct pinmux_gpio pinmux_gpios[] = {
        GPIO_FN(FSIAOBT),       GPIO_FN(FSIAOSLD),      GPIO_FN(FSIAOMC),
        GPIO_FN(FSIACK),        GPIO_FN(FSIAILR),       GPIO_FN(FSIAIBT),
 
+       /* FSI-B */
+       GPIO_FN(FSIBCK),
+
        /* FMSI */
        GPIO_FN(FMSISLD_PORT1), /* FMSISLD Port 1/6 */
        GPIO_FN(FMSISLD_PORT6),
@@ -2097,6 +2109,10 @@ static struct pinmux_gpio pinmux_gpios[] = {
        GPIO_FN(SDENC_CPG),
        GPIO_FN(SDENC_DV_CLKI),
 
+       /* HDMI */
+       GPIO_FN(HDMI_HPD),
+       GPIO_FN(HDMI_CEC),
+
        /* SYSC */
        GPIO_FN(RESETP_PULLUP),
        GPIO_FN(RESETP_PLAIN),
diff --git a/arch/arm/mach-shmobile/pm-r8a7740.c b/arch/arm/mach-shmobile/pm-r8a7740.c
new file mode 100644 (file)
index 0000000..893504d
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * r8a7740 power management support
+ *
+ * Copyright (C) 2012  Renesas Solutions Corp.
+ * Copyright (C) 2012  Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/console.h>
+#include <mach/pm-rmobile.h>
+
+#ifdef CONFIG_PM
+static int r8a7740_pd_a4s_suspend(void)
+{
+       /*
+        * The A4S domain contains the CPU core and therefore it should
+        * only be turned off if the CPU is in use.
+        */
+       return -EBUSY;
+}
+
+struct rmobile_pm_domain r8a7740_pd_a4s = {
+       .genpd.name     = "A4S",
+       .bit_shift      = 10,
+       .gov            = &pm_domain_always_on_gov,
+       .no_debug       = true,
+       .suspend        = r8a7740_pd_a4s_suspend,
+};
+
+static int r8a7740_pd_a3sp_suspend(void)
+{
+       /*
+        * Serial consoles make use of SCIF hardware located in A3SP,
+        * keep such power domain on if "no_console_suspend" is set.
+        */
+       return console_suspend_enabled ? 0 : -EBUSY;
+}
+
+struct rmobile_pm_domain r8a7740_pd_a3sp = {
+       .genpd.name     = "A3SP",
+       .bit_shift      = 11,
+       .gov            = &pm_domain_always_on_gov,
+       .no_debug       = true,
+       .suspend        = r8a7740_pd_a3sp_suspend,
+};
+
+struct rmobile_pm_domain r8a7740_pd_a4lc = {
+       .genpd.name     = "A4LC",
+       .bit_shift      = 1,
+};
+
+#endif /* CONFIG_PM */
diff --git a/arch/arm/mach-shmobile/pm-rmobile.c b/arch/arm/mach-shmobile/pm-rmobile.c
new file mode 100644 (file)
index 0000000..a856254
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * rmobile power management support
+ *
+ * Copyright (C) 2012  Renesas Solutions Corp.
+ * Copyright (C) 2012  Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * based on pm-sh7372.c
+ *  Copyright (C) 2011 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/console.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_clock.h>
+#include <asm/io.h>
+#include <mach/pm-rmobile.h>
+
+/* SYSC */
+#define SPDCR          0xe6180008
+#define SWUCR          0xe6180014
+#define PSTR           0xe6180080
+
+#define PSTR_RETRIES   100
+#define PSTR_DELAY_US  10
+
+#ifdef CONFIG_PM
+static int rmobile_pd_power_down(struct generic_pm_domain *genpd)
+{
+       struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd);
+       unsigned int mask = 1 << rmobile_pd->bit_shift;
+
+       if (rmobile_pd->suspend) {
+               int ret = rmobile_pd->suspend();
+
+               if (ret)
+                       return ret;
+       }
+
+       if (__raw_readl(PSTR) & mask) {
+               unsigned int retry_count;
+               __raw_writel(mask, SPDCR);
+
+               for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
+                       if (!(__raw_readl(SPDCR) & mask))
+                               break;
+                       cpu_relax();
+               }
+       }
+
+       if (!rmobile_pd->no_debug)
+               pr_debug("%s: Power off, 0x%08x -> PSTR = 0x%08x\n",
+                        genpd->name, mask, __raw_readl(PSTR));
+
+       return 0;
+}
+
+static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd,
+                                bool do_resume)
+{
+       unsigned int mask = 1 << rmobile_pd->bit_shift;
+       unsigned int retry_count;
+       int ret = 0;
+
+       if (__raw_readl(PSTR) & mask)
+               goto out;
+
+       __raw_writel(mask, SWUCR);
+
+       for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
+               if (!(__raw_readl(SWUCR) & mask))
+                       break;
+               if (retry_count > PSTR_RETRIES)
+                       udelay(PSTR_DELAY_US);
+               else
+                       cpu_relax();
+       }
+       if (!retry_count)
+               ret = -EIO;
+
+       if (!rmobile_pd->no_debug)
+               pr_debug("%s: Power on, 0x%08x -> PSTR = 0x%08x\n",
+                        rmobile_pd->genpd.name, mask, __raw_readl(PSTR));
+
+out:
+       if (ret == 0 && rmobile_pd->resume && do_resume)
+               rmobile_pd->resume();
+
+       return ret;
+}
+
+static int rmobile_pd_power_up(struct generic_pm_domain *genpd)
+{
+       return __rmobile_pd_power_up(to_rmobile_pd(genpd), true);
+}
+
+static bool rmobile_pd_active_wakeup(struct device *dev)
+{
+       bool (*active_wakeup)(struct device *dev);
+
+       active_wakeup = dev_gpd_data(dev)->ops.active_wakeup;
+       return active_wakeup ? active_wakeup(dev) : true;
+}
+
+static int rmobile_pd_stop_dev(struct device *dev)
+{
+       int (*stop)(struct device *dev);
+
+       stop = dev_gpd_data(dev)->ops.stop;
+       if (stop) {
+               int ret = stop(dev);
+               if (ret)
+                       return ret;
+       }
+       return pm_clk_suspend(dev);
+}
+
+static int rmobile_pd_start_dev(struct device *dev)
+{
+       int (*start)(struct device *dev);
+       int ret;
+
+       ret = pm_clk_resume(dev);
+       if (ret)
+               return ret;
+
+       start = dev_gpd_data(dev)->ops.start;
+       if (start)
+               ret = start(dev);
+
+       return ret;
+}
+
+void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd)
+{
+       struct generic_pm_domain *genpd = &rmobile_pd->genpd;
+       struct dev_power_governor *gov = rmobile_pd->gov;
+
+       pm_genpd_init(genpd, gov ? : &simple_qos_governor, false);
+       genpd->dev_ops.stop             = rmobile_pd_stop_dev;
+       genpd->dev_ops.start            = rmobile_pd_start_dev;
+       genpd->dev_ops.active_wakeup    = rmobile_pd_active_wakeup;
+       genpd->dev_irq_safe             = true;
+       genpd->power_off                = rmobile_pd_power_down;
+       genpd->power_on                 = rmobile_pd_power_up;
+       __rmobile_pd_power_up(rmobile_pd, false);
+}
+
+void rmobile_add_device_to_domain(struct rmobile_pm_domain *rmobile_pd,
+                                struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+
+       pm_genpd_add_device(&rmobile_pd->genpd, dev);
+       if (pm_clk_no_clocks(dev))
+               pm_clk_add(dev, NULL);
+}
+
+void rmobile_pm_add_subdomain(struct rmobile_pm_domain *rmobile_pd,
+                            struct rmobile_pm_domain *rmobile_sd)
+{
+       pm_genpd_add_subdomain(&rmobile_pd->genpd, &rmobile_sd->genpd);
+}
+#endif /* CONFIG_PM */
index a3bdb12acde99771357de0fb95c1e207ece71e3f..79203706922651dad75595869802fa3520c78a8c 100644 (file)
@@ -26,6 +26,7 @@
 #include <asm/suspend.h>
 #include <mach/common.h>
 #include <mach/sh7372.h>
+#include <mach/pm-rmobile.h>
 
 /* DBG */
 #define DBGREG1 0xe6100020
 #define PLLC01STPCR 0xe61500c8
 
 /* SYSC */
-#define SPDCR 0xe6180008
-#define SWUCR 0xe6180014
 #define SBAR 0xe6180020
 #define WUPRMSK 0xe6180028
 #define WUPSMSK 0xe618002c
 #define WUPSMSK2 0xe6180048
-#define PSTR 0xe6180080
 #define WUPSFAC 0xe6180098
 #define IRQCR 0xe618022c
 #define IRQCR2 0xe6180238
 /* AP-System Core */
 #define APARMBAREA 0xe6f10020
 
-#define PSTR_RETRIES 100
-#define PSTR_DELAY_US 10
-
 #ifdef CONFIG_PM
 
-static int pd_power_down(struct generic_pm_domain *genpd)
-{
-       struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
-       unsigned int mask = 1 << sh7372_pd->bit_shift;
-
-       if (sh7372_pd->suspend) {
-               int ret = sh7372_pd->suspend();
-
-               if (ret)
-                       return ret;
-       }
-
-       if (__raw_readl(PSTR) & mask) {
-               unsigned int retry_count;
-
-               __raw_writel(mask, SPDCR);
-
-               for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
-                       if (!(__raw_readl(SPDCR) & mask))
-                               break;
-                       cpu_relax();
-               }
-       }
-
-       if (!sh7372_pd->no_debug)
-               pr_debug("%s: Power off, 0x%08x -> PSTR = 0x%08x\n",
-                        genpd->name, mask, __raw_readl(PSTR));
-
-       return 0;
-}
-
-static int __pd_power_up(struct sh7372_pm_domain *sh7372_pd, bool do_resume)
-{
-       unsigned int mask = 1 << sh7372_pd->bit_shift;
-       unsigned int retry_count;
-       int ret = 0;
-
-       if (__raw_readl(PSTR) & mask)
-               goto out;
-
-       __raw_writel(mask, SWUCR);
-
-       for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
-               if (!(__raw_readl(SWUCR) & mask))
-                       break;
-               if (retry_count > PSTR_RETRIES)
-                       udelay(PSTR_DELAY_US);
-               else
-                       cpu_relax();
-       }
-       if (!retry_count)
-               ret = -EIO;
-
-       if (!sh7372_pd->no_debug)
-               pr_debug("%s: Power on, 0x%08x -> PSTR = 0x%08x\n",
-                        sh7372_pd->genpd.name, mask, __raw_readl(PSTR));
-
- out:
-       if (ret == 0 && sh7372_pd->resume && do_resume)
-               sh7372_pd->resume();
-
-       return ret;
-}
-
-static int pd_power_up(struct generic_pm_domain *genpd)
-{
-        return __pd_power_up(to_sh7372_pd(genpd), true);
-}
-
-static int sh7372_a4r_suspend(void)
-{
-       sh7372_intcs_suspend();
-       __raw_writel(0x300fffff, WUPRMSK); /* avoid wakeup */
-       return 0;
-}
-
-static bool pd_active_wakeup(struct device *dev)
-{
-       bool (*active_wakeup)(struct device *dev);
-
-       active_wakeup = dev_gpd_data(dev)->ops.active_wakeup;
-       return active_wakeup ? active_wakeup(dev) : true;
-}
-
-static int sh7372_stop_dev(struct device *dev)
-{
-       int (*stop)(struct device *dev);
-
-       stop = dev_gpd_data(dev)->ops.stop;
-       if (stop) {
-               int ret = stop(dev);
-               if (ret)
-                       return ret;
-       }
-       return pm_clk_suspend(dev);
-}
-
-static int sh7372_start_dev(struct device *dev)
-{
-       int (*start)(struct device *dev);
-       int ret;
-
-       ret = pm_clk_resume(dev);
-       if (ret)
-               return ret;
-
-       start = dev_gpd_data(dev)->ops.start;
-       if (start)
-               ret = start(dev);
-
-       return ret;
-}
-
-void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
-{
-       struct generic_pm_domain *genpd = &sh7372_pd->genpd;
-       struct dev_power_governor *gov = sh7372_pd->gov;
-
-       pm_genpd_init(genpd, gov ? : &simple_qos_governor, false);
-       genpd->dev_ops.stop = sh7372_stop_dev;
-       genpd->dev_ops.start = sh7372_start_dev;
-       genpd->dev_ops.active_wakeup = pd_active_wakeup;
-       genpd->dev_irq_safe = true;
-       genpd->power_off = pd_power_down;
-       genpd->power_on = pd_power_up;
-       __pd_power_up(sh7372_pd, false);
-}
-
-void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
-                                struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-
-       pm_genpd_add_device(&sh7372_pd->genpd, dev);
-       if (pm_clk_no_clocks(dev))
-               pm_clk_add(dev, NULL);
-}
-
-void sh7372_pm_add_subdomain(struct sh7372_pm_domain *sh7372_pd,
-                            struct sh7372_pm_domain *sh7372_sd)
-{
-       pm_genpd_add_subdomain(&sh7372_pd->genpd, &sh7372_sd->genpd);
-}
-
-struct sh7372_pm_domain sh7372_a4lc = {
+struct rmobile_pm_domain sh7372_pd_a4lc = {
        .genpd.name = "A4LC",
        .bit_shift = 1,
 };
 
-struct sh7372_pm_domain sh7372_a4mp = {
+struct rmobile_pm_domain sh7372_pd_a4mp = {
        .genpd.name = "A4MP",
        .bit_shift = 2,
 };
 
-struct sh7372_pm_domain sh7372_d4 = {
+struct rmobile_pm_domain sh7372_pd_d4 = {
        .genpd.name = "D4",
        .bit_shift = 3,
 };
 
-struct sh7372_pm_domain sh7372_a4r = {
+static int sh7372_a4r_pd_suspend(void)
+{
+       sh7372_intcs_suspend();
+       __raw_writel(0x300fffff, WUPRMSK); /* avoid wakeup */
+       return 0;
+}
+
+struct rmobile_pm_domain sh7372_pd_a4r = {
        .genpd.name = "A4R",
        .bit_shift = 5,
-       .suspend = sh7372_a4r_suspend,
+       .suspend = sh7372_a4r_pd_suspend,
        .resume = sh7372_intcs_resume,
 };
 
-struct sh7372_pm_domain sh7372_a3rv = {
+struct rmobile_pm_domain sh7372_pd_a3rv = {
        .genpd.name = "A3RV",
        .bit_shift = 6,
 };
 
-struct sh7372_pm_domain sh7372_a3ri = {
+struct rmobile_pm_domain sh7372_pd_a3ri = {
        .genpd.name = "A3RI",
        .bit_shift = 8,
 };
 
-static int sh7372_a4s_suspend(void)
+static int sh7372_pd_a4s_suspend(void)
 {
        /*
         * The A4S domain contains the CPU core and therefore it should
@@ -261,15 +119,15 @@ static int sh7372_a4s_suspend(void)
        return -EBUSY;
 }
 
-struct sh7372_pm_domain sh7372_a4s = {
+struct rmobile_pm_domain sh7372_pd_a4s = {
        .genpd.name = "A4S",
        .bit_shift = 10,
        .gov = &pm_domain_always_on_gov,
        .no_debug = true,
-       .suspend = sh7372_a4s_suspend,
+       .suspend = sh7372_pd_a4s_suspend,
 };
 
-static int sh7372_a3sp_suspend(void)
+static int sh7372_a3sp_pd_suspend(void)
 {
        /*
         * Serial consoles make use of SCIF hardware located in A3SP,
@@ -278,32 +136,22 @@ static int sh7372_a3sp_suspend(void)
        return console_suspend_enabled ? 0 : -EBUSY;
 }
 
-struct sh7372_pm_domain sh7372_a3sp = {
+struct rmobile_pm_domain sh7372_pd_a3sp = {
        .genpd.name = "A3SP",
        .bit_shift = 11,
        .gov = &pm_domain_always_on_gov,
        .no_debug = true,
-       .suspend = sh7372_a3sp_suspend,
+       .suspend = sh7372_a3sp_pd_suspend,
 };
 
-struct sh7372_pm_domain sh7372_a3sg = {
+struct rmobile_pm_domain sh7372_pd_a3sg = {
        .genpd.name = "A3SG",
        .bit_shift = 13,
 };
 
-#else /* !CONFIG_PM */
-
-static inline void sh7372_a3sp_init(void) {}
-
-#endif /* !CONFIG_PM */
+#endif /* CONFIG_PM */
 
 #if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
-static int sh7372_do_idle_core_standby(unsigned long unused)
-{
-       cpu_do_idle(); /* WFI when SYSTBCR == 0x10 -> Core Standby */
-       return 0;
-}
-
 static void sh7372_set_reset_vector(unsigned long address)
 {
        /* set reset vector, translate 4k */
@@ -311,21 +159,6 @@ static void sh7372_set_reset_vector(unsigned long address)
        __raw_writel(0, APARMBAREA);
 }
 
-static void sh7372_enter_core_standby(void)
-{
-       sh7372_set_reset_vector(__pa(sh7372_resume_core_standby_sysc));
-
-       /* enter sleep mode with SYSTBCR to 0x10 */
-       __raw_writel(0x10, SYSTBCR);
-       cpu_suspend(0, sh7372_do_idle_core_standby);
-       __raw_writel(0, SYSTBCR);
-
-        /* disable reset vector translation */
-       __raw_writel(0, SBAR);
-}
-#endif
-
-#ifdef CONFIG_SUSPEND
 static void sh7372_enter_sysc(int pllc0_on, unsigned long sleep_mode)
 {
        if (pllc0_on)
@@ -465,22 +298,42 @@ static void sh7372_setup_sysc(unsigned long msk, unsigned long msk2)
 
 static void sh7372_enter_a3sm_common(int pllc0_on)
 {
+       /* use INTCA together with SYSC for wakeup */
+       sh7372_setup_sysc(1 << 0, 0);
        sh7372_set_reset_vector(__pa(sh7372_resume_core_standby_sysc));
        sh7372_enter_sysc(pllc0_on, 1 << 12);
 }
+#endif /* CONFIG_SUSPEND || CONFIG_CPU_IDLE */
 
-static void sh7372_enter_a4s_common(int pllc0_on)
+#ifdef CONFIG_CPU_IDLE
+static int sh7372_do_idle_core_standby(unsigned long unused)
 {
-       sh7372_intca_suspend();
-       memcpy((void *)SMFRAM, sh7372_resume_core_standby_sysc, 0x100);
-       sh7372_set_reset_vector(SMFRAM);
-       sh7372_enter_sysc(pllc0_on, 1 << 10);
-       sh7372_intca_resume();
+       cpu_do_idle(); /* WFI when SYSTBCR == 0x10 -> Core Standby */
+       return 0;
 }
 
-#endif
+static void sh7372_enter_core_standby(void)
+{
+       sh7372_set_reset_vector(__pa(sh7372_resume_core_standby_sysc));
 
-#ifdef CONFIG_CPU_IDLE
+       /* enter sleep mode with SYSTBCR to 0x10 */
+       __raw_writel(0x10, SYSTBCR);
+       cpu_suspend(0, sh7372_do_idle_core_standby);
+       __raw_writel(0, SYSTBCR);
+
+        /* disable reset vector translation */
+       __raw_writel(0, SBAR);
+}
+
+static void sh7372_enter_a3sm_pll_on(void)
+{
+       sh7372_enter_a3sm_common(1);
+}
+
+static void sh7372_enter_a3sm_pll_off(void)
+{
+       sh7372_enter_a3sm_common(0);
+}
 
 static void sh7372_cpuidle_setup(struct cpuidle_driver *drv)
 {
@@ -492,7 +345,24 @@ static void sh7372_cpuidle_setup(struct cpuidle_driver *drv)
        state->target_residency = 20 + 10;
        state->flags = CPUIDLE_FLAG_TIME_VALID;
        shmobile_cpuidle_modes[drv->state_count] = sh7372_enter_core_standby;
+       drv->state_count++;
+
+       state = &drv->states[drv->state_count];
+       snprintf(state->name, CPUIDLE_NAME_LEN, "C3");
+       strncpy(state->desc, "A3SM PLL ON", CPUIDLE_DESC_LEN);
+       state->exit_latency = 20;
+       state->target_residency = 30 + 20;
+       state->flags = CPUIDLE_FLAG_TIME_VALID;
+       shmobile_cpuidle_modes[drv->state_count] = sh7372_enter_a3sm_pll_on;
+       drv->state_count++;
 
+       state = &drv->states[drv->state_count];
+       snprintf(state->name, CPUIDLE_NAME_LEN, "C4");
+       strncpy(state->desc, "A3SM PLL OFF", CPUIDLE_DESC_LEN);
+       state->exit_latency = 120;
+       state->target_residency = 30 + 120;
+       state->flags = CPUIDLE_FLAG_TIME_VALID;
+       shmobile_cpuidle_modes[drv->state_count] = sh7372_enter_a3sm_pll_off;
        drv->state_count++;
 }
 
@@ -505,6 +375,14 @@ static void sh7372_cpuidle_init(void) {}
 #endif
 
 #ifdef CONFIG_SUSPEND
+static void sh7372_enter_a4s_common(int pllc0_on)
+{
+       sh7372_intca_suspend();
+       memcpy((void *)SMFRAM, sh7372_resume_core_standby_sysc, 0x100);
+       sh7372_set_reset_vector(SMFRAM);
+       sh7372_enter_sysc(pllc0_on, 1 << 10);
+       sh7372_intca_resume();
+}
 
 static int sh7372_enter_suspend(suspend_state_t suspend_state)
 {
@@ -512,24 +390,21 @@ static int sh7372_enter_suspend(suspend_state_t suspend_state)
 
        /* check active clocks to determine potential wakeup sources */
        if (sh7372_sysc_valid(&msk, &msk2)) {
-               /* convert INTC mask and sense to SYSC mask and sense */
-               sh7372_setup_sysc(msk, msk2);
-
                if (!console_suspend_enabled &&
-                   sh7372_a4s.genpd.status == GPD_STATE_POWER_OFF) {
+                   sh7372_pd_a4s.genpd.status == GPD_STATE_POWER_OFF) {
+                       /* convert INTC mask/sense to SYSC mask/sense */
+                       sh7372_setup_sysc(msk, msk2);
+
                        /* enter A4S sleep with PLLC0 off */
                        pr_debug("entering A4S\n");
                        sh7372_enter_a4s_common(0);
-               } else {
-                       /* enter A3SM sleep with PLLC0 off */
-                       pr_debug("entering A3SM\n");
-                       sh7372_enter_a3sm_common(0);
+                       return 0;
                }
-       } else {
-               /* default to Core Standby that supports all wakeup sources */
-               pr_debug("entering Core Standby\n");
-               sh7372_enter_core_standby();
        }
+
+       /* default to enter A3SM sleep with PLLC0 off */
+       pr_debug("entering A3SM\n");
+       sh7372_enter_a3sm_common(0);
        return 0;
 }
 
@@ -550,7 +425,7 @@ static int sh7372_pm_notifier_fn(struct notifier_block *notifier,
                 * executed during system suspend and resume, respectively, so
                 * that those functions don't crash while accessing the INTCS.
                 */
-               pm_genpd_poweron(&sh7372_a4r.genpd);
+               pm_genpd_poweron(&sh7372_pd_a4r.genpd);
                break;
        case PM_POST_SUSPEND:
                pm_genpd_poweroff_unused();
index ec4eb49c16938a28b438282353e5194bc2592688..78948a9dba0ec47547f5fca10b7aa71452600d7b 100644 (file)
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
+#include <linux/of_platform.h>
 #include <linux/serial_sci.h>
+#include <linux/sh_dma.h>
 #include <linux/sh_timer.h>
+#include <linux/dma-mapping.h>
+#include <mach/dma-register.h>
 #include <mach/r8a7740.h>
+#include <mach/pm-rmobile.h>
 #include <mach/common.h>
 #include <mach/irqs.h>
 #include <asm/mach-types.h>
@@ -276,6 +281,272 @@ static struct platform_device *r8a7740_early_devices[] __initdata = {
        &cmt10_device,
 };
 
+/* DMA */
+static const struct sh_dmae_slave_config r8a7740_dmae_slaves[] = {
+       {
+               .slave_id       = SHDMA_SLAVE_SDHI0_TX,
+               .addr           = 0xe6850030,
+               .chcr           = CHCR_TX(XMIT_SZ_16BIT),
+               .mid_rid        = 0xc1,
+       }, {
+               .slave_id       = SHDMA_SLAVE_SDHI0_RX,
+               .addr           = 0xe6850030,
+               .chcr           = CHCR_RX(XMIT_SZ_16BIT),
+               .mid_rid        = 0xc2,
+       }, {
+               .slave_id       = SHDMA_SLAVE_SDHI1_TX,
+               .addr           = 0xe6860030,
+               .chcr           = CHCR_TX(XMIT_SZ_16BIT),
+               .mid_rid        = 0xc9,
+       }, {
+               .slave_id       = SHDMA_SLAVE_SDHI1_RX,
+               .addr           = 0xe6860030,
+               .chcr           = CHCR_RX(XMIT_SZ_16BIT),
+               .mid_rid        = 0xca,
+       }, {
+               .slave_id       = SHDMA_SLAVE_SDHI2_TX,
+               .addr           = 0xe6870030,
+               .chcr           = CHCR_TX(XMIT_SZ_16BIT),
+               .mid_rid        = 0xcd,
+       }, {
+               .slave_id       = SHDMA_SLAVE_SDHI2_RX,
+               .addr           = 0xe6870030,
+               .chcr           = CHCR_RX(XMIT_SZ_16BIT),
+               .mid_rid        = 0xce,
+       }, {
+               .slave_id       = SHDMA_SLAVE_FSIA_TX,
+               .addr           = 0xfe1f0024,
+               .chcr           = CHCR_TX(XMIT_SZ_32BIT),
+               .mid_rid        = 0xb1,
+       }, {
+               .slave_id       = SHDMA_SLAVE_FSIA_RX,
+               .addr           = 0xfe1f0020,
+               .chcr           = CHCR_RX(XMIT_SZ_32BIT),
+               .mid_rid        = 0xb2,
+       }, {
+               .slave_id       = SHDMA_SLAVE_FSIB_TX,
+               .addr           = 0xfe1f0064,
+               .chcr           = CHCR_TX(XMIT_SZ_32BIT),
+               .mid_rid        = 0xb5,
+       },
+};
+
+#define DMA_CHANNEL(a, b, c)                   \
+{                                              \
+       .offset         = a,                    \
+       .dmars          = b,                    \
+       .dmars_bit      = c,                    \
+       .chclr_offset   = (0x220 - 0x20) + a    \
+}
+
+static const struct sh_dmae_channel r8a7740_dmae_channels[] = {
+       DMA_CHANNEL(0x00, 0, 0),
+       DMA_CHANNEL(0x10, 0, 8),
+       DMA_CHANNEL(0x20, 4, 0),
+       DMA_CHANNEL(0x30, 4, 8),
+       DMA_CHANNEL(0x50, 8, 0),
+       DMA_CHANNEL(0x60, 8, 8),
+};
+
+static struct sh_dmae_pdata dma_platform_data = {
+       .slave          = r8a7740_dmae_slaves,
+       .slave_num      = ARRAY_SIZE(r8a7740_dmae_slaves),
+       .channel        = r8a7740_dmae_channels,
+       .channel_num    = ARRAY_SIZE(r8a7740_dmae_channels),
+       .ts_low_shift   = TS_LOW_SHIFT,
+       .ts_low_mask    = TS_LOW_BIT << TS_LOW_SHIFT,
+       .ts_high_shift  = TS_HI_SHIFT,
+       .ts_high_mask   = TS_HI_BIT << TS_HI_SHIFT,
+       .ts_shift       = dma_ts_shift,
+       .ts_shift_num   = ARRAY_SIZE(dma_ts_shift),
+       .dmaor_init     = DMAOR_DME,
+       .chclr_present  = 1,
+};
+
+/* Resource order important! */
+static struct resource r8a7740_dmae0_resources[] = {
+       {
+               /* Channel registers and DMAOR */
+               .start  = 0xfe008020,
+               .end    = 0xfe00828f,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               /* DMARSx */
+               .start  = 0xfe009000,
+               .end    = 0xfe00900b,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               .name   = "error_irq",
+               .start  = evt2irq(0x20c0),
+               .end    = evt2irq(0x20c0),
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               /* IRQ for channels 0-5 */
+               .start  = evt2irq(0x2000),
+               .end    = evt2irq(0x20a0),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+/* Resource order important! */
+static struct resource r8a7740_dmae1_resources[] = {
+       {
+               /* Channel registers and DMAOR */
+               .start  = 0xfe018020,
+               .end    = 0xfe01828f,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               /* DMARSx */
+               .start  = 0xfe019000,
+               .end    = 0xfe01900b,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               .name   = "error_irq",
+               .start  = evt2irq(0x21c0),
+               .end    = evt2irq(0x21c0),
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               /* IRQ for channels 0-5 */
+               .start  = evt2irq(0x2100),
+               .end    = evt2irq(0x21a0),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+/* Resource order important! */
+static struct resource r8a7740_dmae2_resources[] = {
+       {
+               /* Channel registers and DMAOR */
+               .start  = 0xfe028020,
+               .end    = 0xfe02828f,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               /* DMARSx */
+               .start  = 0xfe029000,
+               .end    = 0xfe02900b,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               .name   = "error_irq",
+               .start  = evt2irq(0x22c0),
+               .end    = evt2irq(0x22c0),
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               /* IRQ for channels 0-5 */
+               .start  = evt2irq(0x2200),
+               .end    = evt2irq(0x22a0),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device dma0_device = {
+       .name           = "sh-dma-engine",
+       .id             = 0,
+       .resource       = r8a7740_dmae0_resources,
+       .num_resources  = ARRAY_SIZE(r8a7740_dmae0_resources),
+       .dev            = {
+               .platform_data  = &dma_platform_data,
+       },
+};
+
+static struct platform_device dma1_device = {
+       .name           = "sh-dma-engine",
+       .id             = 1,
+       .resource       = r8a7740_dmae1_resources,
+       .num_resources  = ARRAY_SIZE(r8a7740_dmae1_resources),
+       .dev            = {
+               .platform_data  = &dma_platform_data,
+       },
+};
+
+static struct platform_device dma2_device = {
+       .name           = "sh-dma-engine",
+       .id             = 2,
+       .resource       = r8a7740_dmae2_resources,
+       .num_resources  = ARRAY_SIZE(r8a7740_dmae2_resources),
+       .dev            = {
+               .platform_data  = &dma_platform_data,
+       },
+};
+
+/* USB-DMAC */
+static const struct sh_dmae_channel r8a7740_usb_dma_channels[] = {
+       {
+               .offset = 0,
+       }, {
+               .offset = 0x20,
+       },
+};
+
+static const struct sh_dmae_slave_config r8a7740_usb_dma_slaves[] = {
+       {
+               .slave_id       = SHDMA_SLAVE_USBHS_TX,
+               .chcr           = USBTS_INDEX2VAL(USBTS_XMIT_SZ_8BYTE),
+       }, {
+               .slave_id       = SHDMA_SLAVE_USBHS_RX,
+               .chcr           = USBTS_INDEX2VAL(USBTS_XMIT_SZ_8BYTE),
+       },
+};
+
+static struct sh_dmae_pdata usb_dma_platform_data = {
+       .slave          = r8a7740_usb_dma_slaves,
+       .slave_num      = ARRAY_SIZE(r8a7740_usb_dma_slaves),
+       .channel        = r8a7740_usb_dma_channels,
+       .channel_num    = ARRAY_SIZE(r8a7740_usb_dma_channels),
+       .ts_low_shift   = USBTS_LOW_SHIFT,
+       .ts_low_mask    = USBTS_LOW_BIT << USBTS_LOW_SHIFT,
+       .ts_high_shift  = USBTS_HI_SHIFT,
+       .ts_high_mask   = USBTS_HI_BIT << USBTS_HI_SHIFT,
+       .ts_shift       = dma_usbts_shift,
+       .ts_shift_num   = ARRAY_SIZE(dma_usbts_shift),
+       .dmaor_init     = DMAOR_DME,
+       .chcr_offset    = 0x14,
+       .chcr_ie_bit    = 1 << 5,
+       .dmaor_is_32bit = 1,
+       .needs_tend_set = 1,
+       .no_dmars       = 1,
+       .slave_only     = 1,
+};
+
+static struct resource r8a7740_usb_dma_resources[] = {
+       {
+               /* Channel registers and DMAOR */
+               .start  = 0xe68a0020,
+               .end    = 0xe68a0064 - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               /* VCR/SWR/DMICR */
+               .start  = 0xe68a0000,
+               .end    = 0xe68a0014 - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               /* IRQ for channels */
+               .start  = evt2irq(0x0a00),
+               .end    = evt2irq(0x0a00),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device usb_dma_device = {
+       .name           = "sh-dma-engine",
+       .id             = 3,
+       .resource       = r8a7740_usb_dma_resources,
+       .num_resources  = ARRAY_SIZE(r8a7740_usb_dma_resources),
+       .dev            = {
+               .platform_data  = &usb_dma_platform_data,
+       },
+};
+
 /* I2C */
 static struct resource i2c0_resources[] = {
        [0] = {
@@ -322,8 +593,30 @@ static struct platform_device i2c1_device = {
 static struct platform_device *r8a7740_late_devices[] __initdata = {
        &i2c0_device,
        &i2c1_device,
+       &dma0_device,
+       &dma1_device,
+       &dma2_device,
+       &usb_dma_device,
 };
 
+/*
+ * r8a7740 chip has lasting errata on MERAM buffer.
+ * this is work-around for it.
+ * see
+ *     "Media RAM (MERAM)" on r8a7740 documentation
+ */
+#define MEBUFCNTR      0xFE950098
+void r8a7740_meram_workaround(void)
+{
+       void __iomem *reg;
+
+       reg = ioremap_nocache(MEBUFCNTR, 4);
+       if (reg) {
+               iowrite32(0x01600164, reg);
+               iounmap(reg);
+       }
+}
+
 #define ICCR   0x0004
 #define ICSTART        0x0070
 
@@ -380,10 +673,31 @@ void __init r8a7740_add_standard_devices(void)
        r8a7740_i2c_workaround(&i2c0_device);
        r8a7740_i2c_workaround(&i2c1_device);
 
+       /* PM domain */
+       rmobile_init_pm_domain(&r8a7740_pd_a4s);
+       rmobile_init_pm_domain(&r8a7740_pd_a3sp);
+       rmobile_init_pm_domain(&r8a7740_pd_a4lc);
+
+       rmobile_pm_add_subdomain(&r8a7740_pd_a4s, &r8a7740_pd_a3sp);
+
+       /* add devices */
        platform_add_devices(r8a7740_early_devices,
                            ARRAY_SIZE(r8a7740_early_devices));
        platform_add_devices(r8a7740_late_devices,
                             ARRAY_SIZE(r8a7740_late_devices));
+
+       /* add devices to PM domain  */
+
+       rmobile_add_device_to_domain(&r8a7740_pd_a3sp,  &scif0_device);
+       rmobile_add_device_to_domain(&r8a7740_pd_a3sp,  &scif1_device);
+       rmobile_add_device_to_domain(&r8a7740_pd_a3sp,  &scif2_device);
+       rmobile_add_device_to_domain(&r8a7740_pd_a3sp,  &scif3_device);
+       rmobile_add_device_to_domain(&r8a7740_pd_a3sp,  &scif4_device);
+       rmobile_add_device_to_domain(&r8a7740_pd_a3sp,  &scif5_device);
+       rmobile_add_device_to_domain(&r8a7740_pd_a3sp,  &scif6_device);
+       rmobile_add_device_to_domain(&r8a7740_pd_a3sp,  &scif7_device);
+       rmobile_add_device_to_domain(&r8a7740_pd_a3sp,  &scifb_device);
+       rmobile_add_device_to_domain(&r8a7740_pd_a3sp,  &i2c1_device);
 }
 
 static void __init r8a7740_earlytimer_init(void)
@@ -403,3 +717,49 @@ void __init r8a7740_add_early_devices(void)
        /* override timer setup with soc-specific code */
        shmobile_timer.init = r8a7740_earlytimer_init;
 }
+
+#ifdef CONFIG_USE_OF
+
+void __init r8a7740_add_early_devices_dt(void)
+{
+       shmobile_setup_delay(800, 1, 3); /* Cortex-A9 @ 800MHz */
+
+       early_platform_add_devices(r8a7740_early_devices,
+                                  ARRAY_SIZE(r8a7740_early_devices));
+
+       /* setup early console here as well */
+       shmobile_setup_console();
+}
+
+static const struct of_dev_auxdata r8a7740_auxdata_lookup[] __initconst = {
+       { }
+};
+
+void __init r8a7740_add_standard_devices_dt(void)
+{
+       /* clocks are setup late during boot in the case of DT */
+       r8a7740_clock_init(0);
+
+       platform_add_devices(r8a7740_early_devices,
+                           ARRAY_SIZE(r8a7740_early_devices));
+
+       of_platform_populate(NULL, of_default_bus_match_table,
+                            r8a7740_auxdata_lookup, NULL);
+}
+
+static const char *r8a7740_boards_compat_dt[] __initdata = {
+       "renesas,r8a7740",
+       NULL,
+};
+
+DT_MACHINE_START(SH7372_DT, "Generic R8A7740 (Flattened Device Tree)")
+       .map_io         = r8a7740_map_io,
+       .init_early     = r8a7740_add_early_devices_dt,
+       .init_irq       = r8a7740_init_irq,
+       .handle_irq     = shmobile_handle_irq_intc,
+       .init_machine   = r8a7740_add_standard_devices_dt,
+       .timer          = &shmobile_timer,
+       .dt_compat      = r8a7740_boards_compat_dt,
+MACHINE_END
+
+#endif /* CONFIG_USE_OF */
index fafce9ce8218c440028b6083a2254c4452b1a223..838a87be1d5c31cc46a932d58f27a73f00066ca9 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/sh_timer.h>
 #include <linux/pm_domain.h>
 #include <linux/dma-mapping.h>
+#include <mach/dma-register.h>
 #include <mach/hardware.h>
 #include <mach/irqs.h>
 #include <mach/sh7372.h>
@@ -335,151 +336,126 @@ static struct platform_device iic1_device = {
 };
 
 /* DMA */
-/* Transmit sizes and respective CHCR register values */
-enum {
-       XMIT_SZ_8BIT            = 0,
-       XMIT_SZ_16BIT           = 1,
-       XMIT_SZ_32BIT           = 2,
-       XMIT_SZ_64BIT           = 7,
-       XMIT_SZ_128BIT          = 3,
-       XMIT_SZ_256BIT          = 4,
-       XMIT_SZ_512BIT          = 5,
-};
-
-/* log2(size / 8) - used to calculate number of transfers */
-#define TS_SHIFT {                     \
-       [XMIT_SZ_8BIT]          = 0,    \
-       [XMIT_SZ_16BIT]         = 1,    \
-       [XMIT_SZ_32BIT]         = 2,    \
-       [XMIT_SZ_64BIT]         = 3,    \
-       [XMIT_SZ_128BIT]        = 4,    \
-       [XMIT_SZ_256BIT]        = 5,    \
-       [XMIT_SZ_512BIT]        = 6,    \
-}
-
-#define TS_INDEX2VAL(i) ((((i) & 3) << 3) | \
-                        (((i) & 0xc) << (20 - 2)))
-
 static const struct sh_dmae_slave_config sh7372_dmae_slaves[] = {
        {
                .slave_id       = SHDMA_SLAVE_SCIF0_TX,
                .addr           = 0xe6c40020,
-               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .chcr           = CHCR_TX(XMIT_SZ_8BIT),
                .mid_rid        = 0x21,
        }, {
                .slave_id       = SHDMA_SLAVE_SCIF0_RX,
                .addr           = 0xe6c40024,
-               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .chcr           = CHCR_RX(XMIT_SZ_8BIT),
                .mid_rid        = 0x22,
        }, {
                .slave_id       = SHDMA_SLAVE_SCIF1_TX,
                .addr           = 0xe6c50020,
-               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .chcr           = CHCR_TX(XMIT_SZ_8BIT),
                .mid_rid        = 0x25,
        }, {
                .slave_id       = SHDMA_SLAVE_SCIF1_RX,
                .addr           = 0xe6c50024,
-               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .chcr           = CHCR_RX(XMIT_SZ_8BIT),
                .mid_rid        = 0x26,
        }, {
                .slave_id       = SHDMA_SLAVE_SCIF2_TX,
                .addr           = 0xe6c60020,
-               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .chcr           = CHCR_TX(XMIT_SZ_8BIT),
                .mid_rid        = 0x29,
        }, {
                .slave_id       = SHDMA_SLAVE_SCIF2_RX,
                .addr           = 0xe6c60024,
-               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .chcr           = CHCR_RX(XMIT_SZ_8BIT),
                .mid_rid        = 0x2a,
        }, {
                .slave_id       = SHDMA_SLAVE_SCIF3_TX,
                .addr           = 0xe6c70020,
-               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .chcr           = CHCR_TX(XMIT_SZ_8BIT),
                .mid_rid        = 0x2d,
        }, {
                .slave_id       = SHDMA_SLAVE_SCIF3_RX,
                .addr           = 0xe6c70024,
-               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .chcr           = CHCR_RX(XMIT_SZ_8BIT),
                .mid_rid        = 0x2e,
        }, {
                .slave_id       = SHDMA_SLAVE_SCIF4_TX,
                .addr           = 0xe6c80020,
-               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .chcr           = CHCR_TX(XMIT_SZ_8BIT),
                .mid_rid        = 0x39,
        }, {
                .slave_id       = SHDMA_SLAVE_SCIF4_RX,
                .addr           = 0xe6c80024,
-               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .chcr           = CHCR_RX(XMIT_SZ_8BIT),
                .mid_rid        = 0x3a,
        }, {
                .slave_id       = SHDMA_SLAVE_SCIF5_TX,
                .addr           = 0xe6cb0020,
-               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .chcr           = CHCR_TX(XMIT_SZ_8BIT),
                .mid_rid        = 0x35,
        }, {
                .slave_id       = SHDMA_SLAVE_SCIF5_RX,
                .addr           = 0xe6cb0024,
-               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .chcr           = CHCR_RX(XMIT_SZ_8BIT),
                .mid_rid        = 0x36,
        }, {
                .slave_id       = SHDMA_SLAVE_SCIF6_TX,
                .addr           = 0xe6c30040,
-               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .chcr           = CHCR_TX(XMIT_SZ_8BIT),
                .mid_rid        = 0x3d,
        }, {
                .slave_id       = SHDMA_SLAVE_SCIF6_RX,
                .addr           = 0xe6c30060,
-               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .chcr           = CHCR_RX(XMIT_SZ_8BIT),
                .mid_rid        = 0x3e,
        }, {
                .slave_id       = SHDMA_SLAVE_SDHI0_TX,
                .addr           = 0xe6850030,
-               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_16BIT),
+               .chcr           = CHCR_TX(XMIT_SZ_16BIT),
                .mid_rid        = 0xc1,
        }, {
                .slave_id       = SHDMA_SLAVE_SDHI0_RX,
                .addr           = 0xe6850030,
-               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_16BIT),
+               .chcr           = CHCR_RX(XMIT_SZ_16BIT),
                .mid_rid        = 0xc2,
        }, {
                .slave_id       = SHDMA_SLAVE_SDHI1_TX,
                .addr           = 0xe6860030,
-               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_16BIT),
+               .chcr           = CHCR_TX(XMIT_SZ_16BIT),
                .mid_rid        = 0xc9,
        }, {
                .slave_id       = SHDMA_SLAVE_SDHI1_RX,
                .addr           = 0xe6860030,
-               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_16BIT),
+               .chcr           = CHCR_RX(XMIT_SZ_16BIT),
                .mid_rid        = 0xca,
        }, {
                .slave_id       = SHDMA_SLAVE_SDHI2_TX,
                .addr           = 0xe6870030,
-               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_16BIT),
+               .chcr           = CHCR_TX(XMIT_SZ_16BIT),
                .mid_rid        = 0xcd,
        }, {
                .slave_id       = SHDMA_SLAVE_SDHI2_RX,
                .addr           = 0xe6870030,
-               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_16BIT),
+               .chcr           = CHCR_RX(XMIT_SZ_16BIT),
                .mid_rid        = 0xce,
        }, {
                .slave_id       = SHDMA_SLAVE_FSIA_TX,
                .addr           = 0xfe1f0024,
-               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_32BIT),
+               .chcr           = CHCR_TX(XMIT_SZ_32BIT),
                .mid_rid        = 0xb1,
        }, {
                .slave_id       = SHDMA_SLAVE_FSIA_RX,
                .addr           = 0xfe1f0020,
-               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_32BIT),
+               .chcr           = CHCR_RX(XMIT_SZ_32BIT),
                .mid_rid        = 0xb2,
        }, {
                .slave_id       = SHDMA_SLAVE_MMCIF_TX,
                .addr           = 0xe6bd0034,
-               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_32BIT),
+               .chcr           = CHCR_TX(XMIT_SZ_32BIT),
                .mid_rid        = 0xd1,
        }, {
                .slave_id       = SHDMA_SLAVE_MMCIF_RX,
                .addr           = 0xe6bd0034,
-               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_32BIT),
+               .chcr           = CHCR_RX(XMIT_SZ_32BIT),
                .mid_rid        = 0xd2,
        },
 };
@@ -520,19 +496,17 @@ static const struct sh_dmae_channel sh7372_dmae_channels[] = {
        }
 };
 
-static const unsigned int ts_shift[] = TS_SHIFT;
-
 static struct sh_dmae_pdata dma_platform_data = {
        .slave          = sh7372_dmae_slaves,
        .slave_num      = ARRAY_SIZE(sh7372_dmae_slaves),
        .channel        = sh7372_dmae_channels,
        .channel_num    = ARRAY_SIZE(sh7372_dmae_channels),
-       .ts_low_shift   = 3,
-       .ts_low_mask    = 0x18,
-       .ts_high_shift  = (20 - 2),     /* 2 bits for shifted low TS */
-       .ts_high_mask   = 0x00300000,
-       .ts_shift       = ts_shift,
-       .ts_shift_num   = ARRAY_SIZE(ts_shift),
+       .ts_low_shift   = TS_LOW_SHIFT,
+       .ts_low_mask    = TS_LOW_BIT << TS_LOW_SHIFT,
+       .ts_high_shift  = TS_HI_SHIFT,
+       .ts_high_mask   = TS_HI_BIT << TS_HI_SHIFT,
+       .ts_shift       = dma_ts_shift,
+       .ts_shift_num   = ARRAY_SIZE(dma_ts_shift),
        .dmaor_init     = DMAOR_DME,
        .chclr_present  = 1,
 };
@@ -654,17 +628,6 @@ static struct platform_device dma2_device = {
 /*
  * USB-DMAC
  */
-
-unsigned int usbts_shift[] = {3, 4, 5};
-
-enum {
-       XMIT_SZ_8BYTE           = 0,
-       XMIT_SZ_16BYTE          = 1,
-       XMIT_SZ_32BYTE          = 2,
-};
-
-#define USBTS_INDEX2VAL(i) (((i) & 3) << 6)
-
 static const struct sh_dmae_channel sh7372_usb_dmae_channels[] = {
        {
                .offset = 0,
@@ -677,10 +640,10 @@ static const struct sh_dmae_channel sh7372_usb_dmae_channels[] = {
 static const struct sh_dmae_slave_config sh7372_usb_dmae0_slaves[] = {
        {
                .slave_id       = SHDMA_SLAVE_USB0_TX,
-               .chcr           = USBTS_INDEX2VAL(XMIT_SZ_8BYTE),
+               .chcr           = USBTS_INDEX2VAL(USBTS_XMIT_SZ_8BYTE),
        }, {
                .slave_id       = SHDMA_SLAVE_USB0_RX,
-               .chcr           = USBTS_INDEX2VAL(XMIT_SZ_8BYTE),
+               .chcr           = USBTS_INDEX2VAL(USBTS_XMIT_SZ_8BYTE),
        },
 };
 
@@ -689,12 +652,12 @@ static struct sh_dmae_pdata usb_dma0_platform_data = {
        .slave_num      = ARRAY_SIZE(sh7372_usb_dmae0_slaves),
        .channel        = sh7372_usb_dmae_channels,
        .channel_num    = ARRAY_SIZE(sh7372_usb_dmae_channels),
-       .ts_low_shift   = 6,
-       .ts_low_mask    = 0xc0,
-       .ts_high_shift  = 0,
-       .ts_high_mask   = 0,
-       .ts_shift       = usbts_shift,
-       .ts_shift_num   = ARRAY_SIZE(usbts_shift),
+       .ts_low_shift   = USBTS_LOW_SHIFT,
+       .ts_low_mask    = USBTS_LOW_BIT << USBTS_LOW_SHIFT,
+       .ts_high_shift  = USBTS_HI_SHIFT,
+       .ts_high_mask   = USBTS_HI_BIT << USBTS_HI_SHIFT,
+       .ts_shift       = dma_usbts_shift,
+       .ts_shift_num   = ARRAY_SIZE(dma_usbts_shift),
        .dmaor_init     = DMAOR_DME,
        .chcr_offset    = 0x14,
        .chcr_ie_bit    = 1 << 5,
@@ -739,10 +702,10 @@ static struct platform_device usb_dma0_device = {
 static const struct sh_dmae_slave_config sh7372_usb_dmae1_slaves[] = {
        {
                .slave_id       = SHDMA_SLAVE_USB1_TX,
-               .chcr           = USBTS_INDEX2VAL(XMIT_SZ_8BYTE),
+               .chcr           = USBTS_INDEX2VAL(USBTS_XMIT_SZ_8BYTE),
        }, {
                .slave_id       = SHDMA_SLAVE_USB1_RX,
-               .chcr           = USBTS_INDEX2VAL(XMIT_SZ_8BYTE),
+               .chcr           = USBTS_INDEX2VAL(USBTS_XMIT_SZ_8BYTE),
        },
 };
 
@@ -751,12 +714,12 @@ static struct sh_dmae_pdata usb_dma1_platform_data = {
        .slave_num      = ARRAY_SIZE(sh7372_usb_dmae1_slaves),
        .channel        = sh7372_usb_dmae_channels,
        .channel_num    = ARRAY_SIZE(sh7372_usb_dmae_channels),
-       .ts_low_shift   = 6,
-       .ts_low_mask    = 0xc0,
-       .ts_high_shift  = 0,
-       .ts_high_mask   = 0,
-       .ts_shift       = usbts_shift,
-       .ts_shift_num   = ARRAY_SIZE(usbts_shift),
+       .ts_low_shift   = USBTS_LOW_SHIFT,
+       .ts_low_mask    = USBTS_LOW_BIT << USBTS_LOW_SHIFT,
+       .ts_high_shift  = USBTS_HI_SHIFT,
+       .ts_high_mask   = USBTS_HI_BIT << USBTS_HI_SHIFT,
+       .ts_shift       = dma_usbts_shift,
+       .ts_shift_num   = ARRAY_SIZE(dma_usbts_shift),
        .dmaor_init     = DMAOR_DME,
        .chcr_offset    = 0x14,
        .chcr_ie_bit    = 1 << 5,
@@ -1038,21 +1001,21 @@ static struct platform_device *sh7372_late_devices[] __initdata = {
 
 void __init sh7372_add_standard_devices(void)
 {
-       sh7372_init_pm_domain(&sh7372_a4lc);
-       sh7372_init_pm_domain(&sh7372_a4mp);
-       sh7372_init_pm_domain(&sh7372_d4);
-       sh7372_init_pm_domain(&sh7372_a4r);
-       sh7372_init_pm_domain(&sh7372_a3rv);
-       sh7372_init_pm_domain(&sh7372_a3ri);
-       sh7372_init_pm_domain(&sh7372_a4s);
-       sh7372_init_pm_domain(&sh7372_a3sp);
-       sh7372_init_pm_domain(&sh7372_a3sg);
-
-       sh7372_pm_add_subdomain(&sh7372_a4lc, &sh7372_a3rv);
-       sh7372_pm_add_subdomain(&sh7372_a4r, &sh7372_a4lc);
-
-       sh7372_pm_add_subdomain(&sh7372_a4s, &sh7372_a3sg);
-       sh7372_pm_add_subdomain(&sh7372_a4s, &sh7372_a3sp);
+       rmobile_init_pm_domain(&sh7372_pd_a4lc);
+       rmobile_init_pm_domain(&sh7372_pd_a4mp);
+       rmobile_init_pm_domain(&sh7372_pd_d4);
+       rmobile_init_pm_domain(&sh7372_pd_a4r);
+       rmobile_init_pm_domain(&sh7372_pd_a3rv);
+       rmobile_init_pm_domain(&sh7372_pd_a3ri);
+       rmobile_init_pm_domain(&sh7372_pd_a4s);
+       rmobile_init_pm_domain(&sh7372_pd_a3sp);
+       rmobile_init_pm_domain(&sh7372_pd_a3sg);
+
+       rmobile_pm_add_subdomain(&sh7372_pd_a4lc, &sh7372_pd_a3rv);
+       rmobile_pm_add_subdomain(&sh7372_pd_a4r, &sh7372_pd_a4lc);
+
+       rmobile_pm_add_subdomain(&sh7372_pd_a4s, &sh7372_pd_a3sg);
+       rmobile_pm_add_subdomain(&sh7372_pd_a4s, &sh7372_pd_a3sp);
 
        platform_add_devices(sh7372_early_devices,
                            ARRAY_SIZE(sh7372_early_devices));
@@ -1060,30 +1023,30 @@ void __init sh7372_add_standard_devices(void)
        platform_add_devices(sh7372_late_devices,
                            ARRAY_SIZE(sh7372_late_devices));
 
-       sh7372_add_device_to_domain(&sh7372_a3rv, &vpu_device);
-       sh7372_add_device_to_domain(&sh7372_a4mp, &spu0_device);
-       sh7372_add_device_to_domain(&sh7372_a4mp, &spu1_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &scif0_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &scif1_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &scif2_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &scif3_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &scif4_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &scif5_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &scif6_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &iic1_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &dma0_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &dma1_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &dma2_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &usb_dma0_device);
-       sh7372_add_device_to_domain(&sh7372_a3sp, &usb_dma1_device);
-       sh7372_add_device_to_domain(&sh7372_a4r, &iic0_device);
-       sh7372_add_device_to_domain(&sh7372_a4r, &veu0_device);
-       sh7372_add_device_to_domain(&sh7372_a4r, &veu1_device);
-       sh7372_add_device_to_domain(&sh7372_a4r, &veu2_device);
-       sh7372_add_device_to_domain(&sh7372_a4r, &veu3_device);
-       sh7372_add_device_to_domain(&sh7372_a4r, &jpu_device);
-       sh7372_add_device_to_domain(&sh7372_a4r, &tmu00_device);
-       sh7372_add_device_to_domain(&sh7372_a4r, &tmu01_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3rv, &vpu_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4mp, &spu0_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4mp, &spu1_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &scif0_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &scif1_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &scif2_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &scif3_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &scif4_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &scif5_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &scif6_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &iic1_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &dma0_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &dma1_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &dma2_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &usb_dma0_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a3sp, &usb_dma1_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4r, &iic0_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4r, &veu0_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4r, &veu1_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4r, &veu2_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4r, &veu3_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4r, &jpu_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4r, &tmu00_device);
+       rmobile_add_device_to_domain(&sh7372_pd_a4r, &tmu01_device);
 }
 
 static void __init sh7372_earlytimer_init(void)
index d576a6abbade8338a33eb8e39d290e29d57e302d..855b1506caf88b209187ed88d8808968e5f6bf40 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/platform_device.h>
+#include <linux/of_platform.h>
 #include <linux/uio_driver.h>
 #include <linux/delay.h>
 #include <linux/input.h>
@@ -500,3 +501,49 @@ void __init sh7377_add_early_devices(void)
        /* override timer setup with soc-specific code */
        shmobile_timer.init = sh7377_earlytimer_init;
 }
+
+#ifdef CONFIG_USE_OF
+
+void __init sh7377_add_early_devices_dt(void)
+{
+       shmobile_setup_delay(600, 1, 3); /* Cortex-A8 @ 600MHz */
+
+       early_platform_add_devices(sh7377_early_devices,
+                                  ARRAY_SIZE(sh7377_early_devices));
+
+       /* setup early console here as well */
+       shmobile_setup_console();
+}
+
+static const struct of_dev_auxdata sh7377_auxdata_lookup[] __initconst = {
+       { }
+};
+
+void __init sh7377_add_standard_devices_dt(void)
+{
+       /* clocks are setup late during boot in the case of DT */
+       sh7377_clock_init();
+
+       platform_add_devices(sh7377_early_devices,
+                           ARRAY_SIZE(sh7377_early_devices));
+
+       of_platform_populate(NULL, of_default_bus_match_table,
+                            sh7377_auxdata_lookup, NULL);
+}
+
+static const char *sh7377_boards_compat_dt[] __initdata = {
+       "renesas,sh7377",
+       NULL,
+};
+
+DT_MACHINE_START(SH7377_DT, "Generic SH7377 (Flattened Device Tree)")
+       .map_io         = sh7377_map_io,
+       .init_early     = sh7377_add_early_devices_dt,
+       .init_irq       = sh7377_init_irq,
+       .handle_irq     = shmobile_handle_irq_intc,
+       .init_machine   = sh7377_add_standard_devices_dt,
+       .timer          = &shmobile_timer,
+       .dt_compat      = sh7377_boards_compat_dt,
+MACHINE_END
+
+#endif /* CONFIG_USE_OF */
index 04a0dfe754934c32c17d1d7a565201504bc520c1..d230af656fc9c57418906e7ce3af785b7117ffc1 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/sh_dma.h>
 #include <linux/sh_intc.h>
 #include <linux/sh_timer.h>
+#include <mach/dma-register.h>
 #include <mach/hardware.h>
 #include <mach/irqs.h>
 #include <mach/sh73a0.h>
@@ -415,32 +416,6 @@ static struct platform_device i2c4_device = {
        .num_resources  = ARRAY_SIZE(i2c4_resources),
 };
 
-/* Transmit sizes and respective CHCR register values */
-enum {
-       XMIT_SZ_8BIT            = 0,
-       XMIT_SZ_16BIT           = 1,
-       XMIT_SZ_32BIT           = 2,
-       XMIT_SZ_64BIT           = 7,
-       XMIT_SZ_128BIT          = 3,
-       XMIT_SZ_256BIT          = 4,
-       XMIT_SZ_512BIT          = 5,
-};
-
-/* log2(size / 8) - used to calculate number of transfers */
-#define TS_SHIFT {                     \
-       [XMIT_SZ_8BIT]          = 0,    \
-       [XMIT_SZ_16BIT]         = 1,    \
-       [XMIT_SZ_32BIT]         = 2,    \
-       [XMIT_SZ_64BIT]         = 3,    \
-       [XMIT_SZ_128BIT]        = 4,    \
-       [XMIT_SZ_256BIT]        = 5,    \
-       [XMIT_SZ_512BIT]        = 6,    \
-}
-
-#define TS_INDEX2VAL(i) ((((i) & 3) << 3) | (((i) & 0xc) << (20 - 2)))
-#define CHCR_TX(xmit_sz) (DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL((xmit_sz)))
-#define CHCR_RX(xmit_sz) (DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL((xmit_sz)))
-
 static const struct sh_dmae_slave_config sh73a0_dmae_slaves[] = {
        {
                .slave_id       = SHDMA_SLAVE_SCIF0_TX,
@@ -604,19 +579,17 @@ static const struct sh_dmae_channel sh73a0_dmae_channels[] = {
        DMAE_CHANNEL(0x8980),
 };
 
-static const unsigned int ts_shift[] = TS_SHIFT;
-
 static struct sh_dmae_pdata sh73a0_dmae_platform_data = {
        .slave          = sh73a0_dmae_slaves,
        .slave_num      = ARRAY_SIZE(sh73a0_dmae_slaves),
        .channel        = sh73a0_dmae_channels,
        .channel_num    = ARRAY_SIZE(sh73a0_dmae_channels),
-       .ts_low_shift   = 3,
-       .ts_low_mask    = 0x18,
-       .ts_high_shift  = (20 - 2),     /* 2 bits for shifted low TS */
-       .ts_high_mask   = 0x00300000,
-       .ts_shift       = ts_shift,
-       .ts_shift_num   = ARRAY_SIZE(ts_shift),
+       .ts_low_shift   = TS_LOW_SHIFT,
+       .ts_low_mask    = TS_LOW_BIT << TS_LOW_SHIFT,
+       .ts_high_shift  = TS_HI_SHIFT,
+       .ts_high_mask   = TS_HI_BIT << TS_HI_SHIFT,
+       .ts_shift       = dma_ts_shift,
+       .ts_shift_num   = ARRAY_SIZE(dma_ts_shift),
        .dmaor_init     = DMAOR_DME,
 };
 
@@ -651,6 +624,116 @@ static struct platform_device dma0_device = {
        },
 };
 
+/* MPDMAC */
+static const struct sh_dmae_slave_config sh73a0_mpdma_slaves[] = {
+       {
+               .slave_id       = SHDMA_SLAVE_FSI2A_RX,
+               .addr           = 0xec230020,
+               .chcr           = CHCR_RX(XMIT_SZ_32BIT),
+               .mid_rid        = 0xd6, /* CHECK ME */
+       }, {
+               .slave_id       = SHDMA_SLAVE_FSI2A_TX,
+               .addr           = 0xec230024,
+               .chcr           = CHCR_TX(XMIT_SZ_32BIT),
+               .mid_rid        = 0xd5, /* CHECK ME */
+       }, {
+               .slave_id       = SHDMA_SLAVE_FSI2C_RX,
+               .addr           = 0xec230060,
+               .chcr           = CHCR_RX(XMIT_SZ_32BIT),
+               .mid_rid        = 0xda, /* CHECK ME */
+       }, {
+               .slave_id       = SHDMA_SLAVE_FSI2C_TX,
+               .addr           = 0xec230064,
+               .chcr           = CHCR_TX(XMIT_SZ_32BIT),
+               .mid_rid        = 0xd9, /* CHECK ME */
+       }, {
+               .slave_id       = SHDMA_SLAVE_FSI2B_RX,
+               .addr           = 0xec240020,
+               .chcr           = CHCR_RX(XMIT_SZ_32BIT),
+               .mid_rid        = 0x8e, /* CHECK ME */
+       }, {
+               .slave_id       = SHDMA_SLAVE_FSI2B_TX,
+               .addr           = 0xec240024,
+               .chcr           = CHCR_RX(XMIT_SZ_32BIT),
+               .mid_rid        = 0x8d, /* CHECK ME */
+       }, {
+               .slave_id       = SHDMA_SLAVE_FSI2D_RX,
+               .addr           =  0xec240060,
+               .chcr           = CHCR_RX(XMIT_SZ_32BIT),
+               .mid_rid        = 0x9a, /* CHECK ME */
+       },
+};
+
+#define MPDMA_CHANNEL(a, b, c)                 \
+{                                              \
+       .offset         = a,                    \
+       .dmars          = b,                    \
+       .dmars_bit      = c,                    \
+       .chclr_offset   = (0x220 - 0x20) + a    \
+}
+
+static const struct sh_dmae_channel sh73a0_mpdma_channels[] = {
+       MPDMA_CHANNEL(0x00, 0, 0),
+       MPDMA_CHANNEL(0x10, 0, 8),
+       MPDMA_CHANNEL(0x20, 4, 0),
+       MPDMA_CHANNEL(0x30, 4, 8),
+       MPDMA_CHANNEL(0x50, 8, 0),
+       MPDMA_CHANNEL(0x70, 8, 8),
+};
+
+static struct sh_dmae_pdata sh73a0_mpdma_platform_data = {
+       .slave          = sh73a0_mpdma_slaves,
+       .slave_num      = ARRAY_SIZE(sh73a0_mpdma_slaves),
+       .channel        = sh73a0_mpdma_channels,
+       .channel_num    = ARRAY_SIZE(sh73a0_mpdma_channels),
+       .ts_low_shift   = TS_LOW_SHIFT,
+       .ts_low_mask    = TS_LOW_BIT << TS_LOW_SHIFT,
+       .ts_high_shift  = TS_HI_SHIFT,
+       .ts_high_mask   = TS_HI_BIT << TS_HI_SHIFT,
+       .ts_shift       = dma_ts_shift,
+       .ts_shift_num   = ARRAY_SIZE(dma_ts_shift),
+       .dmaor_init     = DMAOR_DME,
+       .chclr_present  = 1,
+};
+
+/* Resource order important! */
+static struct resource sh73a0_mpdma_resources[] = {
+       {
+               /* Channel registers and DMAOR */
+               .start  = 0xec618020,
+               .end    = 0xec61828f,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               /* DMARSx */
+               .start  = 0xec619000,
+               .end    = 0xec61900b,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               .name   = "error_irq",
+               .start  = gic_spi(181),
+               .end    = gic_spi(181),
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               /* IRQ for channels 0-5 */
+               .start  = gic_spi(175),
+               .end    = gic_spi(180),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device mpdma0_device = {
+       .name           = "sh-dma-engine",
+       .id             = 1,
+       .resource       = sh73a0_mpdma_resources,
+       .num_resources  = ARRAY_SIZE(sh73a0_mpdma_resources),
+       .dev            = {
+               .platform_data  = &sh73a0_mpdma_platform_data,
+       },
+};
+
 static struct platform_device *sh73a0_early_devices[] __initdata = {
        &scif0_device,
        &scif1_device,
@@ -673,6 +756,7 @@ static struct platform_device *sh73a0_late_devices[] __initdata = {
        &i2c3_device,
        &i2c4_device,
        &dma0_device,
+       &mpdma0_device,
 };
 
 #define SRCR2          0xe61580b0
index 0f882ecb7d810f4a98700fa6f46c913eec0860c9..6ec300549960196d83ab1542e8715c200f83e50b 100644 (file)
@@ -120,182 +120,156 @@ struct pl08x_channel_data spear300_dma_info[] = {
                .min_signal = 2,
                .max_signal = 2,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart0_tx",
                .min_signal = 3,
                .max_signal = 3,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ssp0_rx",
                .min_signal = 8,
                .max_signal = 8,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ssp0_tx",
                .min_signal = 9,
                .max_signal = 9,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "i2c_rx",
                .min_signal = 10,
                .max_signal = 10,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "i2c_tx",
                .min_signal = 11,
                .max_signal = 11,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "irda",
                .min_signal = 12,
                .max_signal = 12,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "adc",
                .min_signal = 13,
                .max_signal = 13,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "to_jpeg",
                .min_signal = 14,
                .max_signal = 14,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "from_jpeg",
                .min_signal = 15,
                .max_signal = 15,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras0_rx",
                .min_signal = 0,
                .max_signal = 0,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras0_tx",
                .min_signal = 1,
                .max_signal = 1,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras1_rx",
                .min_signal = 2,
                .max_signal = 2,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras1_tx",
                .min_signal = 3,
                .max_signal = 3,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras2_rx",
                .min_signal = 4,
                .max_signal = 4,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras2_tx",
                .min_signal = 5,
                .max_signal = 5,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras3_rx",
                .min_signal = 6,
                .max_signal = 6,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras3_tx",
                .min_signal = 7,
                .max_signal = 7,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras4_rx",
                .min_signal = 8,
                .max_signal = 8,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras4_tx",
                .min_signal = 9,
                .max_signal = 9,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras5_rx",
                .min_signal = 10,
                .max_signal = 10,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras5_tx",
                .min_signal = 11,
                .max_signal = 11,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras6_rx",
                .min_signal = 12,
                .max_signal = 12,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras6_tx",
                .min_signal = 13,
                .max_signal = 13,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras7_rx",
                .min_signal = 14,
                .max_signal = 14,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras7_tx",
                .min_signal = 15,
                .max_signal = 15,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        },
 };
index bbcf4571d361caefd90c2f685365a5a394fb56c5..1d0e435b904525e1d2070bfbc6f68e3ba91eea83 100644 (file)
@@ -205,182 +205,156 @@ struct pl08x_channel_data spear310_dma_info[] = {
                .min_signal = 2,
                .max_signal = 2,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart0_tx",
                .min_signal = 3,
                .max_signal = 3,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ssp0_rx",
                .min_signal = 8,
                .max_signal = 8,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ssp0_tx",
                .min_signal = 9,
                .max_signal = 9,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "i2c_rx",
                .min_signal = 10,
                .max_signal = 10,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "i2c_tx",
                .min_signal = 11,
                .max_signal = 11,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "irda",
                .min_signal = 12,
                .max_signal = 12,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "adc",
                .min_signal = 13,
                .max_signal = 13,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "to_jpeg",
                .min_signal = 14,
                .max_signal = 14,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "from_jpeg",
                .min_signal = 15,
                .max_signal = 15,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart1_rx",
                .min_signal = 0,
                .max_signal = 0,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart1_tx",
                .min_signal = 1,
                .max_signal = 1,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart2_rx",
                .min_signal = 2,
                .max_signal = 2,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart2_tx",
                .min_signal = 3,
                .max_signal = 3,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart3_rx",
                .min_signal = 4,
                .max_signal = 4,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart3_tx",
                .min_signal = 5,
                .max_signal = 5,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart4_rx",
                .min_signal = 6,
                .max_signal = 6,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart4_tx",
                .min_signal = 7,
                .max_signal = 7,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart5_rx",
                .min_signal = 8,
                .max_signal = 8,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart5_tx",
                .min_signal = 9,
                .max_signal = 9,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras5_rx",
                .min_signal = 10,
                .max_signal = 10,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras5_tx",
                .min_signal = 11,
                .max_signal = 11,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras6_rx",
                .min_signal = 12,
                .max_signal = 12,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras6_tx",
                .min_signal = 13,
                .max_signal = 13,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras7_rx",
                .min_signal = 14,
                .max_signal = 14,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras7_tx",
                .min_signal = 15,
                .max_signal = 15,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        },
 };
index 88d483bcd66a37665dc130bd5f0da016192a950a..fd823c624575f12991e99eafdaeb2a3a6e8a4e11 100644 (file)
@@ -213,182 +213,156 @@ struct pl08x_channel_data spear320_dma_info[] = {
                .min_signal = 2,
                .max_signal = 2,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart0_tx",
                .min_signal = 3,
                .max_signal = 3,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ssp0_rx",
                .min_signal = 8,
                .max_signal = 8,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ssp0_tx",
                .min_signal = 9,
                .max_signal = 9,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "i2c0_rx",
                .min_signal = 10,
                .max_signal = 10,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "i2c0_tx",
                .min_signal = 11,
                .max_signal = 11,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "irda",
                .min_signal = 12,
                .max_signal = 12,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "adc",
                .min_signal = 13,
                .max_signal = 13,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "to_jpeg",
                .min_signal = 14,
                .max_signal = 14,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "from_jpeg",
                .min_signal = 15,
                .max_signal = 15,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ssp1_rx",
                .min_signal = 0,
                .max_signal = 0,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ssp1_tx",
                .min_signal = 1,
                .max_signal = 1,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ssp2_rx",
                .min_signal = 2,
                .max_signal = 2,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ssp2_tx",
                .min_signal = 3,
                .max_signal = 3,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "uart1_rx",
                .min_signal = 4,
                .max_signal = 4,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "uart1_tx",
                .min_signal = 5,
                .max_signal = 5,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "uart2_rx",
                .min_signal = 6,
                .max_signal = 6,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "uart2_tx",
                .min_signal = 7,
                .max_signal = 7,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "i2c1_rx",
                .min_signal = 8,
                .max_signal = 8,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "i2c1_tx",
                .min_signal = 9,
                .max_signal = 9,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "i2c2_rx",
                .min_signal = 10,
                .max_signal = 10,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "i2c2_tx",
                .min_signal = 11,
                .max_signal = 11,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "i2s_rx",
                .min_signal = 12,
                .max_signal = 12,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "i2s_tx",
                .min_signal = 13,
                .max_signal = 13,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "rs485_rx",
                .min_signal = 14,
                .max_signal = 14,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "rs485_tx",
                .min_signal = 15,
                .max_signal = 15,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        },
 };
index 66db5f13af844360b1b5845d1b06c13a6d51445a..98144baf88838243d8a45c5bded0f606bd7d99d7 100644 (file)
@@ -46,7 +46,8 @@ struct pl022_ssp_controller pl022_plat_data = {
 struct pl08x_platform_data pl080_plat_data = {
        .memcpy_channel = {
                .bus_id = "memcpy",
-               .cctl = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \
+               .cctl_memcpy =
+                       (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \
                        PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT | \
                        PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT | \
                        PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT | \
index 9af67d003c62ce6f167307020fcd2f52fa181e8a..5a5a52db252bf79d6d776c4a2cbdbbf330ba9e43 100644 (file)
@@ -36,336 +36,288 @@ static struct pl08x_channel_data spear600_dma_info[] = {
                .min_signal = 0,
                .max_signal = 0,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ssp1_tx",
                .min_signal = 1,
                .max_signal = 1,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart0_rx",
                .min_signal = 2,
                .max_signal = 2,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart0_tx",
                .min_signal = 3,
                .max_signal = 3,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart1_rx",
                .min_signal = 4,
                .max_signal = 4,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "uart1_tx",
                .min_signal = 5,
                .max_signal = 5,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ssp2_rx",
                .min_signal = 6,
                .max_signal = 6,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ssp2_tx",
                .min_signal = 7,
                .max_signal = 7,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ssp0_rx",
                .min_signal = 8,
                .max_signal = 8,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ssp0_tx",
                .min_signal = 9,
                .max_signal = 9,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "i2c_rx",
                .min_signal = 10,
                .max_signal = 10,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "i2c_tx",
                .min_signal = 11,
                .max_signal = 11,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "irda",
                .min_signal = 12,
                .max_signal = 12,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "adc",
                .min_signal = 13,
                .max_signal = 13,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "to_jpeg",
                .min_signal = 14,
                .max_signal = 14,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "from_jpeg",
                .min_signal = 15,
                .max_signal = 15,
                .muxval = 0,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras0_rx",
                .min_signal = 0,
                .max_signal = 0,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras0_tx",
                .min_signal = 1,
                .max_signal = 1,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras1_rx",
                .min_signal = 2,
                .max_signal = 2,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras1_tx",
                .min_signal = 3,
                .max_signal = 3,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras2_rx",
                .min_signal = 4,
                .max_signal = 4,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras2_tx",
                .min_signal = 5,
                .max_signal = 5,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras3_rx",
                .min_signal = 6,
                .max_signal = 6,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras3_tx",
                .min_signal = 7,
                .max_signal = 7,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras4_rx",
                .min_signal = 8,
                .max_signal = 8,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras4_tx",
                .min_signal = 9,
                .max_signal = 9,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras5_rx",
                .min_signal = 10,
                .max_signal = 10,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras5_tx",
                .min_signal = 11,
                .max_signal = 11,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras6_rx",
                .min_signal = 12,
                .max_signal = 12,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras6_tx",
                .min_signal = 13,
                .max_signal = 13,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras7_rx",
                .min_signal = 14,
                .max_signal = 14,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ras7_tx",
                .min_signal = 15,
                .max_signal = 15,
                .muxval = 1,
-               .cctl = 0,
                .periph_buses = PL08X_AHB1,
        }, {
                .bus_id = "ext0_rx",
                .min_signal = 0,
                .max_signal = 0,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext0_tx",
                .min_signal = 1,
                .max_signal = 1,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext1_rx",
                .min_signal = 2,
                .max_signal = 2,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext1_tx",
                .min_signal = 3,
                .max_signal = 3,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext2_rx",
                .min_signal = 4,
                .max_signal = 4,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext2_tx",
                .min_signal = 5,
                .max_signal = 5,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext3_rx",
                .min_signal = 6,
                .max_signal = 6,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext3_tx",
                .min_signal = 7,
                .max_signal = 7,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext4_rx",
                .min_signal = 8,
                .max_signal = 8,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext4_tx",
                .min_signal = 9,
                .max_signal = 9,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext5_rx",
                .min_signal = 10,
                .max_signal = 10,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext5_tx",
                .min_signal = 11,
                .max_signal = 11,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext6_rx",
                .min_signal = 12,
                .max_signal = 12,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext6_tx",
                .min_signal = 13,
                .max_signal = 13,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext7_rx",
                .min_signal = 14,
                .max_signal = 14,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        }, {
                .bus_id = "ext7_tx",
                .min_signal = 15,
                .max_signal = 15,
                .muxval = 2,
-               .cctl = 0,
                .periph_buses = PL08X_AHB2,
        },
 };
@@ -373,7 +325,8 @@ static struct pl08x_channel_data spear600_dma_info[] = {
 struct pl08x_platform_data pl080_plat_data = {
        .memcpy_channel = {
                .bus_id = "memcpy",
-               .cctl = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \
+               .cctl_memcpy =
+                       (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \
                        PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT | \
                        PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT | \
                        PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT | \
index d0de9c1192f78eae03b70a8c9d0c7b72e4bc4d0c..c0999633a9ab24054a2c5e2cc9d526c020320b8c 100644 (file)
@@ -64,7 +64,8 @@ struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {
                       &tegra_ehci2_pdata),
        OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB3_BASE, "tegra-ehci.2",
                       &tegra_ehci3_pdata),
-       OF_DEV_AUXDATA("nvidia,tegra20-apbdma", 0x6000a000, "tegra-apbdma", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra20-apbdma", TEGRA_APB_DMA_BASE, "tegra-apbdma", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra20-pwm", TEGRA_PWFM_BASE, "tegra-pwm", NULL),
        {}
 };
 
index ee48214bfd898ea86f2084dba182e85df96bbc58..53bf60f1158044bf6ce12a3d05972f02b17bdfb1 100644 (file)
@@ -33,6 +33,8 @@
 #include <asm/mach/arch.h>
 #include <asm/hardware/gic.h>
 
+#include <mach/iomap.h>
+
 #include "board.h"
 #include "clock.h"
 
@@ -48,6 +50,7 @@ struct of_dev_auxdata tegra30_auxdata_lookup[] __initdata = {
        OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000D000, "tegra-i2c.4", NULL),
        OF_DEV_AUXDATA("nvidia,tegra30-ahub", 0x70080000, "tegra30-ahub", NULL),
        OF_DEV_AUXDATA("nvidia,tegra30-apbdma", 0x6000a000, "tegra-apbdma", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra30-pwm", TEGRA_PWFM_BASE, "tegra-pwm", NULL),
        {}
 };
 
index a310222951da9f284c2e6bd2713792761a9b3e08..8674a890fd1c7071ae2efce55a3a7c888a97c091 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/i2c.h>
+#include <linux/platform_data/i2c-nomadik.h>
 #include <linux/gpio.h>
 #include <linux/amba/bus.h>
 #include <linux/amba/pl022.h>
@@ -40,7 +41,6 @@
 #include <asm/mach/arch.h>
 #include <asm/hardware/gic.h>
 
-#include <plat/i2c.h>
 #include <plat/ste_dma40.h>
 #include <plat/gpio-nomadik.h>
 
@@ -211,24 +211,6 @@ static struct ab8500_platform_data ab8500_platdata = {
        .codec          = &ab8500_codec_pdata,
 };
 
-static struct resource ab8500_resources[] = {
-       [0] = {
-               .start  = IRQ_DB8500_AB8500,
-               .end    = IRQ_DB8500_AB8500,
-               .flags  = IORESOURCE_IRQ
-       }
-};
-
-struct platform_device ab8500_device = {
-       .name = "ab8500-core",
-       .id = 0,
-       .dev = {
-               .platform_data = &ab8500_platdata,
-       },
-       .num_resources = 1,
-       .resource = ab8500_resources,
-};
-
 /*
  * TPS61052
  */
@@ -443,7 +425,6 @@ static struct hash_platform_data u8500_hash1_platform_data = {
 /* add any platform devices here - TODO */
 static struct platform_device *mop500_platform_devs[] __initdata = {
        &mop500_gpio_keys_device,
-       &ab8500_device,
 };
 
 #ifdef CONFIG_STE_DMA40
@@ -605,7 +586,6 @@ static struct platform_device *snowball_platform_devs[] __initdata = {
        &snowball_led_dev,
        &snowball_key_dev,
        &snowball_sbnet_dev,
-       &ab8500_device,
 };
 
 static void __init mop500_init_machine(void)
@@ -617,9 +597,8 @@ static void __init mop500_init_machine(void)
        mop500_gpio_keys[0].gpio = GPIO_PROX_SENSOR;
 
        mop500_pinmaps_init();
-       parent = u8500_init_devices();
+       parent = u8500_init_devices(&ab8500_platdata);
 
-       /* FIXME: parent of ab8500 should be prcmu */
        for (i = 0; i < ARRAY_SIZE(mop500_platform_devs); i++)
                mop500_platform_devs[i]->dev.parent = parent;
 
@@ -652,7 +631,7 @@ static void __init snowball_init_machine(void)
        int i;
 
        snowball_pinmaps_init();
-       parent = u8500_init_devices();
+       parent = u8500_init_devices(&ab8500_platdata);
 
        for (i = 0; i < ARRAY_SIZE(snowball_platform_devs); i++)
                snowball_platform_devs[i]->dev.parent = parent;
@@ -684,7 +663,7 @@ static void __init hrefv60_init_machine(void)
        mop500_gpio_keys[0].gpio = HREFV60_PROX_SENSE_GPIO;
 
        hrefv60_pinmaps_init();
-       parent = u8500_init_devices();
+       parent = u8500_init_devices(&ab8500_platdata);
 
        for (i = 0; i < ARRAY_SIZE(mop500_platform_devs); i++)
                mop500_platform_devs[i]->dev.parent = parent;
@@ -785,9 +764,6 @@ static const struct of_device_id u8500_local_bus_nodes[] = {
        /* only create devices below soc node */
        { .compatible = "stericsson,db8500", },
        { .compatible = "stericsson,db8500-prcmu", },
-       { .compatible = "stericsson,db8500-prcmu-regulator", },
-       { .compatible = "stericsson,ab8500", },
-       { .compatible = "stericsson,ab8500-regulator", },
        { .compatible = "simple-bus"},
        { },
 };
index c8dd94f606dc2d2db89ebf6a3273167d94773d3c..db3c52d56ca46ad54bddbcaaa2f48d7c64f025a6 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/irq.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
+#include <linux/mfd/abx500/ab8500.h>
 
 #include <asm/mach/map.h>
 #include <asm/pmu.h>
@@ -115,7 +116,7 @@ static irqreturn_t db8500_pmu_handler(int irq, void *dev, irq_handler_t handler)
        return ret;
 }
 
-static struct arm_pmu_platdata db8500_pmu_platdata = {
+struct arm_pmu_platdata db8500_pmu_platdata = {
        .handle_irq             = db8500_pmu_handler,
 };
 
@@ -206,7 +207,7 @@ static struct device * __init db8500_soc_device_init(void)
 /*
  * This function is called from the board init
  */
-struct device * __init u8500_init_devices(void)
+struct device * __init u8500_init_devices(struct ab8500_platform_data *ab8500)
 {
        struct device *parent;
        int i;
@@ -223,6 +224,8 @@ struct device * __init u8500_init_devices(void)
        for (i = 0; i < ARRAY_SIZE(platform_devs); i++)
                platform_devs[i]->dev.parent = parent;
 
+       db8500_prcmu_device.dev.platform_data = ab8500;
+
        platform_add_devices(platform_devs, ARRAY_SIZE(platform_devs));
 
        return parent;
index 6e47065602669a3a4c720417137f768763c13363..ecdd8386cffb8fc4a6f16cbc811eba854be41619 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/sys_soc.h>
 #include <linux/amba/bus.h>
-#include <plat/i2c.h>
+#include <linux/platform_data/i2c-nomadik.h>
 #include <mach/crypto-ux500.h>
 
 struct spi_master_cntlr;
@@ -56,27 +56,15 @@ dbx500_add_uart(struct device *parent, const char *name, resource_size_t base,
 
 struct nmk_i2c_controller;
 
-static inline struct platform_device *
+static inline struct amba_device *
 dbx500_add_i2c(struct device *parent, int id, resource_size_t base, int irq,
               struct nmk_i2c_controller *data)
 {
-       struct resource res[] = {
-               DEFINE_RES_MEM(base, SZ_4K),
-               DEFINE_RES_IRQ(irq),
-       };
+       /* Conjure a name similar to what the platform device used to have */
+       char name[16];
 
-       struct platform_device_info pdevinfo = {
-               .parent = parent,
-               .name = "nmk-i2c",
-               .id = id,
-               .res = res,
-               .num_res = ARRAY_SIZE(res),
-               .data = data,
-               .size_data = sizeof(*data),
-               .dma_mask = DMA_BIT_MASK(32),
-       };
-
-       return platform_device_register_full(&pdevinfo);
+       snprintf(name, sizeof(name), "nmk-i2c.%d", id);
+       return amba_apb_device_add(parent, name, base, SZ_4K, irq, 0, data, 0);
 }
 
 static inline struct amba_device *
index 8b7ed82a286665961f04d548a0ab1137027fc7d8..7914e5eaa9c775a913d494adc05f8094103a5dee 100644 (file)
 
 #include <asm/mach/time.h>
 #include <linux/init.h>
+#include <linux/mfd/abx500/ab8500.h>
 
 void __init ux500_map_io(void);
 extern void __init u8500_map_io(void);
 
-extern struct device * __init u8500_init_devices(void);
+extern struct device * __init u8500_init_devices(struct ab8500_platform_data *ab8500);
 
 extern void __init ux500_init_irq(void);
 extern void __init ux500_init_late(void);
index 54e69973f39b7344ca0e7ccdb01b2a1be696c907..7ce51767c99c3843091a86fc06b1660b84fd9f3a 100644 (file)
@@ -5,5 +5,3 @@ obj-$(CONFIG_VTWM_VERSION_WM8505) += devices-wm8505.o
 
 obj-$(CONFIG_MACH_BV07) += bv07.o
 obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
-
-obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
deleted file mode 100644 (file)
index 8ad825e..0000000
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * arch/arm/mach-vt8500/pwm.c
- *
- *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/pwm.h>
-#include <linux/delay.h>
-
-#include <asm/div64.h>
-
-#define VT8500_NR_PWMS 4
-
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
-
-struct pwm_device {
-       struct list_head        node;
-       struct platform_device  *pdev;
-
-       const char      *label;
-
-       void __iomem    *regbase;
-
-       unsigned int    use_count;
-       unsigned int    pwm_id;
-};
-
-#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
-static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
-{
-       int loops = msecs_to_loops(10);
-       while ((readb(reg) & bitmask) && --loops)
-               cpu_relax();
-
-       if (unlikely(!loops))
-               pr_warning("Waiting for status bits 0x%x to clear timed out\n",
-                          bitmask);
-}
-
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
-{
-       unsigned long long c;
-       unsigned long period_cycles, prescale, pv, dc;
-
-       if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
-               return -EINVAL;
-
-       c = 25000000/2; /* wild guess --- need to implement clocks */
-       c = c * period_ns;
-       do_div(c, 1000000000);
-       period_cycles = c;
-
-       if (period_cycles < 1)
-               period_cycles = 1;
-       prescale = (period_cycles - 1) / 4096;
-       pv = period_cycles / (prescale + 1) - 1;
-       if (pv > 4095)
-               pv = 4095;
-
-       if (prescale > 1023)
-               return -EINVAL;
-
-       c = (unsigned long long)pv * duty_ns;
-       do_div(c, period_ns);
-       dc = c;
-
-       pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
-       writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
-
-       pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
-       writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
-
-       pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
-       writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
-
-       return 0;
-}
-EXPORT_SYMBOL(pwm_config);
-
-int pwm_enable(struct pwm_device *pwm)
-{
-       pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
-       writel(5, pwm->regbase + (pwm->pwm_id << 4));
-       return 0;
-}
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
-{
-       pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
-       writel(0, pwm->regbase + (pwm->pwm_id << 4));
-}
-EXPORT_SYMBOL(pwm_disable);
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-       struct pwm_device *pwm;
-       int found = 0;
-
-       mutex_lock(&pwm_lock);
-
-       list_for_each_entry(pwm, &pwm_list, node) {
-               if (pwm->pwm_id == pwm_id) {
-                       found = 1;
-                       break;
-               }
-       }
-
-       if (found) {
-               if (pwm->use_count == 0) {
-                       pwm->use_count++;
-                       pwm->label = label;
-               } else {
-                       pwm = ERR_PTR(-EBUSY);
-               }
-       } else {
-               pwm = ERR_PTR(-ENOENT);
-       }
-
-       mutex_unlock(&pwm_lock);
-       return pwm;
-}
-EXPORT_SYMBOL(pwm_request);
-
-void pwm_free(struct pwm_device *pwm)
-{
-       mutex_lock(&pwm_lock);
-
-       if (pwm->use_count) {
-               pwm->use_count--;
-               pwm->label = NULL;
-       } else {
-               pr_warning("PWM device already freed\n");
-       }
-
-       mutex_unlock(&pwm_lock);
-}
-EXPORT_SYMBOL(pwm_free);
-
-static inline void __add_pwm(struct pwm_device *pwm)
-{
-       mutex_lock(&pwm_lock);
-       list_add_tail(&pwm->node, &pwm_list);
-       mutex_unlock(&pwm_lock);
-}
-
-static int __devinit pwm_probe(struct platform_device *pdev)
-{
-       struct pwm_device *pwms;
-       struct resource *r;
-       int ret = 0;
-       int i;
-
-       pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
-       if (pwms == NULL) {
-               dev_err(&pdev->dev, "failed to allocate memory\n");
-               return -ENOMEM;
-       }
-
-       for (i = 0; i < VT8500_NR_PWMS; i++) {
-               pwms[i].use_count = 0;
-               pwms[i].pwm_id = i;
-               pwms[i].pdev = pdev;
-       }
-
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (r == NULL) {
-               dev_err(&pdev->dev, "no memory resource defined\n");
-               ret = -ENODEV;
-               goto err_free;
-       }
-
-       r = request_mem_region(r->start, resource_size(r), pdev->name);
-       if (r == NULL) {
-               dev_err(&pdev->dev, "failed to request memory resource\n");
-               ret = -EBUSY;
-               goto err_free;
-       }
-
-       pwms[0].regbase = ioremap(r->start, resource_size(r));
-       if (pwms[0].regbase == NULL) {
-               dev_err(&pdev->dev, "failed to ioremap() registers\n");
-               ret = -ENODEV;
-               goto err_free_mem;
-       }
-
-       for (i = 1; i < VT8500_NR_PWMS; i++)
-               pwms[i].regbase = pwms[0].regbase;
-
-       for (i = 0; i < VT8500_NR_PWMS; i++)
-               __add_pwm(&pwms[i]);
-
-       platform_set_drvdata(pdev, pwms);
-       return 0;
-
-err_free_mem:
-       release_mem_region(r->start, resource_size(r));
-err_free:
-       kfree(pwms);
-       return ret;
-}
-
-static int __devexit pwm_remove(struct platform_device *pdev)
-{
-       struct pwm_device *pwms;
-       struct resource *r;
-       int i;
-
-       pwms = platform_get_drvdata(pdev);
-       if (pwms == NULL)
-               return -ENODEV;
-
-       mutex_lock(&pwm_lock);
-
-       for (i = 0; i < VT8500_NR_PWMS; i++)
-               list_del(&pwms[i].node);
-       mutex_unlock(&pwm_lock);
-
-       iounmap(pwms[0].regbase);
-
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(r->start, resource_size(r));
-
-       kfree(pwms);
-       return 0;
-}
-
-static struct platform_driver pwm_driver = {
-       .driver         = {
-               .name   = "vt8500-pwm",
-               .owner  = THIS_MODULE,
-       },
-       .probe          = pwm_probe,
-       .remove         = __devexit_p(pwm_remove),
-};
-
-static int __init pwm_init(void)
-{
-       return platform_driver_register(&pwm_driver);
-}
-arch_initcall(pwm_init);
-
-static void __exit pwm_exit(void)
-{
-       platform_driver_unregister(&pwm_driver);
-}
-module_exit(pwm_exit);
-
-MODULE_LICENSE("GPL");
index 5cfc989940761f0a8aa317b3bc37270d40835178..c2cdf6500f75dc5a1ab5a688ea305099e8d30057 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/memblock.h>
 #include <linux/slab.h>
 #include <linux/iommu.h>
+#include <linux/io.h>
 #include <linux/vmalloc.h>
 #include <linux/sizes.h>
 
@@ -72,7 +73,7 @@ static dma_addr_t arm_dma_map_page(struct device *dev, struct page *page,
             unsigned long offset, size_t size, enum dma_data_direction dir,
             struct dma_attrs *attrs)
 {
-       if (!arch_is_coherent())
+       if (!arch_is_coherent() && !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
                __dma_page_cpu_to_dev(page, offset, size, dir);
        return pfn_to_dma(dev, page_to_pfn(page)) + offset;
 }
@@ -95,7 +96,7 @@ static void arm_dma_unmap_page(struct device *dev, dma_addr_t handle,
                size_t size, enum dma_data_direction dir,
                struct dma_attrs *attrs)
 {
-       if (!arch_is_coherent())
+       if (!arch_is_coherent() && !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
                __dma_page_dev_to_cpu(pfn_to_page(dma_to_pfn(dev, handle)),
                                      handle & ~PAGE_MASK, size, dir);
 }
@@ -124,6 +125,7 @@ struct dma_map_ops arm_dma_ops = {
        .alloc                  = arm_dma_alloc,
        .free                   = arm_dma_free,
        .mmap                   = arm_dma_mmap,
+       .get_sgtable            = arm_dma_get_sgtable,
        .map_page               = arm_dma_map_page,
        .unmap_page             = arm_dma_unmap_page,
        .map_sg                 = arm_dma_map_sg,
@@ -217,115 +219,70 @@ static void __dma_free_buffer(struct page *page, size_t size)
 }
 
 #ifdef CONFIG_MMU
+#ifdef CONFIG_HUGETLB_PAGE
+#error ARM Coherent DMA allocator does not (yet) support huge TLB
+#endif
 
-#define CONSISTENT_OFFSET(x)   (((unsigned long)(x) - consistent_base) >> PAGE_SHIFT)
-#define CONSISTENT_PTE_INDEX(x) (((unsigned long)(x) - consistent_base) >> PMD_SHIFT)
-
-/*
- * These are the page tables (2MB each) covering uncached, DMA consistent allocations
- */
-static pte_t **consistent_pte;
-
-#define DEFAULT_CONSISTENT_DMA_SIZE SZ_2M
+static void *__alloc_from_contiguous(struct device *dev, size_t size,
+                                    pgprot_t prot, struct page **ret_page);
 
-static unsigned long consistent_base = CONSISTENT_END - DEFAULT_CONSISTENT_DMA_SIZE;
+static void *__alloc_remap_buffer(struct device *dev, size_t size, gfp_t gfp,
+                                pgprot_t prot, struct page **ret_page,
+                                const void *caller);
 
-void __init init_consistent_dma_size(unsigned long size)
+static void *
+__dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot,
+       const void *caller)
 {
-       unsigned long base = CONSISTENT_END - ALIGN(size, SZ_2M);
+       struct vm_struct *area;
+       unsigned long addr;
 
-       BUG_ON(consistent_pte); /* Check we're called before DMA region init */
-       BUG_ON(base < VMALLOC_END);
+       /*
+        * DMA allocation can be mapped to user space, so lets
+        * set VM_USERMAP flags too.
+        */
+       area = get_vm_area_caller(size, VM_ARM_DMA_CONSISTENT | VM_USERMAP,
+                                 caller);
+       if (!area)
+               return NULL;
+       addr = (unsigned long)area->addr;
+       area->phys_addr = __pfn_to_phys(page_to_pfn(page));
 
-       /* Grow region to accommodate specified size  */
-       if (base < consistent_base)
-               consistent_base = base;
+       if (ioremap_page_range(addr, addr + size, area->phys_addr, prot)) {
+               vunmap((void *)addr);
+               return NULL;
+       }
+       return (void *)addr;
 }
 
-#include "vmregion.h"
-
-static struct arm_vmregion_head consistent_head = {
-       .vm_lock        = __SPIN_LOCK_UNLOCKED(&consistent_head.vm_lock),
-       .vm_list        = LIST_HEAD_INIT(consistent_head.vm_list),
-       .vm_end         = CONSISTENT_END,
-};
-
-#ifdef CONFIG_HUGETLB_PAGE
-#error ARM Coherent DMA allocator does not (yet) support huge TLB
-#endif
-
-/*
- * Initialise the consistent memory allocation.
- */
-static int __init consistent_init(void)
+static void __dma_free_remap(void *cpu_addr, size_t size)
 {
-       int ret = 0;
-       pgd_t *pgd;
-       pud_t *pud;
-       pmd_t *pmd;
-       pte_t *pte;
-       int i = 0;
-       unsigned long base = consistent_base;
-       unsigned long num_ptes = (CONSISTENT_END - base) >> PMD_SHIFT;
-
-       if (IS_ENABLED(CONFIG_CMA) && !IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU))
-               return 0;
-
-       consistent_pte = kmalloc(num_ptes * sizeof(pte_t), GFP_KERNEL);
-       if (!consistent_pte) {
-               pr_err("%s: no memory\n", __func__);
-               return -ENOMEM;
+       unsigned int flags = VM_ARM_DMA_CONSISTENT | VM_USERMAP;
+       struct vm_struct *area = find_vm_area(cpu_addr);
+       if (!area || (area->flags & flags) != flags) {
+               WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr);
+               return;
        }
-
-       pr_debug("DMA memory: 0x%08lx - 0x%08lx:\n", base, CONSISTENT_END);
-       consistent_head.vm_start = base;
-
-       do {
-               pgd = pgd_offset(&init_mm, base);
-
-               pud = pud_alloc(&init_mm, pgd, base);
-               if (!pud) {
-                       pr_err("%s: no pud tables\n", __func__);
-                       ret = -ENOMEM;
-                       break;
-               }
-
-               pmd = pmd_alloc(&init_mm, pud, base);
-               if (!pmd) {
-                       pr_err("%s: no pmd tables\n", __func__);
-                       ret = -ENOMEM;
-                       break;
-               }
-               WARN_ON(!pmd_none(*pmd));
-
-               pte = pte_alloc_kernel(pmd, base);
-               if (!pte) {
-                       pr_err("%s: no pte tables\n", __func__);
-                       ret = -ENOMEM;
-                       break;
-               }
-
-               consistent_pte[i++] = pte;
-               base += PMD_SIZE;
-       } while (base < CONSISTENT_END);
-
-       return ret;
+       unmap_kernel_range((unsigned long)cpu_addr, size);
+       vunmap(cpu_addr);
 }
-core_initcall(consistent_init);
 
-static void *__alloc_from_contiguous(struct device *dev, size_t size,
-                                    pgprot_t prot, struct page **ret_page);
-
-static struct arm_vmregion_head coherent_head = {
-       .vm_lock        = __SPIN_LOCK_UNLOCKED(&coherent_head.vm_lock),
-       .vm_list        = LIST_HEAD_INIT(coherent_head.vm_list),
+struct dma_pool {
+       size_t size;
+       spinlock_t lock;
+       unsigned long *bitmap;
+       unsigned long nr_pages;
+       void *vaddr;
+       struct page *page;
 };
 
-static size_t coherent_pool_size = DEFAULT_CONSISTENT_DMA_SIZE / 8;
+static struct dma_pool atomic_pool = {
+       .size = SZ_256K,
+};
 
 static int __init early_coherent_pool(char *p)
 {
-       coherent_pool_size = memparse(p, &p);
+       atomic_pool.size = memparse(p, &p);
        return 0;
 }
 early_param("coherent_pool", early_coherent_pool);
@@ -333,32 +290,45 @@ early_param("coherent_pool", early_coherent_pool);
 /*
  * Initialise the coherent pool for atomic allocations.
  */
-static int __init coherent_init(void)
+static int __init atomic_pool_init(void)
 {
+       struct dma_pool *pool = &atomic_pool;
        pgprot_t prot = pgprot_dmacoherent(pgprot_kernel);
-       size_t size = coherent_pool_size;
+       unsigned long nr_pages = pool->size >> PAGE_SHIFT;
+       unsigned long *bitmap;
        struct page *page;
        void *ptr;
+       int bitmap_size = BITS_TO_LONGS(nr_pages) * sizeof(long);
 
-       if (!IS_ENABLED(CONFIG_CMA))
-               return 0;
+       bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+       if (!bitmap)
+               goto no_bitmap;
 
-       ptr = __alloc_from_contiguous(NULL, size, prot, &page);
+       if (IS_ENABLED(CONFIG_CMA))
+               ptr = __alloc_from_contiguous(NULL, pool->size, prot, &page);
+       else
+               ptr = __alloc_remap_buffer(NULL, pool->size, GFP_KERNEL, prot,
+                                          &page, NULL);
        if (ptr) {
-               coherent_head.vm_start = (unsigned long) ptr;
-               coherent_head.vm_end = (unsigned long) ptr + size;
-               printk(KERN_INFO "DMA: preallocated %u KiB pool for atomic coherent allocations\n",
-                      (unsigned)size / 1024);
+               spin_lock_init(&pool->lock);
+               pool->vaddr = ptr;
+               pool->page = page;
+               pool->bitmap = bitmap;
+               pool->nr_pages = nr_pages;
+               pr_info("DMA: preallocated %u KiB pool for atomic coherent allocations\n",
+                      (unsigned)pool->size / 1024);
                return 0;
        }
-       printk(KERN_ERR "DMA: failed to allocate %u KiB pool for atomic coherent allocation\n",
-              (unsigned)size / 1024);
+       kfree(bitmap);
+no_bitmap:
+       pr_err("DMA: failed to allocate %u KiB pool for atomic coherent allocation\n",
+              (unsigned)pool->size / 1024);
        return -ENOMEM;
 }
 /*
  * CMA is activated by core_initcall, so we must be called after it.
  */
-postcore_initcall(coherent_init);
+postcore_initcall(atomic_pool_init);
 
 struct dma_contig_early_reserve {
        phys_addr_t base;
@@ -406,112 +376,6 @@ void __init dma_contiguous_remap(void)
        }
 }
 
-static void *
-__dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot,
-       const void *caller)
-{
-       struct arm_vmregion *c;
-       size_t align;
-       int bit;
-
-       if (!consistent_pte) {
-               pr_err("%s: not initialised\n", __func__);
-               dump_stack();
-               return NULL;
-       }
-
-       /*
-        * Align the virtual region allocation - maximum alignment is
-        * a section size, minimum is a page size.  This helps reduce
-        * fragmentation of the DMA space, and also prevents allocations
-        * smaller than a section from crossing a section boundary.
-        */
-       bit = fls(size - 1);
-       if (bit > SECTION_SHIFT)
-               bit = SECTION_SHIFT;
-       align = 1 << bit;
-
-       /*
-        * Allocate a virtual address in the consistent mapping region.
-        */
-       c = arm_vmregion_alloc(&consistent_head, align, size,
-                           gfp & ~(__GFP_DMA | __GFP_HIGHMEM), caller);
-       if (c) {
-               pte_t *pte;
-               int idx = CONSISTENT_PTE_INDEX(c->vm_start);
-               u32 off = CONSISTENT_OFFSET(c->vm_start) & (PTRS_PER_PTE-1);
-
-               pte = consistent_pte[idx] + off;
-               c->priv = page;
-
-               do {
-                       BUG_ON(!pte_none(*pte));
-
-                       set_pte_ext(pte, mk_pte(page, prot), 0);
-                       page++;
-                       pte++;
-                       off++;
-                       if (off >= PTRS_PER_PTE) {
-                               off = 0;
-                               pte = consistent_pte[++idx];
-                       }
-               } while (size -= PAGE_SIZE);
-
-               dsb();
-
-               return (void *)c->vm_start;
-       }
-       return NULL;
-}
-
-static void __dma_free_remap(void *cpu_addr, size_t size)
-{
-       struct arm_vmregion *c;
-       unsigned long addr;
-       pte_t *ptep;
-       int idx;
-       u32 off;
-
-       c = arm_vmregion_find_remove(&consistent_head, (unsigned long)cpu_addr);
-       if (!c) {
-               pr_err("%s: trying to free invalid coherent area: %p\n",
-                      __func__, cpu_addr);
-               dump_stack();
-               return;
-       }
-
-       if ((c->vm_end - c->vm_start) != size) {
-               pr_err("%s: freeing wrong coherent size (%ld != %d)\n",
-                      __func__, c->vm_end - c->vm_start, size);
-               dump_stack();
-               size = c->vm_end - c->vm_start;
-       }
-
-       idx = CONSISTENT_PTE_INDEX(c->vm_start);
-       off = CONSISTENT_OFFSET(c->vm_start) & (PTRS_PER_PTE-1);
-       ptep = consistent_pte[idx] + off;
-       addr = c->vm_start;
-       do {
-               pte_t pte = ptep_get_and_clear(&init_mm, addr, ptep);
-
-               ptep++;
-               addr += PAGE_SIZE;
-               off++;
-               if (off >= PTRS_PER_PTE) {
-                       off = 0;
-                       ptep = consistent_pte[++idx];
-               }
-
-               if (pte_none(pte) || !pte_present(pte))
-                       pr_crit("%s: bad page in kernel page table\n",
-                               __func__);
-       } while (size -= PAGE_SIZE);
-
-       flush_tlb_kernel_range(c->vm_start, c->vm_end);
-
-       arm_vmregion_free(&consistent_head, c);
-}
-
 static int __dma_update_pte(pte_t *pte, pgtable_t token, unsigned long addr,
                            void *data)
 {
@@ -552,16 +416,17 @@ static void *__alloc_remap_buffer(struct device *dev, size_t size, gfp_t gfp,
        return ptr;
 }
 
-static void *__alloc_from_pool(struct device *dev, size_t size,
-                              struct page **ret_page, const void *caller)
+static void *__alloc_from_pool(size_t size, struct page **ret_page)
 {
-       struct arm_vmregion *c;
+       struct dma_pool *pool = &atomic_pool;
+       unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
+       unsigned int pageno;
+       unsigned long flags;
+       void *ptr = NULL;
        size_t align;
 
-       if (!coherent_head.vm_start) {
-               printk(KERN_ERR "%s: coherent pool not initialised!\n",
-                      __func__);
-               dump_stack();
+       if (!pool->vaddr) {
+               WARN(1, "coherent pool not initialised!\n");
                return NULL;
        }
 
@@ -571,35 +436,41 @@ static void *__alloc_from_pool(struct device *dev, size_t size,
         * size. This helps reduce fragmentation of the DMA space.
         */
        align = PAGE_SIZE << get_order(size);
-       c = arm_vmregion_alloc(&coherent_head, align, size, 0, caller);
-       if (c) {
-               void *ptr = (void *)c->vm_start;
-               struct page *page = virt_to_page(ptr);
-               *ret_page = page;
-               return ptr;
+
+       spin_lock_irqsave(&pool->lock, flags);
+       pageno = bitmap_find_next_zero_area(pool->bitmap, pool->nr_pages,
+                                           0, count, (1 << align) - 1);
+       if (pageno < pool->nr_pages) {
+               bitmap_set(pool->bitmap, pageno, count);
+               ptr = pool->vaddr + PAGE_SIZE * pageno;
+               *ret_page = pool->page + pageno;
        }
-       return NULL;
+       spin_unlock_irqrestore(&pool->lock, flags);
+
+       return ptr;
 }
 
-static int __free_from_pool(void *cpu_addr, size_t size)
+static int __free_from_pool(void *start, size_t size)
 {
-       unsigned long start = (unsigned long)cpu_addr;
-       unsigned long end = start + size;
-       struct arm_vmregion *c;
+       struct dma_pool *pool = &atomic_pool;
+       unsigned long pageno, count;
+       unsigned long flags;
 
-       if (start < coherent_head.vm_start || end > coherent_head.vm_end)
+       if (start < pool->vaddr || start > pool->vaddr + pool->size)
                return 0;
 
-       c = arm_vmregion_find_remove(&coherent_head, (unsigned long)start);
-
-       if ((c->vm_end - c->vm_start) != size) {
-               printk(KERN_ERR "%s: freeing wrong coherent size (%ld != %d)\n",
-                      __func__, c->vm_end - c->vm_start, size);
-               dump_stack();
-               size = c->vm_end - c->vm_start;
+       if (start + size > pool->vaddr + pool->size) {
+               WARN(1, "freeing wrong coherent size from pool\n");
+               return 0;
        }
 
-       arm_vmregion_free(&coherent_head, c);
+       pageno = (start - pool->vaddr) >> PAGE_SHIFT;
+       count = size >> PAGE_SHIFT;
+
+       spin_lock_irqsave(&pool->lock, flags);
+       bitmap_clear(pool->bitmap, pageno, count);
+       spin_unlock_irqrestore(&pool->lock, flags);
+
        return 1;
 }
 
@@ -644,7 +515,7 @@ static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot)
 
 #define __get_dma_pgprot(attrs, prot)  __pgprot(0)
 #define __alloc_remap_buffer(dev, size, gfp, prot, ret, c)     NULL
-#define __alloc_from_pool(dev, size, ret_page, c)              NULL
+#define __alloc_from_pool(size, ret_page)                      NULL
 #define __alloc_from_contiguous(dev, size, prot, ret)          NULL
 #define __free_from_pool(cpu_addr, size)                       0
 #define __free_from_contiguous(dev, page, size)                        do { } while (0)
@@ -702,10 +573,10 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
 
        if (arch_is_coherent() || nommu())
                addr = __alloc_simple_buffer(dev, size, gfp, &page);
+       else if (gfp & GFP_ATOMIC)
+               addr = __alloc_from_pool(size, &page);
        else if (!IS_ENABLED(CONFIG_CMA))
                addr = __alloc_remap_buffer(dev, size, gfp, prot, &page, caller);
-       else if (gfp & GFP_ATOMIC)
-               addr = __alloc_from_pool(dev, size, &page, caller);
        else
                addr = __alloc_from_contiguous(dev, size, prot, &page);
 
@@ -741,16 +612,22 @@ int arm_dma_mmap(struct device *dev, struct vm_area_struct *vma,
 {
        int ret = -ENXIO;
 #ifdef CONFIG_MMU
+       unsigned long nr_vma_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+       unsigned long nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
        unsigned long pfn = dma_to_pfn(dev, dma_addr);
+       unsigned long off = vma->vm_pgoff;
+
        vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot);
 
        if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret))
                return ret;
 
-       ret = remap_pfn_range(vma, vma->vm_start,
-                             pfn + vma->vm_pgoff,
-                             vma->vm_end - vma->vm_start,
-                             vma->vm_page_prot);
+       if (off < nr_pages && nr_vma_pages <= (nr_pages - off)) {
+               ret = remap_pfn_range(vma, vma->vm_start,
+                                     pfn + off,
+                                     vma->vm_end - vma->vm_start,
+                                     vma->vm_page_prot);
+       }
 #endif /* CONFIG_MMU */
 
        return ret;
@@ -785,6 +662,21 @@ void arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
        }
 }
 
+int arm_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
+                void *cpu_addr, dma_addr_t handle, size_t size,
+                struct dma_attrs *attrs)
+{
+       struct page *page = pfn_to_page(dma_to_pfn(dev, handle));
+       int ret;
+
+       ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
+       if (unlikely(ret))
+               return ret;
+
+       sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
+       return 0;
+}
+
 static void dma_cache_maint_page(struct page *page, unsigned long offset,
        size_t size, enum dma_data_direction dir,
        void (*op)(const void *, size_t, int))
@@ -998,9 +890,6 @@ static int arm_dma_set_mask(struct device *dev, u64 dma_mask)
 
 static int __init dma_debug_do_init(void)
 {
-#ifdef CONFIG_MMU
-       arm_vmregion_create_proc("dma-mappings", &consistent_head);
-#endif
        dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES);
        return 0;
 }
@@ -1088,7 +977,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, gfp_t
 
        return pages;
 error:
-       while (--i)
+       while (i--)
                if (pages[i])
                        __free_pages(pages[i], 0);
        if (array_size <= PAGE_SIZE)
@@ -1117,61 +1006,32 @@ static int __iommu_free_buffer(struct device *dev, struct page **pages, size_t s
  * Create a CPU mapping for a specified pages
  */
 static void *
-__iommu_alloc_remap(struct page **pages, size_t size, gfp_t gfp, pgprot_t prot)
+__iommu_alloc_remap(struct page **pages, size_t size, gfp_t gfp, pgprot_t prot,
+                   const void *caller)
 {
-       struct arm_vmregion *c;
-       size_t align;
-       size_t count = size >> PAGE_SHIFT;
-       int bit;
+       unsigned int i, nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
+       struct vm_struct *area;
+       unsigned long p;
 
-       if (!consistent_pte[0]) {
-               pr_err("%s: not initialised\n", __func__);
-               dump_stack();
+       area = get_vm_area_caller(size, VM_ARM_DMA_CONSISTENT | VM_USERMAP,
+                                 caller);
+       if (!area)
                return NULL;
-       }
-
-       /*
-        * Align the virtual region allocation - maximum alignment is
-        * a section size, minimum is a page size.  This helps reduce
-        * fragmentation of the DMA space, and also prevents allocations
-        * smaller than a section from crossing a section boundary.
-        */
-       bit = fls(size - 1);
-       if (bit > SECTION_SHIFT)
-               bit = SECTION_SHIFT;
-       align = 1 << bit;
-
-       /*
-        * Allocate a virtual address in the consistent mapping region.
-        */
-       c = arm_vmregion_alloc(&consistent_head, align, size,
-                           gfp & ~(__GFP_DMA | __GFP_HIGHMEM), NULL);
-       if (c) {
-               pte_t *pte;
-               int idx = CONSISTENT_PTE_INDEX(c->vm_start);
-               int i = 0;
-               u32 off = CONSISTENT_OFFSET(c->vm_start) & (PTRS_PER_PTE-1);
-
-               pte = consistent_pte[idx] + off;
-               c->priv = pages;
-
-               do {
-                       BUG_ON(!pte_none(*pte));
-
-                       set_pte_ext(pte, mk_pte(pages[i], prot), 0);
-                       pte++;
-                       off++;
-                       i++;
-                       if (off >= PTRS_PER_PTE) {
-                               off = 0;
-                               pte = consistent_pte[++idx];
-                       }
-               } while (i < count);
 
-               dsb();
+       area->pages = pages;
+       area->nr_pages = nr_pages;
+       p = (unsigned long)area->addr;
 
-               return (void *)c->vm_start;
+       for (i = 0; i < nr_pages; i++) {
+               phys_addr_t phys = __pfn_to_phys(page_to_pfn(pages[i]));
+               if (ioremap_page_range(p, p + PAGE_SIZE, phys, prot))
+                       goto err;
+               p += PAGE_SIZE;
        }
+       return area->addr;
+err:
+       unmap_kernel_range((unsigned long)area->addr, size);
+       vunmap(area->addr);
        return NULL;
 }
 
@@ -1230,6 +1090,19 @@ static int __iommu_remove_mapping(struct device *dev, dma_addr_t iova, size_t si
        return 0;
 }
 
+static struct page **__iommu_get_pages(void *cpu_addr, struct dma_attrs *attrs)
+{
+       struct vm_struct *area;
+
+       if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs))
+               return cpu_addr;
+
+       area = find_vm_area(cpu_addr);
+       if (area && (area->flags & VM_ARM_DMA_CONSISTENT))
+               return area->pages;
+       return NULL;
+}
+
 static void *arm_iommu_alloc_attrs(struct device *dev, size_t size,
            dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs)
 {
@@ -1248,7 +1121,11 @@ static void *arm_iommu_alloc_attrs(struct device *dev, size_t size,
        if (*handle == DMA_ERROR_CODE)
                goto err_buffer;
 
-       addr = __iommu_alloc_remap(pages, size, gfp, prot);
+       if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs))
+               return pages;
+
+       addr = __iommu_alloc_remap(pages, size, gfp, prot,
+                                  __builtin_return_address(0));
        if (!addr)
                goto err_mapping;
 
@@ -1265,31 +1142,25 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
                    void *cpu_addr, dma_addr_t dma_addr, size_t size,
                    struct dma_attrs *attrs)
 {
-       struct arm_vmregion *c;
+       unsigned long uaddr = vma->vm_start;
+       unsigned long usize = vma->vm_end - vma->vm_start;
+       struct page **pages = __iommu_get_pages(cpu_addr, attrs);
 
        vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot);
-       c = arm_vmregion_find(&consistent_head, (unsigned long)cpu_addr);
-
-       if (c) {
-               struct page **pages = c->priv;
-
-               unsigned long uaddr = vma->vm_start;
-               unsigned long usize = vma->vm_end - vma->vm_start;
-               int i = 0;
 
-               do {
-                       int ret;
+       if (!pages)
+               return -ENXIO;
 
-                       ret = vm_insert_page(vma, uaddr, pages[i++]);
-                       if (ret) {
-                               pr_err("Remapping memory, error: %d\n", ret);
-                               return ret;
-                       }
+       do {
+               int ret = vm_insert_page(vma, uaddr, *pages++);
+               if (ret) {
+                       pr_err("Remapping memory failed: %d\n", ret);
+                       return ret;
+               }
+               uaddr += PAGE_SIZE;
+               usize -= PAGE_SIZE;
+       } while (usize > 0);
 
-                       uaddr += PAGE_SIZE;
-                       usize -= PAGE_SIZE;
-               } while (usize > 0);
-       }
        return 0;
 }
 
@@ -1300,16 +1171,35 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
 void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr,
                          dma_addr_t handle, struct dma_attrs *attrs)
 {
-       struct arm_vmregion *c;
+       struct page **pages = __iommu_get_pages(cpu_addr, attrs);
        size = PAGE_ALIGN(size);
 
-       c = arm_vmregion_find(&consistent_head, (unsigned long)cpu_addr);
-       if (c) {
-               struct page **pages = c->priv;
-               __dma_free_remap(cpu_addr, size);
-               __iommu_remove_mapping(dev, handle, size);
-               __iommu_free_buffer(dev, pages, size);
+       if (!pages) {
+               WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr);
+               return;
        }
+
+       if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) {
+               unmap_kernel_range((unsigned long)cpu_addr, size);
+               vunmap(cpu_addr);
+       }
+
+       __iommu_remove_mapping(dev, handle, size);
+       __iommu_free_buffer(dev, pages, size);
+}
+
+static int arm_iommu_get_sgtable(struct device *dev, struct sg_table *sgt,
+                                void *cpu_addr, dma_addr_t dma_addr,
+                                size_t size, struct dma_attrs *attrs)
+{
+       unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
+       struct page **pages = __iommu_get_pages(cpu_addr, attrs);
+
+       if (!pages)
+               return -ENXIO;
+
+       return sg_alloc_table_from_pages(sgt, pages, count, 0, size,
+                                        GFP_KERNEL);
 }
 
 /*
@@ -1317,7 +1207,7 @@ void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr,
  */
 static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
                          size_t size, dma_addr_t *handle,
-                         enum dma_data_direction dir)
+                         enum dma_data_direction dir, struct dma_attrs *attrs)
 {
        struct dma_iommu_mapping *mapping = dev->archdata.mapping;
        dma_addr_t iova, iova_base;
@@ -1336,7 +1226,8 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
                phys_addr_t phys = page_to_phys(sg_page(s));
                unsigned int len = PAGE_ALIGN(s->offset + s->length);
 
-               if (!arch_is_coherent())
+               if (!arch_is_coherent() &&
+                   !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
                        __dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
 
                ret = iommu_map(mapping->domain, iova, phys, len, 0);
@@ -1383,7 +1274,7 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents,
 
                if (s->offset || (size & ~PAGE_MASK) || size + s->length > max) {
                        if (__map_sg_chunk(dev, start, size, &dma->dma_address,
-                           dir) < 0)
+                           dir, attrs) < 0)
                                goto bad_mapping;
 
                        dma->dma_address += offset;
@@ -1396,7 +1287,7 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents,
                }
                size += s->length;
        }
-       if (__map_sg_chunk(dev, start, size, &dma->dma_address, dir) < 0)
+       if (__map_sg_chunk(dev, start, size, &dma->dma_address, dir, attrs) < 0)
                goto bad_mapping;
 
        dma->dma_address += offset;
@@ -1430,7 +1321,8 @@ void arm_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
                if (sg_dma_len(s))
                        __iommu_remove_mapping(dev, sg_dma_address(s),
                                               sg_dma_len(s));
-               if (!arch_is_coherent())
+               if (!arch_is_coherent() &&
+                   !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
                        __dma_page_dev_to_cpu(sg_page(s), s->offset,
                                              s->length, dir);
        }
@@ -1492,7 +1384,7 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page,
        dma_addr_t dma_addr;
        int ret, len = PAGE_ALIGN(size + offset);
 
-       if (!arch_is_coherent())
+       if (!arch_is_coherent() && !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
                __dma_page_cpu_to_dev(page, offset, size, dir);
 
        dma_addr = __alloc_iova(mapping, len);
@@ -1531,7 +1423,7 @@ static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle,
        if (!iova)
                return;
 
-       if (!arch_is_coherent())
+       if (!arch_is_coherent() && !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
                __dma_page_dev_to_cpu(page, offset, size, dir);
 
        iommu_unmap(mapping->domain, iova, len);
@@ -1571,6 +1463,7 @@ struct dma_map_ops iommu_ops = {
        .alloc          = arm_iommu_alloc_attrs,
        .free           = arm_iommu_free_attrs,
        .mmap           = arm_iommu_mmap_attrs,
+       .get_sgtable    = arm_iommu_get_sgtable,
 
        .map_page               = arm_iommu_map_page,
        .unmap_page             = arm_iommu_unmap_page,
index 2e8a1efdf7b85f3443294b396ec5673496dd635d..6776160618ef0ede79d07b4f6a0873c30df2a1d0 100644 (file)
@@ -59,6 +59,9 @@ extern void __flush_dcache_page(struct address_space *mapping, struct page *page
 #define VM_ARM_MTYPE(mt)               ((mt) << 20)
 #define VM_ARM_MTYPE_MASK      (0x1f << 20)
 
+/* consistent regions used by dma_alloc_attrs() */
+#define VM_ARM_DMA_CONSISTENT  0x20000000
+
 #endif
 
 #ifdef CONFIG_ZONE_DMA
index 845f461f8ec16847e69414f5c966a9ca4f961db6..c2021139cb563fd14a74c327d453f3cf0cee183d 100644 (file)
@@ -38,11 +38,19 @@ ENTRY(v7wbi_flush_user_tlb_range)
        dsb
        mov     r0, r0, lsr #PAGE_SHIFT         @ align address
        mov     r1, r1, lsr #PAGE_SHIFT
+#ifdef CONFIG_ARM_ERRATA_720789
+       mov     r3, #0
+#else
        asid    r3, r3                          @ mask ASID
+#endif
        orr     r0, r3, r0, lsl #PAGE_SHIFT     @ Create initial MVA
        mov     r1, r1, lsl #PAGE_SHIFT
 1:
+#ifdef CONFIG_ARM_ERRATA_720789
+       ALT_SMP(mcr     p15, 0, r0, c8, c3, 3)  @ TLB invalidate U MVA all ASID (shareable)
+#else
        ALT_SMP(mcr     p15, 0, r0, c8, c3, 1)  @ TLB invalidate U MVA (shareable)
+#endif
        ALT_UP(mcr      p15, 0, r0, c8, c7, 1)  @ TLB invalidate U MVA
 
        add     r0, r0, #PAGE_SZ
@@ -67,7 +75,11 @@ ENTRY(v7wbi_flush_kern_tlb_range)
        mov     r0, r0, lsl #PAGE_SHIFT
        mov     r1, r1, lsl #PAGE_SHIFT
 1:
+#ifdef CONFIG_ARM_ERRATA_720789
+       ALT_SMP(mcr     p15, 0, r0, c8, c3, 3)  @ TLB invalidate U MVA all ASID (shareable)
+#else
        ALT_SMP(mcr     p15, 0, r0, c8, c3, 1)  @ TLB invalidate U MVA (shareable)
+#endif
        ALT_UP(mcr      p15, 0, r0, c8, c7, 1)  @ TLB invalidate U MVA
        add     r0, r0, #PAGE_SZ
        cmp     r0, r1
index c722f9ce691827e8924674119b75708ba7ea6428..baf9064c0844bcafef399f943209a570dc34ceec 100644 (file)
@@ -47,12 +47,6 @@ config MXC_TZIC
 config MXC_AVIC
        bool
 
-config MXC_PWM
-       tristate "Enable PWM driver"
-       select HAVE_PWM
-       help
-         Enable support for the i.MX PWM controller(s).
-
 config MXC_DEBUG_BOARD
        bool "Enable MXC debug board(for 3-stack)"
        help
index 63b064b5c1d55148c1c5fa226a6173aafbb3a23f..6ac72003115088fbade50e658ae438b18c0a1610 100644 (file)
@@ -11,7 +11,6 @@ obj-$(CONFIG_MXC_AVIC) += avic.o
 obj-$(CONFIG_IMX_HAVE_IOMUX_V1) += iomux-v1.o
 obj-$(CONFIG_ARCH_MXC_IOMUX_V3) += iomux-v3.o
 obj-$(CONFIG_IRAM_ALLOC) += iram_alloc.o
-obj-$(CONFIG_MXC_PWM)  += pwm.o
 obj-$(CONFIG_MXC_ULPI) += ulpi.o
 obj-$(CONFIG_MXC_USE_EPIT) += epit.o
 obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o
index 375cdd0cf876b114f9e9e6fcbd7751cbc5fddf01..8289d915e615855c9b5628b6503d3c13616045db 100644 (file)
@@ -15,7 +15,7 @@
  *
  **/
 struct imxi2c_platform_data {
-       int bitrate;
+       u32 bitrate;
 };
 
 #endif /* __ASM_ARCH_I2C_H_ */
diff --git a/arch/arm/plat-mxc/pwm.c b/arch/arm/plat-mxc/pwm.c
deleted file mode 100644 (file)
index c0cab22..0000000
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * simple driver for PWM (Pulse Width Modulator) controller
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/pwm.h>
-#include <mach/hardware.h>
-
-
-/* i.MX1 and i.MX21 share the same PWM function block: */
-
-#define MX1_PWMC    0x00   /* PWM Control Register */
-#define MX1_PWMS    0x04   /* PWM Sample Register */
-#define MX1_PWMP    0x08   /* PWM Period Register */
-
-
-/* i.MX27, i.MX31, i.MX35 share the same PWM function block: */
-
-#define MX3_PWMCR                 0x00    /* PWM Control Register */
-#define MX3_PWMSAR                0x0C    /* PWM Sample Register */
-#define MX3_PWMPR                 0x10    /* PWM Period Register */
-#define MX3_PWMCR_PRESCALER(x)    (((x - 1) & 0xFFF) << 4)
-#define MX3_PWMCR_DOZEEN                (1 << 24)
-#define MX3_PWMCR_WAITEN                (1 << 23)
-#define MX3_PWMCR_DBGEN                        (1 << 22)
-#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16)
-#define MX3_PWMCR_CLKSRC_IPG      (1 << 16)
-#define MX3_PWMCR_EN              (1 << 0)
-
-
-
-struct pwm_device {
-       struct list_head        node;
-       struct platform_device *pdev;
-
-       const char      *label;
-       struct clk      *clk;
-
-       int             clk_enabled;
-       void __iomem    *mmio_base;
-
-       unsigned int    use_count;
-       unsigned int    pwm_id;
-};
-
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
-{
-       if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
-               return -EINVAL;
-
-       if (!(cpu_is_mx1() || cpu_is_mx21())) {
-               unsigned long long c;
-               unsigned long period_cycles, duty_cycles, prescale;
-               u32 cr;
-
-               c = clk_get_rate(pwm->clk);
-               c = c * period_ns;
-               do_div(c, 1000000000);
-               period_cycles = c;
-
-               prescale = period_cycles / 0x10000 + 1;
-
-               period_cycles /= prescale;
-               c = (unsigned long long)period_cycles * duty_ns;
-               do_div(c, period_ns);
-               duty_cycles = c;
-
-               /*
-                * according to imx pwm RM, the real period value should be
-                * PERIOD value in PWMPR plus 2.
-                */
-               if (period_cycles > 2)
-                       period_cycles -= 2;
-               else
-                       period_cycles = 0;
-
-               writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR);
-               writel(period_cycles, pwm->mmio_base + MX3_PWMPR);
-
-               cr = MX3_PWMCR_PRESCALER(prescale) |
-                       MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
-                       MX3_PWMCR_DBGEN | MX3_PWMCR_EN;
-
-               if (cpu_is_mx25())
-                       cr |= MX3_PWMCR_CLKSRC_IPG;
-               else
-                       cr |= MX3_PWMCR_CLKSRC_IPG_HIGH;
-
-               writel(cr, pwm->mmio_base + MX3_PWMCR);
-       } else if (cpu_is_mx1() || cpu_is_mx21()) {
-               /* The PWM subsystem allows for exact frequencies. However,
-                * I cannot connect a scope on my device to the PWM line and
-                * thus cannot provide the program the PWM controller
-                * exactly. Instead, I'm relying on the fact that the
-                * Bootloader (u-boot or WinCE+haret) has programmed the PWM
-                * function group already. So I'll just modify the PWM sample
-                * register to follow the ratio of duty_ns vs. period_ns
-                * accordingly.
-                *
-                * This is good enough for programming the brightness of
-                * the LCD backlight.
-                *
-                * The real implementation would divide PERCLK[0] first by
-                * both the prescaler (/1 .. /128) and then by CLKSEL
-                * (/2 .. /16).
-                */
-               u32 max = readl(pwm->mmio_base + MX1_PWMP);
-               u32 p = max * duty_ns / period_ns;
-               writel(max - p, pwm->mmio_base + MX1_PWMS);
-       } else {
-               BUG();
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(pwm_config);
-
-int pwm_enable(struct pwm_device *pwm)
-{
-       int rc = 0;
-
-       if (!pwm->clk_enabled) {
-               rc = clk_prepare_enable(pwm->clk);
-               if (!rc)
-                       pwm->clk_enabled = 1;
-       }
-       return rc;
-}
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
-{
-       writel(0, pwm->mmio_base + MX3_PWMCR);
-
-       if (pwm->clk_enabled) {
-               clk_disable_unprepare(pwm->clk);
-               pwm->clk_enabled = 0;
-       }
-}
-EXPORT_SYMBOL(pwm_disable);
-
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-       struct pwm_device *pwm;
-       int found = 0;
-
-       mutex_lock(&pwm_lock);
-
-       list_for_each_entry(pwm, &pwm_list, node) {
-               if (pwm->pwm_id == pwm_id) {
-                       found = 1;
-                       break;
-               }
-       }
-
-       if (found) {
-               if (pwm->use_count == 0) {
-                       pwm->use_count++;
-                       pwm->label = label;
-               } else
-                       pwm = ERR_PTR(-EBUSY);
-       } else
-               pwm = ERR_PTR(-ENOENT);
-
-       mutex_unlock(&pwm_lock);
-       return pwm;
-}
-EXPORT_SYMBOL(pwm_request);
-
-void pwm_free(struct pwm_device *pwm)
-{
-       mutex_lock(&pwm_lock);
-
-       if (pwm->use_count) {
-               pwm->use_count--;
-               pwm->label = NULL;
-       } else
-               pr_warning("PWM device already freed\n");
-
-       mutex_unlock(&pwm_lock);
-}
-EXPORT_SYMBOL(pwm_free);
-
-static int __devinit mxc_pwm_probe(struct platform_device *pdev)
-{
-       struct pwm_device *pwm;
-       struct resource *r;
-       int ret = 0;
-
-       pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
-       if (pwm == NULL) {
-               dev_err(&pdev->dev, "failed to allocate memory\n");
-               return -ENOMEM;
-       }
-
-       pwm->clk = clk_get(&pdev->dev, "pwm");
-
-       if (IS_ERR(pwm->clk)) {
-               ret = PTR_ERR(pwm->clk);
-               goto err_free;
-       }
-
-       pwm->clk_enabled = 0;
-
-       pwm->use_count = 0;
-       pwm->pwm_id = pdev->id;
-       pwm->pdev = pdev;
-
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (r == NULL) {
-               dev_err(&pdev->dev, "no memory resource defined\n");
-               ret = -ENODEV;
-               goto err_free_clk;
-       }
-
-       r = request_mem_region(r->start, resource_size(r), pdev->name);
-       if (r == NULL) {
-               dev_err(&pdev->dev, "failed to request memory resource\n");
-               ret = -EBUSY;
-               goto err_free_clk;
-       }
-
-       pwm->mmio_base = ioremap(r->start, resource_size(r));
-       if (pwm->mmio_base == NULL) {
-               dev_err(&pdev->dev, "failed to ioremap() registers\n");
-               ret = -ENODEV;
-               goto err_free_mem;
-       }
-
-       mutex_lock(&pwm_lock);
-       list_add_tail(&pwm->node, &pwm_list);
-       mutex_unlock(&pwm_lock);
-
-       platform_set_drvdata(pdev, pwm);
-       return 0;
-
-err_free_mem:
-       release_mem_region(r->start, resource_size(r));
-err_free_clk:
-       clk_put(pwm->clk);
-err_free:
-       kfree(pwm);
-       return ret;
-}
-
-static int __devexit mxc_pwm_remove(struct platform_device *pdev)
-{
-       struct pwm_device *pwm;
-       struct resource *r;
-
-       pwm = platform_get_drvdata(pdev);
-       if (pwm == NULL)
-               return -ENODEV;
-
-       mutex_lock(&pwm_lock);
-       list_del(&pwm->node);
-       mutex_unlock(&pwm_lock);
-
-       iounmap(pwm->mmio_base);
-
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(r->start, resource_size(r));
-
-       clk_put(pwm->clk);
-
-       kfree(pwm);
-       return 0;
-}
-
-static struct platform_driver mxc_pwm_driver = {
-       .driver         = {
-               .name   = "mxc_pwm",
-       },
-       .probe          = mxc_pwm_probe,
-       .remove         = __devexit_p(mxc_pwm_remove),
-};
-
-static int __init mxc_pwm_init(void)
-{
-       return platform_driver_register(&mxc_pwm_driver);
-}
-arch_initcall(mxc_pwm_init);
-
-static void __exit mxc_pwm_exit(void)
-{
-       platform_driver_unregister(&mxc_pwm_driver);
-}
-module_exit(mxc_pwm_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
diff --git a/arch/arm/plat-nomadik/include/plat/i2c.h b/arch/arm/plat-nomadik/include/plat/i2c.h
deleted file mode 100644 (file)
index 8ba70ff..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2009 ST-Ericsson
- *
- * 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 __PLAT_I2C_H
-#define __PLAT_I2C_H
-
-enum i2c_freq_mode {
-       I2C_FREQ_MODE_STANDARD,         /* up to 100 Kb/s */
-       I2C_FREQ_MODE_FAST,             /* up to 400 Kb/s */
-       I2C_FREQ_MODE_HIGH_SPEED,       /* up to 3.4 Mb/s */
-       I2C_FREQ_MODE_FAST_PLUS,        /* up to 1 Mb/s */
-};
-
-/**
- * struct nmk_i2c_controller - client specific controller configuration
- * @clk_freq:  clock frequency for the operation mode
- * @slsu:      Slave data setup time in ns.
- *             The needed setup time for three modes of operation
- *             are 250ns, 100ns and 10ns respectively thus leading
- *             to the values of 14, 6, 2 for a 48 MHz i2c clk
- * @tft:       Tx FIFO Threshold in bytes
- * @rft:       Rx FIFO Threshold in bytes
- * @timeout    Slave response timeout(ms)
- * @sm:                speed mode
- */
-struct nmk_i2c_controller {
-       unsigned long   clk_freq;
-       unsigned short  slsu;
-       unsigned char   tft;
-       unsigned char   rft;
-       int timeout;
-       enum i2c_freq_mode      sm;
-};
-
-#endif /* __PLAT_I2C_H */
index 5493bd95da5ee9f6988bb386a84151511a30b931..eb3e4d555343bb921e71f0598ca53a37fe1a26f1 100644 (file)
@@ -81,8 +81,6 @@ struct omap_mmc_platform_data {
        /* Return context loss count due to PM states changing */
        int (*get_context_loss_count)(struct device *dev);
 
-       u64 dma_mask;
-
        /* Integrating attributes from the omap_hwmod layer */
        u8 controller_flags;
 
index f302d048392db9df129f7b5e025a007e28f11419..af8e484001e54c212ea89e8e16907d374bb46568 100644 (file)
@@ -8,5 +8,4 @@ obj-$(CONFIG_PXA3xx)            += mfp.o
 obj-$(CONFIG_PXA95x)           += mfp.o
 obj-$(CONFIG_ARCH_MMP)         += mfp.o
 
-obj-$(CONFIG_HAVE_PWM)         += pwm.o
 obj-$(CONFIG_PXA_SSP)          += ssp.o
diff --git a/arch/arm/plat-pxa/pwm.c b/arch/arm/plat-pxa/pwm.c
deleted file mode 100644 (file)
index ef32686..0000000
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * linux/arch/arm/mach-pxa/pwm.c
- *
- * simple driver for PWM (Pulse Width Modulator) controller
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * 2008-02-13  initial version
- *             eric miao <eric.miao@marvell.com>
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/pwm.h>
-
-#include <asm/div64.h>
-
-#define HAS_SECONDARY_PWM      0x10
-#define PWM_ID_BASE(d)         ((d) & 0xf)
-
-static const struct platform_device_id pwm_id_table[] = {
-       /*   PWM    has_secondary_pwm? */
-       { "pxa25x-pwm", 0 },
-       { "pxa27x-pwm", 0 | HAS_SECONDARY_PWM },
-       { "pxa168-pwm", 1 },
-       { "pxa910-pwm", 1 },
-       { },
-};
-MODULE_DEVICE_TABLE(platform, pwm_id_table);
-
-/* PWM registers and bits definitions */
-#define PWMCR          (0x00)
-#define PWMDCR         (0x04)
-#define PWMPCR         (0x08)
-
-#define PWMCR_SD       (1 << 6)
-#define PWMDCR_FD      (1 << 10)
-
-struct pwm_device {
-       struct list_head        node;
-       struct pwm_device       *secondary;
-       struct platform_device  *pdev;
-
-       const char      *label;
-       struct clk      *clk;
-       int             clk_enabled;
-       void __iomem    *mmio_base;
-
-       unsigned int    use_count;
-       unsigned int    pwm_id;
-};
-
-/*
- * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
- * duty_ns   = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
- */
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
-{
-       unsigned long long c;
-       unsigned long period_cycles, prescale, pv, dc;
-
-       if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
-               return -EINVAL;
-
-       c = clk_get_rate(pwm->clk);
-       c = c * period_ns;
-       do_div(c, 1000000000);
-       period_cycles = c;
-
-       if (period_cycles < 1)
-               period_cycles = 1;
-       prescale = (period_cycles - 1) / 1024;
-       pv = period_cycles / (prescale + 1) - 1;
-
-       if (prescale > 63)
-               return -EINVAL;
-
-       if (duty_ns == period_ns)
-               dc = PWMDCR_FD;
-       else
-               dc = (pv + 1) * duty_ns / period_ns;
-
-       /* NOTE: the clock to PWM has to be enabled first
-        * before writing to the registers
-        */
-       clk_enable(pwm->clk);
-       __raw_writel(prescale, pwm->mmio_base + PWMCR);
-       __raw_writel(dc, pwm->mmio_base + PWMDCR);
-       __raw_writel(pv, pwm->mmio_base + PWMPCR);
-       clk_disable(pwm->clk);
-
-       return 0;
-}
-EXPORT_SYMBOL(pwm_config);
-
-int pwm_enable(struct pwm_device *pwm)
-{
-       int rc = 0;
-
-       if (!pwm->clk_enabled) {
-               rc = clk_enable(pwm->clk);
-               if (!rc)
-                       pwm->clk_enabled = 1;
-       }
-       return rc;
-}
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
-{
-       if (pwm->clk_enabled) {
-               clk_disable(pwm->clk);
-               pwm->clk_enabled = 0;
-       }
-}
-EXPORT_SYMBOL(pwm_disable);
-
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-       struct pwm_device *pwm;
-       int found = 0;
-
-       mutex_lock(&pwm_lock);
-
-       list_for_each_entry(pwm, &pwm_list, node) {
-               if (pwm->pwm_id == pwm_id) {
-                       found = 1;
-                       break;
-               }
-       }
-
-       if (found) {
-               if (pwm->use_count == 0) {
-                       pwm->use_count++;
-                       pwm->label = label;
-               } else
-                       pwm = ERR_PTR(-EBUSY);
-       } else
-               pwm = ERR_PTR(-ENOENT);
-
-       mutex_unlock(&pwm_lock);
-       return pwm;
-}
-EXPORT_SYMBOL(pwm_request);
-
-void pwm_free(struct pwm_device *pwm)
-{
-       mutex_lock(&pwm_lock);
-
-       if (pwm->use_count) {
-               pwm->use_count--;
-               pwm->label = NULL;
-       } else
-               pr_warning("PWM device already freed\n");
-
-       mutex_unlock(&pwm_lock);
-}
-EXPORT_SYMBOL(pwm_free);
-
-static inline void __add_pwm(struct pwm_device *pwm)
-{
-       mutex_lock(&pwm_lock);
-       list_add_tail(&pwm->node, &pwm_list);
-       mutex_unlock(&pwm_lock);
-}
-
-static int __devinit pwm_probe(struct platform_device *pdev)
-{
-       const struct platform_device_id *id = platform_get_device_id(pdev);
-       struct pwm_device *pwm, *secondary = NULL;
-       struct resource *r;
-       int ret = 0;
-
-       pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
-       if (pwm == NULL) {
-               dev_err(&pdev->dev, "failed to allocate memory\n");
-               return -ENOMEM;
-       }
-
-       pwm->clk = clk_get(&pdev->dev, NULL);
-       if (IS_ERR(pwm->clk)) {
-               ret = PTR_ERR(pwm->clk);
-               goto err_free;
-       }
-       pwm->clk_enabled = 0;
-
-       pwm->use_count = 0;
-       pwm->pwm_id = PWM_ID_BASE(id->driver_data) + pdev->id;
-       pwm->pdev = pdev;
-
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (r == NULL) {
-               dev_err(&pdev->dev, "no memory resource defined\n");
-               ret = -ENODEV;
-               goto err_free_clk;
-       }
-
-       r = request_mem_region(r->start, resource_size(r), pdev->name);
-       if (r == NULL) {
-               dev_err(&pdev->dev, "failed to request memory resource\n");
-               ret = -EBUSY;
-               goto err_free_clk;
-       }
-
-       pwm->mmio_base = ioremap(r->start, resource_size(r));
-       if (pwm->mmio_base == NULL) {
-               dev_err(&pdev->dev, "failed to ioremap() registers\n");
-               ret = -ENODEV;
-               goto err_free_mem;
-       }
-
-       if (id->driver_data & HAS_SECONDARY_PWM) {
-               secondary = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
-               if (secondary == NULL) {
-                       ret = -ENOMEM;
-                       goto err_free_mem;
-               }
-
-               *secondary = *pwm;
-               pwm->secondary = secondary;
-
-               /* registers for the second PWM has offset of 0x10 */
-               secondary->mmio_base = pwm->mmio_base + 0x10;
-               secondary->pwm_id = pdev->id + 2;
-       }
-
-       __add_pwm(pwm);
-       if (secondary)
-               __add_pwm(secondary);
-
-       platform_set_drvdata(pdev, pwm);
-       return 0;
-
-err_free_mem:
-       release_mem_region(r->start, resource_size(r));
-err_free_clk:
-       clk_put(pwm->clk);
-err_free:
-       kfree(pwm);
-       return ret;
-}
-
-static int __devexit pwm_remove(struct platform_device *pdev)
-{
-       struct pwm_device *pwm;
-       struct resource *r;
-
-       pwm = platform_get_drvdata(pdev);
-       if (pwm == NULL)
-               return -ENODEV;
-
-       mutex_lock(&pwm_lock);
-
-       if (pwm->secondary) {
-               list_del(&pwm->secondary->node);
-               kfree(pwm->secondary);
-       }
-
-       list_del(&pwm->node);
-       mutex_unlock(&pwm_lock);
-
-       iounmap(pwm->mmio_base);
-
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(r->start, resource_size(r));
-
-       clk_put(pwm->clk);
-       kfree(pwm);
-       return 0;
-}
-
-static struct platform_driver pwm_driver = {
-       .driver         = {
-               .name   = "pxa25x-pwm",
-               .owner  = THIS_MODULE,
-       },
-       .probe          = pwm_probe,
-       .remove         = __devexit_p(pwm_remove),
-       .id_table       = pwm_id_table,
-};
-
-static int __init pwm_init(void)
-{
-       return platform_driver_register(&pwm_driver);
-}
-arch_initcall(pwm_init);
-
-static void __exit pwm_exit(void)
-{
-       platform_driver_unregister(&pwm_driver);
-}
-module_exit(pwm_exit);
-
-MODULE_LICENSE("GPL v2");
index b78717496677fae8e17ba77392785aeea90e9ffd..9e40e8d007404b9f9533c56ad9ebc626c9870116 100644 (file)
@@ -59,7 +59,3 @@ obj-$(CONFIG_SAMSUNG_WAKEMASK)        += wakeup-mask.o
 
 obj-$(CONFIG_S5P_PM)           += s5p-pm.o s5p-irq-pm.o
 obj-$(CONFIG_S5P_SLEEP)                += s5p-sleep.o
-
-# PWM support
-
-obj-$(CONFIG_HAVE_PWM)         += pwm.o
diff --git a/arch/arm/plat-samsung/pwm.c b/arch/arm/plat-samsung/pwm.c
deleted file mode 100644 (file)
index d358305..0000000
+++ /dev/null
@@ -1,416 +0,0 @@
-/* arch/arm/plat-s3c/pwm.c
- *
- * Copyright (c) 2007 Ben Dooks
- * Copyright (c) 2008 Simtec Electronics
- *     Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org>
- *
- * S3C series PWM device core
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License.
-*/
-
-#include <linux/export.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/pwm.h>
-
-#include <mach/map.h>
-
-#include <plat/regs-timer.h>
-
-struct pwm_device {
-       struct list_head         list;
-       struct platform_device  *pdev;
-
-       struct clk              *clk_div;
-       struct clk              *clk;
-       const char              *label;
-
-       unsigned int             period_ns;
-       unsigned int             duty_ns;
-
-       unsigned char            tcon_base;
-       unsigned char            use_count;
-       unsigned char            pwm_id;
-};
-
-#define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg)
-
-static struct clk *clk_scaler[2];
-
-static inline int pwm_is_tdiv(struct pwm_device *pwm)
-{
-       return clk_get_parent(pwm->clk) == pwm->clk_div;
-}
-
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-       struct pwm_device *pwm;
-       int found = 0;
-
-       mutex_lock(&pwm_lock);
-
-       list_for_each_entry(pwm, &pwm_list, list) {
-               if (pwm->pwm_id == pwm_id) {
-                       found = 1;
-                       break;
-               }
-       }
-
-       if (found) {
-               if (pwm->use_count == 0) {
-                       pwm->use_count = 1;
-                       pwm->label = label;
-               } else
-                       pwm = ERR_PTR(-EBUSY);
-       } else
-               pwm = ERR_PTR(-ENOENT);
-
-       mutex_unlock(&pwm_lock);
-       return pwm;
-}
-
-EXPORT_SYMBOL(pwm_request);
-
-
-void pwm_free(struct pwm_device *pwm)
-{
-       mutex_lock(&pwm_lock);
-
-       if (pwm->use_count) {
-               pwm->use_count--;
-               pwm->label = NULL;
-       } else
-               printk(KERN_ERR "PWM%d device already freed\n", pwm->pwm_id);
-
-       mutex_unlock(&pwm_lock);
-}
-
-EXPORT_SYMBOL(pwm_free);
-
-#define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0))
-#define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2))
-#define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3))
-#define pwm_tcon_manulupdate(pwm) (1 << (pwm->tcon_base + 1))
-
-int pwm_enable(struct pwm_device *pwm)
-{
-       unsigned long flags;
-       unsigned long tcon;
-
-       local_irq_save(flags);
-
-       tcon = __raw_readl(S3C2410_TCON);
-       tcon |= pwm_tcon_start(pwm);
-       __raw_writel(tcon, S3C2410_TCON);
-
-       local_irq_restore(flags);
-
-       return 0;
-}
-
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
-{
-       unsigned long flags;
-       unsigned long tcon;
-
-       local_irq_save(flags);
-
-       tcon = __raw_readl(S3C2410_TCON);
-       tcon &= ~pwm_tcon_start(pwm);
-       __raw_writel(tcon, S3C2410_TCON);
-
-       local_irq_restore(flags);
-}
-
-EXPORT_SYMBOL(pwm_disable);
-
-static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq)
-{
-       unsigned long tin_parent_rate;
-       unsigned int div;
-
-       tin_parent_rate = clk_get_rate(clk_get_parent(pwm->clk_div));
-       pwm_dbg(pwm, "tin parent at %lu\n", tin_parent_rate);
-
-       for (div = 2; div <= 16; div *= 2) {
-               if ((tin_parent_rate / (div << 16)) < freq)
-                       return tin_parent_rate / div;
-       }
-
-       return tin_parent_rate / 16;
-}
-
-#define NS_IN_HZ (1000000000UL)
-
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
-{
-       unsigned long tin_rate;
-       unsigned long tin_ns;
-       unsigned long period;
-       unsigned long flags;
-       unsigned long tcon;
-       unsigned long tcnt;
-       long tcmp;
-
-       /* We currently avoid using 64bit arithmetic by using the
-        * fact that anything faster than 1Hz is easily representable
-        * by 32bits. */
-
-       if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ)
-               return -ERANGE;
-
-       if (duty_ns > period_ns)
-               return -EINVAL;
-
-       if (period_ns == pwm->period_ns &&
-           duty_ns == pwm->duty_ns)
-               return 0;
-
-       /* The TCMP and TCNT can be read without a lock, they're not
-        * shared between the timers. */
-
-       tcmp = __raw_readl(S3C2410_TCMPB(pwm->pwm_id));
-       tcnt = __raw_readl(S3C2410_TCNTB(pwm->pwm_id));
-
-       period = NS_IN_HZ / period_ns;
-
-       pwm_dbg(pwm, "duty_ns=%d, period_ns=%d (%lu)\n",
-               duty_ns, period_ns, period);
-
-       /* Check to see if we are changing the clock rate of the PWM */
-
-       if (pwm->period_ns != period_ns) {
-               if (pwm_is_tdiv(pwm)) {
-                       tin_rate = pwm_calc_tin(pwm, period);
-                       clk_set_rate(pwm->clk_div, tin_rate);
-               } else
-                       tin_rate = clk_get_rate(pwm->clk);
-
-               pwm->period_ns = period_ns;
-
-               pwm_dbg(pwm, "tin_rate=%lu\n", tin_rate);
-
-               tin_ns = NS_IN_HZ / tin_rate;
-               tcnt = period_ns / tin_ns;
-       } else
-               tin_ns = NS_IN_HZ / clk_get_rate(pwm->clk);
-
-       /* Note, counters count down */
-
-       tcmp = duty_ns / tin_ns;
-       tcmp = tcnt - tcmp;
-       /* the pwm hw only checks the compare register after a decrement,
-          so the pin never toggles if tcmp = tcnt */
-       if (tcmp == tcnt)
-               tcmp--;
-
-       pwm_dbg(pwm, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt);
-
-       if (tcmp < 0)
-               tcmp = 0;
-
-       /* Update the PWM register block. */
-
-       local_irq_save(flags);
-
-       __raw_writel(tcmp, S3C2410_TCMPB(pwm->pwm_id));
-       __raw_writel(tcnt, S3C2410_TCNTB(pwm->pwm_id));
-
-       tcon = __raw_readl(S3C2410_TCON);
-       tcon |= pwm_tcon_manulupdate(pwm);
-       tcon |= pwm_tcon_autoreload(pwm);
-       __raw_writel(tcon, S3C2410_TCON);
-
-       tcon &= ~pwm_tcon_manulupdate(pwm);
-       __raw_writel(tcon, S3C2410_TCON);
-
-       local_irq_restore(flags);
-
-       return 0;
-}
-
-EXPORT_SYMBOL(pwm_config);
-
-static int pwm_register(struct pwm_device *pwm)
-{
-       pwm->duty_ns = -1;
-       pwm->period_ns = -1;
-
-       mutex_lock(&pwm_lock);
-       list_add_tail(&pwm->list, &pwm_list);
-       mutex_unlock(&pwm_lock);
-
-       return 0;
-}
-
-static int s3c_pwm_probe(struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct pwm_device *pwm;
-       unsigned long flags;
-       unsigned long tcon;
-       unsigned int id = pdev->id;
-       int ret;
-
-       if (id == 4) {
-               dev_err(dev, "TIMER4 is currently not supported\n");
-               return -ENXIO;
-       }
-
-       pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
-       if (pwm == NULL) {
-               dev_err(dev, "failed to allocate pwm_device\n");
-               return -ENOMEM;
-       }
-
-       pwm->pdev = pdev;
-       pwm->pwm_id = id;
-
-       /* calculate base of control bits in TCON */
-       pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4;
-
-       pwm->clk = clk_get(dev, "pwm-tin");
-       if (IS_ERR(pwm->clk)) {
-               dev_err(dev, "failed to get pwm tin clk\n");
-               ret = PTR_ERR(pwm->clk);
-               goto err_alloc;
-       }
-
-       pwm->clk_div = clk_get(dev, "pwm-tdiv");
-       if (IS_ERR(pwm->clk_div)) {
-               dev_err(dev, "failed to get pwm tdiv clk\n");
-               ret = PTR_ERR(pwm->clk_div);
-               goto err_clk_tin;
-       }
-
-       clk_enable(pwm->clk);
-       clk_enable(pwm->clk_div);
-
-       local_irq_save(flags);
-
-       tcon = __raw_readl(S3C2410_TCON);
-       tcon |= pwm_tcon_invert(pwm);
-       __raw_writel(tcon, S3C2410_TCON);
-
-       local_irq_restore(flags);
-
-
-       ret = pwm_register(pwm);
-       if (ret) {
-               dev_err(dev, "failed to register pwm\n");
-               goto err_clk_tdiv;
-       }
-
-       pwm_dbg(pwm, "config bits %02x\n",
-               (__raw_readl(S3C2410_TCON) >> pwm->tcon_base) & 0x0f);
-
-       dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n",
-                clk_get_rate(pwm->clk),
-                clk_get_rate(pwm->clk_div),
-                pwm_is_tdiv(pwm) ? "div" : "ext", pwm->tcon_base);
-
-       platform_set_drvdata(pdev, pwm);
-       return 0;
-
- err_clk_tdiv:
-       clk_disable(pwm->clk_div);
-       clk_disable(pwm->clk);
-       clk_put(pwm->clk_div);
-
- err_clk_tin:
-       clk_put(pwm->clk);
-
- err_alloc:
-       kfree(pwm);
-       return ret;
-}
-
-static int __devexit s3c_pwm_remove(struct platform_device *pdev)
-{
-       struct pwm_device *pwm = platform_get_drvdata(pdev);
-
-       clk_disable(pwm->clk_div);
-       clk_disable(pwm->clk);
-       clk_put(pwm->clk_div);
-       clk_put(pwm->clk);
-       kfree(pwm);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state)
-{
-       struct pwm_device *pwm = platform_get_drvdata(pdev);
-
-       /* No one preserve these values during suspend so reset them
-        * Otherwise driver leaves PWM unconfigured if same values
-        * passed to pwm_config
-        */
-       pwm->period_ns = 0;
-       pwm->duty_ns = 0;
-
-       return 0;
-}
-
-static int s3c_pwm_resume(struct platform_device *pdev)
-{
-       struct pwm_device *pwm = platform_get_drvdata(pdev);
-       unsigned long tcon;
-
-       /* Restore invertion */
-       tcon = __raw_readl(S3C2410_TCON);
-       tcon |= pwm_tcon_invert(pwm);
-       __raw_writel(tcon, S3C2410_TCON);
-
-       return 0;
-}
-
-#else
-#define s3c_pwm_suspend NULL
-#define s3c_pwm_resume NULL
-#endif
-
-static struct platform_driver s3c_pwm_driver = {
-       .driver         = {
-               .name   = "s3c24xx-pwm",
-               .owner  = THIS_MODULE,
-       },
-       .probe          = s3c_pwm_probe,
-       .remove         = __devexit_p(s3c_pwm_remove),
-       .suspend        = s3c_pwm_suspend,
-       .resume         = s3c_pwm_resume,
-};
-
-static int __init pwm_init(void)
-{
-       int ret;
-
-       clk_scaler[0] = clk_get(NULL, "pwm-scaler0");
-       clk_scaler[1] = clk_get(NULL, "pwm-scaler1");
-
-       if (IS_ERR(clk_scaler[0]) || IS_ERR(clk_scaler[1])) {
-               printk(KERN_ERR "%s: failed to get scaler clocks\n", __func__);
-               return -EINVAL;
-       }
-
-       ret = platform_driver_register(&s3c_pwm_driver);
-       if (ret)
-               printk(KERN_ERR "%s: failed to add pwm driver\n", __func__);
-
-       return ret;
-}
-
-arch_initcall(pwm_init);
index 2bc6b54460a80245db72a745e75de6237ae11405..eb6590ded40dfdaec09b08a7eaf415b4211733ee 100644 (file)
@@ -14,8 +14,8 @@
 #ifndef __PLAT_PL080_H
 #define __PLAT_PL080_H
 
-struct pl08x_dma_chan;
-int pl080_get_signal(struct pl08x_dma_chan *ch);
-void pl080_put_signal(struct pl08x_dma_chan *ch);
+struct pl08x_channel_data;
+int pl080_get_signal(const struct pl08x_channel_data *cd);
+void pl080_put_signal(const struct pl08x_channel_data *cd, int signal);
 
 #endif /* __PLAT_PL080_H */
index 12cf27f935f97372854cc1a0bd2c41251971499f..cfa1199d0f4a86152fddfae9f83d3074c330e3a8 100644 (file)
@@ -27,9 +27,8 @@ struct {
        unsigned char val;
 } signals[16] = {{0, 0}, };
 
-int pl080_get_signal(struct pl08x_dma_chan *ch)
+int pl080_get_signal(const struct pl08x_channel_data *cd)
 {
-       const struct pl08x_channel_data *cd = ch->cd;
        unsigned int signal = cd->min_signal, val;
        unsigned long flags;
 
@@ -63,18 +62,17 @@ int pl080_get_signal(struct pl08x_dma_chan *ch)
        return signal;
 }
 
-void pl080_put_signal(struct pl08x_dma_chan *ch)
+void pl080_put_signal(const struct pl08x_channel_data *cd, int signal)
 {
-       const struct pl08x_channel_data *cd = ch->cd;
        unsigned long flags;
 
        spin_lock_irqsave(&lock, flags);
 
        /* if signal is not used */
-       if (!signals[cd->min_signal].busy)
+       if (!signals[signal].busy)
                BUG();
 
-       signals[cd->min_signal].busy--;
+       signals[signal].busy--;
 
        spin_unlock_irqrestore(&lock, flags);
 }
index 4fa9903b83cf5dbb54a15623ec45fd00552d8521..cc926c98598141a2fbcdb762148ec17782982d06 100644 (file)
@@ -7,18 +7,20 @@
  * 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.
- *
- * Basic entry code, called from the kernel's undefined instruction trap.
- *  r0  = faulted instruction
- *  r5  = faulted PC+4
- *  r9  = successful return
- *  r10 = thread_info structure
- *  lr  = failure return
  */
 #include <asm/thread_info.h>
 #include <asm/vfpmacros.h>
 #include "../kernel/entry-header.S"
 
+@ VFP entry point.
+@
+@  r0  = instruction opcode (32-bit ARM or two 16-bit Thumb)
+@  r2  = PC value to resume execution after successful emulation
+@  r9  = normal "successful" return address
+@  r10 = this threads thread_info structure
+@  lr  = unrecognised instruction return address
+@  IRQs disabled.
+@
 ENTRY(do_vfp)
 #ifdef CONFIG_PREEMPT
        ldr     r4, [r10, #TI_PREEMPT]  @ get preempt count
index 2d30c7f6edd32ddd5b93e6b8356ae679ce983041..ea0349f6358658065b52aa1473e877ee4fa8f5ba 100644 (file)
@@ -16,6 +16,7 @@
  */
 #include <asm/thread_info.h>
 #include <asm/vfpmacros.h>
+#include <linux/kern_levels.h>
 #include "../kernel/entry-header.S"
 
        .macro  DBGSTR, str
@@ -24,7 +25,7 @@
        add     r0, pc, #4
        bl      printk
        b       1f
-       .asciz  "<7>VFP: \str\n"
+       .asciz  KERN_DEBUG "VFP: \str\n"
        .balign 4
 1:     ldmfd   sp!, {r0-r3, ip, lr}
 #endif
@@ -37,7 +38,7 @@
        add     r0, pc, #4
        bl      printk
        b       1f
-       .asciz  "<7>VFP: \str\n"
+       .asciz  KERN_DEBUG "VFP: \str\n"
        .balign 4
 1:     ldmfd   sp!, {r0-r3, ip, lr}
 #endif
@@ -52,7 +53,7 @@
        add     r0, pc, #4
        bl      printk
        b       1f
-       .asciz  "<7>VFP: \str\n"
+       .asciz  KERN_DEBUG "VFP: \str\n"
        .balign 4
 1:     ldmfd   sp!, {r0-r3, ip, lr}
 #endif
 
 @ VFP hardware support entry point.
 @
-@  r0  = faulted instruction
-@  r2  = faulted PC+4
-@  r9  = successful return
+@  r0  = instruction opcode (32-bit ARM or two 16-bit Thumb)
+@  r2  = PC value to resume execution after successful emulation
+@  r9  = normal "successful" return address
 @  r10 = vfp_state union
 @  r11 = CPU number
-@  lr  = failure return
-
+@  lr  = unrecognised instruction return address
+@  IRQs enabled.
 ENTRY(vfp_support_entry)
        DBGSTR3 "instr %08x pc %08x state %p", r0, r2, r10
 
@@ -161,9 +162,12 @@ vfp_hw_state_valid:
                                        @ exception before retrying branch
                                        @ out before setting an FPEXC that
                                        @ stops us reading stuff
-       VFPFMXR FPEXC, r1               @ restore FPEXC last
-       sub     r2, r2, #4
-       str     r2, [sp, #S_PC]         @ retry the instruction
+       VFPFMXR FPEXC, r1               @ Restore FPEXC last
+       sub     r2, r2, #4              @ Retry current instruction - if Thumb
+       str     r2, [sp, #S_PC]         @ mode it's two 16-bit instructions,
+                                       @ else it's one 32-bit instruction, so
+                                       @ always subtract 4 from the following
+                                       @ instruction address.
 #ifdef CONFIG_PREEMPT
        get_thread_info r10
        ldr     r4, [r10, #TI_PREEMPT]  @ get preempt count
index 586961929e964ce63d70987c0c7c36184aff4db8..fb849d044bde9b231d481201c985f84cc37c0a3d 100644 (file)
@@ -457,10 +457,16 @@ static int vfp_pm_suspend(void)
 
                /* disable, just in case */
                fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
+       } else if (vfp_current_hw_state[ti->cpu]) {
+#ifndef CONFIG_SMP
+               fmxr(FPEXC, fpexc | FPEXC_EN);
+               vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc);
+               fmxr(FPEXC, fpexc);
+#endif
        }
 
        /* clear any information we had about last context state */
-       memset(vfp_current_hw_state, 0, sizeof(vfp_current_hw_state));
+       vfp_current_hw_state[ti->cpu] = NULL;
 
        return 0;
 }
index 71d38c76726cbb7acfd525dc78889724af31e07f..5ade51c8a87fbf5cd99d319ddbe55feef8fbe6c7 100644 (file)
@@ -12,6 +12,7 @@ config AVR32
        select HARDIRQS_SW_RESEND
        select GENERIC_IRQ_SHOW
        select ARCH_HAVE_CUSTOM_GPIO_H
+       select ARCH_WANT_IPC_PARSE_VERSION
        select ARCH_HAVE_NMI_SAFE_CMPXCHG
        select GENERIC_CLOCKEVENTS
        help
index dc5263321480edf35264d7fb0e0ac65da679823f..6c80aba7bf961afe82f42eba5969b5592eba8fec 100644 (file)
@@ -97,7 +97,7 @@ static struct atmel_nand_data atstk1006_nand_data __initdata = {
        .enable_pin     = GPIO_PIN_PB(29),
        .ecc_mode       = NAND_ECC_SOFT,
        .parts          = nand_partitions,
-       .num_parts      = ARRAY_SIZE(num_partitions),
+       .num_parts      = ARRAY_SIZE(nand_partitions),
 };
 #endif
 
index f714544e5560ce2fd0033f63b22480a0eef440d1..1358e366f4be93bff867b9b54cd68d713d2ec3f0 100644 (file)
 /* SMP stuff */
 #define __IGNORE_getcpu
 
-#define __ARCH_WANT_IPC_PARSE_VERSION
 #define __ARCH_WANT_STAT64
 #define __ARCH_WANT_SYS_ALARM
 #define __ARCH_WANT_SYS_GETHOSTNAME
index f7040a1e399f0acd24a0cd51a17940e5ca73dfc8..b92e60958617eb25ff90481989f494c3625f5a98 100644 (file)
@@ -61,10 +61,10 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
        const struct exception_table_entry *fixup;
        unsigned long address;
        unsigned long page;
-       int writeaccess;
        long signr;
        int code;
        int fault;
+       unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
 
        if (notify_page_fault(regs, ecr))
                return;
@@ -86,6 +86,7 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
 
        local_irq_enable();
 
+retry:
        down_read(&mm->mmap_sem);
 
        vma = find_vma(mm, address);
@@ -104,7 +105,6 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
         */
 good_area:
        code = SEGV_ACCERR;
-       writeaccess = 0;
 
        switch (ecr) {
        case ECR_PROTECTION_X:
@@ -121,7 +121,7 @@ good_area:
        case ECR_TLB_MISS_W:
                if (!(vma->vm_flags & VM_WRITE))
                        goto bad_area;
-               writeaccess = 1;
+               flags |= FAULT_FLAG_WRITE;
                break;
        default:
                panic("Unhandled case %lu in do_page_fault!", ecr);
@@ -132,7 +132,11 @@ good_area:
         * sure we exit gracefully rather than endlessly redo the
         * fault.
         */
-       fault = handle_mm_fault(mm, vma, address, writeaccess ? FAULT_FLAG_WRITE : 0);
+       fault = handle_mm_fault(mm, vma, address, flags);
+
+       if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
+               return;
+
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
@@ -140,10 +144,23 @@ good_area:
                        goto do_sigbus;
                BUG();
        }
-       if (fault & VM_FAULT_MAJOR)
-               tsk->maj_flt++;
-       else
-               tsk->min_flt++;
+
+       if (flags & FAULT_FLAG_ALLOW_RETRY) {
+               if (fault & VM_FAULT_MAJOR)
+                       tsk->maj_flt++;
+               else
+                       tsk->min_flt++;
+               if (fault & VM_FAULT_RETRY) {
+                       flags &= ~FAULT_FLAG_ALLOW_RETRY;
+
+                       /*
+                        * No need to up_read(&mm->mmap_sem) as we would have
+                        * already released it in __lock_page_or_retry() in
+                        * mm/filemap.c.
+                        */
+                       goto retry;
+               }
+       }
 
        up_read(&mm->mmap_sem);
        return;
index 9b765107e15cf8a17cc4803dfabb1c9ada64b337..f34861920634d15c9de2b1149aa8562a767e64f1 100644 (file)
@@ -33,6 +33,7 @@ config BLACKFIN
        select HAVE_PERF_EVENTS
        select ARCH_HAVE_CUSTOM_GPIO_H
        select ARCH_WANT_OPTIONAL_GPIOLIB
+       select ARCH_WANT_IPC_PARSE_VERSION
        select HAVE_GENERIC_HARDIRQS
        select GENERIC_ATOMIC64
        select GENERIC_IRQ_PROBE
@@ -1002,16 +1003,6 @@ config BFIN_GPTIMERS
          To compile this driver as a module, choose M here: the module
          will be called gptimers.
 
-config HAVE_PWM
-       tristate "Enable PWM API support"
-       depends on BFIN_GPTIMERS
-       help
-         Enable support for the Pulse Width Modulation framework (as
-         found in linux/pwm.h).
-
-         To compile this driver as a module, choose M here: the module
-         will be called pwm.
-
 choice
        prompt "Uncached DMA region"
        default DMA_UNCACHED_1M
index 3287222cba34f12889c464c5b2cb0970542dba61..5b2a0748d7d3e8b215c0d2aba4a3df3319743858 100644 (file)
 #define __IGNORE_getcpu
 
 #ifdef __KERNEL__
-#define __ARCH_WANT_IPC_PARSE_VERSION
 #define __ARCH_WANT_STAT64
 #define __ARCH_WANT_SYS_ALARM
 #define __ARCH_WANT_SYS_GETHOSTNAME
index 08e6625106be52991b79fceef5422df52f159ec5..735f24e074259b88c8cdd0005cf3ddddcd9175fe 100644 (file)
@@ -21,7 +21,6 @@ obj-$(CONFIG_FUNCTION_TRACER)        += ftrace-entry.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER)  += ftrace.o
 CFLAGS_REMOVE_ftrace.o = -pg
 
-obj-$(CONFIG_HAVE_PWM)               += pwm.o
 obj-$(CONFIG_IPIPE)                  += ipipe.o
 obj-$(CONFIG_BFIN_GPTIMERS)          += gptimers.o
 obj-$(CONFIG_CPLB_INFO)              += cplbinfo.o
diff --git a/arch/blackfin/kernel/pwm.c b/arch/blackfin/kernel/pwm.c
deleted file mode 100644 (file)
index 33f5942..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Blackfin Pulse Width Modulation (PWM) core
- *
- * Copyright (c) 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/pwm.h>
-#include <linux/slab.h>
-
-#include <asm/gptimers.h>
-#include <asm/portmux.h>
-
-struct pwm_device {
-       unsigned id;
-       unsigned short pin;
-};
-
-static const unsigned short pwm_to_gptimer_per[] = {
-       P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR4, P_TMR5,
-       P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11,
-};
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-       struct pwm_device *pwm;
-       int ret;
-
-       /* XXX: pwm_id really should be unsigned */
-       if (pwm_id < 0)
-               return NULL;
-
-       pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
-       if (!pwm)
-               return pwm;
-
-       pwm->id = pwm_id;
-       if (pwm->id >= ARRAY_SIZE(pwm_to_gptimer_per))
-               goto err;
-
-       pwm->pin = pwm_to_gptimer_per[pwm->id];
-       ret = peripheral_request(pwm->pin, label);
-       if (ret)
-               goto err;
-
-       return pwm;
- err:
-       kfree(pwm);
-       return NULL;
-}
-EXPORT_SYMBOL(pwm_request);
-
-void pwm_free(struct pwm_device *pwm)
-{
-       peripheral_free(pwm->pin);
-       kfree(pwm);
-}
-EXPORT_SYMBOL(pwm_free);
-
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
-{
-       unsigned long period, duty;
-       unsigned long long val;
-
-       if (duty_ns < 0 || duty_ns > period_ns)
-               return -EINVAL;
-
-       val = (unsigned long long)get_sclk() * period_ns;
-       do_div(val, NSEC_PER_SEC);
-       period = val;
-
-       val = (unsigned long long)period * duty_ns;
-       do_div(val, period_ns);
-       duty = period - val;
-
-       if (duty >= period)
-               duty = period - 1;
-
-       set_gptimer_config(pwm->id, TIMER_MODE_PWM | TIMER_PERIOD_CNT);
-       set_gptimer_pwidth(pwm->id, duty);
-       set_gptimer_period(pwm->id, period);
-
-       return 0;
-}
-EXPORT_SYMBOL(pwm_config);
-
-int pwm_enable(struct pwm_device *pwm)
-{
-       enable_gptimer(pwm->id);
-       return 0;
-}
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
-{
-       disable_gptimer(pwm->id);
-}
-EXPORT_SYMBOL(pwm_disable);
index bb344650a14f255517487a3a06342992801e8237..e92215428a37e315d0b5df9f962df8e4c8090dcc 100644 (file)
@@ -42,6 +42,7 @@ config CRIS
        select HAVE_IDE
        select GENERIC_ATOMIC64
        select HAVE_GENERIC_HARDIRQS
+       select ARCH_WANT_IPC_PARSE_VERSION
        select GENERIC_IRQ_SHOW
        select GENERIC_IOMAP
        select GENERIC_SMP_IDLE_THREAD if ETRAX_ARCH_V32
index f921b8b0f97e6699cca03f3cd90af305dc647508..51873a446f87e870f44db25c3dcfd2d1522eb0d2 100644 (file)
 
 #include <arch/unistd.h>
 
-#define __ARCH_WANT_IPC_PARSE_VERSION
 #define __ARCH_WANT_OLD_READDIR
 #define __ARCH_WANT_OLD_STAT
 #define __ARCH_WANT_STAT64
index a685910d2d5ce562f36f8f4ae02811390600682c..971c0a19facb694706f68722645aa5101afbeea6 100644 (file)
@@ -9,6 +9,7 @@ config FRV
        select GENERIC_IRQ_SHOW
        select ARCH_HAVE_NMI_SAFE_CMPXCHG
        select GENERIC_CPU_DEVICES
+       select ARCH_WANT_IPC_PARSE_VERSION
 
 config ZONE_DMA
        bool
diff --git a/arch/frv/include/asm/cpumask.h b/arch/frv/include/asm/cpumask.h
deleted file mode 100644 (file)
index d999c20..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _ASM_CPUMASK_H
-#define _ASM_CPUMASK_H
-
-#include <asm-generic/cpumask.h>
-
-#endif /* _ASM_CPUMASK_H */
index a569dff7cd590f0936fce6a4b5e03e99325b8674..67f23a311db6e3d5a55ef88b3243c7c01705b469 100644 (file)
 
 #define NR_syscalls 338
 
-#define __ARCH_WANT_IPC_PARSE_VERSION
 /* #define __ARCH_WANT_OLD_READDIR */
 #define __ARCH_WANT_OLD_STAT
 #define __ARCH_WANT_STAT64
index 4531c830d20b708b6dce2ce6bf736dabdf14c4de..f0e52943f9238b17b075d6049f313be4365170c2 100644 (file)
  */
 
 #include <linux/linkage.h>
+#include <linux/kern_levels.h>
 #include <asm/unistd.h>
 
 #define CLONE_VM       0x00000100      /* set if VM shared between processes */
-#define        KERN_ERR        "<3>"
 
        .section .rodata
 kernel_thread_emsg:
index 56e890df5053605a8eb7a66514eb096d0e110b2d..5e8a0d9a09ce0035e424dc0334370607f306deb9 100644 (file)
@@ -3,6 +3,7 @@ config H8300
        default y
        select HAVE_IDE
        select HAVE_GENERIC_HARDIRQS
+       select ARCH_WANT_IPC_PARSE_VERSION
        select GENERIC_IRQ_SHOW
        select GENERIC_CPU_DEVICES
 
index 718511303b4e15d3ddbfbeb7b6c48c1b3a48f0bb..5cd882801d7980ae47455febe80b30a8cd4b27b0 100644 (file)
 
 #define NR_syscalls 321
 
-#define __ARCH_WANT_IPC_PARSE_VERSION
 #define __ARCH_WANT_OLD_READDIR
 #define __ARCH_WANT_OLD_STAT
 #define __ARCH_WANT_STAT64
index 9aa17f1917eaebd9612e2f09e0b9d59b76426b7d..06906427c0ac238484af385b31386f0f09ff3ad5 100644 (file)
@@ -7,7 +7,6 @@ header-y += user.h
 generic-y += auxvec.h
 generic-y += bug.h
 generic-y += bugs.h
-generic-y += cpumask.h
 generic-y += cputime.h
 generic-y += current.h
 generic-y += device.h
@@ -23,7 +22,6 @@ generic-y += ioctl.h
 generic-y += ioctls.h
 generic-y += iomap.h
 generic-y += ipcbuf.h
-generic-y += ipc.h
 generic-y += irq_regs.h
 generic-y += kdebug.h
 generic-y += kmap_types.h
index 8186ec5ea15168a31a1a876b8a7b52ec7c8f4616..310cf5781fad2438a6ab24845724364bed31c7e4 100644 (file)
@@ -126,6 +126,7 @@ config AUDIT_ARCH
 
 menuconfig PARAVIRT_GUEST
        bool "Paravirtualized guest support"
+       depends on BROKEN
        help
          Say Y here to get to see options related to running Linux under
          various hypervisors.  This option alone does not add any kernel code.
@@ -138,8 +139,6 @@ config PARAVIRT
        bool "Enable paravirtualization code"
        depends on PARAVIRT_GUEST
        default y
-       bool
-       default y
        help
          This changes the kernel so it can modify itself when it is run
          under a hypervisor, potentially improving performance significantly
index 7d9116600a3615d90333ddca4f49dff356b510f1..6e6fe1839f5d57206ff5d0bc4f38b2c3a4c98f8c 100644 (file)
@@ -17,8 +17,8 @@
 #include <asm/intrinsics.h>
 
 
-#define ATOMIC_INIT(i)         ((atomic_t) { (i) })
-#define ATOMIC64_INIT(i)       ((atomic64_t) { (i) })
+#define ATOMIC_INIT(i)         { (i) }
+#define ATOMIC64_INIT(i)       { (i) }
 
 #define atomic_read(v)         (*(volatile int *)&(v)->counter)
 #define atomic64_read(v)       (*(volatile long *)&(v)->counter)
index 367d299d99384e1448e9376b1c899af149e0f599..2d1ad4b11a85a9e9e4083c67f9c30ccf921659fa 100644 (file)
@@ -120,7 +120,7 @@ extern void machvec_tlb_migrate_finish (struct mm_struct *);
 # ifdef MACHVEC_PLATFORM_HEADER
 #  include MACHVEC_PLATFORM_HEADER
 # else
-#  define platform_name                ia64_mv.name
+#  define ia64_platform_name   ia64_mv.name
 #  define platform_setup       ia64_mv.setup
 #  define platform_cpu_init    ia64_mv.cpu_init
 #  define platform_irq_init    ia64_mv.irq_init
index 8a0752f40987ebae6cdaf9ccb411bc133de43017..1f7403a2fbee03407f767a6571392b7147793684 100644 (file)
@@ -10,7 +10,7 @@ extern ia64_mv_setup_t dig_setup;
  * platform's machvec structure.  When compiling a non-generic kernel,
  * the macros are used directly.
  */
-#define platform_name          "dig"
+#define ia64_platform_name     "dig"
 #define platform_setup         dig_setup
 
 #endif /* _ASM_IA64_MACHVEC_DIG_h */
index 6ab1de5c45efe74b9e86e542f6b066578e2aa751..44308b4c3f6e4cbe0635038f2bec0fc021d9eaf0 100644 (file)
@@ -11,7 +11,7 @@ extern ia64_mv_dma_init                       pci_iommu_alloc;
  * platform's machvec structure.  When compiling a non-generic kernel,
  * the macros are used directly.
  */
-#define platform_name                          "dig_vtd"
+#define ia64_platform_name                     "dig_vtd"
 #define platform_setup                         dig_setup
 #define platform_dma_init                      pci_iommu_alloc
 
index cf72fc87fdfed8e172235d5a436a03c188148759..e757112793664d3df43ab6faf68605efb4e8bd0c 100644 (file)
@@ -11,7 +11,7 @@ extern ia64_mv_irq_init_t hpsim_irq_init;
  * platform's machvec structure.  When compiling a non-generic kernel,
  * the macros are used directly.
  */
-#define platform_name          "hpsim"
+#define ia64_platform_name     "hpsim"
 #define platform_setup         hpsim_setup
 #define platform_irq_init      hpsim_irq_init
 
index 3bd83d78a412047099be119a2fa1b3094d767f47..c74d3159e9ebcfaa3ae46e79b67d1e5c45046f6c 100644 (file)
@@ -11,7 +11,7 @@ extern ia64_mv_dma_init                       sba_dma_init;
  * platform's machvec structure.  When compiling a non-generic kernel,
  * the macros are used directly.
  */
-#define platform_name                          "hpzx1"
+#define ia64_platform_name                     "hpzx1"
 #define platform_setup                         dig_setup
 #define platform_dma_init                      sba_dma_init
 
index 1091ac39740c6c407c35bb43e8d511eea2aafb02..906ef621077448d3e6a35243078a2da7ca9f4368 100644 (file)
@@ -11,7 +11,7 @@ extern ia64_mv_dma_get_ops                    hwsw_dma_get_ops;
  * platform's machvec structure.  When compiling a non-generic kernel,
  * the macros are used directly.
  */
-#define platform_name                          "hpzx1_swiotlb"
+#define ia64_platform_name                     "hpzx1_swiotlb"
 #define platform_setup                         dig_setup
 #define platform_dma_init                      machvec_noop
 #define platform_dma_get_ops                   hwsw_dma_get_ops
index f061a30aac42c7b297434d0ec88ae0f5d348f5ee..ece9fa85be8864330b783ff37985f58370e68c43 100644 (file)
@@ -71,7 +71,7 @@ extern ia64_mv_pci_fixup_bus_t                sn_pci_fixup_bus;
  * platform's machvec structure.  When compiling a non-generic kernel,
  * the macros are used directly.
  */
-#define platform_name                  "sn2"
+#define ia64_platform_name             "sn2"
 #define platform_setup                 sn_setup
 #define platform_cpu_init              sn_cpu_init
 #define platform_irq_init              sn_irq_init
index 2931447f3813c2cf34c469614923a84b70dfcb22..2c50853f35ac0037297d0860c26ffb5ecb9b88a3 100644 (file)
@@ -20,7 +20,7 @@ extern ia64_mv_setup_t uv_setup;
  * platform's machvec structure.  When compiling a non-generic kernel,
  * the macros are used directly.
  */
-#define platform_name                  "uv"
+#define ia64_platform_name             "uv"
 #define platform_setup                 uv_setup
 
 #endif /* _ASM_IA64_MACHVEC_UV_H */
index 55f9228056cd6ef4138739ceb0812923d64d94c0..8b8bd0eb39236ee3a251068b4ec4e3e9fc3ee7e9 100644 (file)
@@ -13,7 +13,7 @@ extern ia64_mv_send_ipi_t             xen_platform_send_ipi;
  * platform's machvec structure.  When compiling a non-generic kernel,
  * the macros are used directly.
  */
-#define platform_name                          "xen"
+#define ia64_platform_name                     "xen"
 #define platform_setup                         dig_setup
 #define platform_cpu_init                      xen_cpu_init
 #define platform_irq_init                      xen_irq_init
index 832dd3789e9d02c14471730ddfbbf85d60e816da..944152a5091223798fb3427b2dc5bb56ecaef4be 100644 (file)
@@ -719,7 +719,7 @@ enum idle_boot_override {IDLE_NO_OVERRIDE=0, IDLE_HALT, IDLE_FORCE_MWAIT,
 
 void default_idle(void);
 
-#define ia64_platform_is(x) (strcmp(x, platform_name) == 0)
+#define ia64_platform_is(x) (strcmp(x, ia64_platform_name) == 0)
 
 #endif /* !__ASSEMBLY__ */
 
index 5c3e0888265a80a2fe75d48f6ed8177aeb3aad7f..1034884b77da428c59190f9841229fc3e3cb8f41 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/ioport.h>
 #include <linux/kernel_stat.h>
 #include <linux/ptrace.h>
-#include <linux/random.h>      /* for rand_initialize_irq() */
 #include <linux/signal.h>
 #include <linux/smp.h>
 #include <linux/threads.h>
index d7f558c1e7117bfff75a056d4fee9213c6a4b7fb..3fa4bc536953c9494eabc776b1c77a980cb2e148 100644 (file)
@@ -2353,7 +2353,6 @@ pfm_smpl_buffer_alloc(struct task_struct *task, struct file *filp, pfm_context_t
         */
        insert_vm_struct(mm, vma);
 
-       mm->total_vm  += size >> PAGE_SHIFT;
        vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file,
                                                        vma_pages(vma));
        up_write(&task->mm->mmap_sem);
index df5351e3eed7389881b3b9bf7402a3a553552cf8..e7947528aee61967523a6ff95337de2015583167 100644 (file)
@@ -23,6 +23,7 @@ config KVM
        depends on HAVE_KVM && MODULES && EXPERIMENTAL
        # for device assignment:
        depends on PCI
+       depends on BROKEN
        select PREEMPT_NOTIFIERS
        select ANON_INODES
        select HAVE_KVM_IRQCHIP
index f5959c0c1810422358f48498b29e44dcc743b4b5..eab28e314022e7c4456a2860d99eea70c22c8df0 100644 (file)
@@ -30,8 +30,8 @@ static void __devinit pci_fixup_video(struct pci_dev *pdev)
        struct pci_bus *bus;
        u16 config;
 
-       if ((strcmp(platform_name, "dig") != 0)
-           && (strcmp(platform_name, "hpzx1")  != 0))
+       if ((strcmp(ia64_platform_name, "dig") != 0)
+           && (strcmp(ia64_platform_name, "hpzx1")  != 0))
                return;
        /* Maybe, this machine supports legacy memory map. */
 
index b638d5bfa14d0aa34cc546b179a284f9d3a5cbb6..49498bbb96163ec10477fac83fdd9f21f277b5cc 100644 (file)
@@ -7,6 +7,7 @@ config M32R
        select HAVE_KERNEL_GZIP
        select HAVE_KERNEL_BZIP2
        select HAVE_KERNEL_LZMA
+       select ARCH_WANT_IPC_PARSE_VERSION
        select HAVE_GENERIC_HARDIRQS
        select GENERIC_IRQ_PROBE
        select GENERIC_IRQ_SHOW
index 3e1db561aacc0c5923f0f14f455887f421b8bc71..d5e66a480782e0c121da54bceef6e58a57d12337 100644 (file)
 
 #define NR_syscalls 326
 
-#define __ARCH_WANT_IPC_PARSE_VERSION
 #define __ARCH_WANT_STAT64
 #define __ARCH_WANT_SYS_ALARM
 #define __ARCH_WANT_SYS_GETHOSTNAME
index 1471201282605485d05fe0b0afd024988509d55c..0b0f8b8c4a266571d33fcd61900161d7961d8cea 100644 (file)
@@ -10,6 +10,7 @@ config M68K
        select GENERIC_STRNCPY_FROM_USER if MMU
        select GENERIC_STRNLEN_USER if MMU
        select FPU if MMU
+       select ARCH_WANT_IPC_PARSE_VERSION
        select ARCH_USES_GETTIMEOFFSET if MMU && !COLDFIRE
 
 config RWSEM_GENERIC_SPINLOCK
index ea0b502f845ebf69843b90c5534ab11b36fcd2b9..045cfd6a9e31b72edd428160deedd47b6078a51a 100644 (file)
 
 #define NR_syscalls            347
 
-#define __ARCH_WANT_IPC_PARSE_VERSION
 #define __ARCH_WANT_OLD_READDIR
 #define __ARCH_WANT_OLD_STAT
 #define __ARCH_WANT_STAT64
index 0bf44231aaf91c023e2f2ac1dfa03c0a34da3a24..ab9afcaa7f6a44bad81bb9d80a9c5775fcf7c4f8 100644 (file)
@@ -15,6 +15,7 @@ config MICROBLAZE
        select TRACING_SUPPORT
        select OF
        select OF_EARLY_FLATTREE
+       select ARCH_WANT_IPC_PARSE_VERSION
        select IRQ_DOMAIN
        select HAVE_GENERIC_HARDIRQS
        select GENERIC_IRQ_PROBE
index d20ffbc86bebcc4b319ce1ff52117ae5c029463a..6985e6e9d826a689e6049308e663d06dcfdf070f 100644 (file)
 #ifdef __KERNEL__
 #ifndef __ASSEMBLY__
 
-#define __ARCH_WANT_IPC_PARSE_VERSION
 /* #define __ARCH_WANT_OLD_READDIR */
 /* #define __ARCH_WANT_OLD_STAT */
 #define __ARCH_WANT_STAT64
index 5ce8029f558b17a28107d602e7e3d59712262093..d64786d5e2f361763586c0c5b26221c4b83d3121 100644 (file)
@@ -14,6 +14,7 @@ platforms += jz4740
 platforms += lantiq
 platforms += lasat
 platforms += loongson
+platforms += loongson1
 platforms += mipssim
 platforms += mti-malta
 platforms += netlogic
index b3e10fdd389866659212f947971af764b98dcf3f..331d574df99c8d86ecdce41c9366164fac63f396 100644 (file)
@@ -20,12 +20,14 @@ config MIPS
        select ARCH_BINFMT_ELF_RANDOMIZE_PIE
        select RTC_LIB if !MACH_LOONGSON
        select GENERIC_ATOMIC64 if !64BIT
+       select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
        select HAVE_DMA_ATTRS
        select HAVE_DMA_API_DEBUG
        select HAVE_GENERIC_HARDIRQS
        select GENERIC_IRQ_PROBE
        select GENERIC_IRQ_SHOW
        select HAVE_ARCH_JUMP_LABEL
+       select ARCH_WANT_IPC_PARSE_VERSION
        select IRQ_FORCED_THREADING
        select HAVE_MEMBLOCK
        select HAVE_MEMBLOCK_NODE_MAP
@@ -75,6 +77,7 @@ config AR7
        select SYS_SUPPORTS_ZBOOT_UART16550
        select ARCH_REQUIRE_GPIOLIB
        select VLYNQ
+       select HAVE_CLK
        help
          Support for the Texas Instruments AR7 System-on-a-Chip
          family: TNETD7100, 7200 and 7300.
@@ -122,6 +125,7 @@ config BCM63XX
        select SYS_HAS_EARLY_PRINTK
        select SWAP_IO_SPACE
        select ARCH_REQUIRE_GPIOLIB
+       select HAVE_CLK
        help
         Support for BCM63XX based boards
 
@@ -209,6 +213,7 @@ config MACH_JZ4740
        select SYS_HAS_CPU_MIPS32_R1
        select SYS_SUPPORTS_32BIT_KERNEL
        select SYS_SUPPORTS_LITTLE_ENDIAN
+       select SYS_SUPPORTS_ZBOOT_UART16550
        select DMA_NONCOHERENT
        select IRQ_CPU
        select GENERIC_GPIO
@@ -264,6 +269,16 @@ config MACH_LOONGSON
          Chinese Academy of Sciences (CAS) in the People's Republic
          of China. The chief architect is Professor Weiwu Hu.
 
+config MACH_LOONGSON1
+       bool "Loongson 1 family of machines"
+       select SYS_SUPPORTS_ZBOOT
+       help
+         This enables support for the Loongson 1 based machines.
+
+         Loongson 1 is a family of 32-bit MIPS-compatible SoCs developed by
+         the ICT (Institute of Computing Technology) and the Chinese Academy
+         of Sciences.
+
 config MIPS_MALTA
        bool "MIPS Malta board"
        select ARCH_MAY_HAVE_PC_FDC
@@ -787,6 +802,8 @@ config NLM_XLR_BOARD
        select ZONE_DMA if 64BIT
        select SYNC_R4K
        select SYS_HAS_EARLY_PRINTK
+       select USB_ARCH_HAS_OHCI if USB_SUPPORT
+       select USB_ARCH_HAS_EHCI if USB_SUPPORT
        help
          Support for systems based on Netlogic XLR and XLS processors.
          Say Y here if you have a XLR or XLS based board.
@@ -799,7 +816,6 @@ config NLM_XLP_BOARD
        select SYS_HAS_CPU_XLP
        select SYS_SUPPORTS_SMP
        select HW_HAS_PCI
-       select SWAP_IO_SPACE
        select SYS_SUPPORTS_32BIT_KERNEL
        select SYS_SUPPORTS_64BIT_KERNEL
        select 64BIT_PHYS_ADDR
@@ -836,6 +852,7 @@ source "arch/mips/txx9/Kconfig"
 source "arch/mips/vr41xx/Kconfig"
 source "arch/mips/cavium-octeon/Kconfig"
 source "arch/mips/loongson/Kconfig"
+source "arch/mips/loongson1/Kconfig"
 source "arch/mips/netlogic/Kconfig"
 
 endmenu
@@ -1217,6 +1234,14 @@ config CPU_LOONGSON2F
          have a similar programming interface with FPGA northbridge used in
          Loongson2E.
 
+config CPU_LOONGSON1B
+       bool "Loongson 1B"
+       depends on SYS_HAS_CPU_LOONGSON1B
+       select CPU_LOONGSON1
+       help
+         The Loongson 1B is a 32-bit SoC, which implements the MIPS32
+         release 2 instruction set.
+
 config CPU_MIPS32_R1
        bool "MIPS32 Release 1"
        depends on SYS_HAS_CPU_MIPS32_R1
@@ -1432,6 +1457,8 @@ config CPU_CAVIUM_OCTEON
        select WEAK_ORDERING
        select CPU_SUPPORTS_HIGHMEM
        select CPU_SUPPORTS_HUGEPAGES
+       select LIBFDT
+       select USE_OF
        help
          The Cavium Octeon processor is a highly integrated chip containing
          many ethernet hardware widgets for networking tasks. The processor
@@ -1544,6 +1571,14 @@ config CPU_LOONGSON2
        select CPU_SUPPORTS_64BIT_KERNEL
        select CPU_SUPPORTS_HIGHMEM
 
+config CPU_LOONGSON1
+       bool
+       select CPU_MIPS32
+       select CPU_MIPSR2
+       select CPU_HAS_PREFETCH
+       select CPU_SUPPORTS_32BIT_KERNEL
+       select CPU_SUPPORTS_HIGHMEM
+
 config CPU_BMIPS
        bool
        select CPU_MIPS32
@@ -1562,6 +1597,9 @@ config SYS_HAS_CPU_LOONGSON2F
        select CPU_SUPPORTS_ADDRWINCFG if 64BIT
        select CPU_SUPPORTS_UNCACHED_ACCELERATED
 
+config SYS_HAS_CPU_LOONGSON1B
+       bool
+
 config SYS_HAS_CPU_MIPS32_R1
        bool
 
@@ -2366,6 +2404,8 @@ config PCI_DOMAINS
 
 source "drivers/pci/Kconfig"
 
+source "drivers/pci/pcie/Kconfig"
+
 #
 # ISA support is now enabled via select.  Too many systems still have the one
 # or other ISA chip on the board that users don't know about so don't expect
index 295f1a95f7451e2bdaa7f5e8171c5dd06622fcaf..99969484c475c7fc7366d12fa50efd495656a9b9 100644 (file)
@@ -81,10 +81,10 @@ static void mtx1_power_off(void)
 
 void __init board_setup(void)
 {
-#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
+#if IS_ENABLED(CONFIG_USB_OHCI_HCD)
        /* Enable USB power switch */
        alchemy_gpio_direction_output(204, 0);
-#endif /* defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) */
+#endif /* IS_ENABLED(CONFIG_USB_OHCI_HCD) */
 
        /* Initialize sys_pinfunc */
        au_writel(SYS_PF_NI2, SYS_PINFUNC);
index 95cb9113b12c8302ee9ea60e56e86af8c36da067..c0f3ce6dcb56d448bcb2184c5fdc2c61498a8d7c 100644 (file)
@@ -334,13 +334,12 @@ static void __init alchemy_setup_macs(int ctype)
        if (alchemy_get_macs(ctype) < 1)
                return;
 
-       macres = kmalloc(sizeof(struct resource) * MAC_RES_COUNT, GFP_KERNEL);
+       macres = kmemdup(au1xxx_eth0_resources[ctype],
+                        sizeof(struct resource) * MAC_RES_COUNT, GFP_KERNEL);
        if (!macres) {
                printk(KERN_INFO "Alchemy: no memory for MAC0 resources\n");
                return;
        }
-       memcpy(macres, au1xxx_eth0_resources[ctype],
-              sizeof(struct resource) * MAC_RES_COUNT);
        au1xxx_eth0_device.resource = macres;
 
        i = prom_get_ethernet_addr(ethaddr);
@@ -356,13 +355,12 @@ static void __init alchemy_setup_macs(int ctype)
        if (alchemy_get_macs(ctype) < 2)
                return;
 
-       macres = kmalloc(sizeof(struct resource) * MAC_RES_COUNT, GFP_KERNEL);
+       macres = kmemdup(au1xxx_eth1_resources[ctype],
+                        sizeof(struct resource) * MAC_RES_COUNT, GFP_KERNEL);
        if (!macres) {
                printk(KERN_INFO "Alchemy: no memory for MAC1 resources\n");
                return;
        }
-       memcpy(macres, au1xxx_eth1_resources[ctype],
-              sizeof(struct resource) * MAC_RES_COUNT);
        au1xxx_eth1_device.resource = macres;
 
        ethaddr[5] += 1;        /* next addr for 2nd MAC */
index 3c37fb3033640e05eb39fa21496a938634e2c38c..c9e747dd9fc229d3ee1e29a4a8f232c576ed79bb 100644 (file)
@@ -2,7 +2,7 @@
 # Alchemy Develboards
 #
 
-obj-y += prom.o bcsr.o platform.o
+obj-y += bcsr.o platform.o
 obj-$(CONFIG_PM)               += pm.o
 obj-$(CONFIG_MIPS_PB1100)      += pb1100.o
 obj-$(CONFIG_MIPS_PB1500)      += pb1500.o
index 1e83ce2e1147fb4e71bc893630849ee147530198..f2039ef2c293615470481733747b7e198d797d75 100644 (file)
@@ -90,10 +90,7 @@ static void bcsr_csc_handler(unsigned int irq, struct irq_desc *d)
        unsigned short bisr = __raw_readw(bcsr_virt + BCSR_REG_INTSTAT);
 
        disable_irq_nosync(irq);
-
-       for ( ; bisr; bisr &= bisr - 1)
-               generic_handle_irq(bcsr_csc_base + __ffs(bisr));
-
+       generic_handle_irq(bcsr_csc_base + __ffs(bisr));
        enable_irq(irq);
 }
 
index cff50d05ddd4136e954917509ffdbedfb6e444c6..78c77a44a31782e3752e0a00a3f95eedb2722124 100644 (file)
@@ -46,7 +46,7 @@ void __init board_setup(void)
        alchemy_gpio1_input_enable();
        udelay(100);
 
-#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
+#if IS_ENABLED(CONFIG_USB_OHCI_HCD)
        {
                u32 pin_func, sys_freqctrl, sys_clksrc;
 
@@ -93,7 +93,7 @@ void __init board_setup(void)
                pin_func |= SYS_PF_USB;
                au_writel(pin_func, SYS_PINFUNC);
        }
-#endif /* defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) */
+#endif /* IS_ENABLED(CONFIG_USB_OHCI_HCD) */
 
        /* Enable sys bus clock divider when IDLE state or no bus activity. */
        au_writel(au_readl(SYS_POWERCTRL) | (0x3 << 5), SYS_POWERCTRL);
index e7b807b3ec51b9ecbdb850349e0646aa14193744..232fee9420000fcc3f57bbd9eea28eb27b69be74 100644 (file)
@@ -53,7 +53,7 @@ void __init board_setup(void)
        alchemy_gpio_direction_input(201);
        alchemy_gpio_direction_input(203);
 
-#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
+#if IS_ENABLED(CONFIG_USB_OHCI_HCD)
 
        /* Zero and disable FREQ2 */
        sys_freqctrl = au_readl(SYS_FREQCTRL0);
@@ -87,7 +87,7 @@ void __init board_setup(void)
        /* 2nd USB port is USB host */
        pin_func |= SYS_PF_USB;
        au_writel(pin_func, SYS_PINFUNC);
-#endif /* defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) */
+#endif /* IS_ENABLED(CONFIG_USB_OHCI_HCD) */
 
 #ifdef CONFIG_PCI
        {
index 621f70afb63a10189751b8123a6ed04b1e5c78c9..f39042e99d0d45626305a34135e4d8d0f9c01ac4 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/pm.h>
 
+#include <asm/bootinfo.h>
 #include <asm/reboot.h>
+#include <asm/mach-au1x00/au1000.h>
 #include <asm/mach-db1x00/bcsr.h>
 
+#include <prom.h>
+
+void __init prom_init(void)
+{
+       unsigned char *memsize_str;
+       unsigned long memsize;
+
+       prom_argc = (int)fw_arg0;
+       prom_argv = (char **)fw_arg1;
+       prom_envp = (char **)fw_arg2;
+
+       prom_init_cmdline();
+       memsize_str = prom_getenv("memsize");
+       if (!memsize_str || kstrtoul(memsize_str, 0, &memsize))
+               memsize = 64 << 20; /* all devboards have at least 64MB RAM */
+
+       add_memory_region(0, memsize, BOOT_MEM_RAM);
+}
+
+void prom_putchar(unsigned char c)
+{
+#ifdef CONFIG_MIPS_DB1300
+       alchemy_uart_putchar(AU1300_UART2_PHYS_ADDR, c);
+#else
+       alchemy_uart_putchar(AU1000_UART0_PHYS_ADDR, c);
+#endif
+}
+
 
 static struct platform_device db1x00_rtc_dev = {
        .name   = "rtc-au1xxx",
diff --git a/arch/mips/alchemy/devboards/prom.c b/arch/mips/alchemy/devboards/prom.c
deleted file mode 100644 (file)
index 93a2210..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Common code used by all Alchemy develboards.
- *
- * Extracted from files which had this to say:
- *
- * Copyright 2000, 2008 MontaVista Software Inc.
- * Author: MontaVista Software, Inc. <source@mvista.com>
- *
- *  This program is free software; you can redistribute  it and/or modify it
- *  under  the terms of  the GNU General  Public License as published by the
- *  Free Software Foundation;  either version 2 of the  License, or (at your
- *  option) any later version.
- *
- *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
- *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
- *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
- *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
- *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
- *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
- *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *  You should have received a copy of the  GNU General Public License along
- *  with this program; if not, write  to the Free Software Foundation, Inc.,
- *  675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <asm/bootinfo.h>
-#include <asm/mach-au1x00/au1000.h>
-#include <prom.h>
-
-#if defined(CONFIG_MIPS_DB1000) || \
-    defined(CONFIG_MIPS_PB1100) || \
-    defined(CONFIG_MIPS_PB1500)
-#define ALCHEMY_BOARD_DEFAULT_MEMSIZE  0x04000000
-
-#else  /* Au1550/Au1200-based develboards */
-#define ALCHEMY_BOARD_DEFAULT_MEMSIZE  0x08000000
-#endif
-
-void __init prom_init(void)
-{
-       unsigned char *memsize_str;
-       unsigned long memsize;
-
-       prom_argc = (int)fw_arg0;
-       prom_argv = (char **)fw_arg1;
-       prom_envp = (char **)fw_arg2;
-
-       prom_init_cmdline();
-       memsize_str = prom_getenv("memsize");
-       if (!memsize_str || strict_strtoul(memsize_str, 0, &memsize))
-               memsize = ALCHEMY_BOARD_DEFAULT_MEMSIZE;
-
-       add_memory_region(0, memsize, BOOT_MEM_RAM);
-}
-
-void prom_putchar(unsigned char c)
-{
-#ifdef CONFIG_MIPS_DB1300
-       alchemy_uart_putchar(AU1300_UART2_PHYS_ADDR, c);
-#else
-       alchemy_uart_putchar(AU1000_UART0_PHYS_ADDR, c);
-#endif
-}
index 6b1b9ad8d857d01df1254657635c641a53a09148..d03e8799d1cf8c09455c60e0029f5371cf67de7c 100644 (file)
@@ -1,6 +1,10 @@
 menu "CPU support"
        depends on BCM63XX
 
+config BCM63XX_CPU_6328
+       bool "support 6328 CPU"
+       select HW_HAS_PCI
+
 config BCM63XX_CPU_6338
        bool "support 6338 CPU"
        select HW_HAS_PCI
index 6dfdc69928accdfc1d062cf9315b04f8bc28a9c2..833af72c852abdbc806362f364026a99a453a4f6 100644 (file)
@@ -1,5 +1,6 @@
 obj-y          += clk.o cpu.o cs.o gpio.o irq.o prom.o setup.o timer.o \
-                  dev-dsp.o dev-enet.o dev-pcmcia.o dev-uart.o dev-wdt.o
+                  dev-dsp.o dev-enet.o dev-flash.o dev-pcmcia.o dev-rng.o \
+                  dev-spi.o dev-uart.o dev-wdt.o
 obj-$(CONFIG_EARLY_PRINTK)     += early_printk.o
 
 obj-y          += boards/
index 2f1773f3fb7a5594872ddc8b604be143f3127a38..feb05258a4d1519368a0e7f2395415a4a87e84b1 100644 (file)
@@ -11,9 +11,6 @@
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/platform_device.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/physmap.h>
 #include <linux/ssb/ssb.h>
 #include <asm/addrspace.h>
 #include <bcm63xx_board.h>
@@ -24,7 +21,9 @@
 #include <bcm63xx_dev_pci.h>
 #include <bcm63xx_dev_enet.h>
 #include <bcm63xx_dev_dsp.h>
+#include <bcm63xx_dev_flash.h>
 #include <bcm63xx_dev_pcmcia.h>
+#include <bcm63xx_dev_spi.h>
 #include <board_bcm963xx.h>
 
 #define PFX    "board_bcm963xx: "
@@ -33,6 +32,48 @@ static struct bcm963xx_nvram nvram;
 static unsigned int mac_addr_used;
 static struct board_info board;
 
+/*
+ * known 6328 boards
+ */
+#ifdef CONFIG_BCM63XX_CPU_6328
+static struct board_info __initdata board_96328avng = {
+       .name                           = "96328avng",
+       .expected_cpu_id                = 0x6328,
+
+       .has_uart0                      = 1,
+       .has_pci                        = 1,
+
+       .leds = {
+               {
+                       .name           = "96328avng::ppp-fail",
+                       .gpio           = 2,
+                       .active_low     = 1,
+               },
+               {
+                       .name           = "96328avng::power",
+                       .gpio           = 4,
+                       .active_low     = 1,
+                       .default_trigger = "default-on",
+               },
+               {
+                       .name           = "96328avng::power-fail",
+                       .gpio           = 8,
+                       .active_low     = 1,
+               },
+               {
+                       .name           = "96328avng::wps",
+                       .gpio           = 9,
+                       .active_low     = 1,
+               },
+               {
+                       .name           = "96328avng::ppp",
+                       .gpio           = 11,
+                       .active_low     = 1,
+               },
+       },
+};
+#endif
+
 /*
  * known 6338 boards
  */
@@ -592,6 +633,9 @@ static struct board_info __initdata board_DWVS0 = {
  * all boards
  */
 static const struct board_info __initdata *bcm963xx_boards[] = {
+#ifdef CONFIG_BCM63XX_CPU_6328
+       &board_96328avng,
+#endif
 #ifdef CONFIG_BCM63XX_CPU_6338
        &board_96338gw,
        &board_96338w,
@@ -709,9 +753,15 @@ void __init board_prom_init(void)
        char cfe_version[32];
        u32 val;
 
-       /* read base address of boot chip select (0) */
-       val = bcm_mpi_readl(MPI_CSBASE_REG(0));
-       val &= MPI_CSBASE_BASE_MASK;
+       /* read base address of boot chip select (0)
+        * 6328 does not have MPI but boots from a fixed address
+        */
+       if (BCMCPU_IS_6328())
+               val = 0x18000000;
+       else {
+               val = bcm_mpi_readl(MPI_CSBASE_REG(0));
+               val &= MPI_CSBASE_BASE_MASK;
+       }
        boot_addr = (u8 *)KSEG1ADDR(val);
 
        /* dump cfe version */
@@ -808,40 +858,6 @@ void __init board_setup(void)
                panic("unexpected CPU for bcm963xx board");
 }
 
-static struct mtd_partition mtd_partitions[] = {
-       {
-               .name           = "cfe",
-               .offset         = 0x0,
-               .size           = 0x40000,
-       }
-};
-
-static const char *bcm63xx_part_types[] = { "bcm63xxpart", NULL };
-
-static struct physmap_flash_data flash_data = {
-       .width                  = 2,
-       .nr_parts               = ARRAY_SIZE(mtd_partitions),
-       .parts                  = mtd_partitions,
-       .part_probe_types       = bcm63xx_part_types,
-};
-
-static struct resource mtd_resources[] = {
-       {
-               .start          = 0,    /* filled at runtime */
-               .end            = 0,    /* filled at runtime */
-               .flags          = IORESOURCE_MEM,
-       }
-};
-
-static struct platform_device mtd_dev = {
-       .name                   = "physmap-flash",
-       .resource               = mtd_resources,
-       .num_resources          = ARRAY_SIZE(mtd_resources),
-       .dev                    = {
-               .platform_data  = &flash_data,
-       },
-};
-
 static struct gpio_led_platform_data bcm63xx_led_data;
 
 static struct platform_device bcm63xx_gpio_leds = {
@@ -855,8 +871,6 @@ static struct platform_device bcm63xx_gpio_leds = {
  */
 int __init board_register_devices(void)
 {
-       u32 val;
-
        if (board.has_uart0)
                bcm63xx_uart_register(0);
 
@@ -890,14 +904,9 @@ int __init board_register_devices(void)
        }
 #endif
 
-       /* read base address of boot chip select (0) */
-       val = bcm_mpi_readl(MPI_CSBASE_REG(0));
-       val &= MPI_CSBASE_BASE_MASK;
-
-       mtd_resources[0].start = val;
-       mtd_resources[0].end = 0x1FFFFFFF;
+       bcm63xx_spi_register();
 
-       platform_device_register(&mtd_dev);
+       bcm63xx_flash_register();
 
        bcm63xx_led_data.num_leds = ARRAY_SIZE(board.leds);
        bcm63xx_led_data.leds = board.leds;
index 9d57c71b7b582cc9c24f664df6ae156bc9752522..1db48adb543afbc4dfa6c73e023bee83aa5d595f 100644 (file)
@@ -120,7 +120,7 @@ static void enetsw_set(struct clk *clk, int enable)
 {
        if (!BCMCPU_IS_6368())
                return;
-       bcm_hwclock_set(CKCTL_6368_ROBOSW_CLK_EN |
+       bcm_hwclock_set(CKCTL_6368_ROBOSW_EN |
                        CKCTL_6368_SWPKT_USB_EN |
                        CKCTL_6368_SWPKT_SAR_EN, enable);
        if (enable) {
@@ -163,7 +163,7 @@ static void usbh_set(struct clk *clk, int enable)
        if (BCMCPU_IS_6348())
                bcm_hwclock_set(CKCTL_6348_USBH_EN, enable);
        else if (BCMCPU_IS_6368())
-               bcm_hwclock_set(CKCTL_6368_USBH_CLK_EN, enable);
+               bcm_hwclock_set(CKCTL_6368_USBH_EN, enable);
 }
 
 static struct clk clk_usbh = {
@@ -181,9 +181,11 @@ static void spi_set(struct clk *clk, int enable)
                mask = CKCTL_6338_SPI_EN;
        else if (BCMCPU_IS_6348())
                mask = CKCTL_6348_SPI_EN;
-       else
-               /* BCMCPU_IS_6358 */
+       else if (BCMCPU_IS_6358())
                mask = CKCTL_6358_SPI_EN;
+       else
+               /* BCMCPU_IS_6368 */
+               mask = CKCTL_6368_SPI_EN;
        bcm_hwclock_set(mask, enable);
 }
 
@@ -199,7 +201,7 @@ static void xtm_set(struct clk *clk, int enable)
        if (!BCMCPU_IS_6368())
                return;
 
-       bcm_hwclock_set(CKCTL_6368_SAR_CLK_EN |
+       bcm_hwclock_set(CKCTL_6368_SAR_EN |
                        CKCTL_6368_SWPKT_SAR_EN, enable);
 
        if (enable) {
@@ -221,6 +223,18 @@ static struct clk clk_xtm = {
        .set    = xtm_set,
 };
 
+/*
+ * IPsec clock
+ */
+static void ipsec_set(struct clk *clk, int enable)
+{
+       bcm_hwclock_set(CKCTL_6368_IPSEC_EN, enable);
+}
+
+static struct clk clk_ipsec = {
+       .set    = ipsec_set,
+};
+
 /*
  * Internal peripheral clock
  */
@@ -278,6 +292,8 @@ struct clk *clk_get(struct device *dev, const char *id)
                return &clk_periph;
        if (BCMCPU_IS_6358() && !strcmp(id, "pcm"))
                return &clk_pcm;
+       if (BCMCPU_IS_6368() && !strcmp(id, "ipsec"))
+               return &clk_ipsec;
        return ERR_PTR(-ENOENT);
 }
 
index 8f0d6c7725ea278eecf0fedca355fb5e33c7e824..a7afb289b15aa03f93d6665ad55969ba97cfe6a5 100644 (file)
@@ -29,6 +29,14 @@ static u16 bcm63xx_cpu_rev;
 static unsigned int bcm63xx_cpu_freq;
 static unsigned int bcm63xx_memory_size;
 
+static const unsigned long bcm6328_regs_base[] = {
+       __GEN_CPU_REGS_TABLE(6328)
+};
+
+static const int bcm6328_irqs[] = {
+       __GEN_CPU_IRQ_TABLE(6328)
+};
+
 static const unsigned long bcm6338_regs_base[] = {
        __GEN_CPU_REGS_TABLE(6338)
 };
@@ -99,6 +107,33 @@ unsigned int bcm63xx_get_memory_size(void)
 static unsigned int detect_cpu_clock(void)
 {
        switch (bcm63xx_get_cpu_id()) {
+       case BCM6328_CPU_ID:
+       {
+               unsigned int tmp, mips_pll_fcvo;
+
+               tmp = bcm_misc_readl(MISC_STRAPBUS_6328_REG);
+               mips_pll_fcvo = (tmp & STRAPBUS_6328_FCVO_MASK)
+                               >> STRAPBUS_6328_FCVO_SHIFT;
+
+               switch (mips_pll_fcvo) {
+               case 0x12:
+               case 0x14:
+               case 0x19:
+                       return 160000000;
+               case 0x1c:
+                       return 192000000;
+               case 0x13:
+               case 0x15:
+                       return 200000000;
+               case 0x1a:
+                       return 384000000;
+               case 0x16:
+                       return 400000000;
+               default:
+                       return 320000000;
+               }
+
+       }
        case BCM6338_CPU_ID:
                /* BCM6338 has a fixed 240 Mhz frequency */
                return 240000000;
@@ -170,6 +205,9 @@ static unsigned int detect_memory_size(void)
        unsigned int cols = 0, rows = 0, is_32bits = 0, banks = 0;
        u32 val;
 
+       if (BCMCPU_IS_6328())
+               return bcm_ddr_readl(DDR_CSEND_REG) << 24;
+
        if (BCMCPU_IS_6345()) {
                val = bcm_sdram_readl(SDRAM_MBASE_REG);
                return (val * 8 * 1024 * 1024);
@@ -228,17 +266,26 @@ void __init bcm63xx_cpu_init(void)
                bcm63xx_irqs = bcm6345_irqs;
                break;
        case CPU_BMIPS4350:
-               switch (read_c0_prid() & 0xf0) {
-               case 0x10:
+               if ((read_c0_prid() & 0xf0) == 0x10) {
                        expected_cpu_id = BCM6358_CPU_ID;
                        bcm63xx_regs_base = bcm6358_regs_base;
                        bcm63xx_irqs = bcm6358_irqs;
-                       break;
-               case 0x30:
-                       expected_cpu_id = BCM6368_CPU_ID;
-                       bcm63xx_regs_base = bcm6368_regs_base;
-                       bcm63xx_irqs = bcm6368_irqs;
-                       break;
+               } else {
+                       /* all newer chips have the same chip id location */
+                       u16 chip_id = bcm_readw(BCM_6368_PERF_BASE);
+
+                       switch (chip_id) {
+                       case BCM6328_CPU_ID:
+                               expected_cpu_id = BCM6328_CPU_ID;
+                               bcm63xx_regs_base = bcm6328_regs_base;
+                               bcm63xx_irqs = bcm6328_irqs;
+                               break;
+                       case BCM6368_CPU_ID:
+                               expected_cpu_id = BCM6368_CPU_ID;
+                               bcm63xx_regs_base = bcm6368_regs_base;
+                               bcm63xx_irqs = bcm6368_irqs;
+                               break;
+                       }
                }
                break;
        }
index da46d1d3c77cf7e1fc5888e0a2d39e9ce7f24eed..5bb5b154c9bd3cb4aec195f3c1e991b6011d8e96 100644 (file)
@@ -31,7 +31,7 @@ static struct resource voip_dsp_resources[] = {
 
 static struct platform_device bcm63xx_voip_dsp_device = {
        .name           = "bcm63xx-voip-dsp",
-       .id             = 0,
+       .id             = -1,
        .num_resources  = ARRAY_SIZE(voip_dsp_resources),
        .resource       = voip_dsp_resources,
 };
diff --git a/arch/mips/bcm63xx/dev-flash.c b/arch/mips/bcm63xx/dev-flash.c
new file mode 100644 (file)
index 0000000..58371c7
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Broadcom BCM63xx flash registration
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
+ * Copyright (C) 2008 Florian Fainelli <florian@openwrt.org>
+ * Copyright (C) 2012 Jonas Gorski <jonas.gorski@gmail.com>
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
+
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_dev_flash.h>
+#include <bcm63xx_regs.h>
+#include <bcm63xx_io.h>
+
+static struct mtd_partition mtd_partitions[] = {
+       {
+               .name           = "cfe",
+               .offset         = 0x0,
+               .size           = 0x40000,
+       }
+};
+
+static const char *bcm63xx_part_types[] = { "bcm63xxpart", NULL };
+
+static struct physmap_flash_data flash_data = {
+       .width                  = 2,
+       .parts                  = mtd_partitions,
+       .part_probe_types       = bcm63xx_part_types,
+};
+
+static struct resource mtd_resources[] = {
+       {
+               .start          = 0,    /* filled at runtime */
+               .end            = 0,    /* filled at runtime */
+               .flags          = IORESOURCE_MEM,
+       }
+};
+
+static struct platform_device mtd_dev = {
+       .name                   = "physmap-flash",
+       .resource               = mtd_resources,
+       .num_resources          = ARRAY_SIZE(mtd_resources),
+       .dev                    = {
+               .platform_data  = &flash_data,
+       },
+};
+
+static int __init bcm63xx_detect_flash_type(void)
+{
+       u32 val;
+
+       switch (bcm63xx_get_cpu_id()) {
+       case BCM6328_CPU_ID:
+               val = bcm_misc_readl(MISC_STRAPBUS_6328_REG);
+               if (val & STRAPBUS_6328_BOOT_SEL_SERIAL)
+                       return BCM63XX_FLASH_TYPE_SERIAL;
+               else
+                       return BCM63XX_FLASH_TYPE_NAND;
+       case BCM6338_CPU_ID:
+       case BCM6345_CPU_ID:
+       case BCM6348_CPU_ID:
+               /* no way to auto detect so assume parallel */
+               return BCM63XX_FLASH_TYPE_PARALLEL;
+       case BCM6358_CPU_ID:
+               val = bcm_gpio_readl(GPIO_STRAPBUS_REG);
+               if (val & STRAPBUS_6358_BOOT_SEL_PARALLEL)
+                       return BCM63XX_FLASH_TYPE_PARALLEL;
+               else
+                       return BCM63XX_FLASH_TYPE_SERIAL;
+       case BCM6368_CPU_ID:
+               val = bcm_gpio_readl(GPIO_STRAPBUS_REG);
+               switch (val & STRAPBUS_6368_BOOT_SEL_MASK) {
+               case STRAPBUS_6368_BOOT_SEL_NAND:
+                       return BCM63XX_FLASH_TYPE_NAND;
+               case STRAPBUS_6368_BOOT_SEL_SERIAL:
+                       return BCM63XX_FLASH_TYPE_SERIAL;
+               case STRAPBUS_6368_BOOT_SEL_PARALLEL:
+                       return BCM63XX_FLASH_TYPE_PARALLEL;
+               }
+       default:
+               return -EINVAL;
+       }
+}
+
+int __init bcm63xx_flash_register(void)
+{
+       int flash_type;
+       u32 val;
+
+       flash_type = bcm63xx_detect_flash_type();
+
+       switch (flash_type) {
+       case BCM63XX_FLASH_TYPE_PARALLEL:
+               /* read base address of boot chip select (0) */
+               val = bcm_mpi_readl(MPI_CSBASE_REG(0));
+               val &= MPI_CSBASE_BASE_MASK;
+
+               mtd_resources[0].start = val;
+               mtd_resources[0].end = 0x1FFFFFFF;
+
+               return platform_device_register(&mtd_dev);
+       case BCM63XX_FLASH_TYPE_SERIAL:
+               pr_warn("unsupported serial flash detected\n");
+               return -ENODEV;
+       case BCM63XX_FLASH_TYPE_NAND:
+               pr_warn("unsupported NAND flash detected\n");
+               return -ENODEV;
+       default:
+               pr_err("flash detection failed for BCM%x: %d\n",
+                      bcm63xx_get_cpu_id(), flash_type);
+               return -ENODEV;
+       }
+}
diff --git a/arch/mips/bcm63xx/dev-rng.c b/arch/mips/bcm63xx/dev-rng.c
new file mode 100644 (file)
index 0000000..d277b4d
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2011 Florian Fainelli <florian@openwrt.org>
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <bcm63xx_cpu.h>
+
+static struct resource rng_resources[] = {
+       {
+               .start          = -1, /* filled at runtime */
+               .end            = -1, /* filled at runtime */
+               .flags          = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device bcm63xx_rng_device = {
+       .name           = "bcm63xx-rng",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(rng_resources),
+       .resource       = rng_resources,
+};
+
+int __init bcm63xx_rng_register(void)
+{
+       if (!BCMCPU_IS_6368())
+               return -ENODEV;
+
+       rng_resources[0].start = bcm63xx_regset_address(RSET_RNG);
+       rng_resources[0].end = rng_resources[0].start;
+       rng_resources[0].end += RSET_RNG_SIZE - 1;
+
+       return platform_device_register(&bcm63xx_rng_device);
+}
+arch_initcall(bcm63xx_rng_register);
diff --git a/arch/mips/bcm63xx/dev-spi.c b/arch/mips/bcm63xx/dev-spi.c
new file mode 100644 (file)
index 0000000..e39f730
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2009-2011 Florian Fainelli <florian@openwrt.org>
+ * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_dev_spi.h>
+#include <bcm63xx_regs.h>
+
+#ifdef BCMCPU_RUNTIME_DETECT
+/*
+ * register offsets
+ */
+static const unsigned long bcm6338_regs_spi[] = {
+       __GEN_SPI_REGS_TABLE(6338)
+};
+
+static const unsigned long bcm6348_regs_spi[] = {
+       __GEN_SPI_REGS_TABLE(6348)
+};
+
+static const unsigned long bcm6358_regs_spi[] = {
+       __GEN_SPI_REGS_TABLE(6358)
+};
+
+static const unsigned long bcm6368_regs_spi[] = {
+       __GEN_SPI_REGS_TABLE(6368)
+};
+
+const unsigned long *bcm63xx_regs_spi;
+EXPORT_SYMBOL(bcm63xx_regs_spi);
+
+static __init void bcm63xx_spi_regs_init(void)
+{
+       if (BCMCPU_IS_6338())
+               bcm63xx_regs_spi = bcm6338_regs_spi;
+       if (BCMCPU_IS_6348())
+               bcm63xx_regs_spi = bcm6348_regs_spi;
+       if (BCMCPU_IS_6358())
+               bcm63xx_regs_spi = bcm6358_regs_spi;
+       if (BCMCPU_IS_6368())
+               bcm63xx_regs_spi = bcm6368_regs_spi;
+}
+#else
+static __init void bcm63xx_spi_regs_init(void) { }
+#endif
+
+static struct resource spi_resources[] = {
+       {
+               .start          = -1, /* filled at runtime */
+               .end            = -1, /* filled at runtime */
+               .flags          = IORESOURCE_MEM,
+       },
+       {
+               .start          = -1, /* filled at runtime */
+               .flags          = IORESOURCE_IRQ,
+       },
+};
+
+static struct bcm63xx_spi_pdata spi_pdata = {
+       .bus_num                = 0,
+       .num_chipselect         = 8,
+};
+
+static struct platform_device bcm63xx_spi_device = {
+       .name           = "bcm63xx-spi",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(spi_resources),
+       .resource       = spi_resources,
+       .dev            = {
+               .platform_data = &spi_pdata,
+       },
+};
+
+int __init bcm63xx_spi_register(void)
+{
+       struct clk *periph_clk;
+
+       if (BCMCPU_IS_6328() || BCMCPU_IS_6345())
+               return -ENODEV;
+
+       periph_clk = clk_get(NULL, "periph");
+       if (IS_ERR(periph_clk)) {
+               pr_err("unable to get periph clock\n");
+               return -ENODEV;
+       }
+
+       /* Set bus frequency */
+       spi_pdata.speed_hz = clk_get_rate(periph_clk);
+
+       spi_resources[0].start = bcm63xx_regset_address(RSET_SPI);
+       spi_resources[0].end = spi_resources[0].start;
+       spi_resources[1].start = bcm63xx_get_irq_number(IRQ_SPI);
+
+       if (BCMCPU_IS_6338() || BCMCPU_IS_6348()) {
+               spi_resources[0].end += BCM_6338_RSET_SPI_SIZE - 1;
+               spi_pdata.fifo_size = SPI_6338_MSG_DATA_SIZE;
+       }
+
+       if (BCMCPU_IS_6358() || BCMCPU_IS_6368()) {
+               spi_resources[0].end += BCM_6358_RSET_SPI_SIZE - 1;
+               spi_pdata.fifo_size = SPI_6358_MSG_DATA_SIZE;
+       }
+
+       bcm63xx_spi_regs_init();
+
+       return platform_device_register(&bcm63xx_spi_device);
+}
index 3e6c716a4c11b890df34168251c5a611ff56a9af..2a2346a99bcb16d7250dc1c332a3ac339546ccc5 100644 (file)
@@ -21,7 +21,7 @@ static struct resource wdt_resources[] = {
 
 static struct platform_device bcm63xx_wdt_device = {
        .name           = "bcm63xx-wdt",
-       .id             = 0,
+       .id             = -1,
        .num_resources  = ARRAY_SIZE(wdt_resources),
        .resource       = wdt_resources,
 };
index 9a216a451d92fd74228d697ac19ca949a9d11469..18e051ad18a5a4e4f443ac9668045f835d140f8a 100644 (file)
@@ -27,6 +27,17 @@ static void __internal_irq_unmask_32(unsigned int irq) __maybe_unused;
 static void __internal_irq_unmask_64(unsigned int irq) __maybe_unused;
 
 #ifndef BCMCPU_RUNTIME_DETECT
+#ifdef CONFIG_BCM63XX_CPU_6328
+#define irq_stat_reg           PERF_IRQSTAT_6328_REG
+#define irq_mask_reg           PERF_IRQMASK_6328_REG
+#define irq_bits               64
+#define is_ext_irq_cascaded    1
+#define ext_irq_start          (BCM_6328_EXT_IRQ0 - IRQ_INTERNAL_BASE)
+#define ext_irq_end            (BCM_6328_EXT_IRQ3 - IRQ_INTERNAL_BASE)
+#define ext_irq_count          4
+#define ext_irq_cfg_reg1       PERF_EXTIRQ_CFG_REG_6328
+#define ext_irq_cfg_reg2       0
+#endif
 #ifdef CONFIG_BCM63XX_CPU_6338
 #define irq_stat_reg           PERF_IRQSTAT_6338_REG
 #define irq_mask_reg           PERF_IRQMASK_6338_REG
@@ -118,6 +129,16 @@ static void bcm63xx_init_irq(void)
        irq_mask_addr = bcm63xx_regset_address(RSET_PERF);
 
        switch (bcm63xx_get_cpu_id()) {
+       case BCM6328_CPU_ID:
+               irq_stat_addr += PERF_IRQSTAT_6328_REG;
+               irq_mask_addr += PERF_IRQMASK_6328_REG;
+               irq_bits = 64;
+               ext_irq_count = 4;
+               is_ext_irq_cascaded = 1;
+               ext_irq_start = BCM_6328_EXT_IRQ0 - IRQ_INTERNAL_BASE;
+               ext_irq_end = BCM_6328_EXT_IRQ3 - IRQ_INTERNAL_BASE;
+               ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6328;
+               break;
        case BCM6338_CPU_ID:
                irq_stat_addr += PERF_IRQSTAT_6338_REG;
                irq_mask_addr += PERF_IRQMASK_6338_REG;
index 99d7f405cbebf953c2646a842f1221bc7fe58d18..10eaff4580710d4eae4879691502c55ebef3f1d6 100644 (file)
@@ -26,7 +26,9 @@ void __init prom_init(void)
        bcm_wdt_writel(WDT_STOP_2, WDT_CTL_REG);
 
        /* disable all hardware blocks clock for now */
-       if (BCMCPU_IS_6338())
+       if (BCMCPU_IS_6328())
+               mask = CKCTL_6328_ALL_SAFE_EN;
+       else if (BCMCPU_IS_6338())
                mask = CKCTL_6338_ALL_SAFE_EN;
        else if (BCMCPU_IS_6345())
                mask = CKCTL_6345_ALL_SAFE_EN;
index 356b05583e14ca97d7ee9a1968950c41827b1c8f..0e74a13639cd235b47fcbb8070ecab9b01782e2b 100644 (file)
@@ -68,6 +68,9 @@ void bcm63xx_machine_reboot(void)
 
        /* mask and clear all external irq */
        switch (bcm63xx_get_cpu_id()) {
+       case BCM6328_CPU_ID:
+               perf_regs[0] = PERF_EXTIRQ_CFG_REG_6328;
+               break;
        case BCM6338_CPU_ID:
                perf_regs[0] = PERF_EXTIRQ_CFG_REG_6338;
                break;
@@ -95,9 +98,13 @@ void bcm63xx_machine_reboot(void)
                bcm6348_a1_reboot();
 
        printk(KERN_INFO "triggering watchdog soft-reset...\n");
-       reg = bcm_perf_readl(PERF_SYS_PLL_CTL_REG);
-       reg |= SYS_PLL_SOFT_RESET;
-       bcm_perf_writel(reg, PERF_SYS_PLL_CTL_REG);
+       if (BCMCPU_IS_6328()) {
+               bcm_wdt_writel(1, WDT_SOFTRESET_REG);
+       } else {
+               reg = bcm_perf_readl(PERF_SYS_PLL_CTL_REG);
+               reg |= SYS_PLL_SOFT_RESET;
+               bcm_perf_writel(reg, PERF_SYS_PLL_CTL_REG);
+       }
        while (1)
                ;
 }
index 5042d51b0512a087b02b85853aad2d2b38d9d7d6..c2a3fb0ffc873b971c8461257d5e422b91c832e6 100644 (file)
@@ -58,8 +58,12 @@ $(obj)/piggy.o: $(obj)/dummy.o $(obj)/vmlinux.bin.z FORCE
 # Calculate the load address of the compressed kernel image
 hostprogs-y := calc_vmlinuz_load_addr
 
+ifeq ($(CONFIG_MACH_JZ4740),y)
+VMLINUZ_LOAD_ADDRESS := 0x80600000
+else
 VMLINUZ_LOAD_ADDRESS = $(shell $(obj)/calc_vmlinuz_load_addr \
                $(obj)/vmlinux.bin $(VMLINUX_LOAD_ADDRESS))
+endif
 
 vmlinuzobjs-y += $(obj)/piggy.o
 
index c9caaf4fbf60ecb0c4bdedeb2c04647e162ff635..1c7b739b6a1dfcc9383e403307fbca269f3d43a6 100644 (file)
 #define PORT(offset) (CKSEG1ADDR(AR7_REGS_UART0) + (4 * offset))
 #endif
 
+#ifdef CONFIG_MACH_JZ4740
+#define UART0_BASE  0xB0030000
+#define PORT(offset) (UART0_BASE + (4 * offset))
+#endif
+
 #ifndef PORT
 #error please define the serial port address for your own machine
 #endif
diff --git a/arch/mips/cavium-octeon/.gitignore b/arch/mips/cavium-octeon/.gitignore
new file mode 100644 (file)
index 0000000..39c9686
--- /dev/null
@@ -0,0 +1,2 @@
+*.dtb.S
+*.dtb
index 19eb0434269ffc6017bb79da7f00485d2cb13813..bc96e2908f14436616083cf3706fd0dddf718d6a 100644 (file)
@@ -9,9 +9,25 @@
 # Copyright (C) 2005-2009 Cavium Networks
 #
 
+CFLAGS_octeon-platform.o = -I$(src)/../../../scripts/dtc/libfdt
+CFLAGS_setup.o = -I$(src)/../../../scripts/dtc/libfdt
+
 obj-y := cpu.o setup.o serial.o octeon-platform.o octeon-irq.o csrc-octeon.o
 obj-y += dma-octeon.o flash_setup.o
 obj-y += octeon-memcpy.o
 obj-y += executive/
 
 obj-$(CONFIG_SMP)                     += smp.o
+
+DTS_FILES = octeon_3xxx.dts octeon_68xx.dts
+DTB_FILES = $(patsubst %.dts, %.dtb, $(DTS_FILES))
+
+obj-y += $(patsubst %.dts, %.dtb.o, $(DTS_FILES))
+
+$(obj)/%.dtb: $(src)/%.dts FORCE
+       $(call if_changed_dep,dtc)
+
+# Let's keep the .dtb files around in case we want to look at them.
+.SECONDARY:  $(addprefix $(obj)/, $(DTB_FILES))
+
+clean-files += $(DTB_FILES) $(patsubst %.dtb, %.dtb.S, $(DTB_FILES))
diff --git a/arch/mips/cavium-octeon/executive/cvmx-fpa.c b/arch/mips/cavium-octeon/executive/cvmx-fpa.c
deleted file mode 100644 (file)
index ad44b8b..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/***********************license start***************
- * Author: Cavium Networks
- *
- * Contact: support@caviumnetworks.com
- * This file is part of the OCTEON SDK
- *
- * Copyright (c) 2003-2008 Cavium Networks
- *
- * This file 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 file is distributed in the hope that it will be useful, but
- * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
- * NONINFRINGEMENT.  See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this file; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- * or visit http://www.gnu.org/licenses/.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium Networks for more information
- ***********************license end**************************************/
-
-/**
- * @file
- *
- * Support library for the hardware Free Pool Allocator.
- *
- *
- */
-
-#include "cvmx-config.h"
-#include "cvmx.h"
-#include "cvmx-fpa.h"
-#include "cvmx-ipd.h"
-
-/**
- * Current state of all the pools. Use access functions
- * instead of using it directly.
- */
-CVMX_SHARED cvmx_fpa_pool_info_t cvmx_fpa_pool_info[CVMX_FPA_NUM_POOLS];
-
-/**
- * Setup a FPA pool to control a new block of memory. The
- * buffer pointer must be a physical address.
- *
- * @pool:       Pool to initialize
- *                   0 <= pool < 8
- * @name:       Constant character string to name this pool.
- *                   String is not copied.
- * @buffer:     Pointer to the block of memory to use. This must be
- *                   accessible by all processors and external hardware.
- * @block_size: Size for each block controlled by the FPA
- * @num_blocks: Number of blocks
- *
- * Returns 0 on Success,
- *         -1 on failure
- */
-int cvmx_fpa_setup_pool(uint64_t pool, const char *name, void *buffer,
-                       uint64_t block_size, uint64_t num_blocks)
-{
-       char *ptr;
-       if (!buffer) {
-               cvmx_dprintf
-                   ("ERROR: cvmx_fpa_setup_pool: NULL buffer pointer!\n");
-               return -1;
-       }
-       if (pool >= CVMX_FPA_NUM_POOLS) {
-               cvmx_dprintf("ERROR: cvmx_fpa_setup_pool: Illegal pool!\n");
-               return -1;
-       }
-
-       if (block_size < CVMX_FPA_MIN_BLOCK_SIZE) {
-               cvmx_dprintf
-                   ("ERROR: cvmx_fpa_setup_pool: Block size too small.\n");
-               return -1;
-       }
-
-       if (((unsigned long)buffer & (CVMX_FPA_ALIGNMENT - 1)) != 0) {
-               cvmx_dprintf
-                   ("ERROR: cvmx_fpa_setup_pool: Buffer not aligned properly.\n");
-               return -1;
-       }
-
-       cvmx_fpa_pool_info[pool].name = name;
-       cvmx_fpa_pool_info[pool].size = block_size;
-       cvmx_fpa_pool_info[pool].starting_element_count = num_blocks;
-       cvmx_fpa_pool_info[pool].base = buffer;
-
-       ptr = (char *)buffer;
-       while (num_blocks--) {
-               cvmx_fpa_free(ptr, pool, 0);
-               ptr += block_size;
-       }
-       return 0;
-}
-
-/**
- * Shutdown a Memory pool and validate that it had all of
- * the buffers originally placed in it.
- *
- * @pool:   Pool to shutdown
- * Returns Zero on success
- *         - Positive is count of missing buffers
- *         - Negative is too many buffers or corrupted pointers
- */
-uint64_t cvmx_fpa_shutdown_pool(uint64_t pool)
-{
-       uint64_t errors = 0;
-       uint64_t count = 0;
-       uint64_t base = cvmx_ptr_to_phys(cvmx_fpa_pool_info[pool].base);
-       uint64_t finish =
-           base +
-           cvmx_fpa_pool_info[pool].size *
-           cvmx_fpa_pool_info[pool].starting_element_count;
-       void *ptr;
-       uint64_t address;
-
-       count = 0;
-       do {
-               ptr = cvmx_fpa_alloc(pool);
-               if (ptr)
-                       address = cvmx_ptr_to_phys(ptr);
-               else
-                       address = 0;
-               if (address) {
-                       if ((address >= base) && (address < finish) &&
-                           (((address -
-                              base) % cvmx_fpa_pool_info[pool].size) == 0)) {
-                               count++;
-                       } else {
-                               cvmx_dprintf
-                                   ("ERROR: cvmx_fpa_shutdown_pool: Illegal address 0x%llx in pool %s(%d)\n",
-                                    (unsigned long long)address,
-                                    cvmx_fpa_pool_info[pool].name, (int)pool);
-                               errors++;
-                       }
-               }
-       } while (address);
-
-#ifdef CVMX_ENABLE_PKO_FUNCTIONS
-       if (pool == 0)
-               cvmx_ipd_free_ptr();
-#endif
-
-       if (errors) {
-               cvmx_dprintf
-                   ("ERROR: cvmx_fpa_shutdown_pool: Pool %s(%d) started at 0x%llx, ended at 0x%llx, with a step of 0x%llx\n",
-                    cvmx_fpa_pool_info[pool].name, (int)pool,
-                    (unsigned long long)base, (unsigned long long)finish,
-                    (unsigned long long)cvmx_fpa_pool_info[pool].size);
-               return -errors;
-       } else
-               return 0;
-}
-
-uint64_t cvmx_fpa_get_block_size(uint64_t pool)
-{
-       switch (pool) {
-       case 0:
-               return CVMX_FPA_POOL_0_SIZE;
-       case 1:
-               return CVMX_FPA_POOL_1_SIZE;
-       case 2:
-               return CVMX_FPA_POOL_2_SIZE;
-       case 3:
-               return CVMX_FPA_POOL_3_SIZE;
-       case 4:
-               return CVMX_FPA_POOL_4_SIZE;
-       case 5:
-               return CVMX_FPA_POOL_5_SIZE;
-       case 6:
-               return CVMX_FPA_POOL_6_SIZE;
-       case 7:
-               return CVMX_FPA_POOL_7_SIZE;
-       default:
-               return 0;
-       }
-}
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-fpa.c b/arch/mips/cavium-octeon/executive/cvmx-helper-fpa.c
deleted file mode 100644 (file)
index c239e5f..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-/***********************license start***************
- * Author: Cavium Networks
- *
- * Contact: support@caviumnetworks.com
- * This file is part of the OCTEON SDK
- *
- * Copyright (c) 2003-2008 Cavium Networks
- *
- * This file 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 file is distributed in the hope that it will be useful, but
- * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
- * NONINFRINGEMENT.  See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this file; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- * or visit http://www.gnu.org/licenses/.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium Networks for more information
- ***********************license end**************************************/
-
-/**
- * @file
- *
- * Helper functions for FPA setup.
- *
- */
-#include "executive-config.h"
-#include "cvmx-config.h"
-#include "cvmx.h"
-#include "cvmx-bootmem.h"
-#include "cvmx-fpa.h"
-#include "cvmx-helper-fpa.h"
-
-/**
- * Allocate memory for and initialize a single FPA pool.
- *
- * @pool:    Pool to initialize
- * @buffer_size:  Size of buffers to allocate in bytes
- * @buffers: Number of buffers to put in the pool. Zero is allowed
- * @name:    String name of the pool for debugging purposes
- * Returns Zero on success, non-zero on failure
- */
-static int __cvmx_helper_initialize_fpa_pool(int pool, uint64_t buffer_size,
-                                            uint64_t buffers, const char *name)
-{
-       uint64_t current_num;
-       void *memory;
-       uint64_t align = CVMX_CACHE_LINE_SIZE;
-
-       /*
-        * Align the allocation so that power of 2 size buffers are
-        * naturally aligned.
-        */
-       while (align < buffer_size)
-               align = align << 1;
-
-       if (buffers == 0)
-               return 0;
-
-       current_num = cvmx_read_csr(CVMX_FPA_QUEX_AVAILABLE(pool));
-       if (current_num) {
-               cvmx_dprintf("Fpa pool %d(%s) already has %llu buffers. "
-                            "Skipping setup.\n",
-                    pool, name, (unsigned long long)current_num);
-               return 0;
-       }
-
-       memory = cvmx_bootmem_alloc(buffer_size * buffers, align);
-       if (memory == NULL) {
-               cvmx_dprintf("Out of memory initializing fpa pool %d(%s).\n",
-                            pool, name);
-               return -1;
-       }
-       cvmx_fpa_setup_pool(pool, name, memory, buffer_size, buffers);
-       return 0;
-}
-
-/**
- * Allocate memory and initialize the FPA pools using memory
- * from cvmx-bootmem. Specifying zero for the number of
- * buffers will cause that FPA pool to not be setup. This is
- * useful if you aren't using some of the hardware and want
- * to save memory. Use cvmx_helper_initialize_fpa instead of
- * this function directly.
- *
- * @pip_pool: Should always be CVMX_FPA_PACKET_POOL
- * @pip_size: Should always be CVMX_FPA_PACKET_POOL_SIZE
- * @pip_buffers:
- *                 Number of packet buffers.
- * @wqe_pool: Should always be CVMX_FPA_WQE_POOL
- * @wqe_size: Should always be CVMX_FPA_WQE_POOL_SIZE
- * @wqe_entries:
- *                 Number of work queue entries
- * @pko_pool: Should always be CVMX_FPA_OUTPUT_BUFFER_POOL
- * @pko_size: Should always be CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE
- * @pko_buffers:
- *                 PKO Command buffers. You should at minimum have two per
- *                 each PKO queue.
- * @tim_pool: Should always be CVMX_FPA_TIMER_POOL
- * @tim_size: Should always be CVMX_FPA_TIMER_POOL_SIZE
- * @tim_buffers:
- *                 TIM ring buffer command queues. At least two per timer bucket
- *                 is recommened.
- * @dfa_pool: Should always be CVMX_FPA_DFA_POOL
- * @dfa_size: Should always be CVMX_FPA_DFA_POOL_SIZE
- * @dfa_buffers:
- *                 DFA command buffer. A relatively small (32 for example)
- *                 number should work.
- * Returns Zero on success, non-zero if out of memory
- */
-static int __cvmx_helper_initialize_fpa(int pip_pool, int pip_size,
-                                       int pip_buffers, int wqe_pool,
-                                       int wqe_size, int wqe_entries,
-                                       int pko_pool, int pko_size,
-                                       int pko_buffers, int tim_pool,
-                                       int tim_size, int tim_buffers,
-                                       int dfa_pool, int dfa_size,
-                                       int dfa_buffers)
-{
-       int status;
-
-       cvmx_fpa_enable();
-
-       if ((pip_buffers > 0) && (pip_buffers <= 64))
-               cvmx_dprintf
-                   ("Warning: %d packet buffers may not be enough for hardware"
-                    " prefetch. 65 or more is recommended.\n", pip_buffers);
-
-       if (pip_pool >= 0) {
-               status =
-                   __cvmx_helper_initialize_fpa_pool(pip_pool, pip_size,
-                                                     pip_buffers,
-                                                     "Packet Buffers");
-               if (status)
-                       return status;
-       }
-
-       if (wqe_pool >= 0) {
-               status =
-                   __cvmx_helper_initialize_fpa_pool(wqe_pool, wqe_size,
-                                                     wqe_entries,
-                                                     "Work Queue Entries");
-               if (status)
-                       return status;
-       }
-
-       if (pko_pool >= 0) {
-               status =
-                   __cvmx_helper_initialize_fpa_pool(pko_pool, pko_size,
-                                                     pko_buffers,
-                                                     "PKO Command Buffers");
-               if (status)
-                       return status;
-       }
-
-       if (tim_pool >= 0) {
-               status =
-                   __cvmx_helper_initialize_fpa_pool(tim_pool, tim_size,
-                                                     tim_buffers,
-                                                     "TIM Command Buffers");
-               if (status)
-                       return status;
-       }
-
-       if (dfa_pool >= 0) {
-               status =
-                   __cvmx_helper_initialize_fpa_pool(dfa_pool, dfa_size,
-                                                     dfa_buffers,
-                                                     "DFA Command Buffers");
-               if (status)
-                       return status;
-       }
-
-       return 0;
-}
-
-/**
- * Allocate memory and initialize the FPA pools using memory
- * from cvmx-bootmem. Sizes of each element in the pools is
- * controlled by the cvmx-config.h header file. Specifying
- * zero for any parameter will cause that FPA pool to not be
- * setup. This is useful if you aren't using some of the
- * hardware and want to save memory.
- *
- * @packet_buffers:
- *               Number of packet buffers to allocate
- * @work_queue_entries:
- *               Number of work queue entries
- * @pko_buffers:
- *               PKO Command buffers. You should at minimum have two per
- *               each PKO queue.
- * @tim_buffers:
- *               TIM ring buffer command queues. At least two per timer bucket
- *               is recommened.
- * @dfa_buffers:
- *               DFA command buffer. A relatively small (32 for example)
- *               number should work.
- * Returns Zero on success, non-zero if out of memory
- */
-int cvmx_helper_initialize_fpa(int packet_buffers, int work_queue_entries,
-                              int pko_buffers, int tim_buffers,
-                              int dfa_buffers)
-{
-#ifndef CVMX_FPA_PACKET_POOL
-#define CVMX_FPA_PACKET_POOL -1
-#define CVMX_FPA_PACKET_POOL_SIZE 0
-#endif
-#ifndef CVMX_FPA_WQE_POOL
-#define CVMX_FPA_WQE_POOL -1
-#define CVMX_FPA_WQE_POOL_SIZE 0
-#endif
-#ifndef CVMX_FPA_OUTPUT_BUFFER_POOL
-#define CVMX_FPA_OUTPUT_BUFFER_POOL -1
-#define CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE 0
-#endif
-#ifndef CVMX_FPA_TIMER_POOL
-#define CVMX_FPA_TIMER_POOL -1
-#define CVMX_FPA_TIMER_POOL_SIZE 0
-#endif
-#ifndef CVMX_FPA_DFA_POOL
-#define CVMX_FPA_DFA_POOL -1
-#define CVMX_FPA_DFA_POOL_SIZE 0
-#endif
-       return __cvmx_helper_initialize_fpa(CVMX_FPA_PACKET_POOL,
-                                           CVMX_FPA_PACKET_POOL_SIZE,
-                                           packet_buffers, CVMX_FPA_WQE_POOL,
-                                           CVMX_FPA_WQE_POOL_SIZE,
-                                           work_queue_entries,
-                                           CVMX_FPA_OUTPUT_BUFFER_POOL,
-                                           CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE,
-                                           pko_buffers, CVMX_FPA_TIMER_POOL,
-                                           CVMX_FPA_TIMER_POOL_SIZE,
-                                           tim_buffers, CVMX_FPA_DFA_POOL,
-                                           CVMX_FPA_DFA_POOL_SIZE,
-                                           dfa_buffers);
-}
index ffd4ae660f792461015be0f1d7ceb2dec7184dea..7fb1f222b8a538b9b000e99375899f37a0c069a9 100644 (file)
@@ -3,14 +3,17 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 2004-2008, 2009, 2010, 2011 Cavium Networks
+ * Copyright (C) 2004-2012 Cavium, Inc.
  */
 
 #include <linux/interrupt.h>
+#include <linux/irqdomain.h>
 #include <linux/bitops.h>
 #include <linux/percpu.h>
+#include <linux/slab.h>
 #include <linux/irq.h>
 #include <linux/smp.h>
+#include <linux/of.h>
 
 #include <asm/octeon/octeon.h>
 
@@ -42,9 +45,9 @@ struct octeon_core_chip_data {
 
 static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES];
 
-static void __init octeon_irq_set_ciu_mapping(int irq, int line, int bit,
-                                             struct irq_chip *chip,
-                                             irq_flow_handler_t handler)
+static void octeon_irq_set_ciu_mapping(int irq, int line, int bit,
+                                      struct irq_chip *chip,
+                                      irq_flow_handler_t handler)
 {
        union octeon_ciu_chip_data cd;
 
@@ -505,6 +508,85 @@ static void octeon_irq_ciu_enable_all_v2(struct irq_data *data)
        }
 }
 
+static void octeon_irq_gpio_setup(struct irq_data *data)
+{
+       union cvmx_gpio_bit_cfgx cfg;
+       union octeon_ciu_chip_data cd;
+       u32 t = irqd_get_trigger_type(data);
+
+       cd.p = irq_data_get_irq_chip_data(data);
+
+       cfg.u64 = 0;
+       cfg.s.int_en = 1;
+       cfg.s.int_type = (t & IRQ_TYPE_EDGE_BOTH) != 0;
+       cfg.s.rx_xor = (t & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) != 0;
+
+       /* 140 nS glitch filter*/
+       cfg.s.fil_cnt = 7;
+       cfg.s.fil_sel = 3;
+
+       cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.bit - 16), cfg.u64);
+}
+
+static void octeon_irq_ciu_enable_gpio_v2(struct irq_data *data)
+{
+       octeon_irq_gpio_setup(data);
+       octeon_irq_ciu_enable_v2(data);
+}
+
+static void octeon_irq_ciu_enable_gpio(struct irq_data *data)
+{
+       octeon_irq_gpio_setup(data);
+       octeon_irq_ciu_enable(data);
+}
+
+static int octeon_irq_ciu_gpio_set_type(struct irq_data *data, unsigned int t)
+{
+       irqd_set_trigger_type(data, t);
+       octeon_irq_gpio_setup(data);
+
+       return IRQ_SET_MASK_OK;
+}
+
+static void octeon_irq_ciu_disable_gpio_v2(struct irq_data *data)
+{
+       union octeon_ciu_chip_data cd;
+
+       cd.p = irq_data_get_irq_chip_data(data);
+       cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.bit - 16), 0);
+
+       octeon_irq_ciu_disable_all_v2(data);
+}
+
+static void octeon_irq_ciu_disable_gpio(struct irq_data *data)
+{
+       union octeon_ciu_chip_data cd;
+
+       cd.p = irq_data_get_irq_chip_data(data);
+       cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.bit - 16), 0);
+
+       octeon_irq_ciu_disable_all(data);
+}
+
+static void octeon_irq_ciu_gpio_ack(struct irq_data *data)
+{
+       union octeon_ciu_chip_data cd;
+       u64 mask;
+
+       cd.p = irq_data_get_irq_chip_data(data);
+       mask = 1ull << (cd.s.bit - 16);
+
+       cvmx_write_csr(CVMX_GPIO_INT_CLR, mask);
+}
+
+static void octeon_irq_handle_gpio(unsigned int irq, struct irq_desc *desc)
+{
+       if (irqd_get_trigger_type(irq_desc_get_irq_data(desc)) & IRQ_TYPE_EDGE_BOTH)
+               handle_edge_irq(irq, desc);
+       else
+               handle_level_irq(irq, desc);
+}
+
 #ifdef CONFIG_SMP
 
 static void octeon_irq_cpu_offline_ciu(struct irq_data *data)
@@ -650,18 +732,6 @@ static struct irq_chip octeon_irq_chip_ciu_v2 = {
        .name = "CIU",
        .irq_enable = octeon_irq_ciu_enable_v2,
        .irq_disable = octeon_irq_ciu_disable_all_v2,
-       .irq_mask = octeon_irq_ciu_disable_local_v2,
-       .irq_unmask = octeon_irq_ciu_enable_v2,
-#ifdef CONFIG_SMP
-       .irq_set_affinity = octeon_irq_ciu_set_affinity_v2,
-       .irq_cpu_offline = octeon_irq_cpu_offline_ciu,
-#endif
-};
-
-static struct irq_chip octeon_irq_chip_ciu_edge_v2 = {
-       .name = "CIU-E",
-       .irq_enable = octeon_irq_ciu_enable_v2,
-       .irq_disable = octeon_irq_ciu_disable_all_v2,
        .irq_ack = octeon_irq_ciu_ack,
        .irq_mask = octeon_irq_ciu_disable_local_v2,
        .irq_unmask = octeon_irq_ciu_enable_v2,
@@ -675,19 +745,8 @@ static struct irq_chip octeon_irq_chip_ciu = {
        .name = "CIU",
        .irq_enable = octeon_irq_ciu_enable,
        .irq_disable = octeon_irq_ciu_disable_all,
-       .irq_mask = octeon_irq_dummy_mask,
-#ifdef CONFIG_SMP
-       .irq_set_affinity = octeon_irq_ciu_set_affinity,
-       .irq_cpu_offline = octeon_irq_cpu_offline_ciu,
-#endif
-};
-
-static struct irq_chip octeon_irq_chip_ciu_edge = {
-       .name = "CIU-E",
-       .irq_enable = octeon_irq_ciu_enable,
-       .irq_disable = octeon_irq_ciu_disable_all,
-       .irq_mask = octeon_irq_dummy_mask,
        .irq_ack = octeon_irq_ciu_ack,
+       .irq_mask = octeon_irq_dummy_mask,
 #ifdef CONFIG_SMP
        .irq_set_affinity = octeon_irq_ciu_set_affinity,
        .irq_cpu_offline = octeon_irq_cpu_offline_ciu,
@@ -717,6 +776,33 @@ static struct irq_chip octeon_irq_chip_ciu_mbox = {
        .flags = IRQCHIP_ONOFFLINE_ENABLED,
 };
 
+static struct irq_chip octeon_irq_chip_ciu_gpio_v2 = {
+       .name = "CIU-GPIO",
+       .irq_enable = octeon_irq_ciu_enable_gpio_v2,
+       .irq_disable = octeon_irq_ciu_disable_gpio_v2,
+       .irq_ack = octeon_irq_ciu_gpio_ack,
+       .irq_mask = octeon_irq_ciu_disable_local_v2,
+       .irq_unmask = octeon_irq_ciu_enable_v2,
+       .irq_set_type = octeon_irq_ciu_gpio_set_type,
+#ifdef CONFIG_SMP
+       .irq_set_affinity = octeon_irq_ciu_set_affinity_v2,
+#endif
+       .flags = IRQCHIP_SET_TYPE_MASKED,
+};
+
+static struct irq_chip octeon_irq_chip_ciu_gpio = {
+       .name = "CIU-GPIO",
+       .irq_enable = octeon_irq_ciu_enable_gpio,
+       .irq_disable = octeon_irq_ciu_disable_gpio,
+       .irq_mask = octeon_irq_dummy_mask,
+       .irq_ack = octeon_irq_ciu_gpio_ack,
+       .irq_set_type = octeon_irq_ciu_gpio_set_type,
+#ifdef CONFIG_SMP
+       .irq_set_affinity = octeon_irq_ciu_set_affinity,
+#endif
+       .flags = IRQCHIP_SET_TYPE_MASKED,
+};
+
 /*
  * Watchdog interrupts are special.  They are associated with a single
  * core, so we hardwire the affinity to that core.
@@ -764,6 +850,178 @@ static struct irq_chip octeon_irq_chip_ciu_wd = {
        .irq_mask = octeon_irq_dummy_mask,
 };
 
+static bool octeon_irq_ciu_is_edge(unsigned int line, unsigned int bit)
+{
+       bool edge = false;
+
+       if (line == 0)
+               switch (bit) {
+               case 48 ... 49: /* GMX DRP */
+               case 50: /* IPD_DRP */
+               case 52 ... 55: /* Timers */
+               case 58: /* MPI */
+                       edge = true;
+                       break;
+               default:
+                       break;
+               }
+       else /* line == 1 */
+               switch (bit) {
+               case 47: /* PTP */
+                       edge = true;
+                       break;
+               default:
+                       break;
+               }
+       return edge;
+}
+
+struct octeon_irq_gpio_domain_data {
+       unsigned int base_hwirq;
+};
+
+static int octeon_irq_gpio_xlat(struct irq_domain *d,
+                               struct device_node *node,
+                               const u32 *intspec,
+                               unsigned int intsize,
+                               unsigned long *out_hwirq,
+                               unsigned int *out_type)
+{
+       unsigned int type;
+       unsigned int pin;
+       unsigned int trigger;
+       struct octeon_irq_gpio_domain_data *gpiod;
+
+       if (d->of_node != node)
+               return -EINVAL;
+
+       if (intsize < 2)
+               return -EINVAL;
+
+       pin = intspec[0];
+       if (pin >= 16)
+               return -EINVAL;
+
+       trigger = intspec[1];
+
+       switch (trigger) {
+       case 1:
+               type = IRQ_TYPE_EDGE_RISING;
+               break;
+       case 2:
+               type = IRQ_TYPE_EDGE_FALLING;
+               break;
+       case 4:
+               type = IRQ_TYPE_LEVEL_HIGH;
+               break;
+       case 8:
+               type = IRQ_TYPE_LEVEL_LOW;
+               break;
+       default:
+               pr_err("Error: (%s) Invalid irq trigger specification: %x\n",
+                      node->name,
+                      trigger);
+               type = IRQ_TYPE_LEVEL_LOW;
+               break;
+       }
+       *out_type = type;
+       gpiod = d->host_data;
+       *out_hwirq = gpiod->base_hwirq + pin;
+
+       return 0;
+}
+
+static int octeon_irq_ciu_xlat(struct irq_domain *d,
+                              struct device_node *node,
+                              const u32 *intspec,
+                              unsigned int intsize,
+                              unsigned long *out_hwirq,
+                              unsigned int *out_type)
+{
+       unsigned int ciu, bit;
+
+       ciu = intspec[0];
+       bit = intspec[1];
+
+       if (ciu > 1 || bit > 63)
+               return -EINVAL;
+
+       /* These are the GPIO lines */
+       if (ciu == 0 && bit >= 16 && bit < 32)
+               return -EINVAL;
+
+       *out_hwirq = (ciu << 6) | bit;
+       *out_type = 0;
+
+       return 0;
+}
+
+static struct irq_chip *octeon_irq_ciu_chip;
+static struct irq_chip *octeon_irq_gpio_chip;
+
+static bool octeon_irq_virq_in_range(unsigned int virq)
+{
+       /* We cannot let it overflow the mapping array. */
+       if (virq < (1ul << 8 * sizeof(octeon_irq_ciu_to_irq[0][0])))
+               return true;
+
+       WARN_ONCE(true, "virq out of range %u.\n", virq);
+       return false;
+}
+
+static int octeon_irq_ciu_map(struct irq_domain *d,
+                             unsigned int virq, irq_hw_number_t hw)
+{
+       unsigned int line = hw >> 6;
+       unsigned int bit = hw & 63;
+
+       if (!octeon_irq_virq_in_range(virq))
+               return -EINVAL;
+
+       if (line > 1 || octeon_irq_ciu_to_irq[line][bit] != 0)
+               return -EINVAL;
+
+       if (octeon_irq_ciu_is_edge(line, bit))
+               octeon_irq_set_ciu_mapping(virq, line, bit,
+                                          octeon_irq_ciu_chip,
+                                          handle_edge_irq);
+       else
+               octeon_irq_set_ciu_mapping(virq, line, bit,
+                                          octeon_irq_ciu_chip,
+                                          handle_level_irq);
+
+       return 0;
+}
+
+static int octeon_irq_gpio_map(struct irq_domain *d,
+                              unsigned int virq, irq_hw_number_t hw)
+{
+       unsigned int line = hw >> 6;
+       unsigned int bit = hw & 63;
+
+       if (!octeon_irq_virq_in_range(virq))
+               return -EINVAL;
+
+       if (line > 1 || octeon_irq_ciu_to_irq[line][bit] != 0)
+               return -EINVAL;
+
+       octeon_irq_set_ciu_mapping(virq, line, bit,
+                                  octeon_irq_gpio_chip,
+                                  octeon_irq_handle_gpio);
+
+       return 0;
+}
+
+static struct irq_domain_ops octeon_irq_domain_ciu_ops = {
+       .map = octeon_irq_ciu_map,
+       .xlate = octeon_irq_ciu_xlat,
+};
+
+static struct irq_domain_ops octeon_irq_domain_gpio_ops = {
+       .map = octeon_irq_gpio_map,
+       .xlate = octeon_irq_gpio_xlat,
+};
+
 static void octeon_irq_ip2_v1(void)
 {
        const unsigned long core_id = cvmx_get_core_num();
@@ -887,9 +1145,10 @@ static void __init octeon_irq_init_ciu(void)
 {
        unsigned int i;
        struct irq_chip *chip;
-       struct irq_chip *chip_edge;
        struct irq_chip *chip_mbox;
        struct irq_chip *chip_wd;
+       struct device_node *gpio_node;
+       struct device_node *ciu_node;
 
        octeon_irq_init_ciu_percpu();
        octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu;
@@ -901,17 +1160,18 @@ static void __init octeon_irq_init_ciu(void)
                octeon_irq_ip2 = octeon_irq_ip2_v2;
                octeon_irq_ip3 = octeon_irq_ip3_v2;
                chip = &octeon_irq_chip_ciu_v2;
-               chip_edge = &octeon_irq_chip_ciu_edge_v2;
                chip_mbox = &octeon_irq_chip_ciu_mbox_v2;
                chip_wd = &octeon_irq_chip_ciu_wd_v2;
+               octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio_v2;
        } else {
                octeon_irq_ip2 = octeon_irq_ip2_v1;
                octeon_irq_ip3 = octeon_irq_ip3_v1;
                chip = &octeon_irq_chip_ciu;
-               chip_edge = &octeon_irq_chip_ciu_edge;
                chip_mbox = &octeon_irq_chip_ciu_mbox;
                chip_wd = &octeon_irq_chip_ciu_wd;
+               octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio;
        }
+       octeon_irq_ciu_chip = chip;
        octeon_irq_ip4 = octeon_irq_ip4_mask;
 
        /* Mips internal */
@@ -920,80 +1180,49 @@ static void __init octeon_irq_init_ciu(void)
        /* CIU_0 */
        for (i = 0; i < 16; i++)
                octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WORKQ0, 0, i + 0, chip, handle_level_irq);
-       for (i = 0; i < 16; i++)
-               octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_GPIO0, 0, i + 16, chip, handle_level_irq);
 
        octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, chip_mbox, handle_percpu_irq);
        octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, chip_mbox, handle_percpu_irq);
 
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART0, 0, 34, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART1, 0, 35, chip, handle_level_irq);
-
        for (i = 0; i < 4; i++)
                octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_PCI_INT0, 0, i + 36, chip, handle_level_irq);
        for (i = 0; i < 4; i++)
                octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_PCI_MSI0, 0, i + 40, chip, handle_level_irq);
 
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_TWSI, 0, 45, chip, handle_level_irq);
        octeon_irq_set_ciu_mapping(OCTEON_IRQ_RML, 0, 46, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_TRACE0, 0, 47, chip, handle_level_irq);
-
-       for (i = 0; i < 2; i++)
-               octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_GMX_DRP0, 0, i + 48, chip_edge, handle_edge_irq);
-
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_IPD_DRP, 0, 50, chip_edge, handle_edge_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_KEY_ZERO, 0, 51, chip_edge, handle_edge_irq);
-
        for (i = 0; i < 4; i++)
-               octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_TIMER0, 0, i + 52, chip_edge, handle_edge_irq);
+               octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_TIMER0, 0, i + 52, chip, handle_edge_irq);
 
        octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB0, 0, 56, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_PCM, 0, 57, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_MPI, 0, 58, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_TWSI2, 0, 59, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_POWIQ, 0, 60, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_IPDPPTHR, 0, 61, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_MII0, 0, 62, chip, handle_level_irq);
        octeon_irq_set_ciu_mapping(OCTEON_IRQ_BOOTDMA, 0, 63, chip, handle_level_irq);
 
        /* CIU_1 */
        for (i = 0; i < 16; i++)
                octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i + 0, chip_wd, handle_level_irq);
 
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART2, 1, 16, chip, handle_level_irq);
        octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB1, 1, 17, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_MII1, 1, 18, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_NAND, 1, 19, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_MIO, 1, 20, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_IOB, 1, 21, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_FPA, 1, 22, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_POW, 1, 23, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_L2C, 1, 24, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_IPD, 1, 25, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_PIP, 1, 26, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_PKO, 1, 27, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_ZIP, 1, 28, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_TIM, 1, 29, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_RAD, 1, 30, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_KEY, 1, 31, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_DFA, 1, 32, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_USBCTL, 1, 33, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_SLI, 1, 34, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_DPI, 1, 35, chip, handle_level_irq);
-
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_AGX0, 1, 36, chip, handle_level_irq);
-
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_AGL, 1, 46, chip, handle_level_irq);
-
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_PTP, 1, 47, chip_edge, handle_edge_irq);
-
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_PEM0, 1, 48, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_PEM1, 1, 49, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_SRIO0, 1, 50, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_SRIO1, 1, 51, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_LMC0, 1, 52, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_DFM, 1, 56, chip, handle_level_irq);
-       octeon_irq_set_ciu_mapping(OCTEON_IRQ_RST, 1, 63, chip, handle_level_irq);
+
+       gpio_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-gpio");
+       if (gpio_node) {
+               struct octeon_irq_gpio_domain_data *gpiod;
+
+               gpiod = kzalloc(sizeof(*gpiod), GFP_KERNEL);
+               if (gpiod) {
+                       /* gpio domain host_data is the base hwirq number. */
+                       gpiod->base_hwirq = 16;
+                       irq_domain_add_linear(gpio_node, 16, &octeon_irq_domain_gpio_ops, gpiod);
+                       of_node_put(gpio_node);
+               } else
+                       pr_warn("Cannot allocate memory for GPIO irq_domain.\n");
+       } else
+               pr_warn("Cannot find device node for cavium,octeon-3860-gpio.\n");
+
+       ciu_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-ciu");
+       if (ciu_node) {
+               irq_domain_add_tree(ciu_node, &octeon_irq_domain_ciu_ops, NULL);
+               of_node_put(ciu_node);
+       } else
+               pr_warn("Cannot find device node for cavium,octeon-3860-ciu.\n");
 
        /* Enable the CIU lines */
        set_c0_status(STATUSF_IP3 | STATUSF_IP2);
index 88e0cddca2057addb1ad823844cc3e2351e0c2e7..db478dbb9c7beb87c1cef87932e9a5e107ce8991 100644 (file)
        .set    noreorder
        .set    noat
 
+/*
+ * t7 is used as a flag to note inatomic mode.
+ */
+LEAF(__copy_user_inatomic)
+       b       __copy_user_common
+        li     t7, 1
+       END(__copy_user_inatomic)
+
 /*
  * A combined memcpy/__copy_user
  * __copy_user sets len to 0 for success; else to an upper bound of
@@ -174,6 +182,8 @@ LEAF(memcpy)                                        /* a0=dst a1=src a2=len */
        move    v0, dst                         /* return value */
 __memcpy:
 FEXPORT(__copy_user)
+       li      t7, 0                           /* not inatomic */
+__copy_user_common:
        /*
         * Note: dst & src may be unaligned, len may be 0
         * Temps
@@ -412,7 +422,6 @@ l_exc_copy:
         * Assumes src < THREAD_BUADDR($28)
         */
        LOAD    t0, TI_TASK($28)
-        nop
        LOAD    t0, THREAD_BUADDR(t0)
 1:
 EXC(   lb      t1, 0(src),     l_exc)
@@ -422,10 +431,9 @@ EXC(       lb      t1, 0(src),     l_exc)
         ADD    dst, dst, 1
 l_exc:
        LOAD    t0, TI_TASK($28)
-        nop
        LOAD    t0, THREAD_BUADDR(t0)   # t0 is just past last good address
-        nop
        SUB     len, AT, t0             # len number of uncopied bytes
+       bnez    t7, 2f          /* Skip the zeroing out part if inatomic */
        /*
         * Here's where we rely on src and dst being incremented in tandem,
         *   See (3) above.
@@ -443,7 +451,7 @@ l_exc:
        ADD     dst, dst, 1
        bnez    src, 1b
         SUB    src, src, 1
-       jr      ra
+2:     jr      ra
         nop
 
 
index cd61d7281d9162c9e759a28772ec49ee3ef98707..0938df10a71c54f8816c866b86e602deef54102b 100644 (file)
@@ -3,7 +3,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 2004-2010 Cavium Networks
+ * Copyright (C) 2004-2011 Cavium Networks
  * Copyright (C) 2008 Wind River Systems
  */
 
 #include <linux/usb.h>
 #include <linux/dma-mapping.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_fdt.h>
+#include <linux/libfdt.h>
 
 #include <asm/octeon/octeon.h>
 #include <asm/octeon/cvmx-rnm-defs.h>
+#include <asm/octeon/cvmx-helper.h>
+#include <asm/octeon/cvmx-helper-board.h>
 
 static struct octeon_cf_data octeon_cf_data;
 
@@ -162,182 +168,6 @@ out:
 }
 device_initcall(octeon_rng_device_init);
 
-static struct i2c_board_info __initdata octeon_i2c_devices[] = {
-       {
-               I2C_BOARD_INFO("ds1337", 0x68),
-       },
-};
-
-static int __init octeon_i2c_devices_init(void)
-{
-       return i2c_register_board_info(0, octeon_i2c_devices,
-                                      ARRAY_SIZE(octeon_i2c_devices));
-}
-arch_initcall(octeon_i2c_devices_init);
-
-#define OCTEON_I2C_IO_BASE 0x1180000001000ull
-#define OCTEON_I2C_IO_UNIT_OFFSET 0x200
-
-static struct octeon_i2c_data octeon_i2c_data[2];
-
-static int __init octeon_i2c_device_init(void)
-{
-       struct platform_device *pd;
-       int ret = 0;
-       int port, num_ports;
-
-       struct resource i2c_resources[] = {
-               {
-                       .flags  = IORESOURCE_MEM,
-               }, {
-                       .flags  = IORESOURCE_IRQ,
-               }
-       };
-
-       if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX))
-               num_ports = 2;
-       else
-               num_ports = 1;
-
-       for (port = 0; port < num_ports; port++) {
-               octeon_i2c_data[port].sys_freq = octeon_get_io_clock_rate();
-               /*FIXME: should be examined. At the moment is set for 100Khz */
-               octeon_i2c_data[port].i2c_freq = 100000;
-
-               pd = platform_device_alloc("i2c-octeon", port);
-               if (!pd) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-
-               pd->dev.platform_data = octeon_i2c_data + port;
-
-               i2c_resources[0].start =
-                       OCTEON_I2C_IO_BASE + (port * OCTEON_I2C_IO_UNIT_OFFSET);
-               i2c_resources[0].end = i2c_resources[0].start + 0x1f;
-               switch (port) {
-               case 0:
-                       i2c_resources[1].start = OCTEON_IRQ_TWSI;
-                       i2c_resources[1].end = OCTEON_IRQ_TWSI;
-                       break;
-               case 1:
-                       i2c_resources[1].start = OCTEON_IRQ_TWSI2;
-                       i2c_resources[1].end = OCTEON_IRQ_TWSI2;
-                       break;
-               default:
-                       BUG();
-               }
-
-               ret = platform_device_add_resources(pd,
-                                                   i2c_resources,
-                                                   ARRAY_SIZE(i2c_resources));
-               if (ret)
-                       goto fail;
-
-               ret = platform_device_add(pd);
-               if (ret)
-                       goto fail;
-       }
-       return ret;
-fail:
-       platform_device_put(pd);
-out:
-       return ret;
-}
-device_initcall(octeon_i2c_device_init);
-
-/* Octeon SMI/MDIO interface.  */
-static int __init octeon_mdiobus_device_init(void)
-{
-       struct platform_device *pd;
-       int ret = 0;
-
-       if (octeon_is_simulation())
-               return 0; /* No mdio in the simulator. */
-
-       /* The bus number is the platform_device id.  */
-       pd = platform_device_alloc("mdio-octeon", 0);
-       if (!pd) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       ret = platform_device_add(pd);
-       if (ret)
-               goto fail;
-
-       return ret;
-fail:
-       platform_device_put(pd);
-
-out:
-       return ret;
-
-}
-device_initcall(octeon_mdiobus_device_init);
-
-/* Octeon mgmt port Ethernet interface.  */
-static int __init octeon_mgmt_device_init(void)
-{
-       struct platform_device *pd;
-       int ret = 0;
-       int port, num_ports;
-
-       struct resource mgmt_port_resource = {
-               .flags  = IORESOURCE_IRQ,
-               .start  = -1,
-               .end    = -1
-       };
-
-       if (!OCTEON_IS_MODEL(OCTEON_CN56XX) && !OCTEON_IS_MODEL(OCTEON_CN52XX))
-               return 0;
-
-       if (OCTEON_IS_MODEL(OCTEON_CN56XX))
-               num_ports = 1;
-       else
-               num_ports = 2;
-
-       for (port = 0; port < num_ports; port++) {
-               pd = platform_device_alloc("octeon_mgmt", port);
-               if (!pd) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-               /* No DMA restrictions */
-               pd->dev.coherent_dma_mask = DMA_BIT_MASK(64);
-               pd->dev.dma_mask = &pd->dev.coherent_dma_mask;
-
-               switch (port) {
-               case 0:
-                       mgmt_port_resource.start = OCTEON_IRQ_MII0;
-                       break;
-               case 1:
-                       mgmt_port_resource.start = OCTEON_IRQ_MII1;
-                       break;
-               default:
-                       BUG();
-               }
-               mgmt_port_resource.end = mgmt_port_resource.start;
-
-               ret = platform_device_add_resources(pd, &mgmt_port_resource, 1);
-
-               if (ret)
-                       goto fail;
-
-               ret = platform_device_add(pd);
-               if (ret)
-                       goto fail;
-       }
-       return ret;
-fail:
-       platform_device_put(pd);
-
-out:
-       return ret;
-
-}
-device_initcall(octeon_mgmt_device_init);
-
 #ifdef CONFIG_USB
 
 static int __init octeon_ehci_device_init(void)
@@ -440,6 +270,521 @@ device_initcall(octeon_ohci_device_init);
 
 #endif /* CONFIG_USB */
 
+static struct of_device_id __initdata octeon_ids[] = {
+       { .compatible = "simple-bus", },
+       { .compatible = "cavium,octeon-6335-uctl", },
+       { .compatible = "cavium,octeon-3860-bootbus", },
+       { .compatible = "cavium,mdio-mux", },
+       { .compatible = "gpio-leds", },
+       {},
+};
+
+static bool __init octeon_has_88e1145(void)
+{
+       return !OCTEON_IS_MODEL(OCTEON_CN52XX) &&
+              !OCTEON_IS_MODEL(OCTEON_CN6XXX) &&
+              !OCTEON_IS_MODEL(OCTEON_CN56XX);
+}
+
+static void __init octeon_fdt_set_phy(int eth, int phy_addr)
+{
+       const __be32 *phy_handle;
+       const __be32 *alt_phy_handle;
+       const __be32 *reg;
+       u32 phandle;
+       int phy;
+       int alt_phy;
+       const char *p;
+       int current_len;
+       char new_name[20];
+
+       phy_handle = fdt_getprop(initial_boot_params, eth, "phy-handle", NULL);
+       if (!phy_handle)
+               return;
+
+       phandle = be32_to_cpup(phy_handle);
+       phy = fdt_node_offset_by_phandle(initial_boot_params, phandle);
+
+       alt_phy_handle = fdt_getprop(initial_boot_params, eth, "cavium,alt-phy-handle", NULL);
+       if (alt_phy_handle) {
+               u32 alt_phandle = be32_to_cpup(alt_phy_handle);
+               alt_phy = fdt_node_offset_by_phandle(initial_boot_params, alt_phandle);
+       } else {
+               alt_phy = -1;
+       }
+
+       if (phy_addr < 0 || phy < 0) {
+               /* Delete the PHY things */
+               fdt_nop_property(initial_boot_params, eth, "phy-handle");
+               /* This one may fail */
+               fdt_nop_property(initial_boot_params, eth, "cavium,alt-phy-handle");
+               if (phy >= 0)
+                       fdt_nop_node(initial_boot_params, phy);
+               if (alt_phy >= 0)
+                       fdt_nop_node(initial_boot_params, alt_phy);
+               return;
+       }
+
+       if (phy_addr >= 256 && alt_phy > 0) {
+               const struct fdt_property *phy_prop;
+               struct fdt_property *alt_prop;
+               u32 phy_handle_name;
+
+               /* Use the alt phy node instead.*/
+               phy_prop = fdt_get_property(initial_boot_params, eth, "phy-handle", NULL);
+               phy_handle_name = phy_prop->nameoff;
+               fdt_nop_node(initial_boot_params, phy);
+               fdt_nop_property(initial_boot_params, eth, "phy-handle");
+               alt_prop = fdt_get_property_w(initial_boot_params, eth, "cavium,alt-phy-handle", NULL);
+               alt_prop->nameoff = phy_handle_name;
+               phy = alt_phy;
+       }
+
+       phy_addr &= 0xff;
+
+       if (octeon_has_88e1145()) {
+               fdt_nop_property(initial_boot_params, phy, "marvell,reg-init");
+               memset(new_name, 0, sizeof(new_name));
+               strcpy(new_name, "marvell,88e1145");
+               p = fdt_getprop(initial_boot_params, phy, "compatible",
+                               &current_len);
+               if (p && current_len >= strlen(new_name))
+                       fdt_setprop_inplace(initial_boot_params, phy,
+                                       "compatible", new_name, current_len);
+       }
+
+       reg = fdt_getprop(initial_boot_params, phy, "reg", NULL);
+       if (phy_addr == be32_to_cpup(reg))
+               return;
+
+       fdt_setprop_inplace_cell(initial_boot_params, phy, "reg", phy_addr);
+
+       snprintf(new_name, sizeof(new_name), "ethernet-phy@%x", phy_addr);
+
+       p = fdt_get_name(initial_boot_params, phy, &current_len);
+       if (p && current_len == strlen(new_name))
+               fdt_set_name(initial_boot_params, phy, new_name);
+       else
+               pr_err("Error: could not rename ethernet phy: <%s>", p);
+}
+
+static void __init octeon_fdt_set_mac_addr(int n, u64 *pmac)
+{
+       u8 new_mac[6];
+       u64 mac = *pmac;
+       int r;
+
+       new_mac[0] = (mac >> 40) & 0xff;
+       new_mac[1] = (mac >> 32) & 0xff;
+       new_mac[2] = (mac >> 24) & 0xff;
+       new_mac[3] = (mac >> 16) & 0xff;
+       new_mac[4] = (mac >> 8) & 0xff;
+       new_mac[5] = mac & 0xff;
+
+       r = fdt_setprop_inplace(initial_boot_params, n, "local-mac-address",
+                               new_mac, sizeof(new_mac));
+
+       if (r) {
+               pr_err("Setting \"local-mac-address\" failed %d", r);
+               return;
+       }
+       *pmac = mac + 1;
+}
+
+static void __init octeon_fdt_rm_ethernet(int node)
+{
+       const __be32 *phy_handle;
+
+       phy_handle = fdt_getprop(initial_boot_params, node, "phy-handle", NULL);
+       if (phy_handle) {
+               u32 ph = be32_to_cpup(phy_handle);
+               int p = fdt_node_offset_by_phandle(initial_boot_params, ph);
+               if (p >= 0)
+                       fdt_nop_node(initial_boot_params, p);
+       }
+       fdt_nop_node(initial_boot_params, node);
+}
+
+static void __init octeon_fdt_pip_port(int iface, int i, int p, int max, u64 *pmac)
+{
+       char name_buffer[20];
+       int eth;
+       int phy_addr;
+       int ipd_port;
+
+       snprintf(name_buffer, sizeof(name_buffer), "ethernet@%x", p);
+       eth = fdt_subnode_offset(initial_boot_params, iface, name_buffer);
+       if (eth < 0)
+               return;
+       if (p > max) {
+               pr_debug("Deleting port %x:%x\n", i, p);
+               octeon_fdt_rm_ethernet(eth);
+               return;
+       }
+       if (OCTEON_IS_MODEL(OCTEON_CN68XX))
+               ipd_port = (0x100 * i) + (0x10 * p) + 0x800;
+       else
+               ipd_port = 16 * i + p;
+
+       phy_addr = cvmx_helper_board_get_mii_address(ipd_port);
+       octeon_fdt_set_phy(eth, phy_addr);
+       octeon_fdt_set_mac_addr(eth, pmac);
+}
+
+static void __init octeon_fdt_pip_iface(int pip, int idx, u64 *pmac)
+{
+       char name_buffer[20];
+       int iface;
+       int p;
+       int count;
+
+       count = cvmx_helper_interface_enumerate(idx);
+
+       snprintf(name_buffer, sizeof(name_buffer), "interface@%d", idx);
+       iface = fdt_subnode_offset(initial_boot_params, pip, name_buffer);
+       if (iface < 0)
+               return;
+
+       for (p = 0; p < 16; p++)
+               octeon_fdt_pip_port(iface, idx, p, count - 1, pmac);
+}
+
+int __init octeon_prune_device_tree(void)
+{
+       int i, max_port, uart_mask;
+       const char *pip_path;
+       const char *alias_prop;
+       char name_buffer[20];
+       int aliases;
+       u64 mac_addr_base;
+
+       if (fdt_check_header(initial_boot_params))
+               panic("Corrupt Device Tree.");
+
+       aliases = fdt_path_offset(initial_boot_params, "/aliases");
+       if (aliases < 0) {
+               pr_err("Error: No /aliases node in device tree.");
+               return -EINVAL;
+       }
+
+
+       mac_addr_base =
+               ((octeon_bootinfo->mac_addr_base[0] & 0xffull)) << 40 |
+               ((octeon_bootinfo->mac_addr_base[1] & 0xffull)) << 32 |
+               ((octeon_bootinfo->mac_addr_base[2] & 0xffull)) << 24 |
+               ((octeon_bootinfo->mac_addr_base[3] & 0xffull)) << 16 |
+               ((octeon_bootinfo->mac_addr_base[4] & 0xffull)) << 8 |
+               (octeon_bootinfo->mac_addr_base[5] & 0xffull);
+
+       if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN63XX))
+               max_port = 2;
+       else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN68XX))
+               max_port = 1;
+       else
+               max_port = 0;
+
+       if (octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC10E)
+               max_port = 0;
+
+       for (i = 0; i < 2; i++) {
+               int mgmt;
+               snprintf(name_buffer, sizeof(name_buffer),
+                        "mix%d", i);
+               alias_prop = fdt_getprop(initial_boot_params, aliases,
+                                       name_buffer, NULL);
+               if (alias_prop) {
+                       mgmt = fdt_path_offset(initial_boot_params, alias_prop);
+                       if (mgmt < 0)
+                               continue;
+                       if (i >= max_port) {
+                               pr_debug("Deleting mix%d\n", i);
+                               octeon_fdt_rm_ethernet(mgmt);
+                               fdt_nop_property(initial_boot_params, aliases,
+                                                name_buffer);
+                       } else {
+                               int phy_addr = cvmx_helper_board_get_mii_address(CVMX_HELPER_BOARD_MGMT_IPD_PORT + i);
+                               octeon_fdt_set_phy(mgmt, phy_addr);
+                               octeon_fdt_set_mac_addr(mgmt, &mac_addr_base);
+                       }
+               }
+       }
+
+       pip_path = fdt_getprop(initial_boot_params, aliases, "pip", NULL);
+       if (pip_path) {
+               int pip = fdt_path_offset(initial_boot_params, pip_path);
+               if (pip  >= 0)
+                       for (i = 0; i <= 4; i++)
+                               octeon_fdt_pip_iface(pip, i, &mac_addr_base);
+       }
+
+       /* I2C */
+       if (OCTEON_IS_MODEL(OCTEON_CN52XX) ||
+           OCTEON_IS_MODEL(OCTEON_CN63XX) ||
+           OCTEON_IS_MODEL(OCTEON_CN68XX) ||
+           OCTEON_IS_MODEL(OCTEON_CN56XX))
+               max_port = 2;
+       else
+               max_port = 1;
+
+       for (i = 0; i < 2; i++) {
+               int i2c;
+               snprintf(name_buffer, sizeof(name_buffer),
+                        "twsi%d", i);
+               alias_prop = fdt_getprop(initial_boot_params, aliases,
+                                       name_buffer, NULL);
+
+               if (alias_prop) {
+                       i2c = fdt_path_offset(initial_boot_params, alias_prop);
+                       if (i2c < 0)
+                               continue;
+                       if (i >= max_port) {
+                               pr_debug("Deleting twsi%d\n", i);
+                               fdt_nop_node(initial_boot_params, i2c);
+                               fdt_nop_property(initial_boot_params, aliases,
+                                                name_buffer);
+                       }
+               }
+       }
+
+       /* SMI/MDIO */
+       if (OCTEON_IS_MODEL(OCTEON_CN68XX))
+               max_port = 4;
+       else if (OCTEON_IS_MODEL(OCTEON_CN52XX) ||
+                OCTEON_IS_MODEL(OCTEON_CN63XX) ||
+                OCTEON_IS_MODEL(OCTEON_CN56XX))
+               max_port = 2;
+       else
+               max_port = 1;
+
+       for (i = 0; i < 2; i++) {
+               int i2c;
+               snprintf(name_buffer, sizeof(name_buffer),
+                        "smi%d", i);
+               alias_prop = fdt_getprop(initial_boot_params, aliases,
+                                       name_buffer, NULL);
+
+               if (alias_prop) {
+                       i2c = fdt_path_offset(initial_boot_params, alias_prop);
+                       if (i2c < 0)
+                               continue;
+                       if (i >= max_port) {
+                               pr_debug("Deleting smi%d\n", i);
+                               fdt_nop_node(initial_boot_params, i2c);
+                               fdt_nop_property(initial_boot_params, aliases,
+                                                name_buffer);
+                       }
+               }
+       }
+
+       /* Serial */
+       uart_mask = 3;
+
+       /* Right now CN52XX is the only chip with a third uart */
+       if (OCTEON_IS_MODEL(OCTEON_CN52XX))
+               uart_mask |= 4; /* uart2 */
+
+       for (i = 0; i < 3; i++) {
+               int uart;
+               snprintf(name_buffer, sizeof(name_buffer),
+                        "uart%d", i);
+               alias_prop = fdt_getprop(initial_boot_params, aliases,
+                                       name_buffer, NULL);
+
+               if (alias_prop) {
+                       uart = fdt_path_offset(initial_boot_params, alias_prop);
+                       if (uart_mask & (1 << i))
+                               continue;
+                       pr_debug("Deleting uart%d\n", i);
+                       fdt_nop_node(initial_boot_params, uart);
+                       fdt_nop_property(initial_boot_params, aliases,
+                                        name_buffer);
+               }
+       }
+
+       /* Compact Flash */
+       alias_prop = fdt_getprop(initial_boot_params, aliases,
+                                "cf0", NULL);
+       if (alias_prop) {
+               union cvmx_mio_boot_reg_cfgx mio_boot_reg_cfg;
+               unsigned long base_ptr, region_base, region_size;
+               unsigned long region1_base = 0;
+               unsigned long region1_size = 0;
+               int cs, bootbus;
+               bool is_16bit = false;
+               bool is_true_ide = false;
+               __be32 new_reg[6];
+               __be32 *ranges;
+               int len;
+
+               int cf = fdt_path_offset(initial_boot_params, alias_prop);
+               base_ptr = 0;
+               if (octeon_bootinfo->major_version == 1
+                       && octeon_bootinfo->minor_version >= 1) {
+                       if (octeon_bootinfo->compact_flash_common_base_addr)
+                               base_ptr = octeon_bootinfo->compact_flash_common_base_addr;
+               } else {
+                       base_ptr = 0x1d000800;
+               }
+
+               if (!base_ptr)
+                       goto no_cf;
+
+               /* Find CS0 region. */
+               for (cs = 0; cs < 8; cs++) {
+                       mio_boot_reg_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs));
+                       region_base = mio_boot_reg_cfg.s.base << 16;
+                       region_size = (mio_boot_reg_cfg.s.size + 1) << 16;
+                       if (mio_boot_reg_cfg.s.en && base_ptr >= region_base
+                               && base_ptr < region_base + region_size) {
+                               is_16bit = mio_boot_reg_cfg.s.width;
+                               break;
+                       }
+               }
+               if (cs >= 7) {
+                       /* cs and cs + 1 are CS0 and CS1, both must be less than 8. */
+                       goto no_cf;
+               }
+
+               if (!(base_ptr & 0xfffful)) {
+                       /*
+                        * Boot loader signals availability of DMA (true_ide
+                        * mode) by setting low order bits of base_ptr to
+                        * zero.
+                        */
+
+                       /* Asume that CS1 immediately follows. */
+                       mio_boot_reg_cfg.u64 =
+                               cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs + 1));
+                       region1_base = mio_boot_reg_cfg.s.base << 16;
+                       region1_size = (mio_boot_reg_cfg.s.size + 1) << 16;
+                       if (!mio_boot_reg_cfg.s.en)
+                               goto no_cf;
+                       is_true_ide = true;
+
+               } else {
+                       fdt_nop_property(initial_boot_params, cf, "cavium,true-ide");
+                       fdt_nop_property(initial_boot_params, cf, "cavium,dma-engine-handle");
+                       if (!is_16bit) {
+                               __be32 width = cpu_to_be32(8);
+                               fdt_setprop_inplace(initial_boot_params, cf,
+                                               "cavium,bus-width", &width, sizeof(width));
+                       }
+               }
+               new_reg[0] = cpu_to_be32(cs);
+               new_reg[1] = cpu_to_be32(0);
+               new_reg[2] = cpu_to_be32(0x10000);
+               new_reg[3] = cpu_to_be32(cs + 1);
+               new_reg[4] = cpu_to_be32(0);
+               new_reg[5] = cpu_to_be32(0x10000);
+               fdt_setprop_inplace(initial_boot_params, cf,
+                                   "reg",  new_reg, sizeof(new_reg));
+
+               bootbus = fdt_parent_offset(initial_boot_params, cf);
+               if (bootbus < 0)
+                       goto no_cf;
+               ranges = fdt_getprop_w(initial_boot_params, bootbus, "ranges", &len);
+               if (!ranges || len < (5 * 8 * sizeof(__be32)))
+                       goto no_cf;
+
+               ranges[(cs * 5) + 2] = cpu_to_be32(region_base >> 32);
+               ranges[(cs * 5) + 3] = cpu_to_be32(region_base & 0xffffffff);
+               ranges[(cs * 5) + 4] = cpu_to_be32(region_size);
+               if (is_true_ide) {
+                       cs++;
+                       ranges[(cs * 5) + 2] = cpu_to_be32(region1_base >> 32);
+                       ranges[(cs * 5) + 3] = cpu_to_be32(region1_base & 0xffffffff);
+                       ranges[(cs * 5) + 4] = cpu_to_be32(region1_size);
+               }
+               goto end_cf;
+no_cf:
+               fdt_nop_node(initial_boot_params, cf);
+
+end_cf:
+               ;
+       }
+
+       /* 8 char LED */
+       alias_prop = fdt_getprop(initial_boot_params, aliases,
+                                "led0", NULL);
+       if (alias_prop) {
+               union cvmx_mio_boot_reg_cfgx mio_boot_reg_cfg;
+               unsigned long base_ptr, region_base, region_size;
+               int cs, bootbus;
+               __be32 new_reg[6];
+               __be32 *ranges;
+               int len;
+               int led = fdt_path_offset(initial_boot_params, alias_prop);
+
+               base_ptr = octeon_bootinfo->led_display_base_addr;
+               if (base_ptr == 0)
+                       goto no_led;
+               /* Find CS0 region. */
+               for (cs = 0; cs < 8; cs++) {
+                       mio_boot_reg_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs));
+                       region_base = mio_boot_reg_cfg.s.base << 16;
+                       region_size = (mio_boot_reg_cfg.s.size + 1) << 16;
+                       if (mio_boot_reg_cfg.s.en && base_ptr >= region_base
+                               && base_ptr < region_base + region_size)
+                               break;
+               }
+
+               if (cs > 7)
+                       goto no_led;
+
+               new_reg[0] = cpu_to_be32(cs);
+               new_reg[1] = cpu_to_be32(0x20);
+               new_reg[2] = cpu_to_be32(0x20);
+               new_reg[3] = cpu_to_be32(cs);
+               new_reg[4] = cpu_to_be32(0);
+               new_reg[5] = cpu_to_be32(0x20);
+               fdt_setprop_inplace(initial_boot_params, led,
+                                   "reg",  new_reg, sizeof(new_reg));
+
+               bootbus = fdt_parent_offset(initial_boot_params, led);
+               if (bootbus < 0)
+                       goto no_led;
+               ranges = fdt_getprop_w(initial_boot_params, bootbus, "ranges", &len);
+               if (!ranges || len < (5 * 8 * sizeof(__be32)))
+                       goto no_led;
+
+               ranges[(cs * 5) + 2] = cpu_to_be32(region_base >> 32);
+               ranges[(cs * 5) + 3] = cpu_to_be32(region_base & 0xffffffff);
+               ranges[(cs * 5) + 4] = cpu_to_be32(region_size);
+               goto end_led;
+
+no_led:
+               fdt_nop_node(initial_boot_params, led);
+end_led:
+               ;
+       }
+
+       /* OHCI/UHCI USB */
+       alias_prop = fdt_getprop(initial_boot_params, aliases,
+                                "uctl", NULL);
+       if (alias_prop) {
+               int uctl = fdt_path_offset(initial_boot_params, alias_prop);
+
+               if (uctl >= 0 && (!OCTEON_IS_MODEL(OCTEON_CN6XXX) ||
+                                 octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC2E)) {
+                       pr_debug("Deleting uctl\n");
+                       fdt_nop_node(initial_boot_params, uctl);
+                       fdt_nop_property(initial_boot_params, aliases, "uctl");
+               } else if (octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC10E ||
+                          octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC4E) {
+                       /* Missing "refclk-type" defaults to crystal. */
+                       fdt_nop_property(initial_boot_params, uctl, "refclk-type");
+               }
+       }
+
+       return 0;
+}
+
+static int __init octeon_publish_devices(void)
+{
+       return of_platform_bus_probe(NULL, octeon_ids, NULL);
+}
+device_initcall(octeon_publish_devices);
+
 MODULE_AUTHOR("David Daney <ddaney@caviumnetworks.com>");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Platform driver for Octeon SOC");
diff --git a/arch/mips/cavium-octeon/octeon_3xxx.dts b/arch/mips/cavium-octeon/octeon_3xxx.dts
new file mode 100644 (file)
index 0000000..f28b2d0
--- /dev/null
@@ -0,0 +1,571 @@
+/dts-v1/;
+/*
+ * OCTEON 3XXX, 5XXX, 63XX device tree skeleton.
+ *
+ * This device tree is pruned and patched by early boot code before
+ * use.  Because of this, it contains a super-set of the available
+ * devices and properties.
+ */
+/ {
+       compatible = "cavium,octeon-3860";
+       #address-cells = <2>;
+       #size-cells = <2>;
+       interrupt-parent = <&ciu>;
+
+       soc@0 {
+               compatible = "simple-bus";
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges; /* Direct mapping */
+
+               ciu: interrupt-controller@1070000000000 {
+                       compatible = "cavium,octeon-3860-ciu";
+                       interrupt-controller;
+                       /* Interrupts are specified by two parts:
+                        * 1) Controller register (0 or 1)
+                        * 2) Bit within the register (0..63)
+                        */
+                       #interrupt-cells = <2>;
+                       reg = <0x10700 0x00000000 0x0 0x7000>;
+               };
+
+               gpio: gpio-controller@1070000000800 {
+                       #gpio-cells = <2>;
+                       compatible = "cavium,octeon-3860-gpio";
+                       reg = <0x10700 0x00000800 0x0 0x100>;
+                       gpio-controller;
+                       /* Interrupts are specified by two parts:
+                        * 1) GPIO pin number (0..15)
+                        * 2) Triggering (1 - edge rising
+                        *                2 - edge falling
+                        *                4 - level active high
+                        *                8 - level active low)
+                        */
+                       interrupt-controller;
+                       #interrupt-cells = <2>;
+                       /* The GPIO pin connect to 16 consecutive CUI bits */
+                       interrupts = <0 16>, <0 17>, <0 18>, <0 19>,
+                                    <0 20>, <0 21>, <0 22>, <0 23>,
+                                    <0 24>, <0 25>, <0 26>, <0 27>,
+                                    <0 28>, <0 29>, <0 30>, <0 31>;
+               };
+
+               smi0: mdio@1180000001800 {
+                       compatible = "cavium,octeon-3860-mdio";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg = <0x11800 0x00001800 0x0 0x40>;
+
+                       phy0: ethernet-phy@0 {
+                               compatible = "marvell,88e1118";
+                               marvell,reg-init =
+                                       /* Fix rx and tx clock transition timing */
+                                       <2 0x15 0xffcf 0>, /* Reg 2,21 Clear bits 4, 5 */
+                                       /* Adjust LED drive. */
+                                       <3 0x11 0 0x442a>, /* Reg 3,17 <- 0442a */
+                                       /* irq, blink-activity, blink-link */
+                                       <3 0x10 0 0x0242>; /* Reg 3,16 <- 0x0242 */
+                               reg = <0>;
+                       };
+
+                       phy1: ethernet-phy@1 {
+                               compatible = "marvell,88e1118";
+                               marvell,reg-init =
+                                       /* Fix rx and tx clock transition timing */
+                                       <2 0x15 0xffcf 0>, /* Reg 2,21 Clear bits 4, 5 */
+                                       /* Adjust LED drive. */
+                                       <3 0x11 0 0x442a>, /* Reg 3,17 <- 0442a */
+                                       /* irq, blink-activity, blink-link */
+                                       <3 0x10 0 0x0242>; /* Reg 3,16 <- 0x0242 */
+                               reg = <1>;
+                       };
+
+                       phy2: ethernet-phy@2 {
+                               reg = <2>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy3: ethernet-phy@3 {
+                               reg = <3>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy4: ethernet-phy@4 {
+                               reg = <4>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy5: ethernet-phy@5 {
+                               reg = <5>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+
+                       phy6: ethernet-phy@6 {
+                               reg = <6>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy7: ethernet-phy@7 {
+                               reg = <7>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy8: ethernet-phy@8 {
+                               reg = <8>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy9: ethernet-phy@9 {
+                               reg = <9>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+               };
+
+               smi1: mdio@1180000001900 {
+                       compatible = "cavium,octeon-3860-mdio";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg = <0x11800 0x00001900 0x0 0x40>;
+
+                       phy100: ethernet-phy@1 {
+                               reg = <1>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                               interrupt-parent = <&gpio>;
+                               interrupts = <12 8>; /* Pin 12, active low */
+                       };
+                       phy101: ethernet-phy@2 {
+                               reg = <2>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                               interrupt-parent = <&gpio>;
+                               interrupts = <12 8>; /* Pin 12, active low */
+                       };
+                       phy102: ethernet-phy@3 {
+                               reg = <3>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                               interrupt-parent = <&gpio>;
+                               interrupts = <12 8>; /* Pin 12, active low */
+                       };
+                       phy103: ethernet-phy@4 {
+                               reg = <4>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                               interrupt-parent = <&gpio>;
+                               interrupts = <12 8>; /* Pin 12, active low */
+                       };
+               };
+
+               mix0: ethernet@1070000100000 {
+                       compatible = "cavium,octeon-5750-mix";
+                       reg = <0x10700 0x00100000 0x0 0x100>, /* MIX */
+                             <0x11800 0xE0000000 0x0 0x300>, /* AGL */
+                             <0x11800 0xE0000400 0x0 0x400>, /* AGL_SHARED  */
+                             <0x11800 0xE0002000 0x0 0x8>;   /* AGL_PRT_CTL */
+                       cell-index = <0>;
+                       interrupts = <0 62>, <1 46>;
+                       local-mac-address = [ 00 00 00 00 00 00 ];
+                       phy-handle = <&phy0>;
+               };
+
+               mix1: ethernet@1070000100800 {
+                       compatible = "cavium,octeon-5750-mix";
+                       reg = <0x10700 0x00100800 0x0 0x100>, /* MIX */
+                             <0x11800 0xE0000800 0x0 0x300>, /* AGL */
+                             <0x11800 0xE0000400 0x0 0x400>, /* AGL_SHARED  */
+                             <0x11800 0xE0002008 0x0 0x8>;   /* AGL_PRT_CTL */
+                       cell-index = <1>;
+                       interrupts = <1 18>, < 1 46>;
+                       local-mac-address = [ 00 00 00 00 00 00 ];
+                       phy-handle = <&phy1>;
+               };
+
+               pip: pip@11800a0000000 {
+                       compatible = "cavium,octeon-3860-pip";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg = <0x11800 0xa0000000 0x0 0x2000>;
+
+                       interface@0 {
+                               compatible = "cavium,octeon-3860-pip-interface";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               reg = <0>; /* interface */
+
+                               ethernet@0 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x0>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy2>;
+                                       cavium,alt-phy-handle = <&phy100>;
+                               };
+                               ethernet@1 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x1>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy3>;
+                                       cavium,alt-phy-handle = <&phy101>;
+                               };
+                               ethernet@2 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x2>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy4>;
+                                       cavium,alt-phy-handle = <&phy102>;
+                               };
+                               ethernet@3 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x3>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy5>;
+                                       cavium,alt-phy-handle = <&phy103>;
+                               };
+                               ethernet@4 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x4>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                               };
+                               ethernet@5 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x5>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                               };
+                               ethernet@6 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x6>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                               };
+                               ethernet@7 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x7>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                               };
+                               ethernet@8 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x8>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                               };
+                               ethernet@9 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x9>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                               };
+                               ethernet@a {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0xa>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                               };
+                               ethernet@b {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0xb>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                               };
+                               ethernet@c {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0xc>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                               };
+                               ethernet@d {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0xd>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                               };
+                               ethernet@e {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0xe>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                               };
+                               ethernet@f {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0xf>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                               };
+                       };
+
+                       interface@1 {
+                               compatible = "cavium,octeon-3860-pip-interface";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               reg = <1>; /* interface */
+
+                               ethernet@0 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x0>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy6>;
+                               };
+                               ethernet@1 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x1>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy7>;
+                               };
+                               ethernet@2 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x2>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy8>;
+                               };
+                               ethernet@3 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x3>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy9>;
+                               };
+                       };
+               };
+
+               twsi0: i2c@1180000001000 {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       compatible = "cavium,octeon-3860-twsi";
+                       reg = <0x11800 0x00001000 0x0 0x200>;
+                       interrupts = <0 45>;
+                       clock-frequency = <100000>;
+
+                       rtc@68 {
+                               compatible = "dallas,ds1337";
+                               reg = <0x68>;
+                       };
+                       tmp@4c {
+                               compatible = "ti,tmp421";
+                               reg = <0x4c>;
+                       };
+               };
+
+               twsi1: i2c@1180000001200 {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       compatible = "cavium,octeon-3860-twsi";
+                       reg = <0x11800 0x00001200 0x0 0x200>;
+                       interrupts = <0 59>;
+                       clock-frequency = <100000>;
+               };
+
+               uart0: serial@1180000000800 {
+                       compatible = "cavium,octeon-3860-uart","ns16550";
+                       reg = <0x11800 0x00000800 0x0 0x400>;
+                       clock-frequency = <0>;
+                       current-speed = <115200>;
+                       reg-shift = <3>;
+                       interrupts = <0 34>;
+               };
+
+               uart1: serial@1180000000c00 {
+                       compatible = "cavium,octeon-3860-uart","ns16550";
+                       reg = <0x11800 0x00000c00 0x0 0x400>;
+                       clock-frequency = <0>;
+                       current-speed = <115200>;
+                       reg-shift = <3>;
+                       interrupts = <0 35>;
+               };
+
+               uart2: serial@1180000000400 {
+                       compatible = "cavium,octeon-3860-uart","ns16550";
+                       reg = <0x11800 0x00000400 0x0 0x400>;
+                       clock-frequency = <0>;
+                       current-speed = <115200>;
+                       reg-shift = <3>;
+                       interrupts = <1 16>;
+               };
+
+               bootbus: bootbus@1180000000000 {
+                       compatible = "cavium,octeon-3860-bootbus";
+                       reg = <0x11800 0x00000000 0x0 0x200>;
+                       /* The chip select number and offset */
+                       #address-cells = <2>;
+                       /* The size of the chip select region */
+                       #size-cells = <1>;
+                       ranges = <0 0  0x0 0x1f400000  0xc00000>,
+                                <1 0  0x10000 0x30000000  0>,
+                                <2 0  0x10000 0x40000000  0>,
+                                <3 0  0x10000 0x50000000  0>,
+                                <4 0  0x0 0x1d020000  0x10000>,
+                                <5 0  0x0 0x1d040000  0x10000>,
+                                <6 0  0x0 0x1d050000  0x10000>,
+                                <7 0  0x10000 0x90000000  0>;
+
+                       cavium,cs-config@0 {
+                               compatible = "cavium,octeon-3860-bootbus-config";
+                               cavium,cs-index = <0>;
+                               cavium,t-adr  = <20>;
+                               cavium,t-ce   = <60>;
+                               cavium,t-oe   = <60>;
+                               cavium,t-we   = <45>;
+                               cavium,t-rd-hld = <35>;
+                               cavium,t-wr-hld = <45>;
+                               cavium,t-pause  = <0>;
+                               cavium,t-wait   = <0>;
+                               cavium,t-page   = <35>;
+                               cavium,t-rd-dly = <0>;
+
+                               cavium,pages     = <0>;
+                               cavium,bus-width = <8>;
+                       };
+                       cavium,cs-config@4 {
+                               compatible = "cavium,octeon-3860-bootbus-config";
+                               cavium,cs-index = <4>;
+                               cavium,t-adr  = <320>;
+                               cavium,t-ce   = <320>;
+                               cavium,t-oe   = <320>;
+                               cavium,t-we   = <320>;
+                               cavium,t-rd-hld = <320>;
+                               cavium,t-wr-hld = <320>;
+                               cavium,t-pause  = <320>;
+                               cavium,t-wait   = <320>;
+                               cavium,t-page   = <320>;
+                               cavium,t-rd-dly = <0>;
+
+                               cavium,pages     = <0>;
+                               cavium,bus-width = <8>;
+                       };
+                       cavium,cs-config@5 {
+                               compatible = "cavium,octeon-3860-bootbus-config";
+                               cavium,cs-index = <5>;
+                               cavium,t-adr  = <5>;
+                               cavium,t-ce   = <300>;
+                               cavium,t-oe   = <125>;
+                               cavium,t-we   = <150>;
+                               cavium,t-rd-hld = <100>;
+                               cavium,t-wr-hld = <30>;
+                               cavium,t-pause  = <0>;
+                               cavium,t-wait   = <30>;
+                               cavium,t-page   = <320>;
+                               cavium,t-rd-dly = <0>;
+
+                               cavium,pages     = <0>;
+                               cavium,bus-width = <16>;
+                       };
+                       cavium,cs-config@6 {
+                               compatible = "cavium,octeon-3860-bootbus-config";
+                               cavium,cs-index = <6>;
+                               cavium,t-adr  = <5>;
+                               cavium,t-ce   = <300>;
+                               cavium,t-oe   = <270>;
+                               cavium,t-we   = <150>;
+                               cavium,t-rd-hld = <100>;
+                               cavium,t-wr-hld = <70>;
+                               cavium,t-pause  = <0>;
+                               cavium,t-wait   = <0>;
+                               cavium,t-page   = <320>;
+                               cavium,t-rd-dly = <0>;
+
+                               cavium,pages     = <0>;
+                               cavium,wait-mode;
+                               cavium,bus-width = <16>;
+                       };
+
+                       flash0: nor@0,0 {
+                               compatible = "cfi-flash";
+                               reg = <0 0 0x800000>;
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                       };
+
+                       led0: led-display@4,0 {
+                               compatible = "avago,hdsp-253x";
+                               reg = <4 0x20 0x20>, <4 0 0x20>;
+                       };
+
+                       cf0: compact-flash@5,0 {
+                               compatible = "cavium,ebt3000-compact-flash";
+                               reg = <5 0 0x10000>, <6 0 0x10000>;
+                               cavium,bus-width = <16>;
+                               cavium,true-ide;
+                               cavium,dma-engine-handle = <&dma0>;
+                       };
+               };
+
+               dma0: dma-engine@1180000000100 {
+                       compatible = "cavium,octeon-5750-bootbus-dma";
+                       reg = <0x11800 0x00000100 0x0 0x8>;
+                       interrupts = <0 63>;
+               };
+               dma1: dma-engine@1180000000108 {
+                       compatible = "cavium,octeon-5750-bootbus-dma";
+                       reg = <0x11800 0x00000108 0x0 0x8>;
+                       interrupts = <0 63>;
+               };
+
+               uctl: uctl@118006f000000 {
+                       compatible = "cavium,octeon-6335-uctl";
+                       reg = <0x11800 0x6f000000 0x0 0x100>;
+                       ranges; /* Direct mapping */
+                       #address-cells = <2>;
+                       #size-cells = <2>;
+                       /* 12MHz, 24MHz and 48MHz allowed */
+                       refclk-frequency = <12000000>;
+                       /* Either "crystal" or "external" */
+                       refclk-type = "crystal";
+
+                       ehci@16f0000000000 {
+                               compatible = "cavium,octeon-6335-ehci","usb-ehci";
+                               reg = <0x16f00 0x00000000 0x0 0x100>;
+                               interrupts = <0 56>;
+                               big-endian-regs;
+                       };
+                       ohci@16f0000000400 {
+                               compatible = "cavium,octeon-6335-ohci","usb-ohci";
+                               reg = <0x16f00 0x00000400 0x0 0x100>;
+                               interrupts = <0 56>;
+                               big-endian-regs;
+                       };
+               };
+       };
+
+       aliases {
+               mix0 = &mix0;
+               mix1 = &mix1;
+               pip = &pip;
+               smi0 = &smi0;
+               smi1 = &smi1;
+               twsi0 = &twsi0;
+               twsi1 = &twsi1;
+               uart0 = &uart0;
+               uart1 = &uart1;
+               uart2 = &uart2;
+               flash0 = &flash0;
+               cf0 = &cf0;
+               uctl = &uctl;
+               led0 = &led0;
+       };
+ };
diff --git a/arch/mips/cavium-octeon/octeon_68xx.dts b/arch/mips/cavium-octeon/octeon_68xx.dts
new file mode 100644 (file)
index 0000000..1839468
--- /dev/null
@@ -0,0 +1,625 @@
+/dts-v1/;
+/*
+ * OCTEON 68XX device tree skeleton.
+ *
+ * This device tree is pruned and patched by early boot code before
+ * use.  Because of this, it contains a super-set of the available
+ * devices and properties.
+ */
+/ {
+       compatible = "cavium,octeon-6880";
+       #address-cells = <2>;
+       #size-cells = <2>;
+       interrupt-parent = <&ciu2>;
+
+       soc@0 {
+               compatible = "simple-bus";
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges; /* Direct mapping */
+
+               ciu2: interrupt-controller@1070100000000 {
+                       compatible = "cavium,octeon-6880-ciu2";
+                       interrupt-controller;
+                       /* Interrupts are specified by two parts:
+                        * 1) Controller register (0 or 7)
+                        * 2) Bit within the register (0..63)
+                        */
+                       #address-cells = <0>;
+                       #interrupt-cells = <2>;
+                       reg = <0x10701 0x00000000 0x0 0x4000000>;
+               };
+
+               gpio: gpio-controller@1070000000800 {
+                       #gpio-cells = <2>;
+                       compatible = "cavium,octeon-3860-gpio";
+                       reg = <0x10700 0x00000800 0x0 0x100>;
+                       gpio-controller;
+                       /* Interrupts are specified by two parts:
+                        * 1) GPIO pin number (0..15)
+                        * 2) Triggering (1 - edge rising
+                        *                2 - edge falling
+                        *                4 - level active high
+                        *                8 - level active low)
+                        */
+                       interrupt-controller;
+                       #interrupt-cells = <2>;
+                       /* The GPIO pins connect to 16 consecutive CUI bits */
+                       interrupts = <7 0>,  <7 1>,  <7 2>,  <7 3>,
+                                    <7 4>,  <7 5>,  <7 6>,  <7 7>,
+                                    <7 8>,  <7 9>,  <7 10>, <7 11>,
+                                    <7 12>, <7 13>, <7 14>, <7 15>;
+               };
+
+               smi0: mdio@1180000003800 {
+                       compatible = "cavium,octeon-3860-mdio";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg = <0x11800 0x00003800 0x0 0x40>;
+
+                       phy0: ethernet-phy@6 {
+                               compatible = "marvell,88e1118";
+                               marvell,reg-init =
+                                       /* Fix rx and tx clock transition timing */
+                                       <2 0x15 0xffcf 0>, /* Reg 2,21 Clear bits 4, 5 */
+                                       /* Adjust LED drive. */
+                                       <3 0x11 0 0x442a>, /* Reg 3,17 <- 0442a */
+                                       /* irq, blink-activity, blink-link */
+                                       <3 0x10 0 0x0242>; /* Reg 3,16 <- 0x0242 */
+                               reg = <6>;
+                       };
+
+                       phy1: ethernet-phy@1 {
+                               cavium,qlm-trim = "4,sgmii";
+                               reg = <1>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy2: ethernet-phy@2 {
+                               cavium,qlm-trim = "4,sgmii";
+                               reg = <2>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy3: ethernet-phy@3 {
+                               cavium,qlm-trim = "4,sgmii";
+                               reg = <3>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy4: ethernet-phy@4 {
+                               cavium,qlm-trim = "4,sgmii";
+                               reg = <4>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+               };
+
+               smi1: mdio@1180000003880 {
+                       compatible = "cavium,octeon-3860-mdio";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg = <0x11800 0x00003880 0x0 0x40>;
+
+                       phy41: ethernet-phy@1 {
+                               cavium,qlm-trim = "0,sgmii";
+                               reg = <1>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy42: ethernet-phy@2 {
+                               cavium,qlm-trim = "0,sgmii";
+                               reg = <2>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy43: ethernet-phy@3 {
+                               cavium,qlm-trim = "0,sgmii";
+                               reg = <3>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy44: ethernet-phy@4 {
+                               cavium,qlm-trim = "0,sgmii";
+                               reg = <4>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+               };
+
+               smi2: mdio@1180000003900 {
+                       compatible = "cavium,octeon-3860-mdio";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg = <0x11800 0x00003900 0x0 0x40>;
+
+                       phy21: ethernet-phy@1 {
+                               cavium,qlm-trim = "2,sgmii";
+                               reg = <1>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy22: ethernet-phy@2 {
+                               cavium,qlm-trim = "2,sgmii";
+                               reg = <2>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy23: ethernet-phy@3 {
+                               cavium,qlm-trim = "2,sgmii";
+                               reg = <3>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy24: ethernet-phy@4 {
+                               cavium,qlm-trim = "2,sgmii";
+                               reg = <4>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+               };
+
+               smi3: mdio@1180000003980 {
+                       compatible = "cavium,octeon-3860-mdio";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg = <0x11800 0x00003980 0x0 0x40>;
+
+                       phy11: ethernet-phy@1 {
+                               cavium,qlm-trim = "3,sgmii";
+                               reg = <1>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy12: ethernet-phy@2 {
+                               cavium,qlm-trim = "3,sgmii";
+                               reg = <2>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy13: ethernet-phy@3 {
+                               cavium,qlm-trim = "3,sgmii";
+                               reg = <3>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+                       phy14: ethernet-phy@4 {
+                               cavium,qlm-trim = "3,sgmii";
+                               reg = <4>;
+                               compatible = "marvell,88e1149r";
+                               marvell,reg-init = <3 0x10 0 0x5777>,
+                                       <3 0x11 0 0x00aa>,
+                                       <3 0x12 0 0x4105>,
+                                       <3 0x13 0 0x0a60>;
+                       };
+               };
+
+               mix0: ethernet@1070000100000 {
+                       compatible = "cavium,octeon-5750-mix";
+                       reg = <0x10700 0x00100000 0x0 0x100>, /* MIX */
+                             <0x11800 0xE0000000 0x0 0x300>, /* AGL */
+                             <0x11800 0xE0000400 0x0 0x400>, /* AGL_SHARED  */
+                             <0x11800 0xE0002000 0x0 0x8>;   /* AGL_PRT_CTL */
+                       cell-index = <0>;
+                       interrupts = <6 40>, <6 32>;
+                       local-mac-address = [ 00 00 00 00 00 00 ];
+                       phy-handle = <&phy0>;
+               };
+
+               pip: pip@11800a0000000 {
+                       compatible = "cavium,octeon-3860-pip";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg = <0x11800 0xa0000000 0x0 0x2000>;
+
+                       interface@4 {
+                               compatible = "cavium,octeon-3860-pip-interface";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               reg = <0x4>; /* interface */
+
+                               ethernet@0 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x0>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy1>;
+                               };
+                               ethernet@1 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x1>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy2>;
+                               };
+                               ethernet@2 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x2>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy3>;
+                               };
+                               ethernet@3 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x3>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy4>;
+                               };
+                       };
+
+                       interface@3 {
+                               compatible = "cavium,octeon-3860-pip-interface";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               reg = <0x3>; /* interface */
+
+                               ethernet@0 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x0>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy11>;
+                               };
+                               ethernet@1 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x1>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy12>;
+                               };
+                               ethernet@2 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x2>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy13>;
+                               };
+                               ethernet@3 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x3>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy14>;
+                               };
+                       };
+
+                       interface@2 {
+                               compatible = "cavium,octeon-3860-pip-interface";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               reg = <0x2>; /* interface */
+
+                               ethernet@0 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x0>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy21>;
+                               };
+                               ethernet@1 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x1>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy22>;
+                               };
+                               ethernet@2 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x2>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy23>;
+                               };
+                               ethernet@3 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x3>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy24>;
+                               };
+                       };
+
+                       interface@1 {
+                               compatible = "cavium,octeon-3860-pip-interface";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               reg = <0x1>; /* interface */
+
+                               ethernet@0 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x0>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                               };
+                       };
+
+                       interface@0 {
+                               compatible = "cavium,octeon-3860-pip-interface";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               reg = <0x0>; /* interface */
+
+                               ethernet@0 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x0>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy41>;
+                               };
+                               ethernet@1 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x1>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy42>;
+                               };
+                               ethernet@2 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x2>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy43>;
+                               };
+                               ethernet@3 {
+                                       compatible = "cavium,octeon-3860-pip-port";
+                                       reg = <0x3>; /* Port */
+                                       local-mac-address = [ 00 00 00 00 00 00 ];
+                                       phy-handle = <&phy44>;
+                               };
+                       };
+               };
+
+               twsi0: i2c@1180000001000 {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       compatible = "cavium,octeon-3860-twsi";
+                       reg = <0x11800 0x00001000 0x0 0x200>;
+                       interrupts = <3 32>;
+                       clock-frequency = <100000>;
+
+                       rtc@68 {
+                               compatible = "dallas,ds1337";
+                               reg = <0x68>;
+                       };
+                       tmp@4c {
+                               compatible = "ti,tmp421";
+                               reg = <0x4c>;
+                       };
+               };
+
+               twsi1: i2c@1180000001200 {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       compatible = "cavium,octeon-3860-twsi";
+                       reg = <0x11800 0x00001200 0x0 0x200>;
+                       interrupts = <3 33>;
+                       clock-frequency = <100000>;
+               };
+
+               uart0: serial@1180000000800 {
+                       compatible = "cavium,octeon-3860-uart","ns16550";
+                       reg = <0x11800 0x00000800 0x0 0x400>;
+                       clock-frequency = <0>;
+                       current-speed = <115200>;
+                       reg-shift = <3>;
+                       interrupts = <3 36>;
+               };
+
+               uart1: serial@1180000000c00 {
+                       compatible = "cavium,octeon-3860-uart","ns16550";
+                       reg = <0x11800 0x00000c00 0x0 0x400>;
+                       clock-frequency = <0>;
+                       current-speed = <115200>;
+                       reg-shift = <3>;
+                       interrupts = <3 37>;
+               };
+
+               bootbus: bootbus@1180000000000 {
+                       compatible = "cavium,octeon-3860-bootbus";
+                       reg = <0x11800 0x00000000 0x0 0x200>;
+                       /* The chip select number and offset */
+                       #address-cells = <2>;
+                       /* The size of the chip select region */
+                       #size-cells = <1>;
+                       ranges = <0 0  0       0x1f400000  0xc00000>,
+                                <1 0  0x10000 0x30000000  0>,
+                                <2 0  0x10000 0x40000000  0>,
+                                <3 0  0x10000 0x50000000  0>,
+                                <4 0  0       0x1d020000  0x10000>,
+                                <5 0  0       0x1d040000  0x10000>,
+                                <6 0  0       0x1d050000  0x10000>,
+                                <7 0  0x10000 0x90000000  0>;
+
+                       cavium,cs-config@0 {
+                               compatible = "cavium,octeon-3860-bootbus-config";
+                               cavium,cs-index = <0>;
+                               cavium,t-adr  = <10>;
+                               cavium,t-ce   = <50>;
+                               cavium,t-oe   = <50>;
+                               cavium,t-we   = <35>;
+                               cavium,t-rd-hld = <25>;
+                               cavium,t-wr-hld = <35>;
+                               cavium,t-pause  = <0>;
+                               cavium,t-wait   = <300>;
+                               cavium,t-page   = <25>;
+                               cavium,t-rd-dly = <0>;
+
+                               cavium,pages     = <0>;
+                               cavium,bus-width = <8>;
+                       };
+                       cavium,cs-config@4 {
+                               compatible = "cavium,octeon-3860-bootbus-config";
+                               cavium,cs-index = <4>;
+                               cavium,t-adr  = <320>;
+                               cavium,t-ce   = <320>;
+                               cavium,t-oe   = <320>;
+                               cavium,t-we   = <320>;
+                               cavium,t-rd-hld = <320>;
+                               cavium,t-wr-hld = <320>;
+                               cavium,t-pause  = <320>;
+                               cavium,t-wait   = <320>;
+                               cavium,t-page   = <320>;
+                               cavium,t-rd-dly = <0>;
+
+                               cavium,pages     = <0>;
+                               cavium,bus-width = <8>;
+                       };
+                       cavium,cs-config@5 {
+                               compatible = "cavium,octeon-3860-bootbus-config";
+                               cavium,cs-index = <5>;
+                               cavium,t-adr  = <0>;
+                               cavium,t-ce   = <300>;
+                               cavium,t-oe   = <125>;
+                               cavium,t-we   = <150>;
+                               cavium,t-rd-hld = <100>;
+                               cavium,t-wr-hld = <300>;
+                               cavium,t-pause  = <0>;
+                               cavium,t-wait   = <300>;
+                               cavium,t-page   = <310>;
+                               cavium,t-rd-dly = <0>;
+
+                               cavium,pages     = <0>;
+                               cavium,bus-width = <16>;
+                       };
+                       cavium,cs-config@6 {
+                               compatible = "cavium,octeon-3860-bootbus-config";
+                               cavium,cs-index = <6>;
+                               cavium,t-adr  = <0>;
+                               cavium,t-ce   = <30>;
+                               cavium,t-oe   = <125>;
+                               cavium,t-we   = <150>;
+                               cavium,t-rd-hld = <100>;
+                               cavium,t-wr-hld = <30>;
+                               cavium,t-pause  = <0>;
+                               cavium,t-wait   = <30>;
+                               cavium,t-page   = <310>;
+                               cavium,t-rd-dly = <0>;
+
+                               cavium,pages     = <0>;
+                               cavium,wait-mode;
+                               cavium,bus-width = <16>;
+                       };
+
+                       flash0: nor@0,0 {
+                               compatible = "cfi-flash";
+                               reg = <0 0 0x800000>;
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+
+                               partition@0 {
+                                       label = "bootloader";
+                                       reg = <0 0x200000>;
+                                       read-only;
+                               };
+                               partition@200000 {
+                                       label = "kernel";
+                                       reg = <0x200000 0x200000>;
+                               };
+                               partition@400000 {
+                                       label = "cramfs";
+                                       reg = <0x400000 0x3fe000>;
+                               };
+                               partition@7fe000 {
+                                       label = "environment";
+                                       reg = <0x7fe000 0x2000>;
+                                       read-only;
+                               };
+                       };
+
+                       led0: led-display@4,0 {
+                               compatible = "avago,hdsp-253x";
+                               reg = <4 0x20 0x20>, <4 0 0x20>;
+                       };
+
+                       compact-flash@5,0 {
+                               compatible = "cavium,ebt3000-compact-flash";
+                               reg = <5 0 0x10000>, <6 0 0x10000>;
+                               cavium,bus-width = <16>;
+                               cavium,true-ide;
+                               cavium,dma-engine-handle = <&dma0>;
+                       };
+               };
+
+               dma0: dma-engine@1180000000100 {
+                       compatible = "cavium,octeon-5750-bootbus-dma";
+                       reg = <0x11800 0x00000100 0x0 0x8>;
+                       interrupts = <0 63>;
+               };
+               dma1: dma-engine@1180000000108 {
+                       compatible = "cavium,octeon-5750-bootbus-dma";
+                       reg = <0x11800 0x00000108 0x0 0x8>;
+                       interrupts = <0 63>;
+               };
+
+               uctl: uctl@118006f000000 {
+                       compatible = "cavium,octeon-6335-uctl";
+                       reg = <0x11800 0x6f000000 0x0 0x100>;
+                       ranges; /* Direct mapping */
+                       #address-cells = <2>;
+                       #size-cells = <2>;
+                       /* 12MHz, 24MHz and 48MHz allowed */
+                       refclk-frequency = <12000000>;
+                       /* Either "crystal" or "external" */
+                       refclk-type = "crystal";
+
+                       ehci@16f0000000000 {
+                               compatible = "cavium,octeon-6335-ehci","usb-ehci";
+                               reg = <0x16f00 0x00000000 0x0 0x100>;
+                               interrupts = <3 44>;
+                               big-endian-regs;
+                       };
+                       ohci@16f0000000400 {
+                               compatible = "cavium,octeon-6335-ohci","usb-ohci";
+                               reg = <0x16f00 0x00000400 0x0 0x100>;
+                               interrupts = <3 44>;
+                               big-endian-regs;
+                       };
+               };
+       };
+
+       aliases {
+               mix0 = &mix0;
+               pip = &pip;
+               smi0 = &smi0;
+               smi1 = &smi1;
+               smi2 = &smi2;
+               smi3 = &smi3;
+               twsi0 = &twsi0;
+               twsi1 = &twsi1;
+               uart0 = &uart0;
+               uart1 = &uart1;
+               uctl = &uctl;
+               led0 = &led0;
+               flash0 = &flash0;
+       };
+ };
index 057f0ae88c994e6052892d4fc44620c0da7d26fa..138b2216b4f8ba396be6ae788e9a9b3215a13630 100644 (file)
@@ -43,95 +43,67 @@ void octeon_serial_out(struct uart_port *up, int offset, int value)
        cvmx_write_csr((uint64_t)(up->membase + (offset << 3)), (u8)value);
 }
 
-/*
- * Allocated in .bss, so it is all zeroed.
- */
-#define OCTEON_MAX_UARTS 3
-static struct plat_serial8250_port octeon_uart8250_data[OCTEON_MAX_UARTS + 1];
-static struct platform_device octeon_uart8250_device = {
-       .name                   = "serial8250",
-       .id                     = PLAT8250_DEV_PLATFORM,
-       .dev                    = {
-               .platform_data  = octeon_uart8250_data,
-       },
-};
-
-static void __init octeon_uart_set_common(struct plat_serial8250_port *p)
+static int __devinit octeon_serial_probe(struct platform_device *pdev)
 {
-       p->flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
-       p->type = PORT_OCTEON;
-       p->iotype = UPIO_MEM;
-       p->regshift = 3;        /* I/O addresses are every 8 bytes */
+       int irq, res;
+       struct resource *res_mem;
+       struct uart_port port;
+
+       /* All adaptors have an irq.  */
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
+
+       memset(&port, 0, sizeof(port));
+
+       port.flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
+       port.type = PORT_OCTEON;
+       port.iotype = UPIO_MEM;
+       port.regshift = 3;
+       port.dev = &pdev->dev;
+
        if (octeon_is_simulation())
                /* Make simulator output fast*/
-               p->uartclk = 115200 * 16;
+               port.uartclk = 115200 * 16;
        else
-               p->uartclk = octeon_get_io_clock_rate();
-       p->serial_in = octeon_serial_in;
-       p->serial_out = octeon_serial_out;
-}
+               port.uartclk = octeon_get_io_clock_rate();
 
-static int __init octeon_serial_init(void)
-{
-       int enable_uart0;
-       int enable_uart1;
-       int enable_uart2;
-       struct plat_serial8250_port *p;
-
-#ifdef CONFIG_CAVIUM_OCTEON_2ND_KERNEL
-       /*
-        * If we are configured to run as the second of two kernels,
-        * disable uart0 and enable uart1. Uart0 is owned by the first
-        * kernel
-        */
-       enable_uart0 = 0;
-       enable_uart1 = 1;
-#else
-       /*
-        * We are configured for the first kernel. We'll enable uart0
-        * if the bootloader told us to use 0, otherwise will enable
-        * uart 1.
-        */
-       enable_uart0 = (octeon_get_boot_uart() == 0);
-       enable_uart1 = (octeon_get_boot_uart() == 1);
-#ifdef CONFIG_KGDB
-       enable_uart1 = 1;
-#endif
-#endif
-
-       /* Right now CN52XX is the only chip with a third uart */
-       enable_uart2 = OCTEON_IS_MODEL(OCTEON_CN52XX);
-
-       p = octeon_uart8250_data;
-       if (enable_uart0) {
-               /* Add a ttyS device for hardware uart 0 */
-               octeon_uart_set_common(p);
-               p->membase = (void *) CVMX_MIO_UARTX_RBR(0);
-               p->mapbase = CVMX_MIO_UARTX_RBR(0) & ((1ull << 49) - 1);
-               p->irq = OCTEON_IRQ_UART0;
-               p++;
-       }
+       port.serial_in = octeon_serial_in;
+       port.serial_out = octeon_serial_out;
+       port.irq = irq;
 
-       if (enable_uart1) {
-               /* Add a ttyS device for hardware uart 1 */
-               octeon_uart_set_common(p);
-               p->membase = (void *) CVMX_MIO_UARTX_RBR(1);
-               p->mapbase = CVMX_MIO_UARTX_RBR(1) & ((1ull << 49) - 1);
-               p->irq = OCTEON_IRQ_UART1;
-               p++;
-       }
-       if (enable_uart2) {
-               /* Add a ttyS device for hardware uart 2 */
-               octeon_uart_set_common(p);
-               p->membase = (void *) CVMX_MIO_UART2_RBR;
-               p->mapbase = CVMX_MIO_UART2_RBR & ((1ull << 49) - 1);
-               p->irq = OCTEON_IRQ_UART2;
-               p++;
+       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res_mem == NULL) {
+               dev_err(&pdev->dev, "found no memory resource\n");
+               return -ENXIO;
        }
+       port.mapbase = res_mem->start;
+       port.membase = ioremap(res_mem->start, resource_size(res_mem));
 
-       BUG_ON(p > &octeon_uart8250_data[OCTEON_MAX_UARTS]);
+       res = serial8250_register_port(&port);
 
-       return platform_device_register(&octeon_uart8250_device);
+       return res >= 0 ? 0 : res;
 }
 
-device_initcall(octeon_serial_init);
+static struct of_device_id octeon_serial_match[] = {
+       {
+               .compatible = "cavium,octeon-3860-uart",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, octeon_serial_match);
+
+static struct platform_driver octeon_serial_driver = {
+       .probe          = octeon_serial_probe,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "octeon_serial",
+               .of_match_table = octeon_serial_match,
+       },
+};
+
+static int __init octeon_serial_init(void)
+{
+       return platform_driver_register(&octeon_serial_driver);
+}
+late_initcall(octeon_serial_init);
index 260dc247c052ca5874c23594f0db926a7fcfb1eb..919b0fb7bb1a779d77551d9743fe9d321fb6b9c3 100644 (file)
@@ -21,6 +21,8 @@
 #include <linux/platform_device.h>
 #include <linux/serial_core.h>
 #include <linux/serial_8250.h>
+#include <linux/of_fdt.h>
+#include <linux/libfdt.h>
 
 #include <asm/processor.h>
 #include <asm/reboot.h>
@@ -775,3 +777,46 @@ void prom_free_prom_memory(void)
        }
 #endif
 }
+
+int octeon_prune_device_tree(void);
+
+extern const char __dtb_octeon_3xxx_begin;
+extern const char __dtb_octeon_3xxx_end;
+extern const char __dtb_octeon_68xx_begin;
+extern const char __dtb_octeon_68xx_end;
+void __init device_tree_init(void)
+{
+       int dt_size;
+       struct boot_param_header *fdt;
+       bool do_prune;
+
+       if (octeon_bootinfo->minor_version >= 3 && octeon_bootinfo->fdt_addr) {
+               fdt = phys_to_virt(octeon_bootinfo->fdt_addr);
+               if (fdt_check_header(fdt))
+                       panic("Corrupt Device Tree passed to kernel.");
+               dt_size = be32_to_cpu(fdt->totalsize);
+               do_prune = false;
+       } else if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+               fdt = (struct boot_param_header *)&__dtb_octeon_68xx_begin;
+               dt_size = &__dtb_octeon_68xx_end - &__dtb_octeon_68xx_begin;
+               do_prune = true;
+       } else {
+               fdt = (struct boot_param_header *)&__dtb_octeon_3xxx_begin;
+               dt_size = &__dtb_octeon_3xxx_end - &__dtb_octeon_3xxx_begin;
+               do_prune = true;
+       }
+
+       /* Copy the default tree from init memory. */
+       initial_boot_params = early_init_dt_alloc_memory_arch(dt_size, 8);
+       if (initial_boot_params == NULL)
+               panic("Could not allocate initial_boot_params\n");
+       memcpy(initial_boot_params, fdt, dt_size);
+
+       if (do_prune) {
+               octeon_prune_device_tree();
+               pr_info("Using internal Device Tree.\n");
+       } else {
+               pr_info("Using passed Device Tree.\n");
+       }
+       unflatten_device_tree();
+}
diff --git a/arch/mips/configs/ls1b_defconfig b/arch/mips/configs/ls1b_defconfig
new file mode 100644 (file)
index 0000000..80cff8b
--- /dev/null
@@ -0,0 +1,109 @@
+CONFIG_MACH_LOONGSON1=y
+CONFIG_PREEMPT=y
+# CONFIG_SECCOMP is not set
+CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_NAMESPACES=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_RD_BZIP2=y
+CONFIG_RD_LZMA=y
+CONFIG_EXPERT=y
+CONFIG_PERF_EVENTS=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_SUSPEND is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_WIRELESS is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_STANDALONE is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_SCSI=m
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=m
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CHELSIO is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_DA=y
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_WLAN is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_LEGACY_PTY_COUNT=8
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_USB_HID=m
+CONFIG_HID_GENERIC=m
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_STORAGE=m
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_PL2303=m
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_LOONGSON1=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_DNOTIFY is not set
+CONFIG_VFAT_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_ISO8859_1=m
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_FTRACE is not set
+# CONFIG_EARLY_PRINTK is not set
index d0b857d98c91703b01950f9d1a705c1048cd7ac0..138f698d7c00048c4b172718891bac49ef778b61 100644 (file)
@@ -367,6 +367,10 @@ CONFIG_SERIAL_8250_RSA=y
 CONFIG_HW_RANDOM=y
 CONFIG_HW_RANDOM_TIMERIOMEM=m
 CONFIG_RAW_DRIVER=m
+CONFIG_I2C=y
+CONFIG_I2C_XLR=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_DS1374=y
 # CONFIG_HWMON is not set
 # CONFIG_VGA_CONSOLE is not set
 # CONFIG_HID_SUPPORT is not set
index e95ff3054ff61f83bc56a0458142046655daba6c..8c62316f22f438b4b9cc18b23585f3e0ad8b7258 100644 (file)
@@ -101,7 +101,7 @@ void __init prom_free_prom_memory(void)
         * the first page reserved for the exception handlers.
         */
 
-#if defined(CONFIG_DECLANCE) || defined(CONFIG_DECLANCE_MODULE)
+#if IS_ENABLED(CONFIG_DECLANCE)
        /*
         * Leave 128 KB reserved for Lance memory for
         * IOASIC DECstations.
index 83894aa7932cbd13c22f39c76bf3f040de1979f7..c9456e7a7283dfa15b507f021bb3f8f6696a45ca 100644 (file)
@@ -50,15 +50,4 @@ void clk_recalc_rate(struct clk *);
 int clk_register(struct clk *);
 void clk_unregister(struct clk *);
 
-/* the exported API, in addition to clk_set_rate */
-/**
- * clk_set_rate_ex - set the clock rate for a clock source, with additional parameter
- * @clk: clock source
- * @rate: desired clock rate in Hz
- * @algo_id: algorithm id to be passed down to ops->set_rate
- *
- * Returns success (0) or negative errno.
- */
-int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id);
-
 #endif                         /* __ASM_MIPS_CLOCK_H */
index 95e40c1e8ed114a3a95ad9cab9794e129ccbdca2..f21b7c04e95a2f70073e087e69d384e53d11862f 100644 (file)
 #define PRID_REV_VR4181A       0x0070  /* Same as VR4122 */
 #define PRID_REV_VR4130                0x0080
 #define PRID_REV_34K_V1_0_2    0x0022
+#define PRID_REV_LOONGSON1B    0x0020
 #define PRID_REV_LOONGSON2E    0x0002
 #define PRID_REV_LOONGSON2F    0x0003
 
@@ -261,7 +262,7 @@ enum cpu_type_enum {
         */
        CPU_4KC, CPU_4KEC, CPU_4KSC, CPU_24K, CPU_34K, CPU_1004K, CPU_74K,
        CPU_ALCHEMY, CPU_PR4450, CPU_BMIPS32, CPU_BMIPS3300, CPU_BMIPS4350,
-       CPU_BMIPS4380, CPU_BMIPS5000, CPU_JZRISC, CPU_M14KC,
+       CPU_BMIPS4380, CPU_BMIPS5000, CPU_JZRISC, CPU_LOONGSON1, CPU_M14KC,
 
        /*
         * MIPS64 class processors
index 5b8d15bb5fe85a4b6d234f6aaf006d9e451efb8d..e104ddb694a88b0c6a0556a2eef8dad22a3a537f 100644 (file)
@@ -9,6 +9,7 @@
  * compile time if only one CPU support is enabled (idea stolen from
  * arm mach-types)
  */
+#define BCM6328_CPU_ID         0x6328
 #define BCM6338_CPU_ID         0x6338
 #define BCM6345_CPU_ID         0x6345
 #define BCM6348_CPU_ID         0x6348
@@ -20,6 +21,19 @@ u16 __bcm63xx_get_cpu_id(void);
 u16 bcm63xx_get_cpu_rev(void);
 unsigned int bcm63xx_get_cpu_freq(void);
 
+#ifdef CONFIG_BCM63XX_CPU_6328
+# ifdef bcm63xx_get_cpu_id
+#  undef bcm63xx_get_cpu_id
+#  define bcm63xx_get_cpu_id() __bcm63xx_get_cpu_id()
+#  define BCMCPU_RUNTIME_DETECT
+# else
+#  define bcm63xx_get_cpu_id() BCM6328_CPU_ID
+# endif
+# define BCMCPU_IS_6328()      (bcm63xx_get_cpu_id() == BCM6328_CPU_ID)
+#else
+# define BCMCPU_IS_6328()      (0)
+#endif
+
 #ifdef CONFIG_BCM63XX_CPU_6338
 # ifdef bcm63xx_get_cpu_id
 #  undef bcm63xx_get_cpu_id
@@ -102,13 +116,13 @@ enum bcm63xx_regs_set {
        RSET_UART1,
        RSET_GPIO,
        RSET_SPI,
-       RSET_SPI2,
        RSET_UDC0,
        RSET_OHCI0,
        RSET_OHCI_PRIV,
        RSET_USBH_PRIV,
        RSET_MPI,
        RSET_PCMCIA,
+       RSET_PCIE,
        RSET_DSL,
        RSET_ENET0,
        RSET_ENET1,
@@ -130,11 +144,17 @@ enum bcm63xx_regs_set {
        RSET_PCMDMA,
        RSET_PCMDMAC,
        RSET_PCMDMAS,
+       RSET_RNG,
+       RSET_MISC
 };
 
 #define RSET_DSL_LMEM_SIZE             (64 * 1024 * 4)
 #define RSET_DSL_SIZE                  4096
 #define RSET_WDT_SIZE                  12
+#define BCM_6338_RSET_SPI_SIZE         64
+#define BCM_6348_RSET_SPI_SIZE         64
+#define BCM_6358_RSET_SPI_SIZE         1804
+#define BCM_6368_RSET_SPI_SIZE         1804
 #define RSET_ENET_SIZE                 2048
 #define RSET_ENETDMA_SIZE              2048
 #define RSET_ENETSW_SIZE               65536
@@ -149,7 +169,52 @@ enum bcm63xx_regs_set {
 #define RSET_XTMDMA_SIZE               256
 #define RSET_XTMDMAC_SIZE(chans)       (16 * (chans))
 #define RSET_XTMDMAS_SIZE(chans)       (16 * (chans))
+#define RSET_RNG_SIZE                  20
 
+/*
+ * 6328 register sets base address
+ */
+#define BCM_6328_DSL_LMEM_BASE         (0xdeadbeef)
+#define BCM_6328_PERF_BASE             (0xb0000000)
+#define BCM_6328_TIMER_BASE            (0xb0000040)
+#define BCM_6328_WDT_BASE              (0xb000005c)
+#define BCM_6328_UART0_BASE             (0xb0000100)
+#define BCM_6328_UART1_BASE            (0xb0000120)
+#define BCM_6328_GPIO_BASE             (0xb0000080)
+#define BCM_6328_SPI_BASE              (0xdeadbeef)
+#define BCM_6328_UDC0_BASE             (0xdeadbeef)
+#define BCM_6328_USBDMA_BASE           (0xdeadbeef)
+#define BCM_6328_OHCI0_BASE            (0xdeadbeef)
+#define BCM_6328_OHCI_PRIV_BASE                (0xdeadbeef)
+#define BCM_6328_USBH_PRIV_BASE                (0xdeadbeef)
+#define BCM_6328_MPI_BASE              (0xdeadbeef)
+#define BCM_6328_PCMCIA_BASE           (0xdeadbeef)
+#define BCM_6328_PCIE_BASE             (0xb0e40000)
+#define BCM_6328_SDRAM_REGS_BASE       (0xdeadbeef)
+#define BCM_6328_DSL_BASE              (0xb0001900)
+#define BCM_6328_UBUS_BASE             (0xdeadbeef)
+#define BCM_6328_ENET0_BASE            (0xdeadbeef)
+#define BCM_6328_ENET1_BASE            (0xdeadbeef)
+#define BCM_6328_ENETDMA_BASE          (0xb000d800)
+#define BCM_6328_ENETDMAC_BASE         (0xb000da00)
+#define BCM_6328_ENETDMAS_BASE         (0xb000dc00)
+#define BCM_6328_ENETSW_BASE           (0xb0e00000)
+#define BCM_6328_EHCI0_BASE            (0x10002500)
+#define BCM_6328_SDRAM_BASE            (0xdeadbeef)
+#define BCM_6328_MEMC_BASE             (0xdeadbeef)
+#define BCM_6328_DDR_BASE              (0xb0003000)
+#define BCM_6328_M2M_BASE              (0xdeadbeef)
+#define BCM_6328_ATM_BASE              (0xdeadbeef)
+#define BCM_6328_XTM_BASE              (0xdeadbeef)
+#define BCM_6328_XTMDMA_BASE           (0xb000b800)
+#define BCM_6328_XTMDMAC_BASE          (0xdeadbeef)
+#define BCM_6328_XTMDMAS_BASE          (0xdeadbeef)
+#define BCM_6328_PCM_BASE              (0xb000a800)
+#define BCM_6328_PCMDMA_BASE           (0xdeadbeef)
+#define BCM_6328_PCMDMAC_BASE          (0xdeadbeef)
+#define BCM_6328_PCMDMAS_BASE          (0xdeadbeef)
+#define BCM_6328_RNG_BASE              (0xdeadbeef)
+#define BCM_6328_MISC_BASE             (0xb0001800)
 /*
  * 6338 register sets base address
  */
@@ -162,7 +227,6 @@ enum bcm63xx_regs_set {
 #define BCM_6338_UART1_BASE            (0xdeadbeef)
 #define BCM_6338_GPIO_BASE             (0xfffe0400)
 #define BCM_6338_SPI_BASE              (0xfffe0c00)
-#define BCM_6338_SPI2_BASE             (0xdeadbeef)
 #define BCM_6338_UDC0_BASE             (0xdeadbeef)
 #define BCM_6338_USBDMA_BASE           (0xfffe2400)
 #define BCM_6338_OHCI0_BASE            (0xdeadbeef)
@@ -170,6 +234,7 @@ enum bcm63xx_regs_set {
 #define BCM_6338_USBH_PRIV_BASE                (0xdeadbeef)
 #define BCM_6338_MPI_BASE              (0xfffe3160)
 #define BCM_6338_PCMCIA_BASE           (0xdeadbeef)
+#define BCM_6338_PCIE_BASE             (0xdeadbeef)
 #define BCM_6338_SDRAM_REGS_BASE       (0xfffe3100)
 #define BCM_6338_DSL_BASE              (0xfffe1000)
 #define BCM_6338_UBUS_BASE             (0xdeadbeef)
@@ -193,6 +258,8 @@ enum bcm63xx_regs_set {
 #define BCM_6338_PCMDMA_BASE           (0xdeadbeef)
 #define BCM_6338_PCMDMAC_BASE          (0xdeadbeef)
 #define BCM_6338_PCMDMAS_BASE          (0xdeadbeef)
+#define BCM_6338_RNG_BASE              (0xdeadbeef)
+#define BCM_6338_MISC_BASE             (0xdeadbeef)
 
 /*
  * 6345 register sets base address
@@ -206,7 +273,6 @@ enum bcm63xx_regs_set {
 #define BCM_6345_UART1_BASE            (0xdeadbeef)
 #define BCM_6345_GPIO_BASE             (0xfffe0400)
 #define BCM_6345_SPI_BASE              (0xdeadbeef)
-#define BCM_6345_SPI2_BASE             (0xdeadbeef)
 #define BCM_6345_UDC0_BASE             (0xdeadbeef)
 #define BCM_6345_USBDMA_BASE           (0xfffe2800)
 #define BCM_6345_ENET0_BASE            (0xfffe1800)
@@ -216,6 +282,7 @@ enum bcm63xx_regs_set {
 #define BCM_6345_ENETSW_BASE           (0xdeadbeef)
 #define BCM_6345_PCMCIA_BASE           (0xfffe2028)
 #define BCM_6345_MPI_BASE              (0xfffe2000)
+#define BCM_6345_PCIE_BASE             (0xdeadbeef)
 #define BCM_6345_OHCI0_BASE            (0xfffe2100)
 #define BCM_6345_OHCI_PRIV_BASE                (0xfffe2200)
 #define BCM_6345_USBH_PRIV_BASE                (0xdeadbeef)
@@ -237,6 +304,8 @@ enum bcm63xx_regs_set {
 #define BCM_6345_PCMDMA_BASE           (0xdeadbeef)
 #define BCM_6345_PCMDMAC_BASE          (0xdeadbeef)
 #define BCM_6345_PCMDMAS_BASE          (0xdeadbeef)
+#define BCM_6345_RNG_BASE              (0xdeadbeef)
+#define BCM_6345_MISC_BASE             (0xdeadbeef)
 
 /*
  * 6348 register sets base address
@@ -249,13 +318,13 @@ enum bcm63xx_regs_set {
 #define BCM_6348_UART1_BASE            (0xdeadbeef)
 #define BCM_6348_GPIO_BASE             (0xfffe0400)
 #define BCM_6348_SPI_BASE              (0xfffe0c00)
-#define BCM_6348_SPI2_BASE             (0xdeadbeef)
 #define BCM_6348_UDC0_BASE             (0xfffe1000)
 #define BCM_6348_OHCI0_BASE            (0xfffe1b00)
 #define BCM_6348_OHCI_PRIV_BASE                (0xfffe1c00)
 #define BCM_6348_USBH_PRIV_BASE                (0xdeadbeef)
 #define BCM_6348_MPI_BASE              (0xfffe2000)
 #define BCM_6348_PCMCIA_BASE           (0xfffe2054)
+#define BCM_6348_PCIE_BASE             (0xdeadbeef)
 #define BCM_6348_SDRAM_REGS_BASE       (0xfffe2300)
 #define BCM_6348_M2M_BASE              (0xfffe2800)
 #define BCM_6348_DSL_BASE              (0xfffe3000)
@@ -278,6 +347,8 @@ enum bcm63xx_regs_set {
 #define BCM_6348_PCMDMA_BASE           (0xdeadbeef)
 #define BCM_6348_PCMDMAC_BASE          (0xdeadbeef)
 #define BCM_6348_PCMDMAS_BASE          (0xdeadbeef)
+#define BCM_6348_RNG_BASE              (0xdeadbeef)
+#define BCM_6348_MISC_BASE             (0xdeadbeef)
 
 /*
  * 6358 register sets base address
@@ -289,14 +360,14 @@ enum bcm63xx_regs_set {
 #define BCM_6358_UART0_BASE            (0xfffe0100)
 #define BCM_6358_UART1_BASE            (0xfffe0120)
 #define BCM_6358_GPIO_BASE             (0xfffe0080)
-#define BCM_6358_SPI_BASE              (0xdeadbeef)
-#define BCM_6358_SPI2_BASE             (0xfffe0800)
+#define BCM_6358_SPI_BASE              (0xfffe0800)
 #define BCM_6358_UDC0_BASE             (0xfffe0800)
 #define BCM_6358_OHCI0_BASE            (0xfffe1400)
 #define BCM_6358_OHCI_PRIV_BASE                (0xdeadbeef)
 #define BCM_6358_USBH_PRIV_BASE                (0xfffe1500)
 #define BCM_6358_MPI_BASE              (0xfffe1000)
 #define BCM_6358_PCMCIA_BASE           (0xfffe1054)
+#define BCM_6358_PCIE_BASE             (0xdeadbeef)
 #define BCM_6358_SDRAM_REGS_BASE       (0xfffe2300)
 #define BCM_6358_M2M_BASE              (0xdeadbeef)
 #define BCM_6358_DSL_BASE              (0xfffe3000)
@@ -319,6 +390,8 @@ enum bcm63xx_regs_set {
 #define BCM_6358_PCMDMA_BASE           (0xfffe1800)
 #define BCM_6358_PCMDMAC_BASE          (0xfffe1900)
 #define BCM_6358_PCMDMAS_BASE          (0xfffe1a00)
+#define BCM_6358_RNG_BASE              (0xdeadbeef)
+#define BCM_6358_MISC_BASE             (0xdeadbeef)
 
 
 /*
@@ -331,14 +404,14 @@ enum bcm63xx_regs_set {
 #define BCM_6368_UART0_BASE            (0xb0000100)
 #define BCM_6368_UART1_BASE            (0xb0000120)
 #define BCM_6368_GPIO_BASE             (0xb0000080)
-#define BCM_6368_SPI_BASE              (0xdeadbeef)
-#define BCM_6368_SPI2_BASE             (0xb0000800)
+#define BCM_6368_SPI_BASE              (0xb0000800)
 #define BCM_6368_UDC0_BASE             (0xdeadbeef)
 #define BCM_6368_OHCI0_BASE            (0xb0001600)
 #define BCM_6368_OHCI_PRIV_BASE                (0xdeadbeef)
 #define BCM_6368_USBH_PRIV_BASE                (0xb0001700)
 #define BCM_6368_MPI_BASE              (0xb0001000)
 #define BCM_6368_PCMCIA_BASE           (0xb0001054)
+#define BCM_6368_PCIE_BASE             (0xdeadbeef)
 #define BCM_6368_SDRAM_REGS_BASE       (0xdeadbeef)
 #define BCM_6368_M2M_BASE              (0xdeadbeef)
 #define BCM_6368_DSL_BASE              (0xdeadbeef)
@@ -361,6 +434,8 @@ enum bcm63xx_regs_set {
 #define BCM_6368_PCMDMA_BASE           (0xb0005800)
 #define BCM_6368_PCMDMAC_BASE          (0xb0005a00)
 #define BCM_6368_PCMDMAS_BASE          (0xb0005c00)
+#define BCM_6368_RNG_BASE              (0xb0004180)
+#define BCM_6368_MISC_BASE             (0xdeadbeef)
 
 
 extern const unsigned long *bcm63xx_regs_base;
@@ -379,13 +454,13 @@ extern const unsigned long *bcm63xx_regs_base;
        __GEN_RSET_BASE(__cpu, UART1)                                   \
        __GEN_RSET_BASE(__cpu, GPIO)                                    \
        __GEN_RSET_BASE(__cpu, SPI)                                     \
-       __GEN_RSET_BASE(__cpu, SPI2)                                    \
        __GEN_RSET_BASE(__cpu, UDC0)                                    \
        __GEN_RSET_BASE(__cpu, OHCI0)                                   \
        __GEN_RSET_BASE(__cpu, OHCI_PRIV)                               \
        __GEN_RSET_BASE(__cpu, USBH_PRIV)                               \
        __GEN_RSET_BASE(__cpu, MPI)                                     \
        __GEN_RSET_BASE(__cpu, PCMCIA)                                  \
+       __GEN_RSET_BASE(__cpu, PCIE)                                    \
        __GEN_RSET_BASE(__cpu, DSL)                                     \
        __GEN_RSET_BASE(__cpu, ENET0)                                   \
        __GEN_RSET_BASE(__cpu, ENET1)                                   \
@@ -407,6 +482,8 @@ extern const unsigned long *bcm63xx_regs_base;
        __GEN_RSET_BASE(__cpu, PCMDMA)                                  \
        __GEN_RSET_BASE(__cpu, PCMDMAC)                                 \
        __GEN_RSET_BASE(__cpu, PCMDMAS)                                 \
+       __GEN_RSET_BASE(__cpu, RNG)                                     \
+       __GEN_RSET_BASE(__cpu, MISC)                                    \
        }
 
 #define __GEN_CPU_REGS_TABLE(__cpu)                                    \
@@ -418,13 +495,13 @@ extern const unsigned long *bcm63xx_regs_base;
        [RSET_UART1]            = BCM_## __cpu ##_UART1_BASE,           \
        [RSET_GPIO]             = BCM_## __cpu ##_GPIO_BASE,            \
        [RSET_SPI]              = BCM_## __cpu ##_SPI_BASE,             \
-       [RSET_SPI2]             = BCM_## __cpu ##_SPI2_BASE,            \
        [RSET_UDC0]             = BCM_## __cpu ##_UDC0_BASE,            \
        [RSET_OHCI0]            = BCM_## __cpu ##_OHCI0_BASE,           \
        [RSET_OHCI_PRIV]        = BCM_## __cpu ##_OHCI_PRIV_BASE,       \
        [RSET_USBH_PRIV]        = BCM_## __cpu ##_USBH_PRIV_BASE,       \
        [RSET_MPI]              = BCM_## __cpu ##_MPI_BASE,             \
        [RSET_PCMCIA]           = BCM_## __cpu ##_PCMCIA_BASE,          \
+       [RSET_PCIE]             = BCM_## __cpu ##_PCIE_BASE,            \
        [RSET_DSL]              = BCM_## __cpu ##_DSL_BASE,             \
        [RSET_ENET0]            = BCM_## __cpu ##_ENET0_BASE,           \
        [RSET_ENET1]            = BCM_## __cpu ##_ENET1_BASE,           \
@@ -446,6 +523,8 @@ extern const unsigned long *bcm63xx_regs_base;
        [RSET_PCMDMA]           = BCM_## __cpu ##_PCMDMA_BASE,          \
        [RSET_PCMDMAC]          = BCM_## __cpu ##_PCMDMAC_BASE,         \
        [RSET_PCMDMAS]          = BCM_## __cpu ##_PCMDMAS_BASE,         \
+       [RSET_RNG]              = BCM_## __cpu ##_RNG_BASE,             \
+       [RSET_MISC]             = BCM_## __cpu ##_MISC_BASE,            \
 
 
 static inline unsigned long bcm63xx_regset_address(enum bcm63xx_regs_set set)
@@ -453,6 +532,9 @@ static inline unsigned long bcm63xx_regset_address(enum bcm63xx_regs_set set)
 #ifdef BCMCPU_RUNTIME_DETECT
        return bcm63xx_regs_base[set];
 #else
+#ifdef CONFIG_BCM63XX_CPU_6328
+       __GEN_RSET(6328)
+#endif
 #ifdef CONFIG_BCM63XX_CPU_6338
        __GEN_RSET(6338)
 #endif
@@ -478,6 +560,7 @@ static inline unsigned long bcm63xx_regset_address(enum bcm63xx_regs_set set)
  */
 enum bcm63xx_irq {
        IRQ_TIMER = 0,
+       IRQ_SPI,
        IRQ_UART0,
        IRQ_UART1,
        IRQ_DSL,
@@ -505,10 +588,52 @@ enum bcm63xx_irq {
        IRQ_XTM_DMA0,
 };
 
+/*
+ * 6328 irqs
+ */
+#define BCM_6328_HIGH_IRQ_BASE         (IRQ_INTERNAL_BASE + 32)
+
+#define BCM_6328_TIMER_IRQ             (IRQ_INTERNAL_BASE + 31)
+#define BCM_6328_SPI_IRQ               0
+#define BCM_6328_UART0_IRQ             (IRQ_INTERNAL_BASE + 28)
+#define BCM_6328_UART1_IRQ             (BCM_6328_HIGH_IRQ_BASE + 7)
+#define BCM_6328_DSL_IRQ               (IRQ_INTERNAL_BASE + 4)
+#define BCM_6328_UDC0_IRQ              0
+#define BCM_6328_ENET0_IRQ             0
+#define BCM_6328_ENET1_IRQ             0
+#define BCM_6328_ENET_PHY_IRQ          (IRQ_INTERNAL_BASE + 12)
+#define BCM_6328_OHCI0_IRQ             (IRQ_INTERNAL_BASE + 9)
+#define BCM_6328_EHCI0_IRQ             (IRQ_INTERNAL_BASE + 10)
+#define BCM_6328_PCMCIA_IRQ            0
+#define BCM_6328_ENET0_RXDMA_IRQ       0
+#define BCM_6328_ENET0_TXDMA_IRQ       0
+#define BCM_6328_ENET1_RXDMA_IRQ       0
+#define BCM_6328_ENET1_TXDMA_IRQ       0
+#define BCM_6328_PCI_IRQ               (IRQ_INTERNAL_BASE + 23)
+#define BCM_6328_ATM_IRQ               0
+#define BCM_6328_ENETSW_RXDMA0_IRQ     (BCM_6328_HIGH_IRQ_BASE + 0)
+#define BCM_6328_ENETSW_RXDMA1_IRQ     (BCM_6328_HIGH_IRQ_BASE + 1)
+#define BCM_6328_ENETSW_RXDMA2_IRQ     (BCM_6328_HIGH_IRQ_BASE + 2)
+#define BCM_6328_ENETSW_RXDMA3_IRQ     (BCM_6328_HIGH_IRQ_BASE + 3)
+#define BCM_6328_ENETSW_TXDMA0_IRQ     (BCM_6328_HIGH_IRQ_BASE + 4)
+#define BCM_6328_ENETSW_TXDMA1_IRQ     (BCM_6328_HIGH_IRQ_BASE + 5)
+#define BCM_6328_ENETSW_TXDMA2_IRQ     (BCM_6328_HIGH_IRQ_BASE + 6)
+#define BCM_6328_ENETSW_TXDMA3_IRQ     (BCM_6328_HIGH_IRQ_BASE + 7)
+#define BCM_6328_XTM_IRQ               (BCM_6328_HIGH_IRQ_BASE + 31)
+#define BCM_6328_XTM_DMA0_IRQ          (BCM_6328_HIGH_IRQ_BASE + 11)
+
+#define BCM_6328_PCM_DMA0_IRQ          (IRQ_INTERNAL_BASE + 2)
+#define BCM_6328_PCM_DMA1_IRQ          (IRQ_INTERNAL_BASE + 3)
+#define BCM_6328_EXT_IRQ0              (IRQ_INTERNAL_BASE + 24)
+#define BCM_6328_EXT_IRQ1              (IRQ_INTERNAL_BASE + 25)
+#define BCM_6328_EXT_IRQ2              (IRQ_INTERNAL_BASE + 26)
+#define BCM_6328_EXT_IRQ3              (IRQ_INTERNAL_BASE + 27)
+
 /*
  * 6338 irqs
  */
 #define BCM_6338_TIMER_IRQ             (IRQ_INTERNAL_BASE + 0)
+#define BCM_6338_SPI_IRQ               (IRQ_INTERNAL_BASE + 1)
 #define BCM_6338_UART0_IRQ             (IRQ_INTERNAL_BASE + 2)
 #define BCM_6338_UART1_IRQ             0
 #define BCM_6338_DSL_IRQ               (IRQ_INTERNAL_BASE + 5)
@@ -539,6 +664,7 @@ enum bcm63xx_irq {
  * 6345 irqs
  */
 #define BCM_6345_TIMER_IRQ             (IRQ_INTERNAL_BASE + 0)
+#define BCM_6345_SPI_IRQ               0
 #define BCM_6345_UART0_IRQ             (IRQ_INTERNAL_BASE + 2)
 #define BCM_6345_UART1_IRQ             0
 #define BCM_6345_DSL_IRQ               (IRQ_INTERNAL_BASE + 3)
@@ -569,6 +695,7 @@ enum bcm63xx_irq {
  * 6348 irqs
  */
 #define BCM_6348_TIMER_IRQ             (IRQ_INTERNAL_BASE + 0)
+#define BCM_6348_SPI_IRQ               (IRQ_INTERNAL_BASE + 1)
 #define BCM_6348_UART0_IRQ             (IRQ_INTERNAL_BASE + 2)
 #define BCM_6348_UART1_IRQ             0
 #define BCM_6348_DSL_IRQ               (IRQ_INTERNAL_BASE + 4)
@@ -599,6 +726,7 @@ enum bcm63xx_irq {
  * 6358 irqs
  */
 #define BCM_6358_TIMER_IRQ             (IRQ_INTERNAL_BASE + 0)
+#define BCM_6358_SPI_IRQ               (IRQ_INTERNAL_BASE + 1)
 #define BCM_6358_UART0_IRQ             (IRQ_INTERNAL_BASE + 2)
 #define BCM_6358_UART1_IRQ             (IRQ_INTERNAL_BASE + 3)
 #define BCM_6358_DSL_IRQ               (IRQ_INTERNAL_BASE + 29)
@@ -638,6 +766,7 @@ enum bcm63xx_irq {
 #define BCM_6368_HIGH_IRQ_BASE         (IRQ_INTERNAL_BASE + 32)
 
 #define BCM_6368_TIMER_IRQ             (IRQ_INTERNAL_BASE + 0)
+#define BCM_6368_SPI_IRQ               (IRQ_INTERNAL_BASE + 1)
 #define BCM_6368_UART0_IRQ             (IRQ_INTERNAL_BASE + 2)
 #define BCM_6368_UART1_IRQ             (IRQ_INTERNAL_BASE + 3)
 #define BCM_6368_DSL_IRQ               (IRQ_INTERNAL_BASE + 4)
@@ -677,6 +806,7 @@ extern const int *bcm63xx_irqs;
 
 #define __GEN_CPU_IRQ_TABLE(__cpu)                                     \
        [IRQ_TIMER]             = BCM_## __cpu ##_TIMER_IRQ,            \
+       [IRQ_SPI]               = BCM_## __cpu ##_SPI_IRQ,              \
        [IRQ_UART0]             = BCM_## __cpu ##_UART0_IRQ,            \
        [IRQ_UART1]             = BCM_## __cpu ##_UART1_IRQ,            \
        [IRQ_DSL]               = BCM_## __cpu ##_DSL_IRQ,              \
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_flash.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_flash.h
new file mode 100644 (file)
index 0000000..354b848
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __BCM63XX_FLASH_H
+#define __BCM63XX_FLASH_H
+
+enum {
+       BCM63XX_FLASH_TYPE_PARALLEL,
+       BCM63XX_FLASH_TYPE_SERIAL,
+       BCM63XX_FLASH_TYPE_NAND,
+};
+
+int __init bcm63xx_flash_register(void);
+
+#endif /* __BCM63XX_FLASH_H */
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h
new file mode 100644 (file)
index 0000000..7d98dbe
--- /dev/null
@@ -0,0 +1,89 @@
+#ifndef BCM63XX_DEV_SPI_H
+#define BCM63XX_DEV_SPI_H
+
+#include <linux/types.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_regs.h>
+
+int __init bcm63xx_spi_register(void);
+
+struct bcm63xx_spi_pdata {
+       unsigned int    fifo_size;
+       int             bus_num;
+       int             num_chipselect;
+       u32             speed_hz;
+};
+
+enum bcm63xx_regs_spi {
+       SPI_CMD,
+       SPI_INT_STATUS,
+       SPI_INT_MASK_ST,
+       SPI_INT_MASK,
+       SPI_ST,
+       SPI_CLK_CFG,
+       SPI_FILL_BYTE,
+       SPI_MSG_TAIL,
+       SPI_RX_TAIL,
+       SPI_MSG_CTL,
+       SPI_MSG_DATA,
+       SPI_RX_DATA,
+};
+
+#define __GEN_SPI_RSET_BASE(__cpu, __rset)                             \
+       case SPI_## __rset:                                             \
+               return SPI_## __cpu ##_## __rset;
+
+#define __GEN_SPI_RSET(__cpu)                                          \
+       switch (reg) {                                                  \
+       __GEN_SPI_RSET_BASE(__cpu, CMD)                                 \
+       __GEN_SPI_RSET_BASE(__cpu, INT_STATUS)                          \
+       __GEN_SPI_RSET_BASE(__cpu, INT_MASK_ST)                         \
+       __GEN_SPI_RSET_BASE(__cpu, INT_MASK)                            \
+       __GEN_SPI_RSET_BASE(__cpu, ST)                                  \
+       __GEN_SPI_RSET_BASE(__cpu, CLK_CFG)                             \
+       __GEN_SPI_RSET_BASE(__cpu, FILL_BYTE)                           \
+       __GEN_SPI_RSET_BASE(__cpu, MSG_TAIL)                            \
+       __GEN_SPI_RSET_BASE(__cpu, RX_TAIL)                             \
+       __GEN_SPI_RSET_BASE(__cpu, MSG_CTL)                             \
+       __GEN_SPI_RSET_BASE(__cpu, MSG_DATA)                            \
+       __GEN_SPI_RSET_BASE(__cpu, RX_DATA)                             \
+       }
+
+#define __GEN_SPI_REGS_TABLE(__cpu)                                    \
+       [SPI_CMD]               = SPI_## __cpu ##_CMD,                  \
+       [SPI_INT_STATUS]        = SPI_## __cpu ##_INT_STATUS,           \
+       [SPI_INT_MASK_ST]       = SPI_## __cpu ##_INT_MASK_ST,          \
+       [SPI_INT_MASK]          = SPI_## __cpu ##_INT_MASK,             \
+       [SPI_ST]                = SPI_## __cpu ##_ST,                   \
+       [SPI_CLK_CFG]           = SPI_## __cpu ##_CLK_CFG,              \
+       [SPI_FILL_BYTE]         = SPI_## __cpu ##_FILL_BYTE,            \
+       [SPI_MSG_TAIL]          = SPI_## __cpu ##_MSG_TAIL,             \
+       [SPI_RX_TAIL]           = SPI_## __cpu ##_RX_TAIL,              \
+       [SPI_MSG_CTL]           = SPI_## __cpu ##_MSG_CTL,              \
+       [SPI_MSG_DATA]          = SPI_## __cpu ##_MSG_DATA,             \
+       [SPI_RX_DATA]           = SPI_## __cpu ##_RX_DATA,
+
+static inline unsigned long bcm63xx_spireg(enum bcm63xx_regs_spi reg)
+{
+#ifdef BCMCPU_RUNTIME_DETECT
+       extern const unsigned long *bcm63xx_regs_spi;
+
+       return bcm63xx_regs_spi[reg];
+#else
+#ifdef CONFIG_BCM63XX_CPU_6338
+       __GEN_SPI_RSET(6338)
+#endif
+#ifdef CONFIG_BCM63XX_CPU_6348
+       __GEN_SPI_RSET(6348)
+#endif
+#ifdef CONFIG_BCM63XX_CPU_6358
+       __GEN_SPI_RSET(6358)
+#endif
+#ifdef CONFIG_BCM63XX_CPU_6368
+       __GEN_SPI_RSET(6368)
+#endif
+#endif
+       return 0;
+}
+
+#endif /* BCM63XX_DEV_SPI_H */
index 1d7dd96aa460b5d150f4d15660993a1170a4f3e8..0a9891f7580de557e702e7c4708139705b27605e 100644 (file)
@@ -9,6 +9,8 @@ int __init bcm63xx_gpio_init(void);
 static inline unsigned long bcm63xx_gpio_count(void)
 {
        switch (bcm63xx_get_cpu_id()) {
+       case BCM6328_CPU_ID:
+               return 32;
        case BCM6358_CPU_ID:
                return 40;
        case BCM6338_CPU_ID:
index 72477a6441ddb9a39c769c47809878f120de027a..9203d90e610cc801fa70045058c75ed974dcdd3b 100644 (file)
 #define BCM_CB_MEM_END_PA              (BCM_CB_MEM_BASE_PA +           \
                                        BCM_CB_MEM_SIZE - 1)
 
+#define BCM_PCIE_MEM_BASE_PA           0x10f00000
+#define BCM_PCIE_MEM_SIZE              (16 * 1024 * 1024)
+#define BCM_PCIE_MEM_END_PA            (BCM_PCIE_MEM_BASE_PA +         \
+                                       BCM_PCIE_MEM_SIZE - 1)
 
 /*
  * Internal registers are accessed through KSEG3
 #define bcm_mpi_writel(v, o)   bcm_rset_writel(RSET_MPI, (v), (o))
 #define bcm_pcmcia_readl(o)    bcm_rset_readl(RSET_PCMCIA, (o))
 #define bcm_pcmcia_writel(v, o)        bcm_rset_writel(RSET_PCMCIA, (v), (o))
+#define bcm_pcie_readl(o)      bcm_rset_readl(RSET_PCIE, (o))
+#define bcm_pcie_writel(v, o)  bcm_rset_writel(RSET_PCIE, (v), (o))
 #define bcm_sdram_readl(o)     bcm_rset_readl(RSET_SDRAM, (o))
 #define bcm_sdram_writel(v, o) bcm_rset_writel(RSET_SDRAM, (v), (o))
 #define bcm_memc_readl(o)      bcm_rset_readl(RSET_MEMC, (o))
 #define bcm_memc_writel(v, o)  bcm_rset_writel(RSET_MEMC, (v), (o))
 #define bcm_ddr_readl(o)       bcm_rset_readl(RSET_DDR, (o))
 #define bcm_ddr_writel(v, o)   bcm_rset_writel(RSET_DDR, (v), (o))
+#define bcm_misc_readl(o)      bcm_rset_readl(RSET_MISC, (o))
+#define bcm_misc_writel(v, o)  bcm_rset_writel(RSET_MISC, (v), (o))
 
 #endif /* ! BCM63XX_IO_H_ */
index fdcd78ca1b03d054f807ee0c64839c763194f855..4ccc2a748aff2db6913dab0e8b67e1eb6fbceeb8 100644 (file)
 /* Clock Control register */
 #define PERF_CKCTL_REG                 0x4
 
+#define CKCTL_6328_PHYMIPS_EN          (1 << 0)
+#define CKCTL_6328_ADSL_QPROC_EN       (1 << 1)
+#define CKCTL_6328_ADSL_AFE_EN         (1 << 2)
+#define CKCTL_6328_ADSL_EN             (1 << 3)
+#define CKCTL_6328_MIPS_EN             (1 << 4)
+#define CKCTL_6328_SAR_EN              (1 << 5)
+#define CKCTL_6328_PCM_EN              (1 << 6)
+#define CKCTL_6328_USBD_EN             (1 << 7)
+#define CKCTL_6328_USBH_EN             (1 << 8)
+#define CKCTL_6328_HSSPI_EN            (1 << 9)
+#define CKCTL_6328_PCIE_EN             (1 << 10)
+#define CKCTL_6328_ROBOSW_EN           (1 << 11)
+
+#define CKCTL_6328_ALL_SAFE_EN         (CKCTL_6328_PHYMIPS_EN |        \
+                                       CKCTL_6328_ADSL_QPROC_EN |      \
+                                       CKCTL_6328_ADSL_AFE_EN |        \
+                                       CKCTL_6328_ADSL_EN |            \
+                                       CKCTL_6328_SAR_EN  |            \
+                                       CKCTL_6328_PCM_EN  |            \
+                                       CKCTL_6328_USBD_EN |            \
+                                       CKCTL_6328_USBH_EN |            \
+                                       CKCTL_6328_ROBOSW_EN |          \
+                                       CKCTL_6328_PCIE_EN)
+
 #define CKCTL_6338_ADSLPHY_EN          (1 << 0)
 #define CKCTL_6338_MPI_EN              (1 << 1)
 #define CKCTL_6338_DRAM_EN             (1 << 2)
 #define CKCTL_6368_PHYMIPS_EN          (1 << 6)
 #define CKCTL_6368_SWPKT_USB_EN                (1 << 7)
 #define CKCTL_6368_SWPKT_SAR_EN                (1 << 8)
-#define CKCTL_6368_SPI_CLK_EN          (1 << 9)
-#define CKCTL_6368_USBD_CLK_EN         (1 << 10)
-#define CKCTL_6368_SAR_CLK_EN          (1 << 11)
-#define CKCTL_6368_ROBOSW_CLK_EN       (1 << 12)
-#define CKCTL_6368_UTOPIA_CLK_EN       (1 << 13)
-#define CKCTL_6368_PCM_CLK_EN          (1 << 14)
-#define CKCTL_6368_USBH_CLK_EN         (1 << 15)
+#define CKCTL_6368_SPI_EN              (1 << 9)
+#define CKCTL_6368_USBD_EN             (1 << 10)
+#define CKCTL_6368_SAR_EN              (1 << 11)
+#define CKCTL_6368_ROBOSW_EN           (1 << 12)
+#define CKCTL_6368_UTOPIA_EN           (1 << 13)
+#define CKCTL_6368_PCM_EN              (1 << 14)
+#define CKCTL_6368_USBH_EN             (1 << 15)
 #define CKCTL_6368_DISABLE_GLESS_EN    (1 << 16)
-#define CKCTL_6368_NAND_CLK_EN         (1 << 17)
-#define CKCTL_6368_IPSEC_CLK_EN                (1 << 18)
+#define CKCTL_6368_NAND_EN             (1 << 17)
+#define CKCTL_6368_IPSEC_EN            (1 << 18)
 
 #define CKCTL_6368_ALL_SAFE_EN         (CKCTL_6368_SWPKT_USB_EN |      \
                                        CKCTL_6368_SWPKT_SAR_EN |       \
-                                       CKCTL_6368_SPI_CLK_EN |         \
-                                       CKCTL_6368_USBD_CLK_EN |        \
-                                       CKCTL_6368_SAR_CLK_EN |         \
-                                       CKCTL_6368_ROBOSW_CLK_EN |      \
-                                       CKCTL_6368_UTOPIA_CLK_EN |      \
-                                       CKCTL_6368_PCM_CLK_EN |         \
-                                       CKCTL_6368_USBH_CLK_EN |        \
+                                       CKCTL_6368_SPI_EN |             \
+                                       CKCTL_6368_USBD_EN |            \
+                                       CKCTL_6368_SAR_EN |             \
+                                       CKCTL_6368_ROBOSW_EN |          \
+                                       CKCTL_6368_UTOPIA_EN |          \
+                                       CKCTL_6368_PCM_EN |             \
+                                       CKCTL_6368_USBH_EN |            \
                                        CKCTL_6368_DISABLE_GLESS_EN |   \
-                                       CKCTL_6368_NAND_CLK_EN |        \
-                                       CKCTL_6368_IPSEC_CLK_EN)
+                                       CKCTL_6368_NAND_EN |            \
+                                       CKCTL_6368_IPSEC_EN)
 
 /* System PLL Control register  */
 #define PERF_SYS_PLL_CTL_REG           0x8
 #define SYS_PLL_SOFT_RESET             0x1
 
 /* Interrupt Mask register */
+#define PERF_IRQMASK_6328_REG          0x20
 #define PERF_IRQMASK_6338_REG          0xc
 #define PERF_IRQMASK_6345_REG          0xc
 #define PERF_IRQMASK_6348_REG          0xc
 #define PERF_IRQMASK_6368_REG          0x20
 
 /* Interrupt Status register */
+#define PERF_IRQSTAT_6328_REG          0x28
 #define PERF_IRQSTAT_6338_REG          0x10
 #define PERF_IRQSTAT_6345_REG          0x10
 #define PERF_IRQSTAT_6348_REG          0x10
 #define PERF_IRQSTAT_6368_REG          0x28
 
 /* External Interrupt Configuration register */
+#define PERF_EXTIRQ_CFG_REG_6328       0x18
 #define PERF_EXTIRQ_CFG_REG_6338       0x14
 #define PERF_EXTIRQ_CFG_REG_6348       0x14
 #define PERF_EXTIRQ_CFG_REG_6358       0x14
 
 /* Soft Reset register */
 #define PERF_SOFTRESET_REG             0x28
+#define PERF_SOFTRESET_6328_REG                0x10
 #define PERF_SOFTRESET_6368_REG                0x10
 
+#define SOFTRESET_6328_SPI_MASK                (1 << 0)
+#define SOFTRESET_6328_EPHY_MASK       (1 << 1)
+#define SOFTRESET_6328_SAR_MASK                (1 << 2)
+#define SOFTRESET_6328_ENETSW_MASK     (1 << 3)
+#define SOFTRESET_6328_USBS_MASK       (1 << 4)
+#define SOFTRESET_6328_USBH_MASK       (1 << 5)
+#define SOFTRESET_6328_PCM_MASK                (1 << 6)
+#define SOFTRESET_6328_PCIE_CORE_MASK  (1 << 7)
+#define SOFTRESET_6328_PCIE_MASK       (1 << 8)
+#define SOFTRESET_6328_PCIE_EXT_MASK   (1 << 9)
+#define SOFTRESET_6328_PCIE_HARD_MASK  (1 << 10)
+
 #define SOFTRESET_6338_SPI_MASK                (1 << 0)
 #define SOFTRESET_6338_ENET_MASK       (1 << 2)
 #define SOFTRESET_6338_USBH_MASK       (1 << 3)
 /* Watchdog reset length register */
 #define WDT_RSTLEN_REG                 0x8
 
+/* Watchdog soft reset register (BCM6328 only) */
+#define WDT_SOFTRESET_REG              0xc
 
 /*************************************************************************
  * _REG relative to RSET_UARTx
 #define GPIO_BASEMODE_6368_MASK                0x7
 /* those bits must be kept as read in gpio basemode register*/
 
+#define GPIO_STRAPBUS_REG              0x40
+#define STRAPBUS_6358_BOOT_SEL_PARALLEL        (1 << 1)
+#define STRAPBUS_6358_BOOT_SEL_SERIAL  (0 << 1)
+#define STRAPBUS_6368_BOOT_SEL_MASK    0x3
+#define STRAPBUS_6368_BOOT_SEL_NAND    0
+#define STRAPBUS_6368_BOOT_SEL_SERIAL  1
+#define STRAPBUS_6368_BOOT_SEL_PARALLEL        3
+
+
 /*************************************************************************
  * _REG relative to RSET_ENET
  *************************************************************************/
  * _REG relative to RSET_DDR
  *************************************************************************/
 
+#define DDR_CSEND_REG                  0x8
+
 #define DDR_DMIPSPLLCFG_REG            0x18
 #define DMIPSPLLCFG_M1_SHIFT           0
 #define DMIPSPLLCFG_M1_MASK            (0xff << DMIPSPLLCFG_M1_SHIFT)
 #define M2M_SRCID_REG(x)               ((x) * 0x40 + 0x14)
 #define M2M_DSTID_REG(x)               ((x) * 0x40 + 0x18)
 
+/*************************************************************************
+ * _REG relative to RSET_RNG
+ *************************************************************************/
+
+#define RNG_CTRL                       0x00
+#define RNG_EN                         (1 << 0)
+
+#define RNG_STAT                       0x04
+#define RNG_AVAIL_MASK                 (0xff000000)
+
+#define RNG_DATA                       0x08
+#define RNG_THRES                      0x0c
+#define RNG_MASK                       0x10
+
+/*************************************************************************
+ * _REG relative to RSET_SPI
+ *************************************************************************/
+
+/* BCM 6338 SPI core */
+#define SPI_6338_CMD                   0x00    /* 16-bits register */
+#define SPI_6338_INT_STATUS            0x02
+#define SPI_6338_INT_MASK_ST           0x03
+#define SPI_6338_INT_MASK              0x04
+#define SPI_6338_ST                    0x05
+#define SPI_6338_CLK_CFG               0x06
+#define SPI_6338_FILL_BYTE             0x07
+#define SPI_6338_MSG_TAIL              0x09
+#define SPI_6338_RX_TAIL               0x0b
+#define SPI_6338_MSG_CTL               0x40
+#define SPI_6338_MSG_DATA              0x41
+#define SPI_6338_MSG_DATA_SIZE         0x3f
+#define SPI_6338_RX_DATA               0x80
+#define SPI_6338_RX_DATA_SIZE          0x3f
+
+/* BCM 6348 SPI core */
+#define SPI_6348_CMD                   0x00    /* 16-bits register */
+#define SPI_6348_INT_STATUS            0x02
+#define SPI_6348_INT_MASK_ST           0x03
+#define SPI_6348_INT_MASK              0x04
+#define SPI_6348_ST                    0x05
+#define SPI_6348_CLK_CFG               0x06
+#define SPI_6348_FILL_BYTE             0x07
+#define SPI_6348_MSG_TAIL              0x09
+#define SPI_6348_RX_TAIL               0x0b
+#define SPI_6348_MSG_CTL               0x40
+#define SPI_6348_MSG_DATA              0x41
+#define SPI_6348_MSG_DATA_SIZE         0x3f
+#define SPI_6348_RX_DATA               0x80
+#define SPI_6348_RX_DATA_SIZE          0x3f
+
+/* BCM 6358 SPI core */
+#define SPI_6358_MSG_CTL               0x00    /* 16-bits register */
+#define SPI_6358_MSG_DATA              0x02
+#define SPI_6358_MSG_DATA_SIZE         0x21e
+#define SPI_6358_RX_DATA               0x400
+#define SPI_6358_RX_DATA_SIZE          0x220
+#define SPI_6358_CMD                   0x700   /* 16-bits register */
+#define SPI_6358_INT_STATUS            0x702
+#define SPI_6358_INT_MASK_ST           0x703
+#define SPI_6358_INT_MASK              0x704
+#define SPI_6358_ST                    0x705
+#define SPI_6358_CLK_CFG               0x706
+#define SPI_6358_FILL_BYTE             0x707
+#define SPI_6358_MSG_TAIL              0x709
+#define SPI_6358_RX_TAIL               0x70B
+
+/* BCM 6358 SPI core */
+#define SPI_6368_MSG_CTL               0x00    /* 16-bits register */
+#define SPI_6368_MSG_DATA              0x02
+#define SPI_6368_MSG_DATA_SIZE         0x21e
+#define SPI_6368_RX_DATA               0x400
+#define SPI_6368_RX_DATA_SIZE          0x220
+#define SPI_6368_CMD                   0x700   /* 16-bits register */
+#define SPI_6368_INT_STATUS            0x702
+#define SPI_6368_INT_MASK_ST           0x703
+#define SPI_6368_INT_MASK              0x704
+#define SPI_6368_ST                    0x705
+#define SPI_6368_CLK_CFG               0x706
+#define SPI_6368_FILL_BYTE             0x707
+#define SPI_6368_MSG_TAIL              0x709
+#define SPI_6368_RX_TAIL               0x70B
+
+/* Shared SPI definitions */
+
+/* Message configuration */
+#define SPI_FD_RW                      0x00
+#define SPI_HD_W                       0x01
+#define SPI_HD_R                       0x02
+#define SPI_BYTE_CNT_SHIFT             0
+#define SPI_MSG_TYPE_SHIFT             14
+
+/* Command */
+#define SPI_CMD_NOOP                   0x00
+#define SPI_CMD_SOFT_RESET             0x01
+#define SPI_CMD_HARD_RESET             0x02
+#define SPI_CMD_START_IMMEDIATE                0x03
+#define SPI_CMD_COMMAND_SHIFT          0
+#define SPI_CMD_COMMAND_MASK           0x000f
+#define SPI_CMD_DEVICE_ID_SHIFT                4
+#define SPI_CMD_PREPEND_BYTE_CNT_SHIFT 8
+#define SPI_CMD_ONE_BYTE_SHIFT         11
+#define SPI_CMD_ONE_WIRE_SHIFT         12
+#define SPI_DEV_ID_0                   0
+#define SPI_DEV_ID_1                   1
+#define SPI_DEV_ID_2                   2
+#define SPI_DEV_ID_3                   3
+
+/* Interrupt mask */
+#define SPI_INTR_CMD_DONE              0x01
+#define SPI_INTR_RX_OVERFLOW           0x02
+#define SPI_INTR_TX_UNDERFLOW          0x04
+#define SPI_INTR_TX_OVERFLOW           0x08
+#define SPI_INTR_RX_UNDERFLOW          0x10
+#define SPI_INTR_CLEAR_ALL             0x1f
+
+/* Status */
+#define SPI_RX_EMPTY                   0x02
+#define SPI_CMD_BUSY                   0x04
+#define SPI_SERIAL_BUSY                        0x08
+
+/* Clock configuration */
+#define SPI_CLK_20MHZ                  0x00
+#define SPI_CLK_0_391MHZ               0x01
+#define SPI_CLK_0_781MHZ               0x02 /* default */
+#define SPI_CLK_1_563MHZ               0x03
+#define SPI_CLK_3_125MHZ               0x04
+#define SPI_CLK_6_250MHZ               0x05
+#define SPI_CLK_12_50MHZ               0x06
+#define SPI_CLK_MASK                   0x07
+#define SPI_SSOFFTIME_MASK             0x38
+#define SPI_SSOFFTIME_SHIFT            3
+#define SPI_BYTE_SWAP                  0x80
+
+/*************************************************************************
+ * _REG relative to RSET_MISC
+ *************************************************************************/
+#define MISC_SERDES_CTRL_REG           0x0
+#define SERDES_PCIE_EN                 (1 << 0)
+#define SERDES_PCIE_EXD_EN             (1 << 15)
+
+#define MISC_STRAPBUS_6328_REG         0x240
+#define STRAPBUS_6328_FCVO_SHIFT       7
+#define STRAPBUS_6328_FCVO_MASK                (0x1f << STRAPBUS_6328_FCVO_SHIFT)
+#define STRAPBUS_6328_BOOT_SEL_SERIAL  (1 << 28)
+#define STRAPBUS_6328_BOOT_SEL_NAND    (0 << 28)
+
+/*************************************************************************
+ * _REG relative to RSET_PCIE
+ *************************************************************************/
+
+#define PCIE_CONFIG2_REG               0x408
+#define CONFIG2_BAR1_SIZE_EN           1
+#define CONFIG2_BAR1_SIZE_MASK         0xf
+
+#define PCIE_IDVAL3_REG                        0x43c
+#define IDVAL3_CLASS_CODE_MASK         0xffffff
+#define IDVAL3_SUBCLASS_SHIFT          8
+#define IDVAL3_CLASS_SHIFT             16
+
+#define PCIE_DLSTATUS_REG              0x1048
+#define DLSTATUS_PHYLINKUP             (1 << 13)
+
+#define PCIE_BRIDGE_OPT1_REG           0x2820
+#define OPT1_RD_BE_OPT_EN              (1 << 7)
+#define OPT1_RD_REPLY_BE_FIX_EN                (1 << 9)
+#define OPT1_PCIE_BRIDGE_HOLE_DET_EN   (1 << 11)
+#define OPT1_L1_INT_STATUS_MASK_POL    (1 << 12)
+
+#define PCIE_BRIDGE_OPT2_REG           0x2824
+#define OPT2_UBUS_UR_DECODE_DIS                (1 << 2)
+#define OPT2_TX_CREDIT_CHK_EN          (1 << 4)
+#define OPT2_CFG_TYPE1_BD_SEL          (1 << 7)
+#define OPT2_CFG_TYPE1_BUS_NO_SHIFT    16
+#define OPT2_CFG_TYPE1_BUS_NO_MASK     (0xff << OPT2_CFG_TYPE1_BUS_NO_SHIFT)
+
+#define PCIE_BRIDGE_BAR0_BASEMASK_REG  0x2828
+#define PCIE_BRIDGE_BAR1_BASEMASK_REG  0x2830
+#define BASEMASK_REMAP_EN              (1 << 0)
+#define BASEMASK_SWAP_EN               (1 << 1)
+#define BASEMASK_MASK_SHIFT            4
+#define BASEMASK_MASK_MASK             (0xfff << BASEMASK_MASK_SHIFT)
+#define BASEMASK_BASE_SHIFT            20
+#define BASEMASK_BASE_MASK             (0xfff << BASEMASK_BASE_SHIFT)
+
+#define PCIE_BRIDGE_BAR0_REBASE_ADDR_REG 0x282c
+#define PCIE_BRIDGE_BAR1_REBASE_ADDR_REG 0x2834
+#define REBASE_ADDR_BASE_SHIFT         20
+#define REBASE_ADDR_BASE_MASK          (0xfff << REBASE_ADDR_BASE_SHIFT)
+
+#define PCIE_BRIDGE_RC_INT_MASK_REG    0x2854
+#define PCIE_RC_INT_A                  (1 << 0)
+#define PCIE_RC_INT_B                  (1 << 1)
+#define PCIE_RC_INT_C                  (1 << 2)
+#define PCIE_RC_INT_D                  (1 << 3)
+
+#define PCIE_DEVICE_OFFSET             0x8000
+
 #endif /* BCM63XX_REGS_H_ */
index ef94ba73646e315592c303cf743474f09291d538..30931c42379d95841ff667bc2036e1f4939dfe51 100644 (file)
@@ -18,6 +18,7 @@ static inline int is_bcm63xx_internal_registers(phys_t offset)
                if (offset >= 0xfff00000)
                        return 1;
                break;
+       case BCM6328_CPU_ID:
        case BCM6368_CPU_ID:
                if (offset >= 0xb0000000 && offset < 0xb1000000)
                        return 1;
index 5b05f186e3952744bb932508d631932209e9e530..418992042f6fcbc23f1612a82ee886cd969c5fa5 100644 (file)
@@ -41,61 +41,26 @@ enum octeon_irq {
        OCTEON_IRQ_TWSI,
        OCTEON_IRQ_TWSI2,
        OCTEON_IRQ_RML,
-       OCTEON_IRQ_TRACE0,
-       OCTEON_IRQ_GMX_DRP0 = OCTEON_IRQ_TRACE0 + 4,
-       OCTEON_IRQ_IPD_DRP = OCTEON_IRQ_GMX_DRP0 + 5,
-       OCTEON_IRQ_KEY_ZERO,
        OCTEON_IRQ_TIMER0,
        OCTEON_IRQ_TIMER1,
        OCTEON_IRQ_TIMER2,
        OCTEON_IRQ_TIMER3,
        OCTEON_IRQ_USB0,
        OCTEON_IRQ_USB1,
-       OCTEON_IRQ_PCM,
-       OCTEON_IRQ_MPI,
-       OCTEON_IRQ_POWIQ,
-       OCTEON_IRQ_IPDPPTHR,
        OCTEON_IRQ_MII0,
        OCTEON_IRQ_MII1,
        OCTEON_IRQ_BOOTDMA,
-
-       OCTEON_IRQ_NAND,
-       OCTEON_IRQ_MIO,         /* Summary of MIO_BOOT_ERR */
-       OCTEON_IRQ_IOB,         /* Summary of IOB_INT_SUM */
-       OCTEON_IRQ_FPA,         /* Summary of FPA_INT_SUM */
-       OCTEON_IRQ_POW,         /* Summary of POW_ECC_ERR */
-       OCTEON_IRQ_L2C,         /* Summary of L2C_INT_STAT */
-       OCTEON_IRQ_IPD,         /* Summary of IPD_INT_SUM */
-       OCTEON_IRQ_PIP,         /* Summary of PIP_INT_REG */
-       OCTEON_IRQ_PKO,         /* Summary of PKO_REG_ERROR */
-       OCTEON_IRQ_ZIP,         /* Summary of ZIP_ERROR */
-       OCTEON_IRQ_TIM,         /* Summary of TIM_REG_ERROR */
-       OCTEON_IRQ_RAD,         /* Summary of RAD_REG_ERROR */
-       OCTEON_IRQ_KEY,         /* Summary of KEY_INT_SUM */
-       OCTEON_IRQ_DFA,         /* Summary of DFA */
-       OCTEON_IRQ_USBCTL,      /* Summary of USBN0_INT_SUM */
-       OCTEON_IRQ_SLI,         /* Summary of SLI_INT_SUM */
-       OCTEON_IRQ_DPI,         /* Summary of DPI_INT_SUM */
-       OCTEON_IRQ_AGX0,        /* Summary of GMX0*+PCS0_INT*_REG */
-       OCTEON_IRQ_AGL  = OCTEON_IRQ_AGX0 + 5,
-       OCTEON_IRQ_PTP,
-       OCTEON_IRQ_PEM0,
-       OCTEON_IRQ_PEM1,
-       OCTEON_IRQ_SRIO0,
-       OCTEON_IRQ_SRIO1,
-       OCTEON_IRQ_LMC0,
-       OCTEON_IRQ_DFM = OCTEON_IRQ_LMC0 + 4,           /* Summary of DFM */
-       OCTEON_IRQ_RST,
+#ifndef CONFIG_PCI_MSI
+       OCTEON_IRQ_LAST = 127
+#endif
 };
 
 #ifdef CONFIG_PCI_MSI
-/* 152 - 407 represent the MSI interrupts 0-255 */
-#define OCTEON_IRQ_MSI_BIT0    (OCTEON_IRQ_RST + 1)
+/* 256 - 511 represent the MSI interrupts 0-255 */
+#define OCTEON_IRQ_MSI_BIT0    (256)
 
 #define OCTEON_IRQ_MSI_LAST      (OCTEON_IRQ_MSI_BIT0 + 255)
 #define OCTEON_IRQ_LAST          (OCTEON_IRQ_MSI_LAST + 1)
-#else
-#define OCTEON_IRQ_LAST         (OCTEON_IRQ_RST + 1)
 #endif
 
 #endif
index bb5b9a4e29c8226846796a162e1b447f4a0e4ab8..986982db7c38c95cf8ca0e4beac9bcee6570f8fe 100644 (file)
@@ -19,6 +19,8 @@
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 
+#define JZ_NAND_NUM_BANKS 4
+
 struct jz_nand_platform_data {
        int                     num_partitions;
        struct mtd_partition    *partitions;
@@ -27,6 +29,8 @@ struct jz_nand_platform_data {
 
        unsigned int busy_gpio;
 
+       unsigned char banks[JZ_NAND_NUM_BANKS];
+
        void (*ident_callback)(struct platform_device *, struct nand_chip *,
                                struct mtd_partition **, int *num_partitions);
 };
index 1e29b9dd1d7395752d401fca112b2f1f528357c8..5222a007bc212a55746f9155605a836da6763442 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/io.h>
 #include <linux/init.h>
 #include <linux/irq.h>
+#include <linux/kconfig.h>
 
 /* loongson internal northbridge initialization */
 extern void bonito_irq_init(void);
@@ -66,7 +67,7 @@ extern int mach_i8259_irq(void);
 #include <linux/interrupt.h>
 static inline void do_perfcnt_IRQ(void)
 {
-#if defined(CONFIG_OPROFILE) || defined(CONFIG_OPROFILE_MODULE)
+#if IS_ENABLED(CONFIG_OPROFILE)
        do_IRQ(LOONGSON2_PERFCNT_IRQ);
 #endif
 }
@@ -244,7 +245,6 @@ static inline void do_perfcnt_IRQ(void)
 
 #ifdef CONFIG_CPU_SUPPORTS_CPUFREQ
 #include <linux/cpufreq.h>
-extern void loongson2_cpu_wait(void);
 extern struct cpufreq_frequency_table loongson2_clockmod_table[];
 
 /* Chip Config */
diff --git a/arch/mips/include/asm/mach-loongson1/irq.h b/arch/mips/include/asm/mach-loongson1/irq.h
new file mode 100644 (file)
index 0000000..da96ed4
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * IRQ mappings for Loongson 1
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+
+#ifndef __ASM_MACH_LOONGSON1_IRQ_H
+#define __ASM_MACH_LOONGSON1_IRQ_H
+
+/*
+ * CPU core Interrupt Numbers
+ */
+#define MIPS_CPU_IRQ_BASE              0
+#define MIPS_CPU_IRQ(x)                        (MIPS_CPU_IRQ_BASE + (x))
+
+#define SOFTINT0_IRQ                   MIPS_CPU_IRQ(0)
+#define SOFTINT1_IRQ                   MIPS_CPU_IRQ(1)
+#define INT0_IRQ                       MIPS_CPU_IRQ(2)
+#define INT1_IRQ                       MIPS_CPU_IRQ(3)
+#define INT2_IRQ                       MIPS_CPU_IRQ(4)
+#define INT3_IRQ                       MIPS_CPU_IRQ(5)
+#define INT4_IRQ                       MIPS_CPU_IRQ(6)
+#define TIMER_IRQ                      MIPS_CPU_IRQ(7)         /* cpu timer */
+
+#define MIPS_CPU_IRQS          (MIPS_CPU_IRQ(7) + 1 - MIPS_CPU_IRQ_BASE)
+
+/*
+ * INT0~3 Interrupt Numbers
+ */
+#define LS1X_IRQ_BASE                  MIPS_CPU_IRQS
+#define LS1X_IRQ(n, x)                 (LS1X_IRQ_BASE + (n << 5) + (x))
+
+#define LS1X_UART0_IRQ                 LS1X_IRQ(0, 2)
+#define LS1X_UART1_IRQ                 LS1X_IRQ(0, 3)
+#define LS1X_UART2_IRQ                 LS1X_IRQ(0, 4)
+#define LS1X_UART3_IRQ                 LS1X_IRQ(0, 5)
+#define LS1X_CAN0_IRQ                  LS1X_IRQ(0, 6)
+#define LS1X_CAN1_IRQ                  LS1X_IRQ(0, 7)
+#define LS1X_SPI0_IRQ                  LS1X_IRQ(0, 8)
+#define LS1X_SPI1_IRQ                  LS1X_IRQ(0, 9)
+#define LS1X_AC97_IRQ                  LS1X_IRQ(0, 10)
+#define LS1X_DMA0_IRQ                  LS1X_IRQ(0, 13)
+#define LS1X_DMA1_IRQ                  LS1X_IRQ(0, 14)
+#define LS1X_DMA2_IRQ                  LS1X_IRQ(0, 15)
+#define LS1X_PWM0_IRQ                  LS1X_IRQ(0, 17)
+#define LS1X_PWM1_IRQ                  LS1X_IRQ(0, 18)
+#define LS1X_PWM2_IRQ                  LS1X_IRQ(0, 19)
+#define LS1X_PWM3_IRQ                  LS1X_IRQ(0, 20)
+#define LS1X_RTC_INT0_IRQ              LS1X_IRQ(0, 21)
+#define LS1X_RTC_INT1_IRQ              LS1X_IRQ(0, 22)
+#define LS1X_RTC_INT2_IRQ              LS1X_IRQ(0, 23)
+#define LS1X_TOY_INT0_IRQ              LS1X_IRQ(0, 24)
+#define LS1X_TOY_INT1_IRQ              LS1X_IRQ(0, 25)
+#define LS1X_TOY_INT2_IRQ              LS1X_IRQ(0, 26)
+#define LS1X_RTC_TICK_IRQ              LS1X_IRQ(0, 27)
+#define LS1X_TOY_TICK_IRQ              LS1X_IRQ(0, 28)
+
+#define LS1X_EHCI_IRQ                  LS1X_IRQ(1, 0)
+#define LS1X_OHCI_IRQ                  LS1X_IRQ(1, 1)
+#define LS1X_GMAC0_IRQ                 LS1X_IRQ(1, 2)
+#define LS1X_GMAC1_IRQ                 LS1X_IRQ(1, 3)
+
+#define LS1X_IRQS              (LS1X_IRQ(4, 31) + 1 - LS1X_IRQ_BASE)
+
+#define NR_IRQS                        (MIPS_CPU_IRQS + LS1X_IRQS)
+
+#endif /* __ASM_MACH_LOONGSON1_IRQ_H */
diff --git a/arch/mips/include/asm/mach-loongson1/loongson1.h b/arch/mips/include/asm/mach-loongson1/loongson1.h
new file mode 100644 (file)
index 0000000..4e18e88
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * Register mappings for Loongson 1
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+
+#ifndef __ASM_MACH_LOONGSON1_LOONGSON1_H
+#define __ASM_MACH_LOONGSON1_LOONGSON1_H
+
+#define DEFAULT_MEMSIZE                        256     /* If no memsize provided */
+
+/* Loongson 1 Register Bases */
+#define LS1X_INTC_BASE                 0x1fd01040
+#define LS1X_EHCI_BASE                 0x1fe00000
+#define LS1X_OHCI_BASE                 0x1fe08000
+#define LS1X_GMAC0_BASE                        0x1fe10000
+#define LS1X_GMAC1_BASE                        0x1fe20000
+
+#define LS1X_UART0_BASE                        0x1fe40000
+#define LS1X_UART1_BASE                        0x1fe44000
+#define LS1X_UART2_BASE                        0x1fe48000
+#define LS1X_UART3_BASE                        0x1fe4c000
+#define LS1X_CAN0_BASE                 0x1fe50000
+#define LS1X_CAN1_BASE                 0x1fe54000
+#define LS1X_I2C0_BASE                 0x1fe58000
+#define LS1X_I2C1_BASE                 0x1fe68000
+#define LS1X_I2C2_BASE                 0x1fe70000
+#define LS1X_PWM_BASE                  0x1fe5c000
+#define LS1X_WDT_BASE                  0x1fe5c060
+#define LS1X_RTC_BASE                  0x1fe64000
+#define LS1X_AC97_BASE                 0x1fe74000
+#define LS1X_NAND_BASE                 0x1fe78000
+#define LS1X_CLK_BASE                  0x1fe78030
+
+#include <regs-clk.h>
+#include <regs-wdt.h>
+
+#endif /* __ASM_MACH_LOONGSON1_LOONGSON1_H */
diff --git a/arch/mips/include/asm/mach-loongson1/platform.h b/arch/mips/include/asm/mach-loongson1/platform.h
new file mode 100644 (file)
index 0000000..2f17161
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@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.
+ */
+
+
+#ifndef __ASM_MACH_LOONGSON1_PLATFORM_H
+#define __ASM_MACH_LOONGSON1_PLATFORM_H
+
+#include <linux/platform_device.h>
+
+extern struct platform_device ls1x_uart_device;
+extern struct platform_device ls1x_eth0_device;
+extern struct platform_device ls1x_ehci_device;
+extern struct platform_device ls1x_rtc_device;
+
+void ls1x_serial_setup(void);
+
+#endif /* __ASM_MACH_LOONGSON1_PLATFORM_H */
diff --git a/arch/mips/include/asm/mach-loongson1/prom.h b/arch/mips/include/asm/mach-loongson1/prom.h
new file mode 100644 (file)
index 0000000..b871dc4
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@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.
+ */
+
+#ifndef __ASM_MACH_LOONGSON1_PROM_H
+#define __ASM_MACH_LOONGSON1_PROM_H
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+/* environment arguments from bootloader */
+extern unsigned long memsize, highmemsize;
+
+/* loongson-specific command line, env and memory initialization */
+extern char *prom_getenv(char *name);
+extern void __init prom_init_cmdline(void);
+
+#endif /* __ASM_MACH_LOONGSON1_PROM_H */
diff --git a/arch/mips/include/asm/mach-loongson1/regs-clk.h b/arch/mips/include/asm/mach-loongson1/regs-clk.h
new file mode 100644 (file)
index 0000000..8efa7fb
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * Loongson 1 Clock Register Definitions.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __ASM_MACH_LOONGSON1_REGS_CLK_H
+#define __ASM_MACH_LOONGSON1_REGS_CLK_H
+
+#define LS1X_CLK_REG(x) \
+               ((void __iomem *)KSEG1ADDR(LS1X_CLK_BASE + (x)))
+
+#define LS1X_CLK_PLL_FREQ              LS1X_CLK_REG(0x0)
+#define LS1X_CLK_PLL_DIV               LS1X_CLK_REG(0x4)
+
+/* Clock PLL Divisor Register Bits */
+#define DIV_DC_EN                      (0x1 << 31)
+#define DIV_DC                         (0x1f << 26)
+#define DIV_CPU_EN                     (0x1 << 25)
+#define DIV_CPU                                (0x1f << 20)
+#define DIV_DDR_EN                     (0x1 << 19)
+#define DIV_DDR                                (0x1f << 14)
+
+#define DIV_DC_SHIFT                   26
+#define DIV_CPU_SHIFT                  20
+#define DIV_DDR_SHIFT                  14
+
+#endif /* __ASM_MACH_LOONGSON1_REGS_CLK_H */
diff --git a/arch/mips/include/asm/mach-loongson1/regs-wdt.h b/arch/mips/include/asm/mach-loongson1/regs-wdt.h
new file mode 100644 (file)
index 0000000..f897de6
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * Loongson 1 watchdog register definitions.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __ASM_MACH_LOONGSON1_REGS_WDT_H
+#define __ASM_MACH_LOONGSON1_REGS_WDT_H
+
+#define LS1X_WDT_REG(x) \
+               ((void __iomem *)KSEG1ADDR(LS1X_WDT_BASE + (x)))
+
+#define LS1X_WDT_EN                    LS1X_WDT_REG(0x0)
+#define LS1X_WDT_SET                   LS1X_WDT_REG(0x4)
+#define LS1X_WDT_TIMER                 LS1X_WDT_REG(0x8)
+
+#endif /* __ASM_MACH_LOONGSON1_REGS_WDT_H */
diff --git a/arch/mips/include/asm/mach-loongson1/war.h b/arch/mips/include/asm/mach-loongson1/war.h
new file mode 100644 (file)
index 0000000..e3680a8
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2002, 2004, 2007 by Ralf Baechle <ralf@linux-mips.org>
+ */
+#ifndef __ASM_MACH_LOONGSON1_WAR_H
+#define __ASM_MACH_LOONGSON1_WAR_H
+
+#define R4600_V1_INDEX_ICACHEOP_WAR    0
+#define R4600_V1_HIT_CACHEOP_WAR       0
+#define R4600_V2_HIT_CACHEOP_WAR       0
+#define R5432_CP0_INTERRUPT_WAR                0
+#define BCM1250_M3_WAR                 0
+#define SIBYTE_1956_WAR                        0
+#define MIPS4K_ICACHE_REFILL_WAR       0
+#define MIPS_CACHE_SYNC_WAR            0
+#define TX49XX_ICACHE_INDEX_INV_WAR    0
+#define RM9000_CDEX_SMP_WAR            0
+#define ICACHE_REFILLS_WORKAROUND_WAR  0
+#define R10000_LLSC_WAR                        0
+#define MIPS34K_MISSED_ITLB_WAR                0
+
+#endif /* __ASM_MACH_LOONGSON1_WAR_H */
index d193fb68cf270d66e734fc7ef88613417cdb94be..966db4be377ca700d94795f10d3b345f8d250621 100644 (file)
@@ -48,7 +48,6 @@
 #define cpu_has_userlocal      1
 #define cpu_has_mips32r2       1
 #define cpu_has_mips64r2       1
-#define cpu_has_dc_aliases     1
 #else
 #error "Unknown Netlogic CPU"
 #endif
index 5e6912fdd0ed2bbe3a2333cb14709c71bb8db93b..490867b03c8f6cb9d1834ed87e590928830aec26 100644 (file)
@@ -9,7 +9,7 @@
 #define ioswabb(a, x)          (x)
 #define __mem_ioswabb(a, x)    (x)
 #if defined(CONFIG_TOSHIBA_RBTX4939) && \
-       (defined(CONFIG_SMC91X) || defined(CONFIG_SMC91X_MODULE)) && \
+       IS_ENABLED(CONFIG_SMC91X) && \
        defined(__BIG_ENDIAN)
 #define NEEDS_TXX9_IOSWABW
 extern u16 (*ioswabw)(volatile u16 *a, u16 x);
index e71ff4c317f2d0bdd8df430b0f439286abb4559f..5b3cb8553e9abafa3d3bb18b4628d1064b1bff12 100644 (file)
@@ -28,6 +28,9 @@
 #define read_c0_vpeconf0()             __read_32bit_c0_register($1, 2)
 #define write_c0_vpeconf0(val)         __write_32bit_c0_register($1, 2, val)
 
+#define read_c0_vpeconf1()             __read_32bit_c0_register($1, 3)
+#define write_c0_vpeconf1(val)         __write_32bit_c0_register($1, 3, val)
+
 #define read_c0_tcstatus()             __read_32bit_c0_register($2, 1)
 #define write_c0_tcstatus(val)         __write_32bit_c0_register($2, 1, val)
 
 #define VPECONF0_XTC_SHIFT     21
 #define VPECONF0_XTC           (_ULCAST_(0xff) << VPECONF0_XTC_SHIFT)
 
+/* VPEConf1 fields (per VPE) */
+#define VPECONF1_NCP1_SHIFT    0
+#define VPECONF1_NCP1          (_ULCAST_(0xff) << VPECONF1_NCP1_SHIFT)
+#define VPECONF1_NCP2_SHIFT    10
+#define VPECONF1_NCP2          (_ULCAST_(0xff) << VPECONF1_NCP2_SHIFT)
+#define VPECONF1_NCX_SHIFT     20
+#define VPECONF1_NCX           (_ULCAST_(0xff) << VPECONF1_NCX_SHIFT)
+
 /* TCStatus fields (per TC) */
 #define TCSTATUS_TASID         (_ULCAST_(0xff))
 #define TCSTATUS_IXMT_SHIFT    10
@@ -350,6 +361,8 @@ do {                                                                        \
 #define write_vpe_c0_vpecontrol(val)   mttc0(1, 1, val)
 #define read_vpe_c0_vpeconf0()         mftc0(1, 2)
 #define write_vpe_c0_vpeconf0(val)     mttc0(1, 2, val)
+#define read_vpe_c0_vpeconf1()         mftc0(1, 3)
+#define write_vpe_c0_vpeconf1(val)     mttc0(1, 3, val)
 #define read_vpe_c0_count()            mftc0(9, 0)
 #define write_vpe_c0_count(val)                mttc0(9, 0, val)
 #define read_vpe_c0_status()           mftc0(12, 0)
index 530008048c6227fb653ccb8b66b0319c442ce8ea..7531ecd654d651df630d1d520e71f762c47547e3 100644 (file)
@@ -117,6 +117,8 @@ search_module_dbetables(unsigned long addr)
 #define MODULE_PROC_FAMILY "RM9000 "
 #elif defined CONFIG_CPU_SB1
 #define MODULE_PROC_FAMILY "SB1 "
+#elif defined CONFIG_CPU_LOONGSON1
+#define MODULE_PROC_FAMILY "LOONGSON1 "
 #elif defined CONFIG_CPU_LOONGSON2
 #define MODULE_PROC_FAMILY "LOONGSON2 "
 #elif defined CONFIG_CPU_CAVIUM_OCTEON
index bf7d41deb9be4c90e4125df042e85cdcab0124a1..7b63a6b722a0d276d6f4c67a49b2a6042a18bacc 100644 (file)
@@ -47,7 +47,9 @@
 #define CPU_BLOCKID_MAP                10
 
 #define LSU_DEFEATURE          0x304
-#define LSU_CERRLOG_REGID      0x09
+#define LSU_DEBUG_ADDR         0x305
+#define LSU_DEBUG_DATA0                0x306
+#define LSU_CERRLOG_REGID      0x309
 #define SCHED_DEFEATURE                0x700
 
 /* Offsets of interest from the 'MAP' Block */
index 86cc3391e50cf30755a4d013ff6f3f46a9269a7c..2c63f97546404ccd9f5431bddd7e250dcf17476d 100644 (file)
@@ -36,6 +36,9 @@
 #define __NLM_HAL_IOMAP_H__
 
 #define XLP_DEFAULT_IO_BASE             0x18000000
+#define XLP_DEFAULT_PCI_ECFG_BASE      XLP_DEFAULT_IO_BASE
+#define XLP_DEFAULT_PCI_CFG_BASE       0x1c000000
+
 #define NMI_BASE                       0xbfc00000
 #define        XLP_IO_CLK                      133333333
 
 #define        PCI_DEVICE_ID_NLM_PIC           0x1003
 #define        PCI_DEVICE_ID_NLM_PCIE          0x1004
 #define        PCI_DEVICE_ID_NLM_EHCI          0x1007
-#define        PCI_DEVICE_ID_NLM_ILK           0x1008
+#define        PCI_DEVICE_ID_NLM_OHCI          0x1008
 #define        PCI_DEVICE_ID_NLM_NAE           0x1009
 #define        PCI_DEVICE_ID_NLM_POE           0x100A
 #define        PCI_DEVICE_ID_NLM_FMN           0x100B
diff --git a/arch/mips/include/asm/netlogic/xlp-hal/pcibus.h b/arch/mips/include/asm/netlogic/xlp-hal/pcibus.h
new file mode 100644 (file)
index 0000000..66c323d
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2003-2012 Broadcom Corporation
+ * All Rights Reserved
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the Broadcom
+ * license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __NLM_HAL_PCIBUS_H__
+#define        __NLM_HAL_PCIBUS_H__
+
+/* PCIE Memory and IO regions */
+#define        PCIE_MEM_BASE                   0xd0000000ULL
+#define        PCIE_MEM_LIMIT                  0xdfffffffULL
+#define        PCIE_IO_BASE                    0x14000000ULL
+#define        PCIE_IO_LIMIT                   0x15ffffffULL
+
+#define        PCIE_BRIDGE_CMD                 0x1
+#define        PCIE_BRIDGE_MSI_CAP             0x14
+#define        PCIE_BRIDGE_MSI_ADDRL           0x15
+#define        PCIE_BRIDGE_MSI_ADDRH           0x16
+#define        PCIE_BRIDGE_MSI_DATA            0x17
+
+/* XLP Global PCIE configuration space registers */
+#define        PCIE_BYTE_SWAP_MEM_BASE         0x247
+#define        PCIE_BYTE_SWAP_MEM_LIM          0x248
+#define        PCIE_BYTE_SWAP_IO_BASE          0x249
+#define        PCIE_BYTE_SWAP_IO_LIM           0x24A
+#define        PCIE_MSI_STATUS                 0x25A
+#define        PCIE_MSI_EN                     0x25B
+#define        PCIE_INT_EN0                    0x261
+
+/* PCIE_MSI_EN */
+#define        PCIE_MSI_VECTOR_INT_EN          0xFFFFFFFF
+
+/* PCIE_INT_EN0 */
+#define        PCIE_MSI_INT_EN                 (1 << 9)
+
+#ifndef __ASSEMBLY__
+
+#define        nlm_read_pcie_reg(b, r)         nlm_read_reg(b, r)
+#define        nlm_write_pcie_reg(b, r, v)     nlm_write_reg(b, r, v)
+#define        nlm_get_pcie_base(node, inst)   \
+                       nlm_pcicfg_base(XLP_IO_PCIE_OFFSET(node, inst))
+#define        nlm_get_pcie_regbase(node, inst)        \
+                       (nlm_get_pcie_base(node, inst) + XLP_IO_PCI_HDRSZ)
+
+int xlp_pcie_link_irt(int link);
+#endif
+#endif /* __NLM_HAL_PCIBUS_H__ */
index b6628f7ccf74b027edc6f23a4ab5b3797a2792f5..ad8b80233a63b255b12a8227bce551a819711560 100644 (file)
 #define PIC_NUM_USB_IRTS               6
 #define PIC_IRT_USB_0_INDEX            115
 #define PIC_IRT_EHCI_0_INDEX           115
+#define PIC_IRT_OHCI_0_INDEX           116
+#define PIC_IRT_OHCI_1_INDEX           117
 #define PIC_IRT_EHCI_1_INDEX           118
+#define PIC_IRT_OHCI_2_INDEX           119
+#define PIC_IRT_OHCI_3_INDEX           120
 #define PIC_IRT_USB_INDEX(num)         ((num) + PIC_IRT_USB_0_INDEX)
 /* 115 to 120 */
 #define PIC_IRT_GDX_INDEX              121
diff --git a/arch/mips/include/asm/netlogic/xlp-hal/usb.h b/arch/mips/include/asm/netlogic/xlp-hal/usb.h
new file mode 100644 (file)
index 0000000..a9cd350
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2003-2012 Broadcom Corporation
+ * All Rights Reserved
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the Broadcom
+ * license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __NLM_HAL_USB_H__
+#define __NLM_HAL_USB_H__
+
+#define USB_CTL_0                      0x01
+#define USB_PHY_0                      0x0A
+#define USB_PHY_RESET                  0x01
+#define USB_PHY_PORT_RESET_0           0x10
+#define USB_PHY_PORT_RESET_1           0x20
+#define USB_CONTROLLER_RESET           0x01
+#define USB_INT_STATUS                 0x0E
+#define USB_INT_EN                     0x0F
+#define USB_PHY_INTERRUPT_EN           0x01
+#define USB_OHCI_INTERRUPT_EN          0x02
+#define USB_OHCI_INTERRUPT1_EN         0x04
+#define USB_OHCI_INTERRUPT2_EN         0x08
+#define USB_CTRL_INTERRUPT_EN          0x10
+
+#ifndef __ASSEMBLY__
+
+#define nlm_read_usb_reg(b, r)                 nlm_read_reg(b, r)
+#define nlm_write_usb_reg(b, r, v)             nlm_write_reg(b, r, v)
+#define nlm_get_usb_pcibase(node, inst)                \
+       nlm_pcicfg_base(XLP_IO_USB_OFFSET(node, inst))
+#define nlm_get_usb_hcd_base(node, inst)       \
+       nlm_xkphys_map_pcibar0(nlm_get_usb_pcibase(node, inst))
+#define nlm_get_usb_regbase(node, inst)                \
+       (nlm_get_usb_pcibase(node, inst) + XLP_IO_PCI_HDRSZ)
+
+#endif
+#endif /* __NLM_HAL_USB_H__ */
index 1540588e396dbee6600755a3bb3ad2417d8b962e..7e47209327a542b3c090560243690faebe8dda88 100644 (file)
 #ifndef _NLM_HAL_XLP_H
 #define _NLM_HAL_XLP_H
 
-#define PIC_UART_0_IRQ           17
-#define PIC_UART_1_IRQ           18
+#define PIC_UART_0_IRQ                 17
+#define PIC_UART_1_IRQ                 18
+#define PIC_PCIE_LINK_0_IRQ            19
+#define PIC_PCIE_LINK_1_IRQ            20
+#define PIC_PCIE_LINK_2_IRQ            21
+#define PIC_PCIE_LINK_3_IRQ            22
+#define PIC_EHCI_0_IRQ                 23
+#define PIC_EHCI_1_IRQ                 24
+#define PIC_OHCI_0_IRQ                 25
+#define PIC_OHCI_1_IRQ                 26
+#define PIC_OHCI_2_IRQ                 27
+#define PIC_OHCI_3_IRQ                 28
+#define PIC_MMC_IRQ                    29
+#define PIC_I2C_0_IRQ                  30
+#define PIC_I2C_1_IRQ                  31
 
 #ifndef __ASSEMBLY__
 
diff --git a/arch/mips/include/asm/netlogic/xlr/bridge.h b/arch/mips/include/asm/netlogic/xlr/bridge.h
new file mode 100644 (file)
index 0000000..2d02428
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2003-2012 Broadcom Corporation
+ * All Rights Reserved
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the Broadcom
+ * license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _ASM_NLM_BRIDGE_H_
+#define _ASM_NLM_BRIDGE_H_
+
+#define BRIDGE_DRAM_0_BAR              0
+#define BRIDGE_DRAM_1_BAR              1
+#define BRIDGE_DRAM_2_BAR              2
+#define BRIDGE_DRAM_3_BAR              3
+#define BRIDGE_DRAM_4_BAR              4
+#define BRIDGE_DRAM_5_BAR              5
+#define BRIDGE_DRAM_6_BAR              6
+#define BRIDGE_DRAM_7_BAR              7
+#define BRIDGE_DRAM_CHN_0_MTR_0_BAR    8
+#define BRIDGE_DRAM_CHN_0_MTR_1_BAR    9
+#define BRIDGE_DRAM_CHN_0_MTR_2_BAR    10
+#define BRIDGE_DRAM_CHN_0_MTR_3_BAR    11
+#define BRIDGE_DRAM_CHN_0_MTR_4_BAR    12
+#define BRIDGE_DRAM_CHN_0_MTR_5_BAR    13
+#define BRIDGE_DRAM_CHN_0_MTR_6_BAR    14
+#define BRIDGE_DRAM_CHN_0_MTR_7_BAR    15
+#define BRIDGE_DRAM_CHN_1_MTR_0_BAR    16
+#define BRIDGE_DRAM_CHN_1_MTR_1_BAR    17
+#define BRIDGE_DRAM_CHN_1_MTR_2_BAR    18
+#define BRIDGE_DRAM_CHN_1_MTR_3_BAR    19
+#define BRIDGE_DRAM_CHN_1_MTR_4_BAR    20
+#define BRIDGE_DRAM_CHN_1_MTR_5_BAR    21
+#define BRIDGE_DRAM_CHN_1_MTR_6_BAR    22
+#define BRIDGE_DRAM_CHN_1_MTR_7_BAR    23
+#define BRIDGE_CFG_BAR                 24
+#define BRIDGE_PHNX_IO_BAR             25
+#define BRIDGE_FLASH_BAR               26
+#define BRIDGE_SRAM_BAR                        27
+#define BRIDGE_HTMEM_BAR               28
+#define BRIDGE_HTINT_BAR               29
+#define BRIDGE_HTPIC_BAR               30
+#define BRIDGE_HTSM_BAR                        31
+#define BRIDGE_HTIO_BAR                        32
+#define BRIDGE_HTCFG_BAR               33
+#define BRIDGE_PCIXCFG_BAR             34
+#define BRIDGE_PCIXMEM_BAR             35
+#define BRIDGE_PCIXIO_BAR              36
+#define BRIDGE_DEVICE_MASK             37
+#define BRIDGE_AERR_INTR_LOG1          38
+#define BRIDGE_AERR_INTR_LOG2          39
+#define BRIDGE_AERR_INTR_LOG3          40
+#define BRIDGE_AERR_DEV_STAT           41
+#define BRIDGE_AERR1_LOG1              42
+#define BRIDGE_AERR1_LOG2              43
+#define BRIDGE_AERR1_LOG3              44
+#define BRIDGE_AERR1_DEV_STAT          45
+#define BRIDGE_AERR_INTR_EN            46
+#define BRIDGE_AERR_UPG                        47
+#define BRIDGE_AERR_CLEAR              48
+#define BRIDGE_AERR1_CLEAR             49
+#define BRIDGE_SBE_COUNTS              50
+#define BRIDGE_DBE_COUNTS              51
+#define BRIDGE_BITERR_INT_EN           52
+
+#define BRIDGE_SYS2IO_CREDITS          53
+#define BRIDGE_EVNT_CNT_CTRL1          54
+#define BRIDGE_EVNT_COUNTER1           55
+#define BRIDGE_EVNT_CNT_CTRL2          56
+#define BRIDGE_EVNT_COUNTER2           57
+#define BRIDGE_RESERVED1               58
+
+#define BRIDGE_DEFEATURE               59
+#define BRIDGE_SCRATCH0                        60
+#define BRIDGE_SCRATCH1                        61
+#define BRIDGE_SCRATCH2                        62
+#define BRIDGE_SCRATCH3                        63
+
+#endif
diff --git a/arch/mips/include/asm/netlogic/xlr/flash.h b/arch/mips/include/asm/netlogic/xlr/flash.h
new file mode 100644 (file)
index 0000000..f8aca54
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2003-2012 Broadcom Corporation
+ * All Rights Reserved
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the Broadcom
+ * license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _ASM_NLM_FLASH_H_
+#define _ASM_NLM_FLASH_H_
+
+#define FLASH_CSBASE_ADDR(cs)          (cs)
+#define FLASH_CSADDR_MASK(cs)          (0x10 + (cs))
+#define FLASH_CSDEV_PARM(cs)           (0x20 + (cs))
+#define FLASH_CSTIME_PARMA(cs)         (0x30 + (cs))
+#define FLASH_CSTIME_PARMB(cs)         (0x40 + (cs))
+
+#define FLASH_INT_MASK                 0x50
+#define FLASH_INT_STATUS               0x60
+#define FLASH_ERROR_STATUS             0x70
+#define FLASH_ERROR_ADDR               0x80
+
+#define FLASH_NAND_CLE(cs)             (0x90 + (cs))
+#define FLASH_NAND_ALE(cs)             (0xa0 + (cs))
+
+#define FLASH_NAND_CSDEV_PARAM         0x000041e6
+#define FLASH_NAND_CSTIME_PARAMA       0x4f400e22
+#define FLASH_NAND_CSTIME_PARAMB       0x000083cf
+
+#endif
index 51f6ad4aeb14397284f9ac47821293335b2641a7..8492e835b11072b8e054a6ea6948f8b22d5be6a6 100644 (file)
 #ifndef _ASM_NLM_GPIO_H
 #define _ASM_NLM_GPIO_H
 
-#define NETLOGIC_GPIO_INT_EN_REG               0
-#define NETLOGIC_GPIO_INPUT_INVERSION_REG      1
-#define NETLOGIC_GPIO_IO_DIR_REG               2
-#define NETLOGIC_GPIO_IO_DATA_WR_REG           3
-#define NETLOGIC_GPIO_IO_DATA_RD_REG           4
+#define GPIO_INT_EN_REG                        0
+#define GPIO_INPUT_INVERSION_REG       1
+#define GPIO_IO_DIR_REG                        2
+#define GPIO_IO_DATA_WR_REG            3
+#define GPIO_IO_DATA_RD_REG            4
 
-#define NETLOGIC_GPIO_SWRESET_REG              8
-#define NETLOGIC_GPIO_DRAM1_CNTRL_REG          9
-#define NETLOGIC_GPIO_DRAM1_RATIO_REG          10
-#define NETLOGIC_GPIO_DRAM1_RESET_REG          11
-#define NETLOGIC_GPIO_DRAM1_STATUS_REG         12
-#define NETLOGIC_GPIO_DRAM2_CNTRL_REG          13
-#define NETLOGIC_GPIO_DRAM2_RATIO_REG          14
-#define NETLOGIC_GPIO_DRAM2_RESET_REG          15
-#define NETLOGIC_GPIO_DRAM2_STATUS_REG         16
+#define GPIO_SWRESET_REG               8
+#define GPIO_DRAM1_CNTRL_REG           9
+#define GPIO_DRAM1_RATIO_REG           10
+#define GPIO_DRAM1_RESET_REG           11
+#define GPIO_DRAM1_STATUS_REG          12
+#define GPIO_DRAM2_CNTRL_REG           13
+#define GPIO_DRAM2_RATIO_REG           14
+#define GPIO_DRAM2_RESET_REG           15
+#define GPIO_DRAM2_STATUS_REG          16
 
-#define NETLOGIC_GPIO_PWRON_RESET_CFG_REG      21
-#define NETLOGIC_GPIO_BIST_ALL_GO_STATUS_REG   24
-#define NETLOGIC_GPIO_BIST_CPU_GO_STATUS_REG   25
-#define NETLOGIC_GPIO_BIST_DEV_GO_STATUS_REG   26
+#define GPIO_PWRON_RESET_CFG_REG       21
+#define GPIO_BIST_ALL_GO_STATUS_REG    24
+#define GPIO_BIST_CPU_GO_STATUS_REG    25
+#define GPIO_BIST_DEV_GO_STATUS_REG    26
 
-#define NETLOGIC_GPIO_FUSE_BANK_REG            35
-#define NETLOGIC_GPIO_CPU_RESET_REG            40
-#define NETLOGIC_GPIO_RNG_REG                  43
+#define GPIO_FUSE_BANK_REG             35
+#define GPIO_CPU_RESET_REG             40
+#define GPIO_RNG_REG                   43
 
-#define NETLOGIC_PWRON_RESET_PCMCIA_BOOT       17
-#define NETLOGIC_GPIO_LED_BITMAP       0x1700000
-#define NETLOGIC_GPIO_LED_0_SHIFT              20
-#define NETLOGIC_GPIO_LED_1_SHIFT              24
+#define PWRON_RESET_PCMCIA_BOOT                17
 
-#define NETLOGIC_GPIO_LED_OUTPUT_CODE_RESET    0x01
-#define NETLOGIC_GPIO_LED_OUTPUT_CODE_HARD_RESET 0x02
-#define NETLOGIC_GPIO_LED_OUTPUT_CODE_SOFT_RESET 0x03
-#define NETLOGIC_GPIO_LED_OUTPUT_CODE_MAIN     0x04
+#define GPIO_LED_BITMAP                        0x1700000
+#define GPIO_LED_0_SHIFT               20
+#define GPIO_LED_1_SHIFT               24
+
+#define GPIO_LED_OUTPUT_CODE_RESET     0x01
+#define GPIO_LED_OUTPUT_CODE_HARD_RESET 0x02
+#define GPIO_LED_OUTPUT_CODE_SOFT_RESET 0x03
+#define GPIO_LED_OUTPUT_CODE_MAIN      0x04
 
 #endif
diff --git a/arch/mips/include/asm/octeon/cvmx-helper-fpa.h b/arch/mips/include/asm/octeon/cvmx-helper-fpa.h
deleted file mode 100644 (file)
index 5ff8c93..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/***********************license start***************
- * Author: Cavium Networks
- *
- * Contact: support@caviumnetworks.com
- * This file is part of the OCTEON SDK
- *
- * Copyright (c) 2003-2008 Cavium Networks
- *
- * This file 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 file is distributed in the hope that it will be useful, but
- * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
- * NONINFRINGEMENT.  See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this file; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- * or visit http://www.gnu.org/licenses/.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium Networks for more information
- ***********************license end**************************************/
-
-/**
- * @file
- *
- * Helper functions for FPA setup.
- *
- */
-#ifndef __CVMX_HELPER_H_FPA__
-#define __CVMX_HELPER_H_FPA__
-
-/**
- * Allocate memory and initialize the FPA pools using memory
- * from cvmx-bootmem. Sizes of each element in the pools is
- * controlled by the cvmx-config.h header file. Specifying
- * zero for any parameter will cause that FPA pool to not be
- * setup. This is useful if you aren't using some of the
- * hardware and want to save memory.
- *
- * @packet_buffers:
- *               Number of packet buffers to allocate
- * @work_queue_entries:
- *               Number of work queue entries
- * @pko_buffers:
- *               PKO Command buffers. You should at minimum have two per
- *               each PKO queue.
- * @tim_buffers:
- *               TIM ring buffer command queues. At least two per timer bucket
- *               is recommened.
- * @dfa_buffers:
- *               DFA command buffer. A relatively small (32 for example)
- *               number should work.
- * Returns Zero on success, non-zero if out of memory
- */
-extern int cvmx_helper_initialize_fpa(int packet_buffers,
-                                     int work_queue_entries, int pko_buffers,
-                                     int tim_buffers, int dfa_buffers);
-
-#endif /* __CVMX_HELPER_H__ */
index 3169cd79f2ac251901e81ba366050b9c6e87eca5..0ac6b9f412bec4f998f3d6bdbc60190da35595f2 100644 (file)
@@ -61,8 +61,6 @@ typedef union {
        } s;
 } cvmx_helper_link_info_t;
 
-#include "cvmx-helper-fpa.h"
-
 #include <asm/octeon/cvmx-helper-errata.h>
 #include "cvmx-helper-loop.h"
 #include "cvmx-helper-npi.h"
index f72f768cd3a47b14b6be4397ba56a6c29103a9ae..1e2486e235735d91de52a82c05ddc34550e06b22 100644 (file)
@@ -215,11 +215,6 @@ struct octeon_cf_data {
        int             dma_engine;     /* -1 for no DMA */
 };
 
-struct octeon_i2c_data {
-       unsigned int    sys_freq;
-       unsigned int    i2c_freq;
-};
-
 extern void octeon_write_lcd(const char *s);
 extern void octeon_check_cpu_bist(void);
 extern int octeon_get_boot_debug_flag(void);
index 7206d445bab876e926cec6bd242309b893870007..8808bf548b99d2e0b4966b162f22ed4505d046e0 100644 (file)
@@ -20,9 +20,6 @@
 extern int early_init_dt_scan_memory_arch(unsigned long node,
        const char *uname, int depth, void *data);
 
-extern int reserve_mem_mach(unsigned long addr, unsigned long size);
-extern void free_mem_mach(unsigned long addr, unsigned long size);
-
 extern void device_tree_init(void);
 
 static inline unsigned long pci_address_to_pio(phys_addr_t address)
index c9736fc0632542d11920ab141ae69d2f7205579c..8935426a56ab69c4a0b444b6905819262b5a4f97 100644 (file)
@@ -33,6 +33,12 @@ typedef long asiduse;
 #endif
 #endif
 
+/*
+ * VPE Management information
+ */
+
+#define MAX_SMTC_VPES  MAX_SMTC_TLBS   /* FIXME: May not always be true. */
+
 extern asiduse smtc_live_asid[MAX_SMTC_TLBS][MAX_SMTC_ASIDS];
 
 struct mm_struct;
index 653a412c036ca7a6b372862d5ecc88a0038b1c89..3b92efef56d3c9fb139d374a4436d4398bf8c9d5 100644 (file)
@@ -687,7 +687,7 @@ extern size_t __copy_user(void *__to, const void *__from, size_t __n);
        __MODULE_JAL(__copy_user)                                       \
        : "+r" (__cu_to_r), "+r" (__cu_from_r), "+r" (__cu_len_r)       \
        :                                                               \
-       : "$8", "$9", "$10", "$11", "$12", "$15", "$24", "$31",         \
+       : "$8", "$9", "$10", "$11", "$12", "$14", "$15", "$24", "$31",  \
          DADDI_SCRATCH, "memory");                                     \
        __cu_len_r;                                                     \
 })
@@ -797,7 +797,7 @@ extern size_t __copy_user_inatomic(void *__to, const void *__from, size_t __n);
        ".set\treorder"                                                 \
        : "+r" (__cu_to_r), "+r" (__cu_from_r), "+r" (__cu_len_r)       \
        :                                                               \
-       : "$8", "$9", "$10", "$11", "$12", "$15", "$24", "$31",         \
+       : "$8", "$9", "$10", "$11", "$12", "$14", "$15", "$24", "$31",  \
          DADDI_SCRATCH, "memory");                                     \
        __cu_len_r;                                                     \
 })
@@ -820,7 +820,7 @@ extern size_t __copy_user_inatomic(void *__to, const void *__from, size_t __n);
        ".set\treorder"                                                 \
        : "+r" (__cu_to_r), "+r" (__cu_from_r), "+r" (__cu_len_r)       \
        :                                                               \
-       : "$8", "$9", "$10", "$11", "$12", "$15", "$24", "$31",         \
+       : "$8", "$9", "$10", "$11", "$12", "$14", "$15", "$24", "$31",  \
          DADDI_SCRATCH, "memory");                                     \
        __cu_len_r;                                                     \
 })
index 440a21dab575705412805809c57ce31a893825b3..3d9f75f7ffc9a7549af0e7ada587f951c9bd43ef 100644 (file)
@@ -6,6 +6,7 @@
  * Copyright (C) 2004, 2005, 2006, 2008  Thiemo Seufer
  * Copyright (C) 2005  Maciej W. Rozycki
  * Copyright (C) 2006  Ralf Baechle (ralf@linux-mips.org)
+ * Copyright (C) 2012  MIPS Technologies, Inc.
  */
 
 #include <linux/types.h>
@@ -62,8 +63,10 @@ void __uasminit uasm_i##op(u32 **buf, unsigned int a, signed int b)
 
 Ip_u2u1s3(_addiu);
 Ip_u3u1u2(_addu);
-Ip_u2u1u3(_andi);
 Ip_u3u1u2(_and);
+Ip_u2u1u3(_andi);
+Ip_u1u2s3(_bbit0);
+Ip_u1u2s3(_bbit1);
 Ip_u1u2s3(_beq);
 Ip_u1u2s3(_beql);
 Ip_u1s2(_bgez);
@@ -72,55 +75,54 @@ Ip_u1s2(_bltz);
 Ip_u1s2(_bltzl);
 Ip_u1u2s3(_bne);
 Ip_u2s3u1(_cache);
-Ip_u1u2u3(_dmfc0);
-Ip_u1u2u3(_dmtc0);
 Ip_u2u1s3(_daddiu);
 Ip_u3u1u2(_daddu);
+Ip_u2u1msbu3(_dins);
+Ip_u2u1msbu3(_dinsm);
+Ip_u1u2u3(_dmfc0);
+Ip_u1u2u3(_dmtc0);
+Ip_u2u1u3(_drotr);
+Ip_u2u1u3(_drotr32);
 Ip_u2u1u3(_dsll);
 Ip_u2u1u3(_dsll32);
 Ip_u2u1u3(_dsra);
 Ip_u2u1u3(_dsrl);
 Ip_u2u1u3(_dsrl32);
-Ip_u2u1u3(_drotr);
-Ip_u2u1u3(_drotr32);
 Ip_u3u1u2(_dsubu);
 Ip_0(_eret);
 Ip_u1(_j);
 Ip_u1(_jal);
 Ip_u1(_jr);
 Ip_u2s3u1(_ld);
+Ip_u3u1u2(_ldx);
 Ip_u2s3u1(_ll);
 Ip_u2s3u1(_lld);
 Ip_u1s2(_lui);
 Ip_u2s3u1(_lw);
+Ip_u3u1u2(_lwx);
 Ip_u1u2u3(_mfc0);
 Ip_u1u2u3(_mtc0);
-Ip_u2u1u3(_ori);
 Ip_u3u1u2(_or);
+Ip_u2u1u3(_ori);
 Ip_u2s3u1(_pref);
 Ip_0(_rfe);
+Ip_u2u1u3(_rotr);
 Ip_u2s3u1(_sc);
 Ip_u2s3u1(_scd);
 Ip_u2s3u1(_sd);
 Ip_u2u1u3(_sll);
 Ip_u2u1u3(_sra);
 Ip_u2u1u3(_srl);
-Ip_u2u1u3(_rotr);
 Ip_u3u1u2(_subu);
 Ip_u2s3u1(_sw);
+Ip_u1(_syscall);
 Ip_0(_tlbp);
 Ip_0(_tlbr);
 Ip_0(_tlbwi);
 Ip_0(_tlbwr);
 Ip_u3u1u2(_xor);
 Ip_u2u1u3(_xori);
-Ip_u2u1msbu3(_dins);
-Ip_u2u1msbu3(_dinsm);
-Ip_u1(_syscall);
-Ip_u1u2s3(_bbit0);
-Ip_u1u2s3(_bbit1);
-Ip_u3u1u2(_lwx);
-Ip_u3u1u2(_ldx);
+
 
 /* Handle labels. */
 struct uasm_label {
@@ -145,37 +147,37 @@ static inline void __uasminit uasm_l##lb(struct uasm_label **lab, u32 *addr) \
 
 /* convenience macros for instructions */
 #ifdef CONFIG_64BIT
+# define UASM_i_ADDIU(buf, rs, rt, val) uasm_i_daddiu(buf, rs, rt, val)
+# define UASM_i_ADDU(buf, rs, rt, rd) uasm_i_daddu(buf, rs, rt, rd)
+# define UASM_i_LL(buf, rs, rt, off) uasm_i_lld(buf, rs, rt, off)
 # define UASM_i_LW(buf, rs, rt, off) uasm_i_ld(buf, rs, rt, off)
-# define UASM_i_SW(buf, rs, rt, off) uasm_i_sd(buf, rs, rt, off)
+# define UASM_i_LWX(buf, rs, rt, rd) uasm_i_ldx(buf, rs, rt, rd)
+# define UASM_i_MFC0(buf, rt, rd...) uasm_i_dmfc0(buf, rt, rd)
+# define UASM_i_MTC0(buf, rt, rd...) uasm_i_dmtc0(buf, rt, rd)
+# define UASM_i_ROTR(buf, rs, rt, sh) uasm_i_drotr(buf, rs, rt, sh)
+# define UASM_i_SC(buf, rs, rt, off) uasm_i_scd(buf, rs, rt, off)
 # define UASM_i_SLL(buf, rs, rt, sh) uasm_i_dsll(buf, rs, rt, sh)
 # define UASM_i_SRA(buf, rs, rt, sh) uasm_i_dsra(buf, rs, rt, sh)
 # define UASM_i_SRL(buf, rs, rt, sh) uasm_i_dsrl(buf, rs, rt, sh)
 # define UASM_i_SRL_SAFE(buf, rs, rt, sh) uasm_i_dsrl_safe(buf, rs, rt, sh)
-# define UASM_i_ROTR(buf, rs, rt, sh) uasm_i_drotr(buf, rs, rt, sh)
-# define UASM_i_MFC0(buf, rt, rd...) uasm_i_dmfc0(buf, rt, rd)
-# define UASM_i_MTC0(buf, rt, rd...) uasm_i_dmtc0(buf, rt, rd)
-# define UASM_i_ADDIU(buf, rs, rt, val) uasm_i_daddiu(buf, rs, rt, val)
-# define UASM_i_ADDU(buf, rs, rt, rd) uasm_i_daddu(buf, rs, rt, rd)
 # define UASM_i_SUBU(buf, rs, rt, rd) uasm_i_dsubu(buf, rs, rt, rd)
-# define UASM_i_LL(buf, rs, rt, off) uasm_i_lld(buf, rs, rt, off)
-# define UASM_i_SC(buf, rs, rt, off) uasm_i_scd(buf, rs, rt, off)
-# define UASM_i_LWX(buf, rs, rt, rd) uasm_i_ldx(buf, rs, rt, rd)
+# define UASM_i_SW(buf, rs, rt, off) uasm_i_sd(buf, rs, rt, off)
 #else
+# define UASM_i_ADDIU(buf, rs, rt, val) uasm_i_addiu(buf, rs, rt, val)
+# define UASM_i_ADDU(buf, rs, rt, rd) uasm_i_addu(buf, rs, rt, rd)
+# define UASM_i_LL(buf, rs, rt, off) uasm_i_ll(buf, rs, rt, off)
 # define UASM_i_LW(buf, rs, rt, off) uasm_i_lw(buf, rs, rt, off)
-# define UASM_i_SW(buf, rs, rt, off) uasm_i_sw(buf, rs, rt, off)
+# define UASM_i_LWX(buf, rs, rt, rd) uasm_i_lwx(buf, rs, rt, rd)
+# define UASM_i_MFC0(buf, rt, rd...) uasm_i_mfc0(buf, rt, rd)
+# define UASM_i_MTC0(buf, rt, rd...) uasm_i_mtc0(buf, rt, rd)
+# define UASM_i_ROTR(buf, rs, rt, sh) uasm_i_rotr(buf, rs, rt, sh)
+# define UASM_i_SC(buf, rs, rt, off) uasm_i_sc(buf, rs, rt, off)
 # define UASM_i_SLL(buf, rs, rt, sh) uasm_i_sll(buf, rs, rt, sh)
 # define UASM_i_SRA(buf, rs, rt, sh) uasm_i_sra(buf, rs, rt, sh)
 # define UASM_i_SRL(buf, rs, rt, sh) uasm_i_srl(buf, rs, rt, sh)
 # define UASM_i_SRL_SAFE(buf, rs, rt, sh) uasm_i_srl(buf, rs, rt, sh)
-# define UASM_i_ROTR(buf, rs, rt, sh) uasm_i_rotr(buf, rs, rt, sh)
-# define UASM_i_MFC0(buf, rt, rd...) uasm_i_mfc0(buf, rt, rd)
-# define UASM_i_MTC0(buf, rt, rd...) uasm_i_mtc0(buf, rt, rd)
-# define UASM_i_ADDIU(buf, rs, rt, val) uasm_i_addiu(buf, rs, rt, val)
-# define UASM_i_ADDU(buf, rs, rt, rd) uasm_i_addu(buf, rs, rt, rd)
 # define UASM_i_SUBU(buf, rs, rt, rd) uasm_i_subu(buf, rs, rt, rd)
-# define UASM_i_LL(buf, rs, rt, off) uasm_i_ll(buf, rs, rt, off)
-# define UASM_i_SC(buf, rs, rt, off) uasm_i_sc(buf, rs, rt, off)
-# define UASM_i_LWX(buf, rs, rt, rd) uasm_i_lwx(buf, rs, rt, rd)
+# define UASM_i_SW(buf, rs, rt, off) uasm_i_sw(buf, rs, rt, off)
 #endif
 
 #define uasm_i_b(buf, off) uasm_i_beq(buf, 0, 0, off)
@@ -183,19 +185,10 @@ static inline void __uasminit uasm_l##lb(struct uasm_label **lab, u32 *addr) \
 #define uasm_i_beqzl(buf, rs, off) uasm_i_beql(buf, rs, 0, off)
 #define uasm_i_bnez(buf, rs, off) uasm_i_bne(buf, rs, 0, off)
 #define uasm_i_bnezl(buf, rs, off) uasm_i_bnel(buf, rs, 0, off)
+#define uasm_i_ehb(buf) uasm_i_sll(buf, 0, 0, 3)
 #define uasm_i_move(buf, a, b) UASM_i_ADDU(buf, a, 0, b)
 #define uasm_i_nop(buf) uasm_i_sll(buf, 0, 0, 0)
 #define uasm_i_ssnop(buf) uasm_i_sll(buf, 0, 0, 1)
-#define uasm_i_ehb(buf) uasm_i_sll(buf, 0, 0, 3)
-
-static inline void uasm_i_dsrl_safe(u32 **p, unsigned int a1,
-                                   unsigned int a2, unsigned int a3)
-{
-       if (a3 < 32)
-               uasm_i_dsrl(p, a1, a2, a3);
-       else
-               uasm_i_dsrl32(p, a1, a2, a3 - 32);
-}
 
 static inline void uasm_i_drotr_safe(u32 **p, unsigned int a1,
                                     unsigned int a2, unsigned int a3)
@@ -215,6 +208,15 @@ static inline void uasm_i_dsll_safe(u32 **p, unsigned int a1,
                uasm_i_dsll32(p, a1, a2, a3 - 32);
 }
 
+static inline void uasm_i_dsrl_safe(u32 **p, unsigned int a1,
+                                   unsigned int a2, unsigned int a3)
+{
+       if (a3 < 32)
+               uasm_i_dsrl(p, a1, a2, a3);
+       else
+               uasm_i_dsrl32(p, a1, a2, a3 - 32);
+}
+
 /* Handle relocations. */
 struct uasm_reloc {
        u32 *addr;
@@ -234,16 +236,16 @@ void uasm_copy_handler(struct uasm_reloc *rel, struct uasm_label *lab,
 int uasm_insn_has_bdelay(struct uasm_reloc *rel, u32 *addr);
 
 /* Convenience functions for labeled branches. */
-void uasm_il_bltz(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid);
 void uasm_il_b(u32 **p, struct uasm_reloc **r, int lid);
+void uasm_il_bbit0(u32 **p, struct uasm_reloc **r, unsigned int reg,
+                  unsigned int bit, int lid);
+void uasm_il_bbit1(u32 **p, struct uasm_reloc **r, unsigned int reg,
+                  unsigned int bit, int lid);
 void uasm_il_beqz(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid);
 void uasm_il_beqzl(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid);
+void uasm_il_bgezl(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid);
+void uasm_il_bgez(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid);
+void uasm_il_bltz(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid);
 void uasm_il_bne(u32 **p, struct uasm_reloc **r, unsigned int reg1,
                 unsigned int reg2, int lid);
 void uasm_il_bnez(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid);
-void uasm_il_bgezl(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid);
-void uasm_il_bgez(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid);
-void uasm_il_bbit0(u32 **p, struct uasm_reloc **r, unsigned int reg,
-                  unsigned int bit, int lid);
-void uasm_il_bbit1(u32 **p, struct uasm_reloc **r, unsigned int reg,
-                  unsigned int bit, int lid);
index d8dad5340ea30d22eac825883012a14341fbf157..bebbde01be92870cf06ff40ee373b1e6dab71ccb 100644 (file)
 #ifndef __ASSEMBLY__
 
 #define __ARCH_OMIT_COMPAT_SYS_GETDENTS64
-#define __ARCH_WANT_IPC_PARSE_VERSION
 #define __ARCH_WANT_OLD_READDIR
 #define __ARCH_WANT_SYS_ALARM
 #define __ARCH_WANT_SYS_GETHOSTNAME
index 9a91fe9de69675ff1d74140bc658257637356090..9a3d9de4d04ef219faee3f832027c1416e92e61c 100644 (file)
@@ -140,6 +140,7 @@ static void qi_lb60_nand_ident(struct platform_device *pdev,
 static struct jz_nand_platform_data qi_lb60_nand_pdata = {
        .ident_callback = qi_lb60_nand_ident,
        .busy_gpio = 94,
+       .banks = { 1 },
 };
 
 /* Keyboard*/
index 10929e2bc6d82a77714243175073f8ef31a60fcf..e342ed4cbd4378c84749ae7e77406a131cec7c1c 100644 (file)
@@ -157,11 +157,29 @@ static struct resource jz4740_nand_resources[] = {
                .flags  = IORESOURCE_MEM,
        },
        {
-               .name   = "bank",
+               .name   = "bank1",
                .start  = 0x18000000,
                .end    = 0x180C0000 - 1,
                .flags = IORESOURCE_MEM,
        },
+       {
+               .name   = "bank2",
+               .start  = 0x14000000,
+               .end    = 0x140C0000 - 1,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .name   = "bank3",
+               .start  = 0x0C000000,
+               .end    = 0x0C0C0000 - 1,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .name   = "bank4",
+               .start  = 0x08000000,
+               .end    = 0x080C0000 - 1,
+               .flags = IORESOURCE_MEM,
+       },
 };
 
 struct platform_device jz4740_nand_device = {
index 5f1fb95c0d0daf980bc20194be8bb4c6f35f002d..6c0da5afcf17ef1fdcd97d70f83dff183fcd57f3 100644 (file)
@@ -21,6 +21,9 @@
 #include <asm/mach-jz4740/base.h>
 #include <asm/mach-jz4740/timer.h>
 
+#include "reset.h"
+#include "clock.h"
+
 static void jz4740_halt(void)
 {
        while (1) {
@@ -53,21 +56,57 @@ static void jz4740_restart(char *command)
        jz4740_halt();
 }
 
-#define JZ_REG_RTC_CTRL                0x00
-#define JZ_REG_RTC_HIBERNATE   0x20
+#define JZ_REG_RTC_CTRL                        0x00
+#define JZ_REG_RTC_HIBERNATE           0x20
+#define JZ_REG_RTC_WAKEUP_FILTER       0x24
+#define JZ_REG_RTC_RESET_COUNTER       0x28
 
-#define JZ_RTC_CTRL_WRDY       BIT(7)
+#define JZ_RTC_CTRL_WRDY               BIT(7)
+#define JZ_RTC_WAKEUP_FILTER_MASK      0x0000FFE0
+#define JZ_RTC_RESET_COUNTER_MASK      0x00000FE0
 
-static void jz4740_power_off(void)
+static inline void jz4740_rtc_wait_ready(void __iomem *rtc_base)
 {
-       void __iomem *rtc_base = ioremap(JZ4740_RTC_BASE_ADDR, 0x24);
        uint32_t ctrl;
 
        do {
                ctrl = readl(rtc_base + JZ_REG_RTC_CTRL);
        } while (!(ctrl & JZ_RTC_CTRL_WRDY));
+}
 
+static void jz4740_power_off(void)
+{
+       void __iomem *rtc_base = ioremap(JZ4740_RTC_BASE_ADDR, 0x38);
+       unsigned long wakeup_filter_ticks;
+       unsigned long reset_counter_ticks;
+
+       /*
+        * Set minimum wakeup pin assertion time: 100 ms.
+        * Range is 0 to 2 sec if RTC is clocked at 32 kHz.
+        */
+       wakeup_filter_ticks = (100 * jz4740_clock_bdata.rtc_rate) / 1000;
+       if (wakeup_filter_ticks < JZ_RTC_WAKEUP_FILTER_MASK)
+               wakeup_filter_ticks &= JZ_RTC_WAKEUP_FILTER_MASK;
+       else
+               wakeup_filter_ticks = JZ_RTC_WAKEUP_FILTER_MASK;
+       jz4740_rtc_wait_ready(rtc_base);
+       writel(wakeup_filter_ticks, rtc_base + JZ_REG_RTC_WAKEUP_FILTER);
+
+       /*
+        * Set reset pin low-level assertion time after wakeup: 60 ms.
+        * Range is 0 to 125 ms if RTC is clocked at 32 kHz.
+        */
+       reset_counter_ticks = (60 * jz4740_clock_bdata.rtc_rate) / 1000;
+       if (reset_counter_ticks < JZ_RTC_RESET_COUNTER_MASK)
+               reset_counter_ticks &= JZ_RTC_RESET_COUNTER_MASK;
+       else
+               reset_counter_ticks = JZ_RTC_RESET_COUNTER_MASK;
+       jz4740_rtc_wait_ready(rtc_base);
+       writel(reset_counter_ticks, rtc_base + JZ_REG_RTC_RESET_COUNTER);
+
+       jz4740_rtc_wait_ready(rtc_base);
        writel(1, rtc_base + JZ_REG_RTC_HIBERNATE);
+
        jz4740_halt();
 }
 
index f4630e1082ab676a96e052e504995e86716be012..1b51046191e85a77dfb56aa284b96e66b035b083 100644 (file)
@@ -190,6 +190,7 @@ void __init check_wait(void)
        case CPU_CAVIUM_OCTEON_PLUS:
        case CPU_CAVIUM_OCTEON2:
        case CPU_JZRISC:
+       case CPU_LOONGSON1:
        case CPU_XLR:
        case CPU_XLP:
                cpu_wait = r4k_wait;
@@ -330,6 +331,154 @@ static inline void cpu_probe_vmbits(struct cpuinfo_mips *c)
 #endif
 }
 
+static char unknown_isa[] __cpuinitdata = KERN_ERR \
+       "Unsupported ISA type, c0.config0: %d.";
+
+static inline unsigned int decode_config0(struct cpuinfo_mips *c)
+{
+       unsigned int config0;
+       int isa;
+
+       config0 = read_c0_config();
+
+       if (((config0 & MIPS_CONF_MT) >> 7) == 1)
+               c->options |= MIPS_CPU_TLB;
+       isa = (config0 & MIPS_CONF_AT) >> 13;
+       switch (isa) {
+       case 0:
+               switch ((config0 & MIPS_CONF_AR) >> 10) {
+               case 0:
+                       c->isa_level = MIPS_CPU_ISA_M32R1;
+                       break;
+               case 1:
+                       c->isa_level = MIPS_CPU_ISA_M32R2;
+                       break;
+               default:
+                       goto unknown;
+               }
+               break;
+       case 2:
+               switch ((config0 & MIPS_CONF_AR) >> 10) {
+               case 0:
+                       c->isa_level = MIPS_CPU_ISA_M64R1;
+                       break;
+               case 1:
+                       c->isa_level = MIPS_CPU_ISA_M64R2;
+                       break;
+               default:
+                       goto unknown;
+               }
+               break;
+       default:
+               goto unknown;
+       }
+
+       return config0 & MIPS_CONF_M;
+
+unknown:
+       panic(unknown_isa, config0);
+}
+
+static inline unsigned int decode_config1(struct cpuinfo_mips *c)
+{
+       unsigned int config1;
+
+       config1 = read_c0_config1();
+
+       if (config1 & MIPS_CONF1_MD)
+               c->ases |= MIPS_ASE_MDMX;
+       if (config1 & MIPS_CONF1_WR)
+               c->options |= MIPS_CPU_WATCH;
+       if (config1 & MIPS_CONF1_CA)
+               c->ases |= MIPS_ASE_MIPS16;
+       if (config1 & MIPS_CONF1_EP)
+               c->options |= MIPS_CPU_EJTAG;
+       if (config1 & MIPS_CONF1_FP) {
+               c->options |= MIPS_CPU_FPU;
+               c->options |= MIPS_CPU_32FPR;
+       }
+       if (cpu_has_tlb)
+               c->tlbsize = ((config1 & MIPS_CONF1_TLBS) >> 25) + 1;
+
+       return config1 & MIPS_CONF_M;
+}
+
+static inline unsigned int decode_config2(struct cpuinfo_mips *c)
+{
+       unsigned int config2;
+
+       config2 = read_c0_config2();
+
+       if (config2 & MIPS_CONF2_SL)
+               c->scache.flags &= ~MIPS_CACHE_NOT_PRESENT;
+
+       return config2 & MIPS_CONF_M;
+}
+
+static inline unsigned int decode_config3(struct cpuinfo_mips *c)
+{
+       unsigned int config3;
+
+       config3 = read_c0_config3();
+
+       if (config3 & MIPS_CONF3_SM)
+               c->ases |= MIPS_ASE_SMARTMIPS;
+       if (config3 & MIPS_CONF3_DSP)
+               c->ases |= MIPS_ASE_DSP;
+       if (config3 & MIPS_CONF3_VINT)
+               c->options |= MIPS_CPU_VINT;
+       if (config3 & MIPS_CONF3_VEIC)
+               c->options |= MIPS_CPU_VEIC;
+       if (config3 & MIPS_CONF3_MT)
+               c->ases |= MIPS_ASE_MIPSMT;
+       if (config3 & MIPS_CONF3_ULRI)
+               c->options |= MIPS_CPU_ULRI;
+
+       return config3 & MIPS_CONF_M;
+}
+
+static inline unsigned int decode_config4(struct cpuinfo_mips *c)
+{
+       unsigned int config4;
+
+       config4 = read_c0_config4();
+
+       if ((config4 & MIPS_CONF4_MMUEXTDEF) == MIPS_CONF4_MMUEXTDEF_MMUSIZEEXT
+           && cpu_has_tlb)
+               c->tlbsize += (config4 & MIPS_CONF4_MMUSIZEEXT) * 0x40;
+
+       c->kscratch_mask = (config4 >> 16) & 0xff;
+
+       return config4 & MIPS_CONF_M;
+}
+
+static void __cpuinit decode_configs(struct cpuinfo_mips *c)
+{
+       int ok;
+
+       /* MIPS32 or MIPS64 compliant CPU.  */
+       c->options = MIPS_CPU_4KEX | MIPS_CPU_4K_CACHE | MIPS_CPU_COUNTER |
+                    MIPS_CPU_DIVEC | MIPS_CPU_LLSC | MIPS_CPU_MCHECK;
+
+       c->scache.flags = MIPS_CACHE_NOT_PRESENT;
+
+       ok = decode_config0(c);                 /* Read Config registers.  */
+       BUG_ON(!ok);                            /* Arch spec violation!  */
+       if (ok)
+               ok = decode_config1(c);
+       if (ok)
+               ok = decode_config2(c);
+       if (ok)
+               ok = decode_config3(c);
+       if (ok)
+               ok = decode_config4(c);
+
+       mips_probe_watch_registers(c);
+
+       if (cpu_has_mips_r2)
+               c->core = read_c0_ebase() & 0x3ff;
+}
+
 #define R4K_OPTS (MIPS_CPU_TLB | MIPS_CPU_4KEX | MIPS_CPU_4K_CACHE \
                | MIPS_CPU_COUNTER)
 
@@ -638,155 +787,19 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                             MIPS_CPU_32FPR;
                c->tlbsize = 64;
                break;
-       }
-}
-
-static char unknown_isa[] __cpuinitdata = KERN_ERR \
-       "Unsupported ISA type, c0.config0: %d.";
+       case PRID_IMP_LOONGSON1:
+               decode_configs(c);
 
-static inline unsigned int decode_config0(struct cpuinfo_mips *c)
-{
-       unsigned int config0;
-       int isa;
+               c->cputype = CPU_LOONGSON1;
 
-       config0 = read_c0_config();
-
-       if (((config0 & MIPS_CONF_MT) >> 7) == 1)
-               c->options |= MIPS_CPU_TLB;
-       isa = (config0 & MIPS_CONF_AT) >> 13;
-       switch (isa) {
-       case 0:
-               switch ((config0 & MIPS_CONF_AR) >> 10) {
-               case 0:
-                       c->isa_level = MIPS_CPU_ISA_M32R1;
-                       break;
-               case 1:
-                       c->isa_level = MIPS_CPU_ISA_M32R2;
+               switch (c->processor_id & PRID_REV_MASK) {
+               case PRID_REV_LOONGSON1B:
+                       __cpu_name[cpu] = "Loongson 1B";
                        break;
-               default:
-                       goto unknown;
                }
-               break;
-       case 2:
-               switch ((config0 & MIPS_CONF_AR) >> 10) {
-               case 0:
-                       c->isa_level = MIPS_CPU_ISA_M64R1;
-                       break;
-               case 1:
-                       c->isa_level = MIPS_CPU_ISA_M64R2;
-                       break;
-               default:
-                       goto unknown;
-               }
-               break;
-       default:
-               goto unknown;
-       }
-
-       return config0 & MIPS_CONF_M;
-
-unknown:
-       panic(unknown_isa, config0);
-}
 
-static inline unsigned int decode_config1(struct cpuinfo_mips *c)
-{
-       unsigned int config1;
-
-       config1 = read_c0_config1();
-
-       if (config1 & MIPS_CONF1_MD)
-               c->ases |= MIPS_ASE_MDMX;
-       if (config1 & MIPS_CONF1_WR)
-               c->options |= MIPS_CPU_WATCH;
-       if (config1 & MIPS_CONF1_CA)
-               c->ases |= MIPS_ASE_MIPS16;
-       if (config1 & MIPS_CONF1_EP)
-               c->options |= MIPS_CPU_EJTAG;
-       if (config1 & MIPS_CONF1_FP) {
-               c->options |= MIPS_CPU_FPU;
-               c->options |= MIPS_CPU_32FPR;
+               break;
        }
-       if (cpu_has_tlb)
-               c->tlbsize = ((config1 & MIPS_CONF1_TLBS) >> 25) + 1;
-
-       return config1 & MIPS_CONF_M;
-}
-
-static inline unsigned int decode_config2(struct cpuinfo_mips *c)
-{
-       unsigned int config2;
-
-       config2 = read_c0_config2();
-
-       if (config2 & MIPS_CONF2_SL)
-               c->scache.flags &= ~MIPS_CACHE_NOT_PRESENT;
-
-       return config2 & MIPS_CONF_M;
-}
-
-static inline unsigned int decode_config3(struct cpuinfo_mips *c)
-{
-       unsigned int config3;
-
-       config3 = read_c0_config3();
-
-       if (config3 & MIPS_CONF3_SM)
-               c->ases |= MIPS_ASE_SMARTMIPS;
-       if (config3 & MIPS_CONF3_DSP)
-               c->ases |= MIPS_ASE_DSP;
-       if (config3 & MIPS_CONF3_VINT)
-               c->options |= MIPS_CPU_VINT;
-       if (config3 & MIPS_CONF3_VEIC)
-               c->options |= MIPS_CPU_VEIC;
-       if (config3 & MIPS_CONF3_MT)
-               c->ases |= MIPS_ASE_MIPSMT;
-       if (config3 & MIPS_CONF3_ULRI)
-               c->options |= MIPS_CPU_ULRI;
-
-       return config3 & MIPS_CONF_M;
-}
-
-static inline unsigned int decode_config4(struct cpuinfo_mips *c)
-{
-       unsigned int config4;
-
-       config4 = read_c0_config4();
-
-       if ((config4 & MIPS_CONF4_MMUEXTDEF) == MIPS_CONF4_MMUEXTDEF_MMUSIZEEXT
-           && cpu_has_tlb)
-               c->tlbsize += (config4 & MIPS_CONF4_MMUSIZEEXT) * 0x40;
-
-       c->kscratch_mask = (config4 >> 16) & 0xff;
-
-       return config4 & MIPS_CONF_M;
-}
-
-static void __cpuinit decode_configs(struct cpuinfo_mips *c)
-{
-       int ok;
-
-       /* MIPS32 or MIPS64 compliant CPU.  */
-       c->options = MIPS_CPU_4KEX | MIPS_CPU_4K_CACHE | MIPS_CPU_COUNTER |
-                    MIPS_CPU_DIVEC | MIPS_CPU_LLSC | MIPS_CPU_MCHECK;
-
-       c->scache.flags = MIPS_CACHE_NOT_PRESENT;
-
-       ok = decode_config0(c);                 /* Read Config registers.  */
-       BUG_ON(!ok);                            /* Arch spec violation!  */
-       if (ok)
-               ok = decode_config1(c);
-       if (ok)
-               ok = decode_config2(c);
-       if (ok)
-               ok = decode_config3(c);
-       if (ok)
-               ok = decode_config4(c);
-
-       mips_probe_watch_registers(c);
-
-       if (cpu_has_mips_r2)
-               c->core = read_c0_ebase() & 0x3ff;
 }
 
 static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu)
index c3479a432efe9e4dc1f86dff6cca5e020e7991c9..05a5715ee38c5b3e2f8d44cbfd3cced73a549de5 100644 (file)
@@ -2,4 +2,4 @@
 # Makefile for the Linux/MIPS cpufreq.
 #
 
-obj-$(CONFIG_LOONGSON2_CPUFREQ) += loongson2_cpufreq.o loongson2_clock.o
+obj-$(CONFIG_LOONGSON2_CPUFREQ) += loongson2_cpufreq.o
diff --git a/arch/mips/kernel/cpufreq/loongson2_clock.c b/arch/mips/kernel/cpufreq/loongson2_clock.c
deleted file mode 100644 (file)
index 5426779..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2006 - 2008 Lemote Inc. & Insititute of Computing Technology
- * Author: Yanhua, yanh@lemote.com
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#include <linux/module.h>
-#include <linux/cpufreq.h>
-#include <linux/platform_device.h>
-
-#include <asm/clock.h>
-
-#include <loongson.h>
-
-static LIST_HEAD(clock_list);
-static DEFINE_SPINLOCK(clock_lock);
-static DEFINE_MUTEX(clock_list_sem);
-
-/* Minimum CLK support */
-enum {
-       DC_ZERO, DC_25PT = 2, DC_37PT, DC_50PT, DC_62PT, DC_75PT,
-       DC_87PT, DC_DISABLE, DC_RESV
-};
-
-struct cpufreq_frequency_table loongson2_clockmod_table[] = {
-       {DC_RESV, CPUFREQ_ENTRY_INVALID},
-       {DC_ZERO, CPUFREQ_ENTRY_INVALID},
-       {DC_25PT, 0},
-       {DC_37PT, 0},
-       {DC_50PT, 0},
-       {DC_62PT, 0},
-       {DC_75PT, 0},
-       {DC_87PT, 0},
-       {DC_DISABLE, 0},
-       {DC_RESV, CPUFREQ_TABLE_END},
-};
-EXPORT_SYMBOL_GPL(loongson2_clockmod_table);
-
-static struct clk cpu_clk = {
-       .name = "cpu_clk",
-       .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
-       .rate = 800000000,
-};
-
-struct clk *clk_get(struct device *dev, const char *id)
-{
-       return &cpu_clk;
-}
-EXPORT_SYMBOL(clk_get);
-
-static void propagate_rate(struct clk *clk)
-{
-       struct clk *clkp;
-
-       list_for_each_entry(clkp, &clock_list, node) {
-               if (likely(clkp->parent != clk))
-                       continue;
-               if (likely(clkp->ops && clkp->ops->recalc))
-                       clkp->ops->recalc(clkp);
-               if (unlikely(clkp->flags & CLK_RATE_PROPAGATES))
-                       propagate_rate(clkp);
-       }
-}
-
-int clk_enable(struct clk *clk)
-{
-       return 0;
-}
-EXPORT_SYMBOL(clk_enable);
-
-void clk_disable(struct clk *clk)
-{
-}
-EXPORT_SYMBOL(clk_disable);
-
-unsigned long clk_get_rate(struct clk *clk)
-{
-       return (unsigned long)clk->rate;
-}
-EXPORT_SYMBOL(clk_get_rate);
-
-void clk_put(struct clk *clk)
-{
-}
-EXPORT_SYMBOL(clk_put);
-
-int clk_set_rate(struct clk *clk, unsigned long rate)
-{
-       return clk_set_rate_ex(clk, rate, 0);
-}
-EXPORT_SYMBOL_GPL(clk_set_rate);
-
-int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id)
-{
-       int ret = 0;
-       int regval;
-       int i;
-
-       if (likely(clk->ops && clk->ops->set_rate)) {
-               unsigned long flags;
-
-               spin_lock_irqsave(&clock_lock, flags);
-               ret = clk->ops->set_rate(clk, rate, algo_id);
-               spin_unlock_irqrestore(&clock_lock, flags);
-       }
-
-       if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
-               propagate_rate(clk);
-
-       for (i = 0; loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END;
-            i++) {
-               if (loongson2_clockmod_table[i].frequency ==
-                   CPUFREQ_ENTRY_INVALID)
-                       continue;
-               if (rate == loongson2_clockmod_table[i].frequency)
-                       break;
-       }
-       if (rate != loongson2_clockmod_table[i].frequency)
-               return -ENOTSUPP;
-
-       clk->rate = rate;
-
-       regval = LOONGSON_CHIPCFG0;
-       regval = (regval & ~0x7) | (loongson2_clockmod_table[i].index - 1);
-       LOONGSON_CHIPCFG0 = regval;
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(clk_set_rate_ex);
-
-long clk_round_rate(struct clk *clk, unsigned long rate)
-{
-       if (likely(clk->ops && clk->ops->round_rate)) {
-               unsigned long flags, rounded;
-
-               spin_lock_irqsave(&clock_lock, flags);
-               rounded = clk->ops->round_rate(clk, rate);
-               spin_unlock_irqrestore(&clock_lock, flags);
-
-               return rounded;
-       }
-
-       return rate;
-}
-EXPORT_SYMBOL_GPL(clk_round_rate);
-
-/*
- * This is the simple version of Loongson-2 wait, Maybe we need do this in
- * interrupt disabled content
- */
-
-DEFINE_SPINLOCK(loongson2_wait_lock);
-void loongson2_cpu_wait(void)
-{
-       u32 cpu_freq;
-       unsigned long flags;
-
-       spin_lock_irqsave(&loongson2_wait_lock, flags);
-       cpu_freq = LOONGSON_CHIPCFG0;
-       LOONGSON_CHIPCFG0 &= ~0x7;      /* Put CPU into wait mode */
-       LOONGSON_CHIPCFG0 = cpu_freq;   /* Restore CPU state */
-       spin_unlock_irqrestore(&loongson2_wait_lock, flags);
-}
-EXPORT_SYMBOL_GPL(loongson2_cpu_wait);
-
-MODULE_AUTHOR("Yanhua <yanh@lemote.com>");
-MODULE_DESCRIPTION("cpufreq driver for Loongson 2F");
-MODULE_LICENSE("GPL");
index ae5db206347c7fdaf59e9618ba0ee4bc0f0017a8..e7c98e2b78b647bfb312b8977bcc9b140beb1e77 100644 (file)
@@ -19,7 +19,7 @@
 
 #include <asm/clock.h>
 
-#include <loongson.h>
+#include <asm/mach-loongson/loongson.h>
 
 static uint nowait;
 
@@ -181,6 +181,25 @@ static struct platform_driver platform_driver = {
        .id_table = platform_device_ids,
 };
 
+/*
+ * This is the simple version of Loongson-2 wait, Maybe we need do this in
+ * interrupt disabled context.
+ */
+
+static DEFINE_SPINLOCK(loongson2_wait_lock);
+
+static void loongson2_cpu_wait(void)
+{
+       unsigned long flags;
+       u32 cpu_freq;
+
+       spin_lock_irqsave(&loongson2_wait_lock, flags);
+       cpu_freq = LOONGSON_CHIPCFG0;
+       LOONGSON_CHIPCFG0 &= ~0x7;      /* Put CPU into wait mode */
+       LOONGSON_CHIPCFG0 = cpu_freq;   /* Restore CPU state */
+       spin_unlock_irqrestore(&loongson2_wait_lock, flags);
+}
+
 static int __init cpufreq_init(void)
 {
        int ret;
index eb5e394a4650bbe9f0814e51bc00e1b8523c57f1..2f28d3b55687eac42469051c34cb9a6749b21473 100644 (file)
@@ -1559,6 +1559,11 @@ init_hw_perf_events(void)
                mipspmu.general_event_map = &mipsxxcore_event_map;
                mipspmu.cache_event_map = &mipsxxcore_cache_map;
                break;
+       case CPU_LOONGSON1:
+               mipspmu.name = "mips/loongson1";
+               mipspmu.general_event_map = &mipsxxcore_event_map;
+               mipspmu.cache_event_map = &mipsxxcore_cache_map;
+               break;
        case CPU_CAVIUM_OCTEON:
        case CPU_CAVIUM_OCTEON_PLUS:
        case CPU_CAVIUM_OCTEON2:
index f11b2bbb826d223d10eefffb810c9bcdb67eb017..028f6f837ef9c975697cd763fa685126a5be6b48 100644 (file)
@@ -35,16 +35,6 @@ void __init early_init_dt_add_memory_arch(u64 base, u64 size)
        return add_memory_region(base, size, BOOT_MEM_RAM);
 }
 
-int __init reserve_mem_mach(unsigned long addr, unsigned long size)
-{
-       return reserve_bootmem(addr, size, BOOTMEM_DEFAULT);
-}
-
-void __init free_mem_mach(unsigned long addr, unsigned long size)
-{
-       return free_bootmem(addr, size);
-}
-
 void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
 {
        return __alloc_bootmem(size, align, __pa(MAX_DMA_ADDRESS));
@@ -77,25 +67,6 @@ void __init early_init_devtree(void *params)
        of_scan_flat_dt(early_init_dt_scan_memory_arch, NULL);
 }
 
-void __init device_tree_init(void)
-{
-       unsigned long base, size;
-
-       if (!initial_boot_params)
-               return;
-
-       base = virt_to_phys((void *)initial_boot_params);
-       size = be32_to_cpu(initial_boot_params->totalsize);
-
-       /* Before we do anything, lets reserve the dt blob */
-       reserve_mem_mach(base, size);
-
-       unflatten_device_tree();
-
-       /* free the space reserved for the dt blob */
-       free_mem_mach(base, size);
-}
-
 void __init __dt_setup_arch(struct boot_param_header *bph)
 {
        if (be32_to_cpu(bph->magic) != OF_DT_HEADER) {
index 1268392f1d2786bc3abb91fdb9a88ad84fa85ec9..31637d8c87381f04b02bcae714e8b2165952fc9c 100644 (file)
@@ -102,7 +102,9 @@ asmlinkage __cpuinit void start_secondary(void)
 
 #ifdef CONFIG_MIPS_MT_SMTC
        /* Only do cpu_probe for first TC of CPU */
-       if ((read_c0_tcbind() & TCBIND_CURTC) == 0)
+       if ((read_c0_tcbind() & TCBIND_CURTC) != 0)
+               __cpu_name[smp_processor_id()] = __cpu_name[0];
+       else
 #endif /* CONFIG_MIPS_MT_SMTC */
        cpu_probe();
        cpu_report();
index 15b5f3cfd20c48b9424dd5364d29cf1aeff77d4a..1d47843d3cc0211d3259930ef48596316afc2cfc 100644 (file)
@@ -86,6 +86,13 @@ struct smtc_ipi_q IPIQ[NR_CPUS];
 static struct smtc_ipi_q freeIPIq;
 
 
+/*
+ * Number of FPU contexts for each VPE
+ */
+
+static int smtc_nconf1[MAX_SMTC_VPES];
+
+
 /* Forward declarations */
 
 void ipi_decode(struct smtc_ipi *);
@@ -174,9 +181,9 @@ static int __init tintq(char *str)
 
 __setup("tintq=", tintq);
 
-static int imstuckcount[2][8];
+static int imstuckcount[MAX_SMTC_VPES][8];
 /* vpemask represents IM/IE bits of per-VPE Status registers, low-to-high */
-static int vpemask[2][8] = {
+static int vpemask[MAX_SMTC_VPES][8] = {
        {0, 0, 1, 0, 0, 0, 0, 1},
        {0, 0, 0, 0, 0, 0, 0, 1}
 };
@@ -331,6 +338,22 @@ int __init smtc_build_cpu_map(int start_cpu_slot)
 
 static void smtc_tc_setup(int vpe, int tc, int cpu)
 {
+       static int cp1contexts[MAX_SMTC_VPES];
+
+       /*
+        * Make a local copy of the available FPU contexts in order
+        * to keep track of TCs that can have one.
+        */
+       if (tc == 1)
+       {
+               /*
+                * FIXME: Multi-core SMTC hasn't been tested and the
+                *        maximum number of VPEs may change.
+                */
+               cp1contexts[0] = smtc_nconf1[0] - 1;
+               cp1contexts[1] = smtc_nconf1[1];
+       }
+
        settc(tc);
        write_tc_c0_tchalt(TCHALT_H);
        mips_ihb();
@@ -343,22 +366,29 @@ static void smtc_tc_setup(int vpe, int tc, int cpu)
         * an active IPI queue.
         */
        write_tc_c0_tccontext((sizeof(struct smtc_ipi_q) * cpu) << 16);
-       /* Bind tc to vpe */
+
+       /* Bind TC to VPE. */
        write_tc_c0_tcbind(vpe);
+
        /* In general, all TCs should have the same cpu_data indications. */
        memcpy(&cpu_data[cpu], &cpu_data[0], sizeof(struct cpuinfo_mips));
-       /* For 34Kf, start with TC/CPU 0 as sole owner of single FPU context */
-       if (cpu_data[0].cputype == CPU_34K ||
-           cpu_data[0].cputype == CPU_1004K)
+
+       /* Check to see if there is a FPU context available for this TC. */
+       if (!cp1contexts[vpe])
                cpu_data[cpu].options &= ~MIPS_CPU_FPU;
+       else
+               cp1contexts[vpe]--;
+
+       /* Store the TC and VPE into the cpu_data structure. */
        cpu_data[cpu].vpe_id = vpe;
        cpu_data[cpu].tc_id = tc;
-       /* Multi-core SMTC hasn't been tested, but be prepared */
+
+       /* FIXME: Multi-core SMTC hasn't been tested, but be prepared. */
        cpu_data[cpu].core = (read_vpe_c0_ebase() >> 1) & 0xff;
 }
 
 /*
- * Tweak to get Count registes in as close a sync as possible.  The
+ * Tweak to get Count registers synced as closely as possible. The
  * value seems good for 34K-class cores.
  */
 
@@ -466,6 +496,24 @@ void smtc_prepare_cpus(int cpus)
        smtc_configure_tlb();
 
        for (tc = 0, vpe = 0 ; (vpe < nvpe) && (tc < ntc) ; vpe++) {
+               /* Get number of CP1 contexts for each VPE. */
+               if (tc == 0)
+               {
+                       /*
+                        * Do not call settc() for TC0 or the FPU context
+                        * value will be incorrect. Besides, we know that
+                        * we are TC0 anyway.
+                        */
+                       smtc_nconf1[0] = ((read_vpe_c0_vpeconf1() &
+                               VPECONF1_NCP1) >> VPECONF1_NCP1_SHIFT);
+                       if (nvpe == 2)
+                       {
+                               settc(1);
+                               smtc_nconf1[1] = ((read_vpe_c0_vpeconf1() &
+                                       VPECONF1_NCP1) >> VPECONF1_NCP1_SHIFT);
+                               settc(0);
+                       }
+               }
                if (tcpervpe[vpe] == 0)
                        continue;
                if (vpe != 0)
@@ -479,6 +527,18 @@ void smtc_prepare_cpus(int cpus)
                         */
                        if (tc != 0) {
                                smtc_tc_setup(vpe, tc, cpu);
+                               if (vpe != 0) {
+                                       /*
+                                        * Set MVP bit (possibly again).  Do it
+                                        * here to catch CPUs that have no TCs
+                                        * bound to the VPE at reset.  In that
+                                        * case, a TC must be bound to the VPE
+                                        * before we can set VPEControl[MVP]
+                                        */
+                                       write_vpe_c0_vpeconf0(
+                                               read_vpe_c0_vpeconf0() |
+                                               VPECONF0_MVP);
+                               }
                                cpu++;
                        }
                        printk(" %d", tc);
index c3c29354370345ae74c25dce1e4e7e7da9263316..9be3df1fa8a461dc4e1e5563582f483e4ed7c103 100644 (file)
@@ -1253,6 +1253,7 @@ static inline void parity_protection_init(void)
 
        case CPU_5KC:
        case CPU_5KE:
+       case CPU_LOONGSON1:
                write_c0_ecc(0x80000000);
                back_to_back_c0_hazard();
                /* Set the PE bit (bit 31) in the c0_errctl register. */
index d3bcc33f4699ae7dcf8036bf70f0a6be19293d70..ce2f129b081f8886d64968350a77b6f4f51bf45e 100644 (file)
@@ -135,6 +135,11 @@ void clk_deactivate(struct clk *clk)
 }
 EXPORT_SYMBOL(clk_deactivate);
 
+struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
+{
+       return NULL;
+}
+
 static inline u32 get_counter_resolution(void)
 {
        u32 res;
index d185e8477fdf2a8eb38d73ff74ed2c94acb894e8..6cfd6117fbfdc26f13e403d0241e45a5357aeaf2 100644 (file)
@@ -8,7 +8,10 @@
 
 #include <linux/export.h>
 #include <linux/clk.h>
+#include <linux/bootmem.h>
 #include <linux/of_platform.h>
+#include <linux/of_fdt.h>
+
 #include <asm/bootinfo.h>
 #include <asm/time.h>
 
@@ -70,6 +73,25 @@ void __init plat_mem_setup(void)
        __dt_setup_arch(&__dtb_start);
 }
 
+void __init device_tree_init(void)
+{
+       unsigned long base, size;
+
+       if (!initial_boot_params)
+               return;
+
+       base = virt_to_phys((void *)initial_boot_params);
+       size = be32_to_cpu(initial_boot_params->totalsize);
+
+       /* Before we do anything, lets reserve the dt blob */
+       reserve_bootmem(base, size, BOOTMEM_DEFAULT);
+
+       unflatten_device_tree();
+
+       /* free the space reserved for the dt blob */
+       free_bootmem(base, size);
+}
+
 void __init prom_init(void)
 {
        /* call the soc specific detetcion code and get it to fill soc_info */
index 83780f7c842b8c81d0107c31d5f627ed4dd4791b..befbb760ab766aee198df2dfd2ff66d0bd1832d3 100644 (file)
 
 /* clock control register */
 #define CGU_IFCCR      0x0018
+#define CGU_IFCCR_VR9  0x0024
 /* system clock register */
 #define CGU_SYS                0x0010
 /* pci control register */
 #define CGU_PCICR      0x0034
+#define CGU_PCICR_VR9  0x0038
 /* ephy configuration register */
 #define CGU_EPHY       0x10
 /* power control register */
@@ -80,6 +82,9 @@ static void __iomem *pmu_membase;
 void __iomem *ltq_cgu_membase;
 void __iomem *ltq_ebu_membase;
 
+static u32 ifccr = CGU_IFCCR;
+static u32 pcicr = CGU_PCICR;
+
 /* legacy function kept alive to ease clkdev transition */
 void ltq_pmu_enable(unsigned int module)
 {
@@ -103,14 +108,14 @@ EXPORT_SYMBOL(ltq_pmu_disable);
 /* enable a hw clock */
 static int cgu_enable(struct clk *clk)
 {
-       ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) | clk->bits, CGU_IFCCR);
+       ltq_cgu_w32(ltq_cgu_r32(ifccr) | clk->bits, ifccr);
        return 0;
 }
 
 /* disable a hw clock */
 static void cgu_disable(struct clk *clk)
 {
-       ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) & ~clk->bits, CGU_IFCCR);
+       ltq_cgu_w32(ltq_cgu_r32(ifccr) & ~clk->bits, ifccr);
 }
 
 /* enable a clock gate */
@@ -138,22 +143,22 @@ static void pmu_disable(struct clk *clk)
 /* the pci enable helper */
 static int pci_enable(struct clk *clk)
 {
-       unsigned int ifccr = ltq_cgu_r32(CGU_IFCCR);
+       unsigned int val = ltq_cgu_r32(ifccr);
        /* set bus clock speed */
        if (of_machine_is_compatible("lantiq,ar9")) {
-               ifccr &= ~0x1f00000;
+               val &= ~0x1f00000;
                if (clk->rate == CLOCK_33M)
-                       ifccr |= 0xe00000;
+                       val |= 0xe00000;
                else
-                       ifccr |= 0x700000; /* 62.5M */
+                       val |= 0x700000; /* 62.5M */
        } else {
-               ifccr &= ~0xf00000;
+               val &= ~0xf00000;
                if (clk->rate == CLOCK_33M)
-                       ifccr |= 0x800000;
+                       val |= 0x800000;
                else
-                       ifccr |= 0x400000; /* 62.5M */
+                       val |= 0x400000; /* 62.5M */
        }
-       ltq_cgu_w32(ifccr, CGU_IFCCR);
+       ltq_cgu_w32(val, ifccr);
        pmu_enable(clk);
        return 0;
 }
@@ -161,18 +166,16 @@ static int pci_enable(struct clk *clk)
 /* enable the external clock as a source */
 static int pci_ext_enable(struct clk *clk)
 {
-       ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) & ~(1 << 16),
-               CGU_IFCCR);
-       ltq_cgu_w32((1 << 30), CGU_PCICR);
+       ltq_cgu_w32(ltq_cgu_r32(ifccr) & ~(1 << 16), ifccr);
+       ltq_cgu_w32((1 << 30), pcicr);
        return 0;
 }
 
 /* disable the external clock as a source */
 static void pci_ext_disable(struct clk *clk)
 {
-       ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) | (1 << 16),
-               CGU_IFCCR);
-       ltq_cgu_w32((1 << 31) | (1 << 30), CGU_PCICR);
+       ltq_cgu_w32(ltq_cgu_r32(ifccr) | (1 << 16), ifccr);
+       ltq_cgu_w32((1 << 31) | (1 << 30), pcicr);
 }
 
 /* enable a clockout source */
@@ -184,11 +187,11 @@ static int clkout_enable(struct clk *clk)
        for (i = 0; i < 4; i++) {
                if (clk->rates[i] == clk->rate) {
                        int shift = 14 - (2 * clk->module);
-                       unsigned int ifccr = ltq_cgu_r32(CGU_IFCCR);
+                       unsigned int val = ltq_cgu_r32(ifccr);
 
-                       ifccr &= ~(3 << shift);
-                       ifccr |= i << shift;
-                       ltq_cgu_w32(ifccr, CGU_IFCCR);
+                       val &= ~(3 << shift);
+                       val |= i << shift;
+                       ltq_cgu_w32(val, ifccr);
                        return 0;
                }
        }
@@ -336,8 +339,12 @@ void __init ltq_soc_init(void)
        clkdev_add_clkout();
 
        /* add the soc dependent clocks */
-       if (!of_machine_is_compatible("lantiq,vr9"))
+       if (of_machine_is_compatible("lantiq,vr9")) {
+               ifccr = CGU_IFCCR_VR9;
+               pcicr = CGU_PCICR_VR9;
+       } else {
                clkdev_add_pmu("1e180000.etop", NULL, 0, PMU_PPE);
+       }
 
        if (!of_machine_is_compatible("lantiq,ase")) {
                clkdev_add_pmu("1e100c00.serial", NULL, 0, PMU_ASC1);
index 2a7c74fc15fcef917d183201d9f6eac9046fcbf7..399a50a541d4471a9e68bd4b0f18b00d512d5e67 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for MIPS-specific library files..
 #
 
-lib-y  += csum_partial.o delay.o memcpy.o memcpy-inatomic.o memset.o \
+lib-y  += csum_partial.o delay.o memcpy.o memset.o \
           strlen_user.o strncpy_user.o strnlen_user.o uncached.o
 
 obj-y                  += iomap.o
diff --git a/arch/mips/lib/memcpy-inatomic.S b/arch/mips/lib/memcpy-inatomic.S
deleted file mode 100644 (file)
index 68853a0..0000000
+++ /dev/null
@@ -1,451 +0,0 @@
-/*
- * 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.
- *
- * Unified implementation of memcpy, memmove and the __copy_user backend.
- *
- * Copyright (C) 1998, 99, 2000, 01, 2002 Ralf Baechle (ralf@gnu.org)
- * Copyright (C) 1999, 2000, 01, 2002 Silicon Graphics, Inc.
- * Copyright (C) 2002 Broadcom, Inc.
- *   memcpy/copy_user author: Mark Vandevoorde
- * Copyright (C) 2007  Maciej W. Rozycki
- *
- * Mnemonic names for arguments to memcpy/__copy_user
- */
-
-/*
- * Hack to resolve longstanding prefetch issue
- *
- * Prefetching may be fatal on some systems if we're prefetching beyond the
- * end of memory on some systems.  It's also a seriously bad idea on non
- * dma-coherent systems.
- */
-#ifdef CONFIG_DMA_NONCOHERENT
-#undef CONFIG_CPU_HAS_PREFETCH
-#endif
-#ifdef CONFIG_MIPS_MALTA
-#undef CONFIG_CPU_HAS_PREFETCH
-#endif
-
-#include <asm/asm.h>
-#include <asm/asm-offsets.h>
-#include <asm/regdef.h>
-
-#define dst a0
-#define src a1
-#define len a2
-
-/*
- * Spec
- *
- * memcpy copies len bytes from src to dst and sets v0 to dst.
- * It assumes that
- *   - src and dst don't overlap
- *   - src is readable
- *   - dst is writable
- * memcpy uses the standard calling convention
- *
- * __copy_user copies up to len bytes from src to dst and sets a2 (len) to
- * the number of uncopied bytes due to an exception caused by a read or write.
- * __copy_user assumes that src and dst don't overlap, and that the call is
- * implementing one of the following:
- *   copy_to_user
- *     - src is readable  (no exceptions when reading src)
- *   copy_from_user
- *     - dst is writable  (no exceptions when writing dst)
- * __copy_user uses a non-standard calling convention; see
- * include/asm-mips/uaccess.h
- *
- * When an exception happens on a load, the handler must
- # ensure that all of the destination buffer is overwritten to prevent
- * leaking information to user mode programs.
- */
-
-/*
- * Implementation
- */
-
-/*
- * The exception handler for loads requires that:
- *  1- AT contain the address of the byte just past the end of the source
- *     of the copy,
- *  2- src_entry <= src < AT, and
- *  3- (dst - src) == (dst_entry - src_entry),
- * The _entry suffix denotes values when __copy_user was called.
- *
- * (1) is set up up by uaccess.h and maintained by not writing AT in copy_user
- * (2) is met by incrementing src by the number of bytes copied
- * (3) is met by not doing loads between a pair of increments of dst and src
- *
- * The exception handlers for stores adjust len (if necessary) and return.
- * These handlers do not need to overwrite any data.
- *
- * For __rmemcpy and memmove an exception is always a kernel bug, therefore
- * they're not protected.
- */
-
-#define EXC(inst_reg,addr,handler)             \
-9:     inst_reg, addr;                         \
-       .section __ex_table,"a";                \
-       PTR     9b, handler;                    \
-       .previous
-
-/*
- * Only on the 64-bit kernel we can made use of 64-bit registers.
- */
-#ifdef CONFIG_64BIT
-#define USE_DOUBLE
-#endif
-
-#ifdef USE_DOUBLE
-
-#define LOAD   ld
-#define LOADL  ldl
-#define LOADR  ldr
-#define STOREL sdl
-#define STORER sdr
-#define STORE  sd
-#define ADD    daddu
-#define SUB    dsubu
-#define SRL    dsrl
-#define SRA    dsra
-#define SLL    dsll
-#define SLLV   dsllv
-#define SRLV   dsrlv
-#define NBYTES 8
-#define LOG_NBYTES 3
-
-/*
- * As we are sharing code base with the mips32 tree (which use the o32 ABI
- * register definitions). We need to redefine the register definitions from
- * the n64 ABI register naming to the o32 ABI register naming.
- */
-#undef t0
-#undef t1
-#undef t2
-#undef t3
-#define t0     $8
-#define t1     $9
-#define t2     $10
-#define t3     $11
-#define t4     $12
-#define t5     $13
-#define t6     $14
-#define t7     $15
-
-#else
-
-#define LOAD   lw
-#define LOADL  lwl
-#define LOADR  lwr
-#define STOREL swl
-#define STORER swr
-#define STORE  sw
-#define ADD    addu
-#define SUB    subu
-#define SRL    srl
-#define SLL    sll
-#define SRA    sra
-#define SLLV   sllv
-#define SRLV   srlv
-#define NBYTES 4
-#define LOG_NBYTES 2
-
-#endif /* USE_DOUBLE */
-
-#ifdef CONFIG_CPU_LITTLE_ENDIAN
-#define LDFIRST LOADR
-#define LDREST  LOADL
-#define STFIRST STORER
-#define STREST  STOREL
-#define SHIFT_DISCARD SLLV
-#else
-#define LDFIRST LOADL
-#define LDREST  LOADR
-#define STFIRST STOREL
-#define STREST  STORER
-#define SHIFT_DISCARD SRLV
-#endif
-
-#define FIRST(unit) ((unit)*NBYTES)
-#define REST(unit)  (FIRST(unit)+NBYTES-1)
-#define UNIT(unit)  FIRST(unit)
-
-#define ADDRMASK (NBYTES-1)
-
-       .text
-       .set    noreorder
-#ifndef CONFIG_CPU_DADDI_WORKAROUNDS
-       .set    noat
-#else
-       .set    at=v1
-#endif
-
-/*
- * A combined memcpy/__copy_user
- * __copy_user sets len to 0 for success; else to an upper bound of
- * the number of uncopied bytes.
- * memcpy sets v0 to dst.
- */
-       .align  5
-LEAF(__copy_user_inatomic)
-       /*
-        * Note: dst & src may be unaligned, len may be 0
-        * Temps
-        */
-#define rem t8
-
-       /*
-        * The "issue break"s below are very approximate.
-        * Issue delays for dcache fills will perturb the schedule, as will
-        * load queue full replay traps, etc.
-        *
-        * If len < NBYTES use byte operations.
-        */
-       PREF(   0, 0(src) )
-       PREF(   1, 0(dst) )
-       sltu    t2, len, NBYTES
-       and     t1, dst, ADDRMASK
-       PREF(   0, 1*32(src) )
-       PREF(   1, 1*32(dst) )
-       bnez    t2, .Lcopy_bytes_checklen
-        and    t0, src, ADDRMASK
-       PREF(   0, 2*32(src) )
-       PREF(   1, 2*32(dst) )
-       bnez    t1, .Ldst_unaligned
-        nop
-       bnez    t0, .Lsrc_unaligned_dst_aligned
-       /*
-        * use delay slot for fall-through
-        * src and dst are aligned; need to compute rem
-        */
-.Lboth_aligned:
-        SRL    t0, len, LOG_NBYTES+3           # +3 for 8 units/iter
-       beqz    t0, .Lcleanup_both_aligned      # len < 8*NBYTES
-        and    rem, len, (8*NBYTES-1)          # rem = len % (8*NBYTES)
-       PREF(   0, 3*32(src) )
-       PREF(   1, 3*32(dst) )
-       .align  4
-1:
-EXC(   LOAD    t0, UNIT(0)(src),       .Ll_exc)
-EXC(   LOAD    t1, UNIT(1)(src),       .Ll_exc_copy)
-EXC(   LOAD    t2, UNIT(2)(src),       .Ll_exc_copy)
-EXC(   LOAD    t3, UNIT(3)(src),       .Ll_exc_copy)
-       SUB     len, len, 8*NBYTES
-EXC(   LOAD    t4, UNIT(4)(src),       .Ll_exc_copy)
-EXC(   LOAD    t7, UNIT(5)(src),       .Ll_exc_copy)
-       STORE   t0, UNIT(0)(dst)
-       STORE   t1, UNIT(1)(dst)
-EXC(   LOAD    t0, UNIT(6)(src),       .Ll_exc_copy)
-EXC(   LOAD    t1, UNIT(7)(src),       .Ll_exc_copy)
-       ADD     src, src, 8*NBYTES
-       ADD     dst, dst, 8*NBYTES
-       STORE   t2, UNIT(-6)(dst)
-       STORE   t3, UNIT(-5)(dst)
-       STORE   t4, UNIT(-4)(dst)
-       STORE   t7, UNIT(-3)(dst)
-       STORE   t0, UNIT(-2)(dst)
-       STORE   t1, UNIT(-1)(dst)
-       PREF(   0, 8*32(src) )
-       PREF(   1, 8*32(dst) )
-       bne     len, rem, 1b
-        nop
-
-       /*
-        * len == rem == the number of bytes left to copy < 8*NBYTES
-        */
-.Lcleanup_both_aligned:
-       beqz    len, .Ldone
-        sltu   t0, len, 4*NBYTES
-       bnez    t0, .Lless_than_4units
-        and    rem, len, (NBYTES-1)    # rem = len % NBYTES
-       /*
-        * len >= 4*NBYTES
-        */
-EXC(   LOAD    t0, UNIT(0)(src),       .Ll_exc)
-EXC(   LOAD    t1, UNIT(1)(src),       .Ll_exc_copy)
-EXC(   LOAD    t2, UNIT(2)(src),       .Ll_exc_copy)
-EXC(   LOAD    t3, UNIT(3)(src),       .Ll_exc_copy)
-       SUB     len, len, 4*NBYTES
-       ADD     src, src, 4*NBYTES
-       STORE   t0, UNIT(0)(dst)
-       STORE   t1, UNIT(1)(dst)
-       STORE   t2, UNIT(2)(dst)
-       STORE   t3, UNIT(3)(dst)
-       .set    reorder                         /* DADDI_WAR */
-       ADD     dst, dst, 4*NBYTES
-       beqz    len, .Ldone
-       .set    noreorder
-.Lless_than_4units:
-       /*
-        * rem = len % NBYTES
-        */
-       beq     rem, len, .Lcopy_bytes
-        nop
-1:
-EXC(   LOAD    t0, 0(src),             .Ll_exc)
-       ADD     src, src, NBYTES
-       SUB     len, len, NBYTES
-       STORE   t0, 0(dst)
-       .set    reorder                         /* DADDI_WAR */
-       ADD     dst, dst, NBYTES
-       bne     rem, len, 1b
-       .set    noreorder
-
-       /*
-        * src and dst are aligned, need to copy rem bytes (rem < NBYTES)
-        * A loop would do only a byte at a time with possible branch
-        * mispredicts.  Can't do an explicit LOAD dst,mask,or,STORE
-        * because can't assume read-access to dst.  Instead, use
-        * STREST dst, which doesn't require read access to dst.
-        *
-        * This code should perform better than a simple loop on modern,
-        * wide-issue mips processors because the code has fewer branches and
-        * more instruction-level parallelism.
-        */
-#define bits t2
-       beqz    len, .Ldone
-        ADD    t1, dst, len    # t1 is just past last byte of dst
-       li      bits, 8*NBYTES
-       SLL     rem, len, 3     # rem = number of bits to keep
-EXC(   LOAD    t0, 0(src),             .Ll_exc)
-       SUB     bits, bits, rem # bits = number of bits to discard
-       SHIFT_DISCARD t0, t0, bits
-       STREST  t0, -1(t1)
-       jr      ra
-        move   len, zero
-.Ldst_unaligned:
-       /*
-        * dst is unaligned
-        * t0 = src & ADDRMASK
-        * t1 = dst & ADDRMASK; T1 > 0
-        * len >= NBYTES
-        *
-        * Copy enough bytes to align dst
-        * Set match = (src and dst have same alignment)
-        */
-#define match rem
-EXC(   LDFIRST t3, FIRST(0)(src),      .Ll_exc)
-       ADD     t2, zero, NBYTES
-EXC(   LDREST  t3, REST(0)(src),       .Ll_exc_copy)
-       SUB     t2, t2, t1      # t2 = number of bytes copied
-       xor     match, t0, t1
-       STFIRST t3, FIRST(0)(dst)
-       beq     len, t2, .Ldone
-        SUB    len, len, t2
-       ADD     dst, dst, t2
-       beqz    match, .Lboth_aligned
-        ADD    src, src, t2
-
-.Lsrc_unaligned_dst_aligned:
-       SRL     t0, len, LOG_NBYTES+2    # +2 for 4 units/iter
-       PREF(   0, 3*32(src) )
-       beqz    t0, .Lcleanup_src_unaligned
-        and    rem, len, (4*NBYTES-1)   # rem = len % 4*NBYTES
-       PREF(   1, 3*32(dst) )
-1:
-/*
- * Avoid consecutive LD*'s to the same register since some mips
- * implementations can't issue them in the same cycle.
- * It's OK to load FIRST(N+1) before REST(N) because the two addresses
- * are to the same unit (unless src is aligned, but it's not).
- */
-EXC(   LDFIRST t0, FIRST(0)(src),      .Ll_exc)
-EXC(   LDFIRST t1, FIRST(1)(src),      .Ll_exc_copy)
-       SUB     len, len, 4*NBYTES
-EXC(   LDREST  t0, REST(0)(src),       .Ll_exc_copy)
-EXC(   LDREST  t1, REST(1)(src),       .Ll_exc_copy)
-EXC(   LDFIRST t2, FIRST(2)(src),      .Ll_exc_copy)
-EXC(   LDFIRST t3, FIRST(3)(src),      .Ll_exc_copy)
-EXC(   LDREST  t2, REST(2)(src),       .Ll_exc_copy)
-EXC(   LDREST  t3, REST(3)(src),       .Ll_exc_copy)
-       PREF(   0, 9*32(src) )          # 0 is PREF_LOAD  (not streamed)
-       ADD     src, src, 4*NBYTES
-#ifdef CONFIG_CPU_SB1
-       nop                             # improves slotting
-#endif
-       STORE   t0, UNIT(0)(dst)
-       STORE   t1, UNIT(1)(dst)
-       STORE   t2, UNIT(2)(dst)
-       STORE   t3, UNIT(3)(dst)
-       PREF(   1, 9*32(dst) )          # 1 is PREF_STORE (not streamed)
-       .set    reorder                         /* DADDI_WAR */
-       ADD     dst, dst, 4*NBYTES
-       bne     len, rem, 1b
-       .set    noreorder
-
-.Lcleanup_src_unaligned:
-       beqz    len, .Ldone
-        and    rem, len, NBYTES-1  # rem = len % NBYTES
-       beq     rem, len, .Lcopy_bytes
-        nop
-1:
-EXC(   LDFIRST t0, FIRST(0)(src),      .Ll_exc)
-EXC(   LDREST  t0, REST(0)(src),       .Ll_exc_copy)
-       ADD     src, src, NBYTES
-       SUB     len, len, NBYTES
-       STORE   t0, 0(dst)
-       .set    reorder                         /* DADDI_WAR */
-       ADD     dst, dst, NBYTES
-       bne     len, rem, 1b
-       .set    noreorder
-
-.Lcopy_bytes_checklen:
-       beqz    len, .Ldone
-        nop
-.Lcopy_bytes:
-       /* 0 < len < NBYTES  */
-#define COPY_BYTE(N)                   \
-EXC(   lb      t0, N(src), .Ll_exc);   \
-       SUB     len, len, 1;            \
-       beqz    len, .Ldone;            \
-        sb     t0, N(dst)
-
-       COPY_BYTE(0)
-       COPY_BYTE(1)
-#ifdef USE_DOUBLE
-       COPY_BYTE(2)
-       COPY_BYTE(3)
-       COPY_BYTE(4)
-       COPY_BYTE(5)
-#endif
-EXC(   lb      t0, NBYTES-2(src), .Ll_exc)
-       SUB     len, len, 1
-       jr      ra
-        sb     t0, NBYTES-2(dst)
-.Ldone:
-       jr      ra
-        nop
-       END(__copy_user_inatomic)
-
-.Ll_exc_copy:
-       /*
-        * Copy bytes from src until faulting load address (or until a
-        * lb faults)
-        *
-        * When reached by a faulting LDFIRST/LDREST, THREAD_BUADDR($28)
-        * may be more than a byte beyond the last address.
-        * Hence, the lb below may get an exception.
-        *
-        * Assumes src < THREAD_BUADDR($28)
-        */
-       LOAD    t0, TI_TASK($28)
-        nop
-       LOAD    t0, THREAD_BUADDR(t0)
-1:
-EXC(   lb      t1, 0(src),     .Ll_exc)
-       ADD     src, src, 1
-       sb      t1, 0(dst)      # can't fault -- we're copy_from_user
-       .set    reorder                         /* DADDI_WAR */
-       ADD     dst, dst, 1
-       bne     src, t0, 1b
-       .set    noreorder
-.Ll_exc:
-       LOAD    t0, TI_TASK($28)
-        nop
-       LOAD    t0, THREAD_BUADDR(t0)   # t0 is just past last good address
-        nop
-       SUB     len, AT, t0             # len number of uncopied bytes
-       jr      ra
-        nop
index 56a1f85a1ce807504a95d54d49b3d3cab46b33a6..65192c06781e6d876767e6110e2c104e5b0e7a08 100644 (file)
        .set    at=v1
 #endif
 
+/*
+ * t6 is used as a flag to note inatomic mode.
+ */
+LEAF(__copy_user_inatomic)
+       b       __copy_user_common
+        li     t6, 1
+       END(__copy_user_inatomic)
+
 /*
  * A combined memcpy/__copy_user
  * __copy_user sets len to 0 for success; else to an upper bound of
@@ -193,6 +201,8 @@ LEAF(memcpy)                                        /* a0=dst a1=src a2=len */
        move    v0, dst                         /* return value */
 .L__memcpy:
 FEXPORT(__copy_user)
+       li      t6, 0   /* not inatomic */
+__copy_user_common:
        /*
         * Note: dst & src may be unaligned, len may be 0
         * Temps
@@ -458,6 +468,7 @@ EXC(        lb      t1, 0(src),     .Ll_exc)
        LOAD    t0, THREAD_BUADDR(t0)   # t0 is just past last good address
         nop
        SUB     len, AT, t0             # len number of uncopied bytes
+       bnez    t6, .Ldone      /* Skip the zeroing part if inatomic */
        /*
         * Here's where we rely on src and dst being incremented in tandem,
         *   See (3) above.
index aca93eed8779b7756f32d0fb69bf0b32b9227019..263beb9322a8218a79ffef69a9aba244feab9d03 100644 (file)
@@ -41,6 +41,7 @@ config LEMOTE_MACH2F
        select CSRC_R4K if ! MIPS_EXTERNAL_TIMER
        select DMA_NONCOHERENT
        select GENERIC_ISA_DMA_SUPPORT_BROKEN
+       select HAVE_CLK
        select HW_HAS_PCI
        select I8259
        select IRQ_CPU
index 8699a53f0477ee5e14e50d76f7282415acec185e..4f9eaa328a1601a3ff63beaf14b86b65f960a469 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for lemote loongson2f family machines
 #
 
-obj-y += machtype.o irq.o reset.o ec_kb3310b.o
+obj-y += clock.o machtype.o irq.o reset.o ec_kb3310b.o
 
 #
 # Suspend Support
diff --git a/arch/mips/loongson/lemote-2f/clock.c b/arch/mips/loongson/lemote-2f/clock.c
new file mode 100644 (file)
index 0000000..bc739d4
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2006 - 2008 Lemote Inc. & Insititute of Computing Technology
+ * Author: Yanhua, yanh@lemote.com
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+#include <asm/clock.h>
+#include <asm/mach-loongson/loongson.h>
+
+static LIST_HEAD(clock_list);
+static DEFINE_SPINLOCK(clock_lock);
+static DEFINE_MUTEX(clock_list_sem);
+
+/* Minimum CLK support */
+enum {
+       DC_ZERO, DC_25PT = 2, DC_37PT, DC_50PT, DC_62PT, DC_75PT,
+       DC_87PT, DC_DISABLE, DC_RESV
+};
+
+struct cpufreq_frequency_table loongson2_clockmod_table[] = {
+       {DC_RESV, CPUFREQ_ENTRY_INVALID},
+       {DC_ZERO, CPUFREQ_ENTRY_INVALID},
+       {DC_25PT, 0},
+       {DC_37PT, 0},
+       {DC_50PT, 0},
+       {DC_62PT, 0},
+       {DC_75PT, 0},
+       {DC_87PT, 0},
+       {DC_DISABLE, 0},
+       {DC_RESV, CPUFREQ_TABLE_END},
+};
+EXPORT_SYMBOL_GPL(loongson2_clockmod_table);
+
+static struct clk cpu_clk = {
+       .name = "cpu_clk",
+       .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
+       .rate = 800000000,
+};
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+       return &cpu_clk;
+}
+EXPORT_SYMBOL(clk_get);
+
+static void propagate_rate(struct clk *clk)
+{
+       struct clk *clkp;
+
+       list_for_each_entry(clkp, &clock_list, node) {
+               if (likely(clkp->parent != clk))
+                       continue;
+               if (likely(clkp->ops && clkp->ops->recalc))
+                       clkp->ops->recalc(clkp);
+               if (unlikely(clkp->flags & CLK_RATE_PROPAGATES))
+                       propagate_rate(clkp);
+       }
+}
+
+int clk_enable(struct clk *clk)
+{
+       return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+       return (unsigned long)clk->rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+void clk_put(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_put);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+       int ret = 0;
+       int regval;
+       int i;
+
+       if (likely(clk->ops && clk->ops->set_rate)) {
+               unsigned long flags;
+
+               spin_lock_irqsave(&clock_lock, flags);
+               ret = clk->ops->set_rate(clk, rate, 0);
+               spin_unlock_irqrestore(&clock_lock, flags);
+       }
+
+       if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
+               propagate_rate(clk);
+
+       for (i = 0; loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END;
+            i++) {
+               if (loongson2_clockmod_table[i].frequency ==
+                   CPUFREQ_ENTRY_INVALID)
+                       continue;
+               if (rate == loongson2_clockmod_table[i].frequency)
+                       break;
+       }
+       if (rate != loongson2_clockmod_table[i].frequency)
+               return -ENOTSUPP;
+
+       clk->rate = rate;
+
+       regval = LOONGSON_CHIPCFG0;
+       regval = (regval & ~0x7) | (loongson2_clockmod_table[i].index - 1);
+       LOONGSON_CHIPCFG0 = regval;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+       if (likely(clk->ops && clk->ops->round_rate)) {
+               unsigned long flags, rounded;
+
+               spin_lock_irqsave(&clock_lock, flags);
+               rounded = clk->ops->round_rate(clk, rate);
+               spin_unlock_irqrestore(&clock_lock, flags);
+
+               return rounded;
+       }
+
+       return rate;
+}
+EXPORT_SYMBOL_GPL(clk_round_rate);
diff --git a/arch/mips/loongson1/Kconfig b/arch/mips/loongson1/Kconfig
new file mode 100644 (file)
index 0000000..a9a14d6
--- /dev/null
@@ -0,0 +1,22 @@
+if MACH_LOONGSON1
+
+choice
+       prompt "Machine Type"
+
+config LOONGSON1_LS1B
+       bool "Loongson LS1B board"
+       select CEVT_R4K
+       select CSRC_R4K
+       select SYS_HAS_CPU_LOONGSON1B
+       select DMA_NONCOHERENT
+       select BOOT_ELF32
+       select IRQ_CPU
+       select SYS_SUPPORTS_32BIT_KERNEL
+       select SYS_SUPPORTS_LITTLE_ENDIAN
+       select SYS_SUPPORTS_HIGHMEM
+       select SYS_HAS_EARLY_PRINTK
+       select HAVE_CLK
+
+endchoice
+
+endif # MACH_LOONGSON1
diff --git a/arch/mips/loongson1/Makefile b/arch/mips/loongson1/Makefile
new file mode 100644 (file)
index 0000000..9719c75
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Common code for all Loongson 1 based systems
+#
+
+obj-$(CONFIG_MACH_LOONGSON1) += common/
+
+#
+# Loongson LS1B board
+#
+
+obj-$(CONFIG_LOONGSON1_LS1B)  += ls1b/
diff --git a/arch/mips/loongson1/Platform b/arch/mips/loongson1/Platform
new file mode 100644 (file)
index 0000000..99bdefe
--- /dev/null
@@ -0,0 +1,7 @@
+cflags-$(CONFIG_CPU_LOONGSON1)  += \
+       $(call cc-option,-march=mips32r2,-mips32r2 -U_MIPS_ISA -D_MIPS_ISA=_MIPS_ISA_MIPS32) \
+       -Wa,-mips32r2 -Wa,--trap
+
+platform-$(CONFIG_MACH_LOONGSON1)      += loongson1/
+cflags-$(CONFIG_MACH_LOONGSON1)                += -I$(srctree)/arch/mips/include/asm/mach-loongson1
+load-$(CONFIG_LOONGSON1_LS1B)          += 0xffffffff80100000
diff --git a/arch/mips/loongson1/common/Makefile b/arch/mips/loongson1/common/Makefile
new file mode 100644 (file)
index 0000000..b279770
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for common code of loongson1 based machines.
+#
+
+obj-y  += clock.o irq.o platform.o prom.o reset.o setup.o
diff --git a/arch/mips/loongson1/common/clock.c b/arch/mips/loongson1/common/clock.c
new file mode 100644 (file)
index 0000000..1bbbbec
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@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.
+ */
+
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <asm/clock.h>
+#include <asm/time.h>
+
+#include <loongson1.h>
+
+static LIST_HEAD(clocks);
+static DEFINE_MUTEX(clocks_mutex);
+
+struct clk *clk_get(struct device *dev, const char *name)
+{
+       struct clk *c;
+       struct clk *ret = NULL;
+
+       mutex_lock(&clocks_mutex);
+       list_for_each_entry(c, &clocks, node) {
+               if (!strcmp(c->name, name)) {
+                       ret = c;
+                       break;
+               }
+       }
+       mutex_unlock(&clocks_mutex);
+
+       return ret;
+}
+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);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+       return clk->rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+void clk_put(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_put);
+
+static void pll_clk_init(struct clk *clk)
+{
+       u32 pll;
+
+       pll = __raw_readl(LS1X_CLK_PLL_FREQ);
+       clk->rate = (12 + (pll & 0x3f)) * 33 / 2
+                       + ((pll >> 8) & 0x3ff) * 33 / 1024 / 2;
+       clk->rate *= 1000000;
+}
+
+static void cpu_clk_init(struct clk *clk)
+{
+       u32 pll, ctrl;
+
+       pll = clk_get_rate(clk->parent);
+       ctrl = __raw_readl(LS1X_CLK_PLL_DIV) & DIV_CPU;
+       clk->rate = pll / (ctrl >> DIV_CPU_SHIFT);
+}
+
+static void ddr_clk_init(struct clk *clk)
+{
+       u32 pll, ctrl;
+
+       pll = clk_get_rate(clk->parent);
+       ctrl = __raw_readl(LS1X_CLK_PLL_DIV) & DIV_DDR;
+       clk->rate = pll / (ctrl >> DIV_DDR_SHIFT);
+}
+
+static void dc_clk_init(struct clk *clk)
+{
+       u32 pll, ctrl;
+
+       pll = clk_get_rate(clk->parent);
+       ctrl = __raw_readl(LS1X_CLK_PLL_DIV) & DIV_DC;
+       clk->rate = pll / (ctrl >> DIV_DC_SHIFT);
+}
+
+static struct clk_ops pll_clk_ops = {
+       .init   = pll_clk_init,
+};
+
+static struct clk_ops cpu_clk_ops = {
+       .init   = cpu_clk_init,
+};
+
+static struct clk_ops ddr_clk_ops = {
+       .init   = ddr_clk_init,
+};
+
+static struct clk_ops dc_clk_ops = {
+       .init   = dc_clk_init,
+};
+
+static struct clk pll_clk = {
+       .name   = "pll",
+       .ops    = &pll_clk_ops,
+};
+
+static struct clk cpu_clk = {
+       .name   = "cpu",
+       .parent = &pll_clk,
+       .ops    = &cpu_clk_ops,
+};
+
+static struct clk ddr_clk = {
+       .name   = "ddr",
+       .parent = &pll_clk,
+       .ops    = &ddr_clk_ops,
+};
+
+static struct clk dc_clk = {
+       .name   = "dc",
+       .parent = &pll_clk,
+       .ops    = &dc_clk_ops,
+};
+
+int clk_register(struct clk *clk)
+{
+       mutex_lock(&clocks_mutex);
+       list_add(&clk->node, &clocks);
+       if (clk->ops->init)
+               clk->ops->init(clk);
+       mutex_unlock(&clocks_mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL(clk_register);
+
+static struct clk *ls1x_clks[] = {
+       &pll_clk,
+       &cpu_clk,
+       &ddr_clk,
+       &dc_clk,
+};
+
+int __init ls1x_clock_init(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ls1x_clks); i++)
+               clk_register(ls1x_clks[i]);
+
+       return 0;
+}
+
+void __init plat_time_init(void)
+{
+       struct clk *clk;
+
+       /* Initialize LS1X clocks */
+       ls1x_clock_init();
+
+       /* setup mips r4k timer */
+       clk = clk_get(NULL, "cpu");
+       if (IS_ERR(clk))
+               panic("unable to get dc clock, err=%ld", PTR_ERR(clk));
+
+       mips_hpt_frequency = clk_get_rate(clk) / 2;
+}
diff --git a/arch/mips/loongson1/common/irq.c b/arch/mips/loongson1/common/irq.c
new file mode 100644 (file)
index 0000000..41bc8ff
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <asm/irq_cpu.h>
+
+#include <loongson1.h>
+#include <irq.h>
+
+#define LS1X_INTC_REG(n, x) \
+               ((void __iomem *)KSEG1ADDR(LS1X_INTC_BASE + (n * 0x18) + (x)))
+
+#define LS1X_INTC_INTISR(n)            LS1X_INTC_REG(n, 0x0)
+#define LS1X_INTC_INTIEN(n)            LS1X_INTC_REG(n, 0x4)
+#define LS1X_INTC_INTSET(n)            LS1X_INTC_REG(n, 0x8)
+#define LS1X_INTC_INTCLR(n)            LS1X_INTC_REG(n, 0xc)
+#define LS1X_INTC_INTPOL(n)            LS1X_INTC_REG(n, 0x10)
+#define LS1X_INTC_INTEDGE(n)           LS1X_INTC_REG(n, 0x14)
+
+static void ls1x_irq_ack(struct irq_data *d)
+{
+       unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
+       unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
+
+       __raw_writel(__raw_readl(LS1X_INTC_INTCLR(n))
+                       | (1 << bit), LS1X_INTC_INTCLR(n));
+}
+
+static void ls1x_irq_mask(struct irq_data *d)
+{
+       unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
+       unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
+
+       __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
+                       & ~(1 << bit), LS1X_INTC_INTIEN(n));
+}
+
+static void ls1x_irq_mask_ack(struct irq_data *d)
+{
+       unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
+       unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
+
+       __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
+                       & ~(1 << bit), LS1X_INTC_INTIEN(n));
+       __raw_writel(__raw_readl(LS1X_INTC_INTCLR(n))
+                       | (1 << bit), LS1X_INTC_INTCLR(n));
+}
+
+static void ls1x_irq_unmask(struct irq_data *d)
+{
+       unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
+       unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
+
+       __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
+                       | (1 << bit), LS1X_INTC_INTIEN(n));
+}
+
+static struct irq_chip ls1x_irq_chip = {
+       .name           = "LS1X-INTC",
+       .irq_ack        = ls1x_irq_ack,
+       .irq_mask       = ls1x_irq_mask,
+       .irq_mask_ack   = ls1x_irq_mask_ack,
+       .irq_unmask     = ls1x_irq_unmask,
+};
+
+static void ls1x_irq_dispatch(int n)
+{
+       u32 int_status, irq;
+
+       /* Get pending sources, masked by current enables */
+       int_status = __raw_readl(LS1X_INTC_INTISR(n)) &
+                       __raw_readl(LS1X_INTC_INTIEN(n));
+
+       if (int_status) {
+               irq = LS1X_IRQ(n, __ffs(int_status));
+               do_IRQ(irq);
+       }
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+       unsigned int pending;
+
+       pending = read_c0_cause() & read_c0_status() & ST0_IM;
+
+       if (pending & CAUSEF_IP7)
+               do_IRQ(TIMER_IRQ);
+       else if (pending & CAUSEF_IP2)
+               ls1x_irq_dispatch(0); /* INT0 */
+       else if (pending & CAUSEF_IP3)
+               ls1x_irq_dispatch(1); /* INT1 */
+       else if (pending & CAUSEF_IP4)
+               ls1x_irq_dispatch(2); /* INT2 */
+       else if (pending & CAUSEF_IP5)
+               ls1x_irq_dispatch(3); /* INT3 */
+       else if (pending & CAUSEF_IP6)
+               ls1x_irq_dispatch(4); /* INT4 */
+       else
+               spurious_interrupt();
+
+}
+
+struct irqaction cascade_irqaction = {
+       .handler = no_action,
+       .name = "cascade",
+       .flags = IRQF_NO_THREAD,
+};
+
+static void __init ls1x_irq_init(int base)
+{
+       int n;
+
+       /* Disable interrupts and clear pending,
+        * setup all IRQs as high level triggered
+        */
+       for (n = 0; n < 4; n++) {
+               __raw_writel(0x0, LS1X_INTC_INTIEN(n));
+               __raw_writel(0xffffffff, LS1X_INTC_INTCLR(n));
+               __raw_writel(0xffffffff, LS1X_INTC_INTPOL(n));
+               /* set DMA0, DMA1 and DMA2 to edge trigger */
+               __raw_writel(n ? 0x0 : 0xe000, LS1X_INTC_INTEDGE(n));
+       }
+
+
+       for (n = base; n < LS1X_IRQS; n++) {
+               irq_set_chip_and_handler(n, &ls1x_irq_chip,
+                                        handle_level_irq);
+       }
+
+       setup_irq(INT0_IRQ, &cascade_irqaction);
+       setup_irq(INT1_IRQ, &cascade_irqaction);
+       setup_irq(INT2_IRQ, &cascade_irqaction);
+       setup_irq(INT3_IRQ, &cascade_irqaction);
+}
+
+void __init arch_init_irq(void)
+{
+       mips_cpu_irq_init();
+       ls1x_irq_init(LS1X_IRQ_BASE);
+}
diff --git a/arch/mips/loongson1/common/platform.c b/arch/mips/loongson1/common/platform.c
new file mode 100644 (file)
index 0000000..e92d59c
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@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.
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/phy.h>
+#include <linux/serial_8250.h>
+#include <linux/stmmac.h>
+#include <asm-generic/sizes.h>
+
+#include <loongson1.h>
+
+#define LS1X_UART(_id)                                         \
+       {                                                       \
+               .mapbase        = LS1X_UART ## _id ## _BASE,    \
+               .irq            = LS1X_UART ## _id ## _IRQ,     \
+               .iotype         = UPIO_MEM,                     \
+               .flags          = UPF_IOREMAP | UPF_FIXED_TYPE, \
+               .type           = PORT_16550A,                  \
+       }
+
+static struct plat_serial8250_port ls1x_serial8250_port[] = {
+       LS1X_UART(0),
+       LS1X_UART(1),
+       LS1X_UART(2),
+       LS1X_UART(3),
+       {},
+};
+
+struct platform_device ls1x_uart_device = {
+       .name           = "serial8250",
+       .id             = PLAT8250_DEV_PLATFORM,
+       .dev            = {
+               .platform_data = ls1x_serial8250_port,
+       },
+};
+
+void __init ls1x_serial_setup(void)
+{
+       struct clk *clk;
+       struct plat_serial8250_port *p;
+
+       clk = clk_get(NULL, "dc");
+       if (IS_ERR(clk))
+               panic("unable to get dc clock, err=%ld", PTR_ERR(clk));
+
+       for (p = ls1x_serial8250_port; p->flags != 0; ++p)
+               p->uartclk = clk_get_rate(clk);
+}
+
+/* Synopsys Ethernet GMAC */
+static struct resource ls1x_eth0_resources[] = {
+       [0] = {
+               .start  = LS1X_GMAC0_BASE,
+               .end    = LS1X_GMAC0_BASE + SZ_64K - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .name   = "macirq",
+               .start  = LS1X_GMAC0_IRQ,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct stmmac_mdio_bus_data ls1x_mdio_bus_data = {
+       .bus_id         = 0,
+       .phy_mask       = 0,
+};
+
+static struct plat_stmmacenet_data ls1x_eth_data = {
+       .bus_id         = 0,
+       .phy_addr       = -1,
+       .mdio_bus_data  = &ls1x_mdio_bus_data,
+       .has_gmac       = 1,
+       .tx_coe         = 1,
+};
+
+struct platform_device ls1x_eth0_device = {
+       .name           = "stmmaceth",
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(ls1x_eth0_resources),
+       .resource       = ls1x_eth0_resources,
+       .dev            = {
+               .platform_data = &ls1x_eth_data,
+       },
+};
+
+/* USB EHCI */
+static u64 ls1x_ehci_dmamask = DMA_BIT_MASK(32);
+
+static struct resource ls1x_ehci_resources[] = {
+       [0] = {
+               .start  = LS1X_EHCI_BASE,
+               .end    = LS1X_EHCI_BASE + SZ_32K - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = LS1X_EHCI_IRQ,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+struct platform_device ls1x_ehci_device = {
+       .name           = "ls1x-ehci",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(ls1x_ehci_resources),
+       .resource       = ls1x_ehci_resources,
+       .dev            = {
+               .dma_mask = &ls1x_ehci_dmamask,
+       },
+};
+
+/* Real Time Clock */
+struct platform_device ls1x_rtc_device = {
+       .name           = "ls1x-rtc",
+       .id             = -1,
+};
diff --git a/arch/mips/loongson1/common/prom.c b/arch/mips/loongson1/common/prom.c
new file mode 100644 (file)
index 0000000..1f8e49f
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * Modified from arch/mips/pnx833x/common/prom.c.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/serial_reg.h>
+#include <asm/bootinfo.h>
+
+#include <loongson1.h>
+#include <prom.h>
+
+int prom_argc;
+char **prom_argv, **prom_envp;
+unsigned long memsize, highmemsize;
+
+char *prom_getenv(char *envname)
+{
+       char **env = prom_envp;
+       int i;
+
+       i = strlen(envname);
+
+       while (*env) {
+               if (strncmp(envname, *env, i) == 0 && *(*env+i) == '=')
+                       return *env + i + 1;
+               env++;
+       }
+
+       return 0;
+}
+
+static inline unsigned long env_or_default(char *env, unsigned long dfl)
+{
+       char *str = prom_getenv(env);
+       return str ? simple_strtol(str, 0, 0) : dfl;
+}
+
+void __init prom_init_cmdline(void)
+{
+       char *c = &(arcs_cmdline[0]);
+       int i;
+
+       for (i = 1; i < prom_argc; i++) {
+               strcpy(c, prom_argv[i]);
+               c += strlen(prom_argv[i]);
+               if (i < prom_argc-1)
+                       *c++ = ' ';
+       }
+       *c = 0;
+}
+
+void __init prom_init(void)
+{
+       prom_argc = fw_arg0;
+       prom_argv = (char **)fw_arg1;
+       prom_envp = (char **)fw_arg2;
+
+       prom_init_cmdline();
+
+       memsize = env_or_default("memsize", DEFAULT_MEMSIZE);
+       highmemsize = env_or_default("highmemsize", 0x0);
+}
+
+void __init prom_free_prom_memory(void)
+{
+}
+
+#define PORT(offset)   (u8 *)(KSEG1ADDR(LS1X_UART0_BASE + offset))
+
+void __init prom_putchar(char c)
+{
+       int timeout;
+
+       timeout = 1024;
+
+       while (((readb(PORT(UART_LSR)) & UART_LSR_THRE) == 0)
+                       && (timeout-- > 0))
+               ;
+
+       writeb(c, PORT(UART_TX));
+}
diff --git a/arch/mips/loongson1/common/reset.c b/arch/mips/loongson1/common/reset.c
new file mode 100644 (file)
index 0000000..fb979a7
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@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.
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+#include <asm/reboot.h>
+
+#include <loongson1.h>
+
+static void ls1x_restart(char *command)
+{
+       __raw_writel(0x1, LS1X_WDT_EN);
+       __raw_writel(0x5000000, LS1X_WDT_TIMER);
+       __raw_writel(0x1, LS1X_WDT_SET);
+}
+
+static void ls1x_halt(void)
+{
+       while (1) {
+               if (cpu_wait)
+                       cpu_wait();
+       }
+}
+
+static void ls1x_power_off(void)
+{
+       ls1x_halt();
+}
+
+static int __init ls1x_reboot_setup(void)
+{
+       _machine_restart = ls1x_restart;
+       _machine_halt = ls1x_halt;
+       pm_power_off = ls1x_power_off;
+
+       return 0;
+}
+
+arch_initcall(ls1x_reboot_setup);
diff --git a/arch/mips/loongson1/common/setup.c b/arch/mips/loongson1/common/setup.c
new file mode 100644 (file)
index 0000000..62128cc
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@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.
+ */
+
+#include <asm/bootinfo.h>
+
+#include <prom.h>
+
+void __init plat_mem_setup(void)
+{
+       add_memory_region(0x0, (memsize << 20), BOOT_MEM_RAM);
+}
+
+const char *get_system_type(void)
+{
+       unsigned int processor_id = (&current_cpu_data)->processor_id;
+
+       switch (processor_id & PRID_REV_MASK) {
+       case PRID_REV_LOONGSON1B:
+               return "LOONGSON LS1B";
+       default:
+               return "LOONGSON (unknown)";
+       }
+}
diff --git a/arch/mips/loongson1/ls1b/Makefile b/arch/mips/loongson1/ls1b/Makefile
new file mode 100644 (file)
index 0000000..891eac4
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for loongson1B based machines.
+#
+
+obj-y += board.o
diff --git a/arch/mips/loongson1/ls1b/board.c b/arch/mips/loongson1/ls1b/board.c
new file mode 100644 (file)
index 0000000..295b1be
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@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.
+ */
+
+#include <platform.h>
+
+#include <linux/serial_8250.h>
+#include <loongson1.h>
+
+static struct platform_device *ls1b_platform_devices[] __initdata = {
+       &ls1x_uart_device,
+       &ls1x_eth0_device,
+       &ls1x_ehci_device,
+       &ls1x_rtc_device,
+};
+
+static int __init ls1b_platform_init(void)
+{
+       int err;
+
+       ls1x_serial_setup();
+
+       err = platform_add_devices(ls1b_platform_devices,
+                                  ARRAY_SIZE(ls1b_platform_devices));
+       return err;
+}
+
+arch_initcall(ls1b_platform_init);
index 5fa185151fc8c29b121415db015b4bb878a5b7c1..64a28e819064c437dbfc2bd6f90f6f560a54d89b 100644 (file)
@@ -58,18 +58,16 @@ enum fields {
 
 enum opcode {
        insn_invalid,
-       insn_addu, insn_addiu, insn_and, insn_andi, insn_beq,
-       insn_beql, insn_bgez, insn_bgezl, insn_bltz, insn_bltzl,
-       insn_bne, insn_cache, insn_daddu, insn_daddiu, insn_dmfc0,
-       insn_dmtc0, insn_dsll, insn_dsll32, insn_dsra, insn_dsrl,
-       insn_dsrl32, insn_drotr, insn_drotr32, insn_dsubu, insn_eret,
-       insn_j, insn_jal, insn_jr, insn_ld, insn_ll, insn_lld,
-       insn_lui, insn_lw, insn_mfc0, insn_mtc0, insn_or, insn_ori,
-       insn_pref, insn_rfe, insn_sc, insn_scd, insn_sd, insn_sll,
-       insn_sra, insn_srl, insn_rotr, insn_subu, insn_sw, insn_tlbp,
+       insn_addiu, insn_addu, insn_and, insn_andi, insn_bbit0, insn_bbit1,
+       insn_beq, insn_beql, insn_bgez, insn_bgezl, insn_bltz, insn_bltzl,
+       insn_bne, insn_cache, insn_daddiu, insn_daddu, insn_dins, insn_dinsm,
+       insn_dmfc0, insn_dmtc0, insn_drotr, insn_drotr32, insn_dsll,
+       insn_dsll32, insn_dsra, insn_dsrl, insn_dsrl32, insn_dsubu, insn_eret,
+       insn_j, insn_jal, insn_jr, insn_ld, insn_ldx, insn_ll, insn_lld,
+       insn_lui, insn_lw, insn_lwx, insn_mfc0, insn_mtc0, insn_or, insn_ori,
+       insn_pref, insn_rfe, insn_rotr, insn_sc, insn_scd, insn_sd, insn_sll,
+       insn_sra, insn_srl, insn_subu, insn_sw, insn_syscall, insn_tlbp,
        insn_tlbr, insn_tlbwi, insn_tlbwr, insn_xor, insn_xori,
-       insn_dins, insn_dinsm, insn_syscall, insn_bbit0, insn_bbit1,
-       insn_lwx, insn_ldx
 };
 
 struct insn {
@@ -90,65 +88,65 @@ struct insn {
 static struct insn insn_table[] __uasminitdata = {
        { insn_addiu, M(addiu_op, 0, 0, 0, 0, 0), RS | RT | SIMM },
        { insn_addu, M(spec_op, 0, 0, 0, 0, addu_op), RS | RT | RD },
-       { insn_and, M(spec_op, 0, 0, 0, 0, and_op), RS | RT | RD },
        { insn_andi, M(andi_op, 0, 0, 0, 0, 0), RS | RT | UIMM },
-       { insn_beq, M(beq_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
+       { insn_and, M(spec_op, 0, 0, 0, 0, and_op), RS | RT | RD },
+       { insn_bbit0, M(lwc2_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
+       { insn_bbit1, M(swc2_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
        { insn_beql, M(beql_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
-       { insn_bgez, M(bcond_op, 0, bgez_op, 0, 0, 0), RS | BIMM },
+       { insn_beq, M(beq_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
        { insn_bgezl, M(bcond_op, 0, bgezl_op, 0, 0, 0), RS | BIMM },
-       { insn_bltz, M(bcond_op, 0, bltz_op, 0, 0, 0), RS | BIMM },
+       { insn_bgez, M(bcond_op, 0, bgez_op, 0, 0, 0), RS | BIMM },
        { insn_bltzl, M(bcond_op, 0, bltzl_op, 0, 0, 0), RS | BIMM },
+       { insn_bltz, M(bcond_op, 0, bltz_op, 0, 0, 0), RS | BIMM },
        { insn_bne, M(bne_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
        { insn_cache,  M(cache_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
        { insn_daddiu, M(daddiu_op, 0, 0, 0, 0, 0), RS | RT | SIMM },
        { insn_daddu, M(spec_op, 0, 0, 0, 0, daddu_op), RS | RT | RD },
+       { insn_dinsm, M(spec3_op, 0, 0, 0, 0, dinsm_op), RS | RT | RD | RE },
+       { insn_dins, M(spec3_op, 0, 0, 0, 0, dins_op), RS | RT | RD | RE },
        { insn_dmfc0, M(cop0_op, dmfc_op, 0, 0, 0, 0), RT | RD | SET},
        { insn_dmtc0, M(cop0_op, dmtc_op, 0, 0, 0, 0), RT | RD | SET},
-       { insn_dsll, M(spec_op, 0, 0, 0, 0, dsll_op), RT | RD | RE },
+       { insn_drotr32, M(spec_op, 1, 0, 0, 0, dsrl32_op), RT | RD | RE },
+       { insn_drotr, M(spec_op, 1, 0, 0, 0, dsrl_op), RT | RD | RE },
        { insn_dsll32, M(spec_op, 0, 0, 0, 0, dsll32_op), RT | RD | RE },
+       { insn_dsll, M(spec_op, 0, 0, 0, 0, dsll_op), RT | RD | RE },
        { insn_dsra, M(spec_op, 0, 0, 0, 0, dsra_op), RT | RD | RE },
-       { insn_dsrl, M(spec_op, 0, 0, 0, 0, dsrl_op), RT | RD | RE },
        { insn_dsrl32, M(spec_op, 0, 0, 0, 0, dsrl32_op), RT | RD | RE },
-       { insn_drotr, M(spec_op, 1, 0, 0, 0, dsrl_op), RT | RD | RE },
-       { insn_drotr32, M(spec_op, 1, 0, 0, 0, dsrl32_op), RT | RD | RE },
+       { insn_dsrl, M(spec_op, 0, 0, 0, 0, dsrl_op), RT | RD | RE },
        { insn_dsubu, M(spec_op, 0, 0, 0, 0, dsubu_op), RS | RT | RD },
        { insn_eret,  M(cop0_op, cop_op, 0, 0, 0, eret_op),  0 },
-       { insn_j,  M(j_op, 0, 0, 0, 0, 0),  JIMM },
        { insn_jal,  M(jal_op, 0, 0, 0, 0, 0),  JIMM },
+       { insn_j,  M(j_op, 0, 0, 0, 0, 0),  JIMM },
        { insn_jr,  M(spec_op, 0, 0, 0, 0, jr_op),  RS },
        { insn_ld,  M(ld_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
-       { insn_ll,  M(ll_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
+       { insn_ldx, M(spec3_op, 0, 0, 0, ldx_op, lx_op), RS | RT | RD },
        { insn_lld,  M(lld_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
+       { insn_ll,  M(ll_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
        { insn_lui,  M(lui_op, 0, 0, 0, 0, 0),  RT | SIMM },
        { insn_lw,  M(lw_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
+       { insn_lwx, M(spec3_op, 0, 0, 0, lwx_op, lx_op), RS | RT | RD },
        { insn_mfc0,  M(cop0_op, mfc_op, 0, 0, 0, 0),  RT | RD | SET},
        { insn_mtc0,  M(cop0_op, mtc_op, 0, 0, 0, 0),  RT | RD | SET},
-       { insn_or,  M(spec_op, 0, 0, 0, 0, or_op),  RS | RT | RD },
        { insn_ori,  M(ori_op, 0, 0, 0, 0, 0),  RS | RT | UIMM },
+       { insn_or,  M(spec_op, 0, 0, 0, 0, or_op),  RS | RT | RD },
        { insn_pref,  M(pref_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
        { insn_rfe,  M(cop0_op, cop_op, 0, 0, 0, rfe_op),  0 },
-       { insn_sc,  M(sc_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
+       { insn_rotr,  M(spec_op, 1, 0, 0, 0, srl_op),  RT | RD | RE },
        { insn_scd,  M(scd_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
+       { insn_sc,  M(sc_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
        { insn_sd,  M(sd_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
        { insn_sll,  M(spec_op, 0, 0, 0, 0, sll_op),  RT | RD | RE },
        { insn_sra,  M(spec_op, 0, 0, 0, 0, sra_op),  RT | RD | RE },
        { insn_srl,  M(spec_op, 0, 0, 0, 0, srl_op),  RT | RD | RE },
-       { insn_rotr,  M(spec_op, 1, 0, 0, 0, srl_op),  RT | RD | RE },
        { insn_subu,  M(spec_op, 0, 0, 0, 0, subu_op),  RS | RT | RD },
        { insn_sw,  M(sw_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
+       { insn_syscall, M(spec_op, 0, 0, 0, 0, syscall_op), SCIMM},
        { insn_tlbp,  M(cop0_op, cop_op, 0, 0, 0, tlbp_op),  0 },
        { insn_tlbr,  M(cop0_op, cop_op, 0, 0, 0, tlbr_op),  0 },
        { insn_tlbwi,  M(cop0_op, cop_op, 0, 0, 0, tlbwi_op),  0 },
        { insn_tlbwr,  M(cop0_op, cop_op, 0, 0, 0, tlbwr_op),  0 },
-       { insn_xor,  M(spec_op, 0, 0, 0, 0, xor_op),  RS | RT | RD },
        { insn_xori,  M(xori_op, 0, 0, 0, 0, 0),  RS | RT | UIMM },
-       { insn_dins, M(spec3_op, 0, 0, 0, 0, dins_op), RS | RT | RD | RE },
-       { insn_dinsm, M(spec3_op, 0, 0, 0, 0, dinsm_op), RS | RT | RD | RE },
-       { insn_syscall, M(spec_op, 0, 0, 0, 0, syscall_op), SCIMM},
-       { insn_bbit0, M(lwc2_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
-       { insn_bbit1, M(swc2_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
-       { insn_lwx, M(spec3_op, 0, 0, 0, lwx_op, lx_op), RS | RT | RD },
-       { insn_ldx, M(spec3_op, 0, 0, 0, ldx_op, lx_op), RS | RT | RD },
+       { insn_xor,  M(spec_op, 0, 0, 0, 0, xor_op),  RS | RT | RD },
        { insn_invalid, 0, 0 }
 };
 
index f193f7b3bd81ce15b23c80fb6acce438be838649..1902fa22d277dc2cdd8ccd1ccad583a71d8cdc7a 100644 (file)
@@ -54,7 +54,7 @@ void prom_putchar(char c)
 #elif defined(CONFIG_CPU_XLR)
        uartbase = nlm_mmio_base(NETLOGIC_IO_UART_0_OFFSET);
 #endif
-       while (nlm_read_reg(uartbase, UART_LSR) == 0)
+       while ((nlm_read_reg(uartbase, UART_LSR) & UART_LSR_THRE) == 0)
                ;
        nlm_write_reg(uartbase, UART_TX, c);
 }
index c138b1a6dec3c9af46df99ff0c627d02514c54c9..a13355cc97eb4cdcc4df0020007e5ad9c0935e58 100644 (file)
                        XLP_IO_SYS_OFFSET(node) + XLP_IO_PCI_HDRSZ + \
                        SYS_CPU_NONCOHERENT_MODE * 4
 
-.macro __config_lsu
-       li      t0, LSU_DEFEATURE
-       mfcr    t1, t0
+#define        XLP_AX_WORKAROUND       /* enable Ax silicon workarounds */
 
-       lui     t2, 0x4080  /* Enable Unaligned Access, L2HPE */
-       or      t1, t1, t2
-       li      t2, ~0xe    /* S1RCM */
+/* Enable XLP features and workarounds in the LSU */
+.macro xlp_config_lsu
+       li      t0, LSU_DEFEATURE
+       mfcr    t1, t0
+
+       lui     t2, 0x4080      /* Enable Unaligned Access, L2HPE */
+       or      t1, t1, t2
+#ifdef XLP_AX_WORKAROUND
+       li      t2, ~0xe        /* S1RCM */
        and     t1, t1, t2
-       mtcr    t1, t0
+#endif
+       mtcr    t1, t0
 
-       li      t0, SCHED_DEFEATURE
-       lui     t1, 0x0100  /* Experimental: Disable BRU accepting ALU ops */
-       mtcr    t1, t0
+#ifdef XLP_AX_WORKAROUND
+       li      t0, SCHED_DEFEATURE
+       lui     t1, 0x0100      /* Disable BRU accepting ALU ops */
+       mtcr    t1, t0
+#endif
+.endm
+
+/*
+ * This is the code that will be copied to the reset entry point for
+ * XLR and XLP. The XLP cores start here when they are woken up. This
+ * is also the NMI entry point.
+ */
+.macro xlp_flush_l1_dcache
+       li      t0, LSU_DEBUG_DATA0
+       li      t1, LSU_DEBUG_ADDR
+       li      t2, 0           /* index */
+       li      t3, 0x1000      /* loop count */
+1:
+       sll     v0, t2, 5
+       mtcr    zero, t0
+       ori     v1, v0, 0x3     /* way0 | write_enable | write_active */
+       mtcr    v1, t1
+2:
+       mfcr    v1, t1
+       andi    v1, 0x1         /* wait for write_active == 0 */
+       bnez    v1, 2b
+       nop
+       mtcr    zero, t0
+       ori     v1, v0, 0x7     /* way1 | write_enable | write_active */
+       mtcr    v1, t1
+3:
+       mfcr    v1, t1
+       andi    v1, 0x1         /* wait for write_active == 0 */
+       bnez    v1, 3b
+       nop
+       addi    t2, 1
+       bne     t3, t2, 1b
+       nop
 .endm
 
 /*
  * The cores can come start when they are woken up. This is also the NMI
  * entry, so check that first.
  *
- * The data corresponding to reset is stored at RESET_DATA_PHYS location,
- * this will have the thread mask (used when core is woken up) and the
- * current NMI handler in case we reached here for an NMI.
+ * The data corresponding to reset/NMI is stored at RESET_DATA_PHYS
+ * location, this will have the thread mask (used when core is woken up)
+ * and the current NMI handler in case we reached here for an NMI.
  *
  * When a core or thread is newly woken up, it loops in a 'wait'. When
  * the CPU really needs waking up, we send an NMI to it, with the NMI
 FEXPORT(nlm_reset_entry)
        dmtc0   k0, $22, 6
        dmtc0   k1, $22, 7
-       mfc0    k0, CP0_STATUS
-       li      k1, 0x80000
-       and     k1, k0, k1
-       beqz    k1, 1f         /* go to real reset entry */
+       mfc0    k0, CP0_STATUS
+       li      k1, 0x80000
+       and     k1, k0, k1
+       beqz    k1, 1f          /* go to real reset entry */
        nop
-       li      k1, CKSEG1ADDR(RESET_DATA_PHYS)   /* NMI */
+       li      k1, CKSEG1ADDR(RESET_DATA_PHYS) /* NMI */
        ld      k0, BOOT_NMI_HANDLER(k1)
        jr      k0
        nop
@@ -114,21 +154,25 @@ FEXPORT(nlm_reset_entry)
        li      t2, SYS_CPU_COHERENT_BASE(0)
        add     t2, t2, t3              /* t2 <- SYS offset for node */
        lw      t1, 0(t2)
-       and     t1, t1, t0
-       sw      t1, 0(t2)
+       and     t1, t1, t0
+       sw      t1, 0(t2)
 
        /* read back to ensure complete */
-       lw      t1, 0(t2)
+       lw      t1, 0(t2)
        sync
 
        /* Configure LSU on Non-0 Cores. */
-       __config_lsu
+       xlp_config_lsu
+       /* FALL THROUGH */
 
 /*
  * Wake up sibling threads from the initial thread in
  * a core.
  */
 EXPORT(nlm_boot_siblings)
+       /* core L1D flush before enable threads */
+       xlp_flush_l1_dcache
+       /* Enable hw threads by writing to MAP_THREADMODE of the core */
        li      t0, CKSEG1ADDR(RESET_DATA_PHYS)
        lw      t1, BOOT_THREAD_MODE(t0)        /* t1 <- thread mode */
        li      t0, ((CPU_BLOCKID_MAP << 8) | MAP_THREADMODE)
@@ -139,31 +183,28 @@ EXPORT(nlm_boot_siblings)
        /*
         * The new hardware thread starts at the next instruction
         * For all the cases other than core 0 thread 0, we will
-         * jump to the secondary wait function.
-         */
+       * jump to the secondary wait function.
+       */
        mfc0    v0, CP0_EBASE, 1
        andi    v0, 0x7f                /* v0 <- node/core */
 
-#if 1
-       /* A0 errata - Write MMU_SETUP after changing thread mode register. */
+       /* Init MMU in the first thread after changing THREAD_MODE
+        * register (Ax Errata?)
+        */
        andi    v1, v0, 0x3             /* v1 <- thread id */
        bnez    v1, 2f
        nop
 
-        li     t0, MMU_SETUP
-        li     t1, 0
-        mtcr   t1, t0
-       ehb
-#endif
+       li      t0, MMU_SETUP
+       li      t1, 0
+       mtcr    t1, t0
+       _ehb
 
-2:     beqz    v0, 4f
+2:     beqz    v0, 4f          /* boot cpu (cpuid == 0)? */
        nop
 
        /* setup status reg */
-       mfc0    t1, CP0_STATUS
-       li      t0, ST0_BEV
-       or      t1, t0
-       xor     t1, t0
+       move    t1, zero
 #ifdef CONFIG_64BIT
        ori     t1, ST0_KX
 #endif
@@ -183,9 +224,9 @@ EXPORT(nlm_boot_siblings)
         * For the boot CPU, we have to restore registers and
         * return
         */
-4:     dmfc0   t0, $4, 2       /* restore SP from UserLocal */
+4:     dmfc0   t0, $4, 2       /* restore SP from UserLocal */
        li      t1, 0xfadebeef
-       dmtc0   t1, $4, 2       /* restore SP from UserLocal */
+       dmtc0   t1, $4, 2       /* restore SP from UserLocal */
        PTR_SUBU sp, t0, PT_SIZE
        RESTORE_ALL
        jr   ra
@@ -193,7 +234,7 @@ EXPORT(nlm_boot_siblings)
 EXPORT(nlm_reset_entry_end)
 
 FEXPORT(xlp_boot_core0_siblings)       /* "Master" cpu starts from here */
-       __config_lsu
+       xlp_config_lsu
        dmtc0   sp, $4, 2               /* SP saved in UserLocal */
        SAVE_ALL
        sync
@@ -210,6 +251,12 @@ FEXPORT(xlp_boot_core0_siblings)   /* "Master" cpu starts from here */
 
        __CPUINIT
 NESTED(nlm_boot_secondary_cpus, 16, sp)
+       /* Initialize CP0 Status */
+       move    t1, zero
+#ifdef CONFIG_64BIT
+       ori     t1, ST0_KX
+#endif
+       mtc0    t1, CP0_STATUS
        PTR_LA  t1, nlm_next_sp
        PTR_L   sp, 0(t1)
        PTR_LA  t1, nlm_next_gp
@@ -234,36 +281,36 @@ END(nlm_boot_secondary_cpus)
  */
        __CPUINIT
 NESTED(nlm_rmiboot_preboot, 16, sp)
-       mfc0    t0, $15, 1      # read ebase
-       andi    t0, 0x1f        # t0 has the processor_id()
-       andi    t2, t0, 0x3     # thread no
-       sll     t0, 2           # offset in cpu array
+       mfc0    t0, $15, 1      /* read ebase */
+       andi    t0, 0x1f        /* t0 has the processor_id() */
+       andi    t2, t0, 0x3     /* thread num */
+       sll     t0, 2           /* offset in cpu array */
 
-       PTR_LA  t1, nlm_cpu_ready # mark CPU ready
+       PTR_LA  t1, nlm_cpu_ready /* mark CPU ready */
        PTR_ADDU t1, t0
        li      t3, 1
        sw      t3, 0(t1)
 
-       bnez    t2, 1f          # skip thread programming
-       nop                     # for non zero hw threads
+       bnez    t2, 1f          /* skip thread programming */
+       nop                     /* for thread id != 0 */
 
        /*
-        * MMU setup only for first thread in core
+        * XLR MMU setup only for first thread in core
         */
        li      t0, 0x400
        mfcr    t1, t0
-       li      t2, 6           # XLR thread mode mask
+       li      t2, 6           /* XLR thread mode mask */
        nor     t3, t2, zero
-       and     t2, t1, t2      # t2 - current thread mode
+       and     t2, t1, t2      /* t2 - current thread mode */
        li      v0, CKSEG1ADDR(RESET_DATA_PHYS)
-       lw      v1, BOOT_THREAD_MODE(v0) # v1 - new thread mode
+       lw      v1, BOOT_THREAD_MODE(v0) /* v1 - new thread mode */
        sll     v1, 1
-       beq     v1, t2, 1f      # same as request value
-       nop                     # nothing to do */
+       beq     v1, t2, 1f      /* same as request value */
+       nop                     /* nothing to do */
 
-       and     t2, t1, t3      # mask out old thread mode
-       or      t1, t2, v1      # put in new value
-       mtcr    t1, t0          # update core control
+       and     t2, t1, t3      /* mask out old thread mode */
+       or      t1, t2, v1      /* put in new value */
+       mtcr    t1, t0          /* update core control */
 
 1:     wait
        j       1b
index b93ed83474ecec308585e811b66bf32c59da2410..6b4b972218f040ab9e13b33b6a9b5bd9bee6f2e9 100644 (file)
@@ -1,2 +1,4 @@
 obj-y                          += setup.o platform.o nlm_hal.o
+obj-$(CONFIG_OF)               += of.o
 obj-$(CONFIG_SMP)              += wakeup.o
+obj-$(CONFIG_USB)              += usb-init.o
index 9428e7125fed5d08f8f7161479187e51f8c288c3..6c65ac7019125d5cbfedbb72727fcf4c9614dd2f 100644 (file)
@@ -69,6 +69,32 @@ int nlm_irq_to_irt(int irq)
                return PIC_IRT_UART_0_INDEX;
        case PIC_UART_1_IRQ:
                return PIC_IRT_UART_1_INDEX;
+       case PIC_PCIE_LINK_0_IRQ:
+              return PIC_IRT_PCIE_LINK_0_INDEX;
+       case PIC_PCIE_LINK_1_IRQ:
+              return PIC_IRT_PCIE_LINK_1_INDEX;
+       case PIC_PCIE_LINK_2_IRQ:
+              return PIC_IRT_PCIE_LINK_2_INDEX;
+       case PIC_PCIE_LINK_3_IRQ:
+              return PIC_IRT_PCIE_LINK_3_INDEX;
+       case PIC_EHCI_0_IRQ:
+              return PIC_IRT_EHCI_0_INDEX;
+       case PIC_EHCI_1_IRQ:
+              return PIC_IRT_EHCI_1_INDEX;
+       case PIC_OHCI_0_IRQ:
+              return PIC_IRT_OHCI_0_INDEX;
+       case PIC_OHCI_1_IRQ:
+              return PIC_IRT_OHCI_1_INDEX;
+       case PIC_OHCI_2_IRQ:
+              return PIC_IRT_OHCI_2_INDEX;
+       case PIC_OHCI_3_IRQ:
+              return PIC_IRT_OHCI_3_INDEX;
+       case PIC_MMC_IRQ:
+              return PIC_IRT_MMC_INDEX;
+       case PIC_I2C_0_IRQ:
+               return PIC_IRT_I2C_0_INDEX;
+       case PIC_I2C_1_IRQ:
+               return PIC_IRT_I2C_1_INDEX;
        default:
                return -1;
        }
@@ -81,6 +107,32 @@ int nlm_irt_to_irq(int irt)
                return PIC_UART_0_IRQ;
        case PIC_IRT_UART_1_INDEX:
                return PIC_UART_1_IRQ;
+       case PIC_IRT_PCIE_LINK_0_INDEX:
+              return PIC_PCIE_LINK_0_IRQ;
+       case PIC_IRT_PCIE_LINK_1_INDEX:
+              return PIC_PCIE_LINK_1_IRQ;
+       case PIC_IRT_PCIE_LINK_2_INDEX:
+              return PIC_PCIE_LINK_2_IRQ;
+       case PIC_IRT_PCIE_LINK_3_INDEX:
+              return PIC_PCIE_LINK_3_IRQ;
+       case PIC_IRT_EHCI_0_INDEX:
+               return PIC_EHCI_0_IRQ;
+       case PIC_IRT_EHCI_1_INDEX:
+               return PIC_EHCI_1_IRQ;
+       case PIC_IRT_OHCI_0_INDEX:
+               return PIC_OHCI_0_IRQ;
+       case PIC_IRT_OHCI_1_INDEX:
+               return PIC_OHCI_1_IRQ;
+       case PIC_IRT_OHCI_2_INDEX:
+               return PIC_OHCI_2_IRQ;
+       case PIC_IRT_OHCI_3_INDEX:
+               return PIC_OHCI_3_IRQ;
+       case PIC_IRT_MMC_INDEX:
+              return PIC_MMC_IRQ;
+       case PIC_IRT_I2C_0_INDEX:
+               return PIC_I2C_0_IRQ;
+       case PIC_IRT_I2C_1_INDEX:
+               return PIC_I2C_1_IRQ;
        default:
                return -1;
        }
diff --git a/arch/mips/netlogic/xlp/of.c b/arch/mips/netlogic/xlp/of.c
new file mode 100644 (file)
index 0000000..8e3921c
--- /dev/null
@@ -0,0 +1,34 @@
+#include <linux/bootmem.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of_fdt.h>
+#include <asm/byteorder.h>
+
+static int __init reserve_mem_mach(unsigned long addr, unsigned long size)
+{
+       return reserve_bootmem(addr, size, BOOTMEM_DEFAULT);
+}
+
+void __init free_mem_mach(unsigned long addr, unsigned long size)
+{
+       return free_bootmem(addr, size);
+}
+
+void __init device_tree_init(void)
+{
+       unsigned long base, size;
+
+       if (!initial_boot_params)
+               return;
+
+       base = virt_to_phys((void *)initial_boot_params);
+       size = be32_to_cpu(initial_boot_params->totalsize);
+
+       /* Before we do anything, lets reserve the dt blob */
+       reserve_mem_mach(base, size);
+
+       unflatten_device_tree();
+
+       /* free the space reserved for the dt blob */
+       free_mem_mach(base, size);
+}
index 1f5e4cba891d08bbfdbed4331fa99433860a9179..2c510d5854476d6a8b8a8c25e6ab4eb00ff183a9 100644 (file)
@@ -53,7 +53,7 @@
 
 static unsigned int nlm_xlp_uart_in(struct uart_port *p, int offset)
 {
-        return nlm_read_reg(p->iobase, offset);
+       return nlm_read_reg(p->iobase, offset);
 }
 
 static void nlm_xlp_uart_out(struct uart_port *p, int offset, int value)
index b3df7c2aad1e144fe8be92584272b14aed53973f..3dec9f28b65be488d6ac2ce63ca7f1dbe0e7e80f 100644 (file)
@@ -41,6 +41,8 @@
 #include <asm/bootinfo.h>
 
 #include <linux/of_fdt.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
 
 #include <asm/netlogic/haldefs.h>
 #include <asm/netlogic/common.h>
@@ -109,3 +111,17 @@ void __init prom_init(void)
        register_smp_ops(&nlm_smp_ops);
 #endif
 }
+
+static struct of_device_id __initdata xlp_ids[] = {
+       { .compatible = "simple-bus", },
+       {},
+};
+
+int __init xlp8xx_ds_publish_devices(void)
+{
+       if (!of_have_populated_dt())
+               return 0;
+       return of_platform_bus_probe(NULL, xlp_ids, NULL);
+}
+
+device_initcall(xlp8xx_ds_publish_devices);
diff --git a/arch/mips/netlogic/xlp/usb-init.c b/arch/mips/netlogic/xlp/usb-init.c
new file mode 100644 (file)
index 0000000..dbe083a
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2003-2012 Broadcom Corporation
+ * All Rights Reserved
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the Broadcom
+ * license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#include <asm/netlogic/haldefs.h>
+#include <asm/netlogic/xlp-hal/iomap.h>
+#include <asm/netlogic/xlp-hal/xlp.h>
+#include <asm/netlogic/xlp-hal/usb.h>
+
+static void nlm_usb_intr_en(int node, int port)
+{
+       uint32_t val;
+       uint64_t port_addr;
+
+       port_addr = nlm_get_usb_regbase(node, port);
+       val = nlm_read_usb_reg(port_addr, USB_INT_EN);
+       val = USB_CTRL_INTERRUPT_EN  | USB_OHCI_INTERRUPT_EN |
+               USB_OHCI_INTERRUPT1_EN | USB_CTRL_INTERRUPT_EN  |
+               USB_OHCI_INTERRUPT_EN | USB_OHCI_INTERRUPT2_EN;
+       nlm_write_usb_reg(port_addr, USB_INT_EN, val);
+}
+
+static void nlm_usb_hw_reset(int node, int port)
+{
+       uint64_t port_addr;
+       uint32_t val;
+
+       /* reset USB phy */
+       port_addr = nlm_get_usb_regbase(node, port);
+       val = nlm_read_usb_reg(port_addr, USB_PHY_0);
+       val &= ~(USB_PHY_RESET | USB_PHY_PORT_RESET_0 | USB_PHY_PORT_RESET_1);
+       nlm_write_usb_reg(port_addr, USB_PHY_0, val);
+
+       mdelay(100);
+       val = nlm_read_usb_reg(port_addr, USB_CTL_0);
+       val &= ~(USB_CONTROLLER_RESET);
+       val |= 0x4;
+       nlm_write_usb_reg(port_addr, USB_CTL_0, val);
+}
+
+static int __init nlm_platform_usb_init(void)
+{
+       pr_info("Initializing USB Interface\n");
+       nlm_usb_hw_reset(0, 0);
+       nlm_usb_hw_reset(0, 3);
+
+       /* Enable PHY interrupts */
+       nlm_usb_intr_en(0, 0);
+       nlm_usb_intr_en(0, 3);
+
+       return 0;
+}
+
+arch_initcall(nlm_platform_usb_init);
+
+static u64 xlp_usb_dmamask = ~(u32)0;
+
+/* Fixup the IRQ for USB devices which is exist on XLP SOC PCIE bus */
+static void nlm_usb_fixup_final(struct pci_dev *dev)
+{
+       dev->dev.dma_mask               = &xlp_usb_dmamask;
+       dev->dev.coherent_dma_mask      = DMA_BIT_MASK(64);
+       switch (dev->devfn) {
+       case 0x10:
+              dev->irq = PIC_EHCI_0_IRQ;
+              break;
+       case 0x11:
+              dev->irq = PIC_OHCI_0_IRQ;
+              break;
+       case 0x12:
+              dev->irq = PIC_OHCI_1_IRQ;
+              break;
+       case 0x13:
+              dev->irq = PIC_EHCI_1_IRQ;
+              break;
+       case 0x14:
+              dev->irq = PIC_OHCI_2_IRQ;
+              break;
+       case 0x15:
+              dev->irq = PIC_OHCI_3_IRQ;
+              break;
+       }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_NETLOGIC, PCI_DEVICE_ID_NLM_EHCI,
+               nlm_usb_fixup_final);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_NETLOGIC, PCI_DEVICE_ID_NLM_OHCI,
+               nlm_usb_fixup_final);
index f01e4d7a060031e57de49439e3967067c71d4546..c287dea87570922cb1c973b58ae75b5c26d9b8d5 100644 (file)
@@ -1,2 +1,2 @@
-obj-y                          += setup.o platform.o
+obj-y                          += setup.o platform.o platform-flash.o
 obj-$(CONFIG_SMP)              += wakeup.o
diff --git a/arch/mips/netlogic/xlr/platform-flash.c b/arch/mips/netlogic/xlr/platform-flash.c
new file mode 100644 (file)
index 0000000..340ab16
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2011, Netlogic Microsystems.
+ * Copyright 2004, Matt Porter <mporter@kernel.crashing.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/resource.h>
+#include <linux/spi/flash.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/physmap.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/netlogic/haldefs.h>
+#include <asm/netlogic/xlr/iomap.h>
+#include <asm/netlogic/xlr/flash.h>
+#include <asm/netlogic/xlr/bridge.h>
+#include <asm/netlogic/xlr/gpio.h>
+#include <asm/netlogic/xlr/xlr.h>
+
+/*
+ * Default NOR partition layout
+ */
+static struct mtd_partition xlr_nor_parts[] = {
+       {
+               .name = "User FS",
+               .offset = 0x800000,
+               .size   = MTDPART_SIZ_FULL,
+       }
+};
+
+/*
+ * Default NAND partition layout
+ */
+static struct mtd_partition xlr_nand_parts[] = {
+       {
+               .name   = "Root Filesystem",
+               .offset = 64 * 64 * 2048,
+               .size   = 432 * 64 * 2048,
+       },
+       {
+               .name   = "Home Filesystem",
+               .offset = MTDPART_OFS_APPEND,
+               .size   = MTDPART_SIZ_FULL,
+       },
+};
+
+/* Use PHYSMAP flash for NOR */
+struct physmap_flash_data xlr_nor_data = {
+       .width          = 2,
+       .parts          = xlr_nor_parts,
+       .nr_parts       = ARRAY_SIZE(xlr_nor_parts),
+};
+
+static struct resource xlr_nor_res[] = {
+       {
+               .flags  = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device xlr_nor_dev = {
+       .name   = "physmap-flash",
+       .dev    = {
+               .platform_data  = &xlr_nor_data,
+       },
+       .num_resources  = ARRAY_SIZE(xlr_nor_res),
+       .resource       = xlr_nor_res,
+};
+
+const char *xlr_part_probes[] = { "cmdlinepart", NULL };
+
+/*
+ * Use "gen_nand" driver for NAND flash
+ *
+ * There seems to be no way to store a private pointer containing
+ * platform specific info in gen_nand drivier. We will use a global
+ * struct for now, since we currently have only one NAND chip per board.
+ */
+struct xlr_nand_flash_priv {
+       int cs;
+       uint64_t flash_mmio;
+};
+
+static struct xlr_nand_flash_priv nand_priv;
+
+static void xlr_nand_ctrl(struct mtd_info *mtd, int cmd,
+               unsigned int ctrl)
+{
+       if (ctrl & NAND_CLE)
+               nlm_write_reg(nand_priv.flash_mmio,
+                       FLASH_NAND_CLE(nand_priv.cs), cmd);
+       else if (ctrl & NAND_ALE)
+               nlm_write_reg(nand_priv.flash_mmio,
+                       FLASH_NAND_ALE(nand_priv.cs), cmd);
+}
+
+struct platform_nand_data xlr_nand_data = {
+       .chip = {
+               .nr_chips       = 1,
+               .nr_partitions  = ARRAY_SIZE(xlr_nand_parts),
+               .chip_delay     = 50,
+               .partitions     = xlr_nand_parts,
+               .part_probe_types = xlr_part_probes,
+       },
+       .ctrl = {
+               .cmd_ctrl       = xlr_nand_ctrl,
+       },
+};
+
+static struct resource xlr_nand_res[] = {
+       {
+               .flags          = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device xlr_nand_dev = {
+       .name           = "gen_nand",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(xlr_nand_res),
+       .resource       = xlr_nand_res,
+       .dev            = {
+               .platform_data  = &xlr_nand_data,
+       }
+};
+
+/*
+ * XLR/XLS supports upto 8 devices on its FLASH interface. The value in
+ * FLASH_BAR (on the MEM/IO bridge) gives the base for mapping all the
+ * flash devices.
+ * Under this, each flash device has an offset and size given by the
+ * CSBASE_ADDR and CSBASE_MASK registers for the device.
+ *
+ * The CSBASE_ registers are expected to be setup by the bootloader.
+ */
+static void setup_flash_resource(uint64_t flash_mmio,
+       uint64_t flash_map_base, int cs, struct resource *res)
+{
+       u32 base, mask;
+
+       base = nlm_read_reg(flash_mmio, FLASH_CSBASE_ADDR(cs));
+       mask = nlm_read_reg(flash_mmio, FLASH_CSADDR_MASK(cs));
+
+       res->start = flash_map_base + ((unsigned long)base << 16);
+       res->end = res->start + (mask + 1) * 64 * 1024;
+}
+
+static int __init xlr_flash_init(void)
+{
+       uint64_t gpio_mmio, flash_mmio, flash_map_base;
+       u32 gpio_resetcfg, flash_bar;
+       int cs, boot_nand, boot_nor;
+
+       /* Flash address bits 39:24 is in bridge flash BAR */
+       flash_bar = nlm_read_reg(nlm_io_base, BRIDGE_FLASH_BAR);
+       flash_map_base = (flash_bar & 0xffff0000) << 8;
+
+       gpio_mmio = nlm_mmio_base(NETLOGIC_IO_GPIO_OFFSET);
+       flash_mmio = nlm_mmio_base(NETLOGIC_IO_FLASH_OFFSET);
+
+       /* Get the chip reset config */
+       gpio_resetcfg = nlm_read_reg(gpio_mmio, GPIO_PWRON_RESET_CFG_REG);
+
+       /* Check for boot flash type */
+       boot_nor = boot_nand = 0;
+       if (nlm_chip_is_xls()) {
+               /* On XLS, check boot from NAND bit (GPIO reset reg bit 16) */
+               if (gpio_resetcfg & (1 << 16))
+                       boot_nand = 1;
+
+               /* check boot from PCMCIA, (GPIO reset reg bit 15 */
+               if ((gpio_resetcfg & (1 << 15)) == 0)
+                       boot_nor = 1;   /* not set, booted from NOR */
+       } else { /* XLR */
+               /* check boot from PCMCIA (bit 16 in GPIO reset on XLR) */
+               if ((gpio_resetcfg & (1 << 16)) == 0)
+                       boot_nor = 1;   /* not set, booted from NOR */
+       }
+
+       /* boot flash at chip select 0 */
+       cs = 0;
+
+       if (boot_nand) {
+               nand_priv.cs = cs;
+               nand_priv.flash_mmio = flash_mmio;
+               setup_flash_resource(flash_mmio, flash_map_base, cs,
+                        xlr_nand_res);
+
+               /* Initialize NAND flash at CS 0 */
+               nlm_write_reg(flash_mmio, FLASH_CSDEV_PARM(cs),
+                               FLASH_NAND_CSDEV_PARAM);
+               nlm_write_reg(flash_mmio, FLASH_CSTIME_PARMA(cs),
+                               FLASH_NAND_CSTIME_PARAMA);
+               nlm_write_reg(flash_mmio, FLASH_CSTIME_PARMB(cs),
+                               FLASH_NAND_CSTIME_PARAMB);
+
+               pr_info("ChipSelect %d: NAND Flash %pR\n", cs, xlr_nand_res);
+               return platform_device_register(&xlr_nand_dev);
+       }
+
+       if (boot_nor) {
+               setup_flash_resource(flash_mmio, flash_map_base, cs,
+                       xlr_nor_res);
+               pr_info("ChipSelect %d: NOR Flash %pR\n", cs, xlr_nor_res);
+               return platform_device_register(&xlr_nor_dev);
+       }
+       return 0;
+}
+
+arch_initcall(xlr_flash_init);
index eab64b45dffdb197510ee23683667365cbf81d9c..71b44d82621db706844aacfb52f698128a6f58b6 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/resource.h>
 #include <linux/serial_8250.h>
 #include <linux/serial_reg.h>
+#include <linux/i2c.h>
 
 #include <asm/netlogic/haldefs.h>
 #include <asm/netlogic/xlr/iomap.h>
@@ -97,3 +98,142 @@ static int __init nlm_uart_init(void)
 }
 
 arch_initcall(nlm_uart_init);
+
+#ifdef CONFIG_USB
+/* Platform USB devices, only on XLS chips */
+static u64 xls_usb_dmamask = ~(u32)0;
+#define USB_PLATFORM_DEV(n, i, irq)                                    \
+       {                                                               \
+               .name           = n,                                    \
+               .id             = i,                                    \
+               .num_resources  = 2,                                    \
+               .dev            = {                                     \
+                       .dma_mask       = &xls_usb_dmamask,             \
+                       .coherent_dma_mask = 0xffffffff,                \
+               },                                                      \
+               .resource       = (struct resource[]) {                 \
+                       {                                               \
+                               .flags = IORESOURCE_MEM,                \
+                       },                                              \
+                       {                                               \
+                               .start  = irq,                          \
+                               .end    = irq,                          \
+                               .flags = IORESOURCE_IRQ,                \
+                       },                                              \
+               },                                                      \
+       }
+
+static struct platform_device xls_usb_ehci_device =
+                        USB_PLATFORM_DEV("ehci-xls", 0, PIC_USB_IRQ);
+static struct platform_device xls_usb_ohci_device_0 =
+                        USB_PLATFORM_DEV("ohci-xls-0", 1, PIC_USB_IRQ);
+static struct platform_device xls_usb_ohci_device_1 =
+                        USB_PLATFORM_DEV("ohci-xls-1", 2, PIC_USB_IRQ);
+
+static struct platform_device *xls_platform_devices[] = {
+       &xls_usb_ehci_device,
+       &xls_usb_ohci_device_0,
+       &xls_usb_ohci_device_1,
+};
+
+int xls_platform_usb_init(void)
+{
+       uint64_t usb_mmio, gpio_mmio;
+       unsigned long memres;
+       uint32_t val;
+
+       if (!nlm_chip_is_xls())
+               return 0;
+
+       gpio_mmio = nlm_mmio_base(NETLOGIC_IO_GPIO_OFFSET);
+       usb_mmio  = nlm_mmio_base(NETLOGIC_IO_USB_1_OFFSET);
+
+       /* Clear Rogue Phy INTs */
+       nlm_write_reg(usb_mmio, 49, 0x10000000);
+       /* Enable all interrupts */
+       nlm_write_reg(usb_mmio, 50, 0x1f000000);
+
+       /* Enable ports */
+       nlm_write_reg(usb_mmio,  1, 0x07000500);
+
+       val = nlm_read_reg(gpio_mmio, 21);
+       if (((val >> 22) & 0x01) == 0) {
+               pr_info("Detected USB Device mode - Not supported!\n");
+               nlm_write_reg(usb_mmio,  0, 0x01000000);
+               return 0;
+       }
+
+       pr_info("Detected USB Host mode - Adding XLS USB devices.\n");
+       /* Clear reset, host mode */
+       nlm_write_reg(usb_mmio,  0, 0x02000000);
+
+       /* Memory resource for various XLS usb ports */
+       usb_mmio = nlm_mmio_base(NETLOGIC_IO_USB_0_OFFSET);
+       memres = CPHYSADDR((unsigned long)usb_mmio);
+       xls_usb_ehci_device.resource[0].start = memres;
+       xls_usb_ehci_device.resource[0].end = memres + 0x400 - 1;
+
+       memres += 0x400;
+       xls_usb_ohci_device_0.resource[0].start = memres;
+       xls_usb_ohci_device_0.resource[0].end = memres + 0x400 - 1;
+
+       memres += 0x400;
+       xls_usb_ohci_device_1.resource[0].start = memres;
+       xls_usb_ohci_device_1.resource[0].end = memres + 0x400 - 1;
+
+       return platform_add_devices(xls_platform_devices,
+                               ARRAY_SIZE(xls_platform_devices));
+}
+
+arch_initcall(xls_platform_usb_init);
+#endif
+
+#ifdef CONFIG_I2C
+static struct i2c_board_info nlm_i2c_board_info1[] __initdata = {
+       /* All XLR boards have this RTC and Max6657 Temp Chip */
+       [0] = {
+               .type   = "ds1374",
+               .addr   = 0x68
+       },
+       [1] = {
+               .type   = "lm90",
+               .addr   = 0x4c
+       },
+};
+
+static struct resource i2c_resources[] = {
+       [0] = {
+               .start  = 0,    /* filled at init */
+               .end    = 0,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device nlm_xlr_i2c_1 = {
+       .name           = "xlr-i2cbus",
+       .id             = 1,
+       .num_resources  = 1,
+       .resource       = i2c_resources,
+};
+
+static int __init nlm_i2c_init(void)
+{
+       int err = 0;
+       unsigned int offset;
+
+       /* I2C bus 0 does not have any useful devices, configure only bus 1 */
+       offset = NETLOGIC_IO_I2C_1_OFFSET;
+       nlm_xlr_i2c_1.resource[0].start = CPHYSADDR(nlm_mmio_base(offset));
+       nlm_xlr_i2c_1.resource[0].end = nlm_xlr_i2c_1.resource[0].start + 0xfff;
+
+       platform_device_register(&nlm_xlr_i2c_1);
+
+       err = i2c_register_board_info(1, nlm_i2c_board_info1,
+                               ARRAY_SIZE(nlm_i2c_board_info1));
+       if (err < 0)
+               pr_err("nlm-i2c: cannot register board I2C devices\n");
+       return err;
+}
+
+arch_initcall(nlm_i2c_init);
+#endif
index c9d066dedc4ec4f79601bd3a7cd1a60be4316f5b..81b1d311834f521bbd9c1b15d2bf7dbed1568011 100644 (file)
@@ -85,7 +85,7 @@ static void nlm_linux_exit(void)
 
        gpiobase = nlm_mmio_base(NETLOGIC_IO_GPIO_OFFSET);
        /* trigger a chip reset by writing 1 to GPIO_SWRESET_REG */
-       nlm_write_reg(gpiobase, NETLOGIC_GPIO_SWRESET_REG, 1);
+       nlm_write_reg(gpiobase, GPIO_SWRESET_REG, 1);
        for ( ; ; )
                cpu_wait();
 }
index b6e378211a2c940021fb2e7befb342b90b1b1777..f80480a5a0328b3a79d45950c2679577d0345e4a 100644 (file)
@@ -85,6 +85,7 @@ int __init oprofile_arch_init(struct oprofile_operations *ops)
        case CPU_34K:
        case CPU_1004K:
        case CPU_74K:
+       case CPU_LOONGSON1:
        case CPU_SB1:
        case CPU_SB1A:
        case CPU_R10000:
index 4d80a856048d19261e3af077ff1ff5344b3b26d2..28ea1a4cc576c0bb8e566a63a7d4d0caf9221692 100644 (file)
@@ -339,12 +339,6 @@ static int __init mipsxx_init(void)
                break;
 
        case CPU_1004K:
-#if 0
-               /* FIXME: report as 34K for now */
-               op_model_mipsxx_ops.cpu_type = "mips/1004K";
-               break;
-#endif
-
        case CPU_34K:
                op_model_mipsxx_ops.cpu_type = "mips/34K";
                break;
@@ -374,6 +368,10 @@ static int __init mipsxx_init(void)
                op_model_mipsxx_ops.cpu_type = "mips/sb1";
                break;
 
+       case CPU_LOONGSON1:
+               op_model_mipsxx_ops.cpu_type = "mips/loongson1";
+               break;
+
        default:
                printk(KERN_ERR "Profiling unsupported for this CPU\n");
 
index c703f43a9914bee08452c9db536b53b82fc9f037..e13a71cbc3c7550d5f9980f59ad1afe5d684dd27 100644 (file)
@@ -59,6 +59,7 @@ obj-$(CONFIG_WR_PPMC)         += fixup-wrppmc.o
 obj-$(CONFIG_MIKROTIK_RB532)   += pci-rc32434.o ops-rc32434.o fixup-rc32434.o
 obj-$(CONFIG_CPU_CAVIUM_OCTEON)        += pci-octeon.o pcie-octeon.o
 obj-$(CONFIG_CPU_XLR)          += pci-xlr.o
+obj-$(CONFIG_CPU_XLP)          += pci-xlp.o
 
 ifdef CONFIG_PCI_MSI
 obj-$(CONFIG_CPU_CAVIUM_OCTEON)        += msi-octeon.o
index 9553b14002dda51a757cf06ccb0bca5483b86c1a..3e7ce65d776c83ff32a935d38320153c913f54b9 100644 (file)
@@ -37,7 +37,7 @@
 #define VIA_COBALT_BRD_ID_REG  0x94
 #define VIA_COBALT_BRD_REG_to_ID(reg)  ((unsigned char)(reg) >> 4)
 
-static void qube_raq_galileo_early_fixup(struct pci_dev *dev)
+static void __devinit qube_raq_galileo_early_fixup(struct pci_dev *dev)
 {
        if (dev->devfn == PCI_DEVFN(0, 0) &&
                (dev->class >> 8) == PCI_CLASS_MEMORY_OTHER) {
@@ -51,7 +51,7 @@ static void qube_raq_galileo_early_fixup(struct pci_dev *dev)
 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_GT64111,
         qube_raq_galileo_early_fixup);
 
-static void qube_raq_via_bmIDE_fixup(struct pci_dev *dev)
+static void __devinit qube_raq_via_bmIDE_fixup(struct pci_dev *dev)
 {
        unsigned short cfgword;
        unsigned char lt;
@@ -74,7 +74,7 @@ static void qube_raq_via_bmIDE_fixup(struct pci_dev *dev)
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1,
         qube_raq_via_bmIDE_fixup);
 
-static void qube_raq_galileo_fixup(struct pci_dev *dev)
+static void __devinit qube_raq_galileo_fixup(struct pci_dev *dev)
 {
        if (dev->devfn != PCI_DEVFN(0, 0))
                return;
@@ -129,7 +129,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_GT64111,
 
 int cobalt_board_id;
 
-static void qube_raq_via_board_id_fixup(struct pci_dev *dev)
+static void __devinit qube_raq_via_board_id_fixup(struct pci_dev *dev)
 {
        u8 id;
        int retval;
index 70073c98ed320dc6f88f457028c8b9cd094e93ab..819622f93e9cec97a620d24941dcb80549f000ca 100644 (file)
@@ -101,3 +101,17 @@ static void __devinit malta_piix_func1_fixup(struct pci_dev *pdev)
 
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB,
         malta_piix_func1_fixup);
+
+/* Enable PCI 2.1 compatibility in PIIX4 */
+static void __devinit quirk_dlcsetup(struct pci_dev *dev)
+{
+       u8 odlc, ndlc;
+
+       (void) pci_read_config_byte(dev, 0x82, &odlc);
+       /* Enable passive releases and delayed transaction */
+       ndlc = odlc | 7;
+       (void) pci_write_config_byte(dev, 0x82, ndlc);
+}
+
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0,
+       quirk_dlcsetup);
index 3d86823d03a0a5cc158f8d2d7c2bbe7374ee5ca5..76bb1be99d432bc935af5ee93698093dafa42d8a 100644 (file)
@@ -47,7 +47,7 @@ int __devinit pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
        return irq + GROUP4_IRQ_BASE + 4;
 }
 
-static void rc32434_pci_early_fixup(struct pci_dev *dev)
+static void __devinit rc32434_pci_early_fixup(struct pci_dev *dev)
 {
        if (PCI_SLOT(dev->devfn) == 6 && dev->bus->number == 0) {
                /* disable prefetched memory range */
index 822ae179bc56539ea9b9895822fbae12ae0e364b..65c7bd1004860d3e3eb7490d98f78c4718e5be81 100644 (file)
@@ -411,7 +411,7 @@ struct pci_ops bcm63xx_cb_ops = {
  * only one IO window, so it  cannot be shared by PCI and cardbus, use
  * fixup to choose and detect unhandled configuration
  */
-static void bcm63xx_fixup(struct pci_dev *dev)
+static void __devinit bcm63xx_fixup(struct pci_dev *dev)
 {
        static int io_window = -1;
        int i, found, new_io_window;
@@ -465,3 +465,64 @@ static void bcm63xx_fixup(struct pci_dev *dev)
 
 DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, bcm63xx_fixup);
 #endif
+
+static int bcm63xx_pcie_can_access(struct pci_bus *bus, int devfn)
+{
+       switch (bus->number) {
+       case PCIE_BUS_BRIDGE:
+               return (PCI_SLOT(devfn) == 0);
+       case PCIE_BUS_DEVICE:
+               if (PCI_SLOT(devfn) == 0)
+                       return bcm_pcie_readl(PCIE_DLSTATUS_REG)
+                                       & DLSTATUS_PHYLINKUP;
+       default:
+               return false;
+       }
+}
+
+static int bcm63xx_pcie_read(struct pci_bus *bus, unsigned int devfn,
+                            int where, int size, u32 *val)
+{
+       u32 data;
+       u32 reg = where & ~3;
+
+       if (!bcm63xx_pcie_can_access(bus, devfn))
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       if (bus->number == PCIE_BUS_DEVICE)
+               reg += PCIE_DEVICE_OFFSET;
+
+       data = bcm_pcie_readl(reg);
+
+       *val = postprocess_read(data, where, size);
+
+       return PCIBIOS_SUCCESSFUL;
+
+}
+
+static int bcm63xx_pcie_write(struct pci_bus *bus, unsigned int devfn,
+                             int where, int size, u32 val)
+{
+       u32 data;
+       u32 reg = where & ~3;
+
+       if (!bcm63xx_pcie_can_access(bus, devfn))
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       if (bus->number == PCIE_BUS_DEVICE)
+               reg += PCIE_DEVICE_OFFSET;
+
+
+       data = bcm_pcie_readl(reg);
+
+       data = preprocess_write(data, val, where, size);
+       bcm_pcie_writel(data, reg);
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+
+struct pci_ops bcm63xx_pcie_ops = {
+       .read   = bcm63xx_pcie_read,
+       .write  = bcm63xx_pcie_write
+};
index 39eb7c417e2fadf4b1d3999f9b2af9c91934de84..8a48139d219cc2c8918f3f5597333683c3e0ea65 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/pci.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/delay.h>
 #include <asm/bootinfo.h>
 
 #include "pci-bcm63xx.h"
@@ -71,6 +72,26 @@ struct pci_controller bcm63xx_cb_controller = {
 };
 #endif
 
+static struct resource bcm_pcie_mem_resource = {
+       .name   = "bcm63xx PCIe memory space",
+       .start  = BCM_PCIE_MEM_BASE_PA,
+       .end    = BCM_PCIE_MEM_END_PA,
+       .flags  = IORESOURCE_MEM,
+};
+
+static struct resource bcm_pcie_io_resource = {
+       .name   = "bcm63xx PCIe IO space",
+       .start  = 0,
+       .end    = 0,
+       .flags  = 0,
+};
+
+struct pci_controller bcm63xx_pcie_controller = {
+       .pci_ops        = &bcm63xx_pcie_ops,
+       .io_resource    = &bcm_pcie_io_resource,
+       .mem_resource   = &bcm_pcie_mem_resource,
+};
+
 static u32 bcm63xx_int_cfg_readl(u32 reg)
 {
        u32 tmp;
@@ -94,17 +115,99 @@ static void bcm63xx_int_cfg_writel(u32 val, u32 reg)
 
 void __iomem *pci_iospace_start;
 
-static int __init bcm63xx_pci_init(void)
+static void __init bcm63xx_reset_pcie(void)
 {
-       unsigned int mem_size;
        u32 val;
 
-       if (!BCMCPU_IS_6348() && !BCMCPU_IS_6358() && !BCMCPU_IS_6368())
-               return -ENODEV;
+       /* enable clock */
+       val = bcm_perf_readl(PERF_CKCTL_REG);
+       val |= CKCTL_6328_PCIE_EN;
+       bcm_perf_writel(val, PERF_CKCTL_REG);
+
+       /* enable SERDES */
+       val = bcm_misc_readl(MISC_SERDES_CTRL_REG);
+       val |= SERDES_PCIE_EN | SERDES_PCIE_EXD_EN;
+       bcm_misc_writel(val, MISC_SERDES_CTRL_REG);
+
+       /* reset the PCIe core */
+       val = bcm_perf_readl(PERF_SOFTRESET_6328_REG);
+
+       val &= ~SOFTRESET_6328_PCIE_MASK;
+       val &= ~SOFTRESET_6328_PCIE_CORE_MASK;
+       val &= ~SOFTRESET_6328_PCIE_HARD_MASK;
+       val &= ~SOFTRESET_6328_PCIE_EXT_MASK;
+       bcm_perf_writel(val, PERF_SOFTRESET_6328_REG);
+       mdelay(10);
+
+       val |= SOFTRESET_6328_PCIE_MASK;
+       val |= SOFTRESET_6328_PCIE_CORE_MASK;
+       val |= SOFTRESET_6328_PCIE_HARD_MASK;
+       bcm_perf_writel(val, PERF_SOFTRESET_6328_REG);
+       mdelay(10);
+
+       val |= SOFTRESET_6328_PCIE_EXT_MASK;
+       bcm_perf_writel(val, PERF_SOFTRESET_6328_REG);
+       mdelay(200);
+}
 
-       if (!bcm63xx_pci_enabled)
-               return -ENODEV;
+static int __init bcm63xx_register_pcie(void)
+{
+       u32 val;
 
+       bcm63xx_reset_pcie();
+
+       /* configure the PCIe bridge */
+       val = bcm_pcie_readl(PCIE_BRIDGE_OPT1_REG);
+       val |= OPT1_RD_BE_OPT_EN;
+       val |= OPT1_RD_REPLY_BE_FIX_EN;
+       val |= OPT1_PCIE_BRIDGE_HOLE_DET_EN;
+       val |= OPT1_L1_INT_STATUS_MASK_POL;
+       bcm_pcie_writel(val, PCIE_BRIDGE_OPT1_REG);
+
+       /* setup the interrupts */
+       val = bcm_pcie_readl(PCIE_BRIDGE_RC_INT_MASK_REG);
+       val |= PCIE_RC_INT_A | PCIE_RC_INT_B | PCIE_RC_INT_C | PCIE_RC_INT_D;
+       bcm_pcie_writel(val, PCIE_BRIDGE_RC_INT_MASK_REG);
+
+       val = bcm_pcie_readl(PCIE_BRIDGE_OPT2_REG);
+       /* enable credit checking and error checking */
+       val |= OPT2_TX_CREDIT_CHK_EN;
+       val |= OPT2_UBUS_UR_DECODE_DIS;
+
+       /* set device bus/func for the pcie device */
+       val |= (PCIE_BUS_DEVICE << OPT2_CFG_TYPE1_BUS_NO_SHIFT);
+       val |= OPT2_CFG_TYPE1_BD_SEL;
+       bcm_pcie_writel(val, PCIE_BRIDGE_OPT2_REG);
+
+       /* setup class code as bridge */
+       val = bcm_pcie_readl(PCIE_IDVAL3_REG);
+       val &= ~IDVAL3_CLASS_CODE_MASK;
+       val |= (PCI_CLASS_BRIDGE_PCI << IDVAL3_SUBCLASS_SHIFT);
+       bcm_pcie_writel(val, PCIE_IDVAL3_REG);
+
+       /* disable bar1 size */
+       val = bcm_pcie_readl(PCIE_CONFIG2_REG);
+       val &= ~CONFIG2_BAR1_SIZE_MASK;
+       bcm_pcie_writel(val, PCIE_CONFIG2_REG);
+
+       /* set bar0 to little endian */
+       val = (BCM_PCIE_MEM_BASE_PA >> 20) << BASEMASK_BASE_SHIFT;
+       val |= (BCM_PCIE_MEM_BASE_PA >> 20) << BASEMASK_MASK_SHIFT;
+       val |= BASEMASK_REMAP_EN;
+       bcm_pcie_writel(val, PCIE_BRIDGE_BAR0_BASEMASK_REG);
+
+       val = (BCM_PCIE_MEM_BASE_PA >> 20) << REBASE_ADDR_BASE_SHIFT;
+       bcm_pcie_writel(val, PCIE_BRIDGE_BAR0_REBASE_ADDR_REG);
+
+       register_pci_controller(&bcm63xx_pcie_controller);
+
+       return 0;
+}
+
+static int __init bcm63xx_register_pci(void)
+{
+       unsigned int mem_size;
+       u32 val;
        /*
         * configuration  access are  done through  IO space,  remap 4
         * first bytes to access it from CPU.
@@ -221,4 +324,22 @@ static int __init bcm63xx_pci_init(void)
        return 0;
 }
 
+
+static int __init bcm63xx_pci_init(void)
+{
+       if (!bcm63xx_pci_enabled)
+               return -ENODEV;
+
+       switch (bcm63xx_get_cpu_id()) {
+       case BCM6328_CPU_ID:
+               return bcm63xx_register_pcie();
+       case BCM6348_CPU_ID:
+       case BCM6358_CPU_ID:
+       case BCM6368_CPU_ID:
+               return bcm63xx_register_pci();
+       default:
+               return -ENODEV;
+       }
+}
+
 arch_initcall(bcm63xx_pci_init);
index a6e594ef3d6a371300907d141bbfab1a5e0365d7..e6736d558ac746ea7125d831c8644479ca464c91 100644 (file)
  */
 #define CARDBUS_PCI_IDSEL      0x8
 
+
+#define PCIE_BUS_BRIDGE                0
+#define PCIE_BUS_DEVICE                1
+
 /*
  * defined in ops-bcm63xx.c
  */
 extern struct pci_ops bcm63xx_pci_ops;
 extern struct pci_ops bcm63xx_cb_ops;
+extern struct pci_ops bcm63xx_pcie_ops;
 
 /*
  * defined in pci-bcm63xx.c
diff --git a/arch/mips/pci/pci-xlp.c b/arch/mips/pci/pci-xlp.c
new file mode 100644 (file)
index 0000000..140557a
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2003-2012 Broadcom Corporation
+ * All Rights Reserved
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the Broadcom
+ * license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/msi.h>
+#include <linux/mm.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
+#include <linux/console.h>
+
+#include <asm/io.h>
+
+#include <asm/netlogic/interrupt.h>
+#include <asm/netlogic/haldefs.h>
+
+#include <asm/netlogic/xlp-hal/iomap.h>
+#include <asm/netlogic/xlp-hal/pic.h>
+#include <asm/netlogic/xlp-hal/xlp.h>
+#include <asm/netlogic/xlp-hal/pcibus.h>
+#include <asm/netlogic/xlp-hal/bridge.h>
+
+static void *pci_config_base;
+
+#define        pci_cfg_addr(bus, devfn, off) (((bus) << 20) | ((devfn) << 12) | (off))
+
+/* PCI ops */
+static inline u32 pci_cfg_read_32bit(struct pci_bus *bus, unsigned int devfn,
+       int where)
+{
+       u32 data;
+       u32 *cfgaddr;
+
+       cfgaddr = (u32 *)(pci_config_base +
+                       pci_cfg_addr(bus->number, devfn, where & ~3));
+       data = *cfgaddr;
+       return data;
+}
+
+static inline void pci_cfg_write_32bit(struct pci_bus *bus, unsigned int devfn,
+       int where, u32 data)
+{
+       u32 *cfgaddr;
+
+       cfgaddr = (u32 *)(pci_config_base +
+                       pci_cfg_addr(bus->number, devfn, where & ~3));
+       *cfgaddr = data;
+}
+
+static int nlm_pcibios_read(struct pci_bus *bus, unsigned int devfn,
+       int where, int size, u32 *val)
+{
+       u32 data;
+
+       if ((size == 2) && (where & 1))
+               return PCIBIOS_BAD_REGISTER_NUMBER;
+       else if ((size == 4) && (where & 3))
+               return PCIBIOS_BAD_REGISTER_NUMBER;
+
+       data = pci_cfg_read_32bit(bus, devfn, where);
+
+       if (size == 1)
+               *val = (data >> ((where & 3) << 3)) & 0xff;
+       else if (size == 2)
+               *val = (data >> ((where & 3) << 3)) & 0xffff;
+       else
+               *val = data;
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+
+static int nlm_pcibios_write(struct pci_bus *bus, unsigned int devfn,
+               int where, int size, u32 val)
+{
+       u32 data;
+
+       if ((size == 2) && (where & 1))
+               return PCIBIOS_BAD_REGISTER_NUMBER;
+       else if ((size == 4) && (where & 3))
+               return PCIBIOS_BAD_REGISTER_NUMBER;
+
+       data = pci_cfg_read_32bit(bus, devfn, where);
+
+       if (size == 1)
+               data = (data & ~(0xff << ((where & 3) << 3))) |
+                       (val << ((where & 3) << 3));
+       else if (size == 2)
+               data = (data & ~(0xffff << ((where & 3) << 3))) |
+                       (val << ((where & 3) << 3));
+       else
+               data = val;
+
+       pci_cfg_write_32bit(bus, devfn, where, data);
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+struct pci_ops nlm_pci_ops = {
+       .read  = nlm_pcibios_read,
+       .write = nlm_pcibios_write
+};
+
+static struct resource nlm_pci_mem_resource = {
+       .name           = "XLP PCI MEM",
+       .start          = 0xd0000000UL, /* 256MB PCI mem @ 0xd000_0000 */
+       .end            = 0xdfffffffUL,
+       .flags          = IORESOURCE_MEM,
+};
+
+static struct resource nlm_pci_io_resource = {
+       .name           = "XLP IO MEM",
+       .start          = 0x14000000UL, /* 64MB PCI IO @ 0x1000_0000 */
+       .end            = 0x17ffffffUL,
+       .flags          = IORESOURCE_IO,
+};
+
+struct pci_controller nlm_pci_controller = {
+       .index          = 0,
+       .pci_ops        = &nlm_pci_ops,
+       .mem_resource   = &nlm_pci_mem_resource,
+       .mem_offset     = 0x00000000UL,
+       .io_resource    = &nlm_pci_io_resource,
+       .io_offset      = 0x00000000UL,
+};
+
+static int get_irq_vector(const struct pci_dev *dev)
+{
+       /*
+        * For XLP PCIe, there is an IRQ per Link, find out which
+        * link the device is on to assign interrupts
+       */
+       if (dev->bus->self == NULL)
+               return 0;
+
+       switch  (dev->bus->self->devfn) {
+       case 0x8:
+               return PIC_PCIE_LINK_0_IRQ;
+       case 0x9:
+               return PIC_PCIE_LINK_1_IRQ;
+       case 0xa:
+               return PIC_PCIE_LINK_2_IRQ;
+       case 0xb:
+               return PIC_PCIE_LINK_3_IRQ;
+       }
+       WARN(1, "Unexpected devfn %d\n", dev->bus->self->devfn);
+       return 0;
+}
+
+int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+       return get_irq_vector(dev);
+}
+
+/* Do platform specific device initialization at pci_enable_device() time */
+int pcibios_plat_dev_init(struct pci_dev *dev)
+{
+       return 0;
+}
+
+static int xlp_enable_pci_bswap(void)
+{
+       uint64_t pciebase, sysbase;
+       int node, i;
+       u32 reg;
+
+       /* Chip-0 so node set to 0 */
+       node = 0;
+       sysbase = nlm_get_bridge_regbase(node);
+       /*
+        *  Enable byte swap in hardware. Program each link's PCIe SWAP regions
+        * from the link's address ranges.
+        */
+       for (i = 0; i < 4; i++) {
+               pciebase = nlm_pcicfg_base(XLP_IO_PCIE_OFFSET(node, i));
+               if (nlm_read_pci_reg(pciebase, 0) == 0xffffffff)
+                       continue;
+
+               reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEMEM_BASE0 + i);
+               nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_MEM_BASE, reg);
+
+               reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEMEM_LIMIT0 + i);
+               nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_MEM_LIM,
+                       reg | 0xfff);
+
+               reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEIO_BASE0 + i);
+               nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_IO_BASE, reg);
+
+               reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEIO_LIMIT0 + i);
+               nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_IO_LIM, reg | 0xfff);
+       }
+       return 0;
+}
+
+static int __init pcibios_init(void)
+{
+       /* Firmware assigns PCI resources */
+       pci_set_flags(PCI_PROBE_ONLY);
+       pci_config_base = ioremap(XLP_DEFAULT_PCI_ECFG_BASE, 64 << 20);
+
+       /* Extend IO port for memory mapped io */
+       ioport_resource.start =  0;
+       ioport_resource.end   = ~0;
+
+       xlp_enable_pci_bswap();
+       set_io_port_base(CKSEG1);
+       nlm_pci_controller.io_map_base = CKSEG1;
+
+       register_pci_controller(&nlm_pci_controller);
+       pr_info("XLP PCIe Controller %pR%pR.\n", &nlm_pci_io_resource,
+               &nlm_pci_mem_resource);
+
+       return 0;
+}
+arch_initcall(pcibios_init);
index 172af1cd58672e137924e88dae7f37dcf82393be..18af021d289ac83f5930098d20b9d880ae680780 100644 (file)
@@ -375,7 +375,3 @@ static int __init pcibios_init(void)
 }
 
 arch_initcall(pcibios_init);
-
-struct pci_fixup pcibios_fixups[] = {
-       {0}
-};
index 644eb7c3210ff4b794eb4e80c50dedaaba3eabc2..4b328ac430509b73095b24eb806a80003e7124ab 100644 (file)
@@ -91,7 +91,7 @@ void __init pnx833x_board_setup(void)
        pnx833x_gpio_select_function_alt(32);
        pnx833x_gpio_select_function_alt(33);
 
-#if defined(CONFIG_MTD_NAND_PLATFORM) || defined(CONFIG_MTD_NAND_PLATFORM_MODULE)
+#if IS_ENABLED(CONFIG_MTD_NAND_PLATFORM)
        /* Setup MIU for NAND access on CS0...
         *
         * (it seems that we must also configure CS1 for reliable operation,
@@ -117,7 +117,7 @@ void __init pnx833x_board_setup(void)
        pnx833x_gpio_select_output(5);
        pnx833x_gpio_write(1, 5);
 
-#elif defined(CONFIG_MTD_CFI) || defined(CONFIG_MTD_CFI_MODULE)
+#elif IS_ENABLED(CONFIG_MTD_CFI)
 
        /* Set up MIU for 16-bit NOR access on CS0 and CS1... */
 
index b105eca3c02042d2e9319bffce8b7dee68ad6b4c..cd8fcab6b054e7f6c7293b5c10cd503fc85aab45 100644 (file)
@@ -401,6 +401,7 @@ static void __init node_mem_init(cnodeid_t node)
         * Allocate the node data structures on the node first.
         */
        __node_data[node] = __va(slot_freepfn << PAGE_SHIFT);
+       memset(__node_data[node], 0, PAGE_SIZE);
 
        NODE_DATA(node)->bdata = &bootmem_node_data[node];
        NODE_DATA(node)->node_start_pfn = start_pfn;
index 852ae4bb7a85309c1152dd201f4b3d06bdc53591..6d40bc783459fae425577c4da3b145e1907368de 100644 (file)
@@ -20,6 +20,7 @@ config MACH_TXX9
        select SYS_SUPPORTS_32BIT_KERNEL
        select SYS_SUPPORTS_LITTLE_ENDIAN
        select SYS_SUPPORTS_BIG_ENDIAN
+       select HAVE_CLK
 
 config TOSHIBA_JMR3927
        bool "Toshiba JMR-TX3927 board"
index 125db323ab1ec684819c980d5b7a8e2a8f846e00..4efd9185f294643d28aefe755c52c9d52e071f84 100644 (file)
@@ -304,7 +304,7 @@ static void __devinit quirk_slc90e66_bridge(struct pci_dev *dev)
        smsc_fdc37m81x_config_end();
 }
 
-static void quirk_slc90e66_ide(struct pci_dev *dev)
+static void __devinit quirk_slc90e66_ide(struct pci_dev *dev)
 {
        unsigned char dat;
        int regs[2] = {0x41, 0x43};
@@ -339,7 +339,7 @@ static void quirk_slc90e66_ide(struct pci_dev *dev)
 }
 #endif /* CONFIG_TOSHIBA_FPCIB0 */
 
-static void tc35815_fixup(struct pci_dev *dev)
+static void __devinit tc35815_fixup(struct pci_dev *dev)
 {
        /* This device may have PM registers but not they are not suported. */
        if (dev->pm_cap) {
@@ -348,7 +348,7 @@ static void tc35815_fixup(struct pci_dev *dev)
        }
 }
 
-static void final_fixup(struct pci_dev *dev)
+static void __devinit final_fixup(struct pci_dev *dev)
 {
        unsigned char bist;
 
index ae77a7916c03665c5ca03735f18f57822cff1814..560fe89917537972050d9ed8ee09e78a2df736f8 100644 (file)
@@ -632,7 +632,7 @@ void __init txx9_physmap_flash_init(int no, unsigned long addr,
                                    unsigned long size,
                                    const struct physmap_flash_data *pdata)
 {
-#if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP_MODULE)
+#if IS_ENABLED(CONFIG_MTD_PHYSMAP)
        struct resource res = {
                .start = addr,
                .end = addr + size - 1,
@@ -670,8 +670,7 @@ void __init txx9_physmap_flash_init(int no, unsigned long addr,
 void __init txx9_ndfmc_init(unsigned long baseaddr,
                            const struct txx9ndfmc_platform_data *pdata)
 {
-#if defined(CONFIG_MTD_NAND_TXX9NDFMC) || \
-       defined(CONFIG_MTD_NAND_TXX9NDFMC_MODULE)
+#if IS_ENABLED(CONFIG_MTD_NAND_TXX9NDFMC)
        struct resource res = {
                .start = baseaddr,
                .end = baseaddr + 0x1000 - 1,
@@ -687,7 +686,7 @@ void __init txx9_ndfmc_init(unsigned long baseaddr,
 #endif
 }
 
-#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
+#if IS_ENABLED(CONFIG_LEDS_GPIO)
 static DEFINE_SPINLOCK(txx9_iocled_lock);
 
 #define TXX9_IOCLED_MAXLEDS 8
@@ -810,7 +809,7 @@ void __init txx9_iocled_init(unsigned long baseaddr,
 void __init txx9_dmac_init(int id, unsigned long baseaddr, int irq,
                           const struct txx9dmac_platform_data *pdata)
 {
-#if defined(CONFIG_TXX9_DMAC) || defined(CONFIG_TXX9_DMAC_MODULE)
+#if IS_ENABLED(CONFIG_TXX9_DMAC)
        struct resource res[] = {
                {
                        .start = baseaddr,
@@ -866,8 +865,7 @@ void __init txx9_aclc_init(unsigned long baseaddr, int irq,
                           unsigned int dma_chan_out,
                           unsigned int dma_chan_in)
 {
-#if defined(CONFIG_SND_SOC_TXX9ACLC) || \
-       defined(CONFIG_SND_SOC_TXX9ACLC_MODULE)
+#if IS_ENABLED(CONFIG_SND_SOC_TXX9ACLC)
        unsigned int dma_base = dmac_id * TXX9_DMA_MAX_NR_CHANNELS;
        struct resource res[] = {
                {
index 6567895d1f5975315b6f6f141fc4b419e617e9f7..5ff7a9584daf4d4c9cc1fa9c14ae126319a0279d 100644 (file)
@@ -317,7 +317,7 @@ void __init tx4939_sio_init(unsigned int sclk, unsigned int cts_mask)
        }
 }
 
-#if defined(CONFIG_TC35815) || defined(CONFIG_TC35815_MODULE)
+#if IS_ENABLED(CONFIG_TC35815)
 static u32 tx4939_get_eth_speed(struct net_device *dev)
 {
        struct ethtool_cmd cmd;
index 2ad8973ba13d732d8a5770473de98197cf33fe1f..e15641d930922403743cec832774bc347dd06511 100644 (file)
@@ -40,8 +40,7 @@ static void __init rbtx4939_time_init(void)
        tx4939_time_init(0);
 }
 
-#if defined(__BIG_ENDIAN) && \
-       (defined(CONFIG_SMC91X) || defined(CONFIG_SMC91X_MODULE))
+#if defined(__BIG_ENDIAN) && IS_ENABLED(CONFIG_SMC91X)
 #define HAVE_RBTX4939_IOSWAB
 #define IS_CE1_ADDR(addr) \
        ((((unsigned long)(addr) - IO_BASE) & 0xfff00000) == TXX9_CE(1))
@@ -187,7 +186,7 @@ static void __init rbtx4939_update_ioc_pen(void)
 
 #define RBTX4939_MAX_7SEGLEDS  8
 
-#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+#if IS_ENABLED(CONFIG_LEDS_CLASS)
 static u8 led_val[RBTX4939_MAX_7SEGLEDS];
 struct rbtx4939_led_data {
        struct led_classdev cdev;
@@ -263,7 +262,7 @@ static inline void rbtx4939_led_setup(void)
 
 static void __rbtx4939_7segled_putc(unsigned int pos, unsigned char val)
 {
-#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+#if IS_ENABLED(CONFIG_LEDS_CLASS)
        unsigned long flags;
        local_irq_save(flags);
        /* bit7: reserved for LED class */
@@ -287,7 +286,7 @@ static void rbtx4939_7segled_putc(unsigned int pos, unsigned char val)
        __rbtx4939_7segled_putc(pos, val);
 }
 
-#if defined(CONFIG_MTD_RBTX4939) || defined(CONFIG_MTD_RBTX4939_MODULE)
+#if IS_ENABLED(CONFIG_MTD_RBTX4939)
 /* special mapping for boot rom */
 static unsigned long rbtx4939_flash_fixup_ofs(unsigned long ofs)
 {
@@ -463,7 +462,7 @@ static void __init rbtx4939_device_init(void)
                .flags = SMC91X_USE_16BIT,
        };
        struct platform_device *pdev;
-#if defined(CONFIG_TC35815) || defined(CONFIG_TC35815_MODULE)
+#if IS_ENABLED(CONFIG_TC35815)
        int i, j;
        unsigned char ethaddr[2][6];
        u8 bdipsw = readb(rbtx4939_bdipsw_addr) & 0x0f;
index 687f9b4a2ed6cc5fba93f7eda6288a6d10fc7b57..5cfb086b39034417208a3efd4a708c5835c91db7 100644 (file)
@@ -3,6 +3,7 @@ config MN10300
        select HAVE_OPROFILE
        select HAVE_GENERIC_HARDIRQS
        select GENERIC_IRQ_SHOW
+       select ARCH_WANT_IPC_PARSE_VERSION
        select HAVE_ARCH_TRACEHOOK
        select HAVE_ARCH_KGDB
        select HAVE_NMI_WATCHDOG if MN10300_WD_TIMER
diff --git a/arch/mn10300/include/asm/ipc.h b/arch/mn10300/include/asm/ipc.h
deleted file mode 100644 (file)
index a46e3d9..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ipc.h>
index 9051f921cbc7a94252212b625f1161759d054965..866eb14749d7701cb4dafce5566d8aa00df18fe6 100644 (file)
 /*
  * specify the deprecated syscalls we want to support on this arch
  */
-#define __ARCH_WANT_IPC_PARSE_VERSION
 #define __ARCH_WANT_OLD_READDIR
 #define __ARCH_WANT_OLD_STAT
 #define __ARCH_WANT_STAT64
index 3f35c38d7b6498505c1563eef1a8e39adc41ba1f..0922959663a0fd92f0799761500164042e63d74b 100644 (file)
@@ -13,7 +13,6 @@ generic-y += cacheflush.h
 generic-y += checksum.h
 generic-y += cmpxchg.h
 generic-y += cmpxchg-local.h
-generic-y += cpumask.h
 generic-y += cputime.h
 generic-y += current.h
 generic-y += device.h
@@ -43,7 +42,6 @@ generic-y += percpu.h
 generic-y += poll.h
 generic-y += posix_types.h
 generic-y += resource.h
-generic-y += rmap.h
 generic-y += scatterlist.h
 generic-y += sections.h
 generic-y += segment.h
index 9a5d3cdc3e12f16e3dcbd2060e214dbc8ef23881..352f416269ce245c515e25e0cc5cbcf5a446d2a6 100644 (file)
@@ -115,11 +115,13 @@ config PPC
        select HAVE_OPROFILE
        select HAVE_SYSCALL_WRAPPERS if PPC64
        select GENERIC_ATOMIC64 if PPC32
+       select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
        select HAVE_IRQ_WORK
        select HAVE_PERF_EVENTS
        select HAVE_REGS_AND_STACK_ACCESS_API
        select HAVE_HW_BREAKPOINT if PERF_EVENTS && PPC_BOOK3S_64
        select HAVE_GENERIC_HARDIRQS
+       select ARCH_WANT_IPC_PARSE_VERSION
        select SPARSE_IRQ
        select IRQ_PER_CPU
        select IRQ_DOMAIN
index 852e5b27485d4c76b64430c7322a87d23a3189e2..57573bd52caa89243c3379440ae54e89977aec94 100644 (file)
@@ -56,7 +56,7 @@
                ranges = <0x0 0x0 0xffe00000 0x100000>;
        };
 
-       pci0: pcie@ffe08000 {
+       pci2: pcie@ffe08000 {
                reg = <0 0xffe08000 0 0x1000>;
                status = "disabled";
        };
@@ -76,7 +76,7 @@
                };
        };
 
-       pci2: pcie@ffe0a000 {
+       pci0: pcie@ffe0a000 {
                reg = <0 0xffe0a000 0 0x1000>;
                ranges = <0x2000000 0x0 0xe0000000 0 0x80000000 0x0 0x20000000
                          0x1000000 0x0 0x00000000 0 0xffc00000 0x0 0x10000>;
index b5a56ca51cf7d920dc83536601299c0cc17d4788..470247ea68b4d4237d9ab08255fa6e2ecf12d65e 100644 (file)
@@ -56,7 +56,7 @@
                ranges = <0x0 0xf 0xffe00000 0x100000>;
        };
 
-       pci0: pcie@fffe08000 {
+       pci2: pcie@fffe08000 {
                reg = <0xf 0xffe08000 0 0x1000>;
                status = "disabled";
        };
@@ -76,7 +76,7 @@
                };
        };
 
-       pci2: pcie@fffe0a000 {
+       pci0: pcie@fffe0a000 {
                reg = <0xf 0xffe0a000 0 0x1000>;
                ranges = <0x2000000 0x0 0xe0000000 0xc 0x00000000 0x0 0x20000000
                          0x1000000 0x0 0x00000000 0xf 0xffc00000 0x0 0x10000>;
index 22a215e94162362be8525740d0836a14efca4cb9..6cdcadc80c3074db1a667a0a52f952e5ce7558c8 100644 (file)
@@ -58,7 +58,7 @@
                                #size-cells = <1>;
                                compatible = "spansion,s25sl12801";
                                reg = <0>;
-                               spi-max-frequency = <40000000>; /* input clock */
+                               spi-max-frequency = <35000000>; /* input clock */
                                partition@u-boot {
                                        label = "u-boot";
                                        reg = <0x00000000 0x00100000>;
index b1f9597fe312305e40b676d9f8bf3288ecffe49f..29bb11ec6c640677a73c3e16058897789f1fba2f 100644 (file)
@@ -21,8 +21,8 @@ CONFIG_CGROUP_DEVICE=y
 CONFIG_CPUSETS=y
 CONFIG_CGROUP_CPUACCT=y
 CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
-CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
+CONFIG_CGROUP_MEMCG=y
+CONFIG_CGROUP_MEMCG_SWAP=y
 CONFIG_NAMESPACES=y
 CONFIG_RELAY=y
 CONFIG_BLK_DEV_INITRD=y
index 62678e365ca0768e0566329eb957f0058a04897d..78160874809a1e1e5faf8b049baec80a7bd24545 100644 (file)
@@ -27,7 +27,10 @@ extern void *dma_direct_alloc_coherent(struct device *dev, size_t size,
 extern void dma_direct_free_coherent(struct device *dev, size_t size,
                                     void *vaddr, dma_addr_t dma_handle,
                                     struct dma_attrs *attrs);
-
+extern int dma_direct_mmap_coherent(struct device *dev,
+                                   struct vm_area_struct *vma,
+                                   void *cpu_addr, dma_addr_t handle,
+                                   size_t size, struct dma_attrs *attrs);
 
 #ifdef CONFIG_NOT_COHERENT_CACHE
 /*
@@ -207,11 +210,8 @@ static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr)
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
 
-extern int dma_mmap_coherent(struct device *, struct vm_area_struct *,
-                            void *, dma_addr_t, size_t);
 #define ARCH_HAS_DMA_MMAP_COHERENT
 
-
 static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
                enum dma_data_direction direction)
 {
index d3d1b5efd7eb1204405fde26c701e7c52b925fd9..bd377a368611913b55e2ef272da6772540f39215 100644 (file)
 #include <linux/compiler.h>
 #include <linux/linkage.h>
 
-#define __ARCH_WANT_IPC_PARSE_VERSION
 #define __ARCH_WANT_OLD_READDIR
 #define __ARCH_WANT_STAT64
 #define __ARCH_WANT_SYS_ALARM
index bcfdcd22c766f43ebb29dbe495641892215b1cda..2d7bb8ced136c1e618d1e25229553a70512dc927 100644 (file)
@@ -109,6 +109,7 @@ static u64 dma_iommu_get_required_mask(struct device *dev)
 struct dma_map_ops dma_iommu_ops = {
        .alloc                  = dma_iommu_alloc_coherent,
        .free                   = dma_iommu_free_coherent,
+       .mmap                   = dma_direct_mmap_coherent,
        .map_sg                 = dma_iommu_map_sg,
        .unmap_sg               = dma_iommu_unmap_sg,
        .dma_supported          = dma_iommu_dma_supported,
index 4ab88dafb235c8b29955bf1a70a3e04aa255db5a..46943651da23ba25b3e1093ea346fb31e154243c 100644 (file)
@@ -49,6 +49,7 @@ static u64 swiotlb_powerpc_get_required(struct device *dev)
 struct dma_map_ops swiotlb_dma_ops = {
        .alloc = dma_direct_alloc_coherent,
        .free = dma_direct_free_coherent,
+       .mmap = dma_direct_mmap_coherent,
        .map_sg = swiotlb_map_sg_attrs,
        .unmap_sg = swiotlb_unmap_sg_attrs,
        .dma_supported = swiotlb_dma_supported,
index 289be751cd756b71220d90fb7d3820824976bd83..355b9d84b0f8149efd45ed9b5b0035c3b70fac11 100644 (file)
@@ -67,6 +67,24 @@ void dma_direct_free_coherent(struct device *dev, size_t size,
 #endif
 }
 
+int dma_direct_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+                            void *cpu_addr, dma_addr_t handle, size_t size,
+                            struct dma_attrs *attrs)
+{
+       unsigned long pfn;
+
+#ifdef CONFIG_NOT_COHERENT_CACHE
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+       pfn = __dma_get_coherent_pfn((unsigned long)cpu_addr);
+#else
+       pfn = page_to_pfn(virt_to_page(cpu_addr));
+#endif
+       return remap_pfn_range(vma, vma->vm_start,
+                              pfn + vma->vm_pgoff,
+                              vma->vm_end - vma->vm_start,
+                              vma->vm_page_prot);
+}
+
 static int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl,
                             int nents, enum dma_data_direction direction,
                             struct dma_attrs *attrs)
@@ -156,6 +174,7 @@ static inline void dma_direct_sync_single(struct device *dev,
 struct dma_map_ops dma_direct_ops = {
        .alloc                          = dma_direct_alloc_coherent,
        .free                           = dma_direct_free_coherent,
+       .mmap                           = dma_direct_mmap_coherent,
        .map_sg                         = dma_direct_map_sg,
        .unmap_sg                       = dma_direct_unmap_sg,
        .dma_supported                  = dma_direct_dma_supported,
@@ -219,20 +238,3 @@ static int __init dma_init(void)
 }
 fs_initcall(dma_init);
 
-int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
-                     void *cpu_addr, dma_addr_t handle, size_t size)
-{
-       unsigned long pfn;
-
-#ifdef CONFIG_NOT_COHERENT_CACHE
-       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-       pfn = __dma_get_coherent_pfn((unsigned long)cpu_addr);
-#else
-       pfn = page_to_pfn(virt_to_page(cpu_addr));
-#endif
-       return remap_pfn_range(vma, vma->vm_start,
-                              pfn + vma->vm_pgoff,
-                              vma->vm_end - vma->vm_start,
-                              vma->vm_page_prot);
-}
-EXPORT_SYMBOL_GPL(dma_mmap_coherent);
index 3052a931f2b5caca2d32f200a3c844823a73f3b9..02b32216bbc3bb28de3a22b307f6f19aab2cd0a9 100644 (file)
@@ -611,6 +611,7 @@ static u64 vio_dma_get_required_mask(struct device *dev)
 struct dma_map_ops vio_dma_mapping_ops = {
        .alloc             = vio_dma_iommu_alloc_coherent,
        .free              = vio_dma_iommu_free_coherent,
+       .mmap              = dma_direct_mmap_coherent,
        .map_sg            = vio_dma_iommu_map_sg,
        .unmap_sg          = vio_dma_iommu_unmap_sg,
        .map_page          = vio_dma_iommu_map_page,
index ab523f3c1731a0ecf57e3fec538442875d1107ef..9ecf6e35cd8de0de4fe148a7527524a46256c900 100644 (file)
@@ -67,7 +67,6 @@ kvmppc_skip_Hinterrupt:
 #elif defined(CONFIG_PPC_BOOK3S_32)
 
 #define FUNC(name)             name
-#define MTMSR_EERI(reg)                mtmsr   (reg)
 
 .macro INTERRUPT_TRAMPOLINE intno
 
index 89ee02c54561d1802fd72a49afaedd88fc9e2368..3c732acf331dcda708bbc8cc08b28567f5596c44 100644 (file)
@@ -208,6 +208,7 @@ static void p1022ds_set_monitor_port(enum fsl_diu_monitor_port port)
        u8 __iomem *lbc_lcs0_ba = NULL;
        u8 __iomem *lbc_lcs1_ba = NULL;
        phys_addr_t cs0_addr, cs1_addr;
+       u32 br0, or0, br1, or1;
        const __be32 *iprop;
        unsigned int num_laws;
        u8 b;
@@ -256,11 +257,70 @@ static void p1022ds_set_monitor_port(enum fsl_diu_monitor_port port)
        }
        num_laws = be32_to_cpup(iprop);
 
-       cs0_addr = lbc_br_to_phys(ecm, num_laws, in_be32(&lbc->bank[0].br));
-       cs1_addr = lbc_br_to_phys(ecm, num_laws, in_be32(&lbc->bank[1].br));
+       /*
+        * Indirect mode requires both BR0 and BR1 to be set to "GPCM",
+        * otherwise writes to these addresses won't actually appear on the
+        * local bus, and so the PIXIS won't see them.
+        *
+        * In FCM mode, writes go to the NAND controller, which does not pass
+        * them to the localbus directly.  So we force BR0 and BR1 into GPCM
+        * mode, since we don't care about what's behind the localbus any
+        * more.
+        */
+       br0 = in_be32(&lbc->bank[0].br);
+       br1 = in_be32(&lbc->bank[1].br);
+       or0 = in_be32(&lbc->bank[0].or);
+       or1 = in_be32(&lbc->bank[1].or);
+
+       /* Make sure CS0 and CS1 are programmed */
+       if (!(br0 & BR_V) || !(br1 & BR_V)) {
+               pr_err("p1022ds: CS0 and/or CS1 is not programmed\n");
+               goto exit;
+       }
+
+       /*
+        * Use the existing BRx/ORx values if it's already GPCM. Otherwise,
+        * force the values to simple 32KB GPCM windows with the most
+        * conservative timing.
+        */
+       if ((br0 & BR_MSEL) != BR_MS_GPCM) {
+               br0 = (br0 & BR_BA) | BR_V;
+               or0 = 0xFFFF8000 | 0xFF7;
+               out_be32(&lbc->bank[0].br, br0);
+               out_be32(&lbc->bank[0].or, or0);
+       }
+       if ((br1 & BR_MSEL) != BR_MS_GPCM) {
+               br1 = (br1 & BR_BA) | BR_V;
+               or1 = 0xFFFF8000 | 0xFF7;
+               out_be32(&lbc->bank[1].br, br1);
+               out_be32(&lbc->bank[1].or, or1);
+       }
+
+       cs0_addr = lbc_br_to_phys(ecm, num_laws, br0);
+       if (!cs0_addr) {
+               pr_err("p1022ds: could not determine physical address for CS0"
+                      " (BR0=%08x)\n", br0);
+               goto exit;
+       }
+       cs1_addr = lbc_br_to_phys(ecm, num_laws, br1);
+       if (!cs0_addr) {
+               pr_err("p1022ds: could not determine physical address for CS1"
+                      " (BR1=%08x)\n", br1);
+               goto exit;
+       }
 
        lbc_lcs0_ba = ioremap(cs0_addr, 1);
+       if (!lbc_lcs0_ba) {
+               pr_err("p1022ds: could not ioremap CS0 address %llx\n",
+                      (unsigned long long)cs0_addr);
+               goto exit;
+       }
        lbc_lcs1_ba = ioremap(cs1_addr, 1);
+       if (!lbc_lcs1_ba) {
+               pr_err("p1022ds: could not ioremap CS1 address %llx\n",
+                      (unsigned long long)cs1_addr);
+               goto exit;
+       }
 
        /* Make sure we're in indirect mode first. */
        if ((in_be32(&guts->pmuxcr) & PMUXCR_ELBCDIU_MASK) !=
@@ -419,18 +479,6 @@ void __init p1022_ds_pic_init(void)
 
 #if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE)
 
-/*
- * Disables a node in the device tree.
- *
- * This function is called before kmalloc() is available, so the 'new' object
- * should be allocated in the global area.  The easiest way is to do that is
- * to allocate one static local variable for each call to this function.
- */
-static void __init disable_one_node(struct device_node *np, struct property *new)
-{
-       prom_update_property(np, new);
-}
-
 /* TRUE if there is a "video=fslfb" command-line parameter. */
 static bool fslfb;
 
@@ -493,28 +541,58 @@ static void __init p1022_ds_setup_arch(void)
        diu_ops.valid_monitor_port      = p1022ds_valid_monitor_port;
 
        /*
-        * Disable the NOR flash node if there is video=fslfb... command-line
-        * parameter.  When the DIU is active, NOR flash is unavailable, so we
-        * have to disable the node before the MTD driver loads.
+        * Disable the NOR and NAND flash nodes if there is video=fslfb...
+        * command-line parameter.  When the DIU is active, the localbus is
+        * unavailable, so we have to disable these nodes before the MTD
+        * driver loads.
         */
        if (fslfb) {
                struct device_node *np =
                        of_find_compatible_node(NULL, NULL, "fsl,p1022-elbc");
 
                if (np) {
-                       np = of_find_compatible_node(np, NULL, "cfi-flash");
-                       if (np) {
+                       struct device_node *np2;
+
+                       of_node_get(np);
+                       np2 = of_find_compatible_node(np, NULL, "cfi-flash");
+                       if (np2) {
                                static struct property nor_status = {
                                        .name = "status",
                                        .value = "disabled",
                                        .length = sizeof("disabled"),
                                };
 
+                               /*
+                                * prom_update_property() is called before
+                                * kmalloc() is available, so the 'new' object
+                                * should be allocated in the global area.
+                                * The easiest way is to do that is to
+                                * allocate one static local variable for each
+                                * call to this function.
+                                */
                                pr_info("p1022ds: disabling %s node",
-                                       np->full_name);
-                               disable_one_node(np, &nor_status);
-                               of_node_put(np);
+                                       np2->full_name);
+                               prom_update_property(np2, &nor_status);
+                               of_node_put(np2);
                        }
+
+                       of_node_get(np);
+                       np2 = of_find_compatible_node(np, NULL,
+                                                     "fsl,elbc-fcm-nand");
+                       if (np2) {
+                               static struct property nand_status = {
+                                       .name = "status",
+                                       .value = "disabled",
+                                       .length = sizeof("disabled"),
+                               };
+
+                               pr_info("p1022ds: disabling %s node",
+                                       np2->full_name);
+                               prom_update_property(np2, &nand_status);
+                               of_node_put(np2);
+                       }
+
+                       of_node_put(np);
                }
 
        }
index d544d7816df3229780e4f2c58a1a28355c5c4aae..dba1ce235da59e23172cd5d6474d9ce4a72900ac 100644 (file)
@@ -186,10 +186,13 @@ static void spufs_prune_dir(struct dentry *dir)
 static int spufs_rmdir(struct inode *parent, struct dentry *dir)
 {
        /* remove all entries */
+       int res;
        spufs_prune_dir(dir);
        d_drop(dir);
-
-       return simple_rmdir(parent, dir);
+       res = simple_rmdir(parent, dir);
+       /* We have to give up the mm_struct */
+       spu_forget(SPUFS_I(dir->d_inode)->i_ctx);
+       return res;
 }
 
 static int spufs_fill_dir(struct dentry *dir,
@@ -245,9 +248,6 @@ static int spufs_dir_close(struct inode *inode, struct file *file)
        mutex_unlock(&parent->i_mutex);
        WARN_ON(ret);
 
-       /* We have to give up the mm_struct */
-       spu_forget(ctx);
-
        return dcache_dir_close(inode, file);
 }
 
@@ -450,28 +450,24 @@ spufs_create_context(struct inode *inode, struct dentry *dentry,
        struct spu_context *neighbor;
        struct path path = {.mnt = mnt, .dentry = dentry};
 
-       ret = -EPERM;
        if ((flags & SPU_CREATE_NOSCHED) &&
            !capable(CAP_SYS_NICE))
-               goto out_unlock;
+               return -EPERM;
 
-       ret = -EINVAL;
        if ((flags & (SPU_CREATE_NOSCHED | SPU_CREATE_ISOLATE))
            == SPU_CREATE_ISOLATE)
-               goto out_unlock;
+               return -EINVAL;
 
-       ret = -ENODEV;
        if ((flags & SPU_CREATE_ISOLATE) && !isolated_loader)
-               goto out_unlock;
+               return -ENODEV;
 
        gang = NULL;
        neighbor = NULL;
        affinity = flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU);
        if (affinity) {
                gang = SPUFS_I(inode)->i_gang;
-               ret = -EINVAL;
                if (!gang)
-                       goto out_unlock;
+                       return -EINVAL;
                mutex_lock(&gang->aff_mutex);
                neighbor = spufs_assert_affinity(flags, gang, aff_filp);
                if (IS_ERR(neighbor)) {
@@ -492,22 +488,12 @@ spufs_create_context(struct inode *inode, struct dentry *dentry,
        }
 
        ret = spufs_context_open(&path);
-       if (ret < 0) {
+       if (ret < 0)
                WARN_ON(spufs_rmdir(inode, dentry));
-               if (affinity)
-                       mutex_unlock(&gang->aff_mutex);
-               mutex_unlock(&inode->i_mutex);
-               spu_forget(SPUFS_I(dentry->d_inode)->i_ctx);
-               goto out;
-       }
 
 out_aff_unlock:
        if (affinity)
                mutex_unlock(&gang->aff_mutex);
-out_unlock:
-       mutex_unlock(&inode->i_mutex);
-out:
-       dput(dentry);
        return ret;
 }
 
@@ -580,18 +566,13 @@ static int spufs_create_gang(struct inode *inode,
        int ret;
 
        ret = spufs_mkgang(inode, dentry, mode & S_IRWXUGO);
-       if (ret)
-               goto out;
-
-       ret = spufs_gang_open(&path);
-       if (ret < 0) {
-               int err = simple_rmdir(inode, dentry);
-               WARN_ON(err);
+       if (!ret) {
+               ret = spufs_gang_open(&path);
+               if (ret < 0) {
+                       int err = simple_rmdir(inode, dentry);
+                       WARN_ON(err);
+               }
        }
-
-out:
-       mutex_unlock(&inode->i_mutex);
-       dput(dentry);
        return ret;
 }
 
@@ -601,40 +582,32 @@ static struct file_system_type spufs_type;
 long spufs_create(struct path *path, struct dentry *dentry,
                unsigned int flags, umode_t mode, struct file *filp)
 {
+       struct inode *dir = path->dentry->d_inode;
        int ret;
 
-       ret = -EINVAL;
        /* check if we are on spufs */
        if (path->dentry->d_sb->s_type != &spufs_type)
-               goto out;
+               return -EINVAL;
 
        /* don't accept undefined flags */
        if (flags & (~SPU_CREATE_FLAG_ALL))
-               goto out;
+               return -EINVAL;
 
        /* only threads can be underneath a gang */
-       if (path->dentry != path->dentry->d_sb->s_root) {
-               if ((flags & SPU_CREATE_GANG) ||
-                   !SPUFS_I(path->dentry->d_inode)->i_gang)
-                       goto out;
-       }
+       if (path->dentry != path->dentry->d_sb->s_root)
+               if ((flags & SPU_CREATE_GANG) || !SPUFS_I(dir)->i_gang)
+                       return -EINVAL;
 
        mode &= ~current_umask();
 
        if (flags & SPU_CREATE_GANG)
-               ret = spufs_create_gang(path->dentry->d_inode,
-                                        dentry, path->mnt, mode);
+               ret = spufs_create_gang(dir, dentry, path->mnt, mode);
        else
-               ret = spufs_create_context(path->dentry->d_inode,
-                                           dentry, path->mnt, flags, mode,
+               ret = spufs_create_context(dir, dentry, path->mnt, flags, mode,
                                            filp);
        if (ret >= 0)
-               fsnotify_mkdir(path->dentry->d_inode, dentry);
-       return ret;
+               fsnotify_mkdir(dir, dentry);
 
-out:
-       mutex_unlock(&path->dentry->d_inode->i_mutex);
-       dput(dentry);
        return ret;
 }
 
index 5665dcc382c7350208e5c2b084a0e94761ce5051..5b7d8ffbf8907933b21f9be8e4e4333c81a02e9d 100644 (file)
@@ -70,7 +70,7 @@ static long do_spu_create(const char __user *pathname, unsigned int flags,
        ret = PTR_ERR(dentry);
        if (!IS_ERR(dentry)) {
                ret = spufs_create(&path, dentry, flags, mode, neighbor);
-               path_put(&path);
+               done_path_create(&path, dentry);
        }
 
        return ret;
index 60c9c0bd5ba229807badcda45a936b02fc94de53..2aa97ddb7b78841d5ef26bfac393fd9f4c3d01b2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009-2010 Freescale Semiconductor, Inc
+ * Copyright 2009-2010, 2012 Freescale Semiconductor, Inc
  *
  * QorIQ based Cache Controller Memory Mapped Registers
  *
@@ -91,7 +91,7 @@ struct mpc85xx_l2ctlr {
 
 struct sram_parameters {
        unsigned int sram_size;
-       uint64_t sram_offset;
+       phys_addr_t sram_offset;
 };
 
 extern int instantiate_cache_sram(struct platform_device *dev,
index cedabd0f4bfe81e362b8bd0bbc7d5238545dc3d7..68ac3aacb1919477ed4686f859979d9bc17c7b15 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009-2010 Freescale Semiconductor, Inc.
+ * Copyright 2009-2010, 2012 Freescale Semiconductor, Inc.
  *
  * QorIQ (P1/P2) L2 controller init for Cache-SRAM instantiation
  *
@@ -31,24 +31,21 @@ static char *sram_size;
 static char *sram_offset;
 struct mpc85xx_l2ctlr __iomem *l2ctlr;
 
-static long get_cache_sram_size(void)
+static int get_cache_sram_params(struct sram_parameters *sram_params)
 {
-       unsigned long val;
+       unsigned long long addr;
+       unsigned int size;
 
-       if (!sram_size || (strict_strtoul(sram_size, 0, &val) < 0))
+       if (!sram_size || (kstrtouint(sram_size, 0, &size) < 0))
                return -EINVAL;
 
-       return val;
-}
-
-static long get_cache_sram_offset(void)
-{
-       unsigned long val;
-
-       if (!sram_offset || (strict_strtoul(sram_offset, 0, &val) < 0))
+       if (!sram_offset || (kstrtoull(sram_offset, 0, &addr) < 0))
                return -EINVAL;
 
-       return val;
+       sram_params->sram_offset = addr;
+       sram_params->sram_size = size;
+
+       return 0;
 }
 
 static int __init get_size_from_cmdline(char *str)
@@ -93,17 +90,9 @@ static int __devinit mpc85xx_l2ctlr_of_probe(struct platform_device *dev)
        }
        l2cache_size = *prop;
 
-       sram_params.sram_size  = get_cache_sram_size();
-       if ((int)sram_params.sram_size <= 0) {
-               dev_err(&dev->dev,
-                       "Entire L2 as cache, Aborting Cache-SRAM stuff\n");
-               return -EINVAL;
-       }
-
-       sram_params.sram_offset  = get_cache_sram_offset();
-       if ((int64_t)sram_params.sram_offset <= 0) {
+       if (get_cache_sram_params(&sram_params)) {
                dev_err(&dev->dev,
-                       "Entire L2 as cache, provide a valid sram offset\n");
+                       "Entire L2 as cache, provide valid sram offset and size\n");
                return -EINVAL;
        }
 
@@ -125,14 +114,14 @@ static int __devinit mpc85xx_l2ctlr_of_probe(struct platform_device *dev)
         * Write bits[0-17] to srbar0
         */
        out_be32(&l2ctlr->srbar0,
-               sram_params.sram_offset & L2SRAM_BAR_MSK_LO18);
+               lower_32_bits(sram_params.sram_offset) & L2SRAM_BAR_MSK_LO18);
 
        /*
         * Write bits[18-21] to srbare0
         */
 #ifdef CONFIG_PHYS_64BIT
        out_be32(&l2ctlr->srbarea0,
-               (sram_params.sram_offset >> 32) & L2SRAM_BARE_MSK_HI4);
+               upper_32_bits(sram_params.sram_offset) & L2SRAM_BARE_MSK_HI4);
 #endif
 
        clrsetbits_be32(&l2ctlr->ctl, L2CR_L2E, L2CR_L2FI);
index 253dce98c16ef05aef07caad41af6a7ecdd78fa7..14469cf9df68dc8497408a8953cbbbcf7b360ea1 100644 (file)
@@ -111,7 +111,7 @@ static unsigned int icp_hv_get_irq(void)
        if (vec == XICS_IRQ_SPURIOUS)
                return NO_IRQ;
 
-       irq = irq_radix_revmap_lookup(xics_host, vec);
+       irq = irq_find_mapping(xics_host, vec);
        if (likely(irq != NO_IRQ)) {
                xics_push_cppr(vec);
                return irq;
index 4c79b6fbee1c07fefc49fcdc094e94eede95bc59..48861d3fcd070cbc56f4e4189e4396f7196938c4 100644 (file)
@@ -119,7 +119,7 @@ static unsigned int icp_native_get_irq(void)
        if (vec == XICS_IRQ_SPURIOUS)
                return NO_IRQ;
 
-       irq = irq_radix_revmap_lookup(xics_host, vec);
+       irq = irq_find_mapping(xics_host, vec);
        if (likely(irq != NO_IRQ)) {
                xics_push_cppr(vec);
                return irq;
index cd1d18db92c6afdc5e35c7980b4e038b8a6057a4..9049d9f444857fea1c0d50a87164fa63e785097b 100644 (file)
@@ -329,9 +329,6 @@ static int xics_host_map(struct irq_domain *h, unsigned int virq,
 
        pr_devel("xics: map virq %d, hwirq 0x%lx\n", virq, hw);
 
-       /* Insert the interrupt mapping into the radix tree for fast lookup */
-       irq_radix_revmap_insert(xics_host, virq, hw);
-
        /* They aren't all level sensitive but we just don't really know */
        irq_set_status_flags(virq, IRQ_LEVEL);
 
index a39b4690c171621e78e2183c6b1b97bd25f4afaf..76de6b68487c8a44aa4eabecc1dddc7d916546e2 100644 (file)
@@ -85,10 +85,12 @@ config S390
        select HAVE_ARCH_MUTEX_CPU_RELAX
        select HAVE_ARCH_JUMP_LABEL if !MARCH_G5
        select ARCH_SAVE_PAGE_KEYS if HIBERNATION
+       select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
        select HAVE_MEMBLOCK
        select HAVE_MEMBLOCK_NODE_MAP
        select HAVE_CMPXCHG_LOCAL
        select ARCH_DISCARD_MEMBLOCK
+       select BUILDTIME_EXTABLE_SORT
        select ARCH_INLINE_SPIN_TRYLOCK
        select ARCH_INLINE_SPIN_TRYLOCK_BH
        select ARCH_INLINE_SPIN_LOCK
@@ -117,6 +119,7 @@ config S390
        select ARCH_INLINE_WRITE_UNLOCK_BH
        select ARCH_INLINE_WRITE_UNLOCK_IRQ
        select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE
+       select ARCH_WANT_IPC_PARSE_VERSION
        select GENERIC_SMP_IDLE_THREAD
        select GENERIC_TIME_VSYSCALL
        select GENERIC_CLOCKEVENTS
index 37d2bf267964a5103f1b1ae189db2e9f6fd1de91..f39cd710980b91da8d490994bc6e02a1955905f2 100644 (file)
@@ -7,13 +7,16 @@ CONFIG_TASK_DELAY_ACCT=y
 CONFIG_TASK_XACCT=y
 CONFIG_TASK_IO_ACCOUNTING=y
 CONFIG_AUDIT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_RCU_FAST_NO_HZ=y
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_CGROUPS=y
 CONFIG_CPUSETS=y
 CONFIG_CGROUP_CPUACCT=y
 CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
+CONFIG_CGROUP_MEMCG=y
 CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
 CONFIG_CGROUP_SCHED=y
 CONFIG_RT_GROUP_SCHED=y
@@ -35,8 +38,6 @@ CONFIG_MODVERSIONS=y
 CONFIG_PARTITION_ADVANCED=y
 CONFIG_IBM_PARTITION=y
 CONFIG_DEFAULT_DEADLINE=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
 CONFIG_PREEMPT=y
 CONFIG_MEMORY_HOTPLUG=y
 CONFIG_MEMORY_HOTREMOVE=y
index 5c63615f1349f862762ced5e20147acae7f2c5ec..b749c573365767af8487c4c24c07b8cbc8ace903 100644 (file)
@@ -11,7 +11,6 @@
 #include <asm/uaccess.h>
 #include <asm/tlbflush.h>
 #include <asm/ctl_reg.h>
-#include <asm-generic/mm_hooks.h>
 
 static inline int init_new_context(struct task_struct *tsk,
                                   struct mm_struct *mm)
@@ -58,7 +57,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 (user_mode != HOME_SPACE_MODE) {
+       if (addressing_mode != HOME_SPACE_MODE) {
                /* Load primary space page table origin. */
                asm volatile(LCTL_OPCODE" 1,1,%0\n"
                             : : "m" (S390_lowcore.user_asce) );
@@ -91,4 +90,17 @@ static inline void activate_mm(struct mm_struct *prev,
         switch_mm(prev, next, current);
 }
 
+static inline void arch_dup_mmap(struct mm_struct *oldmm,
+                                struct mm_struct *mm)
+{
+#ifdef CONFIG_64BIT
+       if (oldmm->context.asce_limit < mm->context.asce_limit)
+               crst_table_downgrade(mm, oldmm->context.asce_limit);
+#endif
+}
+
+static inline void arch_exit_mmap(struct mm_struct *mm)
+{
+}
+
 #endif /* __S390_MMU_CONTEXT_H */
index c40fa91e38a8da85fcad46d66435701f98076758..11e4e3236937e106aba159b83e7cc6ae6167c7b0 100644 (file)
@@ -120,7 +120,9 @@ struct stack_frame {
        regs->psw.mask  = psw_user_bits | PSW_MASK_BA;                  \
        regs->psw.addr  = new_psw | PSW_ADDR_AMODE;                     \
        regs->gprs[15]  = new_stackp;                                   \
+       __tlb_flush_mm(current->mm);                                    \
        crst_table_downgrade(current->mm, 1UL << 31);                   \
+       update_mm(current->mm, current);                                \
 } while (0)
 
 /* Forward declaration, a strange C thing */
index 57e80534375a603f2f839893bfa6ebc2b79e5845..e6859d16ee2dbb49f87ca15cf5b8a720153df2b6 100644 (file)
@@ -60,7 +60,7 @@ void create_mem_hole(struct mem_chunk memory_chunk[], unsigned long addr,
 #define SECONDARY_SPACE_MODE   2
 #define HOME_SPACE_MODE                3
 
-extern unsigned int user_mode;
+extern unsigned int addressing_mode;
 
 /*
  * Machine features detected in head.S
index 2e37157ba6a92cf48dd6360b994db0803f791624..6756e78f48082f1644f7a0d73cdeecaa5d193da5 100644 (file)
 #define __IGNORE_recvmmsg
 #define __IGNORE_sendmmsg
 
-#define __ARCH_WANT_IPC_PARSE_VERSION
 #define __ARCH_WANT_OLD_READDIR
 #define __ARCH_WANT_SYS_ALARM
 #define __ARCH_WANT_SYS_GETHOSTNAME
index 21be961e8a43e643daa31a26680c91edf2a5ee66..ba500d8dc392056ddd66e4978389d072452f6859 100644 (file)
@@ -110,6 +110,7 @@ struct debug_view debug_raw_view = {
        NULL,
        NULL
 };
+EXPORT_SYMBOL(debug_raw_view);
 
 struct debug_view debug_hex_ascii_view = {
        "hex_ascii",
@@ -119,6 +120,7 @@ struct debug_view debug_hex_ascii_view = {
        NULL,
        NULL
 };
+EXPORT_SYMBOL(debug_hex_ascii_view);
 
 static struct debug_view debug_level_view = {
        "level",
@@ -155,6 +157,7 @@ struct debug_view debug_sprintf_view = {
        NULL,
        NULL
 };
+EXPORT_SYMBOL(debug_sprintf_view);
 
 /* used by dump analysis tools to determine version of debug feature */
 static unsigned int __used debug_feature_version = __DEBUG_FEATURE_VERSION;
@@ -730,6 +733,7 @@ debug_info_t *debug_register(const char *name, int pages_per_area,
        return debug_register_mode(name, pages_per_area, nr_areas, buf_size,
                                   S_IRUSR | S_IWUSR, 0, 0);
 }
+EXPORT_SYMBOL(debug_register);
 
 /*
  * debug_unregister:
@@ -748,6 +752,7 @@ debug_unregister(debug_info_t * id)
 out:
        return;
 }
+EXPORT_SYMBOL(debug_unregister);
 
 /*
  * debug_set_size:
@@ -810,7 +815,7 @@ debug_set_level(debug_info_t* id, int new_level)
         }
        spin_unlock_irqrestore(&id->lock,flags);
 }
-
+EXPORT_SYMBOL(debug_set_level);
 
 /*
  * proceed_active_entry:
@@ -930,7 +935,7 @@ debug_stop_all(void)
        if (debug_stoppable)
                debug_active = 0;
 }
-
+EXPORT_SYMBOL(debug_stop_all);
 
 void debug_set_critical(void)
 {
@@ -963,6 +968,7 @@ debug_event_common(debug_info_t * id, int level, const void *buf, int len)
 
        return active;
 }
+EXPORT_SYMBOL(debug_event_common);
 
 /*
  * debug_exception_common:
@@ -990,6 +996,7 @@ debug_entry_t
 
        return active;
 }
+EXPORT_SYMBOL(debug_exception_common);
 
 /*
  * counts arguments in format string for sprintf view
@@ -1043,6 +1050,7 @@ debug_sprintf_event(debug_info_t* id, int level,char *string,...)
 
        return active;
 }
+EXPORT_SYMBOL(debug_sprintf_event);
 
 /*
  * debug_sprintf_exception:
@@ -1081,25 +1089,7 @@ debug_sprintf_exception(debug_info_t* id, int level,char *string,...)
 
        return active;
 }
-
-/*
- * debug_init:
- * - is called exactly once to initialize the debug feature
- */
-
-static int
-__init debug_init(void)
-{
-       int rc = 0;
-
-       s390dbf_sysctl_header = register_sysctl_table(s390dbf_dir_table);
-       mutex_lock(&debug_mutex);
-       debug_debugfs_root_entry = debugfs_create_dir(DEBUG_DIR_ROOT,NULL);
-       initialized = 1;
-       mutex_unlock(&debug_mutex);
-
-       return rc;
-}
+EXPORT_SYMBOL(debug_sprintf_exception);
 
 /*
  * debug_register_view:
@@ -1147,6 +1137,7 @@ debug_register_view(debug_info_t * id, struct debug_view *view)
 out:
        return rc;
 }
+EXPORT_SYMBOL(debug_register_view);
 
 /*
  * debug_unregister_view:
@@ -1176,6 +1167,7 @@ debug_unregister_view(debug_info_t * id, struct debug_view *view)
 out:
        return rc;
 }
+EXPORT_SYMBOL(debug_unregister_view);
 
 static inline char *
 debug_get_user_string(const char __user *user_buf, size_t user_len)
@@ -1485,6 +1477,7 @@ debug_dflt_header_fn(debug_info_t * id, struct debug_view *view,
                      except_str, entry->id.fields.cpuid, (void *) caller);
        return rc;
 }
+EXPORT_SYMBOL(debug_dflt_header_fn);
 
 /*
  * prints debug data sprintf-formated:
@@ -1533,33 +1526,16 @@ out:
 }
 
 /*
- * clean up module
+ * debug_init:
+ * - is called exactly once to initialize the debug feature
  */
-static void __exit debug_exit(void)
+static int __init debug_init(void)
 {
-       debugfs_remove(debug_debugfs_root_entry);
-       unregister_sysctl_table(s390dbf_sysctl_header);
-       return;
+       s390dbf_sysctl_header = register_sysctl_table(s390dbf_dir_table);
+       mutex_lock(&debug_mutex);
+       debug_debugfs_root_entry = debugfs_create_dir(DEBUG_DIR_ROOT, NULL);
+       initialized = 1;
+       mutex_unlock(&debug_mutex);
+       return 0;
 }
-
-/*
- * module definitions
- */
 postcore_initcall(debug_init);
-module_exit(debug_exit);
-MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(debug_register);
-EXPORT_SYMBOL(debug_unregister); 
-EXPORT_SYMBOL(debug_set_level);
-EXPORT_SYMBOL(debug_stop_all);
-EXPORT_SYMBOL(debug_register_view);
-EXPORT_SYMBOL(debug_unregister_view);
-EXPORT_SYMBOL(debug_event_common);
-EXPORT_SYMBOL(debug_exception_common);
-EXPORT_SYMBOL(debug_hex_ascii_view);
-EXPORT_SYMBOL(debug_raw_view);
-EXPORT_SYMBOL(debug_dflt_header_fn);
-EXPORT_SYMBOL(debug_sprintf_view);
-EXPORT_SYMBOL(debug_sprintf_exception);
-EXPORT_SYMBOL(debug_sprintf_event);
index 1f6b428e276239d94927086f6d5b2ccc668fbe2b..619c5d3507264ca1f7417563a9152b55270374a2 100644 (file)
@@ -1531,7 +1531,7 @@ static int print_insn(char *buffer, unsigned char *code, unsigned long addr)
 
 void show_code(struct pt_regs *regs)
 {
-       char *mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl";
+       char *mode = user_mode(regs) ? "User" : "Krnl";
        unsigned char code[64];
        char buffer[64], *ptr;
        mm_segment_t old_fs;
@@ -1540,7 +1540,7 @@ void show_code(struct pt_regs *regs)
 
        /* Get a snapshot of the 64 bytes surrounding the fault address. */
        old_fs = get_fs();
-       set_fs((regs->psw.mask & PSW_MASK_PSTATE) ? USER_DS : KERNEL_DS);
+       set_fs(user_mode(regs) ? USER_DS : KERNEL_DS);
        for (start = 32; start && regs->psw.addr >= 34 - start; start -= 2) {
                addr = regs->psw.addr - 34 + start;
                if (__copy_from_user(code + start - 2,
index bc95a8ebd9cc3810a0bfb83d34f10cca512d1d92..83c3271c442b75d5d005e1e6130f538deea968a7 100644 (file)
@@ -455,7 +455,6 @@ void __init startup_init(void)
        init_kernel_storage_key();
        lockdep_init();
        lockdep_off();
-       sort_main_extable();
        setup_lowcore_early();
        setup_facility_list();
        detect_machine_type();
index e64d141555ce99843f4995827f4644e1dc127c79..6ffcd3203215a2bcacb50bc35ae12229113b0ff4 100644 (file)
@@ -1583,7 +1583,7 @@ static struct kset *vmcmd_kset;
 
 static void vmcmd_run(struct shutdown_trigger *trigger)
 {
-       char *cmd, *next_cmd;
+       char *cmd;
 
        if (strcmp(trigger->name, ON_REIPL_STR) == 0)
                cmd = vmcmd_on_reboot;
@@ -1600,15 +1600,7 @@ static void vmcmd_run(struct shutdown_trigger *trigger)
 
        if (strlen(cmd) == 0)
                return;
-       do {
-               next_cmd = strchr(cmd, '\n');
-               if (next_cmd) {
-                       next_cmd[0] = 0;
-                       next_cmd += 1;
-               }
-               __cpcmd(cmd, NULL, 0, NULL);
-               cmd = next_cmd;
-       } while (cmd != NULL);
+       __cpcmd(cmd, NULL, 0, NULL);
 }
 
 static int vmcmd_init(void)
index 743c0f32fe3beb0c508a02e81bf775f075242d08..f86c81e13c374be484f591faead417a7fb3ee572 100644 (file)
@@ -302,8 +302,8 @@ static int __init parse_vmalloc(char *arg)
 }
 early_param("vmalloc", parse_vmalloc);
 
-unsigned int user_mode = HOME_SPACE_MODE;
-EXPORT_SYMBOL_GPL(user_mode);
+unsigned int addressing_mode = HOME_SPACE_MODE;
+EXPORT_SYMBOL_GPL(addressing_mode);
 
 static int set_amode_primary(void)
 {
@@ -328,7 +328,7 @@ static int set_amode_primary(void)
  */
 static int __init early_parse_switch_amode(char *p)
 {
-       user_mode = PRIMARY_SPACE_MODE;
+       addressing_mode = PRIMARY_SPACE_MODE;
        return 0;
 }
 early_param("switch_amode", early_parse_switch_amode);
@@ -336,9 +336,9 @@ early_param("switch_amode", early_parse_switch_amode);
 static int __init early_parse_user_mode(char *p)
 {
        if (p && strcmp(p, "primary") == 0)
-               user_mode = PRIMARY_SPACE_MODE;
+               addressing_mode = PRIMARY_SPACE_MODE;
        else if (!p || strcmp(p, "home") == 0)
-               user_mode = HOME_SPACE_MODE;
+               addressing_mode = HOME_SPACE_MODE;
        else
                return 1;
        return 0;
@@ -347,7 +347,7 @@ early_param("user_mode", early_parse_user_mode);
 
 static void setup_addressing_mode(void)
 {
-       if (user_mode == PRIMARY_SPACE_MODE) {
+       if (addressing_mode == PRIMARY_SPACE_MODE) {
                if (set_amode_primary())
                        pr_info("Address spaces switched, "
                                "mvcos available\n");
index af2421a0f3156e9c8a965ee34d95322de55602be..01775c04a90e63a2bbebc2eb5d3ebf0363abafe2 100644 (file)
@@ -185,7 +185,7 @@ void show_registers(struct pt_regs *regs)
 {
        char *mode;
 
-       mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl";
+       mode = user_mode(regs) ? "User" : "Krnl";
        printk("%s PSW : %p %p",
               mode, (void *) regs->psw.mask,
               (void *) regs->psw.addr);
@@ -225,7 +225,7 @@ void show_regs(struct pt_regs *regs)
               (void *) current->thread.ksp);
        show_registers(regs);
        /* Show stack backtrace if pt_regs is from kernel mode */
-       if (!(regs->psw.mask & PSW_MASK_PSTATE))
+       if (!user_mode(regs))
                show_trace(NULL, (unsigned long *) regs->gprs[15]);
        show_last_breaking_event(regs);
 }
@@ -300,7 +300,7 @@ static void __kprobes do_trap(struct pt_regs *regs,
                       regs->int_code, si_signo) == NOTIFY_STOP)
                return;
 
-        if (regs->psw.mask & PSW_MASK_PSTATE) {
+       if (user_mode(regs)) {
                info.si_signo = si_signo;
                info.si_errno = 0;
                info.si_code = si_code;
@@ -341,7 +341,7 @@ void __kprobes do_per_trap(struct pt_regs *regs)
 
 static void default_trap_handler(struct pt_regs *regs)
 {
-        if (regs->psw.mask & PSW_MASK_PSTATE) {
+       if (user_mode(regs)) {
                report_user_fault(regs, SIGSEGV);
                do_exit(SIGSEGV);
        } else
@@ -410,7 +410,7 @@ static void __kprobes illegal_op(struct pt_regs *regs)
 
        location = get_psw_address(regs);
 
-       if (regs->psw.mask & PSW_MASK_PSTATE) {
+       if (user_mode(regs)) {
                if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
                        return;
                if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
@@ -478,7 +478,7 @@ void specification_exception(struct pt_regs *regs)
 
        location = (__u16 __user *) get_psw_address(regs);
 
-        if (regs->psw.mask & PSW_MASK_PSTATE) {
+       if (user_mode(regs)) {
                get_user(*((__u16 *) opcode), location);
                switch (opcode[0]) {
                case 0x28: /* LDR Rx,Ry   */
@@ -531,7 +531,7 @@ static void data_exception(struct pt_regs *regs)
                asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc));
 
 #ifdef CONFIG_MATHEMU
-        else if (regs->psw.mask & PSW_MASK_PSTATE) {
+       else if (user_mode(regs)) {
                __u8 opcode[6];
                get_user(*((__u16 *) opcode), location);
                switch (opcode[0]) {
@@ -598,7 +598,7 @@ static void data_exception(struct pt_regs *regs)
 static void space_switch_exception(struct pt_regs *regs)
 {
        /* Set user psw back to home space mode. */
-       if (regs->psw.mask & PSW_MASK_PSTATE)
+       if (user_mode(regs))
                regs->psw.mask |= PSW_ASC_HOME;
        /* Send SIGILL. */
        do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event");
index ea5590fdca3bb8266caa0e25410dd744b103b4c9..9a19ca367c17bc41e1f8e781ed173a93bba5da12 100644 (file)
@@ -84,7 +84,8 @@ struct vdso_data *vdso_data = &vdso_data_store.data;
  */
 static void vdso_init_data(struct vdso_data *vd)
 {
-       vd->ectg_available = user_mode != HOME_SPACE_MODE && test_facility(31);
+       vd->ectg_available =
+               addressing_mode != HOME_SPACE_MODE && test_facility(31);
 }
 
 #ifdef CONFIG_64BIT
@@ -101,7 +102,7 @@ int vdso_alloc_per_cpu(struct _lowcore *lowcore)
 
        lowcore->vdso_per_cpu_data = __LC_PASTE;
 
-       if (user_mode == HOME_SPACE_MODE || !vdso_enabled)
+       if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled)
                return 0;
 
        segment_table = __get_free_pages(GFP_KERNEL, SEGMENT_ORDER);
@@ -146,7 +147,7 @@ void vdso_free_per_cpu(struct _lowcore *lowcore)
        unsigned long segment_table, page_table, page_frame;
        u32 *psal, *aste;
 
-       if (user_mode == HOME_SPACE_MODE || !vdso_enabled)
+       if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled)
                return;
 
        psal = (u32 *)(addr_t) lowcore->paste[4];
@@ -164,7 +165,7 @@ static void vdso_init_cr5(void)
 {
        unsigned long cr5;
 
-       if (user_mode == HOME_SPACE_MODE || !vdso_enabled)
+       if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled)
                return;
        cr5 = offsetof(struct _lowcore, paste);
        __ctl_load(cr5, 5, 5);
index 21109c63eb12961483134141c67bfc8c2b8f8315..de8fa9bbd35ee9787537e93c18b561b75cacf72f 100644 (file)
@@ -45,7 +45,7 @@ SECTIONS
 
        .dummy : { *(.dummy) } :data
 
-       RODATA
+       RO_DATA_SECTION(PAGE_SIZE)
 
 #ifdef CONFIG_SHARED_KERNEL
        . = ALIGN(0x100000);    /* VM shared segments are 1MB aligned */
index 6a12d1bb6e09d065e5a4b2625ce79ba7209b5c2b..6c013f544146d8bf92d6143378c8f1fbf5631b02 100644 (file)
@@ -49,6 +49,7 @@
 #define VM_FAULT_BADCONTEXT    0x010000
 #define VM_FAULT_BADMAP                0x020000
 #define VM_FAULT_BADACCESS     0x040000
+#define VM_FAULT_SIGNAL        0x080000
 
 static unsigned long store_indication;
 
@@ -110,7 +111,7 @@ static inline int user_space_fault(unsigned long trans_exc_code)
        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)
+       if (addressing_mode == HOME_SPACE_MODE)
                /* User space if the access has been done via home space. */
                return trans_exc_code == 3;
        /*
@@ -219,7 +220,7 @@ static noinline void do_fault_error(struct pt_regs *regs, int fault)
        case VM_FAULT_BADACCESS:
        case VM_FAULT_BADMAP:
                /* Bad memory access. Check if it is kernel or user space. */
-               if (regs->psw.mask & PSW_MASK_PSTATE) {
+               if (user_mode(regs)) {
                        /* User mode accesses just cause a SIGSEGV */
                        si_code = (fault == VM_FAULT_BADMAP) ?
                                SEGV_MAPERR : SEGV_ACCERR;
@@ -229,15 +230,19 @@ static noinline void do_fault_error(struct pt_regs *regs, int fault)
        case VM_FAULT_BADCONTEXT:
                do_no_context(regs);
                break;
+       case VM_FAULT_SIGNAL:
+               if (!user_mode(regs))
+                       do_no_context(regs);
+               break;
        default: /* fault & VM_FAULT_ERROR */
                if (fault & VM_FAULT_OOM) {
-                       if (!(regs->psw.mask & PSW_MASK_PSTATE))
+                       if (!user_mode(regs))
                                do_no_context(regs);
                        else
                                pagefault_out_of_memory();
                } else if (fault & VM_FAULT_SIGBUS) {
                        /* Kernel mode? Handle exceptions or die */
-                       if (!(regs->psw.mask & PSW_MASK_PSTATE))
+                       if (!user_mode(regs))
                                do_no_context(regs);
                        else
                                do_sigbus(regs);
@@ -286,7 +291,7 @@ static inline int do_exception(struct pt_regs *regs, int access)
 
        address = trans_exc_code & __FAIL_ADDR_MASK;
        perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
-       flags = FAULT_FLAG_ALLOW_RETRY;
+       flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
        if (access == VM_WRITE || (trans_exc_code & store_indication) == 0x400)
                flags |= FAULT_FLAG_WRITE;
        down_read(&mm->mmap_sem);
@@ -335,6 +340,11 @@ retry:
         * the fault.
         */
        fault = handle_mm_fault(mm, vma, address, flags);
+       /* No reason to continue if interrupted by SIGKILL. */
+       if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) {
+               fault = VM_FAULT_SIGNAL;
+               goto out;
+       }
        if (unlikely(fault & VM_FAULT_ERROR))
                goto out_up;
 
@@ -426,7 +436,7 @@ void __kprobes do_asce_exception(struct pt_regs *regs)
        }
 
        /* User mode accesses just cause a SIGSEGV */
-       if (regs->psw.mask & PSW_MASK_PSTATE) {
+       if (user_mode(regs)) {
                do_sigsegv(regs, SEGV_MAPERR);
                return;
        }
@@ -441,6 +451,7 @@ int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write)
        struct pt_regs regs;
        int access, fault;
 
+       /* Emulate a uaccess fault from kernel mode. */
        regs.psw.mask = psw_kernel_bits | PSW_MASK_DAT | PSW_MASK_MCHECK;
        if (!irqs_disabled())
                regs.psw.mask |= PSW_MASK_IO | PSW_MASK_EXT;
@@ -450,12 +461,12 @@ int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write)
        regs.int_parm_long = (uaddr & PAGE_MASK) | 2;
        access = write ? VM_WRITE : VM_READ;
        fault = do_exception(&regs, access);
-       if (unlikely(fault)) {
-               if (fault & VM_FAULT_OOM)
-                       return -EFAULT;
-               else if (fault & VM_FAULT_SIGBUS)
-                       do_sigbus(&regs);
-       }
+       /*
+        * Since the fault happened in kernel mode while performing a uaccess
+        * all we need to do now is emulating a fixup in case "fault" is not
+        * zero.
+        * For the calling uaccess functions this results always in -EFAULT.
+        */
        return fault ? -EFAULT : 0;
 }
 
index 573384256c5c5ebd1aa65295e635e808345902d0..c59a5efa58b1ee701f93b4f980397e539265805a 100644 (file)
@@ -103,9 +103,15 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
 
 int s390_mmap_check(unsigned long addr, unsigned long len)
 {
+       int rc;
+
        if (!is_compat_task() &&
-           len >= TASK_SIZE && TASK_SIZE < (1UL << 53))
-               return crst_table_upgrade(current->mm, 1UL << 53);
+           len >= TASK_SIZE && TASK_SIZE < (1UL << 53)) {
+               rc = crst_table_upgrade(current->mm, 1UL << 53);
+               if (rc)
+                       return rc;
+               update_mm(current->mm, current);
+       }
        return 0;
 }
 
@@ -125,6 +131,7 @@ s390_get_unmapped_area(struct file *filp, unsigned long addr,
                rc = crst_table_upgrade(mm, 1UL << 53);
                if (rc)
                        return (unsigned long) rc;
+               update_mm(mm, current);
                area = arch_get_unmapped_area(filp, addr, len, pgoff, flags);
        }
        return area;
@@ -147,6 +154,7 @@ s390_get_unmapped_area_topdown(struct file *filp, const unsigned long addr,
                rc = crst_table_upgrade(mm, 1UL << 53);
                if (rc)
                        return (unsigned long) rc;
+               update_mm(mm, current);
                area = arch_get_unmapped_area_topdown(filp, addr, len,
                                                      pgoff, flags);
        }
index 1cab221077cc872b5f5e5528ff1918d8551fa9d2..18df31d1f2c9841cac3d97248a2806f9fb2d81a4 100644 (file)
@@ -85,7 +85,6 @@ repeat:
                crst_table_free(mm, table);
        if (mm->context.asce_limit < limit)
                goto repeat;
-       update_mm(mm, current);
        return 0;
 }
 
@@ -93,9 +92,6 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit)
 {
        pgd_t *pgd;
 
-       if (mm->context.asce_limit <= limit)
-               return;
-       __tlb_flush_mm(mm);
        while (mm->context.asce_limit > limit) {
                pgd = mm->pgd;
                switch (pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) {
@@ -118,7 +114,6 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit)
                mm->task_size = mm->context.asce_limit;
                crst_table_free(mm, (unsigned long *) pgd);
        }
-       update_mm(mm, current);
 }
 #endif
 
@@ -801,7 +796,7 @@ int s390_enable_sie(void)
        struct mm_struct *mm, *old_mm;
 
        /* Do we have switched amode? If no, we cannot do sie */
-       if (user_mode == HOME_SPACE_MODE)
+       if (addressing_mode == HOME_SPACE_MODE)
                return -EINVAL;
 
        /* Do we have pgstes? if yes, we are done */
index c82f62fb9c28ef6096a486c6a3458725e91b31e3..8a6811b2cdb9523a24cd32341dee0fcef317f661 100644 (file)
@@ -58,7 +58,7 @@ void s390_backtrace(struct pt_regs * const regs, unsigned int depth)
        unsigned long head;
        struct stack_frame* head_sf;
 
-       if (user_mode (regs))
+       if (user_mode(regs))
                return;
 
        head = regs->gprs[15];
index a24595d83ad6c48c5685faed4d59ecb00d410c41..36f5141e80417ac6172ce6965602e4d15a67bdfb 100644 (file)
@@ -21,6 +21,7 @@ config SUPERH
        select HAVE_KERNEL_LZMA
        select HAVE_KERNEL_XZ
        select HAVE_KERNEL_LZO
+       select ARCH_WANT_IPC_PARSE_VERSION
        select HAVE_SYSCALL_TRACEPOINTS
        select HAVE_REGS_AND_STACK_ACCESS_API
        select HAVE_GENERIC_HARDIRQS
@@ -50,6 +51,7 @@ config SUPERH32
        select HAVE_DYNAMIC_FTRACE
        select HAVE_FUNCTION_TRACE_MCOUNT_TEST
        select HAVE_FTRACE_NMI_ENTER if DYNAMIC_FTRACE
+       select ARCH_WANT_IPC_PARSE_VERSION
        select HAVE_FUNCTION_GRAPH_TRACER
        select HAVE_ARCH_KGDB
        select HAVE_HW_BREAKPOINT
index e7583484cc07dd3d9ab257d094f769b94446397a..95ae23fcfdd655f47b7f43d9cc877a408b31eb3e 100644 (file)
@@ -11,7 +11,7 @@ CONFIG_CGROUP_FREEZER=y
 CONFIG_CGROUP_DEVICE=y
 CONFIG_CGROUP_CPUACCT=y
 CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
+CONFIG_CGROUP_MEMCG=y
 CONFIG_BLK_CGROUP=y
 CONFIG_NAMESPACES=y
 CONFIG_BLK_DEV_INITRD=y
index 8a7dd7b59c5c01f9a0adf539e87ad569f6dd678e..76a76a295d7471668dcc9b6b880533839b8e434d 100644 (file)
@@ -18,8 +18,8 @@ CONFIG_CPUSETS=y
 # CONFIG_PROC_PID_CPUSET is not set
 CONFIG_CGROUP_CPUACCT=y
 CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
-CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
+CONFIG_CGROUP_MEMCG=y
+CONFIG_CGROUP_MEMCG_SWAP=y
 CONFIG_CGROUP_SCHED=y
 CONFIG_RT_GROUP_SCHED=y
 CONFIG_BLK_CGROUP=y
index 72c3fad7383f5fc254d90fe3d499eb4a61ce1a47..6bc30ab9fd186ea50d37dd2200d94e78092a21b6 100644 (file)
@@ -11,7 +11,7 @@ CONFIG_CGROUP_DEBUG=y
 CONFIG_CGROUP_DEVICE=y
 CONFIG_CGROUP_CPUACCT=y
 CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
+CONFIG_CGROUP_MEMCG=y
 CONFIG_RELAY=y
 CONFIG_NAMESPACES=y
 CONFIG_UTS_NS=y
index 6bb413036892cb2f50af2db94649584557703b91..cd6c519f8fad7873ab7ab044e723f8b05742076b 100644 (file)
@@ -13,7 +13,7 @@ CONFIG_CGROUP_FREEZER=y
 CONFIG_CGROUP_DEVICE=y
 CONFIG_CGROUP_CPUACCT=y
 CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
+CONFIG_CGROUP_MEMCG=y
 CONFIG_RELAY=y
 CONFIG_NAMESPACES=y
 CONFIG_UTS_NS=y
index 8bfa4d056d7a6574e4ac7f4fa6d6b49a90001d7c..d7f89be9f474f5bc8e92cfd4fb5346bd3b600e21 100644 (file)
@@ -15,8 +15,8 @@ CONFIG_CPUSETS=y
 # CONFIG_PROC_PID_CPUSET is not set
 CONFIG_CGROUP_CPUACCT=y
 CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
-CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
+CONFIG_CGROUP_MEMCG=y
+CONFIG_CGROUP_MEMCG_SWAP=y
 CONFIG_CGROUP_SCHED=y
 CONFIG_RT_GROUP_SCHED=y
 CONFIG_BLK_DEV_INITRD=y
index e800a38c9f8d86ec512ea3c5c557fa1c3707d6b1..7bc67076baac8771d929b0f5764954e9e2c787db 100644 (file)
@@ -6,7 +6,6 @@
 # endif
 
 # define __ARCH_WANT_SYS_RT_SIGSUSPEND
-# define __ARCH_WANT_IPC_PARSE_VERSION
 # define __ARCH_WANT_OLD_READDIR
 # define __ARCH_WANT_OLD_STAT
 # define __ARCH_WANT_STAT64
index e74ff137762661844783fe76a30986fe01308e9e..67f1f6f5f4e14fa35408cda35ad1464297dd2859 100644 (file)
@@ -27,6 +27,7 @@ config SPARC
        select HAVE_ARCH_JUMP_LABEL
        select HAVE_GENERIC_HARDIRQS
        select GENERIC_IRQ_SHOW
+       select ARCH_WANT_IPC_PARSE_VERSION
        select USE_GENERIC_SMP_HELPERS if SMP
        select GENERIC_PCI_IOMAP
        select HAVE_NMI_WATCHDOG if SPARC64
index c7cb0af0eb59cb6dfb8a94e6c6fcdd330a7af29d..fb2693464807dd59020bd737cd115e6f5f45a410 100644 (file)
 #endif
 
 #ifdef __KERNEL__
-#define __ARCH_WANT_IPC_PARSE_VERSION
 #define __ARCH_WANT_OLD_READDIR
 #define __ARCH_WANT_STAT64
 #define __ARCH_WANT_SYS_ALARM
index 435e406fdec3893a24753494eea408434d6810c8..81d92fc9983b1c0244ab395b7dd81c6725d20d26 100644 (file)
@@ -1250,14 +1250,12 @@ int ldc_bind(struct ldc_channel *lp, const char *name)
        snprintf(lp->rx_irq_name, LDC_IRQ_NAME_MAX, "%s RX", name);
        snprintf(lp->tx_irq_name, LDC_IRQ_NAME_MAX, "%s TX", name);
 
-       err = request_irq(lp->cfg.rx_irq, ldc_rx,
-                         IRQF_SAMPLE_RANDOM | IRQF_DISABLED,
+       err = request_irq(lp->cfg.rx_irq, ldc_rx, IRQF_DISABLED,
                          lp->rx_irq_name, lp);
        if (err)
                return err;
 
-       err = request_irq(lp->cfg.tx_irq, ldc_tx,
-                         IRQF_SAMPLE_RANDOM | IRQF_DISABLED,
+       err = request_irq(lp->cfg.tx_irq, ldc_tx, IRQF_DISABLED,
                          lp->tx_irq_name, lp);
        if (err) {
                free_irq(lp->cfg.rx_irq, lp);
index c38e5aaae56f2def7f8255a3347be46247c354f9..0dc1f578608131002da0b869dcfa7c56b4b149e3 100644 (file)
@@ -470,7 +470,7 @@ SYSCALL_DEFINE6(sparc_ipc, unsigned int, call, int, first, unsigned long, second
                switch (call) {
                case SHMAT: {
                        ulong raddr;
-                       err = do_shmat(first, ptr, (int)second, &raddr);
+                       err = do_shmat(first, ptr, (int)second, &raddr, SHMLBA);
                        if (!err) {
                                if (put_user(raddr,
                                             (ulong __user *) third))
index b8d99aca54319312ef1481c4a77bf6d585beb793..0270620a16926956b6b6a6e95422a439c8b09cb2 100644 (file)
@@ -18,8 +18,8 @@ CONFIG_CGROUP_DEVICE=y
 CONFIG_CPUSETS=y
 CONFIG_CGROUP_CPUACCT=y
 CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
-CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
+CONFIG_CGROUP_MEMCG=y
+CONFIG_CGROUP_MEMCG_SWAP=y
 CONFIG_CGROUP_SCHED=y
 CONFIG_RT_GROUP_SCHED=y
 CONFIG_BLK_CGROUP=y
index 2b1fd31894f10329065334a1c30cdbfaf8ac4f24..c11de27a9bcb232061be13744514589282ef68ed 100644 (file)
@@ -17,8 +17,8 @@ CONFIG_CGROUP_DEVICE=y
 CONFIG_CPUSETS=y
 CONFIG_CGROUP_CPUACCT=y
 CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
-CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
+CONFIG_CGROUP_MEMCG=y
+CONFIG_CGROUP_MEMCG_SWAP=y
 CONFIG_CGROUP_SCHED=y
 CONFIG_RT_GROUP_SCHED=y
 CONFIG_BLK_CGROUP=y
index fb7c65ae8de055f03a3245db759e914697c2b3f6..5bd71994452d588d4790585f76a9cd5dfd35e185 100644 (file)
@@ -16,7 +16,6 @@ generic-y += fb.h
 generic-y += fcntl.h
 generic-y += ioctl.h
 generic-y += ioctls.h
-generic-y += ipc.h
 generic-y += ipcbuf.h
 generic-y += irq_regs.h
 generic-y += kdebug.h
index 7823ab12e6a4fb1d27406878f2e1219cd2c5d897..08107a795062c55b0e9633bdd0b2da02351e96c4 100644 (file)
@@ -155,15 +155,15 @@ CONFIG_CPUSETS=y
 CONFIG_PROC_PID_CPUSET=y
 CONFIG_CGROUP_CPUACCT=y
 CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
-CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
-# CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED is not set
-# CONFIG_CGROUP_MEM_RES_CTLR_KMEM is not set
+CONFIG_CGROUP_MEMCG=y
+CONFIG_CGROUP_MEMCG_SWAP=y
+# CONFIG_CGROUP_MEMCG_SWAP_ENABLED is not set
+# CONFIG_CGROUP_MEMCG_KMEM is not set
 CONFIG_CGROUP_SCHED=y
 CONFIG_FAIR_GROUP_SCHED=y
 # CONFIG_CFS_BANDWIDTH is not set
 # CONFIG_RT_GROUP_SCHED is not set
-CONFIG_BLK_CGROUP=m
+CONFIG_BLK_CGROUP=y
 # CONFIG_DEBUG_BLK_CGROUP is not set
 # CONFIG_CHECKPOINT_RESTORE is not set
 CONFIG_NAMESPACES=y
index 45e248c2f43c7c1c8c618aea0eb41980fd28a046..87eebfe03c61aa7de2524ede5d3aac3128aaccb5 100644 (file)
@@ -150,9 +150,11 @@ void chan_enable_winch(struct chan *chan, struct tty_struct *tty)
 static void line_timer_cb(struct work_struct *work)
 {
        struct line *line = container_of(work, struct line, task.work);
+       struct tty_struct *tty = tty_port_tty_get(&line->port);
 
        if (!line->throttled)
-               chan_interrupt(line, line->tty, line->driver->read_irq);
+               chan_interrupt(line, tty, line->driver->read_irq);
+       tty_kref_put(tty);
 }
 
 int enable_chan(struct line *line)
index acfd0e0fd0c98cfcabe626581eea0a722c86b6ff..bbaf2c59830ac3561c432d8ac23d6f64bf62dd79 100644 (file)
@@ -19,9 +19,11 @@ static irqreturn_t line_interrupt(int irq, void *data)
 {
        struct chan *chan = data;
        struct line *line = chan->line;
+       struct tty_struct *tty = tty_port_tty_get(&line->port);
 
        if (line)
-               chan_interrupt(line, line->tty, irq);
+               chan_interrupt(line, tty, irq);
+       tty_kref_put(tty);
        return IRQ_HANDLED;
 }
 
@@ -219,92 +221,6 @@ void line_set_termios(struct tty_struct *tty, struct ktermios * old)
        /* nothing */
 }
 
-static const struct {
-       int  cmd;
-       char *level;
-       char *name;
-} tty_ioctls[] = {
-       /* don't print these, they flood the log ... */
-       { TCGETS,      NULL,       "TCGETS"      },
-       { TCSETS,      NULL,       "TCSETS"      },
-       { TCSETSW,     NULL,       "TCSETSW"     },
-       { TCFLSH,      NULL,       "TCFLSH"      },
-       { TCSBRK,      NULL,       "TCSBRK"      },
-
-       /* general tty stuff */
-       { TCSETSF,     KERN_DEBUG, "TCSETSF"     },
-       { TCGETA,      KERN_DEBUG, "TCGETA"      },
-       { TIOCMGET,    KERN_DEBUG, "TIOCMGET"    },
-       { TCSBRKP,     KERN_DEBUG, "TCSBRKP"     },
-       { TIOCMSET,    KERN_DEBUG, "TIOCMSET"    },
-
-       /* linux-specific ones */
-       { TIOCLINUX,   KERN_INFO,  "TIOCLINUX"   },
-       { KDGKBMODE,   KERN_INFO,  "KDGKBMODE"   },
-       { KDGKBTYPE,   KERN_INFO,  "KDGKBTYPE"   },
-       { KDSIGACCEPT, KERN_INFO,  "KDSIGACCEPT" },
-};
-
-int line_ioctl(struct tty_struct *tty, unsigned int cmd,
-                               unsigned long arg)
-{
-       int ret;
-       int i;
-
-       ret = 0;
-       switch(cmd) {
-#ifdef TIOCGETP
-       case TIOCGETP:
-       case TIOCSETP:
-       case TIOCSETN:
-#endif
-#ifdef TIOCGETC
-       case TIOCGETC:
-       case TIOCSETC:
-#endif
-#ifdef TIOCGLTC
-       case TIOCGLTC:
-       case TIOCSLTC:
-#endif
-       /* Note: these are out of date as we now have TCGETS2 etc but this
-          whole lot should probably go away */
-       case TCGETS:
-       case TCSETSF:
-       case TCSETSW:
-       case TCSETS:
-       case TCGETA:
-       case TCSETAF:
-       case TCSETAW:
-       case TCSETA:
-       case TCXONC:
-       case TCFLSH:
-       case TIOCOUTQ:
-       case TIOCINQ:
-       case TIOCGLCKTRMIOS:
-       case TIOCSLCKTRMIOS:
-       case TIOCPKT:
-       case TIOCGSOFTCAR:
-       case TIOCSSOFTCAR:
-               return -ENOIOCTLCMD;
-#if 0
-       case TCwhatever:
-               /* do something */
-               break;
-#endif
-       default:
-               for (i = 0; i < ARRAY_SIZE(tty_ioctls); i++)
-                       if (cmd == tty_ioctls[i].cmd)
-                               break;
-               if (i == ARRAY_SIZE(tty_ioctls)) {
-                       printk(KERN_ERR "%s: %s: unknown ioctl: 0x%x\n",
-                              __func__, tty->name, cmd);
-               }
-               ret = -ENOIOCTLCMD;
-               break;
-       }
-       return ret;
-}
-
 void line_throttle(struct tty_struct *tty)
 {
        struct line *line = tty->driver_data;
@@ -333,7 +249,7 @@ static irqreturn_t line_write_interrupt(int irq, void *data)
 {
        struct chan *chan = data;
        struct line *line = chan->line;
-       struct tty_struct *tty = line->tty;
+       struct tty_struct *tty;
        int err;
 
        /*
@@ -352,68 +268,42 @@ static irqreturn_t line_write_interrupt(int irq, void *data)
        }
        spin_unlock(&line->lock);
 
+       tty = tty_port_tty_get(&line->port);
        if (tty == NULL)
                return IRQ_NONE;
 
        tty_wakeup(tty);
+       tty_kref_put(tty);
+
        return IRQ_HANDLED;
 }
 
 int line_setup_irq(int fd, int input, int output, struct line *line, void *data)
 {
        const struct line_driver *driver = line->driver;
-       int err = 0, flags = IRQF_SHARED | IRQF_SAMPLE_RANDOM;
+       int err = 0;
 
        if (input)
                err = um_request_irq(driver->read_irq, fd, IRQ_READ,
-                                      line_interrupt, flags,
-                                      driver->read_irq_name, data);
+                                    line_interrupt, IRQF_SHARED,
+                                    driver->read_irq_name, data);
        if (err)
                return err;
        if (output)
                err = um_request_irq(driver->write_irq, fd, IRQ_WRITE,
-                                       line_write_interrupt, flags,
-                                       driver->write_irq_name, data);
+                                    line_write_interrupt, IRQF_SHARED,
+                                    driver->write_irq_name, data);
        return err;
 }
 
-/*
- * Normally, a driver like this can rely mostly on the tty layer
- * locking, particularly when it comes to the driver structure.
- * However, in this case, mconsole requests can come in "from the
- * side", and race with opens and closes.
- *
- * mconsole config requests will want to be sure the device isn't in
- * use, and get_config, open, and close will want a stable
- * configuration.  The checking and modification of the configuration
- * is done under a spinlock.  Checking whether the device is in use is
- * line->tty->count > 1, also under the spinlock.
- *
- * line->count serves to decide whether the device should be enabled or
- * disabled on the host.  If it's equal to 0, then we are doing the
- * first open or last close.  Otherwise, open and close just return.
- */
-
-int line_open(struct line *lines, struct tty_struct *tty)
+static int line_activate(struct tty_port *port, struct tty_struct *tty)
 {
-       struct line *line = &lines[tty->index];
-       int err = -ENODEV;
-
-       mutex_lock(&line->count_lock);
-       if (!line->valid)
-               goto out_unlock;
-
-       err = 0;
-       if (line->count++)
-               goto out_unlock;
-
-       BUG_ON(tty->driver_data);
-       tty->driver_data = line;
-       line->tty = tty;
+       int ret;
+       struct line *line = tty->driver_data;
 
-       err = enable_chan(line);
-       if (err) /* line_close() will be called by our caller */
-               goto out_unlock;
+       ret = enable_chan(line);
+       if (ret)
+               return ret;
 
        if (!line->sigio) {
                chan_enable_winch(line->chan_out, tty);
@@ -421,44 +311,60 @@ int line_open(struct line *lines, struct tty_struct *tty)
        }
 
        chan_window_size(line, &tty->winsize.ws_row,
-                        &tty->winsize.ws_col);
-out_unlock:
-       mutex_unlock(&line->count_lock);
-       return err;
+               &tty->winsize.ws_col);
+
+       return 0;
 }
 
-static void unregister_winch(struct tty_struct *tty);
+static const struct tty_port_operations line_port_ops = {
+       .activate = line_activate,
+};
 
-void line_close(struct tty_struct *tty, struct file * filp)
+int line_open(struct tty_struct *tty, struct file *filp)
 {
        struct line *line = tty->driver_data;
 
-       /*
-        * If line_open fails (and tty->driver_data is never set),
-        * tty_open will call line_close.  So just return in this case.
-        */
-       if (line == NULL)
-               return;
+       return tty_port_open(&line->port, tty, filp);
+}
 
-       /* We ignore the error anyway! */
-       flush_buffer(line);
+int line_install(struct tty_driver *driver, struct tty_struct *tty,
+                struct line *line)
+{
+       int ret;
 
-       mutex_lock(&line->count_lock);
-       BUG_ON(!line->valid);
+       ret = tty_standard_install(driver, tty);
+       if (ret)
+               return ret;
 
-       if (--line->count)
-               goto out_unlock;
+       tty->driver_data = line;
 
-       line->tty = NULL;
-       tty->driver_data = NULL;
+       return 0;
+}
+
+static void unregister_winch(struct tty_struct *tty);
+
+void line_cleanup(struct tty_struct *tty)
+{
+       struct line *line = tty->driver_data;
 
        if (line->sigio) {
                unregister_winch(tty);
                line->sigio = 0;
        }
+}
+
+void line_close(struct tty_struct *tty, struct file * filp)
+{
+       struct line *line = tty->driver_data;
 
-out_unlock:
-       mutex_unlock(&line->count_lock);
+       tty_port_close(&line->port, tty, filp);
+}
+
+void line_hangup(struct tty_struct *tty)
+{
+       struct line *line = tty->driver_data;
+
+       tty_port_hangup(&line->port);
 }
 
 void close_lines(struct line *lines, int nlines)
@@ -476,9 +382,7 @@ int setup_one_line(struct line *lines, int n, char *init,
        struct tty_driver *driver = line->driver->driver;
        int err = -EINVAL;
 
-       mutex_lock(&line->count_lock);
-
-       if (line->count) {
+       if (line->port.count) {
                *error_out = "Device is already open";
                goto out;
        }
@@ -519,7 +423,6 @@ int setup_one_line(struct line *lines, int n, char *init,
                }
        }
 out:
-       mutex_unlock(&line->count_lock);
        return err;
 }
 
@@ -607,13 +510,17 @@ int line_get_config(char *name, struct line *lines, unsigned int num, char *str,
 
        line = &lines[dev];
 
-       mutex_lock(&line->count_lock);
        if (!line->valid)
                CONFIG_CHUNK(str, size, n, "none", 1);
-       else if (line->tty == NULL)
-               CONFIG_CHUNK(str, size, n, line->init_str, 1);
-       else n = chan_config_string(line, str, size, error_out);
-       mutex_unlock(&line->count_lock);
+       else {
+               struct tty_struct *tty = tty_port_tty_get(&line->port);
+               if (tty == NULL) {
+                       CONFIG_CHUNK(str, size, n, line->init_str, 1);
+               } else {
+                       n = chan_config_string(line, str, size, error_out);
+                       tty_kref_put(tty);
+               }
+       }
 
        return n;
 }
@@ -663,8 +570,9 @@ int register_lines(struct line_driver *line_driver,
        driver->init_termios = tty_std_termios;
        
        for (i = 0; i < nlines; i++) {
+               tty_port_init(&lines[i].port);
+               lines[i].port.ops = &line_port_ops;
                spin_lock_init(&lines[i].lock);
-               mutex_init(&lines[i].count_lock);
                lines[i].driver = line_driver;
                INIT_LIST_HEAD(&lines[i].chan_list);
        }
@@ -779,8 +687,7 @@ void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty,
                                   .stack       = stack });
 
        if (um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt,
-                          IRQF_SHARED | IRQF_SAMPLE_RANDOM,
-                          "winch", winch) < 0) {
+                          IRQF_SHARED, "winch", winch) < 0) {
                printk(KERN_ERR "register_winch_irq - failed to register "
                       "IRQ\n");
                goto out_free;
index 0a1834719dbaff1e0d6afa6333c1df3a4f8274db..bae95611e7abc3faa1ccaba25f052cfa4b60b246 100644 (file)
@@ -32,9 +32,7 @@ struct line_driver {
 };
 
 struct line {
-       struct tty_struct *tty;
-       struct mutex count_lock;
-       unsigned long count;
+       struct tty_port port;
        int valid;
 
        char *init_str;
@@ -59,7 +57,11 @@ struct line {
 };
 
 extern void line_close(struct tty_struct *tty, struct file * filp);
-extern int line_open(struct line *lines, struct tty_struct *tty);
+extern int line_open(struct tty_struct *tty, struct file *filp);
+extern int line_install(struct tty_driver *driver, struct tty_struct *tty,
+       struct line *line);
+extern void line_cleanup(struct tty_struct *tty);
+extern void line_hangup(struct tty_struct *tty);
 extern int line_setup(char **conf, unsigned nlines, char **def,
                      char *init, char *name);
 extern int line_write(struct tty_struct *tty, const unsigned char *buf,
@@ -70,8 +72,6 @@ extern int line_chars_in_buffer(struct tty_struct *tty);
 extern void line_flush_buffer(struct tty_struct *tty);
 extern void line_flush_chars(struct tty_struct *tty);
 extern int line_write_room(struct tty_struct *tty);
-extern int line_ioctl(struct tty_struct *tty, unsigned int cmd,
-                               unsigned long arg);
 extern void line_throttle(struct tty_struct *tty);
 extern void line_unthrottle(struct tty_struct *tty);
 
index 43b39d61b538698229a23f654b7fb44a335187b3..664a60e8dfb442fe2cb75c1ed5ba388a7bdb42b0 100644 (file)
@@ -774,8 +774,7 @@ static int __init mconsole_init(void)
        register_reboot_notifier(&reboot_notifier);
 
        err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
-                            IRQF_SHARED | IRQF_SAMPLE_RANDOM,
-                            "mconsole", (void *)sock);
+                            IRQF_SHARED, "mconsole", (void *)sock);
        if (err) {
                printk(KERN_ERR "Failed to get IRQ for management console\n");
                goto out;
index 11866ffd45a988c24da3b38c07d806f2f5f2e1c5..1d83d50236e17a6a56e122aad6763b70b9222f9d 100644 (file)
@@ -100,8 +100,7 @@ static int port_accept(struct port_list *port)
                  .port         = port });
 
        if (um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt,
-                         IRQF_SHARED | IRQF_SAMPLE_RANDOM,
-                         "telnetd", conn)) {
+                         IRQF_SHARED, "telnetd", conn)) {
                printk(KERN_ERR "port_accept : failed to get IRQ for "
                       "telnetd\n");
                goto out_free;
@@ -184,8 +183,7 @@ void *port_data(int port_num)
        }
 
        if (um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt,
-                         IRQF_SHARED | IRQF_SAMPLE_RANDOM,
-                         "port", port)) {
+                         IRQF_SHARED, "port", port)) {
                printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num);
                goto out_close;
        }
index b25296e6218ade5fdeee1d3dff4f369bce5accbe..e32c6aa6396fa2df733b807b9252fa9acd755281 100644 (file)
@@ -131,8 +131,7 @@ static int __init rng_init (void)
        random_fd = err;
 
        err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt,
-                            IRQF_SAMPLE_RANDOM, "random",
-                            NULL);
+                            0, "random", NULL);
        if (err)
                goto err_out_cleanup_hw;
 
index e09801a1327bb9ae10d33d8e55def730c1b4fd58..7e86f00701231f665f806b657c6c94f62fc75d42 100644 (file)
@@ -87,40 +87,13 @@ static int ssl_remove(int n, char **error_out)
                           error_out);
 }
 
-static int ssl_open(struct tty_struct *tty, struct file *filp)
-{
-       int err = line_open(serial_lines, tty);
-
-       if (err)
-               printk(KERN_ERR "Failed to open serial line %d, err = %d\n",
-                      tty->index, err);
-
-       return err;
-}
-
-#if 0
-static void ssl_flush_buffer(struct tty_struct *tty)
-{
-       return;
-}
-
-static void ssl_stop(struct tty_struct *tty)
-{
-       printk(KERN_ERR "Someone should implement ssl_stop\n");
-}
-
-static void ssl_start(struct tty_struct *tty)
-{
-       printk(KERN_ERR "Someone should implement ssl_start\n");
-}
-
-void ssl_hangup(struct tty_struct *tty)
+static int ssl_install(struct tty_driver *driver, struct tty_struct *tty)
 {
+       return line_install(driver, tty, &serial_lines[tty->index]);
 }
-#endif
 
 static const struct tty_operations ssl_ops = {
-       .open                   = ssl_open,
+       .open                   = line_open,
        .close                  = line_close,
        .write                  = line_write,
        .put_char               = line_put_char,
@@ -129,14 +102,11 @@ static const struct tty_operations ssl_ops = {
        .flush_buffer           = line_flush_buffer,
        .flush_chars            = line_flush_chars,
        .set_termios            = line_set_termios,
-       .ioctl                  = line_ioctl,
        .throttle               = line_throttle,
        .unthrottle             = line_unthrottle,
-#if 0
-       .stop                   = ssl_stop,
-       .start                  = ssl_start,
-       .hangup                 = ssl_hangup,
-#endif
+       .install                = ssl_install,
+       .cleanup                = line_cleanup,
+       .hangup                 = line_hangup,
 };
 
 /* Changed by ssl_init and referenced by ssl_exit, which are both serialized
index 7663541c372ee06e5be7097ff2eba2e275f86a23..929b99a261f3a538c2ac25845ab33c8d11dbd96b 100644 (file)
@@ -89,21 +89,17 @@ static int con_remove(int n, char **error_out)
        return line_remove(vts, ARRAY_SIZE(vts), n, error_out);
 }
 
-static int con_open(struct tty_struct *tty, struct file *filp)
-{
-       int err = line_open(vts, tty);
-       if (err)
-               printk(KERN_ERR "Failed to open console %d, err = %d\n",
-                      tty->index, err);
-
-       return err;
-}
-
 /* Set in an initcall, checked in an exitcall */
 static int con_init_done = 0;
 
+static int con_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+       return line_install(driver, tty, &vts[tty->index]);
+}
+
 static const struct tty_operations console_ops = {
-       .open                   = con_open,
+       .open                   = line_open,
+       .install                = con_install,
        .close                  = line_close,
        .write                  = line_write,
        .put_char               = line_put_char,
@@ -112,9 +108,10 @@ static const struct tty_operations console_ops = {
        .flush_buffer           = line_flush_buffer,
        .flush_chars            = line_flush_chars,
        .set_termios            = line_set_termios,
-       .ioctl                  = line_ioctl,
        .throttle               = line_throttle,
        .unthrottle             = line_unthrottle,
+       .cleanup                = line_cleanup,
+       .hangup                 = line_hangup,
 };
 
 static void uml_console_write(struct console *console, const char *string,
index 20505cafa29904f176b735f98cdc94eeaac50181..0643e5bc9f41310b78f5b8cc36cc7abb6175e4f3 100644 (file)
@@ -514,7 +514,7 @@ static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
                goto out;
        }
 
-       fd = os_open_file(ubd_dev->file, global_openflags, 0);
+       fd = os_open_file(ubd_dev->file, of_read(OPENFLAGS()), 0);
        if (fd < 0)
                return fd;
 
index b68bbe269e018cb00154781eec3706060e1b1c8d..e3031e69445d133d94a0e335dfd485bd04c17a4b 100644 (file)
@@ -50,8 +50,7 @@ int xterm_fd(int socket, int *pid_out)
        init_completion(&data->ready);
 
        err = um_request_irq(XTERM_IRQ, socket, IRQ_READ, xterm_interrupt,
-                            IRQF_SHARED | IRQF_SAMPLE_RANDOM,
-                            "xterm", data);
+                            IRQF_SHARED, "xterm", data);
        if (err) {
                printk(KERN_ERR "xterm_fd : failed to get IRQ for xterm, "
                       "err = %d\n",  err);
index e786a6a3ec5e88c3ae6fc61d2036096d79304fcc..442f1d025dc2f838096eaa5a77c066933a1db58d 100644 (file)
@@ -37,6 +37,8 @@ extern int putreg(struct task_struct *child, int regno, unsigned long value);
 
 extern int arch_copy_tls(struct task_struct *new);
 extern void clear_flushed_tls(struct task_struct *task);
+extern void syscall_trace_enter(struct pt_regs *regs);
+extern void syscall_trace_leave(struct pt_regs *regs);
 
 #endif
 
index 896e16602176026d4cbb8a0030f88b5f9ac3c23e..86daa546181568fb10b860c76f428b1f1a12dc47 100644 (file)
@@ -60,7 +60,8 @@ extern unsigned long host_task_size;
 
 extern int linux_main(int argc, char **argv);
 
-extern void (*sig_info[])(int, struct uml_pt_regs *);
+struct siginfo;
+extern void (*sig_info[])(int, struct siginfo *si, struct uml_pt_regs *);
 
 #endif
 
index c6c784df2673a93e7ad0d794a9e1ff38824c0791..2b6d703925b57c8ea862ef7fd1706c8770f42396 100644 (file)
@@ -20,7 +20,8 @@ struct irq_fd {
 
 enum { IRQ_READ, IRQ_WRITE };
 
-extern void sigio_handler(int sig, struct uml_pt_regs *regs);
+struct siginfo;
+extern void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
 extern void free_irq_by_fd(int fd);
 extern void reactivate_fd(int fd, int irqnum);
 extern void deactivate_fd(int fd, int irqnum);
index 00965d06d2ca2022f6514c6506fa7e2fbf9adffa..af6b6dc868bab6b82e1351250edcc1d62f873285 100644 (file)
@@ -9,6 +9,8 @@
 #include "sysdep/ptrace.h"
 #include "sysdep/faultinfo.h"
 
+struct siginfo;
+
 extern int uml_exitcode;
 
 extern int ncpus;
@@ -22,7 +24,7 @@ extern void free_stack(unsigned long stack, int order);
 
 extern int do_signal(void);
 extern void interrupt_end(void);
-extern void relay_signal(int sig, struct uml_pt_regs *regs);
+extern void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs);
 
 extern unsigned long segv(struct faultinfo fi, unsigned long ip,
                          int is_user, struct uml_pt_regs *regs);
@@ -33,9 +35,8 @@ extern unsigned int do_IRQ(int irq, struct uml_pt_regs *regs);
 extern int smp_sigio_handler(void);
 extern void initial_thread_cb(void (*proc)(void *), void *arg);
 extern int is_syscall(unsigned long addr);
-extern void timer_handler(int sig, struct uml_pt_regs *regs);
 
-extern void timer_handler(int sig, struct uml_pt_regs *regs);
+extern void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
 
 extern int start_uml(void);
 extern void paging_init(void);
@@ -59,9 +60,9 @@ extern unsigned long from_irq_stack(int nested);
 extern void syscall_trace(struct uml_pt_regs *regs, int entryexit);
 extern int singlestepping(void *t);
 
-extern void segv_handler(int sig, struct uml_pt_regs *regs);
-extern void bus_handler(int sig, struct uml_pt_regs *regs);
-extern void winch(int sig, struct uml_pt_regs *regs);
+extern void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
+extern void bus_handler(int sig, struct siginfo *si, struct uml_pt_regs *regs);
+extern void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
 extern void fatal_sigsegv(void) __attribute__ ((noreturn));
 
 
index 00506c3d5d6e5ce9cabe83fa7163c67fdec4eb7f..9883026f0730ca89f7acde365d51a26e2929d933 100644 (file)
@@ -30,7 +30,7 @@ static struct irq_fd **last_irq_ptr = &active_fds;
 
 extern void free_irqs(void);
 
-void sigio_handler(int sig, struct uml_pt_regs *regs)
+void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
 {
        struct irq_fd *irq_fd;
        int n;
index ccb9a9d283f165760b20fec6bb3b821f814b0a37..57fc7028714a51af6fc19952d0947a913523ccec 100644 (file)
@@ -151,12 +151,10 @@ void new_thread_handler(void)
         * 0 if it just exits
         */
        n = run_kernel_thread(fn, arg, &current->thread.exec_buf);
-       if (n == 1) {
-               /* Handle any immediate reschedules or signals */
-               interrupt_end();
+       if (n == 1)
                userspace(&current->thread.regs.regs);
-       }
-       else do_exit(0);
+       else
+               do_exit(0);
 }
 
 /* Called magically, see new_thread_handler above */
@@ -175,9 +173,6 @@ void fork_handler(void)
 
        current->thread.prev_sched = NULL;
 
-       /* Handle any immediate reschedules or signals */
-       interrupt_end();
-
        userspace(&current->thread.regs.regs);
 }
 
@@ -193,7 +188,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
        if (current->thread.forking) {
                memcpy(&p->thread.regs.regs, &regs->regs,
                       sizeof(p->thread.regs.regs));
-               UPT_SET_SYSCALL_RETURN(&p->thread.regs.regs, 0);
+               PT_REGS_SET_SYSCALL_RETURN(&p->thread.regs, 0);
                if (sp != 0)
                        REGS_SP(p->thread.regs.regs.gp) = sp;
 
index 06b190390505f848a095648d2c555bd9d996604d..694d551c88996dbee7c974487b9299c9f1efc8fe 100644 (file)
@@ -3,11 +3,12 @@
  * Licensed under the GPL
  */
 
-#include "linux/audit.h"
-#include "linux/ptrace.h"
-#include "linux/sched.h"
-#include "asm/uaccess.h"
-#include "skas_ptrace.h"
+#include <linux/audit.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/tracehook.h>
+#include <asm/uaccess.h>
+#include <skas_ptrace.h>
 
 
 
@@ -162,48 +163,36 @@ static void send_sigtrap(struct task_struct *tsk, struct uml_pt_regs *regs,
  * XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and
  * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check
  */
-void syscall_trace(struct uml_pt_regs *regs, int entryexit)
+void syscall_trace_enter(struct pt_regs *regs)
 {
-       int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit;
-       int tracesysgood;
-
-       if (!entryexit)
-               audit_syscall_entry(HOST_AUDIT_ARCH,
-                                   UPT_SYSCALL_NR(regs),
-                                   UPT_SYSCALL_ARG1(regs),
-                                   UPT_SYSCALL_ARG2(regs),
-                                   UPT_SYSCALL_ARG3(regs),
-                                   UPT_SYSCALL_ARG4(regs));
-       else
-               audit_syscall_exit(regs);
-
-       /* Fake a debug trap */
-       if (is_singlestep)
-               send_sigtrap(current, regs, 0);
+       audit_syscall_entry(HOST_AUDIT_ARCH,
+                           UPT_SYSCALL_NR(&regs->regs),
+                           UPT_SYSCALL_ARG1(&regs->regs),
+                           UPT_SYSCALL_ARG2(&regs->regs),
+                           UPT_SYSCALL_ARG3(&regs->regs),
+                           UPT_SYSCALL_ARG4(&regs->regs));
 
        if (!test_thread_flag(TIF_SYSCALL_TRACE))
                return;
 
-       if (!(current->ptrace & PT_PTRACED))
-               return;
+       tracehook_report_syscall_entry(regs);
+}
 
-       /*
-        * the 0x80 provides a way for the tracing parent to distinguish
-        * between a syscall stop and SIGTRAP delivery
-        */
-       tracesysgood = (current->ptrace & PT_TRACESYSGOOD);
-       ptrace_notify(SIGTRAP | (tracesysgood ? 0x80 : 0));
+void syscall_trace_leave(struct pt_regs *regs)
+{
+       int ptraced = current->ptrace;
 
-       if (entryexit) /* force do_signal() --> is_syscall() */
-               set_thread_flag(TIF_SIGPENDING);
+       audit_syscall_exit(regs);
 
-       /*
-        * this isn't the same as continuing with a signal, but it will do
-        * for normal use.  strace only continues with a signal if the
-        * stopping signal is not SIGTRAP.  -brl
-        */
-       if (current->exit_code) {
-               send_sig(current->exit_code, current, 1);
-               current->exit_code = 0;
-       }
+       /* Fake a debug trap */
+       if (ptraced & PT_DTRACE)
+               send_sigtrap(current, &regs->regs, 0);
+
+       if (!test_thread_flag(TIF_SYSCALL_TRACE))
+               return;
+
+       tracehook_report_syscall_exit(regs, 0);
+       /* force do_signal() --> is_syscall() */
+       if (ptraced & PT_PTRACED)
+               set_thread_flag(TIF_SIGPENDING);
 }
index 2a1639255763973eaed54dfda4d20090a6e3bf5d..c88211139a5160ba5b18fa3117445e2b5f351423 100644 (file)
@@ -25,8 +25,7 @@ int write_sigio_irq(int fd)
        int err;
 
        err = um_request_irq(SIGIO_WRITE_IRQ, fd, IRQ_READ, sigio_interrupt,
-                            IRQF_SAMPLE_RANDOM, "write sigio",
-                            NULL);
+                            0, "write sigio", NULL);
        if (err) {
                printk(KERN_ERR "write_sigio_irq : um_request_irq failed, "
                       "err = %d\n", err);
index 05fbeb480e0b554f313185ef97a6482c61b4fb72..86368a025a9684e0bb4dea3707128c4693befe7c 100644 (file)
@@ -18,7 +18,7 @@ void handle_syscall(struct uml_pt_regs *r)
        long result;
        int syscall;
 
-       syscall_trace(r, 0);
+       syscall_trace_enter(regs);
 
        /*
         * This should go in the declaration of syscall, but when I do that,
@@ -34,7 +34,7 @@ void handle_syscall(struct uml_pt_regs *r)
                result = -ENOSYS;
        else result = EXECUTE_SYSCALL(syscall, regs);
 
-       UPT_SET_SYSCALL_RETURN(r, result);
+       PT_REGS_SET_SYSCALL_RETURN(regs, result);
 
-       syscall_trace(r, 1);
+       syscall_trace_leave(regs);
 }
index d1a23fb3190daa9274e033572331c7a9799d5ce4..5f76d4ba151cf0b9cf79f099f8568061a63a631d 100644 (file)
@@ -13,7 +13,7 @@
 #include "kern_util.h"
 #include "os.h"
 
-void timer_handler(int sig, struct uml_pt_regs *regs)
+void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
 {
        unsigned long flags;
 
index 3be60765c0e25d634282ea66e0902a9dc8d41bd4..0353b98ae35a28208c695816bd9b7883ad6fb22e 100644 (file)
@@ -172,7 +172,7 @@ void fatal_sigsegv(void)
        os_dump_core();
 }
 
-void segv_handler(int sig, struct uml_pt_regs *regs)
+void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
 {
        struct faultinfo * fi = UPT_FAULTINFO(regs);
 
@@ -258,8 +258,11 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
        return 0;
 }
 
-void relay_signal(int sig, struct uml_pt_regs *regs)
+void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs)
 {
+       struct faultinfo *fi;
+       struct siginfo clean_si;
+
        if (!UPT_IS_USER(regs)) {
                if (sig == SIGBUS)
                        printk(KERN_ERR "Bus error - the host /dev/shm or /tmp "
@@ -269,18 +272,40 @@ void relay_signal(int sig, struct uml_pt_regs *regs)
 
        arch_examine_signal(sig, regs);
 
-       current->thread.arch.faultinfo = *UPT_FAULTINFO(regs);
-       force_sig(sig, current);
+       memset(&clean_si, 0, sizeof(clean_si));
+       clean_si.si_signo = si->si_signo;
+       clean_si.si_errno = si->si_errno;
+       clean_si.si_code = si->si_code;
+       switch (sig) {
+       case SIGILL:
+       case SIGFPE:
+       case SIGSEGV:
+       case SIGBUS:
+       case SIGTRAP:
+               fi = UPT_FAULTINFO(regs);
+               clean_si.si_addr = (void __user *) FAULT_ADDRESS(*fi);
+               current->thread.arch.faultinfo = *fi;
+#ifdef __ARCH_SI_TRAPNO
+               clean_si.si_trapno = si->si_trapno;
+#endif
+               break;
+       default:
+               printk(KERN_ERR "Attempted to relay unknown signal %d (si_code = %d)\n",
+                       sig, si->si_code);
+       }
+
+       force_sig_info(sig, &clean_si, current);
 }
 
-void bus_handler(int sig, struct uml_pt_regs *regs)
+void bus_handler(int sig, struct siginfo *si, struct uml_pt_regs *regs)
 {
        if (current->thread.fault_catcher != NULL)
                UML_LONGJMP(current->thread.fault_catcher, 1);
-       else relay_signal(sig, regs);
+       else
+               relay_signal(sig, si, regs);
 }
 
-void winch(int sig, struct uml_pt_regs *regs)
+void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
 {
        do_IRQ(WINCH_IRQ, regs);
 }
index 2c3c3ecd8c01d894c4ed8d12f4159209aaabf58d..0dc2c9f135f67d417df719a049a4379e9108406c 100644 (file)
@@ -1 +1 @@
-void alarm_handler(int, mcontext_t *);
+void alarm_handler(int sig, struct siginfo *unused_si, mcontext_t *mc);
index 2d22f1fcd8e244269ed0e55c7c25e0f09a3b7e8a..6366ce904b9b96145cd6c42b317283f33503e14b 100644 (file)
@@ -13,8 +13,9 @@
 #include "kern_util.h"
 #include "os.h"
 #include "sysdep/mcontext.h"
+#include "internal.h"
 
-void (*sig_info[NSIG])(int, struct uml_pt_regs *) = {
+void (*sig_info[NSIG])(int, siginfo_t *, struct uml_pt_regs *) = {
        [SIGTRAP]       = relay_signal,
        [SIGFPE]        = relay_signal,
        [SIGILL]        = relay_signal,
@@ -24,7 +25,7 @@ void (*sig_info[NSIG])(int, struct uml_pt_regs *) = {
        [SIGIO]         = sigio_handler,
        [SIGVTALRM]     = timer_handler };
 
-static void sig_handler_common(int sig, mcontext_t *mc)
+static void sig_handler_common(int sig, siginfo_t *si, mcontext_t *mc)
 {
        struct uml_pt_regs r;
        int save_errno = errno;
@@ -40,7 +41,7 @@ static void sig_handler_common(int sig, mcontext_t *mc)
        if ((sig != SIGIO) && (sig != SIGWINCH) && (sig != SIGVTALRM))
                unblock_signals();
 
-       (*sig_info[sig])(sig, &r);
+       (*sig_info[sig])(sig, si, &r);
 
        errno = save_errno;
 }
@@ -60,7 +61,7 @@ static void sig_handler_common(int sig, mcontext_t *mc)
 static int signals_enabled;
 static unsigned int signals_pending;
 
-void sig_handler(int sig, mcontext_t *mc)
+void sig_handler(int sig, siginfo_t *si, mcontext_t *mc)
 {
        int enabled;
 
@@ -72,7 +73,7 @@ void sig_handler(int sig, mcontext_t *mc)
 
        block_signals();
 
-       sig_handler_common(sig, mc);
+       sig_handler_common(sig, si, mc);
 
        set_signals(enabled);
 }
@@ -85,10 +86,10 @@ static void real_alarm_handler(mcontext_t *mc)
                get_regs_from_mc(&regs, mc);
        regs.is_user = 0;
        unblock_signals();
-       timer_handler(SIGVTALRM, &regs);
+       timer_handler(SIGVTALRM, NULL, &regs);
 }
 
-void alarm_handler(int sig, mcontext_t *mc)
+void alarm_handler(int sig, struct siginfo *unused_si, mcontext_t *mc)
 {
        int enabled;
 
@@ -119,7 +120,7 @@ void set_sigstack(void *sig_stack, int size)
                panic("enabling signal stack failed, errno = %d\n", errno);
 }
 
-static void (*handlers[_NSIG])(int sig, mcontext_t *mc) = {
+static void (*handlers[_NSIG])(int sig, siginfo_t *si, mcontext_t *mc) = {
        [SIGSEGV] = sig_handler,
        [SIGBUS] = sig_handler,
        [SIGILL] = sig_handler,
@@ -132,7 +133,7 @@ static void (*handlers[_NSIG])(int sig, mcontext_t *mc) = {
 };
 
 
-static void hard_handler(int sig, siginfo_t *info, void *p)
+static void hard_handler(int sig, siginfo_t *si, void *p)
 {
        struct ucontext *uc = p;
        mcontext_t *mc = &uc->uc_mcontext;
@@ -161,7 +162,7 @@ static void hard_handler(int sig, siginfo_t *info, void *p)
                while ((sig = ffs(pending)) != 0){
                        sig--;
                        pending &= ~(1 << sig);
-                       (*handlers[sig])(sig, mc);
+                       (*handlers[sig])(sig, si, mc);
                }
 
                /*
@@ -273,9 +274,12 @@ void unblock_signals(void)
                 * Deal with SIGIO first because the alarm handler might
                 * schedule, leaving the pending SIGIO stranded until we come
                 * back here.
+                *
+                * SIGIO's handler doesn't use siginfo or mcontext,
+                * so they can be NULL.
                 */
                if (save_pending & SIGIO_MASK)
-                       sig_handler_common(SIGIO, NULL);
+                       sig_handler_common(SIGIO, NULL, NULL);
 
                if (save_pending & SIGVTALRM_MASK)
                        real_alarm_handler(NULL);
index cd65727854eb607b54291e23f2708ccf02717b87..d93bb40499f7bd57b264b32b5a36ec4a52ed5a6a 100644 (file)
@@ -346,6 +346,10 @@ void userspace(struct uml_pt_regs *regs)
        int err, status, op, pid = userspace_pid[0];
        /* To prevent races if using_sysemu changes under us.*/
        int local_using_sysemu;
+       siginfo_t si;
+
+       /* Handle any immediate reschedules or signals */
+       interrupt_end();
 
        if (getitimer(ITIMER_VIRTUAL, &timer))
                printk(UM_KERN_ERR "Failed to get itimer, errno = %d\n", errno);
@@ -404,13 +408,17 @@ void userspace(struct uml_pt_regs *regs)
 
                if (WIFSTOPPED(status)) {
                        int sig = WSTOPSIG(status);
+
+                       ptrace(PTRACE_GETSIGINFO, pid, 0, &si);
+
                        switch (sig) {
                        case SIGSEGV:
                                if (PTRACE_FULL_FAULTINFO ||
                                    !ptrace_faultinfo) {
                                        get_skas_faultinfo(pid,
                                                           &regs->faultinfo);
-                                       (*sig_info[SIGSEGV])(SIGSEGV, regs);
+                                       (*sig_info[SIGSEGV])(SIGSEGV, &si,
+                                                            regs);
                                }
                                else handle_segv(pid, regs);
                                break;
@@ -418,14 +426,14 @@ void userspace(struct uml_pt_regs *regs)
                                handle_trap(pid, regs, local_using_sysemu);
                                break;
                        case SIGTRAP:
-                               relay_signal(SIGTRAP, regs);
+                               relay_signal(SIGTRAP, &si, regs);
                                break;
                        case SIGVTALRM:
                                now = os_nsecs();
                                if (now < nsecs)
                                        break;
                                block_signals();
-                               (*sig_info[sig])(sig, regs);
+                               (*sig_info[sig])(sig, &si, regs);
                                unblock_signals();
                                nsecs = timer.it_value.tv_sec *
                                        UM_NSEC_PER_SEC +
@@ -439,7 +447,7 @@ void userspace(struct uml_pt_regs *regs)
                        case SIGFPE:
                        case SIGWINCH:
                                block_signals();
-                               (*sig_info[sig])(sig, regs);
+                               (*sig_info[sig])(sig, &si, regs);
                                unblock_signals();
                                break;
                        default:
index 910499d76a678a1830f5ea634c86acfd67276493..f60238559af309e00b031eedb17ad02ec77516d8 100644 (file)
@@ -87,7 +87,7 @@ static int after_sleep_interval(struct timespec *ts)
 
 static void deliver_alarm(void)
 {
-       alarm_handler(SIGVTALRM, NULL);
+       alarm_handler(SIGVTALRM, NULL, NULL);
 }
 
 static unsigned long long sleep_time(unsigned long long nsecs)
index c70684f859e13473908a1370a9a3bb160db5c4e3..ba2657c492171c5ce5c295d1e1d4ef85391608d8 100644 (file)
@@ -70,6 +70,7 @@ config X86
        select HAVE_ARCH_JUMP_LABEL
        select HAVE_TEXT_POKE_SMP
        select HAVE_GENERIC_HARDIRQS
+       select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
        select SPARSE_IRQ
        select GENERIC_FIND_FIRST_BIT
        select GENERIC_IRQ_PROBE
@@ -84,6 +85,7 @@ config X86
        select GENERIC_IOMAP
        select DCACHE_WORD_ACCESS
        select GENERIC_SMP_IDLE_THREAD
+       select ARCH_WANT_IPC_PARSE_VERSION if X86_32
        select HAVE_ARCH_SECCOMP_FILTER
        select BUILDTIME_EXTABLE_SORT
        select GENERIC_CMOS_UPDATE
index c78f14a0df0029a35cbedcb22594fafa1c9110be..dab39350e51e446917d5b5e302fe1ac2214b10d0 100644 (file)
@@ -234,7 +234,7 @@ extern struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr);
 extern void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap);
 extern void perf_check_microcode(void);
 #else
-static inline perf_guest_switch_msr *perf_guest_get_msrs(int *nr)
+static inline struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr)
 {
        *nr = 0;
        return NULL;
index 4437001d8e3d124853e7e12289befb09c12b2e2f..0d9776e9e2dc3f2a539015a5f2930e00d2727a99 100644 (file)
@@ -15,7 +15,6 @@
 # ifdef CONFIG_X86_32
 
 #  include <asm/unistd_32.h>
-#  define __ARCH_WANT_IPC_PARSE_VERSION
 #  define __ARCH_WANT_STAT64
 #  define __ARCH_WANT_SYS_IPC
 #  define __ARCH_WANT_SYS_OLD_MMAP
index a15df4be151fc924e3cf8a9b25ab18c991c0b58c..821d53b696d1408fb1b71faffda28283052d9733 100644 (file)
@@ -374,7 +374,7 @@ struct x86_pmu {
        /*
         * Intel DebugStore bits
         */
-       int             bts             :1,
+       unsigned int    bts             :1,
                        bts_active      :1,
                        pebs            :1,
                        pebs_active     :1,
index 7a8b9d0abcaa33c754481cf38c3f26c366f701c9..382366977d4c7856d2cd354ebccc908da65e1a4b 100644 (file)
@@ -138,6 +138,84 @@ static u64 intel_pmu_event_map(int hw_event)
        return intel_perfmon_event_map[hw_event];
 }
 
+#define SNB_DMND_DATA_RD       (1ULL << 0)
+#define SNB_DMND_RFO           (1ULL << 1)
+#define SNB_DMND_IFETCH                (1ULL << 2)
+#define SNB_DMND_WB            (1ULL << 3)
+#define SNB_PF_DATA_RD         (1ULL << 4)
+#define SNB_PF_RFO             (1ULL << 5)
+#define SNB_PF_IFETCH          (1ULL << 6)
+#define SNB_LLC_DATA_RD                (1ULL << 7)
+#define SNB_LLC_RFO            (1ULL << 8)
+#define SNB_LLC_IFETCH         (1ULL << 9)
+#define SNB_BUS_LOCKS          (1ULL << 10)
+#define SNB_STRM_ST            (1ULL << 11)
+#define SNB_OTHER              (1ULL << 15)
+#define SNB_RESP_ANY           (1ULL << 16)
+#define SNB_NO_SUPP            (1ULL << 17)
+#define SNB_LLC_HITM           (1ULL << 18)
+#define SNB_LLC_HITE           (1ULL << 19)
+#define SNB_LLC_HITS           (1ULL << 20)
+#define SNB_LLC_HITF           (1ULL << 21)
+#define SNB_LOCAL              (1ULL << 22)
+#define SNB_REMOTE             (0xffULL << 23)
+#define SNB_SNP_NONE           (1ULL << 31)
+#define SNB_SNP_NOT_NEEDED     (1ULL << 32)
+#define SNB_SNP_MISS           (1ULL << 33)
+#define SNB_NO_FWD             (1ULL << 34)
+#define SNB_SNP_FWD            (1ULL << 35)
+#define SNB_HITM               (1ULL << 36)
+#define SNB_NON_DRAM           (1ULL << 37)
+
+#define SNB_DMND_READ          (SNB_DMND_DATA_RD|SNB_LLC_DATA_RD)
+#define SNB_DMND_WRITE         (SNB_DMND_RFO|SNB_LLC_RFO)
+#define SNB_DMND_PREFETCH      (SNB_PF_DATA_RD|SNB_PF_RFO)
+
+#define SNB_SNP_ANY            (SNB_SNP_NONE|SNB_SNP_NOT_NEEDED| \
+                                SNB_SNP_MISS|SNB_NO_FWD|SNB_SNP_FWD| \
+                                SNB_HITM)
+
+#define SNB_DRAM_ANY           (SNB_LOCAL|SNB_REMOTE|SNB_SNP_ANY)
+#define SNB_DRAM_REMOTE                (SNB_REMOTE|SNB_SNP_ANY)
+
+#define SNB_L3_ACCESS          SNB_RESP_ANY
+#define SNB_L3_MISS            (SNB_DRAM_ANY|SNB_NON_DRAM)
+
+static __initconst const u64 snb_hw_cache_extra_regs
+                               [PERF_COUNT_HW_CACHE_MAX]
+                               [PERF_COUNT_HW_CACHE_OP_MAX]
+                               [PERF_COUNT_HW_CACHE_RESULT_MAX] =
+{
+ [ C(LL  ) ] = {
+       [ C(OP_READ) ] = {
+               [ C(RESULT_ACCESS) ] = SNB_DMND_READ|SNB_L3_ACCESS,
+               [ C(RESULT_MISS)   ] = SNB_DMND_READ|SNB_L3_MISS,
+       },
+       [ C(OP_WRITE) ] = {
+               [ C(RESULT_ACCESS) ] = SNB_DMND_WRITE|SNB_L3_ACCESS,
+               [ C(RESULT_MISS)   ] = SNB_DMND_WRITE|SNB_L3_MISS,
+       },
+       [ C(OP_PREFETCH) ] = {
+               [ C(RESULT_ACCESS) ] = SNB_DMND_PREFETCH|SNB_L3_ACCESS,
+               [ C(RESULT_MISS)   ] = SNB_DMND_PREFETCH|SNB_L3_MISS,
+       },
+ },
+ [ C(NODE) ] = {
+       [ C(OP_READ) ] = {
+               [ C(RESULT_ACCESS) ] = SNB_DMND_READ|SNB_DRAM_ANY,
+               [ C(RESULT_MISS)   ] = SNB_DMND_READ|SNB_DRAM_REMOTE,
+       },
+       [ C(OP_WRITE) ] = {
+               [ C(RESULT_ACCESS) ] = SNB_DMND_WRITE|SNB_DRAM_ANY,
+               [ C(RESULT_MISS)   ] = SNB_DMND_WRITE|SNB_DRAM_REMOTE,
+       },
+       [ C(OP_PREFETCH) ] = {
+               [ C(RESULT_ACCESS) ] = SNB_DMND_PREFETCH|SNB_DRAM_ANY,
+               [ C(RESULT_MISS)   ] = SNB_DMND_PREFETCH|SNB_DRAM_REMOTE,
+       },
+ },
+};
+
 static __initconst const u64 snb_hw_cache_event_ids
                                [PERF_COUNT_HW_CACHE_MAX]
                                [PERF_COUNT_HW_CACHE_OP_MAX]
@@ -235,16 +313,16 @@ static __initconst const u64 snb_hw_cache_event_ids
  },
  [ C(NODE) ] = {
        [ C(OP_READ) ] = {
-               [ C(RESULT_ACCESS) ] = -1,
-               [ C(RESULT_MISS)   ] = -1,
+               [ C(RESULT_ACCESS) ] = 0x01b7,
+               [ C(RESULT_MISS)   ] = 0x01b7,
        },
        [ C(OP_WRITE) ] = {
-               [ C(RESULT_ACCESS) ] = -1,
-               [ C(RESULT_MISS)   ] = -1,
+               [ C(RESULT_ACCESS) ] = 0x01b7,
+               [ C(RESULT_MISS)   ] = 0x01b7,
        },
        [ C(OP_PREFETCH) ] = {
-               [ C(RESULT_ACCESS) ] = -1,
-               [ C(RESULT_MISS)   ] = -1,
+               [ C(RESULT_ACCESS) ] = 0x01b7,
+               [ C(RESULT_MISS)   ] = 0x01b7,
        },
  },
 
@@ -1964,6 +2042,8 @@ __init int intel_pmu_init(void)
        case 58: /* IvyBridge */
                memcpy(hw_cache_event_ids, snb_hw_cache_event_ids,
                       sizeof(hw_cache_event_ids));
+               memcpy(hw_cache_extra_regs, snb_hw_cache_extra_regs,
+                      sizeof(hw_cache_extra_regs));
 
                intel_pmu_lbr_init_snb();
 
index 19faffc608865fe8e14a29200a32cb01b8c3b268..7563fda9f0339b935c7b40a347409b966f7ffbdc 100644 (file)
@@ -18,6 +18,7 @@ static struct event_constraint constraint_empty =
        EVENT_CONSTRAINT(0, 0, 0);
 
 DEFINE_UNCORE_FORMAT_ATTR(event, event, "config:0-7");
+DEFINE_UNCORE_FORMAT_ATTR(event_ext, event, "config:0-7,21");
 DEFINE_UNCORE_FORMAT_ATTR(umask, umask, "config:8-15");
 DEFINE_UNCORE_FORMAT_ATTR(edge, edge, "config:18");
 DEFINE_UNCORE_FORMAT_ATTR(tid_en, tid_en, "config:19");
@@ -33,10 +34,81 @@ DEFINE_UNCORE_FORMAT_ATTR(filter_tid, filter_tid, "config1:0-4");
 DEFINE_UNCORE_FORMAT_ATTR(filter_nid, filter_nid, "config1:10-17");
 DEFINE_UNCORE_FORMAT_ATTR(filter_state, filter_state, "config1:18-22");
 DEFINE_UNCORE_FORMAT_ATTR(filter_opc, filter_opc, "config1:23-31");
-DEFINE_UNCORE_FORMAT_ATTR(filter_brand0, filter_brand0, "config1:0-7");
-DEFINE_UNCORE_FORMAT_ATTR(filter_brand1, filter_brand1, "config1:8-15");
-DEFINE_UNCORE_FORMAT_ATTR(filter_brand2, filter_brand2, "config1:16-23");
-DEFINE_UNCORE_FORMAT_ATTR(filter_brand3, filter_brand3, "config1:24-31");
+DEFINE_UNCORE_FORMAT_ATTR(filter_band0, filter_band0, "config1:0-7");
+DEFINE_UNCORE_FORMAT_ATTR(filter_band1, filter_band1, "config1:8-15");
+DEFINE_UNCORE_FORMAT_ATTR(filter_band2, filter_band2, "config1:16-23");
+DEFINE_UNCORE_FORMAT_ATTR(filter_band3, filter_band3, "config1:24-31");
+
+static u64 uncore_msr_read_counter(struct intel_uncore_box *box, struct perf_event *event)
+{
+       u64 count;
+
+       rdmsrl(event->hw.event_base, count);
+
+       return count;
+}
+
+/*
+ * generic get constraint function for shared match/mask registers.
+ */
+static struct event_constraint *
+uncore_get_constraint(struct intel_uncore_box *box, struct perf_event *event)
+{
+       struct intel_uncore_extra_reg *er;
+       struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
+       struct hw_perf_event_extra *reg2 = &event->hw.branch_reg;
+       unsigned long flags;
+       bool ok = false;
+
+       /*
+        * reg->alloc can be set due to existing state, so for fake box we
+        * need to ignore this, otherwise we might fail to allocate proper
+        * fake state for this extra reg constraint.
+        */
+       if (reg1->idx == EXTRA_REG_NONE ||
+           (!uncore_box_is_fake(box) && reg1->alloc))
+               return NULL;
+
+       er = &box->shared_regs[reg1->idx];
+       raw_spin_lock_irqsave(&er->lock, flags);
+       if (!atomic_read(&er->ref) ||
+           (er->config1 == reg1->config && er->config2 == reg2->config)) {
+               atomic_inc(&er->ref);
+               er->config1 = reg1->config;
+               er->config2 = reg2->config;
+               ok = true;
+       }
+       raw_spin_unlock_irqrestore(&er->lock, flags);
+
+       if (ok) {
+               if (!uncore_box_is_fake(box))
+                       reg1->alloc = 1;
+               return NULL;
+       }
+
+       return &constraint_empty;
+}
+
+static void uncore_put_constraint(struct intel_uncore_box *box, struct perf_event *event)
+{
+       struct intel_uncore_extra_reg *er;
+       struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
+
+       /*
+        * Only put constraint if extra reg was actually allocated. Also
+        * takes care of event which do not use an extra shared reg.
+        *
+        * Also, if this is a fake box we shouldn't touch any event state
+        * (reg->alloc) and we don't care about leaving inconsistent box
+        * state either since it will be thrown out.
+        */
+       if (uncore_box_is_fake(box) || !reg1->alloc)
+               return;
+
+       er = &box->shared_regs[reg1->idx];
+       atomic_dec(&er->ref);
+       reg1->alloc = 0;
+}
 
 /* Sandy Bridge-EP uncore support */
 static struct intel_uncore_type snbep_uncore_cbox;
@@ -64,18 +136,15 @@ static void snbep_uncore_pci_enable_box(struct intel_uncore_box *box)
        pci_write_config_dword(pdev, box_ctl, config);
 }
 
-static void snbep_uncore_pci_enable_event(struct intel_uncore_box *box,
-                                       struct perf_event *event)
+static void snbep_uncore_pci_enable_event(struct intel_uncore_box *box, struct perf_event *event)
 {
        struct pci_dev *pdev = box->pci_dev;
        struct hw_perf_event *hwc = &event->hw;
 
-       pci_write_config_dword(pdev, hwc->config_base, hwc->config |
-                               SNBEP_PMON_CTL_EN);
+       pci_write_config_dword(pdev, hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN);
 }
 
-static void snbep_uncore_pci_disable_event(struct intel_uncore_box *box,
-                                       struct perf_event *event)
+static void snbep_uncore_pci_disable_event(struct intel_uncore_box *box, struct perf_event *event)
 {
        struct pci_dev *pdev = box->pci_dev;
        struct hw_perf_event *hwc = &event->hw;
@@ -83,8 +152,7 @@ static void snbep_uncore_pci_disable_event(struct intel_uncore_box *box,
        pci_write_config_dword(pdev, hwc->config_base, hwc->config);
 }
 
-static u64 snbep_uncore_pci_read_counter(struct intel_uncore_box *box,
-                                       struct perf_event *event)
+static u64 snbep_uncore_pci_read_counter(struct intel_uncore_box *box, struct perf_event *event)
 {
        struct pci_dev *pdev = box->pci_dev;
        struct hw_perf_event *hwc = &event->hw;
@@ -92,14 +160,15 @@ static u64 snbep_uncore_pci_read_counter(struct intel_uncore_box *box,
 
        pci_read_config_dword(pdev, hwc->event_base, (u32 *)&count);
        pci_read_config_dword(pdev, hwc->event_base + 4, (u32 *)&count + 1);
+
        return count;
 }
 
 static void snbep_uncore_pci_init_box(struct intel_uncore_box *box)
 {
        struct pci_dev *pdev = box->pci_dev;
-       pci_write_config_dword(pdev, SNBEP_PCI_PMON_BOX_CTL,
-                               SNBEP_PMON_BOX_CTL_INT);
+
+       pci_write_config_dword(pdev, SNBEP_PCI_PMON_BOX_CTL, SNBEP_PMON_BOX_CTL_INT);
 }
 
 static void snbep_uncore_msr_disable_box(struct intel_uncore_box *box)
@@ -112,7 +181,6 @@ static void snbep_uncore_msr_disable_box(struct intel_uncore_box *box)
                rdmsrl(msr, config);
                config |= SNBEP_PMON_BOX_CTL_FRZ;
                wrmsrl(msr, config);
-               return;
        }
 }
 
@@ -126,12 +194,10 @@ static void snbep_uncore_msr_enable_box(struct intel_uncore_box *box)
                rdmsrl(msr, config);
                config &= ~SNBEP_PMON_BOX_CTL_FRZ;
                wrmsrl(msr, config);
-               return;
        }
 }
 
-static void snbep_uncore_msr_enable_event(struct intel_uncore_box *box,
-                                       struct perf_event *event)
+static void snbep_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
 {
        struct hw_perf_event *hwc = &event->hw;
        struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
@@ -150,68 +216,15 @@ static void snbep_uncore_msr_disable_event(struct intel_uncore_box *box,
        wrmsrl(hwc->config_base, hwc->config);
 }
 
-static u64 snbep_uncore_msr_read_counter(struct intel_uncore_box *box,
-                                       struct perf_event *event)
-{
-       struct hw_perf_event *hwc = &event->hw;
-       u64 count;
-
-       rdmsrl(hwc->event_base, count);
-       return count;
-}
-
 static void snbep_uncore_msr_init_box(struct intel_uncore_box *box)
 {
        unsigned msr = uncore_msr_box_ctl(box);
+
        if (msr)
                wrmsrl(msr, SNBEP_PMON_BOX_CTL_INT);
 }
 
-static struct event_constraint *
-snbep_uncore_get_constraint(struct intel_uncore_box *box,
-                           struct perf_event *event)
-{
-       struct intel_uncore_extra_reg *er;
-       struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
-       unsigned long flags;
-       bool ok = false;
-
-       if (reg1->idx == EXTRA_REG_NONE || (box->phys_id >= 0 && reg1->alloc))
-               return NULL;
-
-       er = &box->shared_regs[reg1->idx];
-       raw_spin_lock_irqsave(&er->lock, flags);
-       if (!atomic_read(&er->ref) || er->config1 == reg1->config) {
-               atomic_inc(&er->ref);
-               er->config1 = reg1->config;
-               ok = true;
-       }
-       raw_spin_unlock_irqrestore(&er->lock, flags);
-
-       if (ok) {
-               if (box->phys_id >= 0)
-                       reg1->alloc = 1;
-               return NULL;
-       }
-       return &constraint_empty;
-}
-
-static void snbep_uncore_put_constraint(struct intel_uncore_box *box,
-                                       struct perf_event *event)
-{
-       struct intel_uncore_extra_reg *er;
-       struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
-
-       if (box->phys_id < 0 || !reg1->alloc)
-               return;
-
-       er = &box->shared_regs[reg1->idx];
-       atomic_dec(&er->ref);
-       reg1->alloc = 0;
-}
-
-static int snbep_uncore_hw_config(struct intel_uncore_box *box,
-                                 struct perf_event *event)
+static int snbep_uncore_hw_config(struct intel_uncore_box *box, struct perf_event *event)
 {
        struct hw_perf_event *hwc = &event->hw;
        struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
@@ -221,14 +234,16 @@ static int snbep_uncore_hw_config(struct intel_uncore_box *box,
                        SNBEP_CBO_MSR_OFFSET * box->pmu->pmu_idx;
                reg1->config = event->attr.config1 &
                        SNBEP_CB0_MSR_PMON_BOX_FILTER_MASK;
-       } else if (box->pmu->type == &snbep_uncore_pcu) {
-               reg1->reg = SNBEP_PCU_MSR_PMON_BOX_FILTER;
-               reg1->config = event->attr.config1 &
-                       SNBEP_PCU_MSR_PMON_BOX_FILTER_MASK;
        } else {
-               return 0;
+               if (box->pmu->type == &snbep_uncore_pcu) {
+                       reg1->reg = SNBEP_PCU_MSR_PMON_BOX_FILTER;
+                       reg1->config = event->attr.config1 & SNBEP_PCU_MSR_PMON_BOX_FILTER_MASK;
+               } else {
+                       return 0;
+               }
        }
        reg1->idx = 0;
+
        return 0;
 }
 
@@ -272,10 +287,19 @@ static struct attribute *snbep_uncore_pcu_formats_attr[] = {
        &format_attr_thresh5.attr,
        &format_attr_occ_invert.attr,
        &format_attr_occ_edge.attr,
-       &format_attr_filter_brand0.attr,
-       &format_attr_filter_brand1.attr,
-       &format_attr_filter_brand2.attr,
-       &format_attr_filter_brand3.attr,
+       &format_attr_filter_band0.attr,
+       &format_attr_filter_band1.attr,
+       &format_attr_filter_band2.attr,
+       &format_attr_filter_band3.attr,
+       NULL,
+};
+
+static struct attribute *snbep_uncore_qpi_formats_attr[] = {
+       &format_attr_event_ext.attr,
+       &format_attr_umask.attr,
+       &format_attr_edge.attr,
+       &format_attr_inv.attr,
+       &format_attr_thresh8.attr,
        NULL,
 };
 
@@ -314,15 +338,20 @@ static struct attribute_group snbep_uncore_pcu_format_group = {
        .attrs = snbep_uncore_pcu_formats_attr,
 };
 
+static struct attribute_group snbep_uncore_qpi_format_group = {
+       .name = "format",
+       .attrs = snbep_uncore_qpi_formats_attr,
+};
+
 static struct intel_uncore_ops snbep_uncore_msr_ops = {
        .init_box       = snbep_uncore_msr_init_box,
        .disable_box    = snbep_uncore_msr_disable_box,
        .enable_box     = snbep_uncore_msr_enable_box,
        .disable_event  = snbep_uncore_msr_disable_event,
        .enable_event   = snbep_uncore_msr_enable_event,
-       .read_counter   = snbep_uncore_msr_read_counter,
-       .get_constraint = snbep_uncore_get_constraint,
-       .put_constraint = snbep_uncore_put_constraint,
+       .read_counter   = uncore_msr_read_counter,
+       .get_constraint = uncore_get_constraint,
+       .put_constraint = uncore_put_constraint,
        .hw_config      = snbep_uncore_hw_config,
 };
 
@@ -485,8 +514,13 @@ static struct intel_uncore_type snbep_uncore_qpi = {
        .num_counters   = 4,
        .num_boxes      = 2,
        .perf_ctr_bits  = 48,
+       .perf_ctr       = SNBEP_PCI_PMON_CTR0,
+       .event_ctl      = SNBEP_PCI_PMON_CTL0,
+       .event_mask     = SNBEP_QPI_PCI_PMON_RAW_EVENT_MASK,
+       .box_ctl        = SNBEP_PCI_PMON_BOX_CTL,
+       .ops            = &snbep_uncore_pci_ops,
        .event_descs    = snbep_uncore_qpi_events,
-       SNBEP_UNCORE_PCI_COMMON_INIT(),
+       .format_group   = &snbep_uncore_qpi_format_group,
 };
 
 
@@ -589,188 +623,1208 @@ static void snbep_pci2phy_map_init(void)
                /* get the Node ID mapping */
                pci_read_config_dword(ubox_dev, 0x54, &config);
                /*
-                * every three bits in the Node ID mapping register maps
-                * to a particular node.
+                * every three bits in the Node ID mapping register maps
+                * to a particular node.
+                */
+               for (i = 0; i < 8; i++) {
+                       if (nodeid == ((config >> (3 * i)) & 0x7)) {
+                               pcibus_to_physid[bus] = i;
+                               break;
+                       }
+               }
+       };
+       return;
+}
+/* end of Sandy Bridge-EP uncore support */
+
+/* Sandy Bridge uncore support */
+static void snb_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
+{
+       struct hw_perf_event *hwc = &event->hw;
+
+       if (hwc->idx < UNCORE_PMC_IDX_FIXED)
+               wrmsrl(hwc->config_base, hwc->config | SNB_UNC_CTL_EN);
+       else
+               wrmsrl(hwc->config_base, SNB_UNC_CTL_EN);
+}
+
+static void snb_uncore_msr_disable_event(struct intel_uncore_box *box, struct perf_event *event)
+{
+       wrmsrl(event->hw.config_base, 0);
+}
+
+static void snb_uncore_msr_init_box(struct intel_uncore_box *box)
+{
+       if (box->pmu->pmu_idx == 0) {
+               wrmsrl(SNB_UNC_PERF_GLOBAL_CTL,
+                       SNB_UNC_GLOBAL_CTL_EN | SNB_UNC_GLOBAL_CTL_CORE_ALL);
+       }
+}
+
+static struct attribute *snb_uncore_formats_attr[] = {
+       &format_attr_event.attr,
+       &format_attr_umask.attr,
+       &format_attr_edge.attr,
+       &format_attr_inv.attr,
+       &format_attr_cmask5.attr,
+       NULL,
+};
+
+static struct attribute_group snb_uncore_format_group = {
+       .name           = "format",
+       .attrs          = snb_uncore_formats_attr,
+};
+
+static struct intel_uncore_ops snb_uncore_msr_ops = {
+       .init_box       = snb_uncore_msr_init_box,
+       .disable_event  = snb_uncore_msr_disable_event,
+       .enable_event   = snb_uncore_msr_enable_event,
+       .read_counter   = uncore_msr_read_counter,
+};
+
+static struct event_constraint snb_uncore_cbox_constraints[] = {
+       UNCORE_EVENT_CONSTRAINT(0x80, 0x1),
+       UNCORE_EVENT_CONSTRAINT(0x83, 0x1),
+       EVENT_CONSTRAINT_END
+};
+
+static struct intel_uncore_type snb_uncore_cbox = {
+       .name           = "cbox",
+       .num_counters   = 2,
+       .num_boxes      = 4,
+       .perf_ctr_bits  = 44,
+       .fixed_ctr_bits = 48,
+       .perf_ctr       = SNB_UNC_CBO_0_PER_CTR0,
+       .event_ctl      = SNB_UNC_CBO_0_PERFEVTSEL0,
+       .fixed_ctr      = SNB_UNC_FIXED_CTR,
+       .fixed_ctl      = SNB_UNC_FIXED_CTR_CTRL,
+       .single_fixed   = 1,
+       .event_mask     = SNB_UNC_RAW_EVENT_MASK,
+       .msr_offset     = SNB_UNC_CBO_MSR_OFFSET,
+       .constraints    = snb_uncore_cbox_constraints,
+       .ops            = &snb_uncore_msr_ops,
+       .format_group   = &snb_uncore_format_group,
+};
+
+static struct intel_uncore_type *snb_msr_uncores[] = {
+       &snb_uncore_cbox,
+       NULL,
+};
+/* end of Sandy Bridge uncore support */
+
+/* Nehalem uncore support */
+static void nhm_uncore_msr_disable_box(struct intel_uncore_box *box)
+{
+       wrmsrl(NHM_UNC_PERF_GLOBAL_CTL, 0);
+}
+
+static void nhm_uncore_msr_enable_box(struct intel_uncore_box *box)
+{
+       wrmsrl(NHM_UNC_PERF_GLOBAL_CTL, NHM_UNC_GLOBAL_CTL_EN_PC_ALL | NHM_UNC_GLOBAL_CTL_EN_FC);
+}
+
+static void nhm_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
+{
+       struct hw_perf_event *hwc = &event->hw;
+
+       if (hwc->idx < UNCORE_PMC_IDX_FIXED)
+               wrmsrl(hwc->config_base, hwc->config | SNB_UNC_CTL_EN);
+       else
+               wrmsrl(hwc->config_base, NHM_UNC_FIXED_CTR_CTL_EN);
+}
+
+static struct attribute *nhm_uncore_formats_attr[] = {
+       &format_attr_event.attr,
+       &format_attr_umask.attr,
+       &format_attr_edge.attr,
+       &format_attr_inv.attr,
+       &format_attr_cmask8.attr,
+       NULL,
+};
+
+static struct attribute_group nhm_uncore_format_group = {
+       .name = "format",
+       .attrs = nhm_uncore_formats_attr,
+};
+
+static struct uncore_event_desc nhm_uncore_events[] = {
+       INTEL_UNCORE_EVENT_DESC(clockticks,                "event=0xff,umask=0x00"),
+       INTEL_UNCORE_EVENT_DESC(qmc_writes_full_any,       "event=0x2f,umask=0x0f"),
+       INTEL_UNCORE_EVENT_DESC(qmc_normal_reads_any,      "event=0x2c,umask=0x0f"),
+       INTEL_UNCORE_EVENT_DESC(qhl_request_ioh_reads,     "event=0x20,umask=0x01"),
+       INTEL_UNCORE_EVENT_DESC(qhl_request_ioh_writes,    "event=0x20,umask=0x02"),
+       INTEL_UNCORE_EVENT_DESC(qhl_request_remote_reads,  "event=0x20,umask=0x04"),
+       INTEL_UNCORE_EVENT_DESC(qhl_request_remote_writes, "event=0x20,umask=0x08"),
+       INTEL_UNCORE_EVENT_DESC(qhl_request_local_reads,   "event=0x20,umask=0x10"),
+       INTEL_UNCORE_EVENT_DESC(qhl_request_local_writes,  "event=0x20,umask=0x20"),
+       { /* end: all zeroes */ },
+};
+
+static struct intel_uncore_ops nhm_uncore_msr_ops = {
+       .disable_box    = nhm_uncore_msr_disable_box,
+       .enable_box     = nhm_uncore_msr_enable_box,
+       .disable_event  = snb_uncore_msr_disable_event,
+       .enable_event   = nhm_uncore_msr_enable_event,
+       .read_counter   = uncore_msr_read_counter,
+};
+
+static struct intel_uncore_type nhm_uncore = {
+       .name           = "",
+       .num_counters   = 8,
+       .num_boxes      = 1,
+       .perf_ctr_bits  = 48,
+       .fixed_ctr_bits = 48,
+       .event_ctl      = NHM_UNC_PERFEVTSEL0,
+       .perf_ctr       = NHM_UNC_UNCORE_PMC0,
+       .fixed_ctr      = NHM_UNC_FIXED_CTR,
+       .fixed_ctl      = NHM_UNC_FIXED_CTR_CTRL,
+       .event_mask     = NHM_UNC_RAW_EVENT_MASK,
+       .event_descs    = nhm_uncore_events,
+       .ops            = &nhm_uncore_msr_ops,
+       .format_group   = &nhm_uncore_format_group,
+};
+
+static struct intel_uncore_type *nhm_msr_uncores[] = {
+       &nhm_uncore,
+       NULL,
+};
+/* end of Nehalem uncore support */
+
+/* Nehalem-EX uncore support */
+#define __BITS_VALUE(x, i, n)  ((typeof(x))(((x) >> ((i) * (n))) & \
+                               ((1ULL << (n)) - 1)))
+
+DEFINE_UNCORE_FORMAT_ATTR(event5, event, "config:1-5");
+DEFINE_UNCORE_FORMAT_ATTR(counter, counter, "config:6-7");
+DEFINE_UNCORE_FORMAT_ATTR(mm_cfg, mm_cfg, "config:63");
+DEFINE_UNCORE_FORMAT_ATTR(match, match, "config1:0-63");
+DEFINE_UNCORE_FORMAT_ATTR(mask, mask, "config2:0-63");
+
+static void nhmex_uncore_msr_init_box(struct intel_uncore_box *box)
+{
+       wrmsrl(NHMEX_U_MSR_PMON_GLOBAL_CTL, NHMEX_U_PMON_GLOBAL_EN_ALL);
+}
+
+static void nhmex_uncore_msr_disable_box(struct intel_uncore_box *box)
+{
+       unsigned msr = uncore_msr_box_ctl(box);
+       u64 config;
+
+       if (msr) {
+               rdmsrl(msr, config);
+               config &= ~((1ULL << uncore_num_counters(box)) - 1);
+               /* WBox has a fixed counter */
+               if (uncore_msr_fixed_ctl(box))
+                       config &= ~NHMEX_W_PMON_GLOBAL_FIXED_EN;
+               wrmsrl(msr, config);
+       }
+}
+
+static void nhmex_uncore_msr_enable_box(struct intel_uncore_box *box)
+{
+       unsigned msr = uncore_msr_box_ctl(box);
+       u64 config;
+
+       if (msr) {
+               rdmsrl(msr, config);
+               config |= (1ULL << uncore_num_counters(box)) - 1;
+               /* WBox has a fixed counter */
+               if (uncore_msr_fixed_ctl(box))
+                       config |= NHMEX_W_PMON_GLOBAL_FIXED_EN;
+               wrmsrl(msr, config);
+       }
+}
+
+static void nhmex_uncore_msr_disable_event(struct intel_uncore_box *box, struct perf_event *event)
+{
+       wrmsrl(event->hw.config_base, 0);
+}
+
+static void nhmex_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
+{
+       struct hw_perf_event *hwc = &event->hw;
+
+       if (hwc->idx >= UNCORE_PMC_IDX_FIXED)
+               wrmsrl(hwc->config_base, NHMEX_PMON_CTL_EN_BIT0);
+       else if (box->pmu->type->event_mask & NHMEX_PMON_CTL_EN_BIT0)
+               wrmsrl(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT22);
+       else
+               wrmsrl(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT0);
+}
+
+#define NHMEX_UNCORE_OPS_COMMON_INIT()                         \
+       .init_box       = nhmex_uncore_msr_init_box,            \
+       .disable_box    = nhmex_uncore_msr_disable_box,         \
+       .enable_box     = nhmex_uncore_msr_enable_box,          \
+       .disable_event  = nhmex_uncore_msr_disable_event,       \
+       .read_counter   = uncore_msr_read_counter
+
+static struct intel_uncore_ops nhmex_uncore_ops = {
+       NHMEX_UNCORE_OPS_COMMON_INIT(),
+       .enable_event   = nhmex_uncore_msr_enable_event,
+};
+
+static struct attribute *nhmex_uncore_ubox_formats_attr[] = {
+       &format_attr_event.attr,
+       &format_attr_edge.attr,
+       NULL,
+};
+
+static struct attribute_group nhmex_uncore_ubox_format_group = {
+       .name           = "format",
+       .attrs          = nhmex_uncore_ubox_formats_attr,
+};
+
+static struct intel_uncore_type nhmex_uncore_ubox = {
+       .name           = "ubox",
+       .num_counters   = 1,
+       .num_boxes      = 1,
+       .perf_ctr_bits  = 48,
+       .event_ctl      = NHMEX_U_MSR_PMON_EV_SEL,
+       .perf_ctr       = NHMEX_U_MSR_PMON_CTR,
+       .event_mask     = NHMEX_U_PMON_RAW_EVENT_MASK,
+       .box_ctl        = NHMEX_U_MSR_PMON_GLOBAL_CTL,
+       .ops            = &nhmex_uncore_ops,
+       .format_group   = &nhmex_uncore_ubox_format_group
+};
+
+static struct attribute *nhmex_uncore_cbox_formats_attr[] = {
+       &format_attr_event.attr,
+       &format_attr_umask.attr,
+       &format_attr_edge.attr,
+       &format_attr_inv.attr,
+       &format_attr_thresh8.attr,
+       NULL,
+};
+
+static struct attribute_group nhmex_uncore_cbox_format_group = {
+       .name = "format",
+       .attrs = nhmex_uncore_cbox_formats_attr,
+};
+
+static struct intel_uncore_type nhmex_uncore_cbox = {
+       .name                   = "cbox",
+       .num_counters           = 6,
+       .num_boxes              = 8,
+       .perf_ctr_bits          = 48,
+       .event_ctl              = NHMEX_C0_MSR_PMON_EV_SEL0,
+       .perf_ctr               = NHMEX_C0_MSR_PMON_CTR0,
+       .event_mask             = NHMEX_PMON_RAW_EVENT_MASK,
+       .box_ctl                = NHMEX_C0_MSR_PMON_GLOBAL_CTL,
+       .msr_offset             = NHMEX_C_MSR_OFFSET,
+       .pair_ctr_ctl           = 1,
+       .ops                    = &nhmex_uncore_ops,
+       .format_group           = &nhmex_uncore_cbox_format_group
+};
+
+static struct uncore_event_desc nhmex_uncore_wbox_events[] = {
+       INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0"),
+       { /* end: all zeroes */ },
+};
+
+static struct intel_uncore_type nhmex_uncore_wbox = {
+       .name                   = "wbox",
+       .num_counters           = 4,
+       .num_boxes              = 1,
+       .perf_ctr_bits          = 48,
+       .event_ctl              = NHMEX_W_MSR_PMON_CNT0,
+       .perf_ctr               = NHMEX_W_MSR_PMON_EVT_SEL0,
+       .fixed_ctr              = NHMEX_W_MSR_PMON_FIXED_CTR,
+       .fixed_ctl              = NHMEX_W_MSR_PMON_FIXED_CTL,
+       .event_mask             = NHMEX_PMON_RAW_EVENT_MASK,
+       .box_ctl                = NHMEX_W_MSR_GLOBAL_CTL,
+       .pair_ctr_ctl           = 1,
+       .event_descs            = nhmex_uncore_wbox_events,
+       .ops                    = &nhmex_uncore_ops,
+       .format_group           = &nhmex_uncore_cbox_format_group
+};
+
+static int nhmex_bbox_hw_config(struct intel_uncore_box *box, struct perf_event *event)
+{
+       struct hw_perf_event *hwc = &event->hw;
+       struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
+       struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
+       int ctr, ev_sel;
+
+       ctr = (hwc->config & NHMEX_B_PMON_CTR_MASK) >>
+               NHMEX_B_PMON_CTR_SHIFT;
+       ev_sel = (hwc->config & NHMEX_B_PMON_CTL_EV_SEL_MASK) >>
+                 NHMEX_B_PMON_CTL_EV_SEL_SHIFT;
+
+       /* events that do not use the match/mask registers */
+       if ((ctr == 0 && ev_sel > 0x3) || (ctr == 1 && ev_sel > 0x6) ||
+           (ctr == 2 && ev_sel != 0x4) || ctr == 3)
+               return 0;
+
+       if (box->pmu->pmu_idx == 0)
+               reg1->reg = NHMEX_B0_MSR_MATCH;
+       else
+               reg1->reg = NHMEX_B1_MSR_MATCH;
+       reg1->idx = 0;
+       reg1->config = event->attr.config1;
+       reg2->config = event->attr.config2;
+       return 0;
+}
+
+static void nhmex_bbox_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
+{
+       struct hw_perf_event *hwc = &event->hw;
+       struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
+       struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
+
+       if (reg1->idx != EXTRA_REG_NONE) {
+               wrmsrl(reg1->reg, reg1->config);
+               wrmsrl(reg1->reg + 1, reg2->config);
+       }
+       wrmsrl(hwc->config_base, NHMEX_PMON_CTL_EN_BIT0 |
+               (hwc->config & NHMEX_B_PMON_CTL_EV_SEL_MASK));
+}
+
+/*
+ * The Bbox has 4 counters, but each counter monitors different events.
+ * Use bits 6-7 in the event config to select counter.
+ */
+static struct event_constraint nhmex_uncore_bbox_constraints[] = {
+       EVENT_CONSTRAINT(0 , 1, 0xc0),
+       EVENT_CONSTRAINT(0x40, 2, 0xc0),
+       EVENT_CONSTRAINT(0x80, 4, 0xc0),
+       EVENT_CONSTRAINT(0xc0, 8, 0xc0),
+       EVENT_CONSTRAINT_END,
+};
+
+static struct attribute *nhmex_uncore_bbox_formats_attr[] = {
+       &format_attr_event5.attr,
+       &format_attr_counter.attr,
+       &format_attr_match.attr,
+       &format_attr_mask.attr,
+       NULL,
+};
+
+static struct attribute_group nhmex_uncore_bbox_format_group = {
+       .name = "format",
+       .attrs = nhmex_uncore_bbox_formats_attr,
+};
+
+static struct intel_uncore_ops nhmex_uncore_bbox_ops = {
+       NHMEX_UNCORE_OPS_COMMON_INIT(),
+       .enable_event           = nhmex_bbox_msr_enable_event,
+       .hw_config              = nhmex_bbox_hw_config,
+       .get_constraint         = uncore_get_constraint,
+       .put_constraint         = uncore_put_constraint,
+};
+
+static struct intel_uncore_type nhmex_uncore_bbox = {
+       .name                   = "bbox",
+       .num_counters           = 4,
+       .num_boxes              = 2,
+       .perf_ctr_bits          = 48,
+       .event_ctl              = NHMEX_B0_MSR_PMON_CTL0,
+       .perf_ctr               = NHMEX_B0_MSR_PMON_CTR0,
+       .event_mask             = NHMEX_B_PMON_RAW_EVENT_MASK,
+       .box_ctl                = NHMEX_B0_MSR_PMON_GLOBAL_CTL,
+       .msr_offset             = NHMEX_B_MSR_OFFSET,
+       .pair_ctr_ctl           = 1,
+       .num_shared_regs        = 1,
+       .constraints            = nhmex_uncore_bbox_constraints,
+       .ops                    = &nhmex_uncore_bbox_ops,
+       .format_group           = &nhmex_uncore_bbox_format_group
+};
+
+static int nhmex_sbox_hw_config(struct intel_uncore_box *box, struct perf_event *event)
+{
+       struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
+       struct hw_perf_event_extra *reg2 = &event->hw.branch_reg;
+
+       if (event->attr.config & NHMEX_S_PMON_MM_CFG_EN) {
+               reg1->config = event->attr.config1;
+               reg2->config = event->attr.config2;
+       } else {
+               reg1->config = ~0ULL;
+               reg2->config = ~0ULL;
+       }
+
+       if (box->pmu->pmu_idx == 0)
+               reg1->reg = NHMEX_S0_MSR_MM_CFG;
+       else
+               reg1->reg = NHMEX_S1_MSR_MM_CFG;
+
+       reg1->idx = 0;
+
+       return 0;
+}
+
+static void nhmex_sbox_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
+{
+       struct hw_perf_event *hwc = &event->hw;
+       struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
+       struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
+
+       wrmsrl(reg1->reg, 0);
+       if (reg1->config != ~0ULL || reg2->config != ~0ULL) {
+               wrmsrl(reg1->reg + 1, reg1->config);
+               wrmsrl(reg1->reg + 2, reg2->config);
+               wrmsrl(reg1->reg, NHMEX_S_PMON_MM_CFG_EN);
+       }
+       wrmsrl(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT22);
+}
+
+static struct attribute *nhmex_uncore_sbox_formats_attr[] = {
+       &format_attr_event.attr,
+       &format_attr_umask.attr,
+       &format_attr_edge.attr,
+       &format_attr_inv.attr,
+       &format_attr_thresh8.attr,
+       &format_attr_mm_cfg.attr,
+       &format_attr_match.attr,
+       &format_attr_mask.attr,
+       NULL,
+};
+
+static struct attribute_group nhmex_uncore_sbox_format_group = {
+       .name                   = "format",
+       .attrs                  = nhmex_uncore_sbox_formats_attr,
+};
+
+static struct intel_uncore_ops nhmex_uncore_sbox_ops = {
+       NHMEX_UNCORE_OPS_COMMON_INIT(),
+       .enable_event           = nhmex_sbox_msr_enable_event,
+       .hw_config              = nhmex_sbox_hw_config,
+       .get_constraint         = uncore_get_constraint,
+       .put_constraint         = uncore_put_constraint,
+};
+
+static struct intel_uncore_type nhmex_uncore_sbox = {
+       .name                   = "sbox",
+       .num_counters           = 4,
+       .num_boxes              = 2,
+       .perf_ctr_bits          = 48,
+       .event_ctl              = NHMEX_S0_MSR_PMON_CTL0,
+       .perf_ctr               = NHMEX_S0_MSR_PMON_CTR0,
+       .event_mask             = NHMEX_PMON_RAW_EVENT_MASK,
+       .box_ctl                = NHMEX_S0_MSR_PMON_GLOBAL_CTL,
+       .msr_offset             = NHMEX_S_MSR_OFFSET,
+       .pair_ctr_ctl           = 1,
+       .num_shared_regs        = 1,
+       .ops                    = &nhmex_uncore_sbox_ops,
+       .format_group           = &nhmex_uncore_sbox_format_group
+};
+
+enum {
+       EXTRA_REG_NHMEX_M_FILTER,
+       EXTRA_REG_NHMEX_M_DSP,
+       EXTRA_REG_NHMEX_M_ISS,
+       EXTRA_REG_NHMEX_M_MAP,
+       EXTRA_REG_NHMEX_M_MSC_THR,
+       EXTRA_REG_NHMEX_M_PGT,
+       EXTRA_REG_NHMEX_M_PLD,
+       EXTRA_REG_NHMEX_M_ZDP_CTL_FVC,
+};
+
+static struct extra_reg nhmex_uncore_mbox_extra_regs[] = {
+       MBOX_INC_SEL_EXTAR_REG(0x0, DSP),
+       MBOX_INC_SEL_EXTAR_REG(0x4, MSC_THR),
+       MBOX_INC_SEL_EXTAR_REG(0x5, MSC_THR),
+       MBOX_INC_SEL_EXTAR_REG(0x9, ISS),
+       /* event 0xa uses two extra registers */
+       MBOX_INC_SEL_EXTAR_REG(0xa, ISS),
+       MBOX_INC_SEL_EXTAR_REG(0xa, PLD),
+       MBOX_INC_SEL_EXTAR_REG(0xb, PLD),
+       /* events 0xd ~ 0x10 use the same extra register */
+       MBOX_INC_SEL_EXTAR_REG(0xd, ZDP_CTL_FVC),
+       MBOX_INC_SEL_EXTAR_REG(0xe, ZDP_CTL_FVC),
+       MBOX_INC_SEL_EXTAR_REG(0xf, ZDP_CTL_FVC),
+       MBOX_INC_SEL_EXTAR_REG(0x10, ZDP_CTL_FVC),
+       MBOX_INC_SEL_EXTAR_REG(0x16, PGT),
+       MBOX_SET_FLAG_SEL_EXTRA_REG(0x0, DSP),
+       MBOX_SET_FLAG_SEL_EXTRA_REG(0x1, ISS),
+       MBOX_SET_FLAG_SEL_EXTRA_REG(0x5, PGT),
+       MBOX_SET_FLAG_SEL_EXTRA_REG(0x6, MAP),
+       EVENT_EXTRA_END
+};
+
+static bool nhmex_mbox_get_shared_reg(struct intel_uncore_box *box, int idx, u64 config)
+{
+       struct intel_uncore_extra_reg *er;
+       unsigned long flags;
+       bool ret = false;
+       u64 mask;
+
+       if (idx < EXTRA_REG_NHMEX_M_ZDP_CTL_FVC) {
+               er = &box->shared_regs[idx];
+               raw_spin_lock_irqsave(&er->lock, flags);
+               if (!atomic_read(&er->ref) || er->config == config) {
+                       atomic_inc(&er->ref);
+                       er->config = config;
+                       ret = true;
+               }
+               raw_spin_unlock_irqrestore(&er->lock, flags);
+
+               return ret;
+       }
+       /*
+        * The ZDP_CTL_FVC MSR has 4 fields which are used to control
+        * events 0xd ~ 0x10. Besides these 4 fields, there are additional
+        * fields which are shared.
+        */
+       idx -= EXTRA_REG_NHMEX_M_ZDP_CTL_FVC;
+       if (WARN_ON_ONCE(idx >= 4))
+               return false;
+
+       /* mask of the shared fields */
+       mask = NHMEX_M_PMON_ZDP_CTL_FVC_MASK;
+       er = &box->shared_regs[EXTRA_REG_NHMEX_M_ZDP_CTL_FVC];
+
+       raw_spin_lock_irqsave(&er->lock, flags);
+       /* add mask of the non-shared field if it's in use */
+       if (__BITS_VALUE(atomic_read(&er->ref), idx, 8))
+               mask |= NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+
+       if (!atomic_read(&er->ref) || !((er->config ^ config) & mask)) {
+               atomic_add(1 << (idx * 8), &er->ref);
+               mask = NHMEX_M_PMON_ZDP_CTL_FVC_MASK |
+                       NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+               er->config &= ~mask;
+               er->config |= (config & mask);
+               ret = true;
+       }
+       raw_spin_unlock_irqrestore(&er->lock, flags);
+
+       return ret;
+}
+
+static void nhmex_mbox_put_shared_reg(struct intel_uncore_box *box, int idx)
+{
+       struct intel_uncore_extra_reg *er;
+
+       if (idx < EXTRA_REG_NHMEX_M_ZDP_CTL_FVC) {
+               er = &box->shared_regs[idx];
+               atomic_dec(&er->ref);
+               return;
+       }
+
+       idx -= EXTRA_REG_NHMEX_M_ZDP_CTL_FVC;
+       er = &box->shared_regs[EXTRA_REG_NHMEX_M_ZDP_CTL_FVC];
+       atomic_sub(1 << (idx * 8), &er->ref);
+}
+
+u64 nhmex_mbox_alter_er(struct perf_event *event, int new_idx, bool modify)
+{
+       struct hw_perf_event *hwc = &event->hw;
+       struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
+       int idx, orig_idx = __BITS_VALUE(reg1->idx, 0, 8);
+       u64 config = reg1->config;
+
+       /* get the non-shared control bits and shift them */
+       idx = orig_idx - EXTRA_REG_NHMEX_M_ZDP_CTL_FVC;
+       config &= NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+       if (new_idx > orig_idx) {
+               idx = new_idx - orig_idx;
+               config <<= 3 * idx;
+       } else {
+               idx = orig_idx - new_idx;
+               config >>= 3 * idx;
+       }
+
+       /* add the shared control bits back */
+       config |= NHMEX_M_PMON_ZDP_CTL_FVC_MASK & reg1->config;
+       if (modify) {
+               /* adjust the main event selector */
+               if (new_idx > orig_idx)
+                       hwc->config += idx << NHMEX_M_PMON_CTL_INC_SEL_SHIFT;
+               else
+                       hwc->config -= idx << NHMEX_M_PMON_CTL_INC_SEL_SHIFT;
+               reg1->config = config;
+               reg1->idx = ~0xff | new_idx;
+       }
+       return config;
+}
+
+static struct event_constraint *
+nhmex_mbox_get_constraint(struct intel_uncore_box *box, struct perf_event *event)
+{
+       struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
+       struct hw_perf_event_extra *reg2 = &event->hw.branch_reg;
+       int i, idx[2], alloc = 0;
+       u64 config1 = reg1->config;
+
+       idx[0] = __BITS_VALUE(reg1->idx, 0, 8);
+       idx[1] = __BITS_VALUE(reg1->idx, 1, 8);
+again:
+       for (i = 0; i < 2; i++) {
+               if (!uncore_box_is_fake(box) && (reg1->alloc & (0x1 << i)))
+                       idx[i] = 0xff;
+
+               if (idx[i] == 0xff)
+                       continue;
+
+               if (!nhmex_mbox_get_shared_reg(box, idx[i],
+                               __BITS_VALUE(config1, i, 32)))
+                       goto fail;
+               alloc |= (0x1 << i);
+       }
+
+       /* for the match/mask registers */
+       if ((uncore_box_is_fake(box) || !reg2->alloc) &&
+           !nhmex_mbox_get_shared_reg(box, reg2->idx, reg2->config))
+               goto fail;
+
+       /*
+        * If it's a fake box -- as per validate_{group,event}() we
+        * shouldn't touch event state and we can avoid doing so
+        * since both will only call get_event_constraints() once
+        * on each event, this avoids the need for reg->alloc.
+        */
+       if (!uncore_box_is_fake(box)) {
+               if (idx[0] != 0xff && idx[0] != __BITS_VALUE(reg1->idx, 0, 8))
+                       nhmex_mbox_alter_er(event, idx[0], true);
+               reg1->alloc |= alloc;
+               reg2->alloc = 1;
+       }
+       return NULL;
+fail:
+       if (idx[0] != 0xff && !(alloc & 0x1) &&
+           idx[0] >= EXTRA_REG_NHMEX_M_ZDP_CTL_FVC) {
+               /*
+                * events 0xd ~ 0x10 are functional identical, but are
+                * controlled by different fields in the ZDP_CTL_FVC
+                * register. If we failed to take one field, try the
+                * rest 3 choices.
                 */
-               for (i = 0; i < 8; i++) {
-                       if (nodeid == ((config >> (3 * i)) & 0x7)) {
-                               pcibus_to_physid[bus] = i;
-                               break;
-                       }
+               BUG_ON(__BITS_VALUE(reg1->idx, 1, 8) != 0xff);
+               idx[0] -= EXTRA_REG_NHMEX_M_ZDP_CTL_FVC;
+               idx[0] = (idx[0] + 1) % 4;
+               idx[0] += EXTRA_REG_NHMEX_M_ZDP_CTL_FVC;
+               if (idx[0] != __BITS_VALUE(reg1->idx, 0, 8)) {
+                       config1 = nhmex_mbox_alter_er(event, idx[0], false);
+                       goto again;
                }
-       };
-       return;
-}
-/* end of Sandy Bridge-EP uncore support */
+       }
 
+       if (alloc & 0x1)
+               nhmex_mbox_put_shared_reg(box, idx[0]);
+       if (alloc & 0x2)
+               nhmex_mbox_put_shared_reg(box, idx[1]);
+       return &constraint_empty;
+}
 
-/* Sandy Bridge uncore support */
-static void snb_uncore_msr_enable_event(struct intel_uncore_box *box,
-                                       struct perf_event *event)
+static void nhmex_mbox_put_constraint(struct intel_uncore_box *box, struct perf_event *event)
 {
-       struct hw_perf_event *hwc = &event->hw;
+       struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
+       struct hw_perf_event_extra *reg2 = &event->hw.branch_reg;
 
-       if (hwc->idx < UNCORE_PMC_IDX_FIXED)
-               wrmsrl(hwc->config_base, hwc->config | SNB_UNC_CTL_EN);
-       else
-               wrmsrl(hwc->config_base, SNB_UNC_CTL_EN);
+       if (uncore_box_is_fake(box))
+               return;
+
+       if (reg1->alloc & 0x1)
+               nhmex_mbox_put_shared_reg(box, __BITS_VALUE(reg1->idx, 0, 8));
+       if (reg1->alloc & 0x2)
+               nhmex_mbox_put_shared_reg(box, __BITS_VALUE(reg1->idx, 1, 8));
+       reg1->alloc = 0;
+
+       if (reg2->alloc) {
+               nhmex_mbox_put_shared_reg(box, reg2->idx);
+               reg2->alloc = 0;
+       }
 }
 
-static void snb_uncore_msr_disable_event(struct intel_uncore_box *box,
-                                       struct perf_event *event)
+static int nhmex_mbox_extra_reg_idx(struct extra_reg *er)
 {
-       wrmsrl(event->hw.config_base, 0);
+       if (er->idx < EXTRA_REG_NHMEX_M_ZDP_CTL_FVC)
+               return er->idx;
+       return er->idx + (er->event >> NHMEX_M_PMON_CTL_INC_SEL_SHIFT) - 0xd;
 }
 
-static u64 snb_uncore_msr_read_counter(struct intel_uncore_box *box,
-                                       struct perf_event *event)
+static int nhmex_mbox_hw_config(struct intel_uncore_box *box, struct perf_event *event)
 {
-       u64 count;
-       rdmsrl(event->hw.event_base, count);
-       return count;
+       struct intel_uncore_type *type = box->pmu->type;
+       struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
+       struct hw_perf_event_extra *reg2 = &event->hw.branch_reg;
+       struct extra_reg *er;
+       unsigned msr;
+       int reg_idx = 0;
+
+       if (WARN_ON_ONCE(reg1->idx != -1))
+               return -EINVAL;
+       /*
+        * The mbox events may require 2 extra MSRs at the most. But only
+        * the lower 32 bits in these MSRs are significant, so we can use
+        * config1 to pass two MSRs' config.
+        */
+       for (er = nhmex_uncore_mbox_extra_regs; er->msr; er++) {
+               if (er->event != (event->hw.config & er->config_mask))
+                       continue;
+               if (event->attr.config1 & ~er->valid_mask)
+                       return -EINVAL;
+               if (er->idx == __BITS_VALUE(reg1->idx, 0, 8) ||
+                   er->idx == __BITS_VALUE(reg1->idx, 1, 8))
+                       continue;
+               if (WARN_ON_ONCE(reg_idx >= 2))
+                       return -EINVAL;
+
+               msr = er->msr + type->msr_offset * box->pmu->pmu_idx;
+               if (WARN_ON_ONCE(msr >= 0xffff || er->idx >= 0xff))
+                       return -EINVAL;
+
+               /* always use the 32~63 bits to pass the PLD config */
+               if (er->idx == EXTRA_REG_NHMEX_M_PLD)
+                       reg_idx = 1;
+
+               reg1->idx &= ~(0xff << (reg_idx * 8));
+               reg1->reg &= ~(0xffff << (reg_idx * 16));
+               reg1->idx |= nhmex_mbox_extra_reg_idx(er) << (reg_idx * 8);
+               reg1->reg |= msr << (reg_idx * 16);
+               reg1->config = event->attr.config1;
+               reg_idx++;
+       }
+       /* use config2 to pass the filter config */
+       reg2->idx = EXTRA_REG_NHMEX_M_FILTER;
+       if (event->attr.config2 & NHMEX_M_PMON_MM_CFG_EN)
+               reg2->config = event->attr.config2;
+       else
+               reg2->config = ~0ULL;
+       if (box->pmu->pmu_idx == 0)
+               reg2->reg = NHMEX_M0_MSR_PMU_MM_CFG;
+       else
+               reg2->reg = NHMEX_M1_MSR_PMU_MM_CFG;
+
+       return 0;
 }
 
-static void snb_uncore_msr_init_box(struct intel_uncore_box *box)
+static u64 nhmex_mbox_shared_reg_config(struct intel_uncore_box *box, int idx)
 {
-       if (box->pmu->pmu_idx == 0) {
-               wrmsrl(SNB_UNC_PERF_GLOBAL_CTL,
-                       SNB_UNC_GLOBAL_CTL_EN | SNB_UNC_GLOBAL_CTL_CORE_ALL);
-       }
+       struct intel_uncore_extra_reg *er;
+       unsigned long flags;
+       u64 config;
+
+       if (idx < EXTRA_REG_NHMEX_M_ZDP_CTL_FVC)
+               return box->shared_regs[idx].config;
+
+       er = &box->shared_regs[EXTRA_REG_NHMEX_M_ZDP_CTL_FVC];
+       raw_spin_lock_irqsave(&er->lock, flags);
+       config = er->config;
+       raw_spin_unlock_irqrestore(&er->lock, flags);
+       return config;
 }
 
-static struct attribute *snb_uncore_formats_attr[] = {
-       &format_attr_event.attr,
-       &format_attr_umask.attr,
-       &format_attr_edge.attr,
-       &format_attr_inv.attr,
-       &format_attr_cmask5.attr,
+static void nhmex_mbox_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
+{
+       struct hw_perf_event *hwc = &event->hw;
+       struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
+       struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
+       int idx;
+
+       idx = __BITS_VALUE(reg1->idx, 0, 8);
+       if (idx != 0xff)
+               wrmsrl(__BITS_VALUE(reg1->reg, 0, 16),
+                       nhmex_mbox_shared_reg_config(box, idx));
+       idx = __BITS_VALUE(reg1->idx, 1, 8);
+       if (idx != 0xff)
+               wrmsrl(__BITS_VALUE(reg1->reg, 1, 16),
+                       nhmex_mbox_shared_reg_config(box, idx));
+
+       wrmsrl(reg2->reg, 0);
+       if (reg2->config != ~0ULL) {
+               wrmsrl(reg2->reg + 1,
+                       reg2->config & NHMEX_M_PMON_ADDR_MATCH_MASK);
+               wrmsrl(reg2->reg + 2, NHMEX_M_PMON_ADDR_MASK_MASK &
+                       (reg2->config >> NHMEX_M_PMON_ADDR_MASK_SHIFT));
+               wrmsrl(reg2->reg, NHMEX_M_PMON_MM_CFG_EN);
+       }
+
+       wrmsrl(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT0);
+}
+
+DEFINE_UNCORE_FORMAT_ATTR(count_mode,  count_mode,     "config:2-3");
+DEFINE_UNCORE_FORMAT_ATTR(storage_mode, storage_mode,  "config:4-5");
+DEFINE_UNCORE_FORMAT_ATTR(wrap_mode,   wrap_mode,      "config:6");
+DEFINE_UNCORE_FORMAT_ATTR(flag_mode,   flag_mode,      "config:7");
+DEFINE_UNCORE_FORMAT_ATTR(inc_sel,     inc_sel,        "config:9-13");
+DEFINE_UNCORE_FORMAT_ATTR(set_flag_sel,        set_flag_sel,   "config:19-21");
+DEFINE_UNCORE_FORMAT_ATTR(filter_cfg,  filter_cfg,     "config2:63");
+DEFINE_UNCORE_FORMAT_ATTR(filter_match,        filter_match,   "config2:0-33");
+DEFINE_UNCORE_FORMAT_ATTR(filter_mask, filter_mask,    "config2:34-61");
+DEFINE_UNCORE_FORMAT_ATTR(dsp,         dsp,            "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(thr,         thr,            "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(fvc,         fvc,            "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(pgt,         pgt,            "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(map,         map,            "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(iss,         iss,            "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(pld,         pld,            "config1:32-63");
+
+static struct attribute *nhmex_uncore_mbox_formats_attr[] = {
+       &format_attr_count_mode.attr,
+       &format_attr_storage_mode.attr,
+       &format_attr_wrap_mode.attr,
+       &format_attr_flag_mode.attr,
+       &format_attr_inc_sel.attr,
+       &format_attr_set_flag_sel.attr,
+       &format_attr_filter_cfg.attr,
+       &format_attr_filter_match.attr,
+       &format_attr_filter_mask.attr,
+       &format_attr_dsp.attr,
+       &format_attr_thr.attr,
+       &format_attr_fvc.attr,
+       &format_attr_pgt.attr,
+       &format_attr_map.attr,
+       &format_attr_iss.attr,
+       &format_attr_pld.attr,
        NULL,
 };
 
-static struct attribute_group snb_uncore_format_group = {
-       .name = "format",
-       .attrs = snb_uncore_formats_attr,
+static struct attribute_group nhmex_uncore_mbox_format_group = {
+       .name           = "format",
+       .attrs          = nhmex_uncore_mbox_formats_attr,
 };
 
-static struct intel_uncore_ops snb_uncore_msr_ops = {
-       .init_box       = snb_uncore_msr_init_box,
-       .disable_event  = snb_uncore_msr_disable_event,
-       .enable_event   = snb_uncore_msr_enable_event,
-       .read_counter   = snb_uncore_msr_read_counter,
+static struct uncore_event_desc nhmex_uncore_mbox_events[] = {
+       INTEL_UNCORE_EVENT_DESC(bbox_cmds_read, "inc_sel=0xd,fvc=0x2800"),
+       INTEL_UNCORE_EVENT_DESC(bbox_cmds_write, "inc_sel=0xd,fvc=0x2820"),
+       { /* end: all zeroes */ },
 };
 
-static struct event_constraint snb_uncore_cbox_constraints[] = {
-       UNCORE_EVENT_CONSTRAINT(0x80, 0x1),
-       UNCORE_EVENT_CONSTRAINT(0x83, 0x1),
-       EVENT_CONSTRAINT_END
+static struct intel_uncore_ops nhmex_uncore_mbox_ops = {
+       NHMEX_UNCORE_OPS_COMMON_INIT(),
+       .enable_event   = nhmex_mbox_msr_enable_event,
+       .hw_config      = nhmex_mbox_hw_config,
+       .get_constraint = nhmex_mbox_get_constraint,
+       .put_constraint = nhmex_mbox_put_constraint,
 };
 
-static struct intel_uncore_type snb_uncore_cbox = {
-       .name           = "cbox",
-       .num_counters   = 2,
-       .num_boxes      = 4,
-       .perf_ctr_bits  = 44,
-       .fixed_ctr_bits = 48,
-       .perf_ctr       = SNB_UNC_CBO_0_PER_CTR0,
-       .event_ctl      = SNB_UNC_CBO_0_PERFEVTSEL0,
-       .fixed_ctr      = SNB_UNC_FIXED_CTR,
-       .fixed_ctl      = SNB_UNC_FIXED_CTR_CTRL,
-       .single_fixed   = 1,
-       .event_mask     = SNB_UNC_RAW_EVENT_MASK,
-       .msr_offset     = SNB_UNC_CBO_MSR_OFFSET,
-       .constraints    = snb_uncore_cbox_constraints,
-       .ops            = &snb_uncore_msr_ops,
-       .format_group   = &snb_uncore_format_group,
+static struct intel_uncore_type nhmex_uncore_mbox = {
+       .name                   = "mbox",
+       .num_counters           = 6,
+       .num_boxes              = 2,
+       .perf_ctr_bits          = 48,
+       .event_ctl              = NHMEX_M0_MSR_PMU_CTL0,
+       .perf_ctr               = NHMEX_M0_MSR_PMU_CNT0,
+       .event_mask             = NHMEX_M_PMON_RAW_EVENT_MASK,
+       .box_ctl                = NHMEX_M0_MSR_GLOBAL_CTL,
+       .msr_offset             = NHMEX_M_MSR_OFFSET,
+       .pair_ctr_ctl           = 1,
+       .num_shared_regs        = 8,
+       .event_descs            = nhmex_uncore_mbox_events,
+       .ops                    = &nhmex_uncore_mbox_ops,
+       .format_group           = &nhmex_uncore_mbox_format_group,
 };
 
-static struct intel_uncore_type *snb_msr_uncores[] = {
-       &snb_uncore_cbox,
-       NULL,
-};
-/* end of Sandy Bridge uncore support */
+void nhmex_rbox_alter_er(struct intel_uncore_box *box, struct perf_event *event)
+{
+       struct hw_perf_event *hwc = &event->hw;
+       struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
+       int port;
 
-/* Nehalem uncore support */
-static void nhm_uncore_msr_disable_box(struct intel_uncore_box *box)
+       /* adjust the main event selector */
+       if (reg1->idx % 2) {
+               reg1->idx--;
+               hwc->config -= 1 << NHMEX_R_PMON_CTL_EV_SEL_SHIFT;
+       } else {
+               reg1->idx++;
+               hwc->config += 1 << NHMEX_R_PMON_CTL_EV_SEL_SHIFT;
+       }
+
+       /* adjust address or config of extra register */
+       port = reg1->idx / 6 + box->pmu->pmu_idx * 4;
+       switch (reg1->idx % 6) {
+       case 0:
+               reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG0(port);
+               break;
+       case 1:
+               reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG1(port);
+               break;
+       case 2:
+               /* the 8~15 bits to the 0~7 bits */
+               reg1->config >>= 8;
+               break;
+       case 3:
+               /* the 0~7 bits to the 8~15 bits */
+               reg1->config <<= 8;
+               break;
+       case 4:
+               reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(port);
+               break;
+       case 5:
+               reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(port);
+               break;
+       };
+}
+
+/*
+ * Each rbox has 4 event set which monitor PQI port 0~3 or 4~7.
+ * An event set consists of 6 events, the 3rd and 4th events in
+ * an event set use the same extra register. So an event set uses
+ * 5 extra registers.
+ */
+static struct event_constraint *
+nhmex_rbox_get_constraint(struct intel_uncore_box *box, struct perf_event *event)
 {
-       wrmsrl(NHM_UNC_PERF_GLOBAL_CTL, 0);
+       struct hw_perf_event *hwc = &event->hw;
+       struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
+       struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
+       struct intel_uncore_extra_reg *er;
+       unsigned long flags;
+       int idx, er_idx;
+       u64 config1;
+       bool ok = false;
+
+       if (!uncore_box_is_fake(box) && reg1->alloc)
+               return NULL;
+
+       idx = reg1->idx % 6;
+       config1 = reg1->config;
+again:
+       er_idx = idx;
+       /* the 3rd and 4th events use the same extra register */
+       if (er_idx > 2)
+               er_idx--;
+       er_idx += (reg1->idx / 6) * 5;
+
+       er = &box->shared_regs[er_idx];
+       raw_spin_lock_irqsave(&er->lock, flags);
+       if (idx < 2) {
+               if (!atomic_read(&er->ref) || er->config == reg1->config) {
+                       atomic_inc(&er->ref);
+                       er->config = reg1->config;
+                       ok = true;
+               }
+       } else if (idx == 2 || idx == 3) {
+               /*
+                * these two events use different fields in a extra register,
+                * the 0~7 bits and the 8~15 bits respectively.
+                */
+               u64 mask = 0xff << ((idx - 2) * 8);
+               if (!__BITS_VALUE(atomic_read(&er->ref), idx - 2, 8) ||
+                               !((er->config ^ config1) & mask)) {
+                       atomic_add(1 << ((idx - 2) * 8), &er->ref);
+                       er->config &= ~mask;
+                       er->config |= config1 & mask;
+                       ok = true;
+               }
+       } else {
+               if (!atomic_read(&er->ref) ||
+                               (er->config == (hwc->config >> 32) &&
+                                er->config1 == reg1->config &&
+                                er->config2 == reg2->config)) {
+                       atomic_inc(&er->ref);
+                       er->config = (hwc->config >> 32);
+                       er->config1 = reg1->config;
+                       er->config2 = reg2->config;
+                       ok = true;
+               }
+       }
+       raw_spin_unlock_irqrestore(&er->lock, flags);
+
+       if (!ok) {
+               /*
+                * The Rbox events are always in pairs. The paired
+                * events are functional identical, but use different
+                * extra registers. If we failed to take an extra
+                * register, try the alternative.
+                */
+               if (idx % 2)
+                       idx--;
+               else
+                       idx++;
+               if (idx != reg1->idx % 6) {
+                       if (idx == 2)
+                               config1 >>= 8;
+                       else if (idx == 3)
+                               config1 <<= 8;
+                       goto again;
+               }
+       } else {
+               if (!uncore_box_is_fake(box)) {
+                       if (idx != reg1->idx % 6)
+                               nhmex_rbox_alter_er(box, event);
+                       reg1->alloc = 1;
+               }
+               return NULL;
+       }
+       return &constraint_empty;
 }
 
-static void nhm_uncore_msr_enable_box(struct intel_uncore_box *box)
+static void nhmex_rbox_put_constraint(struct intel_uncore_box *box, struct perf_event *event)
 {
-       wrmsrl(NHM_UNC_PERF_GLOBAL_CTL,
-               NHM_UNC_GLOBAL_CTL_EN_PC_ALL | NHM_UNC_GLOBAL_CTL_EN_FC);
+       struct intel_uncore_extra_reg *er;
+       struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
+       int idx, er_idx;
+
+       if (uncore_box_is_fake(box) || !reg1->alloc)
+               return;
+
+       idx = reg1->idx % 6;
+       er_idx = idx;
+       if (er_idx > 2)
+               er_idx--;
+       er_idx += (reg1->idx / 6) * 5;
+
+       er = &box->shared_regs[er_idx];
+       if (idx == 2 || idx == 3)
+               atomic_sub(1 << ((idx - 2) * 8), &er->ref);
+       else
+               atomic_dec(&er->ref);
+
+       reg1->alloc = 0;
 }
 
-static void nhm_uncore_msr_enable_event(struct intel_uncore_box *box,
-                                       struct perf_event *event)
+static int nhmex_rbox_hw_config(struct intel_uncore_box *box, struct perf_event *event)
 {
        struct hw_perf_event *hwc = &event->hw;
+       struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
+       struct hw_perf_event_extra *reg2 = &event->hw.branch_reg;
+       int port, idx;
 
-       if (hwc->idx < UNCORE_PMC_IDX_FIXED)
-               wrmsrl(hwc->config_base, hwc->config | SNB_UNC_CTL_EN);
-       else
-               wrmsrl(hwc->config_base, NHM_UNC_FIXED_CTR_CTL_EN);
+       idx = (event->hw.config & NHMEX_R_PMON_CTL_EV_SEL_MASK) >>
+               NHMEX_R_PMON_CTL_EV_SEL_SHIFT;
+       if (idx >= 0x18)
+               return -EINVAL;
+
+       reg1->idx = idx;
+       reg1->config = event->attr.config1;
+
+       port = idx / 6 + box->pmu->pmu_idx * 4;
+       idx %= 6;
+       switch (idx) {
+       case 0:
+               reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG0(port);
+               break;
+       case 1:
+               reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG1(port);
+               break;
+       case 2:
+       case 3:
+               reg1->reg = NHMEX_R_MSR_PORTN_QLX_CFG(port);
+               break;
+       case 4:
+       case 5:
+               if (idx == 4)
+                       reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(port);
+               else
+                       reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(port);
+               reg2->config = event->attr.config2;
+               hwc->config |= event->attr.config & (~0ULL << 32);
+               break;
+       };
+       return 0;
 }
 
-static struct attribute *nhm_uncore_formats_attr[] = {
-       &format_attr_event.attr,
-       &format_attr_umask.attr,
-       &format_attr_edge.attr,
-       &format_attr_inv.attr,
-       &format_attr_cmask8.attr,
+static u64 nhmex_rbox_shared_reg_config(struct intel_uncore_box *box, int idx)
+{
+       struct intel_uncore_extra_reg *er;
+       unsigned long flags;
+       u64 config;
+
+       er = &box->shared_regs[idx];
+
+       raw_spin_lock_irqsave(&er->lock, flags);
+       config = er->config;
+       raw_spin_unlock_irqrestore(&er->lock, flags);
+
+       return config;
+}
+
+static void nhmex_rbox_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
+{
+       struct hw_perf_event *hwc = &event->hw;
+       struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
+       struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
+       int idx, er_idx;
+
+       idx = reg1->idx % 6;
+       er_idx = idx;
+       if (er_idx > 2)
+               er_idx--;
+       er_idx += (reg1->idx / 6) * 5;
+
+       switch (idx) {
+       case 0:
+       case 1:
+               wrmsrl(reg1->reg, reg1->config);
+               break;
+       case 2:
+       case 3:
+               wrmsrl(reg1->reg, nhmex_rbox_shared_reg_config(box, er_idx));
+               break;
+       case 4:
+       case 5:
+               wrmsrl(reg1->reg, reg1->config);
+               wrmsrl(reg1->reg + 1, hwc->config >> 32);
+               wrmsrl(reg1->reg + 2, reg2->config);
+               break;
+       };
+
+       wrmsrl(hwc->config_base, NHMEX_PMON_CTL_EN_BIT0 |
+               (hwc->config & NHMEX_R_PMON_CTL_EV_SEL_MASK));
+}
+
+DEFINE_UNCORE_FORMAT_ATTR(xbr_match, xbr_match, "config:32-63");
+DEFINE_UNCORE_FORMAT_ATTR(xbr_mm_cfg, xbr_mm_cfg, "config1:0-63");
+DEFINE_UNCORE_FORMAT_ATTR(xbr_mask, xbr_mask, "config2:0-63");
+DEFINE_UNCORE_FORMAT_ATTR(qlx_cfg, qlx_cfg, "config1:0-15");
+DEFINE_UNCORE_FORMAT_ATTR(iperf_cfg, iperf_cfg, "config1:0-31");
+
+static struct attribute *nhmex_uncore_rbox_formats_attr[] = {
+       &format_attr_event5.attr,
+       &format_attr_xbr_mm_cfg.attr,
+       &format_attr_xbr_match.attr,
+       &format_attr_xbr_mask.attr,
+       &format_attr_qlx_cfg.attr,
+       &format_attr_iperf_cfg.attr,
        NULL,
 };
 
-static struct attribute_group nhm_uncore_format_group = {
+static struct attribute_group nhmex_uncore_rbox_format_group = {
        .name = "format",
-       .attrs = nhm_uncore_formats_attr,
+       .attrs = nhmex_uncore_rbox_formats_attr,
 };
 
-static struct uncore_event_desc nhm_uncore_events[] = {
-       INTEL_UNCORE_EVENT_DESC(clockticks,                "event=0xff,umask=0x00"),
-       INTEL_UNCORE_EVENT_DESC(qmc_writes_full_any,       "event=0x2f,umask=0x0f"),
-       INTEL_UNCORE_EVENT_DESC(qmc_normal_reads_any,      "event=0x2c,umask=0x0f"),
-       INTEL_UNCORE_EVENT_DESC(qhl_request_ioh_reads,     "event=0x20,umask=0x01"),
-       INTEL_UNCORE_EVENT_DESC(qhl_request_ioh_writes,    "event=0x20,umask=0x02"),
-       INTEL_UNCORE_EVENT_DESC(qhl_request_remote_reads,  "event=0x20,umask=0x04"),
-       INTEL_UNCORE_EVENT_DESC(qhl_request_remote_writes, "event=0x20,umask=0x08"),
-       INTEL_UNCORE_EVENT_DESC(qhl_request_local_reads,   "event=0x20,umask=0x10"),
-       INTEL_UNCORE_EVENT_DESC(qhl_request_local_writes,  "event=0x20,umask=0x20"),
+static struct uncore_event_desc nhmex_uncore_rbox_events[] = {
+       INTEL_UNCORE_EVENT_DESC(qpi0_flit_send,         "event=0x0,iperf_cfg=0x80000000"),
+       INTEL_UNCORE_EVENT_DESC(qpi1_filt_send,         "event=0x6,iperf_cfg=0x80000000"),
+       INTEL_UNCORE_EVENT_DESC(qpi0_idle_filt,         "event=0x0,iperf_cfg=0x40000000"),
+       INTEL_UNCORE_EVENT_DESC(qpi1_idle_filt,         "event=0x6,iperf_cfg=0x40000000"),
+       INTEL_UNCORE_EVENT_DESC(qpi0_date_response,     "event=0x0,iperf_cfg=0xc4"),
+       INTEL_UNCORE_EVENT_DESC(qpi1_date_response,     "event=0x6,iperf_cfg=0xc4"),
        { /* end: all zeroes */ },
 };
 
-static struct intel_uncore_ops nhm_uncore_msr_ops = {
-       .disable_box    = nhm_uncore_msr_disable_box,
-       .enable_box     = nhm_uncore_msr_enable_box,
-       .disable_event  = snb_uncore_msr_disable_event,
-       .enable_event   = nhm_uncore_msr_enable_event,
-       .read_counter   = snb_uncore_msr_read_counter,
+static struct intel_uncore_ops nhmex_uncore_rbox_ops = {
+       NHMEX_UNCORE_OPS_COMMON_INIT(),
+       .enable_event           = nhmex_rbox_msr_enable_event,
+       .hw_config              = nhmex_rbox_hw_config,
+       .get_constraint         = nhmex_rbox_get_constraint,
+       .put_constraint         = nhmex_rbox_put_constraint,
 };
 
-static struct intel_uncore_type nhm_uncore = {
-       .name           = "",
-       .num_counters   = 8,
-       .num_boxes      = 1,
-       .perf_ctr_bits  = 48,
-       .fixed_ctr_bits = 48,
-       .event_ctl      = NHM_UNC_PERFEVTSEL0,
-       .perf_ctr       = NHM_UNC_UNCORE_PMC0,
-       .fixed_ctr      = NHM_UNC_FIXED_CTR,
-       .fixed_ctl      = NHM_UNC_FIXED_CTR_CTRL,
-       .event_mask     = NHM_UNC_RAW_EVENT_MASK,
-       .event_descs    = nhm_uncore_events,
-       .ops            = &nhm_uncore_msr_ops,
-       .format_group   = &nhm_uncore_format_group,
+static struct intel_uncore_type nhmex_uncore_rbox = {
+       .name                   = "rbox",
+       .num_counters           = 8,
+       .num_boxes              = 2,
+       .perf_ctr_bits          = 48,
+       .event_ctl              = NHMEX_R_MSR_PMON_CTL0,
+       .perf_ctr               = NHMEX_R_MSR_PMON_CNT0,
+       .event_mask             = NHMEX_R_PMON_RAW_EVENT_MASK,
+       .box_ctl                = NHMEX_R_MSR_GLOBAL_CTL,
+       .msr_offset             = NHMEX_R_MSR_OFFSET,
+       .pair_ctr_ctl           = 1,
+       .num_shared_regs        = 20,
+       .event_descs            = nhmex_uncore_rbox_events,
+       .ops                    = &nhmex_uncore_rbox_ops,
+       .format_group           = &nhmex_uncore_rbox_format_group
 };
 
-static struct intel_uncore_type *nhm_msr_uncores[] = {
-       &nhm_uncore,
+static struct intel_uncore_type *nhmex_msr_uncores[] = {
+       &nhmex_uncore_ubox,
+       &nhmex_uncore_cbox,
+       &nhmex_uncore_bbox,
+       &nhmex_uncore_sbox,
+       &nhmex_uncore_mbox,
+       &nhmex_uncore_rbox,
+       &nhmex_uncore_wbox,
        NULL,
 };
-/* end of Nehalem uncore support */
+/* end of Nehalem-EX uncore support */
 
-static void uncore_assign_hw_event(struct intel_uncore_box *box,
-                               struct perf_event *event, int idx)
+static void uncore_assign_hw_event(struct intel_uncore_box *box, struct perf_event *event, int idx)
 {
        struct hw_perf_event *hwc = &event->hw;
 
@@ -787,8 +1841,7 @@ static void uncore_assign_hw_event(struct intel_uncore_box *box,
        hwc->event_base  = uncore_perf_ctr(box, hwc->idx);
 }
 
-static void uncore_perf_event_update(struct intel_uncore_box *box,
-                                       struct perf_event *event)
+static void uncore_perf_event_update(struct intel_uncore_box *box, struct perf_event *event)
 {
        u64 prev_count, new_count, delta;
        int shift;
@@ -858,14 +1911,12 @@ static void uncore_pmu_init_hrtimer(struct intel_uncore_box *box)
        box->hrtimer.function = uncore_pmu_hrtimer;
 }
 
-struct intel_uncore_box *uncore_alloc_box(struct intel_uncore_type *type,
-                                         int cpu)
+struct intel_uncore_box *uncore_alloc_box(struct intel_uncore_type *type, int cpu)
 {
        struct intel_uncore_box *box;
        int i, size;
 
-       size = sizeof(*box) + type->num_shared_regs *
-               sizeof(struct intel_uncore_extra_reg);
+       size = sizeof(*box) + type->num_shared_regs * sizeof(struct intel_uncore_extra_reg);
 
        box = kmalloc_node(size, GFP_KERNEL | __GFP_ZERO, cpu_to_node(cpu));
        if (!box)
@@ -915,12 +1966,11 @@ static struct intel_uncore_box *uncore_event_to_box(struct perf_event *event)
         * perf core schedules event on the basis of cpu, uncore events are
         * collected by one of the cpus inside a physical package.
         */
-       return uncore_pmu_to_box(uncore_event_to_pmu(event),
-                                smp_processor_id());
+       return uncore_pmu_to_box(uncore_event_to_pmu(event), smp_processor_id());
 }
 
-static int uncore_collect_events(struct intel_uncore_box *box,
-                               struct perf_event *leader, bool dogrp)
+static int
+uncore_collect_events(struct intel_uncore_box *box, struct perf_event *leader, bool dogrp)
 {
        struct perf_event *event;
        int n, max_count;
@@ -952,8 +2002,7 @@ static int uncore_collect_events(struct intel_uncore_box *box,
 }
 
 static struct event_constraint *
-uncore_get_event_constraint(struct intel_uncore_box *box,
-                           struct perf_event *event)
+uncore_get_event_constraint(struct intel_uncore_box *box, struct perf_event *event)
 {
        struct intel_uncore_type *type = box->pmu->type;
        struct event_constraint *c;
@@ -977,15 +2026,13 @@ uncore_get_event_constraint(struct intel_uncore_box *box,
        return &type->unconstrainted;
 }
 
-static void uncore_put_event_constraint(struct intel_uncore_box *box,
-                                       struct perf_event *event)
+static void uncore_put_event_constraint(struct intel_uncore_box *box, struct perf_event *event)
 {
        if (box->pmu->type->ops->put_constraint)
                box->pmu->type->ops->put_constraint(box, event);
 }
 
-static int uncore_assign_events(struct intel_uncore_box *box,
-                               int assign[], int n)
+static int uncore_assign_events(struct intel_uncore_box *box, int assign[], int n)
 {
        unsigned long used_mask[BITS_TO_LONGS(UNCORE_PMC_IDX_MAX)];
        struct event_constraint *c, *constraints[UNCORE_PMC_IDX_MAX];
@@ -1407,8 +2454,7 @@ static bool pcidrv_registered;
 /*
  * add a pci uncore device
  */
-static int __devinit uncore_pci_add(struct intel_uncore_type *type,
-                                   struct pci_dev *pdev)
+static int __devinit uncore_pci_add(struct intel_uncore_type *type, struct pci_dev *pdev)
 {
        struct intel_uncore_pmu *pmu;
        struct intel_uncore_box *box;
@@ -1485,6 +2531,7 @@ static int __devinit uncore_pci_probe(struct pci_dev *pdev,
        struct intel_uncore_type *type;
 
        type = (struct intel_uncore_type *)id->driver_data;
+
        return uncore_pci_add(type, pdev);
 }
 
@@ -1612,8 +2659,8 @@ static int __cpuinit uncore_cpu_prepare(int cpu, int phys_id)
        return 0;
 }
 
-static void __cpuinit uncore_change_context(struct intel_uncore_type **uncores,
-                                           int old_cpu, int new_cpu)
+static void __cpuinit
+uncore_change_context(struct intel_uncore_type **uncores, int old_cpu, int new_cpu)
 {
        struct intel_uncore_type *type;
        struct intel_uncore_pmu *pmu;
@@ -1694,8 +2741,8 @@ static void __cpuinit uncore_event_init_cpu(int cpu)
        uncore_change_context(pci_uncores, -1, cpu);
 }
 
-static int __cpuinit uncore_cpu_notifier(struct notifier_block *self,
                                       unsigned long action, void *hcpu)
+static int
__cpuinit uncore_cpu_notifier(struct notifier_block *self, unsigned long action, void *hcpu)
 {
        unsigned int cpu = (long)hcpu;
 
@@ -1732,12 +2779,12 @@ static int __cpuinit uncore_cpu_notifier(struct notifier_block *self,
 }
 
 static struct notifier_block uncore_cpu_nb __cpuinitdata = {
-       .notifier_call = uncore_cpu_notifier,
+       .notifier_call  = uncore_cpu_notifier,
        /*
         * to migrate uncore events, our notifier should be executed
         * before perf core's notifier.
         */
-       .priority = CPU_PRI_PERF + 1,
+       .priority       = CPU_PRI_PERF + 1,
 };
 
 static void __init uncore_cpu_setup(void *dummy)
@@ -1767,6 +2814,9 @@ static int __init uncore_cpu_init(void)
                        snbep_uncore_cbox.num_boxes = max_cores;
                msr_uncores = snbep_msr_uncores;
                break;
+       case 46:
+               msr_uncores = nhmex_msr_uncores;
+               break;
        default:
                return 0;
        }
index b13e9ea81def5684aadfa70a34e1e21f98a2af56..f3851892e0770c540c9df3a7da4925da9ecf2198 100644 (file)
@@ -5,8 +5,6 @@
 #include "perf_event.h"
 
 #define UNCORE_PMU_NAME_LEN            32
-#define UNCORE_BOX_HASH_SIZE           8
-
 #define UNCORE_PMU_HRTIMER_INTERVAL    (60 * NSEC_PER_SEC)
 
 #define UNCORE_FIXED_EVENT             0xff
                                 SNBEP_PCU_MSR_PMON_CTL_OCC_INVERT | \
                                 SNBEP_PCU_MSR_PMON_CTL_OCC_EDGE_DET)
 
+#define SNBEP_QPI_PCI_PMON_RAW_EVENT_MASK      \
+                               (SNBEP_PMON_RAW_EVENT_MASK | \
+                                SNBEP_PMON_CTL_EV_SEL_EXT)
+
 /* SNB-EP pci control register */
 #define SNBEP_PCI_PMON_BOX_CTL                 0xf4
 #define SNBEP_PCI_PMON_CTL0                    0xd8
 #define SNBEP_PCU_MSR_CORE_C3_CTR              0x3fc
 #define SNBEP_PCU_MSR_CORE_C6_CTR              0x3fd
 
+/* NHM-EX event control */
+#define NHMEX_PMON_CTL_EV_SEL_MASK     0x000000ff
+#define NHMEX_PMON_CTL_UMASK_MASK      0x0000ff00
+#define NHMEX_PMON_CTL_EN_BIT0         (1 << 0)
+#define NHMEX_PMON_CTL_EDGE_DET                (1 << 18)
+#define NHMEX_PMON_CTL_PMI_EN          (1 << 20)
+#define NHMEX_PMON_CTL_EN_BIT22                (1 << 22)
+#define NHMEX_PMON_CTL_INVERT          (1 << 23)
+#define NHMEX_PMON_CTL_TRESH_MASK      0xff000000
+#define NHMEX_PMON_RAW_EVENT_MASK      (NHMEX_PMON_CTL_EV_SEL_MASK | \
+                                        NHMEX_PMON_CTL_UMASK_MASK | \
+                                        NHMEX_PMON_CTL_EDGE_DET | \
+                                        NHMEX_PMON_CTL_INVERT | \
+                                        NHMEX_PMON_CTL_TRESH_MASK)
+
+/* NHM-EX Ubox */
+#define NHMEX_U_MSR_PMON_GLOBAL_CTL            0xc00
+#define NHMEX_U_MSR_PMON_CTR                   0xc11
+#define NHMEX_U_MSR_PMON_EV_SEL                        0xc10
+
+#define NHMEX_U_PMON_GLOBAL_EN                 (1 << 0)
+#define NHMEX_U_PMON_GLOBAL_PMI_CORE_SEL       0x0000001e
+#define NHMEX_U_PMON_GLOBAL_EN_ALL             (1 << 28)
+#define NHMEX_U_PMON_GLOBAL_RST_ALL            (1 << 29)
+#define NHMEX_U_PMON_GLOBAL_FRZ_ALL            (1 << 31)
+
+#define NHMEX_U_PMON_RAW_EVENT_MASK            \
+               (NHMEX_PMON_CTL_EV_SEL_MASK |   \
+                NHMEX_PMON_CTL_EDGE_DET)
+
+/* NHM-EX Cbox */
+#define NHMEX_C0_MSR_PMON_GLOBAL_CTL           0xd00
+#define NHMEX_C0_MSR_PMON_CTR0                 0xd11
+#define NHMEX_C0_MSR_PMON_EV_SEL0              0xd10
+#define NHMEX_C_MSR_OFFSET                     0x20
+
+/* NHM-EX Bbox */
+#define NHMEX_B0_MSR_PMON_GLOBAL_CTL           0xc20
+#define NHMEX_B0_MSR_PMON_CTR0                 0xc31
+#define NHMEX_B0_MSR_PMON_CTL0                 0xc30
+#define NHMEX_B_MSR_OFFSET                     0x40
+#define NHMEX_B0_MSR_MATCH                     0xe45
+#define NHMEX_B0_MSR_MASK                      0xe46
+#define NHMEX_B1_MSR_MATCH                     0xe4d
+#define NHMEX_B1_MSR_MASK                      0xe4e
+
+#define NHMEX_B_PMON_CTL_EN                    (1 << 0)
+#define NHMEX_B_PMON_CTL_EV_SEL_SHIFT          1
+#define NHMEX_B_PMON_CTL_EV_SEL_MASK           \
+               (0x1f << NHMEX_B_PMON_CTL_EV_SEL_SHIFT)
+#define NHMEX_B_PMON_CTR_SHIFT         6
+#define NHMEX_B_PMON_CTR_MASK          \
+               (0x3 << NHMEX_B_PMON_CTR_SHIFT)
+#define NHMEX_B_PMON_RAW_EVENT_MASK            \
+               (NHMEX_B_PMON_CTL_EV_SEL_MASK | \
+                NHMEX_B_PMON_CTR_MASK)
+
+/* NHM-EX Sbox */
+#define NHMEX_S0_MSR_PMON_GLOBAL_CTL           0xc40
+#define NHMEX_S0_MSR_PMON_CTR0                 0xc51
+#define NHMEX_S0_MSR_PMON_CTL0                 0xc50
+#define NHMEX_S_MSR_OFFSET                     0x80
+#define NHMEX_S0_MSR_MM_CFG                    0xe48
+#define NHMEX_S0_MSR_MATCH                     0xe49
+#define NHMEX_S0_MSR_MASK                      0xe4a
+#define NHMEX_S1_MSR_MM_CFG                    0xe58
+#define NHMEX_S1_MSR_MATCH                     0xe59
+#define NHMEX_S1_MSR_MASK                      0xe5a
+
+#define NHMEX_S_PMON_MM_CFG_EN                 (0x1ULL << 63)
+
+/* NHM-EX Mbox */
+#define NHMEX_M0_MSR_GLOBAL_CTL                        0xca0
+#define NHMEX_M0_MSR_PMU_DSP                   0xca5
+#define NHMEX_M0_MSR_PMU_ISS                   0xca6
+#define NHMEX_M0_MSR_PMU_MAP                   0xca7
+#define NHMEX_M0_MSR_PMU_MSC_THR               0xca8
+#define NHMEX_M0_MSR_PMU_PGT                   0xca9
+#define NHMEX_M0_MSR_PMU_PLD                   0xcaa
+#define NHMEX_M0_MSR_PMU_ZDP_CTL_FVC           0xcab
+#define NHMEX_M0_MSR_PMU_CTL0                  0xcb0
+#define NHMEX_M0_MSR_PMU_CNT0                  0xcb1
+#define NHMEX_M_MSR_OFFSET                     0x40
+#define NHMEX_M0_MSR_PMU_MM_CFG                        0xe54
+#define NHMEX_M1_MSR_PMU_MM_CFG                        0xe5c
+
+#define NHMEX_M_PMON_MM_CFG_EN                 (1ULL << 63)
+#define NHMEX_M_PMON_ADDR_MATCH_MASK           0x3ffffffffULL
+#define NHMEX_M_PMON_ADDR_MASK_MASK            0x7ffffffULL
+#define NHMEX_M_PMON_ADDR_MASK_SHIFT           34
+
+#define NHMEX_M_PMON_CTL_EN                    (1 << 0)
+#define NHMEX_M_PMON_CTL_PMI_EN                        (1 << 1)
+#define NHMEX_M_PMON_CTL_COUNT_MODE_SHIFT      2
+#define NHMEX_M_PMON_CTL_COUNT_MODE_MASK       \
+       (0x3 << NHMEX_M_PMON_CTL_COUNT_MODE_SHIFT)
+#define NHMEX_M_PMON_CTL_STORAGE_MODE_SHIFT    4
+#define NHMEX_M_PMON_CTL_STORAGE_MODE_MASK     \
+       (0x3 << NHMEX_M_PMON_CTL_STORAGE_MODE_SHIFT)
+#define NHMEX_M_PMON_CTL_WRAP_MODE             (1 << 6)
+#define NHMEX_M_PMON_CTL_FLAG_MODE             (1 << 7)
+#define NHMEX_M_PMON_CTL_INC_SEL_SHIFT         9
+#define NHMEX_M_PMON_CTL_INC_SEL_MASK          \
+       (0x1f << NHMEX_M_PMON_CTL_INC_SEL_SHIFT)
+#define NHMEX_M_PMON_CTL_SET_FLAG_SEL_SHIFT    19
+#define NHMEX_M_PMON_CTL_SET_FLAG_SEL_MASK     \
+       (0x7 << NHMEX_M_PMON_CTL_SET_FLAG_SEL_SHIFT)
+#define NHMEX_M_PMON_RAW_EVENT_MASK                    \
+               (NHMEX_M_PMON_CTL_COUNT_MODE_MASK |     \
+                NHMEX_M_PMON_CTL_STORAGE_MODE_MASK |   \
+                NHMEX_M_PMON_CTL_WRAP_MODE |           \
+                NHMEX_M_PMON_CTL_FLAG_MODE |           \
+                NHMEX_M_PMON_CTL_INC_SEL_MASK |        \
+                NHMEX_M_PMON_CTL_SET_FLAG_SEL_MASK)
+
+
+#define NHMEX_M_PMON_ZDP_CTL_FVC_FVID_MASK     0x1f
+#define NHMEX_M_PMON_ZDP_CTL_FVC_BCMD_MASK     (0x7 << 5)
+#define NHMEX_M_PMON_ZDP_CTL_FVC_RSP_MASK      (0x7 << 8)
+#define NHMEX_M_PMON_ZDP_CTL_FVC_PBOX_INIT_ERR (1 << 23)
+#define NHMEX_M_PMON_ZDP_CTL_FVC_MASK                  \
+               (NHMEX_M_PMON_ZDP_CTL_FVC_FVID_MASK |   \
+                NHMEX_M_PMON_ZDP_CTL_FVC_BCMD_MASK |   \
+                NHMEX_M_PMON_ZDP_CTL_FVC_RSP_MASK  |   \
+                NHMEX_M_PMON_ZDP_CTL_FVC_PBOX_INIT_ERR)
+#define NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(n) (0x7 << (11 + 3 * (n)))
+
+/*
+ * use the 9~13 bits to select event If the 7th bit is not set,
+ * otherwise use the 19~21 bits to select event.
+ */
+#define MBOX_INC_SEL(x) ((x) << NHMEX_M_PMON_CTL_INC_SEL_SHIFT)
+#define MBOX_SET_FLAG_SEL(x) (((x) << NHMEX_M_PMON_CTL_SET_FLAG_SEL_SHIFT) | \
+                               NHMEX_M_PMON_CTL_FLAG_MODE)
+#define MBOX_INC_SEL_MASK (NHMEX_M_PMON_CTL_INC_SEL_MASK | \
+                          NHMEX_M_PMON_CTL_FLAG_MODE)
+#define MBOX_SET_FLAG_SEL_MASK (NHMEX_M_PMON_CTL_SET_FLAG_SEL_MASK | \
+                               NHMEX_M_PMON_CTL_FLAG_MODE)
+#define MBOX_INC_SEL_EXTAR_REG(c, r) \
+               EVENT_EXTRA_REG(MBOX_INC_SEL(c), NHMEX_M0_MSR_PMU_##r, \
+                               MBOX_INC_SEL_MASK, (u64)-1, NHMEX_M_##r)
+#define MBOX_SET_FLAG_SEL_EXTRA_REG(c, r) \
+               EVENT_EXTRA_REG(MBOX_SET_FLAG_SEL(c), NHMEX_M0_MSR_PMU_##r, \
+                               MBOX_SET_FLAG_SEL_MASK, \
+                               (u64)-1, NHMEX_M_##r)
+
+/* NHM-EX Rbox */
+#define NHMEX_R_MSR_GLOBAL_CTL                 0xe00
+#define NHMEX_R_MSR_PMON_CTL0                  0xe10
+#define NHMEX_R_MSR_PMON_CNT0                  0xe11
+#define NHMEX_R_MSR_OFFSET                     0x20
+
+#define NHMEX_R_MSR_PORTN_QLX_CFG(n)           \
+               ((n) < 4 ? (0xe0c + (n)) : (0xe2c + (n) - 4))
+#define NHMEX_R_MSR_PORTN_IPERF_CFG0(n)                (0xe04 + (n))
+#define NHMEX_R_MSR_PORTN_IPERF_CFG1(n)                (0xe24 + (n))
+#define NHMEX_R_MSR_PORTN_XBR_OFFSET(n)                \
+               (((n) < 4 ? 0 : 0x10) + (n) * 4)
+#define NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(n)   \
+               (0xe60 + NHMEX_R_MSR_PORTN_XBR_OFFSET(n))
+#define NHMEX_R_MSR_PORTN_XBR_SET1_MATCH(n)    \
+               (NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(n) + 1)
+#define NHMEX_R_MSR_PORTN_XBR_SET1_MASK(n)     \
+               (NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(n) + 2)
+#define NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(n)   \
+               (0xe70 + NHMEX_R_MSR_PORTN_XBR_OFFSET(n))
+#define NHMEX_R_MSR_PORTN_XBR_SET2_MATCH(n)    \
+               (NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(n) + 1)
+#define NHMEX_R_MSR_PORTN_XBR_SET2_MASK(n)     \
+               (NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(n) + 2)
+
+#define NHMEX_R_PMON_CTL_EN                    (1 << 0)
+#define NHMEX_R_PMON_CTL_EV_SEL_SHIFT          1
+#define NHMEX_R_PMON_CTL_EV_SEL_MASK           \
+               (0x1f << NHMEX_R_PMON_CTL_EV_SEL_SHIFT)
+#define NHMEX_R_PMON_CTL_PMI_EN                        (1 << 6)
+#define NHMEX_R_PMON_RAW_EVENT_MASK            NHMEX_R_PMON_CTL_EV_SEL_MASK
+
+/* NHM-EX Wbox */
+#define NHMEX_W_MSR_GLOBAL_CTL                 0xc80
+#define NHMEX_W_MSR_PMON_CNT0                  0xc90
+#define NHMEX_W_MSR_PMON_EVT_SEL0              0xc91
+#define NHMEX_W_MSR_PMON_FIXED_CTR             0x394
+#define NHMEX_W_MSR_PMON_FIXED_CTL             0x395
+
+#define NHMEX_W_PMON_GLOBAL_FIXED_EN           (1ULL << 31)
+
 struct intel_uncore_ops;
 struct intel_uncore_pmu;
 struct intel_uncore_box;
@@ -178,6 +367,7 @@ struct intel_uncore_type {
        unsigned msr_offset;
        unsigned num_shared_regs:8;
        unsigned single_fixed:1;
+       unsigned pair_ctr_ctl:1;
        struct event_constraint unconstrainted;
        struct event_constraint *constraints;
        struct intel_uncore_pmu *pmus;
@@ -213,7 +403,7 @@ struct intel_uncore_pmu {
 
 struct intel_uncore_extra_reg {
        raw_spinlock_t lock;
-       u64 config1;
+       u64 config, config1, config2;
        atomic_t ref;
 };
 
@@ -323,14 +513,16 @@ unsigned uncore_msr_fixed_ctr(struct intel_uncore_box *box)
 static inline
 unsigned uncore_msr_event_ctl(struct intel_uncore_box *box, int idx)
 {
-       return idx + box->pmu->type->event_ctl +
+       return box->pmu->type->event_ctl +
+               (box->pmu->type->pair_ctr_ctl ? 2 * idx : idx) +
                box->pmu->type->msr_offset * box->pmu->pmu_idx;
 }
 
 static inline
 unsigned uncore_msr_perf_ctr(struct intel_uncore_box *box, int idx)
 {
-       return idx + box->pmu->type->perf_ctr +
+       return box->pmu->type->perf_ctr +
+               (box->pmu->type->pair_ctr_ctl ? 2 * idx : idx) +
                box->pmu->type->msr_offset * box->pmu->pmu_idx;
 }
 
@@ -422,3 +614,8 @@ static inline void uncore_box_init(struct intel_uncore_box *box)
                        box->pmu->type->ops->init_box(box);
        }
 }
+
+static inline bool uncore_box_is_fake(struct intel_uncore_box *box)
+{
+       return (box->phys_id < 0);
+}
index 41857970517f795739018c01e1fc070f0a831fff..ed858e9e9a7461aa9b4f8aa42a52c79d57d84507 100644 (file)
@@ -944,7 +944,7 @@ void __init e820_reserve_resources(void)
        for (i = 0; i < e820_saved.nr_map; i++) {
                struct e820entry *entry = &e820_saved.map[i];
                firmware_map_add_early(entry->addr,
-                       entry->addr + entry->size - 1,
+                       entry->addr + entry->size,
                        e820_type_to_string(entry->type));
        }
 }
index 950dfb7b84173aed904594624f9c33bda6380ebf..e72cd0df5ba381436b8158ebad5ee0f758ba8eab 100644 (file)
 #define profile_pc(regs) PT_REGS_IP(regs)
 
 #define UPT_RESTART_SYSCALL(r) (UPT_IP(r) -= 2)
-#define UPT_SET_SYSCALL_RETURN(r, res) (UPT_AX(r) = (res))
+#define PT_REGS_SET_SYSCALL_RETURN(r, res) (PT_REGS_AX(r) = (res))
 
-static inline long regs_return_value(struct uml_pt_regs *regs)
+static inline long regs_return_value(struct pt_regs *regs)
 {
-       return UPT_AX(regs);
+       return PT_REGS_AX(regs);
 }
 #endif /* __UM_X86_PTRACE_H */
index 8a3f8351f4380d5ff8da08978ceb83fd4d19d689..8ed64cfae4ff1f46953315f04853ce06831394f5 100644 (file)
@@ -7,6 +7,7 @@ config ZONE_DMA
 config XTENSA
        def_bool y
        select HAVE_IDE
+       select GENERIC_ATOMIC64
        select HAVE_GENERIC_HARDIRQS
        select GENERIC_IRQ_SHOW
        select GENERIC_CPU_DEVICES
diff --git a/arch/xtensa/include/asm/cpumask.h b/arch/xtensa/include/asm/cpumask.h
deleted file mode 100644 (file)
index ebeede3..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * include/asm-xtensa/cpumask.h
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2001 - 2005 Tensilica Inc.
- */
-
-#ifndef _XTENSA_CPUMASK_H
-#define _XTENSA_CPUMASK_H
-
-#include <asm-generic/cpumask.h>
-
-#endif /* _XTENSA_CPUMASK_H */
diff --git a/arch/xtensa/include/asm/rmap.h b/arch/xtensa/include/asm/rmap.h
deleted file mode 100644 (file)
index 649588b..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * include/asm-xtensa/rmap.h
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2001 - 2005 Tensilica Inc.
- */
-
-#ifndef _XTENSA_RMAP_H
-#define _XTENSA_RMAP_H
-
-#include <asm-generic/rmap.h>
-
-#endif
index 816e6d0d686c6f934bae7de3786ee4fc463ac567..05b3f093d5d7cd09435c0395cf5a20e0a07e26ec 100644 (file)
@@ -44,7 +44,7 @@ asmlinkage long xtensa_shmat(int shmid, char __user *shmaddr, int shmflg)
        unsigned long ret;
        long err;
 
-       err = do_shmat(shmid, shmaddr, shmflg, &ret);
+       err = do_shmat(shmid, shmaddr, shmflg, &ret, SHMLBA);
        if (err)
                return err;
        return (long)ret;
index b17885a0b508fe82e84b012b89539caf3862dfed..5a74c53bc69c132cfad6231d9294e49f5cedef93 100644 (file)
@@ -44,6 +44,7 @@ void do_page_fault(struct pt_regs *regs)
 
        int is_write, is_exec;
        int fault;
+       unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
 
        info.si_code = SEGV_MAPERR;
 
@@ -71,6 +72,7 @@ void do_page_fault(struct pt_regs *regs)
               address, exccause, regs->pc, is_write? "w":"", is_exec? "x":"");
 #endif
 
+retry:
        down_read(&mm->mmap_sem);
        vma = find_vma(mm, address);
 
@@ -93,6 +95,7 @@ good_area:
        if (is_write) {
                if (!(vma->vm_flags & VM_WRITE))
                        goto bad_area;
+               flags |= FAULT_FLAG_WRITE;
        } else if (is_exec) {
                if (!(vma->vm_flags & VM_EXEC))
                        goto bad_area;
@@ -104,7 +107,11 @@ good_area:
         * make sure we exit gracefully rather than endlessly redo
         * the fault.
         */
-       fault = handle_mm_fault(mm, vma, address, is_write ? FAULT_FLAG_WRITE : 0);
+       fault = handle_mm_fault(mm, vma, address, flags);
+
+       if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
+               return;
+
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
@@ -112,10 +119,22 @@ good_area:
                        goto do_sigbus;
                BUG();
        }
-       if (fault & VM_FAULT_MAJOR)
-               current->maj_flt++;
-       else
-               current->min_flt++;
+       if (flags & FAULT_FLAG_ALLOW_RETRY) {
+               if (fault & VM_FAULT_MAJOR)
+                       current->maj_flt++;
+               else
+                       current->min_flt++;
+               if (fault & VM_FAULT_RETRY) {
+                       flags &= ~FAULT_FLAG_ALLOW_RETRY;
+
+                        /* No need to up_read(&mm->mmap_sem) as we would
+                        * have already released it in __lock_page_or_retry
+                        * in mm/filemap.c.
+                        */
+
+                       goto retry;
+               }
+       }
 
        up_read(&mm->mmap_sem);
        return;
index e7dee617358e810aec57ff25162ff95604fabb3c..f3b44a65fc7ad5f127bee8bcbadf5b486a7e5c71 100644 (file)
@@ -31,27 +31,6 @@ EXPORT_SYMBOL_GPL(blkcg_root);
 
 static struct blkcg_policy *blkcg_policy[BLKCG_MAX_POLS];
 
-struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup)
-{
-       return container_of(cgroup_subsys_state(cgroup, blkio_subsys_id),
-                           struct blkcg, css);
-}
-EXPORT_SYMBOL_GPL(cgroup_to_blkcg);
-
-static struct blkcg *task_blkcg(struct task_struct *tsk)
-{
-       return container_of(task_subsys_state(tsk, blkio_subsys_id),
-                           struct blkcg, css);
-}
-
-struct blkcg *bio_blkcg(struct bio *bio)
-{
-       if (bio && bio->bi_css)
-               return container_of(bio->bi_css, struct blkcg, css);
-       return task_blkcg(current);
-}
-EXPORT_SYMBOL_GPL(bio_blkcg);
-
 static bool blkcg_policy_enabled(struct request_queue *q,
                                 const struct blkcg_policy *pol)
 {
@@ -84,6 +63,7 @@ static void blkg_free(struct blkcg_gq *blkg)
                kfree(pd);
        }
 
+       blk_exit_rl(&blkg->rl);
        kfree(blkg);
 }
 
@@ -91,16 +71,18 @@ static void blkg_free(struct blkcg_gq *blkg)
  * blkg_alloc - allocate a blkg
  * @blkcg: block cgroup the new blkg is associated with
  * @q: request_queue the new blkg is associated with
+ * @gfp_mask: allocation mask to use
  *
  * Allocate a new blkg assocating @blkcg and @q.
  */
-static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q)
+static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q,
+                                  gfp_t gfp_mask)
 {
        struct blkcg_gq *blkg;
        int i;
 
        /* alloc and init base part */
-       blkg = kzalloc_node(sizeof(*blkg), GFP_ATOMIC, q->node);
+       blkg = kzalloc_node(sizeof(*blkg), gfp_mask, q->node);
        if (!blkg)
                return NULL;
 
@@ -109,6 +91,13 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q)
        blkg->blkcg = blkcg;
        blkg->refcnt = 1;
 
+       /* root blkg uses @q->root_rl, init rl only for !root blkgs */
+       if (blkcg != &blkcg_root) {
+               if (blk_init_rl(&blkg->rl, q, gfp_mask))
+                       goto err_free;
+               blkg->rl.blkg = blkg;
+       }
+
        for (i = 0; i < BLKCG_MAX_POLS; i++) {
                struct blkcg_policy *pol = blkcg_policy[i];
                struct blkg_policy_data *pd;
@@ -117,11 +106,9 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q)
                        continue;
 
                /* alloc per-policy data and attach it to blkg */
-               pd = kzalloc_node(pol->pd_size, GFP_ATOMIC, q->node);
-               if (!pd) {
-                       blkg_free(blkg);
-                       return NULL;
-               }
+               pd = kzalloc_node(pol->pd_size, gfp_mask, q->node);
+               if (!pd)
+                       goto err_free;
 
                blkg->pd[i] = pd;
                pd->blkg = blkg;
@@ -132,6 +119,10 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q)
        }
 
        return blkg;
+
+err_free:
+       blkg_free(blkg);
+       return NULL;
 }
 
 static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg,
@@ -175,9 +166,13 @@ struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, struct request_queue *q)
 }
 EXPORT_SYMBOL_GPL(blkg_lookup);
 
+/*
+ * If @new_blkg is %NULL, this function tries to allocate a new one as
+ * necessary using %GFP_ATOMIC.  @new_blkg is always consumed on return.
+ */
 static struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg,
-                                            struct request_queue *q)
-       __releases(q->queue_lock) __acquires(q->queue_lock)
+                                            struct request_queue *q,
+                                            struct blkcg_gq *new_blkg)
 {
        struct blkcg_gq *blkg;
        int ret;
@@ -189,24 +184,26 @@ static struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg,
        blkg = __blkg_lookup(blkcg, q);
        if (blkg) {
                rcu_assign_pointer(blkcg->blkg_hint, blkg);
-               return blkg;
+               goto out_free;
        }
 
        /* blkg holds a reference to blkcg */
-       if (!css_tryget(&blkcg->css))
-               return ERR_PTR(-EINVAL);
+       if (!css_tryget(&blkcg->css)) {
+               blkg = ERR_PTR(-EINVAL);
+               goto out_free;
+       }
 
        /* allocate */
-       ret = -ENOMEM;
-       blkg = blkg_alloc(blkcg, q);
-       if (unlikely(!blkg))
-               goto err_put;
+       if (!new_blkg) {
+               new_blkg = blkg_alloc(blkcg, q, GFP_ATOMIC);
+               if (unlikely(!new_blkg)) {
+                       blkg = ERR_PTR(-ENOMEM);
+                       goto out_put;
+               }
+       }
+       blkg = new_blkg;
 
        /* insert */
-       ret = radix_tree_preload(GFP_ATOMIC);
-       if (ret)
-               goto err_free;
-
        spin_lock(&blkcg->lock);
        ret = radix_tree_insert(&blkcg->blkg_tree, q->id, blkg);
        if (likely(!ret)) {
@@ -215,15 +212,15 @@ static struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg,
        }
        spin_unlock(&blkcg->lock);
 
-       radix_tree_preload_end();
-
        if (!ret)
                return blkg;
-err_free:
-       blkg_free(blkg);
-err_put:
+
+       blkg = ERR_PTR(ret);
+out_put:
        css_put(&blkcg->css);
-       return ERR_PTR(ret);
+out_free:
+       blkg_free(new_blkg);
+       return blkg;
 }
 
 struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,
@@ -235,7 +232,7 @@ struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,
         */
        if (unlikely(blk_queue_bypass(q)))
                return ERR_PTR(blk_queue_dead(q) ? -EINVAL : -EBUSY);
-       return __blkg_lookup_create(blkcg, q);
+       return __blkg_lookup_create(blkcg, q, NULL);
 }
 EXPORT_SYMBOL_GPL(blkg_lookup_create);
 
@@ -313,6 +310,38 @@ void __blkg_release(struct blkcg_gq *blkg)
 }
 EXPORT_SYMBOL_GPL(__blkg_release);
 
+/*
+ * The next function used by blk_queue_for_each_rl().  It's a bit tricky
+ * because the root blkg uses @q->root_rl instead of its own rl.
+ */
+struct request_list *__blk_queue_next_rl(struct request_list *rl,
+                                        struct request_queue *q)
+{
+       struct list_head *ent;
+       struct blkcg_gq *blkg;
+
+       /*
+        * Determine the current blkg list_head.  The first entry is
+        * root_rl which is off @q->blkg_list and mapped to the head.
+        */
+       if (rl == &q->root_rl) {
+               ent = &q->blkg_list;
+       } else {
+               blkg = container_of(rl, struct blkcg_gq, rl);
+               ent = &blkg->q_node;
+       }
+
+       /* walk to the next list_head, skip root blkcg */
+       ent = ent->next;
+       if (ent == &q->root_blkg->q_node)
+               ent = ent->next;
+       if (ent == &q->blkg_list)
+               return NULL;
+
+       blkg = container_of(ent, struct blkcg_gq, q_node);
+       return &blkg->rl;
+}
+
 static int blkcg_reset_stats(struct cgroup *cgroup, struct cftype *cftype,
                             u64 val)
 {
@@ -734,24 +763,36 @@ int blkcg_activate_policy(struct request_queue *q,
        struct blkcg_gq *blkg;
        struct blkg_policy_data *pd, *n;
        int cnt = 0, ret;
+       bool preloaded;
 
        if (blkcg_policy_enabled(q, pol))
                return 0;
 
+       /* preallocations for root blkg */
+       blkg = blkg_alloc(&blkcg_root, q, GFP_KERNEL);
+       if (!blkg)
+               return -ENOMEM;
+
+       preloaded = !radix_tree_preload(GFP_KERNEL);
+
        blk_queue_bypass_start(q);
 
        /* make sure the root blkg exists and count the existing blkgs */
        spin_lock_irq(q->queue_lock);
 
        rcu_read_lock();
-       blkg = __blkg_lookup_create(&blkcg_root, q);
+       blkg = __blkg_lookup_create(&blkcg_root, q, blkg);
        rcu_read_unlock();
 
+       if (preloaded)
+               radix_tree_preload_end();
+
        if (IS_ERR(blkg)) {
                ret = PTR_ERR(blkg);
                goto out_unlock;
        }
        q->root_blkg = blkg;
+       q->root_rl.blkg = blkg;
 
        list_for_each_entry(blkg, &q->blkg_list, q_node)
                cnt++;
index 8ac457ce7783847522c1340c008d3c7789f3a1b2..24597309e23d38700a6ca2ae80cd9aab71aa3474 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/u64_stats_sync.h>
 #include <linux/seq_file.h>
 #include <linux/radix-tree.h>
+#include <linux/blkdev.h>
 
 /* Max limits for throttle policy */
 #define THROTL_IOPS_MAX                UINT_MAX
@@ -93,6 +94,8 @@ struct blkcg_gq {
        struct list_head                q_node;
        struct hlist_node               blkcg_node;
        struct blkcg                    *blkcg;
+       /* request allocation list for this blkcg-q pair */
+       struct request_list             rl;
        /* reference count */
        int                             refcnt;
 
@@ -120,8 +123,6 @@ struct blkcg_policy {
 
 extern struct blkcg blkcg_root;
 
-struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup);
-struct blkcg *bio_blkcg(struct bio *bio);
 struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, struct request_queue *q);
 struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,
                                    struct request_queue *q);
@@ -160,6 +161,25 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
 void blkg_conf_finish(struct blkg_conf_ctx *ctx);
 
 
+static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup)
+{
+       return container_of(cgroup_subsys_state(cgroup, blkio_subsys_id),
+                           struct blkcg, css);
+}
+
+static inline struct blkcg *task_blkcg(struct task_struct *tsk)
+{
+       return container_of(task_subsys_state(tsk, blkio_subsys_id),
+                           struct blkcg, css);
+}
+
+static inline struct blkcg *bio_blkcg(struct bio *bio)
+{
+       if (bio && bio->bi_css)
+               return container_of(bio->bi_css, struct blkcg, css);
+       return task_blkcg(current);
+}
+
 /**
  * blkg_to_pdata - get policy private data
  * @blkg: blkg of interest
@@ -233,6 +253,95 @@ static inline void blkg_put(struct blkcg_gq *blkg)
                __blkg_release(blkg);
 }
 
+/**
+ * blk_get_rl - get request_list to use
+ * @q: request_queue of interest
+ * @bio: bio which will be attached to the allocated request (may be %NULL)
+ *
+ * The caller wants to allocate a request from @q to use for @bio.  Find
+ * the request_list to use and obtain a reference on it.  Should be called
+ * under queue_lock.  This function is guaranteed to return non-%NULL
+ * request_list.
+ */
+static inline struct request_list *blk_get_rl(struct request_queue *q,
+                                             struct bio *bio)
+{
+       struct blkcg *blkcg;
+       struct blkcg_gq *blkg;
+
+       rcu_read_lock();
+
+       blkcg = bio_blkcg(bio);
+
+       /* bypass blkg lookup and use @q->root_rl directly for root */
+       if (blkcg == &blkcg_root)
+               goto root_rl;
+
+       /*
+        * Try to use blkg->rl.  blkg lookup may fail under memory pressure
+        * or if either the blkcg or queue is going away.  Fall back to
+        * root_rl in such cases.
+        */
+       blkg = blkg_lookup_create(blkcg, q);
+       if (unlikely(IS_ERR(blkg)))
+               goto root_rl;
+
+       blkg_get(blkg);
+       rcu_read_unlock();
+       return &blkg->rl;
+root_rl:
+       rcu_read_unlock();
+       return &q->root_rl;
+}
+
+/**
+ * blk_put_rl - put request_list
+ * @rl: request_list to put
+ *
+ * Put the reference acquired by blk_get_rl().  Should be called under
+ * queue_lock.
+ */
+static inline void blk_put_rl(struct request_list *rl)
+{
+       /* root_rl may not have blkg set */
+       if (rl->blkg && rl->blkg->blkcg != &blkcg_root)
+               blkg_put(rl->blkg);
+}
+
+/**
+ * blk_rq_set_rl - associate a request with a request_list
+ * @rq: request of interest
+ * @rl: target request_list
+ *
+ * Associate @rq with @rl so that accounting and freeing can know the
+ * request_list @rq came from.
+ */
+static inline void blk_rq_set_rl(struct request *rq, struct request_list *rl)
+{
+       rq->rl = rl;
+}
+
+/**
+ * blk_rq_rl - return the request_list a request came from
+ * @rq: request of interest
+ *
+ * Return the request_list @rq is allocated from.
+ */
+static inline struct request_list *blk_rq_rl(struct request *rq)
+{
+       return rq->rl;
+}
+
+struct request_list *__blk_queue_next_rl(struct request_list *rl,
+                                        struct request_queue *q);
+/**
+ * blk_queue_for_each_rl - iterate through all request_lists of a request_queue
+ *
+ * Should be used under queue_lock.
+ */
+#define blk_queue_for_each_rl(rl, q)   \
+       for ((rl) = &(q)->root_rl; (rl); (rl) = __blk_queue_next_rl((rl), (q)))
+
 /**
  * blkg_stat_add - add a value to a blkg_stat
  * @stat: target blkg_stat
@@ -351,6 +460,7 @@ static inline void blkg_rwstat_reset(struct blkg_rwstat *rwstat)
 #else  /* CONFIG_BLK_CGROUP */
 
 struct cgroup;
+struct blkcg;
 
 struct blkg_policy_data {
 };
@@ -361,8 +471,6 @@ struct blkcg_gq {
 struct blkcg_policy {
 };
 
-static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) { return NULL; }
-static inline struct blkcg *bio_blkcg(struct bio *bio) { return NULL; }
 static inline struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, void *key) { return NULL; }
 static inline int blkcg_init_queue(struct request_queue *q) { return 0; }
 static inline void blkcg_drain_queue(struct request_queue *q) { }
@@ -374,6 +482,9 @@ static inline int blkcg_activate_policy(struct request_queue *q,
 static inline void blkcg_deactivate_policy(struct request_queue *q,
                                           const struct blkcg_policy *pol) { }
 
+static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) { return NULL; }
+static inline struct blkcg *bio_blkcg(struct bio *bio) { return NULL; }
+
 static inline struct blkg_policy_data *blkg_to_pd(struct blkcg_gq *blkg,
                                                  struct blkcg_policy *pol) { return NULL; }
 static inline struct blkcg_gq *pd_to_blkg(struct blkg_policy_data *pd) { return NULL; }
@@ -381,5 +492,14 @@ static inline char *blkg_path(struct blkcg_gq *blkg) { return NULL; }
 static inline void blkg_get(struct blkcg_gq *blkg) { }
 static inline void blkg_put(struct blkcg_gq *blkg) { }
 
+static inline struct request_list *blk_get_rl(struct request_queue *q,
+                                             struct bio *bio) { return &q->root_rl; }
+static inline void blk_put_rl(struct request_list *rl) { }
+static inline void blk_rq_set_rl(struct request *rq, struct request_list *rl) { }
+static inline struct request_list *blk_rq_rl(struct request *rq) { return &rq->q->root_rl; }
+
+#define blk_queue_for_each_rl(rl, q)   \
+       for ((rl) = &(q)->root_rl; (rl); (rl) = NULL)
+
 #endif /* CONFIG_BLK_CGROUP */
 #endif /* _BLK_CGROUP_H */
index 93eb3e4f88ce78affc79c5ca6d8b7c7b0759be5a..4b4dbdfbca89fe5769fd4b2f6826f305fca18e26 100644 (file)
@@ -387,7 +387,7 @@ void blk_drain_queue(struct request_queue *q, bool drain_all)
                if (!list_empty(&q->queue_head) && q->request_fn)
                        __blk_run_queue(q);
 
-               drain |= q->rq.elvpriv;
+               drain |= q->nr_rqs_elvpriv;
 
                /*
                 * Unfortunately, requests are queued at and tracked from
@@ -397,7 +397,7 @@ void blk_drain_queue(struct request_queue *q, bool drain_all)
                if (drain_all) {
                        drain |= !list_empty(&q->queue_head);
                        for (i = 0; i < 2; i++) {
-                               drain |= q->rq.count[i];
+                               drain |= q->nr_rqs[i];
                                drain |= q->in_flight[i];
                                drain |= !list_empty(&q->flush_queue[i]);
                        }
@@ -416,9 +416,14 @@ void blk_drain_queue(struct request_queue *q, bool drain_all)
         * left with hung waiters. We need to wake up those waiters.
         */
        if (q->request_fn) {
+               struct request_list *rl;
+
                spin_lock_irq(q->queue_lock);
-               for (i = 0; i < ARRAY_SIZE(q->rq.wait); i++)
-                       wake_up_all(&q->rq.wait[i]);
+
+               blk_queue_for_each_rl(rl, q)
+                       for (i = 0; i < ARRAY_SIZE(rl->wait); i++)
+                               wake_up_all(&rl->wait[i]);
+
                spin_unlock_irq(q->queue_lock);
        }
 }
@@ -517,28 +522,33 @@ void blk_cleanup_queue(struct request_queue *q)
 }
 EXPORT_SYMBOL(blk_cleanup_queue);
 
-static int blk_init_free_list(struct request_queue *q)
+int blk_init_rl(struct request_list *rl, struct request_queue *q,
+               gfp_t gfp_mask)
 {
-       struct request_list *rl = &q->rq;
-
        if (unlikely(rl->rq_pool))
                return 0;
 
+       rl->q = q;
        rl->count[BLK_RW_SYNC] = rl->count[BLK_RW_ASYNC] = 0;
        rl->starved[BLK_RW_SYNC] = rl->starved[BLK_RW_ASYNC] = 0;
-       rl->elvpriv = 0;
        init_waitqueue_head(&rl->wait[BLK_RW_SYNC]);
        init_waitqueue_head(&rl->wait[BLK_RW_ASYNC]);
 
        rl->rq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab,
-                               mempool_free_slab, request_cachep, q->node);
-
+                                         mempool_free_slab, request_cachep,
+                                         gfp_mask, q->node);
        if (!rl->rq_pool)
                return -ENOMEM;
 
        return 0;
 }
 
+void blk_exit_rl(struct request_list *rl)
+{
+       if (rl->rq_pool)
+               mempool_destroy(rl->rq_pool);
+}
+
 struct request_queue *blk_alloc_queue(gfp_t gfp_mask)
 {
        return blk_alloc_queue_node(gfp_mask, -1);
@@ -680,7 +690,7 @@ blk_init_allocated_queue(struct request_queue *q, request_fn_proc *rfn,
        if (!q)
                return NULL;
 
-       if (blk_init_free_list(q))
+       if (blk_init_rl(&q->root_rl, q, GFP_KERNEL))
                return NULL;
 
        q->request_fn           = rfn;
@@ -722,15 +732,15 @@ bool blk_get_queue(struct request_queue *q)
 }
 EXPORT_SYMBOL(blk_get_queue);
 
-static inline void blk_free_request(struct request_queue *q, struct request *rq)
+static inline void blk_free_request(struct request_list *rl, struct request *rq)
 {
        if (rq->cmd_flags & REQ_ELVPRIV) {
-               elv_put_request(q, rq);
+               elv_put_request(rl->q, rq);
                if (rq->elv.icq)
                        put_io_context(rq->elv.icq->ioc);
        }
 
-       mempool_free(rq, q->rq.rq_pool);
+       mempool_free(rq, rl->rq_pool);
 }
 
 /*
@@ -767,18 +777,23 @@ static void ioc_set_batching(struct request_queue *q, struct io_context *ioc)
        ioc->last_waited = jiffies;
 }
 
-static void __freed_request(struct request_queue *q, int sync)
+static void __freed_request(struct request_list *rl, int sync)
 {
-       struct request_list *rl = &q->rq;
+       struct request_queue *q = rl->q;
 
-       if (rl->count[sync] < queue_congestion_off_threshold(q))
+       /*
+        * bdi isn't aware of blkcg yet.  As all async IOs end up root
+        * blkcg anyway, just use root blkcg state.
+        */
+       if (rl == &q->root_rl &&
+           rl->count[sync] < queue_congestion_off_threshold(q))
                blk_clear_queue_congested(q, sync);
 
        if (rl->count[sync] + 1 <= q->nr_requests) {
                if (waitqueue_active(&rl->wait[sync]))
                        wake_up(&rl->wait[sync]);
 
-               blk_clear_queue_full(q, sync);
+               blk_clear_rl_full(rl, sync);
        }
 }
 
@@ -786,19 +801,20 @@ static void __freed_request(struct request_queue *q, int sync)
  * A request has just been released.  Account for it, update the full and
  * congestion status, wake up any waiters.   Called under q->queue_lock.
  */
-static void freed_request(struct request_queue *q, unsigned int flags)
+static void freed_request(struct request_list *rl, unsigned int flags)
 {
-       struct request_list *rl = &q->rq;
+       struct request_queue *q = rl->q;
        int sync = rw_is_sync(flags);
 
+       q->nr_rqs[sync]--;
        rl->count[sync]--;
        if (flags & REQ_ELVPRIV)
-               rl->elvpriv--;
+               q->nr_rqs_elvpriv--;
 
-       __freed_request(q, sync);
+       __freed_request(rl, sync);
 
        if (unlikely(rl->starved[sync ^ 1]))
-               __freed_request(q, sync ^ 1);
+               __freed_request(rl, sync ^ 1);
 }
 
 /*
@@ -837,8 +853,8 @@ static struct io_context *rq_ioc(struct bio *bio)
 }
 
 /**
- * get_request - get a free request
- * @q: request_queue to allocate request from
+ * __get_request - get a free request
+ * @rl: request list to allocate from
  * @rw_flags: RW and SYNC flags
  * @bio: bio to allocate request for (can be %NULL)
  * @gfp_mask: allocation mask
@@ -850,20 +866,16 @@ static struct io_context *rq_ioc(struct bio *bio)
  * Returns %NULL on failure, with @q->queue_lock held.
  * Returns !%NULL on success, with @q->queue_lock *not held*.
  */
-static struct request *get_request(struct request_queue *q, int rw_flags,
-                                  struct bio *bio, gfp_t gfp_mask)
+static struct request *__get_request(struct request_list *rl, int rw_flags,
+                                    struct bio *bio, gfp_t gfp_mask)
 {
+       struct request_queue *q = rl->q;
        struct request *rq;
-       struct request_list *rl = &q->rq;
-       struct elevator_type *et;
-       struct io_context *ioc;
+       struct elevator_type *et = q->elevator->type;
+       struct io_context *ioc = rq_ioc(bio);
        struct io_cq *icq = NULL;
        const bool is_sync = rw_is_sync(rw_flags) != 0;
-       bool retried = false;
        int may_queue;
-retry:
-       et = q->elevator->type;
-       ioc = rq_ioc(bio);
 
        if (unlikely(blk_queue_dead(q)))
                return NULL;
@@ -874,29 +886,15 @@ retry:
 
        if (rl->count[is_sync]+1 >= queue_congestion_on_threshold(q)) {
                if (rl->count[is_sync]+1 >= q->nr_requests) {
-                       /*
-                        * We want ioc to record batching state.  If it's
-                        * not already there, creating a new one requires
-                        * dropping queue_lock, which in turn requires
-                        * retesting conditions to avoid queue hang.
-                        */
-                       if (!ioc && !retried) {
-                               spin_unlock_irq(q->queue_lock);
-                               create_io_context(gfp_mask, q->node);
-                               spin_lock_irq(q->queue_lock);
-                               retried = true;
-                               goto retry;
-                       }
-
                        /*
                         * The queue will fill after this allocation, so set
                         * it as full, and mark this process as "batching".
                         * This process will be allowed to complete a batch of
                         * requests, others will be blocked.
                         */
-                       if (!blk_queue_full(q, is_sync)) {
+                       if (!blk_rl_full(rl, is_sync)) {
                                ioc_set_batching(q, ioc);
-                               blk_set_queue_full(q, is_sync);
+                               blk_set_rl_full(rl, is_sync);
                        } else {
                                if (may_queue != ELV_MQUEUE_MUST
                                                && !ioc_batching(q, ioc)) {
@@ -909,7 +907,12 @@ retry:
                                }
                        }
                }
-               blk_set_queue_congested(q, is_sync);
+               /*
+                * bdi isn't aware of blkcg yet.  As all async IOs end up
+                * root blkcg anyway, just use root blkcg state.
+                */
+               if (rl == &q->root_rl)
+                       blk_set_queue_congested(q, is_sync);
        }
 
        /*
@@ -920,6 +923,7 @@ retry:
        if (rl->count[is_sync] >= (3 * q->nr_requests / 2))
                return NULL;
 
+       q->nr_rqs[is_sync]++;
        rl->count[is_sync]++;
        rl->starved[is_sync] = 0;
 
@@ -935,7 +939,7 @@ retry:
         */
        if (blk_rq_should_init_elevator(bio) && !blk_queue_bypass(q)) {
                rw_flags |= REQ_ELVPRIV;
-               rl->elvpriv++;
+               q->nr_rqs_elvpriv++;
                if (et->icq_cache && ioc)
                        icq = ioc_lookup_icq(ioc, q);
        }
@@ -945,22 +949,19 @@ retry:
        spin_unlock_irq(q->queue_lock);
 
        /* allocate and init request */
-       rq = mempool_alloc(q->rq.rq_pool, gfp_mask);
+       rq = mempool_alloc(rl->rq_pool, gfp_mask);
        if (!rq)
                goto fail_alloc;
 
        blk_rq_init(q, rq);
+       blk_rq_set_rl(rq, rl);
        rq->cmd_flags = rw_flags | REQ_ALLOCED;
 
        /* init elvpriv */
        if (rw_flags & REQ_ELVPRIV) {
                if (unlikely(et->icq_cache && !icq)) {
-                       create_io_context(gfp_mask, q->node);
-                       ioc = rq_ioc(bio);
-                       if (!ioc)
-                               goto fail_elvpriv;
-
-                       icq = ioc_create_icq(ioc, q, gfp_mask);
+                       if (ioc)
+                               icq = ioc_create_icq(ioc, q, gfp_mask);
                        if (!icq)
                                goto fail_elvpriv;
                }
@@ -1000,7 +1001,7 @@ fail_elvpriv:
        rq->elv.icq = NULL;
 
        spin_lock_irq(q->queue_lock);
-       rl->elvpriv--;
+       q->nr_rqs_elvpriv--;
        spin_unlock_irq(q->queue_lock);
        goto out;
 
@@ -1013,7 +1014,7 @@ fail_alloc:
         * queue, but this is pretty rare.
         */
        spin_lock_irq(q->queue_lock);
-       freed_request(q, rw_flags);
+       freed_request(rl, rw_flags);
 
        /*
         * in the very unlikely event that allocation failed and no
@@ -1029,56 +1030,58 @@ rq_starved:
 }
 
 /**
- * get_request_wait - get a free request with retry
+ * get_request - get a free request
  * @q: request_queue to allocate request from
  * @rw_flags: RW and SYNC flags
  * @bio: bio to allocate request for (can be %NULL)
+ * @gfp_mask: allocation mask
  *
- * Get a free request from @q.  This function keeps retrying under memory
- * pressure and fails iff @q is dead.
+ * Get a free request from @q.  If %__GFP_WAIT is set in @gfp_mask, this
+ * function keeps retrying under memory pressure and fails iff @q is dead.
  *
  * Must be callled with @q->queue_lock held and,
  * Returns %NULL on failure, with @q->queue_lock held.
  * Returns !%NULL on success, with @q->queue_lock *not held*.
  */
-static struct request *get_request_wait(struct request_queue *q, int rw_flags,
-                                       struct bio *bio)
+static struct request *get_request(struct request_queue *q, int rw_flags,
+                                  struct bio *bio, gfp_t gfp_mask)
 {
        const bool is_sync = rw_is_sync(rw_flags) != 0;
+       DEFINE_WAIT(wait);
+       struct request_list *rl;
        struct request *rq;
 
-       rq = get_request(q, rw_flags, bio, GFP_NOIO);
-       while (!rq) {
-               DEFINE_WAIT(wait);
-               struct request_list *rl = &q->rq;
-
-               if (unlikely(blk_queue_dead(q)))
-                       return NULL;
+       rl = blk_get_rl(q, bio);        /* transferred to @rq on success */
+retry:
+       rq = __get_request(rl, rw_flags, bio, gfp_mask);
+       if (rq)
+               return rq;
 
-               prepare_to_wait_exclusive(&rl->wait[is_sync], &wait,
-                               TASK_UNINTERRUPTIBLE);
+       if (!(gfp_mask & __GFP_WAIT) || unlikely(blk_queue_dead(q))) {
+               blk_put_rl(rl);
+               return NULL;
+       }
 
-               trace_block_sleeprq(q, bio, rw_flags & 1);
+       /* wait on @rl and retry */
+       prepare_to_wait_exclusive(&rl->wait[is_sync], &wait,
+                                 TASK_UNINTERRUPTIBLE);
 
-               spin_unlock_irq(q->queue_lock);
-               io_schedule();
+       trace_block_sleeprq(q, bio, rw_flags & 1);
 
-               /*
-                * After sleeping, we become a "batching" process and
-                * will be able to allocate at least one request, and
-                * up to a big batch of them for a small period time.
-                * See ioc_batching, ioc_set_batching
-                */
-               create_io_context(GFP_NOIO, q->node);
-               ioc_set_batching(q, current->io_context);
+       spin_unlock_irq(q->queue_lock);
+       io_schedule();
 
-               spin_lock_irq(q->queue_lock);
-               finish_wait(&rl->wait[is_sync], &wait);
+       /*
+        * After sleeping, we become a "batching" process and will be able
+        * to allocate at least one request, and up to a big batch of them
+        * for a small period time.  See ioc_batching, ioc_set_batching
+        */
+       ioc_set_batching(q, current->io_context);
 
-               rq = get_request(q, rw_flags, bio, GFP_NOIO);
-       };
+       spin_lock_irq(q->queue_lock);
+       finish_wait(&rl->wait[is_sync], &wait);
 
-       return rq;
+       goto retry;
 }
 
 struct request *blk_get_request(struct request_queue *q, int rw, gfp_t gfp_mask)
@@ -1087,11 +1090,11 @@ struct request *blk_get_request(struct request_queue *q, int rw, gfp_t gfp_mask)
 
        BUG_ON(rw != READ && rw != WRITE);
 
+       /* create ioc upfront */
+       create_io_context(gfp_mask, q->node);
+
        spin_lock_irq(q->queue_lock);
-       if (gfp_mask & __GFP_WAIT)
-               rq = get_request_wait(q, rw, NULL);
-       else
-               rq = get_request(q, rw, NULL, gfp_mask);
+       rq = get_request(q, rw, NULL, gfp_mask);
        if (!rq)
                spin_unlock_irq(q->queue_lock);
        /* q->queue_lock is unlocked at this point */
@@ -1248,12 +1251,14 @@ void __blk_put_request(struct request_queue *q, struct request *req)
         */
        if (req->cmd_flags & REQ_ALLOCED) {
                unsigned int flags = req->cmd_flags;
+               struct request_list *rl = blk_rq_rl(req);
 
                BUG_ON(!list_empty(&req->queuelist));
                BUG_ON(!hlist_unhashed(&req->hash));
 
-               blk_free_request(q, req);
-               freed_request(q, flags);
+               blk_free_request(rl, req);
+               freed_request(rl, flags);
+               blk_put_rl(rl);
        }
 }
 EXPORT_SYMBOL_GPL(__blk_put_request);
@@ -1481,7 +1486,7 @@ get_rq:
         * Grab a free request. This is might sleep but can not fail.
         * Returns with the queue unlocked.
         */
-       req = get_request_wait(q, rw_flags, bio);
+       req = get_request(q, rw_flags, bio, GFP_NOIO);
        if (unlikely(!req)) {
                bio_endio(bio, -ENODEV);        /* @q is dead */
                goto out_unlock;
@@ -1702,6 +1707,14 @@ generic_make_request_checks(struct bio *bio)
                goto end_io;
        }
 
+       /*
+        * Various block parts want %current->io_context and lazy ioc
+        * allocation ends up trading a lot of pain for a small amount of
+        * memory.  Just allocate it upfront.  This may fail and block
+        * layer knows how to live with it.
+        */
+       create_io_context(GFP_ATOMIC, q->node);
+
        if (blk_throtl_bio(q, bio))
                return false;   /* throttled, will be resubmitted later */
 
@@ -2896,23 +2909,47 @@ static void queue_unplugged(struct request_queue *q, unsigned int depth,
 
 }
 
-static void flush_plug_callbacks(struct blk_plug *plug)
+static void flush_plug_callbacks(struct blk_plug *plug, bool from_schedule)
 {
        LIST_HEAD(callbacks);
 
-       if (list_empty(&plug->cb_list))
-               return;
+       while (!list_empty(&plug->cb_list)) {
+               list_splice_init(&plug->cb_list, &callbacks);
 
-       list_splice_init(&plug->cb_list, &callbacks);
-
-       while (!list_empty(&callbacks)) {
-               struct blk_plug_cb *cb = list_first_entry(&callbacks,
+               while (!list_empty(&callbacks)) {
+                       struct blk_plug_cb *cb = list_first_entry(&callbacks,
                                                          struct blk_plug_cb,
                                                          list);
-               list_del(&cb->list);
-               cb->callback(cb);
+                       list_del(&cb->list);
+                       cb->callback(cb, from_schedule);
+               }
+       }
+}
+
+struct blk_plug_cb *blk_check_plugged(blk_plug_cb_fn unplug, void *data,
+                                     int size)
+{
+       struct blk_plug *plug = current->plug;
+       struct blk_plug_cb *cb;
+
+       if (!plug)
+               return NULL;
+
+       list_for_each_entry(cb, &plug->cb_list, list)
+               if (cb->callback == unplug && cb->data == data)
+                       return cb;
+
+       /* Not currently on the callback list */
+       BUG_ON(size < sizeof(*cb));
+       cb = kzalloc(size, GFP_ATOMIC);
+       if (cb) {
+               cb->data = data;
+               cb->callback = unplug;
+               list_add(&cb->list, &plug->cb_list);
        }
+       return cb;
 }
+EXPORT_SYMBOL(blk_check_plugged);
 
 void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule)
 {
@@ -2924,7 +2961,7 @@ void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule)
 
        BUG_ON(plug->magic != PLUG_MAGIC);
 
-       flush_plug_callbacks(plug);
+       flush_plug_callbacks(plug, from_schedule);
        if (list_empty(&plug->list))
                return;
 
index 893b8007c657e8bd0ca93d5ba61e9e7d02aa892b..fab4cdd3f7bbb8f5d8bda98c3a8771a1ac61bc46 100644 (file)
@@ -244,6 +244,7 @@ int create_task_io_context(struct task_struct *task, gfp_t gfp_flags, int node)
 
        /* initialize */
        atomic_long_set(&ioc->refcount, 1);
+       atomic_set(&ioc->nr_tasks, 1);
        atomic_set(&ioc->active_ref, 1);
        spin_lock_init(&ioc->lock);
        INIT_RADIX_TREE(&ioc->icq_tree, GFP_ATOMIC | __GFP_HIGH);
index d3234fc494adcd48ff1dd69c9feb117e93e333c0..565a6786032f59e40cee28bf4bde4ffb451cffa8 100644 (file)
@@ -143,8 +143,7 @@ void blk_set_stacking_limits(struct queue_limits *lim)
        lim->discard_zeroes_data = 1;
        lim->max_segments = USHRT_MAX;
        lim->max_hw_sectors = UINT_MAX;
-
-       lim->max_sectors = BLK_DEF_MAX_SECTORS;
+       lim->max_sectors = UINT_MAX;
 }
 EXPORT_SYMBOL(blk_set_stacking_limits);
 
index aa41b47c22d2e89525a5bd3cfb9501e67634bff7..9628b291f96057a42cbf6a5492bd7480fe7e93da 100644 (file)
@@ -40,7 +40,7 @@ static ssize_t queue_requests_show(struct request_queue *q, char *page)
 static ssize_t
 queue_requests_store(struct request_queue *q, const char *page, size_t count)
 {
-       struct request_list *rl = &q->rq;
+       struct request_list *rl;
        unsigned long nr;
        int ret;
 
@@ -55,6 +55,9 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count)
        q->nr_requests = nr;
        blk_queue_congestion_threshold(q);
 
+       /* congestion isn't cgroup aware and follows root blkcg for now */
+       rl = &q->root_rl;
+
        if (rl->count[BLK_RW_SYNC] >= queue_congestion_on_threshold(q))
                blk_set_queue_congested(q, BLK_RW_SYNC);
        else if (rl->count[BLK_RW_SYNC] < queue_congestion_off_threshold(q))
@@ -65,19 +68,22 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count)
        else if (rl->count[BLK_RW_ASYNC] < queue_congestion_off_threshold(q))
                blk_clear_queue_congested(q, BLK_RW_ASYNC);
 
-       if (rl->count[BLK_RW_SYNC] >= q->nr_requests) {
-               blk_set_queue_full(q, BLK_RW_SYNC);
-       } else {
-               blk_clear_queue_full(q, BLK_RW_SYNC);
-               wake_up(&rl->wait[BLK_RW_SYNC]);
+       blk_queue_for_each_rl(rl, q) {
+               if (rl->count[BLK_RW_SYNC] >= q->nr_requests) {
+                       blk_set_rl_full(rl, BLK_RW_SYNC);
+               } else {
+                       blk_clear_rl_full(rl, BLK_RW_SYNC);
+                       wake_up(&rl->wait[BLK_RW_SYNC]);
+               }
+
+               if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) {
+                       blk_set_rl_full(rl, BLK_RW_ASYNC);
+               } else {
+                       blk_clear_rl_full(rl, BLK_RW_ASYNC);
+                       wake_up(&rl->wait[BLK_RW_ASYNC]);
+               }
        }
 
-       if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) {
-               blk_set_queue_full(q, BLK_RW_ASYNC);
-       } else {
-               blk_clear_queue_full(q, BLK_RW_ASYNC);
-               wake_up(&rl->wait[BLK_RW_ASYNC]);
-       }
        spin_unlock_irq(q->queue_lock);
        return ret;
 }
@@ -476,7 +482,6 @@ static void blk_release_queue(struct kobject *kobj)
 {
        struct request_queue *q =
                container_of(kobj, struct request_queue, kobj);
-       struct request_list *rl = &q->rq;
 
        blk_sync_queue(q);
 
@@ -489,8 +494,7 @@ static void blk_release_queue(struct kobject *kobj)
                elevator_exit(q->elevator);
        }
 
-       if (rl->rq_pool)
-               mempool_destroy(rl->rq_pool);
+       blk_exit_rl(&q->root_rl);
 
        if (q->queue_tags)
                __blk_queue_free_tags(q);
index 5b0659512047208efdcc3db7d714bc72dfd456f5..e287c19908c8a31d3c4d29b1586921066032afa6 100644 (file)
@@ -1123,9 +1123,6 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio)
                goto out;
        }
 
-       /* bio_associate_current() needs ioc, try creating */
-       create_io_context(GFP_ATOMIC, q->node);
-
        /*
         * A throtl_grp pointer retrieved under rcu can be used to access
         * basic fields like stats and io rates. If a group has no rules,
index 85f6ae42f7d3f698e9e82c75064f428065953e70..2a0ea32d249fdaa9694e0249e435777565535d70 100644 (file)
@@ -18,6 +18,9 @@ static inline void __blk_get_queue(struct request_queue *q)
        kobject_get(&q->kobj);
 }
 
+int blk_init_rl(struct request_list *rl, struct request_queue *q,
+               gfp_t gfp_mask);
+void blk_exit_rl(struct request_list *rl);
 void init_request_from_bio(struct request *req, struct bio *bio);
 void blk_rq_bio_prep(struct request_queue *q, struct request *rq,
                        struct bio *bio);
@@ -33,7 +36,6 @@ bool __blk_end_bidi_request(struct request *rq, int error,
 void blk_rq_timed_out_timer(unsigned long data);
 void blk_delete_timer(struct request *);
 void blk_add_timer(struct request *);
-void __generic_unplug_device(struct request_queue *);
 
 /*
  * Internal atomic flags for request handling
index 7ad49c88f6b197a04c66e05aab9facacd2781af4..deee61fbb7419005886234b47a68b73b5833d20e 100644 (file)
@@ -243,56 +243,3 @@ int bsg_setup_queue(struct device *dev, struct request_queue *q,
        return 0;
 }
 EXPORT_SYMBOL_GPL(bsg_setup_queue);
-
-/**
- * bsg_remove_queue - Deletes the bsg dev from the q
- * @q: the request_queue that is to be torn down.
- *
- * Notes:
- *   Before unregistering the queue empty any requests that are blocked
- */
-void bsg_remove_queue(struct request_queue *q)
-{
-       struct request *req; /* block request */
-       int counts; /* totals for request_list count and starved */
-
-       if (!q)
-               return;
-
-       /* Stop taking in new requests */
-       spin_lock_irq(q->queue_lock);
-       blk_stop_queue(q);
-
-       /* drain all requests in the queue */
-       while (1) {
-               /* need the lock to fetch a request
-                * this may fetch the same reqeust as the previous pass
-                */
-               req = blk_fetch_request(q);
-               /* save requests in use and starved */
-               counts = q->rq.count[0] + q->rq.count[1] +
-                        q->rq.starved[0] + q->rq.starved[1];
-               spin_unlock_irq(q->queue_lock);
-               /* any requests still outstanding? */
-               if (counts == 0)
-                       break;
-
-               /* This may be the same req as the previous iteration,
-                * always send the blk_end_request_all after a prefetch.
-                * It is not okay to not end the request because the
-                * prefetch started the request.
-                */
-               if (req) {
-                       /* return -ENXIO to indicate that this queue is
-                        * going away
-                        */
-                       req->errors = -ENXIO;
-                       blk_end_request_all(req, -ENXIO);
-               }
-
-               msleep(200); /* allow bsg to possibly finish */
-               spin_lock_irq(q->queue_lock);
-       }
-       bsg_unregister_queue(q);
-}
-EXPORT_SYMBOL_GPL(bsg_remove_queue);
index 9cf5583c90ffa75c2b010020fb22fc8ddf55e82a..cac7366957c376cedb2341753520e6f32516572c 100644 (file)
@@ -154,7 +154,7 @@ struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter)
                part = rcu_dereference(ptbl->part[piter->idx]);
                if (!part)
                        continue;
-               if (!part->nr_sects &&
+               if (!part_nr_sects_read(part) &&
                    !(piter->flags & DISK_PITER_INCL_EMPTY) &&
                    !(piter->flags & DISK_PITER_INCL_EMPTY_PART0 &&
                      piter->idx == 0))
@@ -191,7 +191,7 @@ EXPORT_SYMBOL_GPL(disk_part_iter_exit);
 static inline int sector_in_part(struct hd_struct *part, sector_t sector)
 {
        return part->start_sect <= sector &&
-               sector < part->start_sect + part->nr_sects;
+               sector < part->start_sect + part_nr_sects_read(part);
 }
 
 /**
@@ -769,8 +769,8 @@ void __init printk_all_partitions(void)
 
                        printk("%s%s %10llu %s %s", is_part0 ? "" : "  ",
                               bdevt_str(part_devt(part), devt_buf),
-                              (unsigned long long)part->nr_sects >> 1,
-                              disk_name(disk, part->partno, name_buf),
+                              (unsigned long long)part_nr_sects_read(part) >> 1
+                              disk_name(disk, part->partno, name_buf),
                               uuid_buf);
                        if (is_part0) {
                                if (disk->driverfs_dev != NULL &&
@@ -862,7 +862,7 @@ static int show_partition(struct seq_file *seqf, void *v)
        while ((part = disk_part_iter_next(&piter)))
                seq_printf(seqf, "%4d  %7d %10llu %s\n",
                           MAJOR(part_devt(part)), MINOR(part_devt(part)),
-                          (unsigned long long)part->nr_sects >> 1,
+                          (unsigned long long)part_nr_sects_read(part) >> 1,
                           disk_name(sgp, part->partno, buf));
        disk_part_iter_exit(&piter);
 
@@ -1268,6 +1268,16 @@ struct gendisk *alloc_disk_node(int minors, int node_id)
                }
                disk->part_tbl->part[0] = &disk->part0;
 
+               /*
+                * set_capacity() and get_capacity() currently don't use
+                * seqcounter to read/update the part0->nr_sects. Still init
+                * the counter as we can read the sectors in IO submission
+                * patch using seqence counters.
+                *
+                * TODO: Ideally set_capacity() and get_capacity() should be
+                * converted to make use of bd_mutex and sequence counters.
+                */
+               seqcount_init(&disk->part0.nr_sects_seq);
                hd_ref_init(&disk->part0);
 
                disk->minors = minors;
index ba15b2dbfb98ea55911109543f35889ea7b615da..4476e0e85d1687c08b31f81e7f633ae802b14814 100644 (file)
@@ -13,7 +13,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user
 {
        struct block_device *bdevp;
        struct gendisk *disk;
-       struct hd_struct *part;
+       struct hd_struct *part, *lpart;
        struct blkpg_ioctl_arg a;
        struct blkpg_partition p;
        struct disk_part_iter piter;
@@ -36,8 +36,8 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user
                case BLKPG_ADD_PARTITION:
                        start = p.start >> 9;
                        length = p.length >> 9;
-                       /* check for fit in a hd_struct */ 
-                       if (sizeof(sector_t) == sizeof(long) && 
+                       /* check for fit in a hd_struct */
+                       if (sizeof(sector_t) == sizeof(long) &&
                            sizeof(long long) > sizeof(long)) {
                                long pstart = start, plength = length;
                                if (pstart != start || plength != length
@@ -91,6 +91,59 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user
                        mutex_unlock(&bdevp->bd_mutex);
                        bdput(bdevp);
 
+                       return 0;
+               case BLKPG_RESIZE_PARTITION:
+                       start = p.start >> 9;
+                       /* new length of partition in bytes */
+                       length = p.length >> 9;
+                       /* check for fit in a hd_struct */
+                       if (sizeof(sector_t) == sizeof(long) &&
+                           sizeof(long long) > sizeof(long)) {
+                               long pstart = start, plength = length;
+                               if (pstart != start || plength != length
+                                   || pstart < 0 || plength < 0)
+                                       return -EINVAL;
+                       }
+                       part = disk_get_part(disk, partno);
+                       if (!part)
+                               return -ENXIO;
+                       bdevp = bdget(part_devt(part));
+                       if (!bdevp) {
+                               disk_put_part(part);
+                               return -ENOMEM;
+                       }
+                       mutex_lock(&bdevp->bd_mutex);
+                       mutex_lock_nested(&bdev->bd_mutex, 1);
+                       if (start != part->start_sect) {
+                               mutex_unlock(&bdevp->bd_mutex);
+                               mutex_unlock(&bdev->bd_mutex);
+                               bdput(bdevp);
+                               disk_put_part(part);
+                               return -EINVAL;
+                       }
+                       /* overlap? */
+                       disk_part_iter_init(&piter, disk,
+                                           DISK_PITER_INCL_EMPTY);
+                       while ((lpart = disk_part_iter_next(&piter))) {
+                               if (lpart->partno != partno &&
+                                  !(start + length <= lpart->start_sect ||
+                                  start >= lpart->start_sect + lpart->nr_sects)
+                                  ) {
+                                       disk_part_iter_exit(&piter);
+                                       mutex_unlock(&bdevp->bd_mutex);
+                                       mutex_unlock(&bdev->bd_mutex);
+                                       bdput(bdevp);
+                                       disk_put_part(part);
+                                       return -EBUSY;
+                               }
+                       }
+                       disk_part_iter_exit(&piter);
+                       part_nr_sects_write(part, (sector_t)length);
+                       i_size_write(bdevp->bd_inode, p.length);
+                       mutex_unlock(&bdevp->bd_mutex);
+                       mutex_unlock(&bdev->bd_mutex);
+                       bdput(bdevp);
+                       disk_put_part(part);
                        return 0;
                default:
                        return -EINVAL;
index 6df5d6928a440c53b44f158ea99b98d7d9a48660..f1d14519cc040424e79fe0f35f446e4d4e7a123d 100644 (file)
@@ -84,7 +84,7 @@ ssize_t part_size_show(struct device *dev,
                       struct device_attribute *attr, char *buf)
 {
        struct hd_struct *p = dev_to_part(dev);
-       return sprintf(buf, "%llu\n",(unsigned long long)p->nr_sects);
+       return sprintf(buf, "%llu\n",(unsigned long long)part_nr_sects_read(p));
 }
 
 static ssize_t part_ro_show(struct device *dev,
@@ -294,6 +294,8 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno,
                err = -ENOMEM;
                goto out_free;
        }
+
+       seqcount_init(&p->nr_sects_seq);
        pdev = part_to_dev(p);
 
        p->start_sect = start;
index bfc918633fd9275f856d53e280fae9be2f8ba698..ece958d3762e4a0701189ee9be23b1876a6740c7 100644 (file)
@@ -112,6 +112,8 @@ source "drivers/auxdisplay/Kconfig"
 
 source "drivers/uio/Kconfig"
 
+source "drivers/vfio/Kconfig"
+
 source "drivers/vlynq/Kconfig"
 
 source "drivers/virtio/Kconfig"
@@ -148,4 +150,6 @@ source "drivers/iio/Kconfig"
 
 source "drivers/vme/Kconfig"
 
+source "drivers/pwm/Kconfig"
+
 endmenu
index 2ba29ffef2cbd84ff0a55d66a861ab3eee2159fa..5b421840c48d28284978b6a8d6aa94eb15097c08 100644 (file)
@@ -8,6 +8,7 @@
 # GPIO must come after pinctrl as gpios may need to mux pins etc
 obj-y                          += pinctrl/
 obj-y                          += gpio/
+obj-y                          += pwm/
 obj-$(CONFIG_PCI)              += pci/
 obj-$(CONFIG_PARISC)           += parisc/
 obj-$(CONFIG_RAPIDIO)          += rapidio/
@@ -59,6 +60,7 @@ obj-$(CONFIG_ATM)             += atm/
 obj-$(CONFIG_FUSION)           += message/
 obj-y                          += firewire/
 obj-$(CONFIG_UIO)              += uio/
+obj-$(CONFIG_VFIO)             += vfio/
 obj-y                          += cdrom/
 obj-y                          += auxdisplay/
 obj-$(CONFIG_PCCARD)           += pcmcia/
index 45d8097ef4cf0d0aae43e8e0a3576613ca90fdd8..b728880ef10e92d4baac018babd1b515c9c06f5f 100644 (file)
@@ -132,6 +132,33 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
        return AE_OK;
 }
 
+/* Force to use vendor driver when the ACPI device is known to be
+ * buggy */
+static int video_detect_force_vendor(const struct dmi_system_id *d)
+{
+       acpi_video_support |= ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
+       return 0;
+}
+
+static struct dmi_system_id video_detect_dmi_table[] = {
+       /* On Samsung X360, the BIOS will set a flag (VDRV) if generic
+        * ACPI backlight device is used. This flag will definitively break
+        * the backlight interface (even the vendor interface) untill next
+        * reboot. It's why we should prevent video.ko from being used here
+        * and we can't rely on a later call to acpi_video_unregister().
+        */
+       {
+        .callback = video_detect_force_vendor,
+        .ident = "X360",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+               DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
+               DMI_MATCH(DMI_BOARD_NAME, "X360"),
+               },
+       },
+       { },
+};
+
 /*
  * Returns the video capabilities of a specific ACPI graphics device
  *
@@ -164,6 +191,8 @@ long acpi_video_get_capabilities(acpi_handle graphics_handle)
                 *              ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
                 *}
                 */
+
+               dmi_check_system(video_detect_dmi_table);
        } else {
                status = acpi_bus_get_device(graphics_handle, &tmp_dev);
                if (ACPI_FAILURE(status)) {
@@ -182,8 +211,7 @@ long acpi_video_get_capabilities(acpi_handle graphics_handle)
 }
 EXPORT_SYMBOL(acpi_video_get_capabilities);
 
-/* Returns true if video.ko can do backlight switching */
-int acpi_video_backlight_support(void)
+static void acpi_video_caps_check(void)
 {
        /*
         * We must check whether the ACPI graphics device is physically plugged
@@ -191,6 +219,34 @@ int acpi_video_backlight_support(void)
         */
        if (!acpi_video_caps_checked)
                acpi_video_get_capabilities(NULL);
+}
+
+/* Promote the vendor interface instead of the generic video module.
+ * This function allow DMI blacklists to be implemented by externals
+ * platform drivers instead of putting a big blacklist in video_detect.c
+ * After calling this function you will probably want to call
+ * acpi_video_unregister() to make sure the video module is not loaded
+ */
+void acpi_video_dmi_promote_vendor(void)
+{
+       acpi_video_caps_check();
+       acpi_video_support |= ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
+}
+EXPORT_SYMBOL(acpi_video_dmi_promote_vendor);
+
+/* To be called when a driver who previously promoted the vendor
+ * interface */
+void acpi_video_dmi_demote_vendor(void)
+{
+       acpi_video_caps_check();
+       acpi_video_support &= ~ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
+}
+EXPORT_SYMBOL(acpi_video_dmi_demote_vendor);
+
+/* Returns true if video.ko can do backlight switching */
+int acpi_video_backlight_support(void)
+{
+       acpi_video_caps_check();
 
        /* First check for boot param -> highest prio */
        if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR)
index ac6a5beb28f3b99e090f358257ef161476113c32..bfaa5cb1629ae8651f111c46d0e78b694072aac1 100644 (file)
 struct arasan_cf_dev {
        /* pointer to ata_host structure */
        struct ata_host *host;
-       /* clk structure, only if HAVE_CLK is defined */
-#ifdef CONFIG_HAVE_CLK
+       /* clk structure */
        struct clk *clk;
-#endif
 
        /* physical base address of controller */
        dma_addr_t pbase;
@@ -312,13 +310,11 @@ static int cf_init(struct arasan_cf_dev *acdev)
        unsigned long flags;
        int ret = 0;
 
-#ifdef CONFIG_HAVE_CLK
        ret = clk_enable(acdev->clk);
        if (ret) {
                dev_dbg(acdev->host->dev, "clock enable failed");
                return ret;
        }
-#endif
 
        spin_lock_irqsave(&acdev->host->lock, flags);
        /* configure CF interface clock */
@@ -344,9 +340,7 @@ static void cf_exit(struct arasan_cf_dev *acdev)
        writel(readl(acdev->vbase + OP_MODE) & ~CFHOST_ENB,
                        acdev->vbase + OP_MODE);
        spin_unlock_irqrestore(&acdev->host->lock, flags);
-#ifdef CONFIG_HAVE_CLK
        clk_disable(acdev->clk);
-#endif
 }
 
 static void dma_callback(void *dev)
@@ -828,13 +822,11 @@ static int __devinit arasan_cf_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-#ifdef CONFIG_HAVE_CLK
        acdev->clk = clk_get(&pdev->dev, NULL);
        if (IS_ERR(acdev->clk)) {
                dev_warn(&pdev->dev, "Clock not found\n");
                return PTR_ERR(acdev->clk);
        }
-#endif
 
        /* allocate host */
        host = ata_host_alloc(&pdev->dev, 1);
@@ -899,9 +891,7 @@ static int __devinit arasan_cf_probe(struct platform_device *pdev)
                        &arasan_cf_sht);
 
 free_clk:
-#ifdef CONFIG_HAVE_CLK
        clk_put(acdev->clk);
-#endif
        return ret;
 }
 
@@ -912,9 +902,7 @@ static int __devexit arasan_cf_remove(struct platform_device *pdev)
 
        ata_host_detach(host);
        cf_exit(acdev);
-#ifdef CONFIG_HAVE_CLK
        clk_put(acdev->clk);
-#endif
 
        return 0;
 }
index 9b21469482aead0e69004e3285258fd3a0576796..08b4c520938463fd989bba879d791a3e418efd39 100644 (file)
@@ -196,6 +196,7 @@ config CMA
        bool "Contiguous Memory Allocator (EXPERIMENTAL)"
        depends on HAVE_DMA_CONTIGUOUS && HAVE_MEMBLOCK && EXPERIMENTAL
        select MIGRATION
+       select MEMORY_ISOLATION
        help
          This enables the Contiguous Memory Allocator which allows drivers
          to allocate big physically-contiguous blocks of memory for use with
index d91a3a0b23258a516f352162a5d051df17959e80..deb4a456cf8365ac3a45480135786c160473964f 100644 (file)
@@ -156,9 +156,7 @@ static int dev_mkdir(const char *name, umode_t mode)
        if (!err)
                /* mark as kernel-created inode */
                dentry->d_inode->i_private = &thread;
-       dput(dentry);
-       mutex_unlock(&path.dentry->d_inode->i_mutex);
-       path_put(&path);
+       done_path_create(&path, dentry);
        return err;
 }
 
@@ -218,10 +216,7 @@ static int handle_create(const char *nodename, umode_t mode, struct device *dev)
                /* mark as kernel-created inode */
                dentry->d_inode->i_private = &thread;
        }
-       dput(dentry);
-
-       mutex_unlock(&path.dentry->d_inode->i_mutex);
-       path_put(&path);
+       done_path_create(&path, dentry);
        return err;
 }
 
index 6f3676f1559f173b7767a2bdda577df2292ef461..3fbedc75e7c56219d1f0eafeabdebe9e058edc27 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/export.h>
 #include <linux/gfp.h>
+#include <asm-generic/dma-coherent.h>
 
 /*
  * Managed DMA API
@@ -217,4 +218,52 @@ void dmam_release_declared_memory(struct device *dev)
 }
 EXPORT_SYMBOL(dmam_release_declared_memory);
 
+/*
+ * Create scatter-list for the already allocated DMA buffer.
+ */
+int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
+                void *cpu_addr, dma_addr_t handle, size_t size)
+{
+       struct page *page = virt_to_page(cpu_addr);
+       int ret;
+
+       ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
+       if (unlikely(ret))
+               return ret;
+
+       sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
+       return 0;
+}
+EXPORT_SYMBOL(dma_common_get_sgtable);
+
 #endif
+
+/*
+ * Create userspace mapping for the DMA-coherent memory.
+ */
+int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
+                   void *cpu_addr, dma_addr_t dma_addr, size_t size)
+{
+       int ret = -ENXIO;
+#ifdef CONFIG_MMU
+       unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+       unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
+       unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
+       unsigned long off = vma->vm_pgoff;
+
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+       if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret))
+               return ret;
+
+       if (off < count && user_count <= (count - off)) {
+               ret = remap_pfn_range(vma, vma->vm_start,
+                                     pfn + off,
+                                     user_count << PAGE_SHIFT,
+                                     vma->vm_page_prot);
+       }
+#endif /* CONFIG_MMU */
+
+       return ret;
+}
+EXPORT_SYMBOL(dma_common_mmap);
index fc996288090b3e83a907c762580df73e0a38f997..f7b0af7100cdf5cc0c074a09ddd842c0b459b715 100644 (file)
@@ -273,6 +273,7 @@ static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = {
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
        { 0, },
 };
index 26823d97fd9f60925967b51a4fdfc158454bbe1b..9ea4627dc0c233a808f322816c2560fd9c145d3d 100644 (file)
@@ -507,7 +507,9 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
                /* for these chips OTP is always available */
                present = true;
                break;
-
+       case BCMA_CHIP_ID_BCM43228:
+               present = chip_status & BCMA_CC_CHIPST_43228_OTP_PRESENT;
+               break;
        default:
                present = false;
                break;
index e54e31b02b88eb6e927072745f345871a8d96203..3fbef018ce555fe47a716a0de01c2df837a2cc9e 100644 (file)
@@ -411,7 +411,7 @@ w_al_write_transaction(struct drbd_conf *mdev, struct drbd_work *w, int unused)
                + mdev->ldev->md.al_offset + mdev->al_tr_pos;
 
        if (!drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE))
-               drbd_chk_io_error(mdev, 1, true);
+               drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
 
        if (++mdev->al_tr_pos >
            div_ceil(mdev->act_log->nr_elements, AL_EXTENTS_PT))
@@ -876,7 +876,11 @@ int __drbd_set_out_of_sync(struct drbd_conf *mdev, sector_t sector, int size,
        unsigned int enr, count = 0;
        struct lc_element *e;
 
-       if (size <= 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_BIO_SIZE) {
+       /* this should be an empty REQ_FLUSH */
+       if (size == 0)
+               return 0;
+
+       if (size < 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_BIO_SIZE) {
                dev_err(DEV, "sector: %llus, size: %d\n",
                        (unsigned long long)sector, size);
                return 0;
index fcb956bb4b4c525875f961e7c2398b20c35723f4..ba91b408abad75ce7da0ff173595ce23ee51f635 100644 (file)
@@ -1096,7 +1096,7 @@ static int bm_rw(struct drbd_conf *mdev, int rw, unsigned flags, unsigned lazy_w
 
        if (ctx->error) {
                dev_alert(DEV, "we had at least one MD IO ERROR during bitmap IO\n");
-               drbd_chk_io_error(mdev, 1, true);
+               drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
                err = -EIO; /* ctx->error ? */
        }
 
@@ -1212,7 +1212,7 @@ int drbd_bm_write_page(struct drbd_conf *mdev, unsigned int idx) __must_hold(loc
        wait_until_done_or_disk_failure(mdev, mdev->ldev, &ctx->done);
 
        if (ctx->error)
-               drbd_chk_io_error(mdev, 1, true);
+               drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
                /* that should force detach, so the in memory bitmap will be
                 * gone in a moment as well. */
 
index 02f013a073a75b66fe73c8658f1cde96ea7102a7..b2ca143d0053d75487e3a083a4271b9e5d0d285e 100644 (file)
@@ -813,7 +813,6 @@ enum {
        SIGNAL_ASENDER,         /* whether asender wants to be interrupted */
        SEND_PING,              /* whether asender should send a ping asap */
 
-       UNPLUG_QUEUED,          /* only relevant with kernel 2.4 */
        UNPLUG_REMOTE,          /* sending a "UnplugRemote" could help */
        MD_DIRTY,               /* current uuids and flags not yet on disk */
        DISCARD_CONCURRENT,     /* Set on one node, cleared on the peer! */
@@ -824,7 +823,6 @@ enum {
        CRASHED_PRIMARY,        /* This node was a crashed primary.
                                 * Gets cleared when the state.conn
                                 * goes into C_CONNECTED state. */
-       NO_BARRIER_SUPP,        /* underlying block device doesn't implement barriers */
        CONSIDER_RESYNC,
 
        MD_NO_FUA,              /* Users wants us to not use FUA/FLUSH on meta data dev */
@@ -834,6 +832,7 @@ enum {
        BITMAP_IO_QUEUED,       /* Started bitmap IO */
        GO_DISKLESS,            /* Disk is being detached, on io-error or admin request. */
        WAS_IO_ERROR,           /* Local disk failed returned IO error */
+       FORCE_DETACH,           /* Force-detach from local disk, aborting any pending local IO */
        RESYNC_AFTER_NEG,       /* Resync after online grow after the attach&negotiate finished. */
        NET_CONGESTED,          /* The data socket is congested */
 
@@ -851,6 +850,13 @@ enum {
        AL_SUSPENDED,           /* Activity logging is currently suspended. */
        AHEAD_TO_SYNC_SOURCE,   /* Ahead -> SyncSource queued */
        STATE_SENT,             /* Do not change state/UUIDs while this is set */
+
+       CALLBACK_PENDING,       /* Whether we have a call_usermodehelper(, UMH_WAIT_PROC)
+                                * pending, from drbd worker context.
+                                * If set, bdi_write_congested() returns true,
+                                * so shrink_page_list() would not recurse into,
+                                * and potentially deadlock on, this drbd worker.
+                                */
 };
 
 struct drbd_bitmap; /* opaque for drbd_conf */
@@ -1130,8 +1136,8 @@ struct drbd_conf {
        int rs_in_flight; /* resync sectors in flight (to proxy, in proxy and from proxy) */
        int rs_planed;    /* resync sectors already planned */
        atomic_t ap_in_flight; /* App sectors in flight (waiting for ack) */
-       int peer_max_bio_size;
-       int local_max_bio_size;
+       unsigned int peer_max_bio_size;
+       unsigned int local_max_bio_size;
 };
 
 static inline struct drbd_conf *minor_to_mdev(unsigned int minor)
@@ -1435,9 +1441,9 @@ struct bm_extent {
  * hash table. */
 #define HT_SHIFT 8
 #define DRBD_MAX_BIO_SIZE (1U<<(9+HT_SHIFT))
-#define DRBD_MAX_BIO_SIZE_SAFE (1 << 12)       /* Works always = 4k */
+#define DRBD_MAX_BIO_SIZE_SAFE (1U << 12)       /* Works always = 4k */
 
-#define DRBD_MAX_SIZE_H80_PACKET (1 << 15) /* The old header only allows packets up to 32Kib data */
+#define DRBD_MAX_SIZE_H80_PACKET (1U << 15) /* The old header only allows packets up to 32Kib data */
 
 /* Number of elements in the app_reads_hash */
 #define APP_R_HSIZE 15
@@ -1840,12 +1846,20 @@ static inline int drbd_request_state(struct drbd_conf *mdev,
        return _drbd_request_state(mdev, mask, val, CS_VERBOSE + CS_ORDERED);
 }
 
+enum drbd_force_detach_flags {
+       DRBD_IO_ERROR,
+       DRBD_META_IO_ERROR,
+       DRBD_FORCE_DETACH,
+};
+
 #define __drbd_chk_io_error(m,f) __drbd_chk_io_error_(m,f, __func__)
-static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, int forcedetach, const char *where)
+static inline void __drbd_chk_io_error_(struct drbd_conf *mdev,
+               enum drbd_force_detach_flags forcedetach,
+               const char *where)
 {
        switch (mdev->ldev->dc.on_io_error) {
        case EP_PASS_ON:
-               if (!forcedetach) {
+               if (forcedetach == DRBD_IO_ERROR) {
                        if (__ratelimit(&drbd_ratelimit_state))
                                dev_err(DEV, "Local IO failed in %s.\n", where);
                        if (mdev->state.disk > D_INCONSISTENT)
@@ -1856,6 +1870,8 @@ static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, int forcedetach,
        case EP_DETACH:
        case EP_CALL_HELPER:
                set_bit(WAS_IO_ERROR, &mdev->flags);
+               if (forcedetach == DRBD_FORCE_DETACH)
+                       set_bit(FORCE_DETACH, &mdev->flags);
                if (mdev->state.disk > D_FAILED) {
                        _drbd_set_state(_NS(mdev, disk, D_FAILED), CS_HARD, NULL);
                        dev_err(DEV,
@@ -1875,7 +1891,7 @@ static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, int forcedetach,
  */
 #define drbd_chk_io_error(m,e,f) drbd_chk_io_error_(m,e,f, __func__)
 static inline void drbd_chk_io_error_(struct drbd_conf *mdev,
-       int error, int forcedetach, const char *where)
+       int error, enum drbd_force_detach_flags forcedetach, const char *where)
 {
        if (error) {
                unsigned long flags;
@@ -2405,15 +2421,17 @@ static inline void dec_ap_bio(struct drbd_conf *mdev)
        int ap_bio = atomic_dec_return(&mdev->ap_bio_cnt);
 
        D_ASSERT(ap_bio >= 0);
+
+       if (ap_bio == 0 && test_bit(BITMAP_IO, &mdev->flags)) {
+               if (!test_and_set_bit(BITMAP_IO_QUEUED, &mdev->flags))
+                       drbd_queue_work(&mdev->data.work, &mdev->bm_io_work.w);
+       }
+
        /* this currently does wake_up for every dec_ap_bio!
         * maybe rather introduce some type of hysteresis?
         * e.g. (ap_bio == mxb/2 || ap_bio == 0) ? */
        if (ap_bio < mxb)
                wake_up(&mdev->misc_wait);
-       if (ap_bio == 0 && test_bit(BITMAP_IO, &mdev->flags)) {
-               if (!test_and_set_bit(BITMAP_IO_QUEUED, &mdev->flags))
-                       drbd_queue_work(&mdev->data.work, &mdev->bm_io_work.w);
-       }
 }
 
 static inline int drbd_set_ed_uuid(struct drbd_conf *mdev, u64 val)
index 920ede2829d6c5e467e177ac43a3e97e9f550aac..2e0e7fc1dbbaf50e24d8a446a4e20aeb2239f5bb 100644 (file)
@@ -1514,6 +1514,13 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
 
        /* Do not change the order of the if above and the two below... */
        if (os.pdsk == D_DISKLESS && ns.pdsk > D_DISKLESS) {      /* attach on the peer */
+               /* we probably will start a resync soon.
+                * make sure those things are properly reset. */
+               mdev->rs_total = 0;
+               mdev->rs_failed = 0;
+               atomic_set(&mdev->rs_pending_cnt, 0);
+               drbd_rs_cancel_all(mdev);
+
                drbd_send_uuids(mdev);
                drbd_send_state(mdev, ns);
        }
@@ -1630,9 +1637,24 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
                        eh = mdev->ldev->dc.on_io_error;
                        was_io_error = test_and_clear_bit(WAS_IO_ERROR, &mdev->flags);
 
-                       /* Immediately allow completion of all application IO, that waits
-                          for completion from the local disk. */
-                       tl_abort_disk_io(mdev);
+                       if (was_io_error && eh == EP_CALL_HELPER)
+                               drbd_khelper(mdev, "local-io-error");
+
+                       /* Immediately allow completion of all application IO,
+                        * that waits for completion from the local disk,
+                        * if this was a force-detach due to disk_timeout
+                        * or administrator request (drbdsetup detach --force).
+                        * Do NOT abort otherwise.
+                        * Aborting local requests may cause serious problems,
+                        * if requests are completed to upper layers already,
+                        * and then later the already submitted local bio completes.
+                        * This can cause DMA into former bio pages that meanwhile
+                        * have been re-used for other things.
+                        * So aborting local requests may cause crashes,
+                        * or even worse, silent data corruption.
+                        */
+                       if (test_and_clear_bit(FORCE_DETACH, &mdev->flags))
+                               tl_abort_disk_io(mdev);
 
                        /* current state still has to be D_FAILED,
                         * there is only one way out: to D_DISKLESS,
@@ -1653,9 +1675,6 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
                        drbd_md_sync(mdev);
                }
                put_ldev(mdev);
-
-               if (was_io_error && eh == EP_CALL_HELPER)
-                       drbd_khelper(mdev, "local-io-error");
        }
 
         /* second half of local IO error, failure to attach,
@@ -1669,10 +1688,6 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
                                 "ASSERT FAILED: disk is %s while going diskless\n",
                                 drbd_disk_str(mdev->state.disk));
 
-                mdev->rs_total = 0;
-                mdev->rs_failed = 0;
-                atomic_set(&mdev->rs_pending_cnt, 0);
-
                if (ns.conn >= C_CONNECTED)
                        drbd_send_state(mdev, ns);
 
@@ -2194,7 +2209,8 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
 {
        struct p_sizes p;
        sector_t d_size, u_size;
-       int q_order_type, max_bio_size;
+       int q_order_type;
+       unsigned int max_bio_size;
        int ok;
 
        if (get_ldev_if_state(mdev, D_NEGOTIATING)) {
@@ -2203,7 +2219,7 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
                u_size = mdev->ldev->dc.disk_size;
                q_order_type = drbd_queue_order_type(mdev);
                max_bio_size = queue_max_hw_sectors(mdev->ldev->backing_bdev->bd_disk->queue) << 9;
-               max_bio_size = min_t(int, max_bio_size, DRBD_MAX_BIO_SIZE);
+               max_bio_size = min(max_bio_size, DRBD_MAX_BIO_SIZE);
                put_ldev(mdev);
        } else {
                d_size = 0;
@@ -2214,7 +2230,7 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
 
        /* Never allow old drbd (up to 8.3.7) to see more than 32KiB */
        if (mdev->agreed_pro_version <= 94)
-               max_bio_size = min_t(int, max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
+               max_bio_size = min(max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
 
        p.d_size = cpu_to_be64(d_size);
        p.u_size = cpu_to_be64(u_size);
@@ -3541,6 +3557,22 @@ static int drbd_congested(void *congested_data, int bdi_bits)
                goto out;
        }
 
+       if (test_bit(CALLBACK_PENDING, &mdev->flags)) {
+               r |= (1 << BDI_async_congested);
+               /* Without good local data, we would need to read from remote,
+                * and that would need the worker thread as well, which is
+                * currently blocked waiting for that usermode helper to
+                * finish.
+                */
+               if (!get_ldev_if_state(mdev, D_UP_TO_DATE))
+                       r |= (1 << BDI_sync_congested);
+               else
+                       put_ldev(mdev);
+               r &= bdi_bits;
+               reason = 'c';
+               goto out;
+       }
+
        if (get_ldev(mdev)) {
                q = bdev_get_queue(mdev->ldev->backing_bdev);
                r = bdi_congested(&q->backing_dev_info, bdi_bits);
@@ -3604,6 +3636,7 @@ struct drbd_conf *drbd_new_device(unsigned int minor)
        q->backing_dev_info.congested_data = mdev;
 
        blk_queue_make_request(q, drbd_make_request);
+       blk_queue_flush(q, REQ_FLUSH | REQ_FUA);
        /* Setting the max_hw_sectors to an odd value of 8kibyte here
           This triggers a max_bio_size message upon first attach or connect */
        blk_queue_max_hw_sectors(q, DRBD_MAX_BIO_SIZE_SAFE >> 8);
@@ -3870,7 +3903,7 @@ void drbd_md_sync(struct drbd_conf *mdev)
        if (!drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) {
                /* this was a try anyways ... */
                dev_err(DEV, "meta data update failed!\n");
-               drbd_chk_io_error(mdev, 1, true);
+               drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
        }
 
        /* Update mdev->ldev->md.la_size_sect,
@@ -3950,9 +3983,9 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
 
        spin_lock_irq(&mdev->req_lock);
        if (mdev->state.conn < C_CONNECTED) {
-               int peer;
+               unsigned int peer;
                peer = be32_to_cpu(buffer->la_peer_max_bio_size);
-               peer = max_t(int, peer, DRBD_MAX_BIO_SIZE_SAFE);
+               peer = max(peer, DRBD_MAX_BIO_SIZE_SAFE);
                mdev->peer_max_bio_size = peer;
        }
        spin_unlock_irq(&mdev->req_lock);
index 6d4de6a72e8069018b2b4d050b7ef2aa8b259259..fb9dce8daa2468c76992f4ab609adb471bc42af3 100644 (file)
@@ -147,6 +147,9 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd)
        char *argv[] = {usermode_helper, cmd, mb, NULL };
        int ret;
 
+       if (current == mdev->worker.task)
+               set_bit(CALLBACK_PENDING, &mdev->flags);
+
        snprintf(mb, 12, "minor-%d", mdev_to_minor(mdev));
 
        if (get_net_conf(mdev)) {
@@ -189,6 +192,9 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd)
                                usermode_helper, cmd, mb,
                                (ret >> 8) & 0xff, ret);
 
+       if (current == mdev->worker.task)
+               clear_bit(CALLBACK_PENDING, &mdev->flags);
+
        if (ret < 0) /* Ignore any ERRNOs we got. */
                ret = 0;
 
@@ -795,8 +801,8 @@ static int drbd_check_al_size(struct drbd_conf *mdev)
 static void drbd_setup_queue_param(struct drbd_conf *mdev, unsigned int max_bio_size)
 {
        struct request_queue * const q = mdev->rq_queue;
-       int max_hw_sectors = max_bio_size >> 9;
-       int max_segments = 0;
+       unsigned int max_hw_sectors = max_bio_size >> 9;
+       unsigned int max_segments = 0;
 
        if (get_ldev_if_state(mdev, D_ATTACHING)) {
                struct request_queue * const b = mdev->ldev->backing_bdev->bd_disk->queue;
@@ -829,7 +835,7 @@ static void drbd_setup_queue_param(struct drbd_conf *mdev, unsigned int max_bio_
 
 void drbd_reconsider_max_bio_size(struct drbd_conf *mdev)
 {
-       int now, new, local, peer;
+       unsigned int now, new, local, peer;
 
        now = queue_max_hw_sectors(mdev->rq_queue) << 9;
        local = mdev->local_max_bio_size; /* Eventually last known value, from volatile memory */
@@ -840,13 +846,14 @@ void drbd_reconsider_max_bio_size(struct drbd_conf *mdev)
                mdev->local_max_bio_size = local;
                put_ldev(mdev);
        }
+       local = min(local, DRBD_MAX_BIO_SIZE);
 
        /* We may ignore peer limits if the peer is modern enough.
           Because new from 8.3.8 onwards the peer can use multiple
           BIOs for a single peer_request */
        if (mdev->state.conn >= C_CONNECTED) {
                if (mdev->agreed_pro_version < 94) {
-                       peer = min_t(int, mdev->peer_max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
+                       peer = min(mdev->peer_max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
                        /* Correct old drbd (up to 8.3.7) if it believes it can do more than 32KiB */
                } else if (mdev->agreed_pro_version == 94)
                        peer = DRBD_MAX_SIZE_H80_PACKET;
@@ -854,10 +861,10 @@ void drbd_reconsider_max_bio_size(struct drbd_conf *mdev)
                        peer = DRBD_MAX_BIO_SIZE;
        }
 
-       new = min_t(int, local, peer);
+       new = min(local, peer);
 
        if (mdev->state.role == R_PRIMARY && new < now)
-               dev_err(DEV, "ASSERT FAILED new < now; (%d < %d)\n", new, now);
+               dev_err(DEV, "ASSERT FAILED new < now; (%u < %u)\n", new, now);
 
        if (new != now)
                dev_info(DEV, "max BIO size = %u\n", new);
@@ -950,6 +957,14 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
         * to realize a "hot spare" feature (not that I'd recommend that) */
        wait_event(mdev->misc_wait, !atomic_read(&mdev->local_cnt));
 
+       /* make sure there is no leftover from previous force-detach attempts */
+       clear_bit(FORCE_DETACH, &mdev->flags);
+
+       /* and no leftover from previously aborted resync or verify, either */
+       mdev->rs_total = 0;
+       mdev->rs_failed = 0;
+       atomic_set(&mdev->rs_pending_cnt, 0);
+
        /* allocation not in the IO path, cqueue thread context */
        nbc = kzalloc(sizeof(struct drbd_backing_dev), GFP_KERNEL);
        if (!nbc) {
@@ -1345,6 +1360,7 @@ static int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
        }
 
        if (dt.detach_force) {
+               set_bit(FORCE_DETACH, &mdev->flags);
                drbd_force_state(mdev, NS(disk, D_FAILED));
                reply->ret_code = SS_SUCCESS;
                goto out;
@@ -1962,9 +1978,11 @@ static int drbd_nl_invalidate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl
        int retcode;
 
        /* If there is still bitmap IO pending, probably because of a previous
-        * resync just being finished, wait for it before requesting a new resync. */
+        * resync just being finished, wait for it before requesting a new resync.
+        * Also wait for it's after_state_ch(). */
        drbd_suspend_io(mdev);
        wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
+       drbd_flush_workqueue(mdev);
 
        retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T), CS_ORDERED);
 
@@ -2003,9 +2021,11 @@ static int drbd_nl_invalidate_peer(struct drbd_conf *mdev, struct drbd_nl_cfg_re
        int retcode;
 
        /* If there is still bitmap IO pending, probably because of a previous
-        * resync just being finished, wait for it before requesting a new resync. */
+        * resync just being finished, wait for it before requesting a new resync.
+        * Also wait for it's after_state_ch(). */
        drbd_suspend_io(mdev);
        wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
+       drbd_flush_workqueue(mdev);
 
        retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S), CS_ORDERED);
 
index 869bada2ed06838a656d431584afde516a9cd115..5496104f90b9efe295704a271c95b724a8245145 100644 (file)
@@ -245,6 +245,9 @@ static int drbd_seq_show(struct seq_file *seq, void *v)
                    mdev->state.role == R_SECONDARY) {
                        seq_printf(seq, "%2d: cs:Unconfigured\n", i);
                } else {
+                       /* reset mdev->congestion_reason */
+                       bdi_rw_congested(&mdev->rq_queue->backing_dev_info);
+
                        seq_printf(seq,
                           "%2d: cs:%s ro:%s/%s ds:%s/%s %c %c%c%c%c%c%c\n"
                           "    ns:%u nr:%u dw:%u dr:%u al:%u bm:%u "
index ea4836e0ae9829e12206e482cc50b70678a3e4aa..c74ca2df7431f13553d366fd371d717b6ed1f56b 100644 (file)
@@ -277,6 +277,9 @@ static void drbd_pp_free(struct drbd_conf *mdev, struct page *page, int is_net)
        atomic_t *a = is_net ? &mdev->pp_in_use_by_net : &mdev->pp_in_use;
        int i;
 
+       if (page == NULL)
+               return;
+
        if (drbd_pp_vacant > (DRBD_MAX_BIO_SIZE/PAGE_SIZE)*minor_count)
                i = page_chain_free(page);
        else {
@@ -316,7 +319,7 @@ struct drbd_epoch_entry *drbd_alloc_ee(struct drbd_conf *mdev,
                                     gfp_t gfp_mask) __must_hold(local)
 {
        struct drbd_epoch_entry *e;
-       struct page *page;
+       struct page *page = NULL;
        unsigned nr_pages = (data_size + PAGE_SIZE -1) >> PAGE_SHIFT;
 
        if (drbd_insert_fault(mdev, DRBD_FAULT_AL_EE))
@@ -329,9 +332,11 @@ struct drbd_epoch_entry *drbd_alloc_ee(struct drbd_conf *mdev,
                return NULL;
        }
 
-       page = drbd_pp_alloc(mdev, nr_pages, (gfp_mask & __GFP_WAIT));
-       if (!page)
-               goto fail;
+       if (data_size) {
+               page = drbd_pp_alloc(mdev, nr_pages, (gfp_mask & __GFP_WAIT));
+               if (!page)
+                       goto fail;
+       }
 
        INIT_HLIST_NODE(&e->collision);
        e->epoch = NULL;
@@ -1270,7 +1275,6 @@ read_in_block(struct drbd_conf *mdev, u64 id, sector_t sector, int data_size) __
 
        data_size -= dgs;
 
-       ERR_IF(data_size == 0) return NULL;
        ERR_IF(data_size &  0x1ff) return NULL;
        ERR_IF(data_size >  DRBD_MAX_BIO_SIZE) return NULL;
 
@@ -1291,6 +1295,9 @@ read_in_block(struct drbd_conf *mdev, u64 id, sector_t sector, int data_size) __
        if (!e)
                return NULL;
 
+       if (!data_size)
+               return e;
+
        ds = data_size;
        page = e->pages;
        page_chain_for_each(page) {
@@ -1715,6 +1722,10 @@ static int receive_Data(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned
 
        dp_flags = be32_to_cpu(p->dp_flags);
        rw |= wire_flags_to_bio(mdev, dp_flags);
+       if (e->pages == NULL) {
+               D_ASSERT(e->size == 0);
+               D_ASSERT(dp_flags & DP_FLUSH);
+       }
 
        if (dp_flags & DP_MAY_SET_IN_SYNC)
                e->flags |= EE_MAY_SET_IN_SYNC;
@@ -3801,11 +3812,18 @@ void drbd_free_tl_hash(struct drbd_conf *mdev)
        mdev->ee_hash = NULL;
        mdev->ee_hash_s = 0;
 
-       /* paranoia code */
-       for (h = mdev->tl_hash; h < mdev->tl_hash + mdev->tl_hash_s; h++)
-               if (h->first)
-                       dev_err(DEV, "ASSERT FAILED tl_hash[%u] == %p, expected NULL\n",
-                               (int)(h - mdev->tl_hash), h->first);
+       /* We may not have had the chance to wait for all locally pending
+        * application requests. The hlist_add_fake() prevents access after
+        * free on master bio completion. */
+       for (h = mdev->tl_hash; h < mdev->tl_hash + mdev->tl_hash_s; h++) {
+               struct drbd_request *req;
+               struct hlist_node *pos, *n;
+               hlist_for_each_entry_safe(req, pos, n, h, collision) {
+                       hlist_del_init(&req->collision);
+                       hlist_add_fake(&req->collision);
+               }
+       }
+
        kfree(mdev->tl_hash);
        mdev->tl_hash = NULL;
        mdev->tl_hash_s = 0;
index 8e93a6ac9bb65e3497f53c92723a67429ffdf946..910335c30927f0429a4c4b0fddcfe74033c45e8d 100644 (file)
@@ -455,7 +455,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
                req->rq_state |= RQ_LOCAL_COMPLETED;
                req->rq_state &= ~RQ_LOCAL_PENDING;
 
-               __drbd_chk_io_error(mdev, false);
+               __drbd_chk_io_error(mdev, DRBD_IO_ERROR);
                _req_may_be_done_not_susp(req, m);
                break;
 
@@ -477,7 +477,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
                        break;
                }
 
-               __drbd_chk_io_error(mdev, false);
+               __drbd_chk_io_error(mdev, DRBD_IO_ERROR);
 
        goto_queue_for_net_read:
 
@@ -1111,13 +1111,12 @@ void drbd_make_request(struct request_queue *q, struct bio *bio)
        /*
         * what we "blindly" assume:
         */
-       D_ASSERT(bio->bi_size > 0);
        D_ASSERT((bio->bi_size & 0x1ff) == 0);
 
        /* to make some things easier, force alignment of requests within the
         * granularity of our hash tables */
        s_enr = bio->bi_sector >> HT_SHIFT;
-       e_enr = (bio->bi_sector+(bio->bi_size>>9)-1) >> HT_SHIFT;
+       e_enr = bio->bi_size ? (bio->bi_sector+(bio->bi_size>>9)-1) >> HT_SHIFT : s_enr;
 
        if (likely(s_enr == e_enr)) {
                do {
@@ -1275,7 +1274,7 @@ void request_timer_fn(unsigned long data)
                 time_after(now, req->start_time + dt) &&
                !time_in_range(now, mdev->last_reattach_jif, mdev->last_reattach_jif + dt)) {
                dev_warn(DEV, "Local backing device failed to meet the disk-timeout\n");
-               __drbd_chk_io_error(mdev, 1);
+               __drbd_chk_io_error(mdev, DRBD_FORCE_DETACH);
        }
        nt = (time_after(now, req->start_time + et) ? now : req->start_time) + et;
        spin_unlock_irq(&mdev->req_lock);
index 620c70ff223118e6f259a200512203f5010e2cda..6bce2cc179d4112980673b1b9d82ae91d70d390f 100644 (file)
@@ -111,7 +111,7 @@ void drbd_endio_read_sec_final(struct drbd_epoch_entry *e) __releases(local)
        if (list_empty(&mdev->read_ee))
                wake_up(&mdev->ee_wait);
        if (test_bit(__EE_WAS_ERROR, &e->flags))
-               __drbd_chk_io_error(mdev, false);
+               __drbd_chk_io_error(mdev, DRBD_IO_ERROR);
        spin_unlock_irqrestore(&mdev->req_lock, flags);
 
        drbd_queue_work(&mdev->data.work, &e->w);
@@ -154,7 +154,7 @@ static void drbd_endio_write_sec_final(struct drbd_epoch_entry *e) __releases(lo
                : list_empty(&mdev->active_ee);
 
        if (test_bit(__EE_WAS_ERROR, &e->flags))
-               __drbd_chk_io_error(mdev, false);
+               __drbd_chk_io_error(mdev, DRBD_IO_ERROR);
        spin_unlock_irqrestore(&mdev->req_lock, flags);
 
        if (is_syncer_req)
@@ -1501,14 +1501,6 @@ void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side)
                return;
        }
 
-       if (mdev->state.conn < C_AHEAD) {
-               /* In case a previous resync run was aborted by an IO error/detach on the peer. */
-               drbd_rs_cancel_all(mdev);
-               /* This should be done when we abort the resync. We definitely do not
-                  want to have this for connections going back and forth between
-                  Ahead/Behind and SyncSource/SyncTarget */
-       }
-
        if (side == C_SYNC_TARGET) {
                /* Since application IO was locked out during C_WF_BITMAP_T and
                   C_WF_SYNC_UUID we are still unmodified. Before going to C_SYNC_TARGET
index 553f43a90953cab40f421658ecb37fa9c20c8c54..a7d6347aaa7913b2a029014a95a2558d8360597e 100644 (file)
@@ -191,6 +191,7 @@ static int print_unex = 1;
 #include <linux/mutex.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
+#include <linux/async.h>
 
 /*
  * PS/2 floppies have much slower step rates than regular floppies.
@@ -2516,8 +2517,7 @@ static int make_raw_rw_request(void)
        set_fdc((long)current_req->rq_disk->private_data);
 
        raw_cmd = &default_raw_cmd;
-       raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
-           FD_RAW_NEED_SEEK;
+       raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK;
        raw_cmd->cmd_count = NR_RW;
        if (rq_data_dir(current_req) == READ) {
                raw_cmd->flags |= FD_RAW_READ;
@@ -4123,7 +4123,7 @@ static struct kobject *floppy_find(dev_t dev, int *part, void *data)
        return get_disk(disks[drive]);
 }
 
-static int __init floppy_init(void)
+static int __init do_floppy_init(void)
 {
        int i, unit, drive;
        int err, dr;
@@ -4338,6 +4338,24 @@ out_put_disk:
        return err;
 }
 
+#ifndef MODULE
+static __init void floppy_async_init(void *data, async_cookie_t cookie)
+{
+       do_floppy_init();
+}
+#endif
+
+static int __init floppy_init(void)
+{
+#ifdef MODULE
+       return do_floppy_init();
+#else
+       /* Don't hold up the bootup by the floppy initialization */
+       async_schedule(floppy_async_init, NULL);
+       return 0;
+#endif
+}
+
 static const struct io_region {
        int offset;
        int size;
index 061427a75d375a5ed0655bdac8422e668a252a5e..d07c9f7fded600d76192330ef37c35c2135c0fb5 100644 (file)
@@ -154,6 +154,7 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size,
        struct msghdr msg;
        struct kvec iov;
        sigset_t blocked, oldset;
+       unsigned long pflags = current->flags;
 
        if (unlikely(!sock)) {
                dev_err(disk_to_dev(nbd->disk),
@@ -167,8 +168,9 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size,
        siginitsetinv(&blocked, sigmask(SIGKILL));
        sigprocmask(SIG_SETMASK, &blocked, &oldset);
 
+       current->flags |= PF_MEMALLOC;
        do {
-               sock->sk->sk_allocation = GFP_NOIO;
+               sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC;
                iov.iov_base = buf;
                iov.iov_len = size;
                msg.msg_name = NULL;
@@ -214,6 +216,7 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size,
        } while (size > 0);
 
        sigprocmask(SIG_SETMASK, &oldset, NULL);
+       tsk_restore_flags(current, pflags, PF_MEMALLOC);
 
        return result;
 }
@@ -405,6 +408,7 @@ static int nbd_do_it(struct nbd_device *nbd)
 
        BUG_ON(nbd->magic != NBD_MAGIC);
 
+       sk_set_memalloc(nbd->sock->sk);
        nbd->pid = task_pid_nr(current);
        ret = device_create_file(disk_to_dev(nbd->disk), &pid_attr);
        if (ret) {
@@ -481,7 +485,7 @@ static void nbd_handle_req(struct nbd_device *nbd, struct request *req)
                nbd_end_request(req);
        } else {
                spin_lock(&nbd->queue_lock);
-               list_add(&req->queuelist, &nbd->queue_head);
+               list_add_tail(&req->queuelist, &nbd->queue_head);
                spin_unlock(&nbd->queue_lock);
        }
 
index 8f428a8ab003d8c7a029036eea6a1666a96d7dd3..9917943a3572ef577ac7a511ef375d11dae08350 100644 (file)
@@ -55,8 +55,6 @@
 
 #define RBD_MINORS_PER_MAJOR   256             /* max minors per blkdev */
 
-#define RBD_MAX_MD_NAME_LEN    (RBD_MAX_OBJ_NAME_LEN + sizeof(RBD_SUFFIX))
-#define RBD_MAX_POOL_NAME_LEN  64
 #define RBD_MAX_SNAP_NAME_LEN  32
 #define RBD_MAX_OPT_LEN                1024
 
  */
 struct rbd_image_header {
        u64 image_size;
-       char block_name[32];
+       char *object_prefix;
        __u8 obj_order;
        __u8 crypt_type;
        __u8 comp_type;
        struct ceph_snap_context *snapc;
        size_t snap_names_len;
-       u64 snap_seq;
        u32 total_snaps;
 
        char *snap_names;
@@ -150,7 +147,7 @@ struct rbd_snap {
  * a single device
  */
 struct rbd_device {
-       int                     id;             /* blkdev unique id */
+       int                     dev_id;         /* blkdev unique id */
 
        int                     major;          /* blkdev assigned major */
        struct gendisk          *disk;          /* blkdev's gendisk and rq */
@@ -163,20 +160,24 @@ struct rbd_device {
        spinlock_t              lock;           /* queue lock */
 
        struct rbd_image_header header;
-       char                    obj[RBD_MAX_OBJ_NAME_LEN]; /* rbd image name */
-       int                     obj_len;
-       char                    obj_md_name[RBD_MAX_MD_NAME_LEN]; /* hdr nm. */
-       char                    pool_name[RBD_MAX_POOL_NAME_LEN];
-       int                     poolid;
+       char                    *image_name;
+       size_t                  image_name_len;
+       char                    *header_name;
+       char                    *pool_name;
+       int                     pool_id;
 
        struct ceph_osd_event   *watch_event;
        struct ceph_osd_request *watch_request;
 
        /* protects updating the header */
        struct rw_semaphore     header_rwsem;
-       char                    snap_name[RBD_MAX_SNAP_NAME_LEN];
+       /* name of the snapshot this device reads from */
+       char                    *snap_name;
+       /* id of the snapshot this device reads from */
        u64                     snap_id;        /* current snapshot id */
-       int read_only;
+       /* whether the snap_id this device reads from still exists */
+       bool                    snap_exists;
+       int                     read_only;
 
        struct list_head        node;
 
@@ -201,8 +202,7 @@ static ssize_t rbd_snap_add(struct device *dev,
                            struct device_attribute *attr,
                            const char *buf,
                            size_t count);
-static void __rbd_remove_snap_dev(struct rbd_device *rbd_dev,
-                                 struct rbd_snap *snap);
+static void __rbd_remove_snap_dev(struct rbd_snap *snap);
 
 static ssize_t rbd_add(struct bus_type *bus, const char *buf,
                       size_t count);
@@ -240,7 +240,7 @@ static void rbd_put_dev(struct rbd_device *rbd_dev)
        put_device(&rbd_dev->dev);
 }
 
-static int __rbd_refresh_header(struct rbd_device *rbd_dev);
+static int rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver);
 
 static int rbd_open(struct block_device *bdev, fmode_t mode)
 {
@@ -273,9 +273,9 @@ static const struct block_device_operations rbd_bd_ops = {
 
 /*
  * Initialize an rbd client instance.
- * We own *opt.
+ * We own *ceph_opts.
  */
-static struct rbd_client *rbd_client_create(struct ceph_options *opt,
+static struct rbd_client *rbd_client_create(struct ceph_options *ceph_opts,
                                            struct rbd_options *rbd_opts)
 {
        struct rbd_client *rbdc;
@@ -291,10 +291,10 @@ static struct rbd_client *rbd_client_create(struct ceph_options *opt,
 
        mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
 
-       rbdc->client = ceph_create_client(opt, rbdc, 0, 0);
+       rbdc->client = ceph_create_client(ceph_opts, rbdc, 0, 0);
        if (IS_ERR(rbdc->client))
                goto out_mutex;
-       opt = NULL; /* Now rbdc->client is responsible for opt */
+       ceph_opts = NULL; /* Now rbdc->client is responsible for ceph_opts */
 
        ret = ceph_open_session(rbdc->client);
        if (ret < 0)
@@ -317,23 +317,23 @@ out_mutex:
        mutex_unlock(&ctl_mutex);
        kfree(rbdc);
 out_opt:
-       if (opt)
-               ceph_destroy_options(opt);
+       if (ceph_opts)
+               ceph_destroy_options(ceph_opts);
        return ERR_PTR(ret);
 }
 
 /*
  * Find a ceph client with specific addr and configuration.
  */
-static struct rbd_client *__rbd_client_find(struct ceph_options *opt)
+static struct rbd_client *__rbd_client_find(struct ceph_options *ceph_opts)
 {
        struct rbd_client *client_node;
 
-       if (opt->flags & CEPH_OPT_NOSHARE)
+       if (ceph_opts->flags & CEPH_OPT_NOSHARE)
                return NULL;
 
        list_for_each_entry(client_node, &rbd_client_list, node)
-               if (ceph_compare_options(opt, client_node->client) == 0)
+               if (!ceph_compare_options(ceph_opts, client_node->client))
                        return client_node;
        return NULL;
 }
@@ -349,7 +349,7 @@ enum {
        /* string args above */
 };
 
-static match_table_t rbdopt_tokens = {
+static match_table_t rbd_opts_tokens = {
        {Opt_notify_timeout, "notify_timeout=%d"},
        /* int args above */
        /* string args above */
@@ -358,11 +358,11 @@ static match_table_t rbdopt_tokens = {
 
 static int parse_rbd_opts_token(char *c, void *private)
 {
-       struct rbd_options *rbdopt = private;
+       struct rbd_options *rbd_opts = private;
        substring_t argstr[MAX_OPT_ARGS];
        int token, intval, ret;
 
-       token = match_token(c, rbdopt_tokens, argstr);
+       token = match_token(c, rbd_opts_tokens, argstr);
        if (token < 0)
                return -EINVAL;
 
@@ -383,7 +383,7 @@ static int parse_rbd_opts_token(char *c, void *private)
 
        switch (token) {
        case Opt_notify_timeout:
-               rbdopt->notify_timeout = intval;
+               rbd_opts->notify_timeout = intval;
                break;
        default:
                BUG_ON(token);
@@ -400,7 +400,7 @@ static struct rbd_client *rbd_get_client(const char *mon_addr,
                                         char *options)
 {
        struct rbd_client *rbdc;
-       struct ceph_options *opt;
+       struct ceph_options *ceph_opts;
        struct rbd_options *rbd_opts;
 
        rbd_opts = kzalloc(sizeof(*rbd_opts), GFP_KERNEL);
@@ -409,29 +409,29 @@ static struct rbd_client *rbd_get_client(const char *mon_addr,
 
        rbd_opts->notify_timeout = RBD_NOTIFY_TIMEOUT_DEFAULT;
 
-       opt = ceph_parse_options(options, mon_addr,
-                               mon_addr + mon_addr_len,
-                               parse_rbd_opts_token, rbd_opts);
-       if (IS_ERR(opt)) {
+       ceph_opts = ceph_parse_options(options, mon_addr,
+                                       mon_addr + mon_addr_len,
+                                       parse_rbd_opts_token, rbd_opts);
+       if (IS_ERR(ceph_opts)) {
                kfree(rbd_opts);
-               return ERR_CAST(opt);
+               return ERR_CAST(ceph_opts);
        }
 
        spin_lock(&rbd_client_list_lock);
-       rbdc = __rbd_client_find(opt);
+       rbdc = __rbd_client_find(ceph_opts);
        if (rbdc) {
                /* using an existing client */
                kref_get(&rbdc->kref);
                spin_unlock(&rbd_client_list_lock);
 
-               ceph_destroy_options(opt);
+               ceph_destroy_options(ceph_opts);
                kfree(rbd_opts);
 
                return rbdc;
        }
        spin_unlock(&rbd_client_list_lock);
 
-       rbdc = rbd_client_create(opt, rbd_opts);
+       rbdc = rbd_client_create(ceph_opts, rbd_opts);
 
        if (IS_ERR(rbdc))
                kfree(rbd_opts);
@@ -480,46 +480,60 @@ static void rbd_coll_release(struct kref *kref)
        kfree(coll);
 }
 
+static bool rbd_dev_ondisk_valid(struct rbd_image_header_ondisk *ondisk)
+{
+       return !memcmp(&ondisk->text,
+                       RBD_HEADER_TEXT, sizeof (RBD_HEADER_TEXT));
+}
+
 /*
  * Create a new header structure, translate header format from the on-disk
  * header.
  */
 static int rbd_header_from_disk(struct rbd_image_header *header,
                                 struct rbd_image_header_ondisk *ondisk,
-                                u32 allocated_snaps,
-                                gfp_t gfp_flags)
+                                u32 allocated_snaps)
 {
-       u32 i, snap_count;
+       u32 snap_count;
 
-       if (memcmp(ondisk, RBD_HEADER_TEXT, sizeof(RBD_HEADER_TEXT)))
+       if (!rbd_dev_ondisk_valid(ondisk))
                return -ENXIO;
 
        snap_count = le32_to_cpu(ondisk->snap_count);
-       if (snap_count > (UINT_MAX - sizeof(struct ceph_snap_context))
-                        / sizeof (*ondisk))
+       if (snap_count > (SIZE_MAX - sizeof(struct ceph_snap_context))
+                                / sizeof (u64))
                return -EINVAL;
        header->snapc = kmalloc(sizeof(struct ceph_snap_context) +
                                snap_count * sizeof(u64),
-                               gfp_flags);
+                               GFP_KERNEL);
        if (!header->snapc)
                return -ENOMEM;
 
-       header->snap_names_len = le64_to_cpu(ondisk->snap_names_len);
        if (snap_count) {
+               header->snap_names_len = le64_to_cpu(ondisk->snap_names_len);
                header->snap_names = kmalloc(header->snap_names_len,
-                                            gfp_flags);
+                                            GFP_KERNEL);
                if (!header->snap_names)
                        goto err_snapc;
                header->snap_sizes = kmalloc(snap_count * sizeof(u64),
-                                            gfp_flags);
+                                            GFP_KERNEL);
                if (!header->snap_sizes)
                        goto err_names;
        } else {
+               WARN_ON(ondisk->snap_names_len);
+               header->snap_names_len = 0;
                header->snap_names = NULL;
                header->snap_sizes = NULL;
        }
-       memcpy(header->block_name, ondisk->block_name,
+
+       header->object_prefix = kmalloc(sizeof (ondisk->block_name) + 1,
+                                       GFP_KERNEL);
+       if (!header->object_prefix)
+               goto err_sizes;
+
+       memcpy(header->object_prefix, ondisk->block_name,
               sizeof(ondisk->block_name));
+       header->object_prefix[sizeof (ondisk->block_name)] = '\0';
 
        header->image_size = le64_to_cpu(ondisk->image_size);
        header->obj_order = ondisk->options.order;
@@ -527,11 +541,13 @@ static int rbd_header_from_disk(struct rbd_image_header *header,
        header->comp_type = ondisk->options.comp_type;
 
        atomic_set(&header->snapc->nref, 1);
-       header->snap_seq = le64_to_cpu(ondisk->snap_seq);
+       header->snapc->seq = le64_to_cpu(ondisk->snap_seq);
        header->snapc->num_snaps = snap_count;
        header->total_snaps = snap_count;
 
        if (snap_count && allocated_snaps == snap_count) {
+               int i;
+
                for (i = 0; i < snap_count; i++) {
                        header->snapc->snaps[i] =
                                le64_to_cpu(ondisk->snaps[i].id);
@@ -540,16 +556,22 @@ static int rbd_header_from_disk(struct rbd_image_header *header,
                }
 
                /* copy snapshot names */
-               memcpy(header->snap_names, &ondisk->snaps[i],
+               memcpy(header->snap_names, &ondisk->snaps[snap_count],
                        header->snap_names_len);
        }
 
        return 0;
 
+err_sizes:
+       kfree(header->snap_sizes);
+       header->snap_sizes = NULL;
 err_names:
        kfree(header->snap_names);
+       header->snap_names = NULL;
 err_snapc:
        kfree(header->snapc);
+       header->snapc = NULL;
+
        return -ENOMEM;
 }
 
@@ -575,52 +597,50 @@ static int snap_by_name(struct rbd_image_header *header, const char *snap_name,
        return -ENOENT;
 }
 
-static int rbd_header_set_snap(struct rbd_device *dev, u64 *size)
+static int rbd_header_set_snap(struct rbd_device *rbd_dev, u64 *size)
 {
-       struct rbd_image_header *header = &dev->header;
-       struct ceph_snap_context *snapc = header->snapc;
-       int ret = -ENOENT;
-
-       BUILD_BUG_ON(sizeof (dev->snap_name) < sizeof (RBD_SNAP_HEAD_NAME));
+       int ret;
 
-       down_write(&dev->header_rwsem);
+       down_write(&rbd_dev->header_rwsem);
 
-       if (!memcmp(dev->snap_name, RBD_SNAP_HEAD_NAME,
+       if (!memcmp(rbd_dev->snap_name, RBD_SNAP_HEAD_NAME,
                    sizeof (RBD_SNAP_HEAD_NAME))) {
-               if (header->total_snaps)
-                       snapc->seq = header->snap_seq;
-               else
-                       snapc->seq = 0;
-               dev->snap_id = CEPH_NOSNAP;
-               dev->read_only = 0;
+               rbd_dev->snap_id = CEPH_NOSNAP;
+               rbd_dev->snap_exists = false;
+               rbd_dev->read_only = 0;
                if (size)
-                       *size = header->image_size;
+                       *size = rbd_dev->header.image_size;
        } else {
-               ret = snap_by_name(header, dev->snap_name, &snapc->seq, size);
+               u64 snap_id = 0;
+
+               ret = snap_by_name(&rbd_dev->header, rbd_dev->snap_name,
+                                       &snap_id, size);
                if (ret < 0)
                        goto done;
-               dev->snap_id = snapc->seq;
-               dev->read_only = 1;
+               rbd_dev->snap_id = snap_id;
+               rbd_dev->snap_exists = true;
+               rbd_dev->read_only = 1;
        }
 
        ret = 0;
 done:
-       up_write(&dev->header_rwsem);
+       up_write(&rbd_dev->header_rwsem);
        return ret;
 }
 
 static void rbd_header_free(struct rbd_image_header *header)
 {
-       kfree(header->snapc);
-       kfree(header->snap_names);
+       kfree(header->object_prefix);
        kfree(header->snap_sizes);
+       kfree(header->snap_names);
+       ceph_put_snap_context(header->snapc);
 }
 
 /*
  * get the actual striped segment name, offset and length
  */
 static u64 rbd_get_segment(struct rbd_image_header *header,
-                          const char *block_name,
+                          const char *object_prefix,
                           u64 ofs, u64 len,
                           char *seg_name, u64 *segofs)
 {
@@ -628,7 +648,7 @@ static u64 rbd_get_segment(struct rbd_image_header *header,
 
        if (seg_name)
                snprintf(seg_name, RBD_MAX_SEG_NAME_LEN,
-                        "%s.%012llx", block_name, seg);
+                        "%s.%012llx", object_prefix, seg);
 
        ofs = ofs & ((1 << header->obj_order) - 1);
        len = min_t(u64, len, (1 << header->obj_order) - ofs);
@@ -726,9 +746,8 @@ static struct bio *bio_chain_clone(struct bio **old, struct bio **next,
                         * split_bio will BUG_ON if this is not the case
                         */
                        dout("bio_chain_clone split! total=%d remaining=%d"
-                            "bi_size=%d\n",
-                            (int)total, (int)len-total,
-                            (int)old_chain->bi_size);
+                            "bi_size=%u\n",
+                            total, len - total, old_chain->bi_size);
 
                        /* split the bio. We'll release it either in the next
                           call, or it will have to be released outside */
@@ -777,22 +796,24 @@ err_out:
 /*
  * helpers for osd request op vectors.
  */
-static int rbd_create_rw_ops(struct ceph_osd_req_op **ops,
-                           int num_ops,
-                           int opcode,
-                           u32 payload_len)
-{
-       *ops = kzalloc(sizeof(struct ceph_osd_req_op) * (num_ops + 1),
-                      GFP_NOIO);
-       if (!*ops)
-               return -ENOMEM;
-       (*ops)[0].op = opcode;
+static struct ceph_osd_req_op *rbd_create_rw_ops(int num_ops,
+                                       int opcode, u32 payload_len)
+{
+       struct ceph_osd_req_op *ops;
+
+       ops = kzalloc(sizeof (*ops) * (num_ops + 1), GFP_NOIO);
+       if (!ops)
+               return NULL;
+
+       ops[0].op = opcode;
+
        /*
         * op extent offset and length will be set later on
         * in calc_raw_layout()
         */
-       (*ops)[0].payload_len = payload_len;
-       return 0;
+       ops[0].payload_len = payload_len;
+
+       return ops;
 }
 
 static void rbd_destroy_ops(struct ceph_osd_req_op *ops)
@@ -808,8 +829,8 @@ static void rbd_coll_end_req_index(struct request *rq,
        struct request_queue *q;
        int min, max, i;
 
-       dout("rbd_coll_end_req_index %p index %d ret %d len %lld\n",
-            coll, index, ret, len);
+       dout("rbd_coll_end_req_index %p index %d ret %d len %llu\n",
+            coll, index, ret, (unsigned long long) len);
 
        if (!rq)
                return;
@@ -848,16 +869,15 @@ static void rbd_coll_end_req(struct rbd_request *req,
  * Send ceph osd request
  */
 static int rbd_do_request(struct request *rq,
-                         struct rbd_device *dev,
+                         struct rbd_device *rbd_dev,
                          struct ceph_snap_context *snapc,
                          u64 snapid,
-                         const char *obj, u64 ofs, u64 len,
+                         const char *object_name, u64 ofs, u64 len,
                          struct bio *bio,
                          struct page **pages,
                          int num_pages,
                          int flags,
                          struct ceph_osd_req_op *ops,
-                         int num_reply,
                          struct rbd_req_coll *coll,
                          int coll_index,
                          void (*rbd_cb)(struct ceph_osd_request *req,
@@ -887,15 +907,13 @@ static int rbd_do_request(struct request *rq,
                req_data->coll_index = coll_index;
        }
 
-       dout("rbd_do_request obj=%s ofs=%lld len=%lld\n", obj, len, ofs);
-
-       down_read(&dev->header_rwsem);
+       dout("rbd_do_request object_name=%s ofs=%llu len=%llu\n", object_name,
+               (unsigned long long) ofs, (unsigned long long) len);
 
-       osdc = &dev->rbd_client->client->osdc;
+       osdc = &rbd_dev->rbd_client->client->osdc;
        req = ceph_osdc_alloc_request(osdc, flags, snapc, ops,
                                        false, GFP_NOIO, pages, bio);
        if (!req) {
-               up_read(&dev->header_rwsem);
                ret = -ENOMEM;
                goto done_pages;
        }
@@ -912,7 +930,7 @@ static int rbd_do_request(struct request *rq,
        reqhead = req->r_request->front.iov_base;
        reqhead->snapid = cpu_to_le64(CEPH_NOSNAP);
 
-       strncpy(req->r_oid, obj, sizeof(req->r_oid));
+       strncpy(req->r_oid, object_name, sizeof(req->r_oid));
        req->r_oid_len = strlen(req->r_oid);
 
        layout = &req->r_file_layout;
@@ -920,7 +938,7 @@ static int rbd_do_request(struct request *rq,
        layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER);
        layout->fl_stripe_count = cpu_to_le32(1);
        layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER);
-       layout->fl_pg_pool = cpu_to_le32(dev->poolid);
+       layout->fl_pg_pool = cpu_to_le32(rbd_dev->pool_id);
        ceph_calc_raw_layout(osdc, layout, snapid, ofs, &len, &bno,
                                req, ops);
 
@@ -929,7 +947,6 @@ static int rbd_do_request(struct request *rq,
                                snapc,
                                &mtime,
                                req->r_oid, req->r_oid_len);
-       up_read(&dev->header_rwsem);
 
        if (linger_req) {
                ceph_osdc_set_request_linger(osdc, req);
@@ -944,8 +961,9 @@ static int rbd_do_request(struct request *rq,
                ret = ceph_osdc_wait_request(osdc, req);
                if (ver)
                        *ver = le64_to_cpu(req->r_reassert_version.version);
-               dout("reassert_ver=%lld\n",
-                    le64_to_cpu(req->r_reassert_version.version));
+               dout("reassert_ver=%llu\n",
+                       (unsigned long long)
+                               le64_to_cpu(req->r_reassert_version.version));
                ceph_osdc_put_request(req);
        }
        return ret;
@@ -979,7 +997,8 @@ static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg)
        bytes = le64_to_cpu(op->extent.length);
        read_op = (le16_to_cpu(op->op) == CEPH_OSD_OP_READ);
 
-       dout("rbd_req_cb bytes=%lld readop=%d rc=%d\n", bytes, read_op, rc);
+       dout("rbd_req_cb bytes=%llu readop=%d rc=%d\n",
+               (unsigned long long) bytes, read_op, (int) rc);
 
        if (rc == -ENOENT && read_op) {
                zero_bio_chain(req_data->bio, 0);
@@ -1006,14 +1025,12 @@ static void rbd_simple_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg
 /*
  * Do a synchronous ceph osd operation
  */
-static int rbd_req_sync_op(struct rbd_device *dev,
+static int rbd_req_sync_op(struct rbd_device *rbd_dev,
                           struct ceph_snap_context *snapc,
                           u64 snapid,
-                          int opcode,
                           int flags,
-                          struct ceph_osd_req_op *orig_ops,
-                          int num_reply,
-                          const char *obj,
+                          struct ceph_osd_req_op *ops,
+                          const char *object_name,
                           u64 ofs, u64 len,
                           char *buf,
                           struct ceph_osd_request **linger_req,
@@ -1022,45 +1039,28 @@ static int rbd_req_sync_op(struct rbd_device *dev,
        int ret;
        struct page **pages;
        int num_pages;
-       struct ceph_osd_req_op *ops = orig_ops;
-       u32 payload_len;
+
+       BUG_ON(ops == NULL);
 
        num_pages = calc_pages_for(ofs , len);
        pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL);
        if (IS_ERR(pages))
                return PTR_ERR(pages);
 
-       if (!orig_ops) {
-               payload_len = (flags & CEPH_OSD_FLAG_WRITE ? len : 0);
-               ret = rbd_create_rw_ops(&ops, 1, opcode, payload_len);
-               if (ret < 0)
-                       goto done;
-
-               if ((flags & CEPH_OSD_FLAG_WRITE) && buf) {
-                       ret = ceph_copy_to_page_vector(pages, buf, ofs, len);
-                       if (ret < 0)
-                               goto done_ops;
-               }
-       }
-
-       ret = rbd_do_request(NULL, dev, snapc, snapid,
-                         obj, ofs, len, NULL,
+       ret = rbd_do_request(NULL, rbd_dev, snapc, snapid,
+                         object_name, ofs, len, NULL,
                          pages, num_pages,
                          flags,
                          ops,
-                         2,
                          NULL, 0,
                          NULL,
                          linger_req, ver);
        if (ret < 0)
-               goto done_ops;
+               goto done;
 
        if ((flags & CEPH_OSD_FLAG_READ) && buf)
                ret = ceph_copy_from_page_vector(pages, buf, ofs, ret);
 
-done_ops:
-       if (!orig_ops)
-               rbd_destroy_ops(ops);
 done:
        ceph_release_page_vector(pages, num_pages);
        return ret;
@@ -1070,10 +1070,10 @@ done:
  * Do an asynchronous ceph osd operation
  */
 static int rbd_do_op(struct request *rq,
-                    struct rbd_device *rbd_dev ,
+                    struct rbd_device *rbd_dev,
                     struct ceph_snap_context *snapc,
                     u64 snapid,
-                    int opcode, int flags, int num_reply,
+                    int opcode, int flags,
                     u64 ofs, u64 len,
                     struct bio *bio,
                     struct rbd_req_coll *coll,
@@ -1091,14 +1091,15 @@ static int rbd_do_op(struct request *rq,
                return -ENOMEM;
 
        seg_len = rbd_get_segment(&rbd_dev->header,
-                                 rbd_dev->header.block_name,
+                                 rbd_dev->header.object_prefix,
                                  ofs, len,
                                  seg_name, &seg_ofs);
 
        payload_len = (flags & CEPH_OSD_FLAG_WRITE ? seg_len : 0);
 
-       ret = rbd_create_rw_ops(&ops, 1, opcode, payload_len);
-       if (ret < 0)
+       ret = -ENOMEM;
+       ops = rbd_create_rw_ops(1, opcode, payload_len);
+       if (!ops)
                goto done;
 
        /* we've taken care of segment sizes earlier when we
@@ -1112,7 +1113,6 @@ static int rbd_do_op(struct request *rq,
                             NULL, 0,
                             flags,
                             ops,
-                            num_reply,
                             coll, coll_index,
                             rbd_req_cb, 0, NULL);
 
@@ -1136,7 +1136,6 @@ static int rbd_req_write(struct request *rq,
        return rbd_do_op(rq, rbd_dev, snapc, CEPH_NOSNAP,
                         CEPH_OSD_OP_WRITE,
                         CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
-                        2,
                         ofs, len, bio, coll, coll_index);
 }
 
@@ -1155,55 +1154,58 @@ static int rbd_req_read(struct request *rq,
                         snapid,
                         CEPH_OSD_OP_READ,
                         CEPH_OSD_FLAG_READ,
-                        2,
                         ofs, len, bio, coll, coll_index);
 }
 
 /*
  * Request sync osd read
  */
-static int rbd_req_sync_read(struct rbd_device *dev,
-                         struct ceph_snap_context *snapc,
+static int rbd_req_sync_read(struct rbd_device *rbd_dev,
                          u64 snapid,
-                         const char *obj,
+                         const char *object_name,
                          u64 ofs, u64 len,
                          char *buf,
                          u64 *ver)
 {
-       return rbd_req_sync_op(dev, NULL,
+       struct ceph_osd_req_op *ops;
+       int ret;
+
+       ops = rbd_create_rw_ops(1, CEPH_OSD_OP_READ, 0);
+       if (!ops)
+               return -ENOMEM;
+
+       ret = rbd_req_sync_op(rbd_dev, NULL,
                               snapid,
-                              CEPH_OSD_OP_READ,
                               CEPH_OSD_FLAG_READ,
-                              NULL,
-                              1, obj, ofs, len, buf, NULL, ver);
+                              ops, object_name, ofs, len, buf, NULL, ver);
+       rbd_destroy_ops(ops);
+
+       return ret;
 }
 
 /*
  * Request sync osd watch
  */
-static int rbd_req_sync_notify_ack(struct rbd_device *dev,
+static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev,
                                   u64 ver,
-                                  u64 notify_id,
-                                  const char *obj)
+                                  u64 notify_id)
 {
        struct ceph_osd_req_op *ops;
-       struct page **pages = NULL;
        int ret;
 
-       ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_NOTIFY_ACK, 0);
-       if (ret < 0)
-               return ret;
+       ops = rbd_create_rw_ops(1, CEPH_OSD_OP_NOTIFY_ACK, 0);
+       if (!ops)
+               return -ENOMEM;
 
-       ops[0].watch.ver = cpu_to_le64(dev->header.obj_version);
+       ops[0].watch.ver = cpu_to_le64(ver);
        ops[0].watch.cookie = notify_id;
        ops[0].watch.flag = 0;
 
-       ret = rbd_do_request(NULL, dev, NULL, CEPH_NOSNAP,
-                         obj, 0, 0, NULL,
-                         pages, 0,
+       ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP,
+                         rbd_dev->header_name, 0, 0, NULL,
+                         NULL, 0,
                          CEPH_OSD_FLAG_READ,
                          ops,
-                         1,
                          NULL, 0,
                          rbd_simple_req_cb, 0, NULL);
 
@@ -1213,54 +1215,53 @@ static int rbd_req_sync_notify_ack(struct rbd_device *dev,
 
 static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
 {
-       struct rbd_device *dev = (struct rbd_device *)data;
+       struct rbd_device *rbd_dev = (struct rbd_device *)data;
+       u64 hver;
        int rc;
 
-       if (!dev)
+       if (!rbd_dev)
                return;
 
-       dout("rbd_watch_cb %s notify_id=%lld opcode=%d\n", dev->obj_md_name,
-               notify_id, (int)opcode);
-       mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
-       rc = __rbd_refresh_header(dev);
-       mutex_unlock(&ctl_mutex);
+       dout("rbd_watch_cb %s notify_id=%llu opcode=%u\n",
+               rbd_dev->header_name, (unsigned long long) notify_id,
+               (unsigned int) opcode);
+       rc = rbd_refresh_header(rbd_dev, &hver);
        if (rc)
                pr_warning(RBD_DRV_NAME "%d got notification but failed to "
-                          " update snaps: %d\n", dev->major, rc);
+                          " update snaps: %d\n", rbd_dev->major, rc);
 
-       rbd_req_sync_notify_ack(dev, ver, notify_id, dev->obj_md_name);
+       rbd_req_sync_notify_ack(rbd_dev, hver, notify_id);
 }
 
 /*
  * Request sync osd watch
  */
-static int rbd_req_sync_watch(struct rbd_device *dev,
-                             const char *obj,
-                             u64 ver)
+static int rbd_req_sync_watch(struct rbd_device *rbd_dev)
 {
        struct ceph_osd_req_op *ops;
-       struct ceph_osd_client *osdc = &dev->rbd_client->client->osdc;
+       struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
+       int ret;
 
-       int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_WATCH, 0);
-       if (ret < 0)
-               return ret;
+       ops = rbd_create_rw_ops(1, CEPH_OSD_OP_WATCH, 0);
+       if (!ops)
+               return -ENOMEM;
 
        ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0,
-                                    (void *)dev, &dev->watch_event);
+                                    (void *)rbd_dev, &rbd_dev->watch_event);
        if (ret < 0)
                goto fail;
 
-       ops[0].watch.ver = cpu_to_le64(ver);
-       ops[0].watch.cookie = cpu_to_le64(dev->watch_event->cookie);
+       ops[0].watch.ver = cpu_to_le64(rbd_dev->header.obj_version);
+       ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie);
        ops[0].watch.flag = 1;
 
-       ret = rbd_req_sync_op(dev, NULL,
+       ret = rbd_req_sync_op(rbd_dev, NULL,
                              CEPH_NOSNAP,
-                             0,
                              CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
                              ops,
-                             1, obj, 0, 0, NULL,
-                             &dev->watch_request, NULL);
+                             rbd_dev->header_name,
+                             0, 0, NULL,
+                             &rbd_dev->watch_request, NULL);
 
        if (ret < 0)
                goto fail_event;
@@ -1269,8 +1270,8 @@ static int rbd_req_sync_watch(struct rbd_device *dev,
        return 0;
 
 fail_event:
-       ceph_osdc_cancel_event(dev->watch_event);
-       dev->watch_event = NULL;
+       ceph_osdc_cancel_event(rbd_dev->watch_event);
+       rbd_dev->watch_event = NULL;
 fail:
        rbd_destroy_ops(ops);
        return ret;
@@ -1279,64 +1280,65 @@ fail:
 /*
  * Request sync osd unwatch
  */
-static int rbd_req_sync_unwatch(struct rbd_device *dev,
-                               const char *obj)
+static int rbd_req_sync_unwatch(struct rbd_device *rbd_dev)
 {
        struct ceph_osd_req_op *ops;
+       int ret;
 
-       int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_WATCH, 0);
-       if (ret < 0)
-               return ret;
+       ops = rbd_create_rw_ops(1, CEPH_OSD_OP_WATCH, 0);
+       if (!ops)
+               return -ENOMEM;
 
        ops[0].watch.ver = 0;
-       ops[0].watch.cookie = cpu_to_le64(dev->watch_event->cookie);
+       ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie);
        ops[0].watch.flag = 0;
 
-       ret = rbd_req_sync_op(dev, NULL,
+       ret = rbd_req_sync_op(rbd_dev, NULL,
                              CEPH_NOSNAP,
-                             0,
                              CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
                              ops,
-                             1, obj, 0, 0, NULL, NULL, NULL);
+                             rbd_dev->header_name,
+                             0, 0, NULL, NULL, NULL);
+
 
        rbd_destroy_ops(ops);
-       ceph_osdc_cancel_event(dev->watch_event);
-       dev->watch_event = NULL;
+       ceph_osdc_cancel_event(rbd_dev->watch_event);
+       rbd_dev->watch_event = NULL;
        return ret;
 }
 
 struct rbd_notify_info {
-       struct rbd_device *dev;
+       struct rbd_device *rbd_dev;
 };
 
 static void rbd_notify_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
 {
-       struct rbd_device *dev = (struct rbd_device *)data;
-       if (!dev)
+       struct rbd_device *rbd_dev = (struct rbd_device *)data;
+       if (!rbd_dev)
                return;
 
-       dout("rbd_notify_cb %s notify_id=%lld opcode=%d\n", dev->obj_md_name,
-               notify_id, (int)opcode);
+       dout("rbd_notify_cb %s notify_id=%llu opcode=%u\n",
+                       rbd_dev->header_name, (unsigned long long) notify_id,
+                       (unsigned int) opcode);
 }
 
 /*
  * Request sync osd notify
  */
-static int rbd_req_sync_notify(struct rbd_device *dev,
-                         const char *obj)
+static int rbd_req_sync_notify(struct rbd_device *rbd_dev)
 {
        struct ceph_osd_req_op *ops;
-       struct ceph_osd_client *osdc = &dev->rbd_client->client->osdc;
+       struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
        struct ceph_osd_event *event;
        struct rbd_notify_info info;
        int payload_len = sizeof(u32) + sizeof(u32);
        int ret;
 
-       ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_NOTIFY, payload_len);
-       if (ret < 0)
-               return ret;
+       ops = rbd_create_rw_ops(1, CEPH_OSD_OP_NOTIFY, payload_len);
+       if (!ops)
+               return -ENOMEM;
 
-       info.dev = dev;
+       info.rbd_dev = rbd_dev;
 
        ret = ceph_osdc_create_event(osdc, rbd_notify_cb, 1,
                                     (void *)&info, &event);
@@ -1349,12 +1351,12 @@ static int rbd_req_sync_notify(struct rbd_device *dev,
        ops[0].watch.prot_ver = RADOS_NOTIFY_VER;
        ops[0].watch.timeout = 12;
 
-       ret = rbd_req_sync_op(dev, NULL,
+       ret = rbd_req_sync_op(rbd_dev, NULL,
                               CEPH_NOSNAP,
-                              0,
                               CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
                               ops,
-                              1, obj, 0, 0, NULL, NULL, NULL);
+                              rbd_dev->header_name,
+                              0, 0, NULL, NULL, NULL);
        if (ret < 0)
                goto fail_event;
 
@@ -1373,36 +1375,37 @@ fail:
 /*
  * Request sync osd read
  */
-static int rbd_req_sync_exec(struct rbd_device *dev,
-                            const char *obj,
-                            const char *cls,
-                            const char *method,
+static int rbd_req_sync_exec(struct rbd_device *rbd_dev,
+                            const char *object_name,
+                            const char *class_name,
+                            const char *method_name,
                             const char *data,
                             int len,
                             u64 *ver)
 {
        struct ceph_osd_req_op *ops;
-       int cls_len = strlen(cls);
-       int method_len = strlen(method);
-       int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_CALL,
-                                   cls_len + method_len + len);
-       if (ret < 0)
-               return ret;
+       int class_name_len = strlen(class_name);
+       int method_name_len = strlen(method_name);
+       int ret;
 
-       ops[0].cls.class_name = cls;
-       ops[0].cls.class_len = (__u8)cls_len;
-       ops[0].cls.method_name = method;
-       ops[0].cls.method_len = (__u8)method_len;
+       ops = rbd_create_rw_ops(1, CEPH_OSD_OP_CALL,
+                                   class_name_len + method_name_len + len);
+       if (!ops)
+               return -ENOMEM;
+
+       ops[0].cls.class_name = class_name;
+       ops[0].cls.class_len = (__u8) class_name_len;
+       ops[0].cls.method_name = method_name;
+       ops[0].cls.method_len = (__u8) method_name_len;
        ops[0].cls.argc = 0;
        ops[0].cls.indata = data;
        ops[0].cls.indata_len = len;
 
-       ret = rbd_req_sync_op(dev, NULL,
+       ret = rbd_req_sync_op(rbd_dev, NULL,
                               CEPH_NOSNAP,
-                              0,
                               CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
                               ops,
-                              1, obj, 0, 0, NULL, NULL, ver);
+                              object_name, 0, 0, NULL, NULL, ver);
 
        rbd_destroy_ops(ops);
 
@@ -1437,10 +1440,12 @@ static void rbd_rq_fn(struct request_queue *q)
                struct bio *bio;
                struct bio *rq_bio, *next_bio = NULL;
                bool do_write;
-               int size, op_size = 0;
+               unsigned int size;
+               u64 op_size = 0;
                u64 ofs;
                int num_segs, cur_seg = 0;
                struct rbd_req_coll *coll;
+               struct ceph_snap_context *snapc;
 
                /* peek at request from block layer */
                if (!rq)
@@ -1467,23 +1472,38 @@ static void rbd_rq_fn(struct request_queue *q)
 
                spin_unlock_irq(q->queue_lock);
 
+               down_read(&rbd_dev->header_rwsem);
+
+               if (rbd_dev->snap_id != CEPH_NOSNAP && !rbd_dev->snap_exists) {
+                       up_read(&rbd_dev->header_rwsem);
+                       dout("request for non-existent snapshot");
+                       spin_lock_irq(q->queue_lock);
+                       __blk_end_request_all(rq, -ENXIO);
+                       continue;
+               }
+
+               snapc = ceph_get_snap_context(rbd_dev->header.snapc);
+
+               up_read(&rbd_dev->header_rwsem);
+
                dout("%s 0x%x bytes at 0x%llx\n",
                     do_write ? "write" : "read",
-                    size, blk_rq_pos(rq) * SECTOR_SIZE);
+                    size, (unsigned long long) blk_rq_pos(rq) * SECTOR_SIZE);
 
                num_segs = rbd_get_num_segments(&rbd_dev->header, ofs, size);
                coll = rbd_alloc_coll(num_segs);
                if (!coll) {
                        spin_lock_irq(q->queue_lock);
                        __blk_end_request_all(rq, -ENOMEM);
+                       ceph_put_snap_context(snapc);
                        continue;
                }
 
                do {
                        /* a bio clone to be passed down to OSD req */
-                       dout("rq->bio->bi_vcnt=%d\n", rq->bio->bi_vcnt);
+                       dout("rq->bio->bi_vcnt=%hu\n", rq->bio->bi_vcnt);
                        op_size = rbd_get_segment(&rbd_dev->header,
-                                                 rbd_dev->header.block_name,
+                                                 rbd_dev->header.object_prefix,
                                                  ofs, size,
                                                  NULL, NULL);
                        kref_get(&coll->kref);
@@ -1499,7 +1519,7 @@ static void rbd_rq_fn(struct request_queue *q)
                        /* init OSD command: write or read */
                        if (do_write)
                                rbd_req_write(rq, rbd_dev,
-                                             rbd_dev->header.snapc,
+                                             snapc,
                                              ofs,
                                              op_size, bio,
                                              coll, cur_seg);
@@ -1522,6 +1542,8 @@ next_seg:
                if (bp)
                        bio_pair_release(bp);
                spin_lock_irq(q->queue_lock);
+
+               ceph_put_snap_context(snapc);
        }
 }
 
@@ -1592,18 +1614,19 @@ static int rbd_read_header(struct rbd_device *rbd_dev,
                        return -ENOMEM;
 
                rc = rbd_req_sync_read(rbd_dev,
-                                      NULL, CEPH_NOSNAP,
-                                      rbd_dev->obj_md_name,
+                                      CEPH_NOSNAP,
+                                      rbd_dev->header_name,
                                       0, len,
                                       (char *)dh, &ver);
                if (rc < 0)
                        goto out_dh;
 
-               rc = rbd_header_from_disk(header, dh, snap_count, GFP_KERNEL);
+               rc = rbd_header_from_disk(header, dh, snap_count);
                if (rc < 0) {
                        if (rc == -ENXIO)
                                pr_warning("unrecognized header format"
-                                          " for image %s", rbd_dev->obj);
+                                          " for image %s\n",
+                                          rbd_dev->image_name);
                        goto out_dh;
                }
 
@@ -1628,7 +1651,7 @@ out_dh:
 /*
  * create a snapshot
  */
-static int rbd_header_add_snap(struct rbd_device *dev,
+static int rbd_header_add_snap(struct rbd_device *rbd_dev,
                               const char *snap_name,
                               gfp_t gfp_flags)
 {
@@ -1636,16 +1659,15 @@ static int rbd_header_add_snap(struct rbd_device *dev,
        u64 new_snapid;
        int ret;
        void *data, *p, *e;
-       u64 ver;
        struct ceph_mon_client *monc;
 
        /* we should create a snapshot only if we're pointing at the head */
-       if (dev->snap_id != CEPH_NOSNAP)
+       if (rbd_dev->snap_id != CEPH_NOSNAP)
                return -EINVAL;
 
-       monc = &dev->rbd_client->client->monc;
-       ret = ceph_monc_create_snapid(monc, dev->poolid, &new_snapid);
-       dout("created snapid=%lld\n", new_snapid);
+       monc = &rbd_dev->rbd_client->client->monc;
+       ret = ceph_monc_create_snapid(monc, rbd_dev->pool_id, &new_snapid);
+       dout("created snapid=%llu\n", (unsigned long long) new_snapid);
        if (ret < 0)
                return ret;
 
@@ -1659,19 +1681,13 @@ static int rbd_header_add_snap(struct rbd_device *dev,
        ceph_encode_string_safe(&p, e, snap_name, name_len, bad);
        ceph_encode_64_safe(&p, e, new_snapid, bad);
 
-       ret = rbd_req_sync_exec(dev, dev->obj_md_name, "rbd", "snap_add",
-                               data, p - data, &ver);
+       ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name,
+                               "rbd", "snap_add",
+                               data, p - data, NULL);
 
        kfree(data);
 
-       if (ret < 0)
-               return ret;
-
-       down_write(&dev->header_rwsem);
-       dev->header.snapc->seq = new_snapid;
-       up_write(&dev->header_rwsem);
-
-       return 0;
+       return ret < 0 ? ret : 0;
 bad:
        return -ERANGE;
 }
@@ -1679,52 +1695,52 @@ bad:
 static void __rbd_remove_all_snaps(struct rbd_device *rbd_dev)
 {
        struct rbd_snap *snap;
+       struct rbd_snap *next;
 
-       while (!list_empty(&rbd_dev->snaps)) {
-               snap = list_first_entry(&rbd_dev->snaps, struct rbd_snap, node);
-               __rbd_remove_snap_dev(rbd_dev, snap);
-       }
+       list_for_each_entry_safe(snap, next, &rbd_dev->snaps, node)
+               __rbd_remove_snap_dev(snap);
 }
 
 /*
  * only read the first part of the ondisk header, without the snaps info
  */
-static int __rbd_refresh_header(struct rbd_device *rbd_dev)
+static int __rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver)
 {
        int ret;
        struct rbd_image_header h;
-       u64 snap_seq;
-       int follow_seq = 0;
 
        ret = rbd_read_header(rbd_dev, &h);
        if (ret < 0)
                return ret;
 
-       /* resized? */
-       set_capacity(rbd_dev->disk, h.image_size / SECTOR_SIZE);
-
        down_write(&rbd_dev->header_rwsem);
 
-       snap_seq = rbd_dev->header.snapc->seq;
-       if (rbd_dev->header.total_snaps &&
-           rbd_dev->header.snapc->snaps[0] == snap_seq)
-               /* pointing at the head, will need to follow that
-                  if head moves */
-               follow_seq = 1;
+       /* resized? */
+       if (rbd_dev->snap_id == CEPH_NOSNAP) {
+               sector_t size = (sector_t) h.image_size / SECTOR_SIZE;
 
-       kfree(rbd_dev->header.snapc);
-       kfree(rbd_dev->header.snap_names);
+               dout("setting size to %llu sectors", (unsigned long long) size);
+               set_capacity(rbd_dev->disk, size);
+       }
+
+       /* rbd_dev->header.object_prefix shouldn't change */
        kfree(rbd_dev->header.snap_sizes);
+       kfree(rbd_dev->header.snap_names);
+       /* osd requests may still refer to snapc */
+       ceph_put_snap_context(rbd_dev->header.snapc);
 
+       if (hver)
+               *hver = h.obj_version;
+       rbd_dev->header.obj_version = h.obj_version;
+       rbd_dev->header.image_size = h.image_size;
        rbd_dev->header.total_snaps = h.total_snaps;
        rbd_dev->header.snapc = h.snapc;
        rbd_dev->header.snap_names = h.snap_names;
        rbd_dev->header.snap_names_len = h.snap_names_len;
        rbd_dev->header.snap_sizes = h.snap_sizes;
-       if (follow_seq)
-               rbd_dev->header.snapc->seq = rbd_dev->header.snapc->snaps[0];
-       else
-               rbd_dev->header.snapc->seq = snap_seq;
+       /* Free the extra copy of the object prefix */
+       WARN_ON(strcmp(rbd_dev->header.object_prefix, h.object_prefix));
+       kfree(h.object_prefix);
 
        ret = __rbd_init_snaps_header(rbd_dev);
 
@@ -1733,6 +1749,17 @@ static int __rbd_refresh_header(struct rbd_device *rbd_dev)
        return ret;
 }
 
+static int rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver)
+{
+       int ret;
+
+       mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
+       ret = __rbd_refresh_header(rbd_dev, hver);
+       mutex_unlock(&ctl_mutex);
+
+       return ret;
+}
+
 static int rbd_init_disk(struct rbd_device *rbd_dev)
 {
        struct gendisk *disk;
@@ -1762,7 +1789,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
                goto out;
 
        snprintf(disk->disk_name, sizeof(disk->disk_name), RBD_DRV_NAME "%d",
-                rbd_dev->id);
+                rbd_dev->dev_id);
        disk->major = rbd_dev->major;
        disk->first_minor = 0;
        disk->fops = &rbd_bd_ops;
@@ -1819,8 +1846,13 @@ static ssize_t rbd_size_show(struct device *dev,
                             struct device_attribute *attr, char *buf)
 {
        struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
+       sector_t size;
+
+       down_read(&rbd_dev->header_rwsem);
+       size = get_capacity(rbd_dev->disk);
+       up_read(&rbd_dev->header_rwsem);
 
-       return sprintf(buf, "%llu\n", (unsigned long long)rbd_dev->header.image_size);
+       return sprintf(buf, "%llu\n", (unsigned long long) size * SECTOR_SIZE);
 }
 
 static ssize_t rbd_major_show(struct device *dev,
@@ -1848,12 +1880,20 @@ static ssize_t rbd_pool_show(struct device *dev,
        return sprintf(buf, "%s\n", rbd_dev->pool_name);
 }
 
+static ssize_t rbd_pool_id_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
+
+       return sprintf(buf, "%d\n", rbd_dev->pool_id);
+}
+
 static ssize_t rbd_name_show(struct device *dev,
                             struct device_attribute *attr, char *buf)
 {
        struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
 
-       return sprintf(buf, "%s\n", rbd_dev->obj);
+       return sprintf(buf, "%s\n", rbd_dev->image_name);
 }
 
 static ssize_t rbd_snap_show(struct device *dev,
@@ -1871,23 +1911,18 @@ static ssize_t rbd_image_refresh(struct device *dev,
                                 size_t size)
 {
        struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
-       int rc;
-       int ret = size;
-
-       mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
+       int ret;
 
-       rc = __rbd_refresh_header(rbd_dev);
-       if (rc < 0)
-               ret = rc;
+       ret = rbd_refresh_header(rbd_dev, NULL);
 
-       mutex_unlock(&ctl_mutex);
-       return ret;
+       return ret < 0 ? ret : size;
 }
 
 static DEVICE_ATTR(size, S_IRUGO, rbd_size_show, NULL);
 static DEVICE_ATTR(major, S_IRUGO, rbd_major_show, NULL);
 static DEVICE_ATTR(client_id, S_IRUGO, rbd_client_id_show, NULL);
 static DEVICE_ATTR(pool, S_IRUGO, rbd_pool_show, NULL);
+static DEVICE_ATTR(pool_id, S_IRUGO, rbd_pool_id_show, NULL);
 static DEVICE_ATTR(name, S_IRUGO, rbd_name_show, NULL);
 static DEVICE_ATTR(refresh, S_IWUSR, NULL, rbd_image_refresh);
 static DEVICE_ATTR(current_snap, S_IRUGO, rbd_snap_show, NULL);
@@ -1898,6 +1933,7 @@ static struct attribute *rbd_attrs[] = {
        &dev_attr_major.attr,
        &dev_attr_client_id.attr,
        &dev_attr_pool.attr,
+       &dev_attr_pool_id.attr,
        &dev_attr_name.attr,
        &dev_attr_current_snap.attr,
        &dev_attr_refresh.attr,
@@ -1977,15 +2013,13 @@ static struct device_type rbd_snap_device_type = {
        .release        = rbd_snap_dev_release,
 };
 
-static void __rbd_remove_snap_dev(struct rbd_device *rbd_dev,
-                                 struct rbd_snap *snap)
+static void __rbd_remove_snap_dev(struct rbd_snap *snap)
 {
        list_del(&snap->node);
        device_unregister(&snap->dev);
 }
 
-static int rbd_register_snap_dev(struct rbd_device *rbd_dev,
-                                 struct rbd_snap *snap,
+static int rbd_register_snap_dev(struct rbd_snap *snap,
                                  struct device *parent)
 {
        struct device *dev = &snap->dev;
@@ -2000,29 +2034,36 @@ static int rbd_register_snap_dev(struct rbd_device *rbd_dev,
        return ret;
 }
 
-static int __rbd_add_snap_dev(struct rbd_device *rbd_dev,
-                             int i, const char *name,
-                             struct rbd_snap **snapp)
+static struct rbd_snap *__rbd_add_snap_dev(struct rbd_device *rbd_dev,
+                                             int i, const char *name)
 {
+       struct rbd_snap *snap;
        int ret;
-       struct rbd_snap *snap = kzalloc(sizeof(*snap), GFP_KERNEL);
+
+       snap = kzalloc(sizeof (*snap), GFP_KERNEL);
        if (!snap)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
+
+       ret = -ENOMEM;
        snap->name = kstrdup(name, GFP_KERNEL);
+       if (!snap->name)
+               goto err;
+
        snap->size = rbd_dev->header.snap_sizes[i];
        snap->id = rbd_dev->header.snapc->snaps[i];
        if (device_is_registered(&rbd_dev->dev)) {
-               ret = rbd_register_snap_dev(rbd_dev, snap,
-                                            &rbd_dev->dev);
+               ret = rbd_register_snap_dev(snap, &rbd_dev->dev);
                if (ret < 0)
                        goto err;
        }
-       *snapp = snap;
-       return 0;
+
+       return snap;
+
 err:
        kfree(snap->name);
        kfree(snap);
-       return ret;
+
+       return ERR_PTR(ret);
 }
 
 /*
@@ -2055,7 +2096,6 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev)
        const char *name, *first_name;
        int i = rbd_dev->header.total_snaps;
        struct rbd_snap *snap, *old_snap = NULL;
-       int ret;
        struct list_head *p, *n;
 
        first_name = rbd_dev->header.snap_names;
@@ -2070,8 +2110,15 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev)
                        cur_id = rbd_dev->header.snapc->snaps[i - 1];
 
                if (!i || old_snap->id < cur_id) {
-                       /* old_snap->id was skipped, thus was removed */
-                       __rbd_remove_snap_dev(rbd_dev, old_snap);
+                       /*
+                        * old_snap->id was skipped, thus was
+                        * removed.  If this rbd_dev is mapped to
+                        * the removed snapshot, record that it no
+                        * longer exists, to prevent further I/O.
+                        */
+                       if (rbd_dev->snap_id == old_snap->id)
+                               rbd_dev->snap_exists = false;
+                       __rbd_remove_snap_dev(old_snap);
                        continue;
                }
                if (old_snap->id == cur_id) {
@@ -2091,9 +2138,9 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev)
                        if (cur_id >= old_snap->id)
                                break;
                        /* a new snapshot */
-                       ret = __rbd_add_snap_dev(rbd_dev, i - 1, name, &snap);
-                       if (ret < 0)
-                               return ret;
+                       snap = __rbd_add_snap_dev(rbd_dev, i - 1, name);
+                       if (IS_ERR(snap))
+                               return PTR_ERR(snap);
 
                        /* note that we add it backward so using n and not p */
                        list_add(&snap->node, n);
@@ -2107,9 +2154,9 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev)
                        WARN_ON(1);
                        return -EINVAL;
                }
-               ret = __rbd_add_snap_dev(rbd_dev, i - 1, name, &snap);
-               if (ret < 0)
-                       return ret;
+               snap = __rbd_add_snap_dev(rbd_dev, i - 1, name);
+               if (IS_ERR(snap))
+                       return PTR_ERR(snap);
                list_add(&snap->node, &rbd_dev->snaps);
        }
 
@@ -2129,14 +2176,13 @@ static int rbd_bus_add_dev(struct rbd_device *rbd_dev)
        dev->type = &rbd_device_type;
        dev->parent = &rbd_root_dev;
        dev->release = rbd_dev_release;
-       dev_set_name(dev, "%d", rbd_dev->id);
+       dev_set_name(dev, "%d", rbd_dev->dev_id);
        ret = device_register(dev);
        if (ret < 0)
                goto out;
 
        list_for_each_entry(snap, &rbd_dev->snaps, node) {
-               ret = rbd_register_snap_dev(rbd_dev, snap,
-                                            &rbd_dev->dev);
+               ret = rbd_register_snap_dev(snap, &rbd_dev->dev);
                if (ret < 0)
                        break;
        }
@@ -2155,12 +2201,9 @@ static int rbd_init_watch_dev(struct rbd_device *rbd_dev)
        int ret, rc;
 
        do {
-               ret = rbd_req_sync_watch(rbd_dev, rbd_dev->obj_md_name,
-                                        rbd_dev->header.obj_version);
+               ret = rbd_req_sync_watch(rbd_dev);
                if (ret == -ERANGE) {
-                       mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
-                       rc = __rbd_refresh_header(rbd_dev);
-                       mutex_unlock(&ctl_mutex);
+                       rc = rbd_refresh_header(rbd_dev, NULL);
                        if (rc < 0)
                                return rc;
                }
@@ -2177,7 +2220,7 @@ static atomic64_t rbd_id_max = ATOMIC64_INIT(0);
  */
 static void rbd_id_get(struct rbd_device *rbd_dev)
 {
-       rbd_dev->id = atomic64_inc_return(&rbd_id_max);
+       rbd_dev->dev_id = atomic64_inc_return(&rbd_id_max);
 
        spin_lock(&rbd_dev_list_lock);
        list_add_tail(&rbd_dev->node, &rbd_dev_list);
@@ -2191,7 +2234,7 @@ static void rbd_id_get(struct rbd_device *rbd_dev)
 static void rbd_id_put(struct rbd_device *rbd_dev)
 {
        struct list_head *tmp;
-       int rbd_id = rbd_dev->id;
+       int rbd_id = rbd_dev->dev_id;
        int max_id;
 
        BUG_ON(rbd_id < 1);
@@ -2282,19 +2325,58 @@ static inline size_t copy_token(const char **buf,
 }
 
 /*
- * This fills in the pool_name, obj, obj_len, snap_name, obj_len,
+ * Finds the next token in *buf, dynamically allocates a buffer big
+ * enough to hold a copy of it, and copies the token into the new
+ * buffer.  The copy is guaranteed to be terminated with '\0'.  Note
+ * that a duplicate buffer is created even for a zero-length token.
+ *
+ * Returns a pointer to the newly-allocated duplicate, or a null
+ * pointer if memory for the duplicate was not available.  If
+ * the lenp argument is a non-null pointer, the length of the token
+ * (not including the '\0') is returned in *lenp.
+ *
+ * If successful, the *buf pointer will be updated to point beyond
+ * the end of the found token.
+ *
+ * Note: uses GFP_KERNEL for allocation.
+ */
+static inline char *dup_token(const char **buf, size_t *lenp)
+{
+       char *dup;
+       size_t len;
+
+       len = next_token(buf);
+       dup = kmalloc(len + 1, GFP_KERNEL);
+       if (!dup)
+               return NULL;
+
+       memcpy(dup, *buf, len);
+       *(dup + len) = '\0';
+       *buf += len;
+
+       if (lenp)
+               *lenp = len;
+
+       return dup;
+}
+
+/*
+ * This fills in the pool_name, image_name, image_name_len, snap_name,
  * rbd_dev, rbd_md_name, and name fields of the given rbd_dev, based
  * on the list of monitor addresses and other options provided via
  * /sys/bus/rbd/add.
+ *
+ * Note: rbd_dev is assumed to have been initially zero-filled.
  */
 static int rbd_add_parse_args(struct rbd_device *rbd_dev,
                              const char *buf,
                              const char **mon_addrs,
                              size_t *mon_addrs_size,
                              char *options,
-                             size_t options_size)
+                            size_t options_size)
 {
-       size_t  len;
+       size_t len;
+       int ret;
 
        /* The first four tokens are required */
 
@@ -2310,56 +2392,74 @@ static int rbd_add_parse_args(struct rbd_device *rbd_dev,
        if (!len || len >= options_size)
                return -EINVAL;
 
-       len = copy_token(&buf, rbd_dev->pool_name, sizeof (rbd_dev->pool_name));
-       if (!len || len >= sizeof (rbd_dev->pool_name))
-               return -EINVAL;
-
-       len = copy_token(&buf, rbd_dev->obj, sizeof (rbd_dev->obj));
-       if (!len || len >= sizeof (rbd_dev->obj))
-               return -EINVAL;
+       ret = -ENOMEM;
+       rbd_dev->pool_name = dup_token(&buf, NULL);
+       if (!rbd_dev->pool_name)
+               goto out_err;
 
-       /* We have the object length in hand, save it. */
+       rbd_dev->image_name = dup_token(&buf, &rbd_dev->image_name_len);
+       if (!rbd_dev->image_name)
+               goto out_err;
 
-       rbd_dev->obj_len = len;
+       /* Create the name of the header object */
 
-       BUILD_BUG_ON(RBD_MAX_MD_NAME_LEN
-                               < RBD_MAX_OBJ_NAME_LEN + sizeof (RBD_SUFFIX));
-       sprintf(rbd_dev->obj_md_name, "%s%s", rbd_dev->obj, RBD_SUFFIX);
+       rbd_dev->header_name = kmalloc(rbd_dev->image_name_len
+                                               + sizeof (RBD_SUFFIX),
+                                       GFP_KERNEL);
+       if (!rbd_dev->header_name)
+               goto out_err;
+       sprintf(rbd_dev->header_name, "%s%s", rbd_dev->image_name, RBD_SUFFIX);
 
        /*
-        * The snapshot name is optional, but it's an error if it's
-        * too long.  If no snapshot is supplied, fill in the default.
+        * The snapshot name is optional.  If none is is supplied,
+        * we use the default value.
         */
-       len = copy_token(&buf, rbd_dev->snap_name, sizeof (rbd_dev->snap_name));
-       if (!len)
+       rbd_dev->snap_name = dup_token(&buf, &len);
+       if (!rbd_dev->snap_name)
+               goto out_err;
+       if (!len) {
+               /* Replace the empty name with the default */
+               kfree(rbd_dev->snap_name);
+               rbd_dev->snap_name
+                       = kmalloc(sizeof (RBD_SNAP_HEAD_NAME), GFP_KERNEL);
+               if (!rbd_dev->snap_name)
+                       goto out_err;
+
                memcpy(rbd_dev->snap_name, RBD_SNAP_HEAD_NAME,
                        sizeof (RBD_SNAP_HEAD_NAME));
-       else if (len >= sizeof (rbd_dev->snap_name))
-               return -EINVAL;
+       }
 
        return 0;
+
+out_err:
+       kfree(rbd_dev->header_name);
+       kfree(rbd_dev->image_name);
+       kfree(rbd_dev->pool_name);
+       rbd_dev->pool_name = NULL;
+
+       return ret;
 }
 
 static ssize_t rbd_add(struct bus_type *bus,
                       const char *buf,
                       size_t count)
 {
-       struct rbd_device *rbd_dev;
+       char *options;
+       struct rbd_device *rbd_dev = NULL;
        const char *mon_addrs = NULL;
        size_t mon_addrs_size = 0;
-       char *options = NULL;
        struct ceph_osd_client *osdc;
        int rc = -ENOMEM;
 
        if (!try_module_get(THIS_MODULE))
                return -ENODEV;
 
-       rbd_dev = kzalloc(sizeof(*rbd_dev), GFP_KERNEL);
-       if (!rbd_dev)
-               goto err_nomem;
        options = kmalloc(count, GFP_KERNEL);
        if (!options)
                goto err_nomem;
+       rbd_dev = kzalloc(sizeof(*rbd_dev), GFP_KERNEL);
+       if (!rbd_dev)
+               goto err_nomem;
 
        /* static rbd_device initialization */
        spin_lock_init(&rbd_dev->lock);
@@ -2367,15 +2467,13 @@ static ssize_t rbd_add(struct bus_type *bus,
        INIT_LIST_HEAD(&rbd_dev->snaps);
        init_rwsem(&rbd_dev->header_rwsem);
 
-       init_rwsem(&rbd_dev->header_rwsem);
-
        /* generate unique id: find highest unique id, add one */
        rbd_id_get(rbd_dev);
 
        /* Fill in the device name, now that we have its id. */
        BUILD_BUG_ON(DEV_NAME_LEN
                        < sizeof (RBD_DRV_NAME) + MAX_INT_FORMAT_WIDTH);
-       sprintf(rbd_dev->name, "%s%d", RBD_DRV_NAME, rbd_dev->id);
+       sprintf(rbd_dev->name, "%s%d", RBD_DRV_NAME, rbd_dev->dev_id);
 
        /* parse add command */
        rc = rbd_add_parse_args(rbd_dev, buf, &mon_addrs, &mon_addrs_size,
@@ -2395,7 +2493,7 @@ static ssize_t rbd_add(struct bus_type *bus,
        rc = ceph_pg_poolid_by_name(osdc->osdmap, rbd_dev->pool_name);
        if (rc < 0)
                goto err_out_client;
-       rbd_dev->poolid = rc;
+       rbd_dev->pool_id = rc;
 
        /* register our block device */
        rc = register_blkdev(0, rbd_dev->name);
@@ -2435,10 +2533,16 @@ err_out_blkdev:
 err_out_client:
        rbd_put_client(rbd_dev);
 err_put_id:
+       if (rbd_dev->pool_name) {
+               kfree(rbd_dev->snap_name);
+               kfree(rbd_dev->header_name);
+               kfree(rbd_dev->image_name);
+               kfree(rbd_dev->pool_name);
+       }
        rbd_id_put(rbd_dev);
 err_nomem:
-       kfree(options);
        kfree(rbd_dev);
+       kfree(options);
 
        dout("Error adding device %s\n", buf);
        module_put(THIS_MODULE);
@@ -2446,7 +2550,7 @@ err_nomem:
        return (ssize_t) rc;
 }
 
-static struct rbd_device *__rbd_get_dev(unsigned long id)
+static struct rbd_device *__rbd_get_dev(unsigned long dev_id)
 {
        struct list_head *tmp;
        struct rbd_device *rbd_dev;
@@ -2454,7 +2558,7 @@ static struct rbd_device *__rbd_get_dev(unsigned long id)
        spin_lock(&rbd_dev_list_lock);
        list_for_each(tmp, &rbd_dev_list) {
                rbd_dev = list_entry(tmp, struct rbd_device, node);
-               if (rbd_dev->id == id) {
+               if (rbd_dev->dev_id == dev_id) {
                        spin_unlock(&rbd_dev_list_lock);
                        return rbd_dev;
                }
@@ -2474,7 +2578,7 @@ static void rbd_dev_release(struct device *dev)
                                                    rbd_dev->watch_request);
        }
        if (rbd_dev->watch_event)
-               rbd_req_sync_unwatch(rbd_dev, rbd_dev->obj_md_name);
+               rbd_req_sync_unwatch(rbd_dev);
 
        rbd_put_client(rbd_dev);
 
@@ -2483,6 +2587,10 @@ static void rbd_dev_release(struct device *dev)
        unregister_blkdev(rbd_dev->major, rbd_dev->name);
 
        /* done with the id, and with the rbd_dev */
+       kfree(rbd_dev->snap_name);
+       kfree(rbd_dev->header_name);
+       kfree(rbd_dev->pool_name);
+       kfree(rbd_dev->image_name);
        rbd_id_put(rbd_dev);
        kfree(rbd_dev);
 
@@ -2544,7 +2652,7 @@ static ssize_t rbd_snap_add(struct device *dev,
        if (ret < 0)
                goto err_unlock;
 
-       ret = __rbd_refresh_header(rbd_dev);
+       ret = __rbd_refresh_header(rbd_dev, NULL);
        if (ret < 0)
                goto err_unlock;
 
@@ -2553,7 +2661,7 @@ static ssize_t rbd_snap_add(struct device *dev,
        mutex_unlock(&ctl_mutex);
 
        /* make a best effort, don't error if failed */
-       rbd_req_sync_notify(rbd_dev, rbd_dev->obj_md_name);
+       rbd_req_sync_notify(rbd_dev);
 
        ret = count;
        kfree(name);
index 950708688f1719109962e86b450186c845f89cf7..0924e9e41a60ce4b297f8fb62a17ca6d81c90cf1 100644 (file)
@@ -31,7 +31,6 @@
 #define RBD_MIN_OBJ_ORDER       16
 #define RBD_MAX_OBJ_ORDER       30
 
-#define RBD_MAX_OBJ_NAME_LEN   96
 #define RBD_MAX_SEG_NAME_LEN   128
 
 #define RBD_COMP_NONE          0
index 9a72277a31df0cb131f879bb8a1503a65a57b7cc..eb0d8216f557434b36e6fbc809b75c33bbbc1292 100644 (file)
@@ -513,42 +513,19 @@ static void process_page(unsigned long data)
        }
 }
 
-struct mm_plug_cb {
-       struct blk_plug_cb cb;
-       struct cardinfo *card;
-};
-
-static void mm_unplug(struct blk_plug_cb *cb)
+static void mm_unplug(struct blk_plug_cb *cb, bool from_schedule)
 {
-       struct mm_plug_cb *mmcb = container_of(cb, struct mm_plug_cb, cb);
+       struct cardinfo *card = cb->data;
 
-       spin_lock_irq(&mmcb->card->lock);
-       activate(mmcb->card);
-       spin_unlock_irq(&mmcb->card->lock);
-       kfree(mmcb);
+       spin_lock_irq(&card->lock);
+       activate(card);
+       spin_unlock_irq(&card->lock);
+       kfree(cb);
 }
 
 static int mm_check_plugged(struct cardinfo *card)
 {
-       struct blk_plug *plug = current->plug;
-       struct mm_plug_cb *mmcb;
-
-       if (!plug)
-               return 0;
-
-       list_for_each_entry(mmcb, &plug->cb_list, cb.list) {
-               if (mmcb->cb.callback == mm_unplug && mmcb->card == card)
-                       return 1;
-       }
-       /* Not currently on the callback list */
-       mmcb = kmalloc(sizeof(*mmcb), GFP_ATOMIC);
-       if (!mmcb)
-               return 0;
-
-       mmcb->card = card;
-       mmcb->cb.callback = mm_unplug;
-       list_add(&mmcb->cb.list, &plug->cb_list);
-       return 1;
+       return !!blk_check_plugged(mm_unplug, card, sizeof(struct blk_plug_cb));
 }
 
 static void mm_make_request(struct request_queue *q, struct bio *bio)
index 693187df76012e1ace1f3c9b58fd4e78d6ec3aab..c0bbeb4707542ac2370b0337de5f3e4a3037d619 100644 (file)
@@ -21,8 +21,6 @@ struct workqueue_struct *virtblk_wq;
 
 struct virtio_blk
 {
-       spinlock_t lock;
-
        struct virtio_device *vdev;
        struct virtqueue *vq;
 
@@ -65,7 +63,7 @@ static void blk_done(struct virtqueue *vq)
        unsigned int len;
        unsigned long flags;
 
-       spin_lock_irqsave(&vblk->lock, flags);
+       spin_lock_irqsave(vblk->disk->queue->queue_lock, flags);
        while ((vbr = virtqueue_get_buf(vblk->vq, &len)) != NULL) {
                int error;
 
@@ -99,7 +97,7 @@ static void blk_done(struct virtqueue *vq)
        }
        /* In case queue is stopped waiting for more buffers. */
        blk_start_queue(vblk->disk->queue);
-       spin_unlock_irqrestore(&vblk->lock, flags);
+       spin_unlock_irqrestore(vblk->disk->queue->queue_lock, flags);
 }
 
 static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
@@ -397,6 +395,83 @@ static int virtblk_name_format(char *prefix, int index, char *buf, int buflen)
        return 0;
 }
 
+static int virtblk_get_cache_mode(struct virtio_device *vdev)
+{
+       u8 writeback;
+       int err;
+
+       err = virtio_config_val(vdev, VIRTIO_BLK_F_CONFIG_WCE,
+                               offsetof(struct virtio_blk_config, wce),
+                               &writeback);
+       if (err)
+               writeback = virtio_has_feature(vdev, VIRTIO_BLK_F_WCE);
+
+       return writeback;
+}
+
+static void virtblk_update_cache_mode(struct virtio_device *vdev)
+{
+       u8 writeback = virtblk_get_cache_mode(vdev);
+       struct virtio_blk *vblk = vdev->priv;
+
+       if (writeback)
+               blk_queue_flush(vblk->disk->queue, REQ_FLUSH);
+       else
+               blk_queue_flush(vblk->disk->queue, 0);
+
+       revalidate_disk(vblk->disk);
+}
+
+static const char *const virtblk_cache_types[] = {
+       "write through", "write back"
+};
+
+static ssize_t
+virtblk_cache_type_store(struct device *dev, struct device_attribute *attr,
+                        const char *buf, size_t count)
+{
+       struct gendisk *disk = dev_to_disk(dev);
+       struct virtio_blk *vblk = disk->private_data;
+       struct virtio_device *vdev = vblk->vdev;
+       int i;
+       u8 writeback;
+
+       BUG_ON(!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_CONFIG_WCE));
+       for (i = ARRAY_SIZE(virtblk_cache_types); --i >= 0; )
+               if (sysfs_streq(buf, virtblk_cache_types[i]))
+                       break;
+
+       if (i < 0)
+               return -EINVAL;
+
+       writeback = i;
+       vdev->config->set(vdev,
+                         offsetof(struct virtio_blk_config, wce),
+                         &writeback, sizeof(writeback));
+
+       virtblk_update_cache_mode(vdev);
+       return count;
+}
+
+static ssize_t
+virtblk_cache_type_show(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct gendisk *disk = dev_to_disk(dev);
+       struct virtio_blk *vblk = disk->private_data;
+       u8 writeback = virtblk_get_cache_mode(vblk->vdev);
+
+       BUG_ON(writeback >= ARRAY_SIZE(virtblk_cache_types));
+       return snprintf(buf, 40, "%s\n", virtblk_cache_types[writeback]);
+}
+
+static const struct device_attribute dev_attr_cache_type_ro =
+       __ATTR(cache_type, S_IRUGO,
+              virtblk_cache_type_show, NULL);
+static const struct device_attribute dev_attr_cache_type_rw =
+       __ATTR(cache_type, S_IRUGO|S_IWUSR,
+              virtblk_cache_type_show, virtblk_cache_type_store);
+
 static int __devinit virtblk_probe(struct virtio_device *vdev)
 {
        struct virtio_blk *vblk;
@@ -431,7 +506,6 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
                goto out_free_index;
        }
 
-       spin_lock_init(&vblk->lock);
        vblk->vdev = vdev;
        vblk->sg_elems = sg_elems;
        sg_init_table(vblk->sg, vblk->sg_elems);
@@ -456,7 +530,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
                goto out_mempool;
        }
 
-       q = vblk->disk->queue = blk_init_queue(do_virtblk_request, &vblk->lock);
+       q = vblk->disk->queue = blk_init_queue(do_virtblk_request, NULL);
        if (!q) {
                err = -ENOMEM;
                goto out_put_disk;
@@ -474,8 +548,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
        vblk->index = index;
 
        /* configure queue flush support */
-       if (virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH))
-               blk_queue_flush(q, REQ_FLUSH);
+       virtblk_update_cache_mode(vdev);
 
        /* If disk is read-only in the host, the guest should obey */
        if (virtio_has_feature(vdev, VIRTIO_BLK_F_RO))
@@ -553,6 +626,14 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
        if (err)
                goto out_del_disk;
 
+       if (virtio_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE))
+               err = device_create_file(disk_to_dev(vblk->disk),
+                                        &dev_attr_cache_type_rw);
+       else
+               err = device_create_file(disk_to_dev(vblk->disk),
+                                        &dev_attr_cache_type_ro);
+       if (err)
+               goto out_del_disk;
        return 0;
 
 out_del_disk:
@@ -576,30 +657,20 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
 {
        struct virtio_blk *vblk = vdev->priv;
        int index = vblk->index;
-       struct virtblk_req *vbr;
-       unsigned long flags;
 
        /* Prevent config work handler from accessing the device. */
        mutex_lock(&vblk->config_lock);
        vblk->config_enable = false;
        mutex_unlock(&vblk->config_lock);
 
+       del_gendisk(vblk->disk);
+       blk_cleanup_queue(vblk->disk->queue);
+
        /* Stop all the virtqueues. */
        vdev->config->reset(vdev);
 
        flush_work(&vblk->config_work);
 
-       del_gendisk(vblk->disk);
-
-       /* Abort requests dispatched to driver. */
-       spin_lock_irqsave(&vblk->lock, flags);
-       while ((vbr = virtqueue_detach_unused_buf(vblk->vq))) {
-               __blk_end_request_all(vbr->req, -EIO);
-               mempool_free(vbr, vblk->pool);
-       }
-       spin_unlock_irqrestore(&vblk->lock, flags);
-
-       blk_cleanup_queue(vblk->disk->queue);
        put_disk(vblk->disk);
        mempool_destroy(vblk->pool);
        vdev->config->del_vqs(vdev);
@@ -655,7 +726,7 @@ static const struct virtio_device_id id_table[] = {
 static unsigned int features[] = {
        VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_GEOMETRY,
        VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE, VIRTIO_BLK_F_SCSI,
-       VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY
+       VIRTIO_BLK_F_WCE, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_CONFIG_WCE
 };
 
 /*
index e4fb3374dcd2aaa6d0f834cd9394564a3c022a38..2c2d2e5c15974c30755fd61715a189029dff7cd6 100644 (file)
@@ -888,9 +888,8 @@ static int setup_blkring(struct xenbus_device *dev,
        if (err)
                goto fail;
 
-       err = bind_evtchn_to_irqhandler(info->evtchn,
-                                       blkif_interrupt,
-                                       IRQF_SAMPLE_RANDOM, "blkif", info);
+       err = bind_evtchn_to_irqhandler(info->evtchn, blkif_interrupt, 0,
+                                       "blkif", info);
        if (err <= 0) {
                xenbus_dev_fatal(dev, err,
                                 "bind_evtchn_to_irqhandler failed");
index b01d67328243c28dff8291932f16c7bc29b7e888..7c0d391996b5b4e82432b9b3418d427f7a42894a 100644 (file)
@@ -73,6 +73,20 @@ config HW_RANDOM_ATMEL
 
          If unsure, say Y.
 
+config HW_RANDOM_BCM63XX
+       tristate "Broadcom BCM63xx Random Number Generator support"
+       depends on HW_RANDOM && BCM63XX
+       default HW_RANDOM
+       ---help---
+         This driver provides kernel-side support for the Random Number
+         Generator hardware found on the Broadcom BCM63xx SoCs.
+
+         To compile this driver as a module, choose M here: the
+         module will be called bcm63xx-rng
+
+         If unusure, say Y.
+
+
 config HW_RANDOM_GEODE
        tristate "AMD Geode HW Random Number Generator support"
        depends on HW_RANDOM && X86_32 && PCI
index 8d6d173b65e6ea51cb806b60400e0d157629da96..39a757ca15b65c59b188d0489b12488e4b4c6ac3 100644 (file)
@@ -8,6 +8,7 @@ obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o
 obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o
 obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o
 obj-$(CONFIG_HW_RANDOM_ATMEL) += atmel-rng.o
+obj-$(CONFIG_HW_RANDOM_BCM63XX)        += bcm63xx-rng.o
 obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o
 obj-$(CONFIG_HW_RANDOM_N2RNG) += n2-rng.o
 n2-rng-y := n2-drv.o n2-asm.o
diff --git a/drivers/char/hw_random/bcm63xx-rng.c b/drivers/char/hw_random/bcm63xx-rng.c
new file mode 100644 (file)
index 0000000..aec6a42
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Broadcom BCM63xx Random Number Generator support
+ *
+ * Copyright (C) 2011, Florian Fainelli <florian@openwrt.org>
+ * Copyright (C) 2009, Broadcom Corporation
+ *
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/hw_random.h>
+
+#include <bcm63xx_io.h>
+#include <bcm63xx_regs.h>
+
+struct bcm63xx_rng_priv {
+       struct clk *clk;
+       void __iomem *regs;
+};
+
+#define to_rng_priv(rng)       ((struct bcm63xx_rng_priv *)rng->priv)
+
+static int bcm63xx_rng_init(struct hwrng *rng)
+{
+       struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
+       u32 val;
+
+       val = bcm_readl(priv->regs + RNG_CTRL);
+       val |= RNG_EN;
+       bcm_writel(val, priv->regs + RNG_CTRL);
+
+       return 0;
+}
+
+static void bcm63xx_rng_cleanup(struct hwrng *rng)
+{
+       struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
+       u32 val;
+
+       val = bcm_readl(priv->regs + RNG_CTRL);
+       val &= ~RNG_EN;
+       bcm_writel(val, priv->regs + RNG_CTRL);
+}
+
+static int bcm63xx_rng_data_present(struct hwrng *rng, int wait)
+{
+       struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
+
+       return bcm_readl(priv->regs + RNG_STAT) & RNG_AVAIL_MASK;
+}
+
+static int bcm63xx_rng_data_read(struct hwrng *rng, u32 *data)
+{
+       struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
+
+       *data = bcm_readl(priv->regs + RNG_DATA);
+
+       return 4;
+}
+
+static int __devinit bcm63xx_rng_probe(struct platform_device *pdev)
+{
+       struct resource *r;
+       struct clk *clk;
+       int ret;
+       struct bcm63xx_rng_priv *priv;
+       struct hwrng *rng;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               dev_err(&pdev->dev, "no iomem resource\n");
+               ret = -ENXIO;
+               goto out;
+       }
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               dev_err(&pdev->dev, "no memory for private structure\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       rng = kzalloc(sizeof(*rng), GFP_KERNEL);
+       if (!rng) {
+               dev_err(&pdev->dev, "no memory for rng structure\n");
+               ret = -ENOMEM;
+               goto out_free_priv;
+       }
+
+       platform_set_drvdata(pdev, rng);
+       rng->priv = (unsigned long)priv;
+       rng->name = pdev->name;
+       rng->init = bcm63xx_rng_init;
+       rng->cleanup = bcm63xx_rng_cleanup;
+       rng->data_present = bcm63xx_rng_data_present;
+       rng->data_read = bcm63xx_rng_data_read;
+
+       clk = clk_get(&pdev->dev, "ipsec");
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "no clock for device\n");
+               ret = PTR_ERR(clk);
+               goto out_free_rng;
+       }
+
+       priv->clk = clk;
+
+       if (!devm_request_mem_region(&pdev->dev, r->start,
+                                       resource_size(r), pdev->name)) {
+               dev_err(&pdev->dev, "request mem failed");
+               ret = -ENOMEM;
+               goto out_free_rng;
+       }
+
+       priv->regs = devm_ioremap_nocache(&pdev->dev, r->start,
+                                       resource_size(r));
+       if (!priv->regs) {
+               dev_err(&pdev->dev, "ioremap failed");
+               ret = -ENOMEM;
+               goto out_free_rng;
+       }
+
+       clk_enable(clk);
+
+       ret = hwrng_register(rng);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register rng device\n");
+               goto out_clk_disable;
+       }
+
+       dev_info(&pdev->dev, "registered RNG driver\n");
+
+       return 0;
+
+out_clk_disable:
+       clk_disable(clk);
+out_free_rng:
+       platform_set_drvdata(pdev, NULL);
+       kfree(rng);
+out_free_priv:
+       kfree(priv);
+out:
+       return ret;
+}
+
+static int __devexit bcm63xx_rng_remove(struct platform_device *pdev)
+{
+       struct hwrng *rng = platform_get_drvdata(pdev);
+       struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
+
+       hwrng_unregister(rng);
+       clk_disable(priv->clk);
+       kfree(priv);
+       kfree(rng);
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver bcm63xx_rng_driver = {
+       .probe          = bcm63xx_rng_probe,
+       .remove         = __devexit_p(bcm63xx_rng_remove),
+       .driver         = {
+               .name   = "bcm63xx-rng",
+               .owner  = THIS_MODULE,
+       },
+};
+
+module_platform_driver(bcm63xx_rng_driver);
+
+MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_DESCRIPTION("Broadcom BCM63xx RNG driver");
+MODULE_LICENSE("GPL");
index 723725bbb96b774036f9fee05562b207d1cecfc0..5708299507d0e5b59ad401d495f1b0baa80dbe65 100644 (file)
@@ -55,6 +55,7 @@ static void register_buffer(u8 *buf, size_t size)
 
 static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
 {
+       int ret;
 
        if (!busy) {
                busy = true;
@@ -65,7 +66,9 @@ static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
        if (!wait)
                return 0;
 
-       wait_for_completion(&have_data);
+       ret = wait_for_completion_killable(&have_data);
+       if (ret < 0)
+               return ret;
 
        busy = false;
 
@@ -85,7 +88,7 @@ static struct hwrng virtio_hwrng = {
        .read           = virtio_read,
 };
 
-static int virtrng_probe(struct virtio_device *vdev)
+static int probe_common(struct virtio_device *vdev)
 {
        int err;
 
@@ -103,13 +106,37 @@ static int virtrng_probe(struct virtio_device *vdev)
        return 0;
 }
 
-static void __devexit virtrng_remove(struct virtio_device *vdev)
+static void remove_common(struct virtio_device *vdev)
 {
        vdev->config->reset(vdev);
+       busy = false;
        hwrng_unregister(&virtio_hwrng);
        vdev->config->del_vqs(vdev);
 }
 
+static int virtrng_probe(struct virtio_device *vdev)
+{
+       return probe_common(vdev);
+}
+
+static void __devexit virtrng_remove(struct virtio_device *vdev)
+{
+       remove_common(vdev);
+}
+
+#ifdef CONFIG_PM
+static int virtrng_freeze(struct virtio_device *vdev)
+{
+       remove_common(vdev);
+       return 0;
+}
+
+static int virtrng_restore(struct virtio_device *vdev)
+{
+       return probe_common(vdev);
+}
+#endif
+
 static struct virtio_device_id id_table[] = {
        { VIRTIO_ID_RNG, VIRTIO_DEV_ANY_ID },
        { 0 },
@@ -121,6 +148,10 @@ static struct virtio_driver virtio_rng_driver = {
        .id_table =     id_table,
        .probe =        virtrng_probe,
        .remove =       __devexit_p(virtrng_remove),
+#ifdef CONFIG_PM
+       .freeze =       virtrng_freeze,
+       .restore =      virtrng_restore,
+#endif
 };
 
 static int __init init(void)
index 8b78750f1efe89d50a1ea075420ea032f4ad2ecd..845f97fd18326fbe2301a515da53483d966907a8 100644 (file)
@@ -283,7 +283,7 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma,
        vdata->flags = flags;
        vdata->type = type;
        spin_lock_init(&vdata->lock);
-       vdata->refcnt = ATOMIC_INIT(1);
+       atomic_set(&vdata->refcnt, 1);
        vma->vm_private_data = vdata;
 
        vma->vm_flags |= (VM_IO | VM_RESERVED | VM_PFNMAP | VM_DONTEXPAND);
index 4ec04a754733baed5dd8595256a9c7998965d323..b86eae9b77dfaeb04dd2d4efefd6ebc01b9e0a93 100644 (file)
  * The current exported interfaces for gathering environmental noise
  * from the devices are:
  *
+ *     void add_device_randomness(const void *buf, unsigned int size);
  *     void add_input_randomness(unsigned int type, unsigned int code,
  *                                unsigned int value);
- *     void add_interrupt_randomness(int irq);
+ *     void add_interrupt_randomness(int irq, int irq_flags);
  *     void add_disk_randomness(struct gendisk *disk);
  *
+ * add_device_randomness() is for adding data to the random pool that
+ * is likely to differ between two devices (or possibly even per boot).
+ * This would be things like MAC addresses or serial numbers, or the
+ * read-out of the RTC. This does *not* add any actual entropy to the
+ * pool, but it initializes the pool to different values for devices
+ * that might otherwise be identical and have very little entropy
+ * available to them (particularly common in the embedded world).
+ *
  * add_input_randomness() uses the input layer interrupt timing, as well as
  * the event type information from the hardware.
  *
- * add_interrupt_randomness() uses the inter-interrupt timing as random
- * inputs to the entropy pool.  Note that not all interrupts are good
- * sources of randomness!  For example, the timer interrupts is not a
- * good choice, because the periodicity of the interrupts is too
- * regular, and hence predictable to an attacker.  Network Interface
- * Controller interrupts are a better measure, since the timing of the
- * NIC interrupts are more unpredictable.
+ * add_interrupt_randomness() uses the interrupt timing as random
+ * inputs to the entropy pool. Using the cycle counters and the irq source
+ * as inputs, it feeds the randomness roughly once a second.
  *
  * add_disk_randomness() uses what amounts to the seek time of block
  * layer request events, on a per-disk_devt basis, as input to the
 #include <linux/percpu.h>
 #include <linux/cryptohash.h>
 #include <linux/fips.h>
+#include <linux/ptrace.h>
+#include <linux/kmemcheck.h>
 
 #ifdef CONFIG_GENERIC_HARDIRQS
 # include <linux/irq.h>
 #include <asm/processor.h>
 #include <asm/uaccess.h>
 #include <asm/irq.h>
+#include <asm/irq_regs.h>
 #include <asm/io.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/random.h>
+
 /*
  * Configuration information
  */
 #define SEC_XFER_SIZE 512
 #define EXTRACT_SIZE 10
 
+#define LONGS(x) (((x) + sizeof(unsigned long) - 1)/sizeof(unsigned long))
+
 /*
  * The minimum number of bits of entropy before we wake up a read on
  * /dev/random.  Should be enough to do a significant reseed.
@@ -420,8 +433,10 @@ struct entropy_store {
        /* read-write data: */
        spinlock_t lock;
        unsigned add_ptr;
+       unsigned input_rotate;
        int entropy_count;
-       int input_rotate;
+       int entropy_total;
+       unsigned int initialized:1;
        __u8 last_data[EXTRACT_SIZE];
 };
 
@@ -454,6 +469,10 @@ static struct entropy_store nonblocking_pool = {
        .pool = nonblocking_pool_data
 };
 
+static __u32 const twist_table[8] = {
+       0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
+       0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
+
 /*
  * This function adds bytes into the entropy "pool".  It does not
  * update the entropy estimate.  The caller should call
@@ -464,29 +483,24 @@ static struct entropy_store nonblocking_pool = {
  * it's cheap to do so and helps slightly in the expected case where
  * the entropy is concentrated in the low-order bits.
  */
-static void mix_pool_bytes_extract(struct entropy_store *r, const void *in,
-                                  int nbytes, __u8 out[64])
+static void _mix_pool_bytes(struct entropy_store *r, const void *in,
+                           int nbytes, __u8 out[64])
 {
-       static __u32 const twist_table[8] = {
-               0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
-               0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
        unsigned long i, j, tap1, tap2, tap3, tap4, tap5;
        int input_rotate;
        int wordmask = r->poolinfo->poolwords - 1;
        const char *bytes = in;
        __u32 w;
-       unsigned long flags;
 
-       /* Taps are constant, so we can load them without holding r->lock.  */
        tap1 = r->poolinfo->tap1;
        tap2 = r->poolinfo->tap2;
        tap3 = r->poolinfo->tap3;
        tap4 = r->poolinfo->tap4;
        tap5 = r->poolinfo->tap5;
 
-       spin_lock_irqsave(&r->lock, flags);
-       input_rotate = r->input_rotate;
-       i = r->add_ptr;
+       smp_rmb();
+       input_rotate = ACCESS_ONCE(r->input_rotate);
+       i = ACCESS_ONCE(r->add_ptr);
 
        /* mix one byte at a time to simplify size handling and churn faster */
        while (nbytes--) {
@@ -513,19 +527,61 @@ static void mix_pool_bytes_extract(struct entropy_store *r, const void *in,
                input_rotate += i ? 7 : 14;
        }
 
-       r->input_rotate = input_rotate;
-       r->add_ptr = i;
+       ACCESS_ONCE(r->input_rotate) = input_rotate;
+       ACCESS_ONCE(r->add_ptr) = i;
+       smp_wmb();
 
        if (out)
                for (j = 0; j < 16; j++)
                        ((__u32 *)out)[j] = r->pool[(i - j) & wordmask];
+}
+
+static void __mix_pool_bytes(struct entropy_store *r, const void *in,
+                            int nbytes, __u8 out[64])
+{
+       trace_mix_pool_bytes_nolock(r->name, nbytes, _RET_IP_);
+       _mix_pool_bytes(r, in, nbytes, out);
+}
+
+static void mix_pool_bytes(struct entropy_store *r, const void *in,
+                          int nbytes, __u8 out[64])
+{
+       unsigned long flags;
 
+       trace_mix_pool_bytes(r->name, nbytes, _RET_IP_);
+       spin_lock_irqsave(&r->lock, flags);
+       _mix_pool_bytes(r, in, nbytes, out);
        spin_unlock_irqrestore(&r->lock, flags);
 }
 
-static void mix_pool_bytes(struct entropy_store *r, const void *in, int bytes)
+struct fast_pool {
+       __u32           pool[4];
+       unsigned long   last;
+       unsigned short  count;
+       unsigned char   rotate;
+       unsigned char   last_timer_intr;
+};
+
+/*
+ * This is a fast mixing routine used by the interrupt randomness
+ * collector.  It's hardcoded for an 128 bit pool and assumes that any
+ * locks that might be needed are taken by the caller.
+ */
+static void fast_mix(struct fast_pool *f, const void *in, int nbytes)
 {
-       mix_pool_bytes_extract(r, in, bytes, NULL);
+       const char      *bytes = in;
+       __u32           w;
+       unsigned        i = f->count;
+       unsigned        input_rotate = f->rotate;
+
+       while (nbytes--) {
+               w = rol32(*bytes++, input_rotate & 31) ^ f->pool[i & 3] ^
+                       f->pool[(i + 1) & 3];
+               f->pool[i & 3] = (w >> 3) ^ twist_table[w & 7];
+               input_rotate += (i++ & 3) ? 7 : 14;
+       }
+       f->count = i;
+       f->rotate = input_rotate;
 }
 
 /*
@@ -533,30 +589,38 @@ static void mix_pool_bytes(struct entropy_store *r, const void *in, int bytes)
  */
 static void credit_entropy_bits(struct entropy_store *r, int nbits)
 {
-       unsigned long flags;
-       int entropy_count;
+       int entropy_count, orig;
 
        if (!nbits)
                return;
 
-       spin_lock_irqsave(&r->lock, flags);
-
        DEBUG_ENT("added %d entropy credits to %s\n", nbits, r->name);
-       entropy_count = r->entropy_count;
+retry:
+       entropy_count = orig = ACCESS_ONCE(r->entropy_count);
        entropy_count += nbits;
+
        if (entropy_count < 0) {
                DEBUG_ENT("negative entropy/overflow\n");
                entropy_count = 0;
        } else if (entropy_count > r->poolinfo->POOLBITS)
                entropy_count = r->poolinfo->POOLBITS;
-       r->entropy_count = entropy_count;
+       if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
+               goto retry;
+
+       if (!r->initialized && nbits > 0) {
+               r->entropy_total += nbits;
+               if (r->entropy_total > 128)
+                       r->initialized = 1;
+       }
+
+       trace_credit_entropy_bits(r->name, nbits, entropy_count,
+                                 r->entropy_total, _RET_IP_);
 
        /* should we wake readers? */
        if (r == &input_pool && entropy_count >= random_read_wakeup_thresh) {
                wake_up_interruptible(&random_read_wait);
                kill_fasync(&fasync, SIGIO, POLL_IN);
        }
-       spin_unlock_irqrestore(&r->lock, flags);
 }
 
 /*********************************************************************
@@ -572,42 +636,24 @@ struct timer_rand_state {
        unsigned dont_count_entropy:1;
 };
 
-#ifndef CONFIG_GENERIC_HARDIRQS
-
-static struct timer_rand_state *irq_timer_state[NR_IRQS];
-
-static struct timer_rand_state *get_timer_rand_state(unsigned int irq)
-{
-       return irq_timer_state[irq];
-}
-
-static void set_timer_rand_state(unsigned int irq,
-                                struct timer_rand_state *state)
-{
-       irq_timer_state[irq] = state;
-}
-
-#else
-
-static struct timer_rand_state *get_timer_rand_state(unsigned int irq)
-{
-       struct irq_desc *desc;
-
-       desc = irq_to_desc(irq);
-
-       return desc->timer_rand_state;
-}
-
-static void set_timer_rand_state(unsigned int irq,
-                                struct timer_rand_state *state)
+/*
+ * Add device- or boot-specific data to the input and nonblocking
+ * pools to help initialize them to unique values.
+ *
+ * None of this adds any entropy, it is meant to avoid the
+ * problem of the nonblocking pool having similar initial state
+ * across largely identical devices.
+ */
+void add_device_randomness(const void *buf, unsigned int size)
 {
-       struct irq_desc *desc;
+       unsigned long time = get_cycles() ^ jiffies;
 
-       desc = irq_to_desc(irq);
-
-       desc->timer_rand_state = state;
+       mix_pool_bytes(&input_pool, buf, size, NULL);
+       mix_pool_bytes(&input_pool, &time, sizeof(time), NULL);
+       mix_pool_bytes(&nonblocking_pool, buf, size, NULL);
+       mix_pool_bytes(&nonblocking_pool, &time, sizeof(time), NULL);
 }
-#endif
+EXPORT_SYMBOL(add_device_randomness);
 
 static struct timer_rand_state input_timer_state;
 
@@ -637,13 +683,9 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num)
                goto out;
 
        sample.jiffies = jiffies;
-
-       /* Use arch random value, fall back to cycles */
-       if (!arch_get_random_int(&sample.cycles))
-               sample.cycles = get_cycles();
-
+       sample.cycles = get_cycles();
        sample.num = num;
-       mix_pool_bytes(&input_pool, &sample, sizeof(sample));
+       mix_pool_bytes(&input_pool, &sample, sizeof(sample), NULL);
 
        /*
         * Calculate number of bits of randomness we probably added.
@@ -700,17 +742,48 @@ void add_input_randomness(unsigned int type, unsigned int code,
 }
 EXPORT_SYMBOL_GPL(add_input_randomness);
 
-void add_interrupt_randomness(int irq)
+static DEFINE_PER_CPU(struct fast_pool, irq_randomness);
+
+void add_interrupt_randomness(int irq, int irq_flags)
 {
-       struct timer_rand_state *state;
+       struct entropy_store    *r;
+       struct fast_pool        *fast_pool = &__get_cpu_var(irq_randomness);
+       struct pt_regs          *regs = get_irq_regs();
+       unsigned long           now = jiffies;
+       __u32                   input[4], cycles = get_cycles();
+
+       input[0] = cycles ^ jiffies;
+       input[1] = irq;
+       if (regs) {
+               __u64 ip = instruction_pointer(regs);
+               input[2] = ip;
+               input[3] = ip >> 32;
+       }
 
-       state = get_timer_rand_state(irq);
+       fast_mix(fast_pool, input, sizeof(input));
 
-       if (state == NULL)
+       if ((fast_pool->count & 1023) &&
+           !time_after(now, fast_pool->last + HZ))
                return;
 
-       DEBUG_ENT("irq event %d\n", irq);
-       add_timer_randomness(state, 0x100 + irq);
+       fast_pool->last = now;
+
+       r = nonblocking_pool.initialized ? &input_pool : &nonblocking_pool;
+       __mix_pool_bytes(r, &fast_pool->pool, sizeof(fast_pool->pool), NULL);
+       /*
+        * If we don't have a valid cycle counter, and we see
+        * back-to-back timer interrupts, then skip giving credit for
+        * any entropy.
+        */
+       if (cycles == 0) {
+               if (irq_flags & __IRQF_TIMER) {
+                       if (fast_pool->last_timer_intr)
+                               return;
+                       fast_pool->last_timer_intr = 1;
+               } else
+                       fast_pool->last_timer_intr = 0;
+       }
+       credit_entropy_bits(r, 1);
 }
 
 #ifdef CONFIG_BLOCK
@@ -742,7 +815,7 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
  */
 static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes)
 {
-       __u32 tmp[OUTPUT_POOL_WORDS];
+       __u32   tmp[OUTPUT_POOL_WORDS];
 
        if (r->pull && r->entropy_count < nbytes * 8 &&
            r->entropy_count < r->poolinfo->POOLBITS) {
@@ -761,7 +834,7 @@ static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes)
 
                bytes = extract_entropy(r->pull, tmp, bytes,
                                        random_read_wakeup_thresh / 8, rsvd);
-               mix_pool_bytes(r, tmp, bytes);
+               mix_pool_bytes(r, tmp, bytes, NULL);
                credit_entropy_bits(r, bytes*8);
        }
 }
@@ -820,13 +893,19 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min,
 static void extract_buf(struct entropy_store *r, __u8 *out)
 {
        int i;
-       __u32 hash[5], workspace[SHA_WORKSPACE_WORDS];
+       union {
+               __u32 w[5];
+               unsigned long l[LONGS(EXTRACT_SIZE)];
+       } hash;
+       __u32 workspace[SHA_WORKSPACE_WORDS];
        __u8 extract[64];
+       unsigned long flags;
 
        /* Generate a hash across the pool, 16 words (512 bits) at a time */
-       sha_init(hash);
+       sha_init(hash.w);
+       spin_lock_irqsave(&r->lock, flags);
        for (i = 0; i < r->poolinfo->poolwords; i += 16)
-               sha_transform(hash, (__u8 *)(r->pool + i), workspace);
+               sha_transform(hash.w, (__u8 *)(r->pool + i), workspace);
 
        /*
         * We mix the hash back into the pool to prevent backtracking
@@ -837,13 +916,14 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
         * brute-forcing the feedback as hard as brute-forcing the
         * hash.
         */
-       mix_pool_bytes_extract(r, hash, sizeof(hash), extract);
+       __mix_pool_bytes(r, hash.w, sizeof(hash.w), extract);
+       spin_unlock_irqrestore(&r->lock, flags);
 
        /*
         * To avoid duplicates, we atomically extract a portion of the
         * pool while mixing, and hash one final time.
         */
-       sha_transform(hash, extract, workspace);
+       sha_transform(hash.w, extract, workspace);
        memset(extract, 0, sizeof(extract));
        memset(workspace, 0, sizeof(workspace));
 
@@ -852,20 +932,32 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
         * pattern, we fold it in half. Thus, we always feed back
         * twice as much data as we output.
         */
-       hash[0] ^= hash[3];
-       hash[1] ^= hash[4];
-       hash[2] ^= rol32(hash[2], 16);
-       memcpy(out, hash, EXTRACT_SIZE);
-       memset(hash, 0, sizeof(hash));
+       hash.w[0] ^= hash.w[3];
+       hash.w[1] ^= hash.w[4];
+       hash.w[2] ^= rol32(hash.w[2], 16);
+
+       /*
+        * If we have a architectural hardware random number
+        * generator, mix that in, too.
+        */
+       for (i = 0; i < LONGS(EXTRACT_SIZE); i++) {
+               unsigned long v;
+               if (!arch_get_random_long(&v))
+                       break;
+               hash.l[i] ^= v;
+       }
+
+       memcpy(out, &hash, EXTRACT_SIZE);
+       memset(&hash, 0, sizeof(hash));
 }
 
 static ssize_t extract_entropy(struct entropy_store *r, void *buf,
-                              size_t nbytes, int min, int reserved)
+                                size_t nbytes, int min, int reserved)
 {
        ssize_t ret = 0, i;
        __u8 tmp[EXTRACT_SIZE];
-       unsigned long flags;
 
+       trace_extract_entropy(r->name, nbytes, r->entropy_count, _RET_IP_);
        xfer_secondary_pool(r, nbytes);
        nbytes = account(r, nbytes, min, reserved);
 
@@ -873,6 +965,8 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
                extract_buf(r, tmp);
 
                if (fips_enabled) {
+                       unsigned long flags;
+
                        spin_lock_irqsave(&r->lock, flags);
                        if (!memcmp(tmp, r->last_data, EXTRACT_SIZE))
                                panic("Hardware RNG duplicated output!\n");
@@ -898,6 +992,7 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
        ssize_t ret = 0, i;
        __u8 tmp[EXTRACT_SIZE];
 
+       trace_extract_entropy_user(r->name, nbytes, r->entropy_count, _RET_IP_);
        xfer_secondary_pool(r, nbytes);
        nbytes = account(r, nbytes, 0, 0);
 
@@ -931,17 +1026,35 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
 
 /*
  * This function is the exported kernel interface.  It returns some
- * number of good random numbers, suitable for seeding TCP sequence
- * numbers, etc.
+ * number of good random numbers, suitable for key generation, seeding
+ * TCP sequence numbers, etc.  It does not use the hw random number
+ * generator, if available; use get_random_bytes_arch() for that.
  */
 void get_random_bytes(void *buf, int nbytes)
+{
+       extract_entropy(&nonblocking_pool, buf, nbytes, 0, 0);
+}
+EXPORT_SYMBOL(get_random_bytes);
+
+/*
+ * This function will use the architecture-specific hardware random
+ * number generator if it is available.  The arch-specific hw RNG will
+ * almost certainly be faster than what we can do in software, but it
+ * is impossible to verify that it is implemented securely (as
+ * opposed, to, say, the AES encryption of a sequence number using a
+ * key known by the NSA).  So it's useful if we need the speed, but
+ * only if we're willing to trust the hardware manufacturer not to
+ * have put in a back door.
+ */
+void get_random_bytes_arch(void *buf, int nbytes)
 {
        char *p = buf;
 
+       trace_get_random_bytes(nbytes, _RET_IP_);
        while (nbytes) {
                unsigned long v;
                int chunk = min(nbytes, (int)sizeof(unsigned long));
-               
+
                if (!arch_get_random_long(&v))
                        break;
                
@@ -950,9 +1063,11 @@ void get_random_bytes(void *buf, int nbytes)
                nbytes -= chunk;
        }
 
-       extract_entropy(&nonblocking_pool, p, nbytes, 0, 0);
+       if (nbytes)
+               extract_entropy(&nonblocking_pool, p, nbytes, 0, 0);
 }
-EXPORT_SYMBOL(get_random_bytes);
+EXPORT_SYMBOL(get_random_bytes_arch);
+
 
 /*
  * init_std_data - initialize pool with system data
@@ -966,23 +1081,30 @@ EXPORT_SYMBOL(get_random_bytes);
 static void init_std_data(struct entropy_store *r)
 {
        int i;
-       ktime_t now;
-       unsigned long flags;
+       ktime_t now = ktime_get_real();
+       unsigned long rv;
 
-       spin_lock_irqsave(&r->lock, flags);
        r->entropy_count = 0;
-       spin_unlock_irqrestore(&r->lock, flags);
-
-       now = ktime_get_real();
-       mix_pool_bytes(r, &now, sizeof(now));
-       for (i = r->poolinfo->POOLBYTES; i > 0; i -= sizeof flags) {
-               if (!arch_get_random_long(&flags))
+       r->entropy_total = 0;
+       mix_pool_bytes(r, &now, sizeof(now), NULL);
+       for (i = r->poolinfo->POOLBYTES; i > 0; i -= sizeof(rv)) {
+               if (!arch_get_random_long(&rv))
                        break;
-               mix_pool_bytes(r, &flags, sizeof(flags));
+               mix_pool_bytes(r, &rv, sizeof(rv), NULL);
        }
-       mix_pool_bytes(r, utsname(), sizeof(*(utsname())));
+       mix_pool_bytes(r, utsname(), sizeof(*(utsname())), NULL);
 }
 
+/*
+ * Note that setup_arch() may call add_device_randomness()
+ * long before we get here. This allows seeding of the pools
+ * with some platform dependent data very early in the boot
+ * process. But it limits our options here. We must use
+ * statically allocated structures that already have all
+ * initializations complete at compile time. We should also
+ * take care not to overwrite the precious per platform data
+ * we were given.
+ */
 static int rand_initialize(void)
 {
        init_std_data(&input_pool);
@@ -992,24 +1114,6 @@ static int rand_initialize(void)
 }
 module_init(rand_initialize);
 
-void rand_initialize_irq(int irq)
-{
-       struct timer_rand_state *state;
-
-       state = get_timer_rand_state(irq);
-
-       if (state)
-               return;
-
-       /*
-        * If kzalloc returns null, we just won't use that entropy
-        * source.
-        */
-       state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
-       if (state)
-               set_timer_rand_state(irq, state);
-}
-
 #ifdef CONFIG_BLOCK
 void rand_initialize_disk(struct gendisk *disk)
 {
@@ -1117,7 +1221,7 @@ write_pool(struct entropy_store *r, const char __user *buffer, size_t count)
                count -= bytes;
                p += bytes;
 
-               mix_pool_bytes(r, buf, bytes);
+               mix_pool_bytes(r, buf, bytes, NULL);
                cond_resched();
        }
 
@@ -1279,6 +1383,7 @@ static int proc_do_uuid(ctl_table *table, int write,
 }
 
 static int sysctl_poolsize = INPUT_POOL_WORDS * 32;
+extern ctl_table random_table[];
 ctl_table random_table[] = {
        {
                .procname       = "poolsize",
@@ -1344,7 +1449,7 @@ late_initcall(random_int_secret_init);
  * value is not cryptographically secure but for several uses the cost of
  * depleting entropy is too high
  */
-DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash);
+static DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash);
 unsigned int get_random_int(void)
 {
        __u32 *hash;
index 3f99b9099658ebe70a76a10f14af31716e4987c9..7f0b5ca785160733839e6652cf7d67b410fe32e9 100644 (file)
@@ -25,7 +25,6 @@ menu "Common Clock Framework"
 
 config COMMON_CLK_DEBUG
        bool "DebugFS representation of clock tree"
-       depends on COMMON_CLK
        select DEBUG_FS
        ---help---
          Creates a directory hierchy in debugfs for visualizing the clk
index c87fdd7105609d7bddd836dd9fa2e640753bb22a..efdfd009c2701a40b18a7ec8025ce7500fb98c53 100644 (file)
@@ -465,6 +465,9 @@ static void __clk_disable(struct clk *clk)
        if (!clk)
                return;
 
+       if (WARN_ON(IS_ERR(clk)))
+               return;
+
        if (WARN_ON(clk->enable_count == 0))
                return;
 
index a88331644ebfc7fcf3be31478e7a47ec148cb4a3..e64c253cb1698d456ba853bb8c2ce4f03b2016cd 100644 (file)
@@ -65,20 +65,20 @@ static unsigned int clkdiv_cpu0_5250[CPUFREQ_LEVEL_END][8] = {
         * Clock divider value for following
         * { ARM, CPUD, ACP, PERIPH, ATB, PCLK_DBG, APLL, ARM2 }
         */
-       { 0, 3, 7, 7, 6, 1, 3, 0 },     /* 1700 MHz - N/A */
-       { 0, 3, 7, 7, 6, 1, 3, 0 },     /* 1600 MHz - N/A */
-       { 0, 3, 7, 7, 5, 1, 3, 0 },     /* 1500 MHz - N/A */
-       { 0, 3, 7, 7, 6, 1, 3, 0 },     /* 1400 MHz */
-       { 0, 3, 7, 7, 6, 1, 3, 0 },     /* 1300 MHz */
-       { 0, 3, 7, 7, 5, 1, 3, 0 },     /* 1200 MHz */
-       { 0, 2, 7, 7, 5, 1, 2, 0 },     /* 1100 MHz */
-       { 0, 2, 7, 7, 4, 1, 2, 0 },     /* 1000 MHz */
-       { 0, 2, 7, 7, 4, 1, 2, 0 },     /* 900 MHz */
-       { 0, 2, 7, 7, 3, 1, 1, 0 },     /* 800 MHz */
+       { 0, 3, 7, 7, 7, 3, 5, 0 },     /* 1700 MHz */
+       { 0, 3, 7, 7, 7, 1, 4, 0 },     /* 1600 MHz */
+       { 0, 2, 7, 7, 7, 1, 4, 0 },     /* 1500 MHz */
+       { 0, 2, 7, 7, 6, 1, 4, 0 },     /* 1400 MHz */
+       { 0, 2, 7, 7, 6, 1, 3, 0 },     /* 1300 MHz */
+       { 0, 2, 7, 7, 5, 1, 3, 0 },     /* 1200 MHz */
+       { 0, 3, 7, 7, 5, 1, 3, 0 },     /* 1100 MHz */
+       { 0, 1, 7, 7, 4, 1, 2, 0 },     /* 1000 MHz */
+       { 0, 1, 7, 7, 4, 1, 2, 0 },     /* 900 MHz */
+       { 0, 1, 7, 7, 4, 1, 2, 0 },     /* 800 MHz */
        { 0, 1, 7, 7, 3, 1, 1, 0 },     /* 700 MHz */
-       { 0, 1, 7, 7, 2, 1, 1, 0 },     /* 600 MHz */
+       { 0, 1, 7, 7, 3, 1, 1, 0 },     /* 600 MHz */
        { 0, 1, 7, 7, 2, 1, 1, 0 },     /* 500 MHz */
-       { 0, 1, 7, 7, 1, 1, 1, 0 },     /* 400 MHz */
+       { 0, 1, 7, 7, 2, 1, 1, 0 },     /* 400 MHz */
        { 0, 1, 7, 7, 1, 1, 1, 0 },     /* 300 MHz */
        { 0, 1, 7, 7, 1, 1, 1, 0 },     /* 200 MHz */
 };
@@ -87,9 +87,9 @@ static unsigned int clkdiv_cpu1_5250[CPUFREQ_LEVEL_END][2] = {
        /* Clock divider value for following
         * { COPY, HPM }
         */
-       { 0, 2 },       /* 1700 MHz - N/A */
-       { 0, 2 },       /* 1600 MHz - N/A */
-       { 0, 2 },       /* 1500 MHz - N/A */
+       { 0, 2 },       /* 1700 MHz */
+       { 0, 2 },       /* 1600 MHz */
+       { 0, 2 },       /* 1500 MHz */
        { 0, 2 },       /* 1400 MHz */
        { 0, 2 },       /* 1300 MHz */
        { 0, 2 },       /* 1200 MHz */
@@ -106,10 +106,10 @@ static unsigned int clkdiv_cpu1_5250[CPUFREQ_LEVEL_END][2] = {
 };
 
 static unsigned int exynos5_apll_pms_table[CPUFREQ_LEVEL_END] = {
-       (0),                            /* 1700 MHz - N/A */
-       (0),                            /* 1600 MHz - N/A */
-       (0),                            /* 1500 MHz - N/A */
-       (0),                            /* 1400 MHz */
+       ((425 << 16) | (6 << 8) | 0),   /* 1700 MHz */
+       ((200 << 16) | (3 << 8) | 0),   /* 1600 MHz */
+       ((250 << 16) | (4 << 8) | 0),   /* 1500 MHz */
+       ((175 << 16) | (3 << 8) | 0),   /* 1400 MHz */
        ((325 << 16) | (6 << 8) | 0),   /* 1300 MHz */
        ((200 << 16) | (4 << 8) | 0),   /* 1200 MHz */
        ((275 << 16) | (6 << 8) | 0),   /* 1100 MHz */
@@ -126,9 +126,10 @@ static unsigned int exynos5_apll_pms_table[CPUFREQ_LEVEL_END] = {
 
 /* ASV group voltage table */
 static const unsigned int asv_voltage_5250[CPUFREQ_LEVEL_END] = {
-       0, 0, 0, 0, 0, 0, 0,    /* 1700 MHz ~ 1100 MHz Not supported */
-       1175000, 1125000, 1075000, 1050000, 1000000,
-       950000, 925000, 925000, 900000
+       1300000, 1250000, 1225000, 1200000, 1150000,
+       1125000, 1100000, 1075000, 1050000, 1025000,
+       1012500, 1000000,  975000,  950000,  937500,
+       925000
 };
 
 static void set_clkdiv(unsigned int div_index)
@@ -248,15 +249,7 @@ static void __init set_volt_table(void)
 {
        unsigned int i;
 
-       exynos5250_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID;
-       exynos5250_freq_table[L1].frequency = CPUFREQ_ENTRY_INVALID;
-       exynos5250_freq_table[L2].frequency = CPUFREQ_ENTRY_INVALID;
-       exynos5250_freq_table[L3].frequency = CPUFREQ_ENTRY_INVALID;
-       exynos5250_freq_table[L4].frequency = CPUFREQ_ENTRY_INVALID;
-       exynos5250_freq_table[L5].frequency = CPUFREQ_ENTRY_INVALID;
-       exynos5250_freq_table[L6].frequency = CPUFREQ_ENTRY_INVALID;
-
-       max_support_idx = L7;
+       max_support_idx = L0;
 
        for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++)
                exynos5250_volt_table[i] = asv_voltage_5250[i];
index 67b97c5fd85978bb09d4c9ab08ebe3afa86ff695..a8bd0310f8fec7fe5f8f7bceaf1987f7f38bd8dc 100644 (file)
@@ -1610,8 +1610,7 @@ static int spu_map_ino(struct platform_device *dev, struct spu_mdesc_info *ip,
 
        sprintf(p->irq_name, "%s-%d", irq_name, index);
 
-       return request_irq(p->irq, handler, IRQF_SAMPLE_RANDOM,
-                          p->irq_name, p);
+       return request_irq(p->irq, handler, 0, p->irq_name, p);
 }
 
 static struct kmem_cache *queue_cache[2];
index d45cf1bcbde5f6b096d5cf38bf13b9c9ddf23a86..d06ea2950dd9ffb9ad266bcedbb2db934e020376 100644 (file)
@@ -53,6 +53,7 @@ config AMBA_PL08X
        bool "ARM PrimeCell PL080 or PL081 support"
        depends on ARM_AMBA && EXPERIMENTAL
        select DMA_ENGINE
+       select DMA_VIRTUAL_CHANNELS
        help
          Platform has a PL08x DMAC device
          which can provide DMA engine support
@@ -269,6 +270,7 @@ config DMA_SA11X0
        tristate "SA-11x0 DMA support"
        depends on ARCH_SA1100
        select DMA_ENGINE
+       select DMA_VIRTUAL_CHANNELS
        help
          Support the DMA engine found on Intel StrongARM SA-1100 and
          SA-1110 SoCs.  This DMA engine can only be used with on-chip
@@ -284,9 +286,18 @@ config MMP_TDMA
 
          Say Y here if you enabled MMP ADMA, otherwise say N.
 
+config DMA_OMAP
+       tristate "OMAP DMA support"
+       depends on ARCH_OMAP
+       select DMA_ENGINE
+       select DMA_VIRTUAL_CHANNELS
+
 config DMA_ENGINE
        bool
 
+config DMA_VIRTUAL_CHANNELS
+       tristate
+
 comment "DMA Clients"
        depends on DMA_ENGINE
 
index 640356add0a31f0bea96cc0568a2f290e41b0494..4cf6b128ab9a466b8f4c2c5237064f0f8e80846f 100644 (file)
@@ -2,6 +2,7 @@ ccflags-$(CONFIG_DMADEVICES_DEBUG)  := -DDEBUG
 ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG
 
 obj-$(CONFIG_DMA_ENGINE) += dmaengine.o
+obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o
 obj-$(CONFIG_NET_DMA) += iovlock.o
 obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o
 obj-$(CONFIG_DMATEST) += dmatest.o
@@ -30,3 +31,4 @@ obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
 obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
 obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
 obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
+obj-$(CONFIG_DMA_OMAP) += omap-dma.o
index 49ecbbb8932df2ecbc10aa601edd274acc21cf19..6fbeebb9486fdae644a0268393c7650033b856b4 100644 (file)
 #include <asm/hardware/pl080.h>
 
 #include "dmaengine.h"
+#include "virt-dma.h"
 
 #define DRIVER_NAME    "pl08xdmac"
 
 static struct amba_driver pl08x_amba_driver;
+struct pl08x_driver_data;
 
 /**
  * struct vendor_data - vendor-specific config parameters for PL08x derivatives
@@ -118,6 +120,123 @@ struct pl08x_lli {
        u32 cctl;
 };
 
+/**
+ * struct pl08x_bus_data - information of source or destination
+ * busses for a transfer
+ * @addr: current address
+ * @maxwidth: the maximum width of a transfer on this bus
+ * @buswidth: the width of this bus in bytes: 1, 2 or 4
+ */
+struct pl08x_bus_data {
+       dma_addr_t addr;
+       u8 maxwidth;
+       u8 buswidth;
+};
+
+/**
+ * struct pl08x_phy_chan - holder for the physical channels
+ * @id: physical index to this channel
+ * @lock: a lock to use when altering an instance of this struct
+ * @serving: the virtual channel currently being served by this physical
+ * channel
+ * @locked: channel unavailable for the system, e.g. dedicated to secure
+ * world
+ */
+struct pl08x_phy_chan {
+       unsigned int id;
+       void __iomem *base;
+       spinlock_t lock;
+       struct pl08x_dma_chan *serving;
+       bool locked;
+};
+
+/**
+ * struct pl08x_sg - structure containing data per sg
+ * @src_addr: src address of sg
+ * @dst_addr: dst address of sg
+ * @len: transfer len in bytes
+ * @node: node for txd's dsg_list
+ */
+struct pl08x_sg {
+       dma_addr_t src_addr;
+       dma_addr_t dst_addr;
+       size_t len;
+       struct list_head node;
+};
+
+/**
+ * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor
+ * @vd: virtual DMA descriptor
+ * @dsg_list: list of children sg's
+ * @llis_bus: DMA memory address (physical) start for the LLIs
+ * @llis_va: virtual memory address start for the LLIs
+ * @cctl: control reg values for current txd
+ * @ccfg: config reg values for current txd
+ * @done: this marks completed descriptors, which should not have their
+ *   mux released.
+ */
+struct pl08x_txd {
+       struct virt_dma_desc vd;
+       struct list_head dsg_list;
+       dma_addr_t llis_bus;
+       struct pl08x_lli *llis_va;
+       /* Default cctl value for LLIs */
+       u32 cctl;
+       /*
+        * Settings to be put into the physical channel when we
+        * trigger this txd.  Other registers are in llis_va[0].
+        */
+       u32 ccfg;
+       bool done;
+};
+
+/**
+ * struct pl08x_dma_chan_state - holds the PL08x specific virtual channel
+ * states
+ * @PL08X_CHAN_IDLE: the channel is idle
+ * @PL08X_CHAN_RUNNING: the channel has allocated a physical transport
+ * channel and is running a transfer on it
+ * @PL08X_CHAN_PAUSED: the channel has allocated a physical transport
+ * channel, but the transfer is currently paused
+ * @PL08X_CHAN_WAITING: the channel is waiting for a physical transport
+ * channel to become available (only pertains to memcpy channels)
+ */
+enum pl08x_dma_chan_state {
+       PL08X_CHAN_IDLE,
+       PL08X_CHAN_RUNNING,
+       PL08X_CHAN_PAUSED,
+       PL08X_CHAN_WAITING,
+};
+
+/**
+ * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel
+ * @vc: wrappped virtual channel
+ * @phychan: the physical channel utilized by this channel, if there is one
+ * @name: name of channel
+ * @cd: channel platform data
+ * @runtime_addr: address for RX/TX according to the runtime config
+ * @at: active transaction on this channel
+ * @lock: a lock for this channel data
+ * @host: a pointer to the host (internal use)
+ * @state: whether the channel is idle, paused, running etc
+ * @slave: whether this channel is a device (slave) or for memcpy
+ * @signal: the physical DMA request signal which this channel is using
+ * @mux_use: count of descriptors using this DMA request signal setting
+ */
+struct pl08x_dma_chan {
+       struct virt_dma_chan vc;
+       struct pl08x_phy_chan *phychan;
+       const char *name;
+       const struct pl08x_channel_data *cd;
+       struct dma_slave_config cfg;
+       struct pl08x_txd *at;
+       struct pl08x_driver_data *host;
+       enum pl08x_dma_chan_state state;
+       bool slave;
+       int signal;
+       unsigned mux_use;
+};
+
 /**
  * struct pl08x_driver_data - the local state holder for the PL08x
  * @slave: slave engine for this instance
@@ -128,7 +247,6 @@ struct pl08x_lli {
  * @pd: platform data passed in from the platform/machine
  * @phy_chans: array of data for the physical channels
  * @pool: a pool for the LLI descriptors
- * @pool_ctr: counter of LLIs in the pool
  * @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI
  * fetches
  * @mem_buses: set to indicate memory transfers on AHB2.
@@ -143,10 +261,8 @@ struct pl08x_driver_data {
        struct pl08x_platform_data *pd;
        struct pl08x_phy_chan *phy_chans;
        struct dma_pool *pool;
-       int pool_ctr;
        u8 lli_buses;
        u8 mem_buses;
-       spinlock_t lock;
 };
 
 /*
@@ -162,12 +278,51 @@ struct pl08x_driver_data {
 
 static inline struct pl08x_dma_chan *to_pl08x_chan(struct dma_chan *chan)
 {
-       return container_of(chan, struct pl08x_dma_chan, chan);
+       return container_of(chan, struct pl08x_dma_chan, vc.chan);
 }
 
 static inline struct pl08x_txd *to_pl08x_txd(struct dma_async_tx_descriptor *tx)
 {
-       return container_of(tx, struct pl08x_txd, tx);
+       return container_of(tx, struct pl08x_txd, vd.tx);
+}
+
+/*
+ * Mux handling.
+ *
+ * This gives us the DMA request input to the PL08x primecell which the
+ * peripheral described by the channel data will be routed to, possibly
+ * via a board/SoC specific external MUX.  One important point to note
+ * here is that this does not depend on the physical channel.
+ */
+static int pl08x_request_mux(struct pl08x_dma_chan *plchan)
+{
+       const struct pl08x_platform_data *pd = plchan->host->pd;
+       int ret;
+
+       if (plchan->mux_use++ == 0 && pd->get_signal) {
+               ret = pd->get_signal(plchan->cd);
+               if (ret < 0) {
+                       plchan->mux_use = 0;
+                       return ret;
+               }
+
+               plchan->signal = ret;
+       }
+       return 0;
+}
+
+static void pl08x_release_mux(struct pl08x_dma_chan *plchan)
+{
+       const struct pl08x_platform_data *pd = plchan->host->pd;
+
+       if (plchan->signal >= 0) {
+               WARN_ON(plchan->mux_use == 0);
+
+               if (--plchan->mux_use == 0 && pd->put_signal) {
+                       pd->put_signal(plchan->cd, plchan->signal);
+                       plchan->signal = -1;
+               }
+       }
 }
 
 /*
@@ -189,20 +344,25 @@ static int pl08x_phy_channel_busy(struct pl08x_phy_chan *ch)
  * been set when the LLIs were constructed.  Poke them into the hardware
  * and start the transfer.
  */
-static void pl08x_start_txd(struct pl08x_dma_chan *plchan,
-       struct pl08x_txd *txd)
+static void pl08x_start_next_txd(struct pl08x_dma_chan *plchan)
 {
        struct pl08x_driver_data *pl08x = plchan->host;
        struct pl08x_phy_chan *phychan = plchan->phychan;
-       struct pl08x_lli *lli = &txd->llis_va[0];
+       struct virt_dma_desc *vd = vchan_next_desc(&plchan->vc);
+       struct pl08x_txd *txd = to_pl08x_txd(&vd->tx);
+       struct pl08x_lli *lli;
        u32 val;
 
+       list_del(&txd->vd.node);
+
        plchan->at = txd;
 
        /* Wait for channel inactive */
        while (pl08x_phy_channel_busy(phychan))
                cpu_relax();
 
+       lli = &txd->llis_va[0];
+
        dev_vdbg(&pl08x->adev->dev,
                "WRITE channel %d: csrc=0x%08x, cdst=0x%08x, "
                "clli=0x%08x, cctl=0x%08x, ccfg=0x%08x\n",
@@ -311,10 +471,8 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
 {
        struct pl08x_phy_chan *ch;
        struct pl08x_txd *txd;
-       unsigned long flags;
        size_t bytes = 0;
 
-       spin_lock_irqsave(&plchan->lock, flags);
        ch = plchan->phychan;
        txd = plchan->at;
 
@@ -354,18 +512,6 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
                }
        }
 
-       /* Sum up all queued transactions */
-       if (!list_empty(&plchan->pend_list)) {
-               struct pl08x_txd *txdi;
-               list_for_each_entry(txdi, &plchan->pend_list, node) {
-                       struct pl08x_sg *dsg;
-                       list_for_each_entry(dsg, &txd->dsg_list, node)
-                               bytes += dsg->len;
-               }
-       }
-
-       spin_unlock_irqrestore(&plchan->lock, flags);
-
        return bytes;
 }
 
@@ -391,7 +537,6 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x,
 
                if (!ch->locked && !ch->serving) {
                        ch->serving = virt_chan;
-                       ch->signal = -1;
                        spin_unlock_irqrestore(&ch->lock, flags);
                        break;
                }
@@ -404,25 +549,114 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x,
                return NULL;
        }
 
-       pm_runtime_get_sync(&pl08x->adev->dev);
        return ch;
 }
 
+/* Mark the physical channel as free.  Note, this write is atomic. */
 static inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x,
                                         struct pl08x_phy_chan *ch)
 {
-       unsigned long flags;
+       ch->serving = NULL;
+}
+
+/*
+ * Try to allocate a physical channel.  When successful, assign it to
+ * this virtual channel, and initiate the next descriptor.  The
+ * virtual channel lock must be held at this point.
+ */
+static void pl08x_phy_alloc_and_start(struct pl08x_dma_chan *plchan)
+{
+       struct pl08x_driver_data *pl08x = plchan->host;
+       struct pl08x_phy_chan *ch;
 
-       spin_lock_irqsave(&ch->lock, flags);
+       ch = pl08x_get_phy_channel(pl08x, plchan);
+       if (!ch) {
+               dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name);
+               plchan->state = PL08X_CHAN_WAITING;
+               return;
+       }
 
-       /* Stop the channel and clear its interrupts */
-       pl08x_terminate_phy_chan(pl08x, ch);
+       dev_dbg(&pl08x->adev->dev, "allocated physical channel %d for xfer on %s\n",
+               ch->id, plchan->name);
 
-       pm_runtime_put(&pl08x->adev->dev);
+       plchan->phychan = ch;
+       plchan->state = PL08X_CHAN_RUNNING;
+       pl08x_start_next_txd(plchan);
+}
 
-       /* Mark it as free */
-       ch->serving = NULL;
-       spin_unlock_irqrestore(&ch->lock, flags);
+static void pl08x_phy_reassign_start(struct pl08x_phy_chan *ch,
+       struct pl08x_dma_chan *plchan)
+{
+       struct pl08x_driver_data *pl08x = plchan->host;
+
+       dev_dbg(&pl08x->adev->dev, "reassigned physical channel %d for xfer on %s\n",
+               ch->id, plchan->name);
+
+       /*
+        * We do this without taking the lock; we're really only concerned
+        * about whether this pointer is NULL or not, and we're guaranteed
+        * that this will only be called when it _already_ is non-NULL.
+        */
+       ch->serving = plchan;
+       plchan->phychan = ch;
+       plchan->state = PL08X_CHAN_RUNNING;
+       pl08x_start_next_txd(plchan);
+}
+
+/*
+ * Free a physical DMA channel, potentially reallocating it to another
+ * virtual channel if we have any pending.
+ */
+static void pl08x_phy_free(struct pl08x_dma_chan *plchan)
+{
+       struct pl08x_driver_data *pl08x = plchan->host;
+       struct pl08x_dma_chan *p, *next;
+
+ retry:
+       next = NULL;
+
+       /* Find a waiting virtual channel for the next transfer. */
+       list_for_each_entry(p, &pl08x->memcpy.channels, vc.chan.device_node)
+               if (p->state == PL08X_CHAN_WAITING) {
+                       next = p;
+                       break;
+               }
+
+       if (!next) {
+               list_for_each_entry(p, &pl08x->slave.channels, vc.chan.device_node)
+                       if (p->state == PL08X_CHAN_WAITING) {
+                               next = p;
+                               break;
+                       }
+       }
+
+       /* Ensure that the physical channel is stopped */
+       pl08x_terminate_phy_chan(pl08x, plchan->phychan);
+
+       if (next) {
+               bool success;
+
+               /*
+                * Eww.  We know this isn't going to deadlock
+                * but lockdep probably doesn't.
+                */
+               spin_lock(&next->vc.lock);
+               /* Re-check the state now that we have the lock */
+               success = next->state == PL08X_CHAN_WAITING;
+               if (success)
+                       pl08x_phy_reassign_start(plchan->phychan, next);
+               spin_unlock(&next->vc.lock);
+
+               /* If the state changed, try to find another channel */
+               if (!success)
+                       goto retry;
+       } else {
+               /* No more jobs, so free up the physical channel */
+               pl08x_put_phy_channel(pl08x, plchan->phychan);
+       }
+
+       plchan->phychan = NULL;
+       plchan->state = PL08X_CHAN_IDLE;
 }
 
 /*
@@ -585,8 +819,6 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
                return 0;
        }
 
-       pl08x->pool_ctr++;
-
        bd.txd = txd;
        bd.lli_bus = (pl08x->lli_buses & PL08X_AHB2) ? PL080_LLI_LM_AHB2 : 0;
        cctl = txd->cctl;
@@ -802,18 +1034,14 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
        return num_llis;
 }
 
-/* You should call this with the struct pl08x lock held */
 static void pl08x_free_txd(struct pl08x_driver_data *pl08x,
                           struct pl08x_txd *txd)
 {
        struct pl08x_sg *dsg, *_dsg;
 
-       /* Free the LLI */
        if (txd->llis_va)
                dma_pool_free(pl08x->pool, txd->llis_va, txd->llis_bus);
 
-       pl08x->pool_ctr--;
-
        list_for_each_entry_safe(dsg, _dsg, &txd->dsg_list, node) {
                list_del(&dsg->node);
                kfree(dsg);
@@ -822,133 +1050,75 @@ static void pl08x_free_txd(struct pl08x_driver_data *pl08x,
        kfree(txd);
 }
 
-static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x,
-                               struct pl08x_dma_chan *plchan)
+static void pl08x_unmap_buffers(struct pl08x_txd *txd)
 {
-       struct pl08x_txd *txdi = NULL;
-       struct pl08x_txd *next;
-
-       if (!list_empty(&plchan->pend_list)) {
-               list_for_each_entry_safe(txdi,
-                                        next, &plchan->pend_list, node) {
-                       list_del(&txdi->node);
-                       pl08x_free_txd(pl08x, txdi);
+       struct device *dev = txd->vd.tx.chan->device->dev;
+       struct pl08x_sg *dsg;
+
+       if (!(txd->vd.tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
+               if (txd->vd.tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE)
+                       list_for_each_entry(dsg, &txd->dsg_list, node)
+                               dma_unmap_single(dev, dsg->src_addr, dsg->len,
+                                               DMA_TO_DEVICE);
+               else {
+                       list_for_each_entry(dsg, &txd->dsg_list, node)
+                               dma_unmap_page(dev, dsg->src_addr, dsg->len,
+                                               DMA_TO_DEVICE);
                }
        }
+       if (!(txd->vd.tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
+               if (txd->vd.tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE)
+                       list_for_each_entry(dsg, &txd->dsg_list, node)
+                               dma_unmap_single(dev, dsg->dst_addr, dsg->len,
+                                               DMA_FROM_DEVICE);
+               else
+                       list_for_each_entry(dsg, &txd->dsg_list, node)
+                               dma_unmap_page(dev, dsg->dst_addr, dsg->len,
+                                               DMA_FROM_DEVICE);
+       }
 }
 
-/*
- * The DMA ENGINE API
- */
-static int pl08x_alloc_chan_resources(struct dma_chan *chan)
+static void pl08x_desc_free(struct virt_dma_desc *vd)
 {
-       return 0;
-}
+       struct pl08x_txd *txd = to_pl08x_txd(&vd->tx);
+       struct pl08x_dma_chan *plchan = to_pl08x_chan(vd->tx.chan);
 
-static void pl08x_free_chan_resources(struct dma_chan *chan)
-{
+       if (!plchan->slave)
+               pl08x_unmap_buffers(txd);
+
+       if (!txd->done)
+               pl08x_release_mux(plchan);
+
+       pl08x_free_txd(plchan->host, txd);
 }
 
-/*
- * This should be called with the channel plchan->lock held
- */
-static int prep_phy_channel(struct pl08x_dma_chan *plchan,
-                           struct pl08x_txd *txd)
+static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x,
+                               struct pl08x_dma_chan *plchan)
 {
-       struct pl08x_driver_data *pl08x = plchan->host;
-       struct pl08x_phy_chan *ch;
-       int ret;
-
-       /* Check if we already have a channel */
-       if (plchan->phychan) {
-               ch = plchan->phychan;
-               goto got_channel;
-       }
+       LIST_HEAD(head);
+       struct pl08x_txd *txd;
 
-       ch = pl08x_get_phy_channel(pl08x, plchan);
-       if (!ch) {
-               /* No physical channel available, cope with it */
-               dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name);
-               return -EBUSY;
-       }
+       vchan_get_all_descriptors(&plchan->vc, &head);
 
-       /*
-        * OK we have a physical channel: for memcpy() this is all we
-        * need, but for slaves the physical signals may be muxed!
-        * Can the platform allow us to use this channel?
-        */
-       if (plchan->slave && pl08x->pd->get_signal) {
-               ret = pl08x->pd->get_signal(plchan);
-               if (ret < 0) {
-                       dev_dbg(&pl08x->adev->dev,
-                               "unable to use physical channel %d for transfer on %s due to platform restrictions\n",
-                               ch->id, plchan->name);
-                       /* Release physical channel & return */
-                       pl08x_put_phy_channel(pl08x, ch);
-                       return -EBUSY;
-               }
-               ch->signal = ret;
+       while (!list_empty(&head)) {
+               txd = list_first_entry(&head, struct pl08x_txd, vd.node);
+               list_del(&txd->vd.node);
+               pl08x_desc_free(&txd->vd);
        }
-
-       plchan->phychan = ch;
-       dev_dbg(&pl08x->adev->dev, "allocated physical channel %d and signal %d for xfer on %s\n",
-                ch->id,
-                ch->signal,
-                plchan->name);
-
-got_channel:
-       /* Assign the flow control signal to this channel */
-       if (txd->direction == DMA_MEM_TO_DEV)
-               txd->ccfg |= ch->signal << PL080_CONFIG_DST_SEL_SHIFT;
-       else if (txd->direction == DMA_DEV_TO_MEM)
-               txd->ccfg |= ch->signal << PL080_CONFIG_SRC_SEL_SHIFT;
-
-       plchan->phychan_hold++;
-
-       return 0;
 }
 
-static void release_phy_channel(struct pl08x_dma_chan *plchan)
+/*
+ * The DMA ENGINE API
+ */
+static int pl08x_alloc_chan_resources(struct dma_chan *chan)
 {
-       struct pl08x_driver_data *pl08x = plchan->host;
-
-       if ((plchan->phychan->signal >= 0) && pl08x->pd->put_signal) {
-               pl08x->pd->put_signal(plchan);
-               plchan->phychan->signal = -1;
-       }
-       pl08x_put_phy_channel(pl08x, plchan->phychan);
-       plchan->phychan = NULL;
+       return 0;
 }
 
-static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx)
+static void pl08x_free_chan_resources(struct dma_chan *chan)
 {
-       struct pl08x_dma_chan *plchan = to_pl08x_chan(tx->chan);
-       struct pl08x_txd *txd = to_pl08x_txd(tx);
-       unsigned long flags;
-       dma_cookie_t cookie;
-
-       spin_lock_irqsave(&plchan->lock, flags);
-       cookie = dma_cookie_assign(tx);
-
-       /* Put this onto the pending list */
-       list_add_tail(&txd->node, &plchan->pend_list);
-
-       /*
-        * If there was no physical channel available for this memcpy,
-        * stack the request up and indicate that the channel is waiting
-        * for a free physical channel.
-        */
-       if (!plchan->slave && !plchan->phychan) {
-               /* Do this memcpy whenever there is a channel ready */
-               plchan->state = PL08X_CHAN_WAITING;
-               plchan->waiting = txd;
-       } else {
-               plchan->phychan_hold--;
-       }
-
-       spin_unlock_irqrestore(&plchan->lock, flags);
-
-       return cookie;
+       /* Ensure all queued descriptors are freed */
+       vchan_free_chan_resources(to_virt_chan(chan));
 }
 
 static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt(
@@ -968,23 +1138,53 @@ static enum dma_status pl08x_dma_tx_status(struct dma_chan *chan,
                dma_cookie_t cookie, struct dma_tx_state *txstate)
 {
        struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
+       struct virt_dma_desc *vd;
+       unsigned long flags;
        enum dma_status ret;
+       size_t bytes = 0;
 
        ret = dma_cookie_status(chan, cookie, txstate);
        if (ret == DMA_SUCCESS)
                return ret;
 
+       /*
+        * There's no point calculating the residue if there's
+        * no txstate to store the value.
+        */
+       if (!txstate) {
+               if (plchan->state == PL08X_CHAN_PAUSED)
+                       ret = DMA_PAUSED;
+               return ret;
+       }
+
+       spin_lock_irqsave(&plchan->vc.lock, flags);
+       ret = dma_cookie_status(chan, cookie, txstate);
+       if (ret != DMA_SUCCESS) {
+               vd = vchan_find_desc(&plchan->vc, cookie);
+               if (vd) {
+                       /* On the issued list, so hasn't been processed yet */
+                       struct pl08x_txd *txd = to_pl08x_txd(&vd->tx);
+                       struct pl08x_sg *dsg;
+
+                       list_for_each_entry(dsg, &txd->dsg_list, node)
+                               bytes += dsg->len;
+               } else {
+                       bytes = pl08x_getbytes_chan(plchan);
+               }
+       }
+       spin_unlock_irqrestore(&plchan->vc.lock, flags);
+
        /*
         * This cookie not complete yet
         * Get number of bytes left in the active transactions and queue
         */
-       dma_set_residue(txstate, pl08x_getbytes_chan(plchan));
+       dma_set_residue(txstate, bytes);
 
-       if (plchan->state == PL08X_CHAN_PAUSED)
-               return DMA_PAUSED;
+       if (plchan->state == PL08X_CHAN_PAUSED && ret == DMA_IN_PROGRESS)
+               ret = DMA_PAUSED;
 
        /* Whether waiting or running, we're in progress */
-       return DMA_IN_PROGRESS;
+       return ret;
 }
 
 /* PrimeCell DMA extension */
@@ -1080,38 +1280,14 @@ static u32 pl08x_burst(u32 maxburst)
        return burst_sizes[i].reg;
 }
 
-static int dma_set_runtime_config(struct dma_chan *chan,
-                                 struct dma_slave_config *config)
+static u32 pl08x_get_cctl(struct pl08x_dma_chan *plchan,
+       enum dma_slave_buswidth addr_width, u32 maxburst)
 {
-       struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
-       struct pl08x_driver_data *pl08x = plchan->host;
-       enum dma_slave_buswidth addr_width;
-       u32 width, burst, maxburst;
-       u32 cctl = 0;
-
-       if (!plchan->slave)
-               return -EINVAL;
-
-       /* Transfer direction */
-       plchan->runtime_direction = config->direction;
-       if (config->direction == DMA_MEM_TO_DEV) {
-               addr_width = config->dst_addr_width;
-               maxburst = config->dst_maxburst;
-       } else if (config->direction == DMA_DEV_TO_MEM) {
-               addr_width = config->src_addr_width;
-               maxburst = config->src_maxburst;
-       } else {
-               dev_err(&pl08x->adev->dev,
-                       "bad runtime_config: alien transfer direction\n");
-               return -EINVAL;
-       }
+       u32 width, burst, cctl = 0;
 
        width = pl08x_width(addr_width);
-       if (width == ~0) {
-               dev_err(&pl08x->adev->dev,
-                       "bad runtime_config: alien address width\n");
-               return -EINVAL;
-       }
+       if (width == ~0)
+               return ~0;
 
        cctl |= width << PL080_CONTROL_SWIDTH_SHIFT;
        cctl |= width << PL080_CONTROL_DWIDTH_SHIFT;
@@ -1128,28 +1304,23 @@ static int dma_set_runtime_config(struct dma_chan *chan,
        cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT;
        cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT;
 
-       plchan->device_fc = config->device_fc;
+       return pl08x_cctl(cctl);
+}
 
-       if (plchan->runtime_direction == DMA_DEV_TO_MEM) {
-               plchan->src_addr = config->src_addr;
-               plchan->src_cctl = pl08x_cctl(cctl) | PL080_CONTROL_DST_INCR |
-                       pl08x_select_bus(plchan->cd->periph_buses,
-                                        pl08x->mem_buses);
-       } else {
-               plchan->dst_addr = config->dst_addr;
-               plchan->dst_cctl = pl08x_cctl(cctl) | PL080_CONTROL_SRC_INCR |
-                       pl08x_select_bus(pl08x->mem_buses,
-                                        plchan->cd->periph_buses);
-       }
+static int dma_set_runtime_config(struct dma_chan *chan,
+                                 struct dma_slave_config *config)
+{
+       struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
 
-       dev_dbg(&pl08x->adev->dev,
-               "configured channel %s (%s) for %s, data width %d, "
-               "maxburst %d words, LE, CCTL=0x%08x\n",
-               dma_chan_name(chan), plchan->name,
-               (config->direction == DMA_DEV_TO_MEM) ? "RX" : "TX",
-               addr_width,
-               maxburst,
-               cctl);
+       if (!plchan->slave)
+               return -EINVAL;
+
+       /* Reject definitely invalid configurations */
+       if (config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
+           config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
+               return -EINVAL;
+
+       plchan->cfg = *config;
 
        return 0;
 }
@@ -1163,95 +1334,19 @@ static void pl08x_issue_pending(struct dma_chan *chan)
        struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
        unsigned long flags;
 
-       spin_lock_irqsave(&plchan->lock, flags);
-       /* Something is already active, or we're waiting for a channel... */
-       if (plchan->at || plchan->state == PL08X_CHAN_WAITING) {
-               spin_unlock_irqrestore(&plchan->lock, flags);
-               return;
-       }
-
-       /* Take the first element in the queue and execute it */
-       if (!list_empty(&plchan->pend_list)) {
-               struct pl08x_txd *next;
-
-               next = list_first_entry(&plchan->pend_list,
-                                       struct pl08x_txd,
-                                       node);
-               list_del(&next->node);
-               plchan->state = PL08X_CHAN_RUNNING;
-
-               pl08x_start_txd(plchan, next);
+       spin_lock_irqsave(&plchan->vc.lock, flags);
+       if (vchan_issue_pending(&plchan->vc)) {
+               if (!plchan->phychan && plchan->state != PL08X_CHAN_WAITING)
+                       pl08x_phy_alloc_and_start(plchan);
        }
-
-       spin_unlock_irqrestore(&plchan->lock, flags);
-}
-
-static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
-                                       struct pl08x_txd *txd)
-{
-       struct pl08x_driver_data *pl08x = plchan->host;
-       unsigned long flags;
-       int num_llis, ret;
-
-       num_llis = pl08x_fill_llis_for_desc(pl08x, txd);
-       if (!num_llis) {
-               spin_lock_irqsave(&plchan->lock, flags);
-               pl08x_free_txd(pl08x, txd);
-               spin_unlock_irqrestore(&plchan->lock, flags);
-               return -EINVAL;
-       }
-
-       spin_lock_irqsave(&plchan->lock, flags);
-
-       /*
-        * See if we already have a physical channel allocated,
-        * else this is the time to try to get one.
-        */
-       ret = prep_phy_channel(plchan, txd);
-       if (ret) {
-               /*
-                * No physical channel was available.
-                *
-                * memcpy transfers can be sorted out at submission time.
-                *
-                * Slave transfers may have been denied due to platform
-                * channel muxing restrictions.  Since there is no guarantee
-                * that this will ever be resolved, and the signal must be
-                * acquired AFTER acquiring the physical channel, we will let
-                * them be NACK:ed with -EBUSY here. The drivers can retry
-                * the prep() call if they are eager on doing this using DMA.
-                */
-               if (plchan->slave) {
-                       pl08x_free_txd_list(pl08x, plchan);
-                       pl08x_free_txd(pl08x, txd);
-                       spin_unlock_irqrestore(&plchan->lock, flags);
-                       return -EBUSY;
-               }
-       } else
-               /*
-                * Else we're all set, paused and ready to roll, status
-                * will switch to PL08X_CHAN_RUNNING when we call
-                * issue_pending(). If there is something running on the
-                * channel already we don't change its state.
-                */
-               if (plchan->state == PL08X_CHAN_IDLE)
-                       plchan->state = PL08X_CHAN_PAUSED;
-
-       spin_unlock_irqrestore(&plchan->lock, flags);
-
-       return 0;
+       spin_unlock_irqrestore(&plchan->vc.lock, flags);
 }
 
-static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan,
-       unsigned long flags)
+static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan)
 {
        struct pl08x_txd *txd = kzalloc(sizeof(*txd), GFP_NOWAIT);
 
        if (txd) {
-               dma_async_tx_descriptor_init(&txd->tx, &plchan->chan);
-               txd->tx.flags = flags;
-               txd->tx.tx_submit = pl08x_tx_submit;
-               INIT_LIST_HEAD(&txd->node);
                INIT_LIST_HEAD(&txd->dsg_list);
 
                /* Always enable error and terminal interrupts */
@@ -1274,7 +1369,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
        struct pl08x_sg *dsg;
        int ret;
 
-       txd = pl08x_get_txd(plchan, flags);
+       txd = pl08x_get_txd(plchan);
        if (!txd) {
                dev_err(&pl08x->adev->dev,
                        "%s no memory for descriptor\n", __func__);
@@ -1290,14 +1385,13 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
        }
        list_add_tail(&dsg->node, &txd->dsg_list);
 
-       txd->direction = DMA_NONE;
        dsg->src_addr = src;
        dsg->dst_addr = dest;
        dsg->len = len;
 
        /* Set platform data for m2m */
        txd->ccfg |= PL080_FLOW_MEM2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
-       txd->cctl = pl08x->pd->memcpy_channel.cctl &
+       txd->cctl = pl08x->pd->memcpy_channel.cctl_memcpy &
                        ~(PL080_CONTROL_DST_AHB2 | PL080_CONTROL_SRC_AHB2);
 
        /* Both to be incremented or the code will break */
@@ -1307,11 +1401,13 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
                txd->cctl |= pl08x_select_bus(pl08x->mem_buses,
                                              pl08x->mem_buses);
 
-       ret = pl08x_prep_channel_resources(plchan, txd);
-       if (ret)
+       ret = pl08x_fill_llis_for_desc(plchan->host, txd);
+       if (!ret) {
+               pl08x_free_txd(pl08x, txd);
                return NULL;
+       }
 
-       return &txd->tx;
+       return vchan_tx_prep(&plchan->vc, &txd->vd, flags);
 }
 
 static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
@@ -1324,36 +1420,40 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
        struct pl08x_txd *txd;
        struct pl08x_sg *dsg;
        struct scatterlist *sg;
+       enum dma_slave_buswidth addr_width;
        dma_addr_t slave_addr;
        int ret, tmp;
+       u8 src_buses, dst_buses;
+       u32 maxburst, cctl;
 
        dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n",
                        __func__, sg_dma_len(sgl), plchan->name);
 
-       txd = pl08x_get_txd(plchan, flags);
+       txd = pl08x_get_txd(plchan);
        if (!txd) {
                dev_err(&pl08x->adev->dev, "%s no txd\n", __func__);
                return NULL;
        }
 
-       if (direction != plchan->runtime_direction)
-               dev_err(&pl08x->adev->dev, "%s DMA setup does not match "
-                       "the direction configured for the PrimeCell\n",
-                       __func__);
-
        /*
         * Set up addresses, the PrimeCell configured address
         * will take precedence since this may configure the
         * channel target address dynamically at runtime.
         */
-       txd->direction = direction;
-
        if (direction == DMA_MEM_TO_DEV) {
-               txd->cctl = plchan->dst_cctl;
-               slave_addr = plchan->dst_addr;
+               cctl = PL080_CONTROL_SRC_INCR;
+               slave_addr = plchan->cfg.dst_addr;
+               addr_width = plchan->cfg.dst_addr_width;
+               maxburst = plchan->cfg.dst_maxburst;
+               src_buses = pl08x->mem_buses;
+               dst_buses = plchan->cd->periph_buses;
        } else if (direction == DMA_DEV_TO_MEM) {
-               txd->cctl = plchan->src_cctl;
-               slave_addr = plchan->src_addr;
+               cctl = PL080_CONTROL_DST_INCR;
+               slave_addr = plchan->cfg.src_addr;
+               addr_width = plchan->cfg.src_addr_width;
+               maxburst = plchan->cfg.src_maxburst;
+               src_buses = plchan->cd->periph_buses;
+               dst_buses = pl08x->mem_buses;
        } else {
                pl08x_free_txd(pl08x, txd);
                dev_err(&pl08x->adev->dev,
@@ -1361,7 +1461,17 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
                return NULL;
        }
 
-       if (plchan->device_fc)
+       cctl |= pl08x_get_cctl(plchan, addr_width, maxburst);
+       if (cctl == ~0) {
+               pl08x_free_txd(pl08x, txd);
+               dev_err(&pl08x->adev->dev,
+                       "DMA slave configuration botched?\n");
+               return NULL;
+       }
+
+       txd->cctl = cctl | pl08x_select_bus(src_buses, dst_buses);
+
+       if (plchan->cfg.device_fc)
                tmp = (direction == DMA_MEM_TO_DEV) ? PL080_FLOW_MEM2PER_PER :
                        PL080_FLOW_PER2MEM_PER;
        else
@@ -1370,9 +1480,28 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
 
        txd->ccfg |= tmp << PL080_CONFIG_FLOW_CONTROL_SHIFT;
 
+       ret = pl08x_request_mux(plchan);
+       if (ret < 0) {
+               pl08x_free_txd(pl08x, txd);
+               dev_dbg(&pl08x->adev->dev,
+                       "unable to mux for transfer on %s due to platform restrictions\n",
+                       plchan->name);
+               return NULL;
+       }
+
+       dev_dbg(&pl08x->adev->dev, "allocated DMA request signal %d for xfer on %s\n",
+                plchan->signal, plchan->name);
+
+       /* Assign the flow control signal to this channel */
+       if (direction == DMA_MEM_TO_DEV)
+               txd->ccfg |= plchan->signal << PL080_CONFIG_DST_SEL_SHIFT;
+       else
+               txd->ccfg |= plchan->signal << PL080_CONFIG_SRC_SEL_SHIFT;
+
        for_each_sg(sgl, sg, sg_len, tmp) {
                dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT);
                if (!dsg) {
+                       pl08x_release_mux(plchan);
                        pl08x_free_txd(pl08x, txd);
                        dev_err(&pl08x->adev->dev, "%s no mem for pl080 sg\n",
                                        __func__);
@@ -1390,11 +1519,14 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
                }
        }
 
-       ret = pl08x_prep_channel_resources(plchan, txd);
-       if (ret)
+       ret = pl08x_fill_llis_for_desc(plchan->host, txd);
+       if (!ret) {
+               pl08x_release_mux(plchan);
+               pl08x_free_txd(pl08x, txd);
                return NULL;
+       }
 
-       return &txd->tx;
+       return vchan_tx_prep(&plchan->vc, &txd->vd, flags);
 }
 
 static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
@@ -1415,9 +1547,9 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
         * Anything succeeds on channels with no physical allocation and
         * no queued transfers.
         */
-       spin_lock_irqsave(&plchan->lock, flags);
+       spin_lock_irqsave(&plchan->vc.lock, flags);
        if (!plchan->phychan && !plchan->at) {
-               spin_unlock_irqrestore(&plchan->lock, flags);
+               spin_unlock_irqrestore(&plchan->vc.lock, flags);
                return 0;
        }
 
@@ -1426,18 +1558,15 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
                plchan->state = PL08X_CHAN_IDLE;
 
                if (plchan->phychan) {
-                       pl08x_terminate_phy_chan(pl08x, plchan->phychan);
-
                        /*
                         * Mark physical channel as free and free any slave
                         * signal
                         */
-                       release_phy_channel(plchan);
-                       plchan->phychan_hold = 0;
+                       pl08x_phy_free(plchan);
                }
                /* Dequeue jobs and free LLIs */
                if (plchan->at) {
-                       pl08x_free_txd(pl08x, plchan->at);
+                       pl08x_desc_free(&plchan->at->vd);
                        plchan->at = NULL;
                }
                /* Dequeue jobs not yet fired as well */
@@ -1457,7 +1586,7 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
                break;
        }
 
-       spin_unlock_irqrestore(&plchan->lock, flags);
+       spin_unlock_irqrestore(&plchan->vc.lock, flags);
 
        return ret;
 }
@@ -1494,123 +1623,6 @@ static void pl08x_ensure_on(struct pl08x_driver_data *pl08x)
        writel(PL080_CONFIG_ENABLE, pl08x->base + PL080_CONFIG);
 }
 
-static void pl08x_unmap_buffers(struct pl08x_txd *txd)
-{
-       struct device *dev = txd->tx.chan->device->dev;
-       struct pl08x_sg *dsg;
-
-       if (!(txd->tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-               if (txd->tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE)
-                       list_for_each_entry(dsg, &txd->dsg_list, node)
-                               dma_unmap_single(dev, dsg->src_addr, dsg->len,
-                                               DMA_TO_DEVICE);
-               else {
-                       list_for_each_entry(dsg, &txd->dsg_list, node)
-                               dma_unmap_page(dev, dsg->src_addr, dsg->len,
-                                               DMA_TO_DEVICE);
-               }
-       }
-       if (!(txd->tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
-               if (txd->tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE)
-                       list_for_each_entry(dsg, &txd->dsg_list, node)
-                               dma_unmap_single(dev, dsg->dst_addr, dsg->len,
-                                               DMA_FROM_DEVICE);
-               else
-                       list_for_each_entry(dsg, &txd->dsg_list, node)
-                               dma_unmap_page(dev, dsg->dst_addr, dsg->len,
-                                               DMA_FROM_DEVICE);
-       }
-}
-
-static void pl08x_tasklet(unsigned long data)
-{
-       struct pl08x_dma_chan *plchan = (struct pl08x_dma_chan *) data;
-       struct pl08x_driver_data *pl08x = plchan->host;
-       struct pl08x_txd *txd;
-       unsigned long flags;
-
-       spin_lock_irqsave(&plchan->lock, flags);
-
-       txd = plchan->at;
-       plchan->at = NULL;
-
-       if (txd) {
-               /* Update last completed */
-               dma_cookie_complete(&txd->tx);
-       }
-
-       /* If a new descriptor is queued, set it up plchan->at is NULL here */
-       if (!list_empty(&plchan->pend_list)) {
-               struct pl08x_txd *next;
-
-               next = list_first_entry(&plchan->pend_list,
-                                       struct pl08x_txd,
-                                       node);
-               list_del(&next->node);
-
-               pl08x_start_txd(plchan, next);
-       } else if (plchan->phychan_hold) {
-               /*
-                * This channel is still in use - we have a new txd being
-                * prepared and will soon be queued.  Don't give up the
-                * physical channel.
-                */
-       } else {
-               struct pl08x_dma_chan *waiting = NULL;
-
-               /*
-                * No more jobs, so free up the physical channel
-                * Free any allocated signal on slave transfers too
-                */
-               release_phy_channel(plchan);
-               plchan->state = PL08X_CHAN_IDLE;
-
-               /*
-                * And NOW before anyone else can grab that free:d up
-                * physical channel, see if there is some memcpy pending
-                * that seriously needs to start because of being stacked
-                * up while we were choking the physical channels with data.
-                */
-               list_for_each_entry(waiting, &pl08x->memcpy.channels,
-                                   chan.device_node) {
-                       if (waiting->state == PL08X_CHAN_WAITING &&
-                               waiting->waiting != NULL) {
-                               int ret;
-
-                               /* This should REALLY not fail now */
-                               ret = prep_phy_channel(waiting,
-                                                      waiting->waiting);
-                               BUG_ON(ret);
-                               waiting->phychan_hold--;
-                               waiting->state = PL08X_CHAN_RUNNING;
-                               waiting->waiting = NULL;
-                               pl08x_issue_pending(&waiting->chan);
-                               break;
-                       }
-               }
-       }
-
-       spin_unlock_irqrestore(&plchan->lock, flags);
-
-       if (txd) {
-               dma_async_tx_callback callback = txd->tx.callback;
-               void *callback_param = txd->tx.callback_param;
-
-               /* Don't try to unmap buffers on slave channels */
-               if (!plchan->slave)
-                       pl08x_unmap_buffers(txd);
-
-               /* Free the descriptor */
-               spin_lock_irqsave(&plchan->lock, flags);
-               pl08x_free_txd(pl08x, txd);
-               spin_unlock_irqrestore(&plchan->lock, flags);
-
-               /* Callback to signal completion */
-               if (callback)
-                       callback(callback_param);
-       }
-}
-
 static irqreturn_t pl08x_irq(int irq, void *dev)
 {
        struct pl08x_driver_data *pl08x = dev;
@@ -1635,6 +1647,7 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
                        /* Locate physical channel */
                        struct pl08x_phy_chan *phychan = &pl08x->phy_chans[i];
                        struct pl08x_dma_chan *plchan = phychan->serving;
+                       struct pl08x_txd *tx;
 
                        if (!plchan) {
                                dev_err(&pl08x->adev->dev,
@@ -1643,8 +1656,29 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
                                continue;
                        }
 
-                       /* Schedule tasklet on this channel */
-                       tasklet_schedule(&plchan->tasklet);
+                       spin_lock(&plchan->vc.lock);
+                       tx = plchan->at;
+                       if (tx) {
+                               plchan->at = NULL;
+                               /*
+                                * This descriptor is done, release its mux
+                                * reservation.
+                                */
+                               pl08x_release_mux(plchan);
+                               tx->done = true;
+                               vchan_cookie_complete(&tx->vd);
+
+                               /*
+                                * And start the next descriptor (if any),
+                                * otherwise free this channel.
+                                */
+                               if (vchan_next_desc(&plchan->vc))
+                                       pl08x_start_next_txd(plchan);
+                               else
+                                       pl08x_phy_free(plchan);
+                       }
+                       spin_unlock(&plchan->vc.lock);
+
                        mask |= (1 << i);
                }
        }
@@ -1654,16 +1688,10 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
 
 static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan)
 {
-       u32 cctl = pl08x_cctl(chan->cd->cctl);
-
        chan->slave = true;
        chan->name = chan->cd->bus_id;
-       chan->src_addr = chan->cd->addr;
-       chan->dst_addr = chan->cd->addr;
-       chan->src_cctl = cctl | PL080_CONTROL_DST_INCR |
-               pl08x_select_bus(chan->cd->periph_buses, chan->host->mem_buses);
-       chan->dst_cctl = cctl | PL080_CONTROL_SRC_INCR |
-               pl08x_select_bus(chan->host->mem_buses, chan->cd->periph_buses);
+       chan->cfg.src_addr = chan->cd->addr;
+       chan->cfg.dst_addr = chan->cd->addr;
 }
 
 /*
@@ -1693,6 +1721,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
 
                chan->host = pl08x;
                chan->state = PL08X_CHAN_IDLE;
+               chan->signal = -1;
 
                if (slave) {
                        chan->cd = &pl08x->pd->slave_channels[i];
@@ -1705,26 +1734,12 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
                                return -ENOMEM;
                        }
                }
-               if (chan->cd->circular_buffer) {
-                       dev_err(&pl08x->adev->dev,
-                               "channel %s: circular buffers not supported\n",
-                               chan->name);
-                       kfree(chan);
-                       continue;
-               }
                dev_dbg(&pl08x->adev->dev,
                         "initialize virtual channel \"%s\"\n",
                         chan->name);
 
-               chan->chan.device = dmadev;
-               dma_cookie_init(&chan->chan);
-
-               spin_lock_init(&chan->lock);
-               INIT_LIST_HEAD(&chan->pend_list);
-               tasklet_init(&chan->tasklet, pl08x_tasklet,
-                            (unsigned long) chan);
-
-               list_add_tail(&chan->chan.device_node, &dmadev->channels);
+               chan->vc.desc_free = pl08x_desc_free;
+               vchan_init(&chan->vc, dmadev);
        }
        dev_info(&pl08x->adev->dev, "initialized %d virtual %s channels\n",
                 i, slave ? "slave" : "memcpy");
@@ -1737,8 +1752,8 @@ static void pl08x_free_virtual_channels(struct dma_device *dmadev)
        struct pl08x_dma_chan *next;
 
        list_for_each_entry_safe(chan,
-                                next, &dmadev->channels, chan.device_node) {
-               list_del(&chan->chan.device_node);
+                                next, &dmadev->channels, vc.chan.device_node) {
+               list_del(&chan->vc.chan.device_node);
                kfree(chan);
        }
 }
@@ -1791,7 +1806,7 @@ static int pl08x_debugfs_show(struct seq_file *s, void *data)
        seq_printf(s, "\nPL08x virtual memcpy channels:\n");
        seq_printf(s, "CHANNEL:\tSTATE:\n");
        seq_printf(s, "--------\t------\n");
-       list_for_each_entry(chan, &pl08x->memcpy.channels, chan.device_node) {
+       list_for_each_entry(chan, &pl08x->memcpy.channels, vc.chan.device_node) {
                seq_printf(s, "%s\t\t%s\n", chan->name,
                           pl08x_state_str(chan->state));
        }
@@ -1799,7 +1814,7 @@ static int pl08x_debugfs_show(struct seq_file *s, void *data)
        seq_printf(s, "\nPL08x virtual slave channels:\n");
        seq_printf(s, "CHANNEL:\tSTATE:\n");
        seq_printf(s, "--------\t------\n");
-       list_for_each_entry(chan, &pl08x->slave.channels, chan.device_node) {
+       list_for_each_entry(chan, &pl08x->slave.channels, vc.chan.device_node) {
                seq_printf(s, "%s\t\t%s\n", chan->name,
                           pl08x_state_str(chan->state));
        }
@@ -1851,9 +1866,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
                goto out_no_pl08x;
        }
 
-       pm_runtime_set_active(&adev->dev);
-       pm_runtime_enable(&adev->dev);
-
        /* Initialize memcpy engine */
        dma_cap_set(DMA_MEMCPY, pl08x->memcpy.cap_mask);
        pl08x->memcpy.dev = &adev->dev;
@@ -1903,8 +1915,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
                goto out_no_lli_pool;
        }
 
-       spin_lock_init(&pl08x->lock);
-
        pl08x->base = ioremap(adev->res.start, resource_size(&adev->res));
        if (!pl08x->base) {
                ret = -ENOMEM;
@@ -1942,7 +1952,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
                ch->id = i;
                ch->base = pl08x->base + PL080_Cx_BASE(i);
                spin_lock_init(&ch->lock);
-               ch->signal = -1;
 
                /*
                 * Nomadik variants can have channels that are locked
@@ -2007,7 +2016,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
                 amba_part(adev), amba_rev(adev),
                 (unsigned long long)adev->res.start, adev->irq[0]);
 
-       pm_runtime_put(&adev->dev);
        return 0;
 
 out_no_slave_reg:
@@ -2026,9 +2034,6 @@ out_no_ioremap:
        dma_pool_destroy(pl08x->pool);
 out_no_lli_pool:
 out_no_platdata:
-       pm_runtime_put(&adev->dev);
-       pm_runtime_disable(&adev->dev);
-
        kfree(pl08x);
 out_no_pl08x:
        amba_release_regions(adev);
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
new file mode 100644 (file)
index 0000000..ae05618
--- /dev/null
@@ -0,0 +1,669 @@
+/*
+ * OMAP DMAengine support
+ *
+ * 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/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/omap-dma.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+#include <plat/dma.h>
+
+struct omap_dmadev {
+       struct dma_device ddev;
+       spinlock_t lock;
+       struct tasklet_struct task;
+       struct list_head pending;
+};
+
+struct omap_chan {
+       struct virt_dma_chan vc;
+       struct list_head node;
+
+       struct dma_slave_config cfg;
+       unsigned dma_sig;
+       bool cyclic;
+
+       int dma_ch;
+       struct omap_desc *desc;
+       unsigned sgidx;
+};
+
+struct omap_sg {
+       dma_addr_t addr;
+       uint32_t en;            /* number of elements (24-bit) */
+       uint32_t fn;            /* number of frames (16-bit) */
+};
+
+struct omap_desc {
+       struct virt_dma_desc vd;
+       enum dma_transfer_direction dir;
+       dma_addr_t dev_addr;
+
+       int16_t fi;             /* for OMAP_DMA_SYNC_PACKET */
+       uint8_t es;             /* OMAP_DMA_DATA_TYPE_xxx */
+       uint8_t sync_mode;      /* OMAP_DMA_SYNC_xxx */
+       uint8_t sync_type;      /* OMAP_DMA_xxx_SYNC* */
+       uint8_t periph_port;    /* Peripheral port */
+
+       unsigned sglen;
+       struct omap_sg sg[0];
+};
+
+static const unsigned es_bytes[] = {
+       [OMAP_DMA_DATA_TYPE_S8] = 1,
+       [OMAP_DMA_DATA_TYPE_S16] = 2,
+       [OMAP_DMA_DATA_TYPE_S32] = 4,
+};
+
+static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d)
+{
+       return container_of(d, struct omap_dmadev, ddev);
+}
+
+static inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c)
+{
+       return container_of(c, struct omap_chan, vc.chan);
+}
+
+static inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t)
+{
+       return container_of(t, struct omap_desc, vd.tx);
+}
+
+static void omap_dma_desc_free(struct virt_dma_desc *vd)
+{
+       kfree(container_of(vd, struct omap_desc, vd));
+}
+
+static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
+       unsigned idx)
+{
+       struct omap_sg *sg = d->sg + idx;
+
+       if (d->dir == DMA_DEV_TO_MEM)
+               omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+                       OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
+       else
+               omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+                       OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
+
+       omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
+               d->sync_mode, c->dma_sig, d->sync_type);
+
+       omap_start_dma(c->dma_ch);
+}
+
+static void omap_dma_start_desc(struct omap_chan *c)
+{
+       struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
+       struct omap_desc *d;
+
+       if (!vd) {
+               c->desc = NULL;
+               return;
+       }
+
+       list_del(&vd->node);
+
+       c->desc = d = to_omap_dma_desc(&vd->tx);
+       c->sgidx = 0;
+
+       if (d->dir == DMA_DEV_TO_MEM)
+               omap_set_dma_src_params(c->dma_ch, d->periph_port,
+                       OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, d->fi);
+       else
+               omap_set_dma_dest_params(c->dma_ch, d->periph_port,
+                       OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, d->fi);
+
+       omap_dma_start_sg(c, d, 0);
+}
+
+static void omap_dma_callback(int ch, u16 status, void *data)
+{
+       struct omap_chan *c = data;
+       struct omap_desc *d;
+       unsigned long flags;
+
+       spin_lock_irqsave(&c->vc.lock, flags);
+       d = c->desc;
+       if (d) {
+               if (!c->cyclic) {
+                       if (++c->sgidx < d->sglen) {
+                               omap_dma_start_sg(c, d, c->sgidx);
+                       } else {
+                               omap_dma_start_desc(c);
+                               vchan_cookie_complete(&d->vd);
+                       }
+               } else {
+                       vchan_cyclic_callback(&d->vd);
+               }
+       }
+       spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+/*
+ * This callback schedules all pending channels.  We could be more
+ * clever here by postponing allocation of the real DMA channels to
+ * this point, and freeing them when our virtual channel becomes idle.
+ *
+ * We would then need to deal with 'all channels in-use'
+ */
+static void omap_dma_sched(unsigned long data)
+{
+       struct omap_dmadev *d = (struct omap_dmadev *)data;
+       LIST_HEAD(head);
+
+       spin_lock_irq(&d->lock);
+       list_splice_tail_init(&d->pending, &head);
+       spin_unlock_irq(&d->lock);
+
+       while (!list_empty(&head)) {
+               struct omap_chan *c = list_first_entry(&head,
+                       struct omap_chan, node);
+
+               spin_lock_irq(&c->vc.lock);
+               list_del_init(&c->node);
+               omap_dma_start_desc(c);
+               spin_unlock_irq(&c->vc.lock);
+       }
+}
+
+static int omap_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+       struct omap_chan *c = to_omap_dma_chan(chan);
+
+       dev_info(c->vc.chan.device->dev, "allocating channel for %u\n", c->dma_sig);
+
+       return omap_request_dma(c->dma_sig, "DMA engine",
+               omap_dma_callback, c, &c->dma_ch);
+}
+
+static void omap_dma_free_chan_resources(struct dma_chan *chan)
+{
+       struct omap_chan *c = to_omap_dma_chan(chan);
+
+       vchan_free_chan_resources(&c->vc);
+       omap_free_dma(c->dma_ch);
+
+       dev_info(c->vc.chan.device->dev, "freeing channel for %u\n", c->dma_sig);
+}
+
+static size_t omap_dma_sg_size(struct omap_sg *sg)
+{
+       return sg->en * sg->fn;
+}
+
+static size_t omap_dma_desc_size(struct omap_desc *d)
+{
+       unsigned i;
+       size_t size;
+
+       for (size = i = 0; i < d->sglen; i++)
+               size += omap_dma_sg_size(&d->sg[i]);
+
+       return size * es_bytes[d->es];
+}
+
+static size_t omap_dma_desc_size_pos(struct omap_desc *d, dma_addr_t addr)
+{
+       unsigned i;
+       size_t size, es_size = es_bytes[d->es];
+
+       for (size = i = 0; i < d->sglen; i++) {
+               size_t this_size = omap_dma_sg_size(&d->sg[i]) * es_size;
+
+               if (size)
+                       size += this_size;
+               else if (addr >= d->sg[i].addr &&
+                        addr < d->sg[i].addr + this_size)
+                       size += d->sg[i].addr + this_size - addr;
+       }
+       return size;
+}
+
+static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
+       dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+       struct omap_chan *c = to_omap_dma_chan(chan);
+       struct virt_dma_desc *vd;
+       enum dma_status ret;
+       unsigned long flags;
+
+       ret = dma_cookie_status(chan, cookie, txstate);
+       if (ret == DMA_SUCCESS || !txstate)
+               return ret;
+
+       spin_lock_irqsave(&c->vc.lock, flags);
+       vd = vchan_find_desc(&c->vc, cookie);
+       if (vd) {
+               txstate->residue = omap_dma_desc_size(to_omap_dma_desc(&vd->tx));
+       } else if (c->desc && c->desc->vd.tx.cookie == cookie) {
+               struct omap_desc *d = c->desc;
+               dma_addr_t pos;
+
+               if (d->dir == DMA_MEM_TO_DEV)
+                       pos = omap_get_dma_src_pos(c->dma_ch);
+               else if (d->dir == DMA_DEV_TO_MEM)
+                       pos = omap_get_dma_dst_pos(c->dma_ch);
+               else
+                       pos = 0;
+
+               txstate->residue = omap_dma_desc_size_pos(d, pos);
+       } else {
+               txstate->residue = 0;
+       }
+       spin_unlock_irqrestore(&c->vc.lock, flags);
+
+       return ret;
+}
+
+static void omap_dma_issue_pending(struct dma_chan *chan)
+{
+       struct omap_chan *c = to_omap_dma_chan(chan);
+       unsigned long flags;
+
+       spin_lock_irqsave(&c->vc.lock, flags);
+       if (vchan_issue_pending(&c->vc) && !c->desc) {
+               struct omap_dmadev *d = to_omap_dma_dev(chan->device);
+               spin_lock(&d->lock);
+               if (list_empty(&c->node))
+                       list_add_tail(&c->node, &d->pending);
+               spin_unlock(&d->lock);
+               tasklet_schedule(&d->task);
+       }
+       spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
+       struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen,
+       enum dma_transfer_direction dir, unsigned long tx_flags, void *context)
+{
+       struct omap_chan *c = to_omap_dma_chan(chan);
+       enum dma_slave_buswidth dev_width;
+       struct scatterlist *sgent;
+       struct omap_desc *d;
+       dma_addr_t dev_addr;
+       unsigned i, j = 0, es, en, frame_bytes, sync_type;
+       u32 burst;
+
+       if (dir == DMA_DEV_TO_MEM) {
+               dev_addr = c->cfg.src_addr;
+               dev_width = c->cfg.src_addr_width;
+               burst = c->cfg.src_maxburst;
+               sync_type = OMAP_DMA_SRC_SYNC;
+       } else if (dir == DMA_MEM_TO_DEV) {
+               dev_addr = c->cfg.dst_addr;
+               dev_width = c->cfg.dst_addr_width;
+               burst = c->cfg.dst_maxburst;
+               sync_type = OMAP_DMA_DST_SYNC;
+       } else {
+               dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
+               return NULL;
+       }
+
+       /* Bus width translates to the element size (ES) */
+       switch (dev_width) {
+       case DMA_SLAVE_BUSWIDTH_1_BYTE:
+               es = OMAP_DMA_DATA_TYPE_S8;
+               break;
+       case DMA_SLAVE_BUSWIDTH_2_BYTES:
+               es = OMAP_DMA_DATA_TYPE_S16;
+               break;
+       case DMA_SLAVE_BUSWIDTH_4_BYTES:
+               es = OMAP_DMA_DATA_TYPE_S32;
+               break;
+       default: /* not reached */
+               return NULL;
+       }
+
+       /* Now allocate and setup the descriptor. */
+       d = kzalloc(sizeof(*d) + sglen * sizeof(d->sg[0]), GFP_ATOMIC);
+       if (!d)
+               return NULL;
+
+       d->dir = dir;
+       d->dev_addr = dev_addr;
+       d->es = es;
+       d->sync_mode = OMAP_DMA_SYNC_FRAME;
+       d->sync_type = sync_type;
+       d->periph_port = OMAP_DMA_PORT_TIPB;
+
+       /*
+        * Build our scatterlist entries: each contains the address,
+        * the number of elements (EN) in each frame, and the number of
+        * frames (FN).  Number of bytes for this entry = ES * EN * FN.
+        *
+        * Burst size translates to number of elements with frame sync.
+        * Note: DMA engine defines burst to be the number of dev-width
+        * transfers.
+        */
+       en = burst;
+       frame_bytes = es_bytes[es] * en;
+       for_each_sg(sgl, sgent, sglen, i) {
+               d->sg[j].addr = sg_dma_address(sgent);
+               d->sg[j].en = en;
+               d->sg[j].fn = sg_dma_len(sgent) / frame_bytes;
+               j++;
+       }
+
+       d->sglen = j;
+
+       return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
+}
+
+static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
+       struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
+       size_t period_len, enum dma_transfer_direction dir, void *context)
+{
+       struct omap_chan *c = to_omap_dma_chan(chan);
+       enum dma_slave_buswidth dev_width;
+       struct omap_desc *d;
+       dma_addr_t dev_addr;
+       unsigned es, sync_type;
+       u32 burst;
+
+       if (dir == DMA_DEV_TO_MEM) {
+               dev_addr = c->cfg.src_addr;
+               dev_width = c->cfg.src_addr_width;
+               burst = c->cfg.src_maxburst;
+               sync_type = OMAP_DMA_SRC_SYNC;
+       } else if (dir == DMA_MEM_TO_DEV) {
+               dev_addr = c->cfg.dst_addr;
+               dev_width = c->cfg.dst_addr_width;
+               burst = c->cfg.dst_maxburst;
+               sync_type = OMAP_DMA_DST_SYNC;
+       } else {
+               dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
+               return NULL;
+       }
+
+       /* Bus width translates to the element size (ES) */
+       switch (dev_width) {
+       case DMA_SLAVE_BUSWIDTH_1_BYTE:
+               es = OMAP_DMA_DATA_TYPE_S8;
+               break;
+       case DMA_SLAVE_BUSWIDTH_2_BYTES:
+               es = OMAP_DMA_DATA_TYPE_S16;
+               break;
+       case DMA_SLAVE_BUSWIDTH_4_BYTES:
+               es = OMAP_DMA_DATA_TYPE_S32;
+               break;
+       default: /* not reached */
+               return NULL;
+       }
+
+       /* Now allocate and setup the descriptor. */
+       d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC);
+       if (!d)
+               return NULL;
+
+       d->dir = dir;
+       d->dev_addr = dev_addr;
+       d->fi = burst;
+       d->es = es;
+       d->sync_mode = OMAP_DMA_SYNC_PACKET;
+       d->sync_type = sync_type;
+       d->periph_port = OMAP_DMA_PORT_MPUI;
+       d->sg[0].addr = buf_addr;
+       d->sg[0].en = period_len / es_bytes[es];
+       d->sg[0].fn = buf_len / period_len;
+       d->sglen = 1;
+
+       if (!c->cyclic) {
+               c->cyclic = true;
+               omap_dma_link_lch(c->dma_ch, c->dma_ch);
+               omap_enable_dma_irq(c->dma_ch, OMAP_DMA_FRAME_IRQ);
+               omap_disable_dma_irq(c->dma_ch, OMAP_DMA_BLOCK_IRQ);
+       }
+
+       if (!cpu_class_is_omap1()) {
+               omap_set_dma_src_burst_mode(c->dma_ch, OMAP_DMA_DATA_BURST_16);
+               omap_set_dma_dest_burst_mode(c->dma_ch, OMAP_DMA_DATA_BURST_16);
+       }
+
+       return vchan_tx_prep(&c->vc, &d->vd, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+}
+
+static int omap_dma_slave_config(struct omap_chan *c, struct dma_slave_config *cfg)
+{
+       if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
+           cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
+               return -EINVAL;
+
+       memcpy(&c->cfg, cfg, sizeof(c->cfg));
+
+       return 0;
+}
+
+static int omap_dma_terminate_all(struct omap_chan *c)
+{
+       struct omap_dmadev *d = to_omap_dma_dev(c->vc.chan.device);
+       unsigned long flags;
+       LIST_HEAD(head);
+
+       spin_lock_irqsave(&c->vc.lock, flags);
+
+       /* Prevent this channel being scheduled */
+       spin_lock(&d->lock);
+       list_del_init(&c->node);
+       spin_unlock(&d->lock);
+
+       /*
+        * Stop DMA activity: we assume the callback will not be called
+        * after omap_stop_dma() returns (even if it does, it will see
+        * c->desc is NULL and exit.)
+        */
+       if (c->desc) {
+               c->desc = NULL;
+               omap_stop_dma(c->dma_ch);
+       }
+
+       if (c->cyclic) {
+               c->cyclic = false;
+               omap_dma_unlink_lch(c->dma_ch, c->dma_ch);
+       }
+
+       vchan_get_all_descriptors(&c->vc, &head);
+       spin_unlock_irqrestore(&c->vc.lock, flags);
+       vchan_dma_desc_free_list(&c->vc, &head);
+
+       return 0;
+}
+
+static int omap_dma_pause(struct omap_chan *c)
+{
+       /* FIXME: not supported by platform private API */
+       return -EINVAL;
+}
+
+static int omap_dma_resume(struct omap_chan *c)
+{
+       /* FIXME: not supported by platform private API */
+       return -EINVAL;
+}
+
+static int omap_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+       unsigned long arg)
+{
+       struct omap_chan *c = to_omap_dma_chan(chan);
+       int ret;
+
+       switch (cmd) {
+       case DMA_SLAVE_CONFIG:
+               ret = omap_dma_slave_config(c, (struct dma_slave_config *)arg);
+               break;
+
+       case DMA_TERMINATE_ALL:
+               ret = omap_dma_terminate_all(c);
+               break;
+
+       case DMA_PAUSE:
+               ret = omap_dma_pause(c);
+               break;
+
+       case DMA_RESUME:
+               ret = omap_dma_resume(c);
+               break;
+
+       default:
+               ret = -ENXIO;
+               break;
+       }
+
+       return ret;
+}
+
+static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig)
+{
+       struct omap_chan *c;
+
+       c = kzalloc(sizeof(*c), GFP_KERNEL);
+       if (!c)
+               return -ENOMEM;
+
+       c->dma_sig = dma_sig;
+       c->vc.desc_free = omap_dma_desc_free;
+       vchan_init(&c->vc, &od->ddev);
+       INIT_LIST_HEAD(&c->node);
+
+       od->ddev.chancnt++;
+
+       return 0;
+}
+
+static void omap_dma_free(struct omap_dmadev *od)
+{
+       tasklet_kill(&od->task);
+       while (!list_empty(&od->ddev.channels)) {
+               struct omap_chan *c = list_first_entry(&od->ddev.channels,
+                       struct omap_chan, vc.chan.device_node);
+
+               list_del(&c->vc.chan.device_node);
+               tasklet_kill(&c->vc.task);
+               kfree(c);
+       }
+       kfree(od);
+}
+
+static int omap_dma_probe(struct platform_device *pdev)
+{
+       struct omap_dmadev *od;
+       int rc, i;
+
+       od = kzalloc(sizeof(*od), GFP_KERNEL);
+       if (!od)
+               return -ENOMEM;
+
+       dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
+       dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask);
+       od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources;
+       od->ddev.device_free_chan_resources = omap_dma_free_chan_resources;
+       od->ddev.device_tx_status = omap_dma_tx_status;
+       od->ddev.device_issue_pending = omap_dma_issue_pending;
+       od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg;
+       od->ddev.device_prep_dma_cyclic = omap_dma_prep_dma_cyclic;
+       od->ddev.device_control = omap_dma_control;
+       od->ddev.dev = &pdev->dev;
+       INIT_LIST_HEAD(&od->ddev.channels);
+       INIT_LIST_HEAD(&od->pending);
+       spin_lock_init(&od->lock);
+
+       tasklet_init(&od->task, omap_dma_sched, (unsigned long)od);
+
+       for (i = 0; i < 127; i++) {
+               rc = omap_dma_chan_init(od, i);
+               if (rc) {
+                       omap_dma_free(od);
+                       return rc;
+               }
+       }
+
+       rc = dma_async_device_register(&od->ddev);
+       if (rc) {
+               pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
+                       rc);
+               omap_dma_free(od);
+       } else {
+               platform_set_drvdata(pdev, od);
+       }
+
+       dev_info(&pdev->dev, "OMAP DMA engine driver\n");
+
+       return rc;
+}
+
+static int omap_dma_remove(struct platform_device *pdev)
+{
+       struct omap_dmadev *od = platform_get_drvdata(pdev);
+
+       dma_async_device_unregister(&od->ddev);
+       omap_dma_free(od);
+
+       return 0;
+}
+
+static struct platform_driver omap_dma_driver = {
+       .probe  = omap_dma_probe,
+       .remove = omap_dma_remove,
+       .driver = {
+               .name = "omap-dma-engine",
+               .owner = THIS_MODULE,
+       },
+};
+
+bool omap_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+       if (chan->device->dev->driver == &omap_dma_driver.driver) {
+               struct omap_chan *c = to_omap_dma_chan(chan);
+               unsigned req = *(unsigned *)param;
+
+               return req == c->dma_sig;
+       }
+       return false;
+}
+EXPORT_SYMBOL_GPL(omap_dma_filter_fn);
+
+static struct platform_device *pdev;
+
+static const struct platform_device_info omap_dma_dev_info = {
+       .name = "omap-dma-engine",
+       .id = -1,
+       .dma_mask = DMA_BIT_MASK(32),
+};
+
+static int omap_dma_init(void)
+{
+       int rc = platform_driver_register(&omap_dma_driver);
+
+       if (rc == 0) {
+               pdev = platform_device_register_full(&omap_dma_dev_info);
+               if (IS_ERR(pdev)) {
+                       platform_driver_unregister(&omap_dma_driver);
+                       rc = PTR_ERR(pdev);
+               }
+       }
+       return rc;
+}
+subsys_initcall(omap_dma_init);
+
+static void __exit omap_dma_exit(void)
+{
+       platform_device_unregister(pdev);
+       platform_driver_unregister(&omap_dma_driver);
+}
+module_exit(omap_dma_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_LICENSE("GPL");
index ec78ccef91325c46dfb4098433da5de42007cc32..f5a73606217ee6118f9562e5f49b34338691611e 100644 (file)
@@ -21,6 +21,8 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 
+#include "virt-dma.h"
+
 #define NR_PHY_CHAN    6
 #define DMA_ALIGN      3
 #define DMA_MAX_SIZE   0x1fff
@@ -72,12 +74,13 @@ struct sa11x0_dma_sg {
 };
 
 struct sa11x0_dma_desc {
-       struct dma_async_tx_descriptor tx;
+       struct virt_dma_desc    vd;
+
        u32                     ddar;
        size_t                  size;
+       unsigned                period;
+       bool                    cyclic;
 
-       /* maybe protected by c->lock */
-       struct list_head        node;
        unsigned                sglen;
        struct sa11x0_dma_sg    sg[0];
 };
@@ -85,15 +88,11 @@ struct sa11x0_dma_desc {
 struct sa11x0_dma_phy;
 
 struct sa11x0_dma_chan {
-       struct dma_chan         chan;
-       spinlock_t              lock;
-       dma_cookie_t            lc;
+       struct virt_dma_chan    vc;
 
-       /* protected by c->lock */
+       /* protected by c->vc.lock */
        struct sa11x0_dma_phy   *phy;
        enum dma_status         status;
-       struct list_head        desc_submitted;
-       struct list_head        desc_issued;
 
        /* protected by d->lock */
        struct list_head        node;
@@ -109,7 +108,7 @@ struct sa11x0_dma_phy {
 
        struct sa11x0_dma_chan  *vchan;
 
-       /* Protected by c->lock */
+       /* Protected by c->vc.lock */
        unsigned                sg_load;
        struct sa11x0_dma_desc  *txd_load;
        unsigned                sg_done;
@@ -127,13 +126,12 @@ struct sa11x0_dma_dev {
        spinlock_t              lock;
        struct tasklet_struct   task;
        struct list_head        chan_pending;
-       struct list_head        desc_complete;
        struct sa11x0_dma_phy   phy[NR_PHY_CHAN];
 };
 
 static struct sa11x0_dma_chan *to_sa11x0_dma_chan(struct dma_chan *chan)
 {
-       return container_of(chan, struct sa11x0_dma_chan, chan);
+       return container_of(chan, struct sa11x0_dma_chan, vc.chan);
 }
 
 static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev)
@@ -141,27 +139,26 @@ static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev)
        return container_of(dmadev, struct sa11x0_dma_dev, slave);
 }
 
-static struct sa11x0_dma_desc *to_sa11x0_dma_tx(struct dma_async_tx_descriptor *tx)
+static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c)
 {
-       return container_of(tx, struct sa11x0_dma_desc, tx);
+       struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
+
+       return vd ? container_of(vd, struct sa11x0_dma_desc, vd) : NULL;
 }
 
-static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c)
+static void sa11x0_dma_free_desc(struct virt_dma_desc *vd)
 {
-       if (list_empty(&c->desc_issued))
-               return NULL;
-
-       return list_first_entry(&c->desc_issued, struct sa11x0_dma_desc, node);
+       kfree(container_of(vd, struct sa11x0_dma_desc, vd));
 }
 
 static void sa11x0_dma_start_desc(struct sa11x0_dma_phy *p, struct sa11x0_dma_desc *txd)
 {
-       list_del(&txd->node);
+       list_del(&txd->vd.node);
        p->txd_load = txd;
        p->sg_load = 0;
 
        dev_vdbg(p->dev->slave.dev, "pchan %u: txd %p[%x]: starting: DDAR:%x\n",
-               p->num, txd, txd->tx.cookie, txd->ddar);
+               p->num, &txd->vd, txd->vd.tx.cookie, txd->ddar);
 }
 
 static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p,
@@ -183,19 +180,24 @@ static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p,
                return;
 
        if (p->sg_load == txd->sglen) {
-               struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c);
+               if (!txd->cyclic) {
+                       struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c);
 
-               /*
-                * We have reached the end of the current descriptor.
-                * Peek at the next descriptor, and if compatible with
-                * the current, start processing it.
-                */
-               if (txn && txn->ddar == txd->ddar) {
-                       txd = txn;
-                       sa11x0_dma_start_desc(p, txn);
+                       /*
+                        * We have reached the end of the current descriptor.
+                        * Peek at the next descriptor, and if compatible with
+                        * the current, start processing it.
+                        */
+                       if (txn && txn->ddar == txd->ddar) {
+                               txd = txn;
+                               sa11x0_dma_start_desc(p, txn);
+                       } else {
+                               p->txd_load = NULL;
+                               return;
+                       }
                } else {
-                       p->txd_load = NULL;
-                       return;
+                       /* Cyclic: reset back to beginning */
+                       p->sg_load = 0;
                }
        }
 
@@ -229,21 +231,21 @@ static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p,
        struct sa11x0_dma_desc *txd = p->txd_done;
 
        if (++p->sg_done == txd->sglen) {
-               struct sa11x0_dma_dev *d = p->dev;
-
-               dev_vdbg(d->slave.dev, "pchan %u: txd %p[%x]: completed\n",
-                       p->num, p->txd_done, p->txd_done->tx.cookie);
-
-               c->lc = txd->tx.cookie;
+               if (!txd->cyclic) {
+                       vchan_cookie_complete(&txd->vd);
 
-               spin_lock(&d->lock);
-               list_add_tail(&txd->node, &d->desc_complete);
-               spin_unlock(&d->lock);
+                       p->sg_done = 0;
+                       p->txd_done = p->txd_load;
 
-               p->sg_done = 0;
-               p->txd_done = p->txd_load;
+                       if (!p->txd_done)
+                               tasklet_schedule(&p->dev->task);
+               } else {
+                       if ((p->sg_done % txd->period) == 0)
+                               vchan_cyclic_callback(&txd->vd);
 
-               tasklet_schedule(&d->task);
+                       /* Cyclic: reset back to beginning */
+                       p->sg_done = 0;
+               }
        }
 
        sa11x0_dma_start_sg(p, c);
@@ -280,7 +282,7 @@ static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id)
        if (c) {
                unsigned long flags;
 
-               spin_lock_irqsave(&c->lock, flags);
+               spin_lock_irqsave(&c->vc.lock, flags);
                /*
                 * Now that we're holding the lock, check that the vchan
                 * really is associated with this pchan before touching the
@@ -294,7 +296,7 @@ static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id)
                        if (dcsr & DCSR_DONEB)
                                sa11x0_dma_complete(p, c);
                }
-               spin_unlock_irqrestore(&c->lock, flags);
+               spin_unlock_irqrestore(&c->vc.lock, flags);
        }
 
        return IRQ_HANDLED;
@@ -332,28 +334,15 @@ static void sa11x0_dma_tasklet(unsigned long arg)
        struct sa11x0_dma_dev *d = (struct sa11x0_dma_dev *)arg;
        struct sa11x0_dma_phy *p;
        struct sa11x0_dma_chan *c;
-       struct sa11x0_dma_desc *txd, *txn;
-       LIST_HEAD(head);
        unsigned pch, pch_alloc = 0;
 
        dev_dbg(d->slave.dev, "tasklet enter\n");
 
-       /* Get the completed tx descriptors */
-       spin_lock_irq(&d->lock);
-       list_splice_init(&d->desc_complete, &head);
-       spin_unlock_irq(&d->lock);
-
-       list_for_each_entry(txd, &head, node) {
-               c = to_sa11x0_dma_chan(txd->tx.chan);
-
-               dev_dbg(d->slave.dev, "vchan %p: txd %p[%x] completed\n",
-                       c, txd, txd->tx.cookie);
-
-               spin_lock_irq(&c->lock);
+       list_for_each_entry(c, &d->slave.channels, vc.chan.device_node) {
+               spin_lock_irq(&c->vc.lock);
                p = c->phy;
-               if (p) {
-                       if (!p->txd_done)
-                               sa11x0_dma_start_txd(c);
+               if (p && !p->txd_done) {
+                       sa11x0_dma_start_txd(c);
                        if (!p->txd_done) {
                                /* No current txd associated with this channel */
                                dev_dbg(d->slave.dev, "pchan %u: free\n", p->num);
@@ -363,7 +352,7 @@ static void sa11x0_dma_tasklet(unsigned long arg)
                                p->vchan = NULL;
                        }
                }
-               spin_unlock_irq(&c->lock);
+               spin_unlock_irq(&c->vc.lock);
        }
 
        spin_lock_irq(&d->lock);
@@ -380,7 +369,7 @@ static void sa11x0_dma_tasklet(unsigned long arg)
                        /* Mark this channel allocated */
                        p->vchan = c;
 
-                       dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, c);
+                       dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, &c->vc);
                }
        }
        spin_unlock_irq(&d->lock);
@@ -390,42 +379,18 @@ static void sa11x0_dma_tasklet(unsigned long arg)
                        p = &d->phy[pch];
                        c = p->vchan;
 
-                       spin_lock_irq(&c->lock);
+                       spin_lock_irq(&c->vc.lock);
                        c->phy = p;
 
                        sa11x0_dma_start_txd(c);
-                       spin_unlock_irq(&c->lock);
+                       spin_unlock_irq(&c->vc.lock);
                }
        }
 
-       /* Now free the completed tx descriptor, and call their callbacks */
-       list_for_each_entry_safe(txd, txn, &head, node) {
-               dma_async_tx_callback callback = txd->tx.callback;
-               void *callback_param = txd->tx.callback_param;
-
-               dev_dbg(d->slave.dev, "txd %p[%x]: callback and free\n",
-                       txd, txd->tx.cookie);
-
-               kfree(txd);
-
-               if (callback)
-                       callback(callback_param);
-       }
-
        dev_dbg(d->slave.dev, "tasklet exit\n");
 }
 
 
-static void sa11x0_dma_desc_free(struct sa11x0_dma_dev *d, struct list_head *head)
-{
-       struct sa11x0_dma_desc *txd, *txn;
-
-       list_for_each_entry_safe(txd, txn, head, node) {
-               dev_dbg(d->slave.dev, "txd %p: freeing\n", txd);
-               kfree(txd);
-       }
-}
-
 static int sa11x0_dma_alloc_chan_resources(struct dma_chan *chan)
 {
        return 0;
@@ -436,18 +401,12 @@ static void sa11x0_dma_free_chan_resources(struct dma_chan *chan)
        struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
        struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
        unsigned long flags;
-       LIST_HEAD(head);
 
-       spin_lock_irqsave(&c->lock, flags);
-       spin_lock(&d->lock);
+       spin_lock_irqsave(&d->lock, flags);
        list_del_init(&c->node);
-       spin_unlock(&d->lock);
-
-       list_splice_tail_init(&c->desc_submitted, &head);
-       list_splice_tail_init(&c->desc_issued, &head);
-       spin_unlock_irqrestore(&c->lock, flags);
+       spin_unlock_irqrestore(&d->lock, flags);
 
-       sa11x0_dma_desc_free(d, &head);
+       vchan_free_chan_resources(&c->vc);
 }
 
 static dma_addr_t sa11x0_dma_pos(struct sa11x0_dma_phy *p)
@@ -472,33 +431,47 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan,
        struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
        struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
        struct sa11x0_dma_phy *p;
-       struct sa11x0_dma_desc *txd;
-       dma_cookie_t last_used, last_complete;
+       struct virt_dma_desc *vd;
        unsigned long flags;
        enum dma_status ret;
-       size_t bytes = 0;
-
-       last_used = c->chan.cookie;
-       last_complete = c->lc;
 
-       ret = dma_async_is_complete(cookie, last_complete, last_used);
-       if (ret == DMA_SUCCESS) {
-               dma_set_tx_state(state, last_complete, last_used, 0);
+       ret = dma_cookie_status(&c->vc.chan, cookie, state);
+       if (ret == DMA_SUCCESS)
                return ret;
-       }
 
-       spin_lock_irqsave(&c->lock, flags);
+       if (!state)
+               return c->status;
+
+       spin_lock_irqsave(&c->vc.lock, flags);
        p = c->phy;
-       ret = c->status;
-       if (p) {
-               dma_addr_t addr = sa11x0_dma_pos(p);
 
-               dev_vdbg(d->slave.dev, "tx_status: addr:%x\n", addr);
+       /*
+        * If the cookie is on our issue queue, then the residue is
+        * its total size.
+        */
+       vd = vchan_find_desc(&c->vc, cookie);
+       if (vd) {
+               state->residue = container_of(vd, struct sa11x0_dma_desc, vd)->size;
+       } else if (!p) {
+               state->residue = 0;
+       } else {
+               struct sa11x0_dma_desc *txd;
+               size_t bytes = 0;
 
-               txd = p->txd_done;
+               if (p->txd_done && p->txd_done->vd.tx.cookie == cookie)
+                       txd = p->txd_done;
+               else if (p->txd_load && p->txd_load->vd.tx.cookie == cookie)
+                       txd = p->txd_load;
+               else
+                       txd = NULL;
+
+               ret = c->status;
                if (txd) {
+                       dma_addr_t addr = sa11x0_dma_pos(p);
                        unsigned i;
 
+                       dev_vdbg(d->slave.dev, "tx_status: addr:%x\n", addr);
+
                        for (i = 0; i < txd->sglen; i++) {
                                dev_vdbg(d->slave.dev, "tx_status: [%u] %x+%x\n",
                                        i, txd->sg[i].addr, txd->sg[i].len);
@@ -521,17 +494,11 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan,
                                bytes += txd->sg[i].len;
                        }
                }
-               if (txd != p->txd_load && p->txd_load)
-                       bytes += p->txd_load->size;
-       }
-       list_for_each_entry(txd, &c->desc_issued, node) {
-               bytes += txd->size;
+               state->residue = bytes;
        }
-       spin_unlock_irqrestore(&c->lock, flags);
-
-       dma_set_tx_state(state, last_complete, last_used, bytes);
+       spin_unlock_irqrestore(&c->vc.lock, flags);
 
-       dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", bytes);
+       dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", state->residue);
 
        return ret;
 }
@@ -547,40 +514,20 @@ static void sa11x0_dma_issue_pending(struct dma_chan *chan)
        struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
        unsigned long flags;
 
-       spin_lock_irqsave(&c->lock, flags);
-       list_splice_tail_init(&c->desc_submitted, &c->desc_issued);
-       if (!list_empty(&c->desc_issued)) {
-               spin_lock(&d->lock);
-               if (!c->phy && list_empty(&c->node)) {
-                       list_add_tail(&c->node, &d->chan_pending);
-                       tasklet_schedule(&d->task);
-                       dev_dbg(d->slave.dev, "vchan %p: issued\n", c);
+       spin_lock_irqsave(&c->vc.lock, flags);
+       if (vchan_issue_pending(&c->vc)) {
+               if (!c->phy) {
+                       spin_lock(&d->lock);
+                       if (list_empty(&c->node)) {
+                               list_add_tail(&c->node, &d->chan_pending);
+                               tasklet_schedule(&d->task);
+                               dev_dbg(d->slave.dev, "vchan %p: issued\n", &c->vc);
+                       }
+                       spin_unlock(&d->lock);
                }
-               spin_unlock(&d->lock);
        } else
-               dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", c);
-       spin_unlock_irqrestore(&c->lock, flags);
-}
-
-static dma_cookie_t sa11x0_dma_tx_submit(struct dma_async_tx_descriptor *tx)
-{
-       struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(tx->chan);
-       struct sa11x0_dma_desc *txd = to_sa11x0_dma_tx(tx);
-       unsigned long flags;
-
-       spin_lock_irqsave(&c->lock, flags);
-       c->chan.cookie += 1;
-       if (c->chan.cookie < 0)
-               c->chan.cookie = 1;
-       txd->tx.cookie = c->chan.cookie;
-
-       list_add_tail(&txd->node, &c->desc_submitted);
-       spin_unlock_irqrestore(&c->lock, flags);
-
-       dev_dbg(tx->chan->device->dev, "vchan %p: txd %p[%x]: submitted\n",
-               c, txd, txd->tx.cookie);
-
-       return txd->tx.cookie;
+               dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", &c->vc);
+       spin_unlock_irqrestore(&c->vc.lock, flags);
 }
 
 static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
@@ -596,7 +543,7 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
        /* SA11x0 channels can only operate in their native direction */
        if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) {
                dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n",
-                       c, c->ddar, dir);
+                       &c->vc, c->ddar, dir);
                return NULL;
        }
 
@@ -612,14 +559,14 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
                        j += DIV_ROUND_UP(len, DMA_MAX_SIZE & ~DMA_ALIGN) - 1;
                if (addr & DMA_ALIGN) {
                        dev_dbg(chan->device->dev, "vchan %p: bad buffer alignment: %08x\n",
-                               c, addr);
+                               &c->vc, addr);
                        return NULL;
                }
        }
 
        txd = kzalloc(sizeof(*txd) + j * sizeof(txd->sg[0]), GFP_ATOMIC);
        if (!txd) {
-               dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", c);
+               dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", &c->vc);
                return NULL;
        }
 
@@ -655,17 +602,73 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
                } while (len);
        }
 
-       dma_async_tx_descriptor_init(&txd->tx, &c->chan);
-       txd->tx.flags = flags;
-       txd->tx.tx_submit = sa11x0_dma_tx_submit;
        txd->ddar = c->ddar;
        txd->size = size;
        txd->sglen = j;
 
        dev_dbg(chan->device->dev, "vchan %p: txd %p: size %u nr %u\n",
-               c, txd, txd->size, txd->sglen);
+               &c->vc, &txd->vd, txd->size, txd->sglen);
 
-       return &txd->tx;
+       return vchan_tx_prep(&c->vc, &txd->vd, flags);
+}
+
+static struct dma_async_tx_descriptor *sa11x0_dma_prep_dma_cyclic(
+       struct dma_chan *chan, dma_addr_t addr, size_t size, size_t period,
+       enum dma_transfer_direction dir, void *context)
+{
+       struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
+       struct sa11x0_dma_desc *txd;
+       unsigned i, j, k, sglen, sgperiod;
+
+       /* SA11x0 channels can only operate in their native direction */
+       if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) {
+               dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n",
+                       &c->vc, c->ddar, dir);
+               return NULL;
+       }
+
+       sgperiod = DIV_ROUND_UP(period, DMA_MAX_SIZE & ~DMA_ALIGN);
+       sglen = size * sgperiod / period;
+
+       /* Do not allow zero-sized txds */
+       if (sglen == 0)
+               return NULL;
+
+       txd = kzalloc(sizeof(*txd) + sglen * sizeof(txd->sg[0]), GFP_ATOMIC);
+       if (!txd) {
+               dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", &c->vc);
+               return NULL;
+       }
+
+       for (i = k = 0; i < size / period; i++) {
+               size_t tlen, len = period;
+
+               for (j = 0; j < sgperiod; j++, k++) {
+                       tlen = len;
+
+                       if (tlen > DMA_MAX_SIZE) {
+                               unsigned mult = DIV_ROUND_UP(tlen, DMA_MAX_SIZE & ~DMA_ALIGN);
+                               tlen = (tlen / mult) & ~DMA_ALIGN;
+                       }
+
+                       txd->sg[k].addr = addr;
+                       txd->sg[k].len = tlen;
+                       addr += tlen;
+                       len -= tlen;
+               }
+
+               WARN_ON(len != 0);
+       }
+
+       WARN_ON(k != sglen);
+
+       txd->ddar = c->ddar;
+       txd->size = size;
+       txd->sglen = sglen;
+       txd->cyclic = 1;
+       txd->period = sgperiod;
+
+       return vchan_tx_prep(&c->vc, &txd->vd, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
 }
 
 static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg)
@@ -695,8 +698,8 @@ static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_c
        if (maxburst == 8)
                ddar |= DDAR_BS;
 
-       dev_dbg(c->chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n",
-               c, addr, width, maxburst);
+       dev_dbg(c->vc.chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n",
+               &c->vc, addr, width, maxburst);
 
        c->ddar = ddar | (addr & 0xf0000000) | (addr & 0x003ffffc) << 6;
 
@@ -718,16 +721,13 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
                return sa11x0_dma_slave_config(c, (struct dma_slave_config *)arg);
 
        case DMA_TERMINATE_ALL:
-               dev_dbg(d->slave.dev, "vchan %p: terminate all\n", c);
+               dev_dbg(d->slave.dev, "vchan %p: terminate all\n", &c->vc);
                /* Clear the tx descriptor lists */
-               spin_lock_irqsave(&c->lock, flags);
-               list_splice_tail_init(&c->desc_submitted, &head);
-               list_splice_tail_init(&c->desc_issued, &head);
+               spin_lock_irqsave(&c->vc.lock, flags);
+               vchan_get_all_descriptors(&c->vc, &head);
 
                p = c->phy;
                if (p) {
-                       struct sa11x0_dma_desc *txd, *txn;
-
                        dev_dbg(d->slave.dev, "pchan %u: terminating\n", p->num);
                        /* vchan is assigned to a pchan - stop the channel */
                        writel(DCSR_RUN | DCSR_IE |
@@ -735,17 +735,13 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
                                DCSR_STRTB | DCSR_DONEB,
                                p->base + DMA_DCSR_C);
 
-                       list_for_each_entry_safe(txd, txn, &d->desc_complete, node)
-                               if (txd->tx.chan == &c->chan)
-                                       list_move(&txd->node, &head);
-
                        if (p->txd_load) {
                                if (p->txd_load != p->txd_done)
-                                       list_add_tail(&p->txd_load->node, &head);
+                                       list_add_tail(&p->txd_load->vd.node, &head);
                                p->txd_load = NULL;
                        }
                        if (p->txd_done) {
-                               list_add_tail(&p->txd_done->node, &head);
+                               list_add_tail(&p->txd_done->vd.node, &head);
                                p->txd_done = NULL;
                        }
                        c->phy = NULL;
@@ -754,14 +750,14 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
                        spin_unlock(&d->lock);
                        tasklet_schedule(&d->task);
                }
-               spin_unlock_irqrestore(&c->lock, flags);
-               sa11x0_dma_desc_free(d, &head);
+               spin_unlock_irqrestore(&c->vc.lock, flags);
+               vchan_dma_desc_free_list(&c->vc, &head);
                ret = 0;
                break;
 
        case DMA_PAUSE:
-               dev_dbg(d->slave.dev, "vchan %p: pause\n", c);
-               spin_lock_irqsave(&c->lock, flags);
+               dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc);
+               spin_lock_irqsave(&c->vc.lock, flags);
                if (c->status == DMA_IN_PROGRESS) {
                        c->status = DMA_PAUSED;
 
@@ -774,26 +770,26 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
                                spin_unlock(&d->lock);
                        }
                }
-               spin_unlock_irqrestore(&c->lock, flags);
+               spin_unlock_irqrestore(&c->vc.lock, flags);
                ret = 0;
                break;
 
        case DMA_RESUME:
-               dev_dbg(d->slave.dev, "vchan %p: resume\n", c);
-               spin_lock_irqsave(&c->lock, flags);
+               dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc);
+               spin_lock_irqsave(&c->vc.lock, flags);
                if (c->status == DMA_PAUSED) {
                        c->status = DMA_IN_PROGRESS;
 
                        p = c->phy;
                        if (p) {
                                writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_S);
-                       } else if (!list_empty(&c->desc_issued)) {
+                       } else if (!list_empty(&c->vc.desc_issued)) {
                                spin_lock(&d->lock);
                                list_add_tail(&c->node, &d->chan_pending);
                                spin_unlock(&d->lock);
                        }
                }
-               spin_unlock_irqrestore(&c->lock, flags);
+               spin_unlock_irqrestore(&c->vc.lock, flags);
                ret = 0;
                break;
 
@@ -853,15 +849,13 @@ static int __devinit sa11x0_dma_init_dmadev(struct dma_device *dmadev,
                        return -ENOMEM;
                }
 
-               c->chan.device = dmadev;
                c->status = DMA_IN_PROGRESS;
                c->ddar = chan_desc[i].ddar;
                c->name = chan_desc[i].name;
-               spin_lock_init(&c->lock);
-               INIT_LIST_HEAD(&c->desc_submitted);
-               INIT_LIST_HEAD(&c->desc_issued);
                INIT_LIST_HEAD(&c->node);
-               list_add_tail(&c->chan.device_node, &dmadev->channels);
+
+               c->vc.desc_free = sa11x0_dma_free_desc;
+               vchan_init(&c->vc, dmadev);
        }
 
        return dma_async_device_register(dmadev);
@@ -890,8 +884,9 @@ static void sa11x0_dma_free_channels(struct dma_device *dmadev)
 {
        struct sa11x0_dma_chan *c, *cn;
 
-       list_for_each_entry_safe(c, cn, &dmadev->channels, chan.device_node) {
-               list_del(&c->chan.device_node);
+       list_for_each_entry_safe(c, cn, &dmadev->channels, vc.chan.device_node) {
+               list_del(&c->vc.chan.device_node);
+               tasklet_kill(&c->vc.task);
                kfree(c);
        }
 }
@@ -915,7 +910,6 @@ static int __devinit sa11x0_dma_probe(struct platform_device *pdev)
 
        spin_lock_init(&d->lock);
        INIT_LIST_HEAD(&d->chan_pending);
-       INIT_LIST_HEAD(&d->desc_complete);
 
        d->base = ioremap(res->start, resource_size(res));
        if (!d->base) {
@@ -947,7 +941,9 @@ static int __devinit sa11x0_dma_probe(struct platform_device *pdev)
        }
 
        dma_cap_set(DMA_SLAVE, d->slave.cap_mask);
+       dma_cap_set(DMA_CYCLIC, d->slave.cap_mask);
        d->slave.device_prep_slave_sg = sa11x0_dma_prep_slave_sg;
+       d->slave.device_prep_dma_cyclic = sa11x0_dma_prep_dma_cyclic;
        ret = sa11x0_dma_init_dmadev(&d->slave, &pdev->dev);
        if (ret) {
                dev_warn(d->slave.dev, "failed to register slave async device: %d\n",
diff --git a/drivers/dma/virt-dma.c b/drivers/dma/virt-dma.c
new file mode 100644 (file)
index 0000000..6f80432
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Virtual DMA channel support for DMAengine
+ *
+ * Copyright (C) 2012 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/device.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+
+static struct virt_dma_desc *to_virt_desc(struct dma_async_tx_descriptor *tx)
+{
+       return container_of(tx, struct virt_dma_desc, tx);
+}
+
+dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+       struct virt_dma_chan *vc = to_virt_chan(tx->chan);
+       struct virt_dma_desc *vd = to_virt_desc(tx);
+       unsigned long flags;
+       dma_cookie_t cookie;
+
+       spin_lock_irqsave(&vc->lock, flags);
+       cookie = dma_cookie_assign(tx);
+
+       list_add_tail(&vd->node, &vc->desc_submitted);
+       spin_unlock_irqrestore(&vc->lock, flags);
+
+       dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted\n",
+               vc, vd, cookie);
+
+       return cookie;
+}
+EXPORT_SYMBOL_GPL(vchan_tx_submit);
+
+struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *vc,
+       dma_cookie_t cookie)
+{
+       struct virt_dma_desc *vd;
+
+       list_for_each_entry(vd, &vc->desc_issued, node)
+               if (vd->tx.cookie == cookie)
+                       return vd;
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(vchan_find_desc);
+
+/*
+ * This tasklet handles the completion of a DMA descriptor by
+ * calling its callback and freeing it.
+ */
+static void vchan_complete(unsigned long arg)
+{
+       struct virt_dma_chan *vc = (struct virt_dma_chan *)arg;
+       struct virt_dma_desc *vd;
+       dma_async_tx_callback cb = NULL;
+       void *cb_data = NULL;
+       LIST_HEAD(head);
+
+       spin_lock_irq(&vc->lock);
+       list_splice_tail_init(&vc->desc_completed, &head);
+       vd = vc->cyclic;
+       if (vd) {
+               vc->cyclic = NULL;
+               cb = vd->tx.callback;
+               cb_data = vd->tx.callback_param;
+       }
+       spin_unlock_irq(&vc->lock);
+
+       if (cb)
+               cb(cb_data);
+
+       while (!list_empty(&head)) {
+               vd = list_first_entry(&head, struct virt_dma_desc, node);
+               cb = vd->tx.callback;
+               cb_data = vd->tx.callback_param;
+
+               list_del(&vd->node);
+
+               vc->desc_free(vd);
+
+               if (cb)
+                       cb(cb_data);
+       }
+}
+
+void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head)
+{
+       while (!list_empty(head)) {
+               struct virt_dma_desc *vd = list_first_entry(head,
+                       struct virt_dma_desc, node);
+               list_del(&vd->node);
+               dev_dbg(vc->chan.device->dev, "txd %p: freeing\n", vd);
+               vc->desc_free(vd);
+       }
+}
+EXPORT_SYMBOL_GPL(vchan_dma_desc_free_list);
+
+void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
+{
+       dma_cookie_init(&vc->chan);
+
+       spin_lock_init(&vc->lock);
+       INIT_LIST_HEAD(&vc->desc_submitted);
+       INIT_LIST_HEAD(&vc->desc_issued);
+       INIT_LIST_HEAD(&vc->desc_completed);
+
+       tasklet_init(&vc->task, vchan_complete, (unsigned long)vc);
+
+       vc->chan.device = dmadev;
+       list_add_tail(&vc->chan.device_node, &dmadev->channels);
+}
+EXPORT_SYMBOL_GPL(vchan_init);
+
+MODULE_AUTHOR("Russell King");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/virt-dma.h b/drivers/dma/virt-dma.h
new file mode 100644 (file)
index 0000000..85c19d6
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Virtual DMA channel support for DMAengine
+ *
+ * Copyright (C) 2012 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef VIRT_DMA_H
+#define VIRT_DMA_H
+
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+
+#include "dmaengine.h"
+
+struct virt_dma_desc {
+       struct dma_async_tx_descriptor tx;
+       /* protected by vc.lock */
+       struct list_head node;
+};
+
+struct virt_dma_chan {
+       struct dma_chan chan;
+       struct tasklet_struct task;
+       void (*desc_free)(struct virt_dma_desc *);
+
+       spinlock_t lock;
+
+       /* protected by vc.lock */
+       struct list_head desc_submitted;
+       struct list_head desc_issued;
+       struct list_head desc_completed;
+
+       struct virt_dma_desc *cyclic;
+};
+
+static inline struct virt_dma_chan *to_virt_chan(struct dma_chan *chan)
+{
+       return container_of(chan, struct virt_dma_chan, chan);
+}
+
+void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head);
+void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev);
+struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *, dma_cookie_t);
+
+/**
+ * vchan_tx_prep - prepare a descriptor
+ * vc: virtual channel allocating this descriptor
+ * vd: virtual descriptor to prepare
+ * tx_flags: flags argument passed in to prepare function
+ */
+static inline struct dma_async_tx_descriptor *vchan_tx_prep(struct virt_dma_chan *vc,
+       struct virt_dma_desc *vd, unsigned long tx_flags)
+{
+       extern dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *);
+
+       dma_async_tx_descriptor_init(&vd->tx, &vc->chan);
+       vd->tx.flags = tx_flags;
+       vd->tx.tx_submit = vchan_tx_submit;
+
+       return &vd->tx;
+}
+
+/**
+ * vchan_issue_pending - move submitted descriptors to issued list
+ * vc: virtual channel to update
+ *
+ * vc.lock must be held by caller
+ */
+static inline bool vchan_issue_pending(struct virt_dma_chan *vc)
+{
+       list_splice_tail_init(&vc->desc_submitted, &vc->desc_issued);
+       return !list_empty(&vc->desc_issued);
+}
+
+/**
+ * vchan_cookie_complete - report completion of a descriptor
+ * vd: virtual descriptor to update
+ *
+ * vc.lock must be held by caller
+ */
+static inline void vchan_cookie_complete(struct virt_dma_desc *vd)
+{
+       struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);
+
+       dma_cookie_complete(&vd->tx);
+       dev_vdbg(vc->chan.device->dev, "txd %p[%x]: marked complete\n",
+               vd, vd->tx.cookie);
+       list_add_tail(&vd->node, &vc->desc_completed);
+
+       tasklet_schedule(&vc->task);
+}
+
+/**
+ * vchan_cyclic_callback - report the completion of a period
+ * vd: virtual descriptor
+ */
+static inline void vchan_cyclic_callback(struct virt_dma_desc *vd)
+{
+       struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);
+
+       vc->cyclic = vd;
+       tasklet_schedule(&vc->task);
+}
+
+/**
+ * vchan_next_desc - peek at the next descriptor to be processed
+ * vc: virtual channel to obtain descriptor from
+ *
+ * vc.lock must be held by caller
+ */
+static inline struct virt_dma_desc *vchan_next_desc(struct virt_dma_chan *vc)
+{
+       if (list_empty(&vc->desc_issued))
+               return NULL;
+
+       return list_first_entry(&vc->desc_issued, struct virt_dma_desc, node);
+}
+
+/**
+ * vchan_get_all_descriptors - obtain all submitted and issued descriptors
+ * vc: virtual channel to get descriptors from
+ * head: list of descriptors found
+ *
+ * vc.lock must be held by caller
+ *
+ * Removes all submitted and issued descriptors from internal lists, and
+ * provides a list of all descriptors found
+ */
+static inline void vchan_get_all_descriptors(struct virt_dma_chan *vc,
+       struct list_head *head)
+{
+       list_splice_tail_init(&vc->desc_submitted, head);
+       list_splice_tail_init(&vc->desc_issued, head);
+       list_splice_tail_init(&vc->desc_completed, head);
+}
+
+static inline void vchan_free_chan_resources(struct virt_dma_chan *vc)
+{
+       unsigned long flags;
+       LIST_HEAD(head);
+
+       spin_lock_irqsave(&vc->lock, flags);
+       vchan_get_all_descriptors(vc, &head);
+       spin_unlock_irqrestore(&vc->lock, flags);
+
+       vchan_dma_desc_free_list(vc, &head);
+}
+
+#endif
index fdffa1beca17288eeab2055d25382545c3dbbdcf..409b92b8d346087b4ec292bda8790d03cc764224 100644 (file)
@@ -7,7 +7,7 @@
 menuconfig EDAC
        bool "EDAC (Error Detection And Correction) reporting"
        depends on HAS_IOMEM
-       depends on X86 || PPC || TILE
+       depends on X86 || PPC || TILE || ARM
        help
          EDAC is designed to report errors in the core system.
          These are low-level errors that are reported in the CPU or
@@ -31,6 +31,14 @@ if EDAC
 
 comment "Reporting subsystems"
 
+config EDAC_LEGACY_SYSFS
+       bool "EDAC legacy sysfs"
+       default y
+       help
+         Enable the compatibility sysfs nodes.
+         Use 'Y' if your edac utilities aren't ported to work with the newer
+         structures.
+
 config EDAC_DEBUG
        bool "Debugging"
        help
@@ -294,4 +302,18 @@ config EDAC_TILE
          Support for error detection and correction on the
          Tilera memory controller.
 
+config EDAC_HIGHBANK_MC
+       tristate "Highbank Memory Controller"
+       depends on EDAC_MM_EDAC && ARCH_HIGHBANK
+       help
+         Support for error detection and correction on the
+         Calxeda Highbank memory controller.
+
+config EDAC_HIGHBANK_L2
+       tristate "Highbank L2 Cache"
+       depends on EDAC_MM_EDAC && ARCH_HIGHBANK
+       help
+         Support for error detection and correction on the
+         Calxeda Highbank memory controller.
+
 endif # EDAC
index 196a63dd37c5c41e0914502620fb3a8424c7d2a4..7e5129a733f8cc6b8afdc7dac91228167949512d 100644 (file)
@@ -55,3 +55,6 @@ obj-$(CONFIG_EDAC_AMD8111)            += amd8111_edac.o
 obj-$(CONFIG_EDAC_AMD8131)             += amd8131_edac.o
 
 obj-$(CONFIG_EDAC_TILE)                        += tile_edac.o
+
+obj-$(CONFIG_EDAC_HIGHBANK_MC) += highbank_mc_edac.o
+obj-$(CONFIG_EDAC_HIGHBANK_L2) += highbank_l2_edac.o
index 7be9b7288e90eaaf5fab79f34dcac2ceafbc3a2b..5a297a26211d622b0f0ceedb0389fcbeb4223baf 100644 (file)
@@ -321,8 +321,8 @@ found:
        return edac_mc_find((int)node_id);
 
 err_no_match:
-       debugf2("sys_addr 0x%lx doesn't match any node\n",
-               (unsigned long)sys_addr);
+       edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n",
+                (unsigned long)sys_addr);
 
        return NULL;
 }
@@ -393,15 +393,15 @@ static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
                mask = ~mask;
 
                if ((input_addr & mask) == (base & mask)) {
-                       debugf2("InputAddr 0x%lx matches csrow %d (node %d)\n",
-                               (unsigned long)input_addr, csrow,
-                               pvt->mc_node_id);
+                       edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n",
+                                (unsigned long)input_addr, csrow,
+                                pvt->mc_node_id);
 
                        return csrow;
                }
        }
-       debugf2("no matching csrow for InputAddr 0x%lx (MC node %d)\n",
-               (unsigned long)input_addr, pvt->mc_node_id);
+       edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n",
+                (unsigned long)input_addr, pvt->mc_node_id);
 
        return -1;
 }
@@ -430,20 +430,20 @@ int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
 
        /* only revE and later have the DRAM Hole Address Register */
        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);
+               edac_dbg(1, "  revision %d for node %d does not support DHAR\n",
+                        pvt->ext_model, pvt->mc_node_id);
                return 1;
        }
 
        /* valid for Fam10h and above */
        if (boot_cpu_data.x86 >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
-               debugf1("  Dram Memory Hoisting is DISABLED on this system\n");
+               edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this system\n");
                return 1;
        }
 
        if (!dhar_valid(pvt)) {
-               debugf1("  Dram Memory Hoisting is DISABLED on this node %d\n",
-                       pvt->mc_node_id);
+               edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this node %d\n",
+                        pvt->mc_node_id);
                return 1;
        }
 
@@ -475,9 +475,9 @@ int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
        else
                *hole_offset = k8_dhar_offset(pvt);
 
-       debugf1("  DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
-               pvt->mc_node_id, (unsigned long)*hole_base,
-               (unsigned long)*hole_offset, (unsigned long)*hole_size);
+       edac_dbg(1, "  DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
+                pvt->mc_node_id, (unsigned long)*hole_base,
+                (unsigned long)*hole_offset, (unsigned long)*hole_size);
 
        return 0;
 }
@@ -528,10 +528,9 @@ static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
                        /* use DHAR to translate SysAddr to DramAddr */
                        dram_addr = sys_addr - hole_offset;
 
-                       debugf2("using DHAR to translate SysAddr 0x%lx to "
-                               "DramAddr 0x%lx\n",
-                               (unsigned long)sys_addr,
-                               (unsigned long)dram_addr);
+                       edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
+                                (unsigned long)sys_addr,
+                                (unsigned long)dram_addr);
 
                        return dram_addr;
                }
@@ -548,9 +547,8 @@ static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
         */
        dram_addr = (sys_addr & GENMASK(0, 39)) - dram_base;
 
-       debugf2("using DRAM Base register to translate SysAddr 0x%lx to "
-               "DramAddr 0x%lx\n", (unsigned long)sys_addr,
-               (unsigned long)dram_addr);
+       edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
+                (unsigned long)sys_addr, (unsigned long)dram_addr);
        return dram_addr;
 }
 
@@ -586,9 +584,9 @@ static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
        input_addr = ((dram_addr >> intlv_shift) & GENMASK(12, 35)) +
                      (dram_addr & 0xfff);
 
-       debugf2("  Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
-               intlv_shift, (unsigned long)dram_addr,
-               (unsigned long)input_addr);
+       edac_dbg(2, "  Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
+                intlv_shift, (unsigned long)dram_addr,
+                (unsigned long)input_addr);
 
        return input_addr;
 }
@@ -604,8 +602,8 @@ static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
        input_addr =
            dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
 
-       debugf2("SysAdddr 0x%lx translates to InputAddr 0x%lx\n",
-               (unsigned long)sys_addr, (unsigned long)input_addr);
+       edac_dbg(2, "SysAdddr 0x%lx translates to InputAddr 0x%lx\n",
+                (unsigned long)sys_addr, (unsigned long)input_addr);
 
        return input_addr;
 }
@@ -637,8 +635,8 @@ static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr)
 
        intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
        if (intlv_shift == 0) {
-               debugf1("    InputAddr 0x%lx translates to DramAddr of "
-                       "same value\n", (unsigned long)input_addr);
+               edac_dbg(1, "    InputAddr 0x%lx translates to DramAddr of same value\n",
+                        (unsigned long)input_addr);
 
                return input_addr;
        }
@@ -649,9 +647,9 @@ static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr)
        intlv_sel = dram_intlv_sel(pvt, node_id) & ((1 << intlv_shift) - 1);
        dram_addr = bits + (intlv_sel << 12);
 
-       debugf1("InputAddr 0x%lx translates to DramAddr 0x%lx "
-               "(%d node interleave bits)\n", (unsigned long)input_addr,
-               (unsigned long)dram_addr, intlv_shift);
+       edac_dbg(1, "InputAddr 0x%lx translates to DramAddr 0x%lx (%d node interleave bits)\n",
+                (unsigned long)input_addr,
+                (unsigned long)dram_addr, intlv_shift);
 
        return dram_addr;
 }
@@ -673,9 +671,9 @@ static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr)
                    (dram_addr < (hole_base + hole_size))) {
                        sys_addr = dram_addr + hole_offset;
 
-                       debugf1("using DHAR to translate DramAddr 0x%lx to "
-                               "SysAddr 0x%lx\n", (unsigned long)dram_addr,
-                               (unsigned long)sys_addr);
+                       edac_dbg(1, "using DHAR to translate DramAddr 0x%lx to SysAddr 0x%lx\n",
+                                (unsigned long)dram_addr,
+                                (unsigned long)sys_addr);
 
                        return sys_addr;
                }
@@ -697,9 +695,9 @@ static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr)
         */
        sys_addr |= ~((sys_addr & (1ull << 39)) - 1);
 
-       debugf1("    Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n",
-               pvt->mc_node_id, (unsigned long)dram_addr,
-               (unsigned long)sys_addr);
+       edac_dbg(1, "    Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n",
+                pvt->mc_node_id, (unsigned long)dram_addr,
+                (unsigned long)sys_addr);
 
        return sys_addr;
 }
@@ -768,49 +766,48 @@ static void amd64_debug_display_dimm_sizes(struct amd64_pvt *, u8);
 
 static void amd64_dump_dramcfg_low(u32 dclr, int chan)
 {
-       debugf1("F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
+       edac_dbg(1, "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");
+       edac_dbg(1, "  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");
+       edac_dbg(1, "  PAR/ERR parity: %s\n",
+                (dclr & BIT(8)) ?  "enabled" : "disabled");
 
        if (boot_cpu_data.x86 == 0x10)
-               debugf1("  DCT 128bit mode width: %s\n",
-                       (dclr & BIT(11)) ?  "128b" : "64b");
+               edac_dbg(1, "  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");
+       edac_dbg(1, "  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 dump_misc_regs(struct amd64_pvt *pvt)
 {
-       debugf1("F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
+       edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
 
-       debugf1("  NB two channel DRAM capable: %s\n",
-               (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
+       edac_dbg(1, "  NB two channel DRAM capable: %s\n",
+                (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
 
-       debugf1("  ECC capable: %s, ChipKill ECC capable: %s\n",
-               (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
-               (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
+       edac_dbg(1, "  ECC capable: %s, ChipKill ECC capable: %s\n",
+                (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
+                (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
 
        amd64_dump_dramcfg_low(pvt->dclr0, 0);
 
-       debugf1("F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
+       edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
 
-       debugf1("F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, "
-                       "offset: 0x%08x\n",
-                       pvt->dhar, dhar_base(pvt),
-                       (boot_cpu_data.x86 == 0xf) ? k8_dhar_offset(pvt)
-                                                  : f10_dhar_offset(pvt));
+       edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n",
+                pvt->dhar, dhar_base(pvt),
+                (boot_cpu_data.x86 == 0xf) ? k8_dhar_offset(pvt)
+                : f10_dhar_offset(pvt));
 
-       debugf1("  DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
+       edac_dbg(1, "  DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
 
        amd64_debug_display_dimm_sizes(pvt, 0);
 
@@ -857,15 +854,15 @@ static void read_dct_base_mask(struct amd64_pvt *pvt)
                u32 *base1 = &pvt->csels[1].csbases[cs];
 
                if (!amd64_read_dct_pci_cfg(pvt, reg0, base0))
-                       debugf0("  DCSB0[%d]=0x%08x reg: F2x%x\n",
-                               cs, *base0, reg0);
+                       edac_dbg(0, "  DCSB0[%d]=0x%08x reg: F2x%x\n",
+                                cs, *base0, reg0);
 
                if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt))
                        continue;
 
                if (!amd64_read_dct_pci_cfg(pvt, reg1, base1))
-                       debugf0("  DCSB1[%d]=0x%08x reg: F2x%x\n",
-                               cs, *base1, reg1);
+                       edac_dbg(0, "  DCSB1[%d]=0x%08x reg: F2x%x\n",
+                                cs, *base1, reg1);
        }
 
        for_each_chip_select_mask(cs, 0, pvt) {
@@ -875,15 +872,15 @@ static void read_dct_base_mask(struct amd64_pvt *pvt)
                u32 *mask1 = &pvt->csels[1].csmasks[cs];
 
                if (!amd64_read_dct_pci_cfg(pvt, reg0, mask0))
-                       debugf0("    DCSM0[%d]=0x%08x reg: F2x%x\n",
-                               cs, *mask0, reg0);
+                       edac_dbg(0, "    DCSM0[%d]=0x%08x reg: F2x%x\n",
+                                cs, *mask0, reg0);
 
                if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt))
                        continue;
 
                if (!amd64_read_dct_pci_cfg(pvt, reg1, mask1))
-                       debugf0("    DCSM1[%d]=0x%08x reg: F2x%x\n",
-                               cs, *mask1, reg1);
+                       edac_dbg(0, "    DCSM1[%d]=0x%08x reg: F2x%x\n",
+                                cs, *mask1, reg1);
        }
 }
 
@@ -1049,24 +1046,22 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
        if (!src_mci) {
                amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
                             (unsigned long)sys_addr);
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                     page, offset, syndrome,
                                     -1, -1, -1,
-                                    EDAC_MOD_STR,
                                     "failed to map error addr to a node",
-                                    NULL);
+                                    "");
                return;
        }
 
        /* Now map the sys_addr to a CSROW */
        csrow = sys_addr_to_csrow(src_mci, sys_addr);
        if (csrow < 0) {
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                     page, offset, syndrome,
                                     -1, -1, -1,
-                                    EDAC_MOD_STR,
                                     "failed to map error addr to a csrow",
-                                    NULL);
+                                    "");
                return;
        }
 
@@ -1082,12 +1077,11 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
                        amd64_mc_warn(src_mci, "unknown syndrome 0x%04x - "
                                      "possible error reporting race\n",
                                      syndrome);
-                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                             page, offset, syndrome,
                                             csrow, -1, -1,
-                                            EDAC_MOD_STR,
                                             "unknown syndrome - possible error reporting race",
-                                            NULL);
+                                            "");
                        return;
                }
        } else {
@@ -1102,10 +1096,10 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
                channel = ((sys_addr & BIT(3)) != 0);
        }
 
-       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, src_mci,
+       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, src_mci, 1,
                             page, offset, syndrome,
                             csrow, channel, -1,
-                            EDAC_MOD_STR, "", NULL);
+                            "", "");
 }
 
 static int ddr2_cs_size(unsigned i, bool dct_width)
@@ -1193,7 +1187,7 @@ static int f1x_early_channel_count(struct amd64_pvt *pvt)
         * 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");
+       edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
 
        /*
         * Check DRAM Bank Address Mapping values for each DIMM to see if there
@@ -1272,25 +1266,24 @@ static void read_dram_ctl_register(struct amd64_pvt *pvt)
                return;
 
        if (!amd64_read_dct_pci_cfg(pvt, DCT_SEL_LO, &pvt->dct_sel_lo)) {
-               debugf0("F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
-                       pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
+               edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
+                        pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
 
-               debugf0("  DCTs operate in %s mode.\n",
-                       (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
+               edac_dbg(0, "  DCTs operate in %s mode\n",
+                        (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
 
                if (!dct_ganging_enabled(pvt))
-                       debugf0("  Address range split per DCT: %s\n",
-                               (dct_high_range_enabled(pvt) ? "yes" : "no"));
+                       edac_dbg(0, "  Address range split per DCT: %s\n",
+                                (dct_high_range_enabled(pvt) ? "yes" : "no"));
 
-               debugf0("  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"));
+               edac_dbg(0, "  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("  channel interleave: %s, "
-                       "interleave bits selector: 0x%x\n",
-                       (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
-                       dct_sel_interleave_addr(pvt));
+               edac_dbg(0, "  channel interleave: %s, "
+                        "interleave bits selector: 0x%x\n",
+                        (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
+                        dct_sel_interleave_addr(pvt));
        }
 
        amd64_read_dct_pci_cfg(pvt, DCT_SEL_HI, &pvt->dct_sel_hi);
@@ -1428,7 +1421,7 @@ static int f1x_lookup_addr_in_dct(u64 in_addr, u32 nid, u8 dct)
 
        pvt = mci->pvt_info;
 
-       debugf1("input addr: 0x%llx, DCT: %d\n", in_addr, dct);
+       edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
 
        for_each_chip_select(csrow, dct, pvt) {
                if (!csrow_enabled(csrow, dct, pvt))
@@ -1436,19 +1429,18 @@ static int f1x_lookup_addr_in_dct(u64 in_addr, u32 nid, u8 dct)
 
                get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
 
-               debugf1("    CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
-                       csrow, cs_base, cs_mask);
+               edac_dbg(1, "    CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
+                        csrow, cs_base, cs_mask);
 
                cs_mask = ~cs_mask;
 
-               debugf1("    (InputAddr & ~CSMask)=0x%llx "
-                       "(CSBase & ~CSMask)=0x%llx\n",
-                       (in_addr & cs_mask), (cs_base & cs_mask));
+               edac_dbg(1, "    (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
+                        (in_addr & cs_mask), (cs_base & cs_mask));
 
                if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
                        cs_found = f10_process_possible_spare(pvt, dct, csrow);
 
-                       debugf1(" MATCH csrow=%d\n", cs_found);
+                       edac_dbg(1, " MATCH csrow=%d\n", cs_found);
                        break;
                }
        }
@@ -1505,8 +1497,8 @@ static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
        u8 intlv_en   = dram_intlv_en(pvt, range);
        u32 intlv_sel = dram_intlv_sel(pvt, range);
 
-       debugf1("(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
-               range, sys_addr, get_dram_limit(pvt, range));
+       edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
+                range, sys_addr, get_dram_limit(pvt, range));
 
        if (dhar_valid(pvt) &&
            dhar_base(pvt) <= sys_addr &&
@@ -1562,7 +1554,7 @@ static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
                                     (chan_addr & 0xfff);
        }
 
-       debugf1("   Normalized DCT addr: 0x%llx\n", chan_addr);
+       edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
 
        cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
 
@@ -1616,12 +1608,11 @@ static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
        csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &nid, &chan);
 
        if (csrow < 0) {
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                     page, offset, syndrome,
                                     -1, -1, -1,
-                                    EDAC_MOD_STR,
                                     "failed to map error addr to a csrow",
-                                    NULL);
+                                    "");
                return;
        }
 
@@ -1633,10 +1624,10 @@ static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
        if (dct_ganging_enabled(pvt))
                chan = get_channel_from_ecc_syndrome(mci, syndrome);
 
-       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                             page, offset, syndrome,
                             csrow, chan, -1,
-                            EDAC_MOD_STR, "", NULL);
+                            "", "");
 }
 
 /*
@@ -1664,7 +1655,8 @@ static void amd64_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
        dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->csels[1].csbases
                                                   : pvt->csels[0].csbases;
 
-       debugf1("F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n", ctrl, dbam);
+       edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
+                ctrl, dbam);
 
        edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
 
@@ -1840,7 +1832,7 @@ static int decode_syndrome(u16 syndrome, u16 *vectors, unsigned num_vecs,
                }
        }
 
-       debugf0("syndrome(%x) not found\n", syndrome);
+       edac_dbg(0, "syndrome(%x) not found\n", syndrome);
        return -1;
 }
 
@@ -1917,12 +1909,11 @@ static void amd64_handle_ce(struct mem_ctl_info *mci, struct mce *m)
        /* Ensure that the Error Address is VALID */
        if (!(m->status & MCI_STATUS_ADDRV)) {
                amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                     0, 0, 0,
                                     -1, -1, -1,
-                                    EDAC_MOD_STR,
                                     "HW has no ERROR_ADDRESS available",
-                                    NULL);
+                                    "");
                return;
        }
 
@@ -1946,12 +1937,11 @@ static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m)
 
        if (!(m->status & MCI_STATUS_ADDRV)) {
                amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                     0, 0, 0,
                                     -1, -1, -1,
-                                    EDAC_MOD_STR,
                                     "HW has no ERROR_ADDRESS available",
-                                    NULL);
+                                    "");
                return;
        }
 
@@ -1966,11 +1956,11 @@ static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m)
        if (!src_mci) {
                amd64_mc_err(mci, "ERROR ADDRESS (0x%lx) NOT mapped to a MC\n",
                                  (unsigned long)sys_addr);
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                     page, offset, 0,
                                     -1, -1, -1,
-                                    EDAC_MOD_STR,
-                                    "ERROR ADDRESS NOT mapped to a MC", NULL);
+                                    "ERROR ADDRESS NOT mapped to a MC",
+                                    "");
                return;
        }
 
@@ -1980,17 +1970,16 @@ static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m)
        if (csrow < 0) {
                amd64_mc_err(mci, "ERROR_ADDRESS (0x%lx) NOT mapped to CS\n",
                                  (unsigned long)sys_addr);
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                     page, offset, 0,
                                     -1, -1, -1,
-                                    EDAC_MOD_STR,
                                     "ERROR ADDRESS NOT mapped to CS",
-                                    NULL);
+                                    "");
        } else {
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                     page, offset, 0,
                                     csrow, -1, -1,
-                                    EDAC_MOD_STR, "", NULL);
+                                    "", "");
        }
 }
 
@@ -2047,9 +2036,9 @@ static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f3_id)
 
                return -ENODEV;
        }
-       debugf1("F1: %s\n", pci_name(pvt->F1));
-       debugf1("F2: %s\n", pci_name(pvt->F2));
-       debugf1("F3: %s\n", pci_name(pvt->F3));
+       edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
+       edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
+       edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
 
        return 0;
 }
@@ -2076,15 +2065,15 @@ static void read_mc_regs(struct amd64_pvt *pvt)
         * those are Read-As-Zero
         */
        rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
-       debugf0("  TOP_MEM:  0x%016llx\n", pvt->top_mem);
+       edac_dbg(0, "  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, pvt->top_mem2);
-               debugf0("  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
+               edac_dbg(0, "  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
        } else
-               debugf0("  TOP_MEM2 disabled.\n");
+               edac_dbg(0, "  TOP_MEM2 disabled\n");
 
        amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
 
@@ -2100,17 +2089,17 @@ static void read_mc_regs(struct amd64_pvt *pvt)
                if (!rw)
                        continue;
 
-               debugf1("  DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
-                       range,
-                       get_dram_base(pvt, range),
-                       get_dram_limit(pvt, range));
+               edac_dbg(1, "  DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
+                        range,
+                        get_dram_base(pvt, range),
+                        get_dram_limit(pvt, range));
 
-               debugf1("   IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
-                       dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
-                       (rw & 0x1) ? "R" : "-",
-                       (rw & 0x2) ? "W" : "-",
-                       dram_intlv_sel(pvt, range),
-                       dram_dst_node(pvt, range));
+               edac_dbg(1, "   IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
+                        dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
+                        (rw & 0x1) ? "R" : "-",
+                        (rw & 0x2) ? "W" : "-",
+                        dram_intlv_sel(pvt, range),
+                        dram_dst_node(pvt, range));
        }
 
        read_dct_base_mask(pvt);
@@ -2191,9 +2180,9 @@ static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
 
        nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT);
 
-       debugf0("  (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode);
-       debugf0("    nr_pages/channel= %u  channel-count = %d\n",
-               nr_pages, pvt->channel_count);
+       edac_dbg(0, "  (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode);
+       edac_dbg(0, "    nr_pages/channel= %u  channel-count = %d\n",
+                nr_pages, pvt->channel_count);
 
        return nr_pages;
 }
@@ -2205,6 +2194,7 @@ static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
 static int init_csrows(struct mem_ctl_info *mci)
 {
        struct csrow_info *csrow;
+       struct dimm_info *dimm;
        struct amd64_pvt *pvt = mci->pvt_info;
        u64 base, mask;
        u32 val;
@@ -2217,22 +2207,19 @@ static int init_csrows(struct mem_ctl_info *mci)
 
        pvt->nbcfg = val;
 
-       debugf0("node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
-               pvt->mc_node_id, val,
-               !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
+       edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
+                pvt->mc_node_id, val,
+                !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
 
        for_each_chip_select(i, 0, pvt) {
-               csrow = &mci->csrows[i];
+               csrow = mci->csrows[i];
 
                if (!csrow_enabled(i, 0, pvt) && !csrow_enabled(i, 1, pvt)) {
-                       debugf1("----CSROW %d EMPTY for node %d\n", i,
-                               pvt->mc_node_id);
+                       edac_dbg(1, "----CSROW %d VALID for MC node %d\n",
+                                i, pvt->mc_node_id);
                        continue;
                }
 
-               debugf1("----CSROW %d VALID for MC node %d\n",
-                       i, pvt->mc_node_id);
-
                empty = 0;
                if (csrow_enabled(i, 0, pvt))
                        nr_pages = amd64_csrow_nr_pages(pvt, 0, i);
@@ -2244,8 +2231,9 @@ static int init_csrows(struct mem_ctl_info *mci)
 
                mtype = amd64_determine_memory_type(pvt, i);
 
-               debugf1("  for MC node %d csrow %d:\n", pvt->mc_node_id, i);
-               debugf1("    nr_pages: %u\n", nr_pages * pvt->channel_count);
+               edac_dbg(1, "  for MC node %d csrow %d:\n", pvt->mc_node_id, i);
+               edac_dbg(1, "    nr_pages: %u\n",
+                        nr_pages * pvt->channel_count);
 
                /*
                 * determine whether CHIPKILL or JUST ECC or NO ECC is operating
@@ -2257,9 +2245,10 @@ static int init_csrows(struct mem_ctl_info *mci)
                        edac_mode = EDAC_NONE;
 
                for (j = 0; j < pvt->channel_count; j++) {
-                       csrow->channels[j].dimm->mtype = mtype;
-                       csrow->channels[j].dimm->edac_mode = edac_mode;
-                       csrow->channels[j].dimm->nr_pages = nr_pages;
+                       dimm = csrow->channels[j]->dimm;
+                       dimm->mtype = mtype;
+                       dimm->edac_mode = edac_mode;
+                       dimm->nr_pages = nr_pages;
                }
        }
 
@@ -2296,9 +2285,9 @@ static bool amd64_nb_mce_bank_enabled_on_node(unsigned nid)
                struct msr *reg = per_cpu_ptr(msrs, cpu);
                nbe = reg->l & MSR_MCGCTL_NBE;
 
-               debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
-                       cpu, reg->q,
-                       (nbe ? "enabled" : "disabled"));
+               edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
+                        cpu, reg->q,
+                        (nbe ? "enabled" : "disabled"));
 
                if (!nbe)
                        goto out;
@@ -2369,8 +2358,8 @@ static bool enable_ecc_error_reporting(struct ecc_settings *s, u8 nid,
 
        amd64_read_pci_cfg(F3, NBCFG, &value);
 
-       debugf0("1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
-               nid, value, !!(value & NBCFG_ECC_ENABLE));
+       edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
+                nid, value, !!(value & NBCFG_ECC_ENABLE));
 
        if (!(value & NBCFG_ECC_ENABLE)) {
                amd64_warn("DRAM ECC disabled on this node, enabling...\n");
@@ -2394,8 +2383,8 @@ static bool enable_ecc_error_reporting(struct ecc_settings *s, u8 nid,
                s->flags.nb_ecc_prev = 1;
        }
 
-       debugf0("2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
-               nid, value, !!(value & NBCFG_ECC_ENABLE));
+       edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
+                nid, value, !!(value & NBCFG_ECC_ENABLE));
 
        return ret;
 }
@@ -2463,26 +2452,29 @@ static bool ecc_enabled(struct pci_dev *F3, u8 nid)
        return true;
 }
 
-struct mcidev_sysfs_attribute sysfs_attrs[ARRAY_SIZE(amd64_dbg_attrs) +
-                                         ARRAY_SIZE(amd64_inj_attrs) +
-                                         1];
-
-struct mcidev_sysfs_attribute terminator = { .attr = { .name = NULL } };
-
-static void set_mc_sysfs_attrs(struct mem_ctl_info *mci)
+static int set_mc_sysfs_attrs(struct mem_ctl_info *mci)
 {
-       unsigned int i = 0, j = 0;
+       int rc;
 
-       for (; i < ARRAY_SIZE(amd64_dbg_attrs); i++)
-               sysfs_attrs[i] = amd64_dbg_attrs[i];
+       rc = amd64_create_sysfs_dbg_files(mci);
+       if (rc < 0)
+               return rc;
 
-       if (boot_cpu_data.x86 >= 0x10)
-               for (j = 0; j < ARRAY_SIZE(amd64_inj_attrs); j++, i++)
-                       sysfs_attrs[i] = amd64_inj_attrs[j];
+       if (boot_cpu_data.x86 >= 0x10) {
+               rc = amd64_create_sysfs_inject_files(mci);
+               if (rc < 0)
+                       return rc;
+       }
+
+       return 0;
+}
 
-       sysfs_attrs[i] = terminator;
+static void del_mc_sysfs_attrs(struct mem_ctl_info *mci)
+{
+       amd64_remove_sysfs_dbg_files(mci);
 
-       mci->mc_driver_sysfs_attributes = sysfs_attrs;
+       if (boot_cpu_data.x86 >= 0x10)
+               amd64_remove_sysfs_inject_files(mci);
 }
 
 static void setup_mci_misc_attrs(struct mem_ctl_info *mci,
@@ -2601,20 +2593,22 @@ static int amd64_init_one_instance(struct pci_dev *F2)
                goto err_siblings;
 
        mci->pvt_info = pvt;
-       mci->dev = &pvt->F2->dev;
+       mci->pdev = &pvt->F2->dev;
 
        setup_mci_misc_attrs(mci, fam_type);
 
        if (init_csrows(mci))
                mci->edac_cap = EDAC_FLAG_NONE;
 
-       set_mc_sysfs_attrs(mci);
-
        ret = -ENODEV;
        if (edac_mc_add_mc(mci)) {
-               debugf1("failed edac_mc_add_mc()\n");
+               edac_dbg(1, "failed edac_mc_add_mc()\n");
                goto err_add_mc;
        }
+       if (set_mc_sysfs_attrs(mci)) {
+               edac_dbg(1, "failed edac_mc_add_mc()\n");
+               goto err_add_sysfs;
+       }
 
        /* register stuff with EDAC MCE */
        if (report_gart_errors)
@@ -2628,6 +2622,8 @@ static int amd64_init_one_instance(struct pci_dev *F2)
 
        return 0;
 
+err_add_sysfs:
+       edac_mc_del_mc(mci->pdev);
 err_add_mc:
        edac_mc_free(mci);
 
@@ -2651,7 +2647,7 @@ static int __devinit amd64_probe_one_instance(struct pci_dev *pdev,
 
        ret = pci_enable_device(pdev);
        if (ret < 0) {
-               debugf0("ret=%d\n", ret);
+               edac_dbg(0, "ret=%d\n", ret);
                return -EIO;
        }
 
@@ -2698,6 +2694,8 @@ static void __devexit amd64_remove_one_instance(struct pci_dev *pdev)
        struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
        struct ecc_settings *s = ecc_stngs[nid];
 
+       mci = find_mci_by_dev(&pdev->dev);
+       del_mc_sysfs_attrs(mci);
        /* Remove from EDAC CORE tracking list */
        mci = edac_mc_del_mc(&pdev->dev);
        if (!mci)
index 9a666cb985b2bd446edba7c249f3740d23e49d8e..8d4804732bacb77be17f07465e48f24ea513ab0c 100644 (file)
@@ -413,20 +413,33 @@ struct ecc_settings {
 };
 
 #ifdef CONFIG_EDAC_DEBUG
-#define NUM_DBG_ATTRS 5
+int amd64_create_sysfs_dbg_files(struct mem_ctl_info *mci);
+void amd64_remove_sysfs_dbg_files(struct mem_ctl_info *mci);
+
 #else
-#define NUM_DBG_ATTRS 0
+static inline int amd64_create_sysfs_dbg_files(struct mem_ctl_info *mci)
+{
+       return 0;
+}
+static void inline amd64_remove_sysfs_dbg_files(struct mem_ctl_info *mci)
+{
+}
 #endif
 
 #ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION
-#define NUM_INJ_ATTRS 5
+int amd64_create_sysfs_inject_files(struct mem_ctl_info *mci);
+void amd64_remove_sysfs_inject_files(struct mem_ctl_info *mci);
+
 #else
-#define NUM_INJ_ATTRS 0
+static inline int amd64_create_sysfs_inject_files(struct mem_ctl_info *mci)
+{
+       return 0;
+}
+static inline void amd64_remove_sysfs_inject_files(struct mem_ctl_info *mci)
+{
+}
 #endif
 
-extern struct mcidev_sysfs_attribute amd64_dbg_attrs[NUM_DBG_ATTRS],
-                                    amd64_inj_attrs[NUM_INJ_ATTRS];
-
 /*
  * Each of the PCI Device IDs types have their own set of hardware accessor
  * functions and per device encoding/decoding logic.
@@ -460,3 +473,5 @@ int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
 
 int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
                             u64 *hole_offset, u64 *hole_size);
+
+#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
index e3562288f4ce80589ab0680f4fc179335da80822..2c1bbf7406058f4f80e1edb170db160c488cdcbc 100644 (file)
@@ -1,8 +1,11 @@
 #include "amd64_edac.h"
 
 #define EDAC_DCT_ATTR_SHOW(reg)                                                \
-static ssize_t amd64_##reg##_show(struct mem_ctl_info *mci, char *data)        \
+static ssize_t amd64_##reg##_show(struct device *dev,                  \
+                              struct device_attribute *mattr,          \
+                              char *data)                              \
 {                                                                      \
+       struct mem_ctl_info *mci = to_mci(dev);                         \
        struct amd64_pvt *pvt = mci->pvt_info;                          \
                return sprintf(data, "0x%016llx\n", (u64)pvt->reg);     \
 }
@@ -12,8 +15,12 @@ EDAC_DCT_ATTR_SHOW(dbam0);
 EDAC_DCT_ATTR_SHOW(top_mem);
 EDAC_DCT_ATTR_SHOW(top_mem2);
 
-static ssize_t amd64_hole_show(struct mem_ctl_info *mci, char *data)
+static ssize_t amd64_hole_show(struct device *dev,
+                              struct device_attribute *mattr,
+                              char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
+
        u64 hole_base = 0;
        u64 hole_offset = 0;
        u64 hole_size = 0;
@@ -27,46 +34,40 @@ static ssize_t amd64_hole_show(struct mem_ctl_info *mci, char *data)
 /*
  * update NUM_DBG_ATTRS in case you add new members
  */
-struct mcidev_sysfs_attribute amd64_dbg_attrs[] = {
+static DEVICE_ATTR(dhar, S_IRUGO, amd64_dhar_show, NULL);
+static DEVICE_ATTR(dbam, S_IRUGO, amd64_dbam0_show, NULL);
+static DEVICE_ATTR(topmem, S_IRUGO, amd64_top_mem_show, NULL);
+static DEVICE_ATTR(topmem2, S_IRUGO, amd64_top_mem2_show, NULL);
+static DEVICE_ATTR(dram_hole, S_IRUGO, amd64_hole_show, NULL);
+
+int amd64_create_sysfs_dbg_files(struct mem_ctl_info *mci)
+{
+       int rc;
+
+       rc = device_create_file(&mci->dev, &dev_attr_dhar);
+       if (rc < 0)
+               return rc;
+       rc = device_create_file(&mci->dev, &dev_attr_dbam);
+       if (rc < 0)
+               return rc;
+       rc = device_create_file(&mci->dev, &dev_attr_topmem);
+       if (rc < 0)
+               return rc;
+       rc = device_create_file(&mci->dev, &dev_attr_topmem2);
+       if (rc < 0)
+               return rc;
+       rc = device_create_file(&mci->dev, &dev_attr_dram_hole);
+       if (rc < 0)
+               return rc;
 
-       {
-               .attr = {
-                       .name = "dhar",
-                       .mode = (S_IRUGO)
-               },
-               .show = amd64_dhar_show,
-               .store = NULL,
-       },
-       {
-               .attr = {
-                       .name = "dbam",
-                       .mode = (S_IRUGO)
-               },
-               .show = amd64_dbam0_show,
-               .store = NULL,
-       },
-       {
-               .attr = {
-                       .name = "topmem",
-                       .mode = (S_IRUGO)
-               },
-               .show = amd64_top_mem_show,
-               .store = NULL,
-       },
-       {
-               .attr = {
-                       .name = "topmem2",
-                       .mode = (S_IRUGO)
-               },
-               .show = amd64_top_mem2_show,
-               .store = NULL,
-       },
-       {
-               .attr = {
-                       .name = "dram_hole",
-                       .mode = (S_IRUGO)
-               },
-               .show = amd64_hole_show,
-               .store = NULL,
-       },
-};
+       return 0;
+}
+
+void amd64_remove_sysfs_dbg_files(struct mem_ctl_info *mci)
+{
+       device_remove_file(&mci->dev, &dev_attr_dhar);
+       device_remove_file(&mci->dev, &dev_attr_dbam);
+       device_remove_file(&mci->dev, &dev_attr_topmem);
+       device_remove_file(&mci->dev, &dev_attr_topmem2);
+       device_remove_file(&mci->dev, &dev_attr_dram_hole);
+}
index 303f10e03dda946b5aebce3c82c1f07b564ca53b..53d972e00dfb084d812419463b7a0984a9c1deda 100644 (file)
@@ -1,7 +1,10 @@
 #include "amd64_edac.h"
 
-static ssize_t amd64_inject_section_show(struct mem_ctl_info *mci, char *buf)
+static ssize_t amd64_inject_section_show(struct device *dev,
+                                        struct device_attribute *mattr,
+                                        char *buf)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct amd64_pvt *pvt = mci->pvt_info;
        return sprintf(buf, "0x%x\n", pvt->injection.section);
 }
@@ -12,9 +15,11 @@ static ssize_t amd64_inject_section_show(struct mem_ctl_info *mci, char *buf)
  *
  * range: 0..3
  */
-static ssize_t amd64_inject_section_store(struct mem_ctl_info *mci,
+static ssize_t amd64_inject_section_store(struct device *dev,
+                                         struct device_attribute *mattr,
                                          const char *data, size_t count)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct amd64_pvt *pvt = mci->pvt_info;
        unsigned long value;
        int ret = 0;
@@ -33,8 +38,11 @@ static ssize_t amd64_inject_section_store(struct mem_ctl_info *mci,
        return ret;
 }
 
-static ssize_t amd64_inject_word_show(struct mem_ctl_info *mci, char *buf)
+static ssize_t amd64_inject_word_show(struct device *dev,
+                                       struct device_attribute *mattr,
+                                       char *buf)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct amd64_pvt *pvt = mci->pvt_info;
        return sprintf(buf, "0x%x\n", pvt->injection.word);
 }
@@ -45,9 +53,11 @@ static ssize_t amd64_inject_word_show(struct mem_ctl_info *mci, char *buf)
  *
  * range: 0..8
  */
-static ssize_t amd64_inject_word_store(struct mem_ctl_info *mci,
-                                       const char *data, size_t count)
+static ssize_t amd64_inject_word_store(struct device *dev,
+                                      struct device_attribute *mattr,
+                                      const char *data, size_t count)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct amd64_pvt *pvt = mci->pvt_info;
        unsigned long value;
        int ret = 0;
@@ -66,8 +76,11 @@ static ssize_t amd64_inject_word_store(struct mem_ctl_info *mci,
        return ret;
 }
 
-static ssize_t amd64_inject_ecc_vector_show(struct mem_ctl_info *mci, char *buf)
+static ssize_t amd64_inject_ecc_vector_show(struct device *dev,
+                                           struct device_attribute *mattr,
+                                           char *buf)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct amd64_pvt *pvt = mci->pvt_info;
        return sprintf(buf, "0x%x\n", pvt->injection.bit_map);
 }
@@ -77,9 +90,11 @@ static ssize_t amd64_inject_ecc_vector_show(struct mem_ctl_info *mci, char *buf)
  * corresponding bit within the error injection word above. When used during a
  * DRAM ECC read, it holds the contents of the of the DRAM ECC bits.
  */
-static ssize_t amd64_inject_ecc_vector_store(struct mem_ctl_info *mci,
-                                            const char *data, size_t count)
+static ssize_t amd64_inject_ecc_vector_store(struct device *dev,
+                                      struct device_attribute *mattr,
+                                      const char *data, size_t count)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct amd64_pvt *pvt = mci->pvt_info;
        unsigned long value;
        int ret = 0;
@@ -103,9 +118,11 @@ static ssize_t amd64_inject_ecc_vector_store(struct mem_ctl_info *mci,
  * Do a DRAM ECC read. Assemble staged values in the pvt area, format into
  * fields needed by the injection registers and read the NB Array Data Port.
  */
-static ssize_t amd64_inject_read_store(struct mem_ctl_info *mci,
-                                       const char *data, size_t count)
+static ssize_t amd64_inject_read_store(struct device *dev,
+                                      struct device_attribute *mattr,
+                                      const char *data, size_t count)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct amd64_pvt *pvt = mci->pvt_info;
        unsigned long value;
        u32 section, word_bits;
@@ -125,7 +142,8 @@ static ssize_t amd64_inject_read_store(struct mem_ctl_info *mci,
                /* Issue 'word' and 'bit' along with the READ request */
                amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits);
 
-               debugf0("section=0x%x word_bits=0x%x\n", section, word_bits);
+               edac_dbg(0, "section=0x%x word_bits=0x%x\n",
+                        section, word_bits);
 
                return count;
        }
@@ -136,9 +154,11 @@ static ssize_t amd64_inject_read_store(struct mem_ctl_info *mci,
  * Do a DRAM ECC write. Assemble staged values in the pvt area and format into
  * fields needed by the injection registers.
  */
-static ssize_t amd64_inject_write_store(struct mem_ctl_info *mci,
+static ssize_t amd64_inject_write_store(struct device *dev,
+                                       struct device_attribute *mattr,
                                        const char *data, size_t count)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct amd64_pvt *pvt = mci->pvt_info;
        unsigned long value;
        u32 section, word_bits;
@@ -158,7 +178,8 @@ static ssize_t amd64_inject_write_store(struct mem_ctl_info *mci,
                /* Issue 'word' and 'bit' along with the READ request */
                amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits);
 
-               debugf0("section=0x%x word_bits=0x%x\n", section, word_bits);
+               edac_dbg(0, "section=0x%x word_bits=0x%x\n",
+                        section, word_bits);
 
                return count;
        }
@@ -168,46 +189,47 @@ static ssize_t amd64_inject_write_store(struct mem_ctl_info *mci,
 /*
  * update NUM_INJ_ATTRS in case you add new members
  */
-struct mcidev_sysfs_attribute amd64_inj_attrs[] = {
-
-       {
-               .attr = {
-                       .name = "inject_section",
-                       .mode = (S_IRUGO | S_IWUSR)
-               },
-               .show = amd64_inject_section_show,
-               .store = amd64_inject_section_store,
-       },
-       {
-               .attr = {
-                       .name = "inject_word",
-                       .mode = (S_IRUGO | S_IWUSR)
-               },
-               .show = amd64_inject_word_show,
-               .store = amd64_inject_word_store,
-       },
-       {
-               .attr = {
-                       .name = "inject_ecc_vector",
-                       .mode = (S_IRUGO | S_IWUSR)
-               },
-               .show = amd64_inject_ecc_vector_show,
-               .store = amd64_inject_ecc_vector_store,
-       },
-       {
-               .attr = {
-                       .name = "inject_write",
-                       .mode = (S_IRUGO | S_IWUSR)
-               },
-               .show = NULL,
-               .store = amd64_inject_write_store,
-       },
-       {
-               .attr = {
-                       .name = "inject_read",
-                       .mode = (S_IRUGO | S_IWUSR)
-               },
-               .show = NULL,
-               .store = amd64_inject_read_store,
-       },
-};
+
+static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR,
+                  amd64_inject_section_show, amd64_inject_section_store);
+static DEVICE_ATTR(inject_word, S_IRUGO | S_IWUSR,
+                  amd64_inject_word_show, amd64_inject_word_store);
+static DEVICE_ATTR(inject_ecc_vector, S_IRUGO | S_IWUSR,
+                  amd64_inject_ecc_vector_show, amd64_inject_ecc_vector_store);
+static DEVICE_ATTR(inject_write, S_IRUGO | S_IWUSR,
+                  NULL, amd64_inject_write_store);
+static DEVICE_ATTR(inject_read, S_IRUGO | S_IWUSR,
+                  NULL, amd64_inject_read_store);
+
+
+int amd64_create_sysfs_inject_files(struct mem_ctl_info *mci)
+{
+       int rc;
+
+       rc = device_create_file(&mci->dev, &dev_attr_inject_section);
+       if (rc < 0)
+               return rc;
+       rc = device_create_file(&mci->dev, &dev_attr_inject_word);
+       if (rc < 0)
+               return rc;
+       rc = device_create_file(&mci->dev, &dev_attr_inject_ecc_vector);
+       if (rc < 0)
+               return rc;
+       rc = device_create_file(&mci->dev, &dev_attr_inject_write);
+       if (rc < 0)
+               return rc;
+       rc = device_create_file(&mci->dev, &dev_attr_inject_read);
+       if (rc < 0)
+               return rc;
+
+       return 0;
+}
+
+void amd64_remove_sysfs_inject_files(struct mem_ctl_info *mci)
+{
+       device_remove_file(&mci->dev, &dev_attr_inject_section);
+       device_remove_file(&mci->dev, &dev_attr_inject_word);
+       device_remove_file(&mci->dev, &dev_attr_inject_ecc_vector);
+       device_remove_file(&mci->dev, &dev_attr_inject_write);
+       device_remove_file(&mci->dev, &dev_attr_inject_read);
+}
index 9774d443fa57616a9f7e64ba06a2fae3e3bc2f92..29eeb68a200caf7cb091698d1acbecf4eca80bcb 100644 (file)
@@ -105,7 +105,7 @@ static void amd76x_get_error_info(struct mem_ctl_info *mci,
 {
        struct pci_dev *pdev;
 
-       pdev = to_pci_dev(mci->dev);
+       pdev = to_pci_dev(mci->pdev);
        pci_read_config_dword(pdev, AMD76X_ECC_MODE_STATUS,
                        &info->ecc_mode_status);
 
@@ -145,10 +145,10 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci,
 
                if (handle_errors) {
                        row = (info->ecc_mode_status >> 4) & 0xf;
-                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
-                                            mci->csrows[row].first_page, 0, 0,
+                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
+                                            mci->csrows[row]->first_page, 0, 0,
                                             row, 0, -1,
-                                            mci->ctl_name, "", NULL);
+                                            mci->ctl_name, "");
                }
        }
 
@@ -160,10 +160,10 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci,
 
                if (handle_errors) {
                        row = info->ecc_mode_status & 0xf;
-                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
-                                            mci->csrows[row].first_page, 0, 0,
+                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
+                                            mci->csrows[row]->first_page, 0, 0,
                                             row, 0, -1,
-                                            mci->ctl_name, "", NULL);
+                                            mci->ctl_name, "");
                }
        }
 
@@ -180,7 +180,7 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci,
 static void amd76x_check(struct mem_ctl_info *mci)
 {
        struct amd76x_error_info info;
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
        amd76x_get_error_info(mci, &info);
        amd76x_process_error_info(mci, &info, 1);
 }
@@ -194,8 +194,8 @@ static void amd76x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
        int index;
 
        for (index = 0; index < mci->nr_csrows; index++) {
-               csrow = &mci->csrows[index];
-               dimm = csrow->channels[0].dimm;
+               csrow = mci->csrows[index];
+               dimm = csrow->channels[0]->dimm;
 
                /* find the DRAM Chip Select Base address and mask */
                pci_read_config_dword(pdev,
@@ -241,7 +241,7 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx)
        u32 ems_mode;
        struct amd76x_error_info discard;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
        pci_read_config_dword(pdev, AMD76X_ECC_MODE_STATUS, &ems);
        ems_mode = (ems >> 10) & 0x3;
 
@@ -256,8 +256,8 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx)
        if (mci == NULL)
                return -ENOMEM;
 
-       debugf0("%s(): mci = %p\n", __func__, mci);
-       mci->dev = &pdev->dev;
+       edac_dbg(0, "mci = %p\n", mci);
+       mci->pdev = &pdev->dev;
        mci->mtype_cap = MEM_FLAG_RDDR;
        mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
        mci->edac_cap = ems_mode ?
@@ -276,7 +276,7 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx)
         * type of memory controller.  The ID is therefore hardcoded to 0.
         */
        if (edac_mc_add_mc(mci)) {
-               debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(3, "failed edac_mc_add_mc()\n");
                goto fail;
        }
 
@@ -292,7 +292,7 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx)
        }
 
        /* get this far and it's successful */
-       debugf3("%s(): success\n", __func__);
+       edac_dbg(3, "success\n");
        return 0;
 
 fail:
@@ -304,7 +304,7 @@ fail:
 static int __devinit amd76x_init_one(struct pci_dev *pdev,
                                const struct pci_device_id *ent)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /* don't need to call pci_enable_device() */
        return amd76x_probe1(pdev, ent->driver_data);
@@ -322,7 +322,7 @@ static void __devexit amd76x_remove_one(struct pci_dev *pdev)
 {
        struct mem_ctl_info *mci;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        if (amd76x_pci)
                edac_pci_release_generic_ctl(amd76x_pci);
index 69ee6aab5c716fefd0b919a5da4b46ef5c7c4a64..a1bbd8edd2575e4faf0633c972f1a6cf12a3815a 100644 (file)
@@ -33,10 +33,10 @@ struct cell_edac_priv
 static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar)
 {
        struct cell_edac_priv           *priv = mci->pvt_info;
-       struct csrow_info               *csrow = &mci->csrows[0];
+       struct csrow_info               *csrow = mci->csrows[0];
        unsigned long                   address, pfn, offset, syndrome;
 
-       dev_dbg(mci->dev, "ECC CE err on node %d, channel %d, ar = 0x%016llx\n",
+       dev_dbg(mci->pdev, "ECC CE err on node %d, channel %d, ar = 0x%016llx\n",
                priv->node, chan, ar);
 
        /* Address decoding is likely a bit bogus, to dbl check */
@@ -48,18 +48,18 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar)
        syndrome = (ar & 0x000000001fe00000ul) >> 21;
 
        /* TODO: Decoding of the error address */
-       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                             csrow->first_page + pfn, offset, syndrome,
-                            0, chan, -1, "", "", NULL);
+                            0, chan, -1, "", "");
 }
 
 static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar)
 {
        struct cell_edac_priv           *priv = mci->pvt_info;
-       struct csrow_info               *csrow = &mci->csrows[0];
+       struct csrow_info               *csrow = mci->csrows[0];
        unsigned long                   address, pfn, offset;
 
-       dev_dbg(mci->dev, "ECC UE err on node %d, channel %d, ar = 0x%016llx\n",
+       dev_dbg(mci->pdev, "ECC UE err on node %d, channel %d, ar = 0x%016llx\n",
                priv->node, chan, ar);
 
        /* Address decoding is likely a bit bogus, to dbl check */
@@ -70,9 +70,9 @@ static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar)
        offset = address & ~PAGE_MASK;
 
        /* TODO: Decoding of the error address */
-       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                             csrow->first_page + pfn, offset, 0,
-                            0, chan, -1, "", "", NULL);
+                            0, chan, -1, "", "");
 }
 
 static void cell_edac_check(struct mem_ctl_info *mci)
@@ -83,7 +83,7 @@ static void cell_edac_check(struct mem_ctl_info *mci)
        fir = in_be64(&priv->regs->mic_fir);
 #ifdef DEBUG
        if (fir != priv->prev_fir) {
-               dev_dbg(mci->dev, "fir change : 0x%016lx\n", fir);
+               dev_dbg(mci->pdev, "fir change : 0x%016lx\n", fir);
                priv->prev_fir = fir;
        }
 #endif
@@ -119,14 +119,14 @@ static void cell_edac_check(struct mem_ctl_info *mci)
                mb();   /* sync up */
 #ifdef DEBUG
                fir = in_be64(&priv->regs->mic_fir);
-               dev_dbg(mci->dev, "fir clear  : 0x%016lx\n", fir);
+               dev_dbg(mci->pdev, "fir clear  : 0x%016lx\n", fir);
 #endif
        }
 }
 
 static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci)
 {
-       struct csrow_info               *csrow = &mci->csrows[0];
+       struct csrow_info               *csrow = mci->csrows[0];
        struct dimm_info                *dimm;
        struct cell_edac_priv           *priv = mci->pvt_info;
        struct device_node              *np;
@@ -150,12 +150,12 @@ static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci)
                csrow->last_page = csrow->first_page + nr_pages - 1;
 
                for (j = 0; j < csrow->nr_channels; j++) {
-                       dimm = csrow->channels[j].dimm;
+                       dimm = csrow->channels[j]->dimm;
                        dimm->mtype = MEM_XDR;
                        dimm->edac_mode = EDAC_SECDED;
                        dimm->nr_pages = nr_pages / csrow->nr_channels;
                }
-               dev_dbg(mci->dev,
+               dev_dbg(mci->pdev,
                        "Initialized on node %d, chanmask=0x%x,"
                        " first_page=0x%lx, nr_pages=0x%x\n",
                        priv->node, priv->chanmask,
@@ -212,7 +212,7 @@ static int __devinit cell_edac_probe(struct platform_device *pdev)
        priv->regs = regs;
        priv->node = pdev->id;
        priv->chanmask = chanmask;
-       mci->dev = &pdev->dev;
+       mci->pdev = &pdev->dev;
        mci->mtype_cap = MEM_FLAG_XDR;
        mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
        mci->edac_cap = EDAC_FLAG_EC | EDAC_FLAG_SECDED;
index e22030a9de66fbc0c38fd2d0b274b1464dda7a2f..c2ef1349587368d666d9465a3c6996b893d10771 100644 (file)
@@ -316,13 +316,12 @@ static void get_total_mem(struct cpc925_mc_pdata *pdata)
                reg += aw;
                size = of_read_number(reg, sw);
                reg += sw;
-               debugf1("%s: start 0x%lx, size 0x%lx\n", __func__,
-                       start, size);
+               edac_dbg(1, "start 0x%lx, size 0x%lx\n", start, size);
                pdata->total_mem += size;
        } while (reg < reg_end);
 
        of_node_put(np);
-       debugf0("%s: total_mem 0x%lx\n", __func__, pdata->total_mem);
+       edac_dbg(0, "total_mem 0x%lx\n", pdata->total_mem);
 }
 
 static void cpc925_init_csrows(struct mem_ctl_info *mci)
@@ -330,8 +329,9 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci)
        struct cpc925_mc_pdata *pdata = mci->pvt_info;
        struct csrow_info *csrow;
        struct dimm_info *dimm;
+       enum dev_type dtype;
        int index, j;
-       u32 mbmr, mbbar, bba;
+       u32 mbmr, mbbar, bba, grain;
        unsigned long row_size, nr_pages, last_nr_pages = 0;
 
        get_total_mem(pdata);
@@ -347,7 +347,7 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci)
                if (bba == 0)
                        continue; /* not populated */
 
-               csrow = &mci->csrows[index];
+               csrow = mci->csrows[index];
 
                row_size = bba * (1UL << 28);   /* 256M */
                csrow->first_page = last_nr_pages;
@@ -355,37 +355,36 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci)
                csrow->last_page = csrow->first_page + nr_pages - 1;
                last_nr_pages = csrow->last_page + 1;
 
+               switch (csrow->nr_channels) {
+               case 1: /* Single channel */
+                       grain = 32; /* four-beat burst of 32 bytes */
+                       break;
+               case 2: /* Dual channel */
+               default:
+                       grain = 64; /* four-beat burst of 64 bytes */
+                       break;
+               }
+               switch ((mbmr & MBMR_MODE_MASK) >> MBMR_MODE_SHIFT) {
+               case 6: /* 0110, no way to differentiate X8 VS X16 */
+               case 5: /* 0101 */
+               case 8: /* 1000 */
+                       dtype = DEV_X16;
+                       break;
+               case 7: /* 0111 */
+               case 9: /* 1001 */
+                       dtype = DEV_X8;
+                       break;
+               default:
+                       dtype = DEV_UNKNOWN;
+               break;
+               }
                for (j = 0; j < csrow->nr_channels; j++) {
-                       dimm = csrow->channels[j].dimm;
-
+                       dimm = csrow->channels[j]->dimm;
                        dimm->nr_pages = nr_pages / csrow->nr_channels;
                        dimm->mtype = MEM_RDDR;
                        dimm->edac_mode = EDAC_SECDED;
-
-                       switch (csrow->nr_channels) {
-                       case 1: /* Single channel */
-                               dimm->grain = 32; /* four-beat burst of 32 bytes */
-                               break;
-                       case 2: /* Dual channel */
-                       default:
-                               dimm->grain = 64; /* four-beat burst of 64 bytes */
-                               break;
-                       }
-
-                       switch ((mbmr & MBMR_MODE_MASK) >> MBMR_MODE_SHIFT) {
-                       case 6: /* 0110, no way to differentiate X8 VS X16 */
-                       case 5: /* 0101 */
-                       case 8: /* 1000 */
-                               dimm->dtype = DEV_X16;
-                               break;
-                       case 7: /* 0111 */
-                       case 9: /* 1001 */
-                               dimm->dtype = DEV_X8;
-                               break;
-                       default:
-                               dimm->dtype = DEV_UNKNOWN;
-                               break;
-                       }
+                       dimm->grain = grain;
+                       dimm->dtype = dtype;
                }
        }
 }
@@ -463,7 +462,7 @@ static void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear,
        *csrow = rank;
 
 #ifdef CONFIG_EDAC_DEBUG
-       if (mci->csrows[rank].first_page == 0) {
+       if (mci->csrows[rank]->first_page == 0) {
                cpc925_mc_printk(mci, KERN_ERR, "ECC occurs in a "
                        "non-populated csrow, broken hardware?\n");
                return;
@@ -471,7 +470,7 @@ static void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear,
 #endif
 
        /* Revert csrow number */
-       pa = mci->csrows[rank].first_page << PAGE_SHIFT;
+       pa = mci->csrows[rank]->first_page << PAGE_SHIFT;
 
        /* Revert column address */
        col += bcnt;
@@ -512,7 +511,7 @@ static void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear,
        *offset = pa & (PAGE_SIZE - 1);
        *pfn = pa >> PAGE_SHIFT;
 
-       debugf0("%s: ECC physical address 0x%lx\n", __func__, pa);
+       edac_dbg(0, "ECC physical address 0x%lx\n", pa);
 }
 
 static int cpc925_mc_find_channel(struct mem_ctl_info *mci, u16 syndrome)
@@ -555,18 +554,18 @@ static void cpc925_mc_check(struct mem_ctl_info *mci)
        if (apiexcp & CECC_EXCP_DETECTED) {
                cpc925_mc_printk(mci, KERN_INFO, "DRAM CECC Fault\n");
                channel = cpc925_mc_find_channel(mci, syndrome);
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                     pfn, offset, syndrome,
                                     csrow, channel, -1,
-                                    mci->ctl_name, "", NULL);
+                                    mci->ctl_name, "");
        }
 
        if (apiexcp & UECC_EXCP_DETECTED) {
                cpc925_mc_printk(mci, KERN_INFO, "DRAM UECC Fault\n");
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                     pfn, offset, 0,
                                     csrow, -1, -1,
-                                    mci->ctl_name, "", NULL);
+                                    mci->ctl_name, "");
        }
 
        cpc925_mc_printk(mci, KERN_INFO, "Dump registers:\n");
@@ -852,8 +851,8 @@ static void cpc925_add_edac_devices(void __iomem *vbase)
                        goto err2;
                }
 
-               debugf0("%s: Successfully added edac device for %s\n",
-                       __func__, dev_info->ctl_name);
+               edac_dbg(0, "Successfully added edac device for %s\n",
+                        dev_info->ctl_name);
 
                continue;
 
@@ -884,8 +883,8 @@ static void cpc925_del_edac_devices(void)
                if (dev_info->exit)
                        dev_info->exit(dev_info);
 
-               debugf0("%s: Successfully deleted edac device for %s\n",
-                       __func__, dev_info->ctl_name);
+               edac_dbg(0, "Successfully deleted edac device for %s\n",
+                        dev_info->ctl_name);
        }
 }
 
@@ -900,7 +899,7 @@ static int cpc925_get_sdram_scrub_rate(struct mem_ctl_info *mci)
        mscr = __raw_readl(pdata->vbase + REG_MSCR_OFFSET);
        si = (mscr & MSCR_SI_MASK) >> MSCR_SI_SHIFT;
 
-       debugf0("%s, Mem Scrub Ctrl Register 0x%x\n", __func__, mscr);
+       edac_dbg(0, "Mem Scrub Ctrl Register 0x%x\n", mscr);
 
        if (((mscr & MSCR_SCRUB_MOD_MASK) != MSCR_BACKGR_SCRUB) ||
            (si == 0)) {
@@ -928,8 +927,7 @@ static int cpc925_mc_get_channels(void __iomem *vbase)
            ((mbcr & MBCR_64BITBUS_MASK) == 0))
                dual = 1;
 
-       debugf0("%s: %s channel\n", __func__,
-               (dual > 0) ? "Dual" : "Single");
+       edac_dbg(0, "%s channel\n", (dual > 0) ? "Dual" : "Single");
 
        return dual;
 }
@@ -944,7 +942,7 @@ static int __devinit cpc925_probe(struct platform_device *pdev)
        struct resource *r;
        int res = 0, nr_channels;
 
-       debugf0("%s: %s platform device found!\n", __func__, pdev->name);
+       edac_dbg(0, "%s platform device found!\n", pdev->name);
 
        if (!devres_open_group(&pdev->dev, cpc925_probe, GFP_KERNEL)) {
                res = -ENOMEM;
@@ -995,7 +993,7 @@ static int __devinit cpc925_probe(struct platform_device *pdev)
        pdata->edac_idx = edac_mc_idx++;
        pdata->name = pdev->name;
 
-       mci->dev = &pdev->dev;
+       mci->pdev = &pdev->dev;
        platform_set_drvdata(pdev, mci);
        mci->dev_name = dev_name(&pdev->dev);
        mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR;
@@ -1026,7 +1024,7 @@ static int __devinit cpc925_probe(struct platform_device *pdev)
        cpc925_add_edac_devices(vbase);
 
        /* get this far and it's successful */
-       debugf0("%s: success\n", __func__);
+       edac_dbg(0, "success\n");
 
        res = 0;
        goto out;
index 3186512c97393f80e1da0748cf496746ba91e52e..a5ed6b795fd4331452dd112626218c102f565d40 100644 (file)
@@ -309,7 +309,7 @@ static unsigned long ctl_page_to_phys(struct mem_ctl_info *mci,
        u32 remap;
        struct e752x_pvt *pvt = (struct e752x_pvt *)mci->pvt_info;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        if (page < pvt->tolm)
                return page;
@@ -335,7 +335,7 @@ static void do_process_ce(struct mem_ctl_info *mci, u16 error_one,
        int i;
        struct e752x_pvt *pvt = (struct e752x_pvt *)mci->pvt_info;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        /* convert the addr to 4k page */
        page = sec1_add >> (PAGE_SHIFT - 4);
@@ -371,10 +371,10 @@ static void do_process_ce(struct mem_ctl_info *mci, u16 error_one,
        channel = !(error_one & 1);
 
        /* e752x mc reads 34:6 of the DRAM linear address */
-       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                             page, offset_in_page(sec1_add << 4), sec1_syndrome,
                             row, channel, -1,
-                            "e752x CE", "", NULL);
+                            "e752x CE", "");
 }
 
 static inline void process_ce(struct mem_ctl_info *mci, u16 error_one,
@@ -394,7 +394,7 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one,
        int row;
        struct e752x_pvt *pvt = (struct e752x_pvt *)mci->pvt_info;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        if (error_one & 0x0202) {
                error_2b = ded_add;
@@ -408,11 +408,11 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one,
                        edac_mc_find_csrow_by_page(mci, block_page);
 
                /* e752x mc reads 34:6 of the DRAM linear address */
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                        block_page,
                                        offset_in_page(error_2b << 4), 0,
                                         row, -1, -1,
-                                       "e752x UE from Read", "", NULL);
+                                       "e752x UE from Read", "");
 
        }
        if (error_one & 0x0404) {
@@ -427,11 +427,11 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one,
                        edac_mc_find_csrow_by_page(mci, block_page);
 
                /* e752x mc reads 34:6 of the DRAM linear address */
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                        block_page,
                                        offset_in_page(error_2b << 4), 0,
                                        row, -1, -1,
-                                       "e752x UE from Scruber", "", NULL);
+                                       "e752x UE from Scruber", "");
        }
 }
 
@@ -453,10 +453,10 @@ static inline void process_ue_no_info_wr(struct mem_ctl_info *mci,
        if (!handle_error)
                return;
 
-       debugf3("%s()\n", __func__);
-       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+       edac_dbg(3, "\n");
+       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
                             -1, -1, -1,
-                            "e752x UE log memory write", "", NULL);
+                            "e752x UE log memory write", "");
 }
 
 static void do_process_ded_retry(struct mem_ctl_info *mci, u16 error,
@@ -982,7 +982,7 @@ static void e752x_check(struct mem_ctl_info *mci)
 {
        struct e752x_error_info info;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
        e752x_get_error_info(mci, &info);
        e752x_process_error_info(mci, &info, 1);
 }
@@ -1069,6 +1069,7 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
                        u16 ddrcsr)
 {
        struct csrow_info *csrow;
+       enum edac_type edac_mode;
        unsigned long last_cumul_size;
        int index, mem_dev, drc_chan;
        int drc_drbg;           /* DRB granularity 0=64mb, 1=128mb */
@@ -1095,14 +1096,13 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
        for (last_cumul_size = index = 0; index < mci->nr_csrows; index++) {
                /* mem_dev 0=x8, 1=x4 */
                mem_dev = (dra >> (index * 4 + 2)) & 0x3;
-               csrow = &mci->csrows[remap_csrow_index(mci, index)];
+               csrow = mci->csrows[remap_csrow_index(mci, index)];
 
                mem_dev = (mem_dev == 2);
                pci_read_config_byte(pdev, E752X_DRB + index, &value);
                /* convert a 128 or 64 MiB DRB to a page size. */
                cumul_size = value << (25 + drc_drbg - PAGE_SHIFT);
-               debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
-                       cumul_size);
+               edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size);
                if (cumul_size == last_cumul_size)
                        continue;       /* not populated */
 
@@ -1111,29 +1111,29 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
                nr_pages = cumul_size - last_cumul_size;
                last_cumul_size = cumul_size;
 
+               /*
+               * if single channel or x8 devices then SECDED
+               * if dual channel and x4 then S4ECD4ED
+               */
+               if (drc_ddim) {
+                       if (drc_chan && mem_dev) {
+                               edac_mode = EDAC_S4ECD4ED;
+                               mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
+                       } else {
+                               edac_mode = EDAC_SECDED;
+                               mci->edac_cap |= EDAC_FLAG_SECDED;
+                       }
+               } else
+                       edac_mode = EDAC_NONE;
                for (i = 0; i < csrow->nr_channels; i++) {
-                       struct dimm_info *dimm = csrow->channels[i].dimm;
+                       struct dimm_info *dimm = csrow->channels[i]->dimm;
 
-                       debugf3("Initializing rank at (%i,%i)\n", index, i);
+                       edac_dbg(3, "Initializing rank at (%i,%i)\n", index, i);
                        dimm->nr_pages = nr_pages / csrow->nr_channels;
                        dimm->grain = 1 << 12;  /* 4KiB - resolution of CELOG */
                        dimm->mtype = MEM_RDDR; /* only one type supported */
                        dimm->dtype = mem_dev ? DEV_X4 : DEV_X8;
-
-                       /*
-                       * if single channel or x8 devices then SECDED
-                       * if dual channel and x4 then S4ECD4ED
-                       */
-                       if (drc_ddim) {
-                               if (drc_chan && mem_dev) {
-                                       dimm->edac_mode = EDAC_S4ECD4ED;
-                                       mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
-                               } else {
-                                       dimm->edac_mode = EDAC_SECDED;
-                                       mci->edac_cap |= EDAC_FLAG_SECDED;
-                               }
-                       } else
-                               dimm->edac_mode = EDAC_NONE;
+                       dimm->edac_mode = edac_mode;
                }
        }
 }
@@ -1269,8 +1269,8 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
        int drc_chan;           /* Number of channels 0=1chan,1=2chan */
        struct e752x_error_info discard;
 
-       debugf0("%s(): mci\n", __func__);
-       debugf0("Starting Probe1\n");
+       edac_dbg(0, "mci\n");
+       edac_dbg(0, "Starting Probe1\n");
 
        /* check to see if device 0 function 1 is enabled; if it isn't, we
         * assume the BIOS has reserved it for a reason and is expecting
@@ -1300,7 +1300,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
        if (mci == NULL)
                return -ENOMEM;
 
-       debugf3("%s(): init mci\n", __func__);
+       edac_dbg(3, "init mci\n");
        mci->mtype_cap = MEM_FLAG_RDDR;
        /* 3100 IMCH supports SECDEC only */
        mci->edac_ctl_cap = (dev_idx == I3100) ? EDAC_FLAG_SECDED :
@@ -1308,9 +1308,9 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
        /* FIXME - what if different memory types are in different csrows? */
        mci->mod_name = EDAC_MOD_STR;
        mci->mod_ver = E752X_REVISION;
-       mci->dev = &pdev->dev;
+       mci->pdev = &pdev->dev;
 
-       debugf3("%s(): init pvt\n", __func__);
+       edac_dbg(3, "init pvt\n");
        pvt = (struct e752x_pvt *)mci->pvt_info;
        pvt->dev_info = &e752x_devs[dev_idx];
        pvt->mc_symmetric = ((ddrcsr & 0x10) != 0);
@@ -1320,7 +1320,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
                return -ENODEV;
        }
 
-       debugf3("%s(): more mci init\n", __func__);
+       edac_dbg(3, "more mci init\n");
        mci->ctl_name = pvt->dev_info->ctl_name;
        mci->dev_name = pci_name(pdev);
        mci->edac_check = e752x_check;
@@ -1342,7 +1342,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
                mci->edac_cap = EDAC_FLAG_SECDED; /* the only mode supported */
        else
                mci->edac_cap |= EDAC_FLAG_NONE;
-       debugf3("%s(): tolm, remapbase, remaplimit\n", __func__);
+       edac_dbg(3, "tolm, remapbase, remaplimit\n");
 
        /* load the top of low memory, remap base, and remap limit vars */
        pci_read_config_word(pdev, E752X_TOLM, &pci_data);
@@ -1359,7 +1359,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
         * type of memory controller.  The ID is therefore hardcoded to 0.
         */
        if (edac_mc_add_mc(mci)) {
-               debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(3, "failed edac_mc_add_mc()\n");
                goto fail;
        }
 
@@ -1377,7 +1377,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
        }
 
        /* get this far and it's successful */
-       debugf3("%s(): success\n", __func__);
+       edac_dbg(3, "success\n");
        return 0;
 
 fail:
@@ -1393,7 +1393,7 @@ fail:
 static int __devinit e752x_init_one(struct pci_dev *pdev,
                                const struct pci_device_id *ent)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /* wake up and enable device */
        if (pci_enable_device(pdev) < 0)
@@ -1407,7 +1407,7 @@ static void __devexit e752x_remove_one(struct pci_dev *pdev)
        struct mem_ctl_info *mci;
        struct e752x_pvt *pvt;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        if (e752x_pci)
                edac_pci_release_generic_ctl(e752x_pci);
@@ -1453,7 +1453,7 @@ static int __init e752x_init(void)
 {
        int pci_rc;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        /* Ensure that the OPSTATE is set correctly for POLL or NMI */
        opstate_init();
@@ -1464,7 +1464,7 @@ static int __init e752x_init(void)
 
 static void __exit e752x_exit(void)
 {
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
        pci_unregister_driver(&e752x_driver);
 }
 
index 9a9c1a5467977ca6bb69042651310b2830275757..9ff57f361a43384e0d86be624067fb64cdc6e36b 100644 (file)
@@ -166,7 +166,7 @@ static const struct e7xxx_dev_info e7xxx_devs[] = {
 /* FIXME - is this valid for both SECDED and S4ECD4ED? */
 static inline int e7xxx_find_channel(u16 syndrome)
 {
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        if ((syndrome & 0xff00) == 0)
                return 0;
@@ -186,7 +186,7 @@ static unsigned long ctl_page_to_phys(struct mem_ctl_info *mci,
        u32 remap;
        struct e7xxx_pvt *pvt = (struct e7xxx_pvt *)mci->pvt_info;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        if ((page < pvt->tolm) ||
                ((page >= 0x100000) && (page < pvt->remapbase)))
@@ -208,7 +208,7 @@ static void process_ce(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
        int row;
        int channel;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
        /* read the error address */
        error_1b = info->dram_celog_add;
        /* FIXME - should use PAGE_SHIFT */
@@ -219,15 +219,15 @@ static void process_ce(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
        row = edac_mc_find_csrow_by_page(mci, page);
        /* convert syndrome to channel */
        channel = e7xxx_find_channel(syndrome);
-       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, page, 0, syndrome,
-                            row, channel, -1, "e7xxx CE", "", NULL);
+       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, page, 0, syndrome,
+                            row, channel, -1, "e7xxx CE", "");
 }
 
 static void process_ce_no_info(struct mem_ctl_info *mci)
 {
-       debugf3("%s()\n", __func__);
-       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, -1, -1, -1,
-                            "e7xxx CE log register overflow", "", NULL);
+       edac_dbg(3, "\n");
+       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1,
+                            "e7xxx CE log register overflow", "");
 }
 
 static void process_ue(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
@@ -235,23 +235,23 @@ static void process_ue(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
        u32 error_2b, block_page;
        int row;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
        /* read the error address */
        error_2b = info->dram_uelog_add;
        /* FIXME - should use PAGE_SHIFT */
        block_page = error_2b >> 6;     /* convert to 4k address */
        row = edac_mc_find_csrow_by_page(mci, block_page);
 
-       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, block_page, 0, 0,
-                            row, -1, -1, "e7xxx UE", "", NULL);
+       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, block_page, 0, 0,
+                            row, -1, -1, "e7xxx UE", "");
 }
 
 static void process_ue_no_info(struct mem_ctl_info *mci)
 {
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
-       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, -1, -1, -1,
-                            "e7xxx UE log register overflow", "", NULL);
+       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1,
+                            "e7xxx UE log register overflow", "");
 }
 
 static void e7xxx_get_error_info(struct mem_ctl_info *mci,
@@ -334,7 +334,7 @@ static void e7xxx_check(struct mem_ctl_info *mci)
 {
        struct e7xxx_error_info info;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
        e7xxx_get_error_info(mci, &info);
        e7xxx_process_error_info(mci, &info, 1);
 }
@@ -362,6 +362,7 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
        int drc_chan, drc_drbg, drc_ddim, mem_dev;
        struct csrow_info *csrow;
        struct dimm_info *dimm;
+       enum edac_type edac_mode;
 
        pci_read_config_dword(pdev, E7XXX_DRA, &dra);
        drc_chan = dual_channel_active(drc, dev_idx);
@@ -377,13 +378,12 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
        for (index = 0; index < mci->nr_csrows; index++) {
                /* mem_dev 0=x8, 1=x4 */
                mem_dev = (dra >> (index * 4 + 3)) & 0x1;
-               csrow = &mci->csrows[index];
+               csrow = mci->csrows[index];
 
                pci_read_config_byte(pdev, E7XXX_DRB + index, &value);
                /* convert a 64 or 32 MiB DRB to a page size. */
                cumul_size = value << (25 + drc_drbg - PAGE_SHIFT);
-               debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
-                       cumul_size);
+               edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size);
                if (cumul_size == last_cumul_size)
                        continue;       /* not populated */
 
@@ -392,28 +392,29 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
                nr_pages = cumul_size - last_cumul_size;
                last_cumul_size = cumul_size;
 
+               /*
+               * if single channel or x8 devices then SECDED
+               * if dual channel and x4 then S4ECD4ED
+               */
+               if (drc_ddim) {
+                       if (drc_chan && mem_dev) {
+                               edac_mode = EDAC_S4ECD4ED;
+                               mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
+                       } else {
+                               edac_mode = EDAC_SECDED;
+                               mci->edac_cap |= EDAC_FLAG_SECDED;
+                       }
+               } else
+                       edac_mode = EDAC_NONE;
+
                for (j = 0; j < drc_chan + 1; j++) {
-                       dimm = csrow->channels[j].dimm;
+                       dimm = csrow->channels[j]->dimm;
 
                        dimm->nr_pages = nr_pages / (drc_chan + 1);
                        dimm->grain = 1 << 12;  /* 4KiB - resolution of CELOG */
                        dimm->mtype = MEM_RDDR; /* only one type supported */
                        dimm->dtype = mem_dev ? DEV_X4 : DEV_X8;
-
-                       /*
-                       * if single channel or x8 devices then SECDED
-                       * if dual channel and x4 then S4ECD4ED
-                       */
-                       if (drc_ddim) {
-                               if (drc_chan && mem_dev) {
-                                       dimm->edac_mode = EDAC_S4ECD4ED;
-                                       mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
-                               } else {
-                                       dimm->edac_mode = EDAC_SECDED;
-                                       mci->edac_cap |= EDAC_FLAG_SECDED;
-                               }
-                       } else
-                               dimm->edac_mode = EDAC_NONE;
+                       dimm->edac_mode = edac_mode;
                }
        }
 }
@@ -428,7 +429,7 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx)
        int drc_chan;
        struct e7xxx_error_info discard;
 
-       debugf0("%s(): mci\n", __func__);
+       edac_dbg(0, "mci\n");
 
        pci_read_config_dword(pdev, E7XXX_DRC, &drc);
 
@@ -451,15 +452,15 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx)
        if (mci == NULL)
                return -ENOMEM;
 
-       debugf3("%s(): init mci\n", __func__);
+       edac_dbg(3, "init mci\n");
        mci->mtype_cap = MEM_FLAG_RDDR;
        mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED |
                EDAC_FLAG_S4ECD4ED;
        /* FIXME - what if different memory types are in different csrows? */
        mci->mod_name = EDAC_MOD_STR;
        mci->mod_ver = E7XXX_REVISION;
-       mci->dev = &pdev->dev;
-       debugf3("%s(): init pvt\n", __func__);
+       mci->pdev = &pdev->dev;
+       edac_dbg(3, "init pvt\n");
        pvt = (struct e7xxx_pvt *)mci->pvt_info;
        pvt->dev_info = &e7xxx_devs[dev_idx];
        pvt->bridge_ck = pci_get_device(PCI_VENDOR_ID_INTEL,
@@ -472,14 +473,14 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx)
                goto fail0;
        }
 
-       debugf3("%s(): more mci init\n", __func__);
+       edac_dbg(3, "more mci init\n");
        mci->ctl_name = pvt->dev_info->ctl_name;
        mci->dev_name = pci_name(pdev);
        mci->edac_check = e7xxx_check;
        mci->ctl_page_to_phys = ctl_page_to_phys;
        e7xxx_init_csrows(mci, pdev, dev_idx, drc);
        mci->edac_cap |= EDAC_FLAG_NONE;
-       debugf3("%s(): tolm, remapbase, remaplimit\n", __func__);
+       edac_dbg(3, "tolm, remapbase, remaplimit\n");
        /* load the top of low memory, remap base, and remap limit vars */
        pci_read_config_word(pdev, E7XXX_TOLM, &pci_data);
        pvt->tolm = ((u32) pci_data) << 4;
@@ -498,7 +499,7 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx)
         * type of memory controller.  The ID is therefore hardcoded to 0.
         */
        if (edac_mc_add_mc(mci)) {
-               debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(3, "failed edac_mc_add_mc()\n");
                goto fail1;
        }
 
@@ -514,7 +515,7 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx)
        }
 
        /* get this far and it's successful */
-       debugf3("%s(): success\n", __func__);
+       edac_dbg(3, "success\n");
        return 0;
 
 fail1:
@@ -530,7 +531,7 @@ fail0:
 static int __devinit e7xxx_init_one(struct pci_dev *pdev,
                                const struct pci_device_id *ent)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /* wake up and enable device */
        return pci_enable_device(pdev) ?
@@ -542,7 +543,7 @@ static void __devexit e7xxx_remove_one(struct pci_dev *pdev)
        struct mem_ctl_info *mci;
        struct e7xxx_pvt *pvt;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        if (e7xxx_pci)
                edac_pci_release_generic_ctl(e7xxx_pci);
index 117490d4f8359d0fbe9ab50729270e1e0424fe09..23bb99fa44f1e7ffc6307eeb9d64fbda43f591d4 100644 (file)
@@ -71,26 +71,21 @@ extern const char *edac_mem_types[];
 #ifdef CONFIG_EDAC_DEBUG
 extern int edac_debug_level;
 
-#define edac_debug_printk(level, fmt, arg...)                           \
-       do {                                                            \
-               if (level <= edac_debug_level)                          \
-                       edac_printk(KERN_DEBUG, EDAC_DEBUG,             \
-                                   "%s: " fmt, __func__, ##arg);       \
-       } while (0)
-
-#define debugf0( ... ) edac_debug_printk(0, __VA_ARGS__ )
-#define debugf1( ... ) edac_debug_printk(1, __VA_ARGS__ )
-#define debugf2( ... ) edac_debug_printk(2, __VA_ARGS__ )
-#define debugf3( ... ) edac_debug_printk(3, __VA_ARGS__ )
-#define debugf4( ... ) edac_debug_printk(4, __VA_ARGS__ )
+#define edac_dbg(level, fmt, ...)                                      \
+do {                                                                   \
+       if (level <= edac_debug_level)                                  \
+               edac_printk(KERN_DEBUG, EDAC_DEBUG,                     \
+                           "%s: " fmt, __func__, ##__VA_ARGS__);       \
+} while (0)
 
 #else                          /* !CONFIG_EDAC_DEBUG */
 
-#define debugf0( ... )
-#define debugf1( ... )
-#define debugf2( ... )
-#define debugf3( ... )
-#define debugf4( ... )
+#define edac_dbg(level, fmt, ...)                                      \
+do {                                                                   \
+       if (0)                                                          \
+               edac_printk(KERN_DEBUG, EDAC_DEBUG,                     \
+                           "%s: " fmt, __func__, ##__VA_ARGS__);       \
+} while (0)
 
 #endif                         /* !CONFIG_EDAC_DEBUG */
 
@@ -460,15 +455,15 @@ extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci,
                                      unsigned long page);
 void edac_mc_handle_error(const enum hw_event_mc_err_type type,
                          struct mem_ctl_info *mci,
+                         const u16 error_count,
                          const unsigned long page_frame_number,
                          const unsigned long offset_in_page,
                          const unsigned long syndrome,
-                         const int layer0,
-                         const int layer1,
-                         const int layer2,
+                         const int top_layer,
+                         const int mid_layer,
+                         const int low_layer,
                          const char *msg,
-                         const char *other_detail,
-                         const void *mcelog);
+                         const char *other_detail);
 
 /*
  * edac_device APIs
index ee3f1f810c1e094c27dbd012d51c4cdfa9b4ee55..211021dfec734a5e5466e2155eaf108ee4ec997c 100644 (file)
@@ -40,12 +40,13 @@ static LIST_HEAD(edac_device_list);
 #ifdef CONFIG_EDAC_DEBUG
 static void edac_device_dump_device(struct edac_device_ctl_info *edac_dev)
 {
-       debugf3("\tedac_dev = %p dev_idx=%d \n", edac_dev, edac_dev->dev_idx);
-       debugf4("\tedac_dev->edac_check = %p\n", edac_dev->edac_check);
-       debugf3("\tdev = %p\n", edac_dev->dev);
-       debugf3("\tmod_name:ctl_name = %s:%s\n",
-               edac_dev->mod_name, edac_dev->ctl_name);
-       debugf3("\tpvt_info = %p\n\n", edac_dev->pvt_info);
+       edac_dbg(3, "\tedac_dev = %p dev_idx=%d\n",
+                edac_dev, edac_dev->dev_idx);
+       edac_dbg(4, "\tedac_dev->edac_check = %p\n", edac_dev->edac_check);
+       edac_dbg(3, "\tdev = %p\n", edac_dev->dev);
+       edac_dbg(3, "\tmod_name:ctl_name = %s:%s\n",
+                edac_dev->mod_name, edac_dev->ctl_name);
+       edac_dbg(3, "\tpvt_info = %p\n\n", edac_dev->pvt_info);
 }
 #endif                         /* CONFIG_EDAC_DEBUG */
 
@@ -82,8 +83,7 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
        void *pvt, *p;
        int err;
 
-       debugf4("%s() instances=%d blocks=%d\n",
-               __func__, nr_instances, nr_blocks);
+       edac_dbg(4, "instances=%d blocks=%d\n", nr_instances, nr_blocks);
 
        /* Calculate the size of memory we need to allocate AND
         * determine the offsets of the various item arrays
@@ -156,8 +156,8 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
        /* Name of this edac device */
        snprintf(dev_ctl->name,sizeof(dev_ctl->name),"%s",edac_device_name);
 
-       debugf4("%s() edac_dev=%p next after end=%p\n",
-               __func__, dev_ctl, pvt + sz_private );
+       edac_dbg(4, "edac_dev=%p next after end=%p\n",
+                dev_ctl, pvt + sz_private);
 
        /* Initialize every Instance */
        for (instance = 0; instance < nr_instances; instance++) {
@@ -178,10 +178,8 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
                        snprintf(blk->name, sizeof(blk->name),
                                 "%s%d", edac_block_name, block+offset_value);
 
-                       debugf4("%s() instance=%d inst_p=%p block=#%d "
-                               "block_p=%p name='%s'\n",
-                               __func__, instance, inst, block,
-                               blk, blk->name);
+                       edac_dbg(4, "instance=%d inst_p=%p block=#%d block_p=%p name='%s'\n",
+                                instance, inst, block, blk, blk->name);
 
                        /* if there are NO attributes OR no attribute pointer
                         * then continue on to next block iteration
@@ -194,8 +192,8 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
                        attrib_p = &dev_attrib[block*nr_instances*nr_attrib];
                        blk->block_attributes = attrib_p;
 
-                       debugf4("%s() THIS BLOCK_ATTRIB=%p\n",
-                               __func__, blk->block_attributes);
+                       edac_dbg(4, "THIS BLOCK_ATTRIB=%p\n",
+                                blk->block_attributes);
 
                        /* Initialize every user specified attribute in this
                         * block with the data the caller passed in
@@ -214,11 +212,10 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
 
                                attrib->block = blk;    /* up link */
 
-                               debugf4("%s() alloc-attrib=%p attrib_name='%s' "
-                                       "attrib-spec=%p spec-name=%s\n",
-                                       __func__, attrib, attrib->attr.name,
-                                       &attrib_spec[attr],
-                                       attrib_spec[attr].attr.name
+                               edac_dbg(4, "alloc-attrib=%p attrib_name='%s' attrib-spec=%p spec-name=%s\n",
+                                        attrib, attrib->attr.name,
+                                        &attrib_spec[attr],
+                                        attrib_spec[attr].attr.name
                                        );
                        }
                }
@@ -273,7 +270,7 @@ static struct edac_device_ctl_info *find_edac_device_by_dev(struct device *dev)
        struct edac_device_ctl_info *edac_dev;
        struct list_head *item;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        list_for_each(item, &edac_device_list) {
                edac_dev = list_entry(item, struct edac_device_ctl_info, link);
@@ -408,7 +405,7 @@ static void edac_device_workq_function(struct work_struct *work_req)
 void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev,
                                unsigned msec)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /* take the arg 'msec' and set it into the control structure
         * to used in the time period calculation
@@ -496,7 +493,7 @@ EXPORT_SYMBOL_GPL(edac_device_alloc_index);
  */
 int edac_device_add_device(struct edac_device_ctl_info *edac_dev)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
 #ifdef CONFIG_EDAC_DEBUG
        if (edac_debug_level >= 3)
@@ -570,7 +567,7 @@ struct edac_device_ctl_info *edac_device_del_device(struct device *dev)
 {
        struct edac_device_ctl_info *edac_dev;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        mutex_lock(&device_ctls_mutex);
 
index b4ea185ccebf6f42e32536cae7f6c54e9064ecf5..fb68a06ad6837fcce67b3a3baefb98026ad146c3 100644 (file)
@@ -202,7 +202,7 @@ static void edac_device_ctrl_master_release(struct kobject *kobj)
 {
        struct edac_device_ctl_info *edac_dev = to_edacdev(kobj);
 
-       debugf4("%s() control index=%d\n", __func__, edac_dev->dev_idx);
+       edac_dbg(4, "control index=%d\n", edac_dev->dev_idx);
 
        /* decrement the EDAC CORE module ref count */
        module_put(edac_dev->owner);
@@ -233,12 +233,12 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
        struct bus_type *edac_subsys;
        int err;
 
-       debugf1("%s()\n", __func__);
+       edac_dbg(1, "\n");
 
        /* get the /sys/devices/system/edac reference */
        edac_subsys = edac_get_sysfs_subsys();
        if (edac_subsys == NULL) {
-               debugf1("%s() no edac_subsys error\n", __func__);
+               edac_dbg(1, "no edac_subsys error\n");
                err = -ENODEV;
                goto err_out;
        }
@@ -264,8 +264,8 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
                                   &edac_subsys->dev_root->kobj,
                                   "%s", edac_dev->name);
        if (err) {
-               debugf1("%s()Failed to register '.../edac/%s'\n",
-                       __func__, edac_dev->name);
+               edac_dbg(1, "Failed to register '.../edac/%s'\n",
+                        edac_dev->name);
                goto err_kobj_reg;
        }
        kobject_uevent(&edac_dev->kobj, KOBJ_ADD);
@@ -274,8 +274,7 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
         * edac_device_unregister_sysfs_main_kobj() must be used
         */
 
-       debugf4("%s() Registered '.../edac/%s' kobject\n",
-               __func__, edac_dev->name);
+       edac_dbg(4, "Registered '.../edac/%s' kobject\n", edac_dev->name);
 
        return 0;
 
@@ -296,9 +295,8 @@ err_out:
  */
 void edac_device_unregister_sysfs_main_kobj(struct edac_device_ctl_info *dev)
 {
-       debugf0("%s()\n", __func__);
-       debugf4("%s() name of kobject is: %s\n",
-               __func__, kobject_name(&dev->kobj));
+       edac_dbg(0, "\n");
+       edac_dbg(4, "name of kobject is: %s\n", kobject_name(&dev->kobj));
 
        /*
         * Unregister the edac device's kobject and
@@ -336,7 +334,7 @@ static void edac_device_ctrl_instance_release(struct kobject *kobj)
 {
        struct edac_device_instance *instance;
 
-       debugf1("%s()\n", __func__);
+       edac_dbg(1, "\n");
 
        /* map from this kobj to the main control struct
         * and then dec the main kobj count
@@ -442,7 +440,7 @@ static void edac_device_ctrl_block_release(struct kobject *kobj)
 {
        struct edac_device_block *block;
 
-       debugf1("%s()\n", __func__);
+       edac_dbg(1, "\n");
 
        /* get the container of the kobj */
        block = to_block(kobj);
@@ -524,10 +522,10 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev,
        struct edac_dev_sysfs_block_attribute *sysfs_attrib;
        struct kobject *main_kobj;
 
-       debugf4("%s() Instance '%s' inst_p=%p  block '%s'  block_p=%p\n",
-               __func__, instance->name, instance, block->name, block);
-       debugf4("%s() block kobj=%p  block kobj->parent=%p\n",
-               __func__, &block->kobj, &block->kobj.parent);
+       edac_dbg(4, "Instance '%s' inst_p=%p  block '%s'  block_p=%p\n",
+                instance->name, instance, block->name, block);
+       edac_dbg(4, "block kobj=%p  block kobj->parent=%p\n",
+                &block->kobj, &block->kobj.parent);
 
        /* init this block's kobject */
        memset(&block->kobj, 0, sizeof(struct kobject));
@@ -546,8 +544,7 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev,
                                   &instance->kobj,
                                   "%s", block->name);
        if (err) {
-               debugf1("%s() Failed to register instance '%s'\n",
-                       __func__, block->name);
+               edac_dbg(1, "Failed to register instance '%s'\n", block->name);
                kobject_put(main_kobj);
                err = -ENODEV;
                goto err_out;
@@ -560,11 +557,9 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev,
        if (sysfs_attrib && block->nr_attribs) {
                for (i = 0; i < block->nr_attribs; i++, sysfs_attrib++) {
 
-                       debugf4("%s() creating block attrib='%s' "
-                               "attrib->%p to kobj=%p\n",
-                               __func__,
-                               sysfs_attrib->attr.name,
-                               sysfs_attrib, &block->kobj);
+                       edac_dbg(4, "creating block attrib='%s' attrib->%p to kobj=%p\n",
+                                sysfs_attrib->attr.name,
+                                sysfs_attrib, &block->kobj);
 
                        /* Create each block_attribute file */
                        err = sysfs_create_file(&block->kobj,
@@ -647,14 +642,14 @@ static int edac_device_create_instance(struct edac_device_ctl_info *edac_dev,
        err = kobject_init_and_add(&instance->kobj, &ktype_instance_ctrl,
                                   &edac_dev->kobj, "%s", instance->name);
        if (err != 0) {
-               debugf2("%s() Failed to register instance '%s'\n",
-                       __func__, instance->name);
+               edac_dbg(2, "Failed to register instance '%s'\n",
+                        instance->name);
                kobject_put(main_kobj);
                goto err_out;
        }
 
-       debugf4("%s() now register '%d' blocks for instance %d\n",
-               __func__, instance->nr_blocks, idx);
+       edac_dbg(4, "now register '%d' blocks for instance %d\n",
+                instance->nr_blocks, idx);
 
        /* register all blocks of this instance */
        for (i = 0; i < instance->nr_blocks; i++) {
@@ -670,8 +665,8 @@ static int edac_device_create_instance(struct edac_device_ctl_info *edac_dev,
        }
        kobject_uevent(&instance->kobj, KOBJ_ADD);
 
-       debugf4("%s() Registered instance %d '%s' kobject\n",
-               __func__, idx, instance->name);
+       edac_dbg(4, "Registered instance %d '%s' kobject\n",
+                idx, instance->name);
 
        return 0;
 
@@ -715,7 +710,7 @@ static int edac_device_create_instances(struct edac_device_ctl_info *edac_dev)
        int i, j;
        int err;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /* iterate over creation of the instances */
        for (i = 0; i < edac_dev->nr_instances; i++) {
@@ -817,12 +812,12 @@ int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev)
        int err;
        struct kobject *edac_kobj = &edac_dev->kobj;
 
-       debugf0("%s() idx=%d\n", __func__, edac_dev->dev_idx);
+       edac_dbg(0, "idx=%d\n", edac_dev->dev_idx);
 
        /*  go create any main attributes callers wants */
        err = edac_device_add_main_sysfs_attributes(edac_dev);
        if (err) {
-               debugf0("%s() failed to add sysfs attribs\n", __func__);
+               edac_dbg(0, "failed to add sysfs attribs\n");
                goto err_out;
        }
 
@@ -832,8 +827,7 @@ int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev)
        err = sysfs_create_link(edac_kobj,
                                &edac_dev->dev->kobj, EDAC_DEVICE_SYMLINK);
        if (err) {
-               debugf0("%s() sysfs_create_link() returned err= %d\n",
-                       __func__, err);
+               edac_dbg(0, "sysfs_create_link() returned err= %d\n", err);
                goto err_remove_main_attribs;
        }
 
@@ -843,14 +837,13 @@ int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev)
         */
        err = edac_device_create_instances(edac_dev);
        if (err) {
-               debugf0("%s() edac_device_create_instances() "
-                       "returned err= %d\n", __func__, err);
+               edac_dbg(0, "edac_device_create_instances() returned err= %d\n",
+                        err);
                goto err_remove_link;
        }
 
 
-       debugf4("%s() create-instances done, idx=%d\n",
-               __func__, edac_dev->dev_idx);
+       edac_dbg(4, "create-instances done, idx=%d\n", edac_dev->dev_idx);
 
        return 0;
 
@@ -873,7 +866,7 @@ err_out:
  */
 void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /* remove any main attributes for this device */
        edac_device_remove_main_sysfs_attributes(edac_dev);
index de5ba86e8b8998df830a0e62647378e4667489f4..616d90bcb3a4106929523c4a98fe32fb27f6a9a6 100644 (file)
 #include <linux/list.h>
 #include <linux/ctype.h>
 #include <linux/edac.h>
+#include <linux/bitops.h>
 #include <asm/uaccess.h>
 #include <asm/page.h>
 #include <asm/edac.h>
 #include "edac_core.h"
 #include "edac_module.h"
 
+#define CREATE_TRACE_POINTS
+#define TRACE_INCLUDE_PATH ../../include/ras
+#include <ras/ras_event.h>
+
 /* lock to memory controller's control array */
 static DEFINE_MUTEX(mem_ctls_mutex);
 static LIST_HEAD(mc_devices);
 
+unsigned edac_dimm_info_location(struct dimm_info *dimm, char *buf,
+                                unsigned len)
+{
+       struct mem_ctl_info *mci = dimm->mci;
+       int i, n, count = 0;
+       char *p = buf;
+
+       for (i = 0; i < mci->n_layers; i++) {
+               n = snprintf(p, len, "%s %d ",
+                             edac_layer_name[mci->layers[i].type],
+                             dimm->location[i]);
+               p += n;
+               len -= n;
+               count += n;
+               if (!len)
+                       break;
+       }
+
+       return count;
+}
+
 #ifdef CONFIG_EDAC_DEBUG
 
 static void edac_mc_dump_channel(struct rank_info *chan)
 {
-       debugf4("\tchannel = %p\n", chan);
-       debugf4("\tchannel->chan_idx = %d\n", chan->chan_idx);
-       debugf4("\tchannel->csrow = %p\n\n", chan->csrow);
-       debugf4("\tchannel->dimm = %p\n", chan->dimm);
+       edac_dbg(4, "  channel->chan_idx = %d\n", chan->chan_idx);
+       edac_dbg(4, "    channel = %p\n", chan);
+       edac_dbg(4, "    channel->csrow = %p\n", chan->csrow);
+       edac_dbg(4, "    channel->dimm = %p\n", chan->dimm);
 }
 
-static void edac_mc_dump_dimm(struct dimm_info *dimm)
+static void edac_mc_dump_dimm(struct dimm_info *dimm, int number)
 {
-       int i;
-
-       debugf4("\tdimm = %p\n", dimm);
-       debugf4("\tdimm->label = '%s'\n", dimm->label);
-       debugf4("\tdimm->nr_pages = 0x%x\n", dimm->nr_pages);
-       debugf4("\tdimm location ");
-       for (i = 0; i < dimm->mci->n_layers; i++) {
-               printk(KERN_CONT "%d", dimm->location[i]);
-               if (i < dimm->mci->n_layers - 1)
-                       printk(KERN_CONT ".");
-       }
-       printk(KERN_CONT "\n");
-       debugf4("\tdimm->grain = %d\n", dimm->grain);
-       debugf4("\tdimm->nr_pages = 0x%x\n", dimm->nr_pages);
+       char location[80];
+
+       edac_dimm_info_location(dimm, location, sizeof(location));
+
+       edac_dbg(4, "%s%i: %smapped as virtual row %d, chan %d\n",
+                dimm->mci->mem_is_per_rank ? "rank" : "dimm",
+                number, location, dimm->csrow, dimm->cschannel);
+       edac_dbg(4, "  dimm = %p\n", dimm);
+       edac_dbg(4, "  dimm->label = '%s'\n", dimm->label);
+       edac_dbg(4, "  dimm->nr_pages = 0x%x\n", dimm->nr_pages);
+       edac_dbg(4, "  dimm->grain = %d\n", dimm->grain);
+       edac_dbg(4, "  dimm->nr_pages = 0x%x\n", dimm->nr_pages);
 }
 
 static void edac_mc_dump_csrow(struct csrow_info *csrow)
 {
-       debugf4("\tcsrow = %p\n", csrow);
-       debugf4("\tcsrow->csrow_idx = %d\n", csrow->csrow_idx);
-       debugf4("\tcsrow->first_page = 0x%lx\n", csrow->first_page);
-       debugf4("\tcsrow->last_page = 0x%lx\n", csrow->last_page);
-       debugf4("\tcsrow->page_mask = 0x%lx\n", csrow->page_mask);
-       debugf4("\tcsrow->nr_channels = %d\n", csrow->nr_channels);
-       debugf4("\tcsrow->channels = %p\n", csrow->channels);
-       debugf4("\tcsrow->mci = %p\n\n", csrow->mci);
+       edac_dbg(4, "csrow->csrow_idx = %d\n", csrow->csrow_idx);
+       edac_dbg(4, "  csrow = %p\n", csrow);
+       edac_dbg(4, "  csrow->first_page = 0x%lx\n", csrow->first_page);
+       edac_dbg(4, "  csrow->last_page = 0x%lx\n", csrow->last_page);
+       edac_dbg(4, "  csrow->page_mask = 0x%lx\n", csrow->page_mask);
+       edac_dbg(4, "  csrow->nr_channels = %d\n", csrow->nr_channels);
+       edac_dbg(4, "  csrow->channels = %p\n", csrow->channels);
+       edac_dbg(4, "  csrow->mci = %p\n", csrow->mci);
 }
 
 static void edac_mc_dump_mci(struct mem_ctl_info *mci)
 {
-       debugf3("\tmci = %p\n", mci);
-       debugf3("\tmci->mtype_cap = %lx\n", mci->mtype_cap);
-       debugf3("\tmci->edac_ctl_cap = %lx\n", mci->edac_ctl_cap);
-       debugf3("\tmci->edac_cap = %lx\n", mci->edac_cap);
-       debugf4("\tmci->edac_check = %p\n", mci->edac_check);
-       debugf3("\tmci->nr_csrows = %d, csrows = %p\n",
-               mci->nr_csrows, mci->csrows);
-       debugf3("\tmci->nr_dimms = %d, dimms = %p\n",
-               mci->tot_dimms, mci->dimms);
-       debugf3("\tdev = %p\n", mci->dev);
-       debugf3("\tmod_name:ctl_name = %s:%s\n", mci->mod_name, mci->ctl_name);
-       debugf3("\tpvt_info = %p\n\n", mci->pvt_info);
+       edac_dbg(3, "\tmci = %p\n", mci);
+       edac_dbg(3, "\tmci->mtype_cap = %lx\n", mci->mtype_cap);
+       edac_dbg(3, "\tmci->edac_ctl_cap = %lx\n", mci->edac_ctl_cap);
+       edac_dbg(3, "\tmci->edac_cap = %lx\n", mci->edac_cap);
+       edac_dbg(4, "\tmci->edac_check = %p\n", mci->edac_check);
+       edac_dbg(3, "\tmci->nr_csrows = %d, csrows = %p\n",
+                mci->nr_csrows, mci->csrows);
+       edac_dbg(3, "\tmci->nr_dimms = %d, dimms = %p\n",
+                mci->tot_dimms, mci->dimms);
+       edac_dbg(3, "\tdev = %p\n", mci->pdev);
+       edac_dbg(3, "\tmod_name:ctl_name = %s:%s\n",
+                mci->mod_name, mci->ctl_name);
+       edac_dbg(3, "\tpvt_info = %p\n\n", mci->pvt_info);
 }
 
 #endif                         /* CONFIG_EDAC_DEBUG */
@@ -205,15 +230,15 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
 {
        struct mem_ctl_info *mci;
        struct edac_mc_layer *layer;
-       struct csrow_info *csi, *csr;
-       struct rank_info *chi, *chp, *chan;
+       struct csrow_info *csr;
+       struct rank_info *chan;
        struct dimm_info *dimm;
        u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS];
        unsigned pos[EDAC_MAX_LAYERS];
        unsigned size, tot_dimms = 1, count = 1;
        unsigned tot_csrows = 1, tot_channels = 1, tot_errcount = 0;
        void *pvt, *p, *ptr = NULL;
-       int i, j, err, row, chn, n, len;
+       int i, j, row, chn, n, len, off;
        bool per_rank = false;
 
        BUG_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0);
@@ -239,26 +264,24 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
         */
        mci = edac_align_ptr(&ptr, sizeof(*mci), 1);
        layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers);
-       csi = edac_align_ptr(&ptr, sizeof(*csi), tot_csrows);
-       chi = edac_align_ptr(&ptr, sizeof(*chi), tot_csrows * tot_channels);
-       dimm = edac_align_ptr(&ptr, sizeof(*dimm), tot_dimms);
        for (i = 0; i < n_layers; i++) {
                count *= layers[i].size;
-               debugf4("%s: errcount layer %d size %d\n", __func__, i, count);
+               edac_dbg(4, "errcount layer %d size %d\n", i, count);
                ce_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count);
                ue_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count);
                tot_errcount += 2 * count;
        }
 
-       debugf4("%s: allocating %d error counters\n", __func__, tot_errcount);
+       edac_dbg(4, "allocating %d error counters\n", tot_errcount);
        pvt = edac_align_ptr(&ptr, sz_pvt, 1);
        size = ((unsigned long)pvt) + sz_pvt;
 
-       debugf1("%s(): allocating %u bytes for mci data (%d %s, %d csrows/channels)\n",
-               __func__, size,
-               tot_dimms,
-               per_rank ? "ranks" : "dimms",
-               tot_csrows * tot_channels);
+       edac_dbg(1, "allocating %u bytes for mci data (%d %s, %d csrows/channels)\n",
+                size,
+                tot_dimms,
+                per_rank ? "ranks" : "dimms",
+                tot_csrows * tot_channels);
+
        mci = kzalloc(size, GFP_KERNEL);
        if (mci == NULL)
                return NULL;
@@ -267,9 +290,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
         * rather than an imaginary chunk of memory located at address 0.
         */
        layer = (struct edac_mc_layer *)(((char *)mci) + ((unsigned long)layer));
-       csi = (struct csrow_info *)(((char *)mci) + ((unsigned long)csi));
-       chi = (struct rank_info *)(((char *)mci) + ((unsigned long)chi));
-       dimm = (struct dimm_info *)(((char *)mci) + ((unsigned long)dimm));
        for (i = 0; i < n_layers; i++) {
                mci->ce_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ce_per_layer[i]));
                mci->ue_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ue_per_layer[i]));
@@ -278,8 +298,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
 
        /* setup index and various internal pointers */
        mci->mc_idx = mc_num;
-       mci->csrows = csi;
-       mci->dimms  = dimm;
        mci->tot_dimms = tot_dimms;
        mci->pvt_info = pvt;
        mci->n_layers = n_layers;
@@ -290,40 +308,57 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
        mci->mem_is_per_rank = per_rank;
 
        /*
-        * Fill the csrow struct
+        * Alocate and fill the csrow/channels structs
         */
+       mci->csrows = kcalloc(sizeof(*mci->csrows), tot_csrows, GFP_KERNEL);
+       if (!mci->csrows)
+               goto error;
        for (row = 0; row < tot_csrows; row++) {
-               csr = &csi[row];
+               csr = kzalloc(sizeof(**mci->csrows), GFP_KERNEL);
+               if (!csr)
+                       goto error;
+               mci->csrows[row] = csr;
                csr->csrow_idx = row;
                csr->mci = mci;
                csr->nr_channels = tot_channels;
-               chp = &chi[row * tot_channels];
-               csr->channels = chp;
+               csr->channels = kcalloc(sizeof(*csr->channels), tot_channels,
+                                       GFP_KERNEL);
+               if (!csr->channels)
+                       goto error;
 
                for (chn = 0; chn < tot_channels; chn++) {
-                       chan = &chp[chn];
+                       chan = kzalloc(sizeof(**csr->channels), GFP_KERNEL);
+                       if (!chan)
+                               goto error;
+                       csr->channels[chn] = chan;
                        chan->chan_idx = chn;
                        chan->csrow = csr;
                }
        }
 
        /*
-        * Fill the dimm struct
+        * Allocate and fill the dimm structs
         */
+       mci->dimms  = kcalloc(sizeof(*mci->dimms), tot_dimms, GFP_KERNEL);
+       if (!mci->dimms)
+               goto error;
+
        memset(&pos, 0, sizeof(pos));
        row = 0;
        chn = 0;
-       debugf4("%s: initializing %d %s\n", __func__, tot_dimms,
-               per_rank ? "ranks" : "dimms");
        for (i = 0; i < tot_dimms; i++) {
-               chan = &csi[row].channels[chn];
-               dimm = EDAC_DIMM_PTR(layer, mci->dimms, n_layers,
-                              pos[0], pos[1], pos[2]);
-               dimm->mci = mci;
+               chan = mci->csrows[row]->channels[chn];
+               off = EDAC_DIMM_OFF(layer, n_layers, pos[0], pos[1], pos[2]);
+               if (off < 0 || off >= tot_dimms) {
+                       edac_mc_printk(mci, KERN_ERR, "EDAC core bug: EDAC_DIMM_OFF is trying to do an illegal data access\n");
+                       goto error;
+               }
 
-               debugf2("%s: %d: %s%zd (%d:%d:%d): row %d, chan %d\n", __func__,
-                       i, per_rank ? "rank" : "dimm", (dimm - mci->dimms),
-                       pos[0], pos[1], pos[2], row, chn);
+               dimm = kzalloc(sizeof(**mci->dimms), GFP_KERNEL);
+               if (!dimm)
+                       goto error;
+               mci->dimms[off] = dimm;
+               dimm->mci = mci;
 
                /*
                 * Copy DIMM location and initialize it.
@@ -367,16 +402,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
        }
 
        mci->op_state = OP_ALLOC;
-       INIT_LIST_HEAD(&mci->grp_kobj_list);
-
-       /*
-        * Initialize the 'root' kobj for the edac_mc controller
-        */
-       err = edac_mc_register_sysfs_main_kobj(mci);
-       if (err) {
-               kfree(mci);
-               return NULL;
-       }
 
        /* at this point, the root kobj is valid, and in order to
         * 'free' the object, then the function:
@@ -384,7 +409,30 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
         * which will perform kobj unregistration and the actual free
         * will occur during the kobject callback operation
         */
+
        return mci;
+
+error:
+       if (mci->dimms) {
+               for (i = 0; i < tot_dimms; i++)
+                       kfree(mci->dimms[i]);
+               kfree(mci->dimms);
+       }
+       if (mci->csrows) {
+               for (chn = 0; chn < tot_channels; chn++) {
+                       csr = mci->csrows[chn];
+                       if (csr) {
+                               for (chn = 0; chn < tot_channels; chn++)
+                                       kfree(csr->channels[chn]);
+                               kfree(csr);
+                       }
+                       kfree(mci->csrows[i]);
+               }
+               kfree(mci->csrows);
+       }
+       kfree(mci);
+
+       return NULL;
 }
 EXPORT_SYMBOL_GPL(edac_mc_alloc);
 
@@ -395,12 +443,10 @@ EXPORT_SYMBOL_GPL(edac_mc_alloc);
  */
 void edac_mc_free(struct mem_ctl_info *mci)
 {
-       debugf1("%s()\n", __func__);
+       edac_dbg(1, "\n");
 
-       edac_mc_unregister_sysfs_main_kobj(mci);
-
-       /* free the mci instance memory here */
-       kfree(mci);
+       /* the mci instance is freed here, when the sysfs object is dropped */
+       edac_unregister_sysfs(mci);
 }
 EXPORT_SYMBOL_GPL(edac_mc_free);
 
@@ -417,12 +463,12 @@ struct mem_ctl_info *find_mci_by_dev(struct device *dev)
        struct mem_ctl_info *mci;
        struct list_head *item;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        list_for_each(item, &mc_devices) {
                mci = list_entry(item, struct mem_ctl_info, link);
 
-               if (mci->dev == dev)
+               if (mci->pdev == dev)
                        return mci;
        }
 
@@ -485,7 +531,7 @@ static void edac_mc_workq_function(struct work_struct *work_req)
  */
 static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /* if this instance is not in the POLL state, then simply return */
        if (mci->op_state != OP_RUNNING_POLL)
@@ -512,8 +558,7 @@ static void edac_mc_workq_teardown(struct mem_ctl_info *mci)
 
        status = cancel_delayed_work(&mci->work);
        if (status == 0) {
-               debugf0("%s() not canceled, flush the queue\n",
-                       __func__);
+               edac_dbg(0, "not canceled, flush the queue\n");
 
                /* workq instance might be running, wait for it */
                flush_workqueue(edac_workqueue);
@@ -574,7 +619,7 @@ static int add_mc_to_global_list(struct mem_ctl_info *mci)
 
        insert_before = &mc_devices;
 
-       p = find_mci_by_dev(mci->dev);
+       p = find_mci_by_dev(mci->pdev);
        if (unlikely(p != NULL))
                goto fail0;
 
@@ -596,7 +641,7 @@ static int add_mc_to_global_list(struct mem_ctl_info *mci)
 
 fail0:
        edac_printk(KERN_WARNING, EDAC_MC,
-               "%s (%s) %s %s already assigned %d\n", dev_name(p->dev),
+               "%s (%s) %s %s already assigned %d\n", dev_name(p->pdev),
                edac_dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx);
        return 1;
 
@@ -660,7 +705,7 @@ EXPORT_SYMBOL(edac_mc_find);
 /* FIXME - should a warning be printed if no error detection? correction? */
 int edac_mc_add_mc(struct mem_ctl_info *mci)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
 #ifdef CONFIG_EDAC_DEBUG
        if (edac_debug_level >= 3)
@@ -670,15 +715,22 @@ int edac_mc_add_mc(struct mem_ctl_info *mci)
                int i;
 
                for (i = 0; i < mci->nr_csrows; i++) {
+                       struct csrow_info *csrow = mci->csrows[i];
+                       u32 nr_pages = 0;
                        int j;
 
-                       edac_mc_dump_csrow(&mci->csrows[i]);
-                       for (j = 0; j < mci->csrows[i].nr_channels; j++)
-                               edac_mc_dump_channel(&mci->csrows[i].
-                                               channels[j]);
+                       for (j = 0; j < csrow->nr_channels; j++)
+                               nr_pages += csrow->channels[j]->dimm->nr_pages;
+                       if (!nr_pages)
+                               continue;
+                       edac_mc_dump_csrow(csrow);
+                       for (j = 0; j < csrow->nr_channels; j++)
+                               if (csrow->channels[j]->dimm->nr_pages)
+                                       edac_mc_dump_channel(csrow->channels[j]);
                }
                for (i = 0; i < mci->tot_dimms; i++)
-                       edac_mc_dump_dimm(&mci->dimms[i]);
+                       if (mci->dimms[i]->nr_pages)
+                               edac_mc_dump_dimm(mci->dimms[i], i);
        }
 #endif
        mutex_lock(&mem_ctls_mutex);
@@ -732,7 +784,7 @@ struct mem_ctl_info *edac_mc_del_mc(struct device *dev)
 {
        struct mem_ctl_info *mci;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        mutex_lock(&mem_ctls_mutex);
 
@@ -770,7 +822,7 @@ static void edac_mc_scrub_block(unsigned long page, unsigned long offset,
        void *virt_addr;
        unsigned long flags = 0;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        /* ECC error page was not in our memory. Ignore it. */
        if (!pfn_valid(page))
@@ -797,26 +849,26 @@ static void edac_mc_scrub_block(unsigned long page, unsigned long offset,
 /* FIXME - should return -1 */
 int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
 {
-       struct csrow_info *csrows = mci->csrows;
+       struct csrow_info **csrows = mci->csrows;
        int row, i, j, n;
 
-       debugf1("MC%d: %s(): 0x%lx\n", mci->mc_idx, __func__, page);
+       edac_dbg(1, "MC%d: 0x%lx\n", mci->mc_idx, page);
        row = -1;
 
        for (i = 0; i < mci->nr_csrows; i++) {
-               struct csrow_info *csrow = &csrows[i];
+               struct csrow_info *csrow = csrows[i];
                n = 0;
                for (j = 0; j < csrow->nr_channels; j++) {
-                       struct dimm_info *dimm = csrow->channels[j].dimm;
+                       struct dimm_info *dimm = csrow->channels[j]->dimm;
                        n += dimm->nr_pages;
                }
                if (n == 0)
                        continue;
 
-               debugf3("MC%d: %s(): first(0x%lx) page(0x%lx) last(0x%lx) "
-                       "mask(0x%lx)\n", mci->mc_idx, __func__,
-                       csrow->first_page, page, csrow->last_page,
-                       csrow->page_mask);
+               edac_dbg(3, "MC%d: first(0x%lx) page(0x%lx) last(0x%lx) mask(0x%lx)\n",
+                        mci->mc_idx,
+                        csrow->first_page, page, csrow->last_page,
+                        csrow->page_mask);
 
                if ((page >= csrow->first_page) &&
                    (page <= csrow->last_page) &&
@@ -845,15 +897,16 @@ const char *edac_layer_name[] = {
 EXPORT_SYMBOL_GPL(edac_layer_name);
 
 static void edac_inc_ce_error(struct mem_ctl_info *mci,
-                                   bool enable_per_layer_report,
-                                   const int pos[EDAC_MAX_LAYERS])
+                             bool enable_per_layer_report,
+                             const int pos[EDAC_MAX_LAYERS],
+                             const u16 count)
 {
        int i, index = 0;
 
-       mci->ce_mc++;
+       mci->ce_mc += count;
 
        if (!enable_per_layer_report) {
-               mci->ce_noinfo_count++;
+               mci->ce_noinfo_count += count;
                return;
        }
 
@@ -861,7 +914,7 @@ static void edac_inc_ce_error(struct mem_ctl_info *mci,
                if (pos[i] < 0)
                        break;
                index += pos[i];
-               mci->ce_per_layer[i][index]++;
+               mci->ce_per_layer[i][index] += count;
 
                if (i < mci->n_layers - 1)
                        index *= mci->layers[i + 1].size;
@@ -870,14 +923,15 @@ static void edac_inc_ce_error(struct mem_ctl_info *mci,
 
 static void edac_inc_ue_error(struct mem_ctl_info *mci,
                                    bool enable_per_layer_report,
-                                   const int pos[EDAC_MAX_LAYERS])
+                                   const int pos[EDAC_MAX_LAYERS],
+                                   const u16 count)
 {
        int i, index = 0;
 
-       mci->ue_mc++;
+       mci->ue_mc += count;
 
        if (!enable_per_layer_report) {
-               mci->ce_noinfo_count++;
+               mci->ce_noinfo_count += count;
                return;
        }
 
@@ -885,7 +939,7 @@ static void edac_inc_ue_error(struct mem_ctl_info *mci,
                if (pos[i] < 0)
                        break;
                index += pos[i];
-               mci->ue_per_layer[i][index]++;
+               mci->ue_per_layer[i][index] += count;
 
                if (i < mci->n_layers - 1)
                        index *= mci->layers[i + 1].size;
@@ -893,6 +947,7 @@ static void edac_inc_ue_error(struct mem_ctl_info *mci,
 }
 
 static void edac_ce_error(struct mem_ctl_info *mci,
+                         const u16 error_count,
                          const int pos[EDAC_MAX_LAYERS],
                          const char *msg,
                          const char *location,
@@ -902,23 +957,25 @@ static void edac_ce_error(struct mem_ctl_info *mci,
                          const bool enable_per_layer_report,
                          const unsigned long page_frame_number,
                          const unsigned long offset_in_page,
-                         u32 grain)
+                         long grain)
 {
        unsigned long remapped_page;
 
        if (edac_mc_get_log_ce()) {
                if (other_detail && *other_detail)
                        edac_mc_printk(mci, KERN_WARNING,
-                                      "CE %s on %s (%s%s - %s)\n",
+                                      "%d CE %s on %s (%s %s - %s)\n",
+                                      error_count,
                                       msg, label, location,
                                       detail, other_detail);
                else
                        edac_mc_printk(mci, KERN_WARNING,
-                                      "CE %s on %s (%s%s)\n",
+                                      "%d CE %s on %s (%s %s)\n",
+                                      error_count,
                                       msg, label, location,
                                       detail);
        }
-       edac_inc_ce_error(mci, enable_per_layer_report, pos);
+       edac_inc_ce_error(mci, enable_per_layer_report, pos, error_count);
 
        if (mci->scrub_mode & SCRUB_SW_SRC) {
                /*
@@ -942,6 +999,7 @@ static void edac_ce_error(struct mem_ctl_info *mci,
 }
 
 static void edac_ue_error(struct mem_ctl_info *mci,
+                         const u16 error_count,
                          const int pos[EDAC_MAX_LAYERS],
                          const char *msg,
                          const char *location,
@@ -953,12 +1011,14 @@ static void edac_ue_error(struct mem_ctl_info *mci,
        if (edac_mc_get_log_ue()) {
                if (other_detail && *other_detail)
                        edac_mc_printk(mci, KERN_WARNING,
-                                      "UE %s on %s (%s%s - %s)\n",
+                                      "%d UE %s on %s (%s %s - %s)\n",
+                                      error_count,
                                       msg, label, location, detail,
                                       other_detail);
                else
                        edac_mc_printk(mci, KERN_WARNING,
-                                      "UE %s on %s (%s%s)\n",
+                                      "%d UE %s on %s (%s %s)\n",
+                                      error_count,
                                       msg, label, location, detail);
        }
 
@@ -971,33 +1031,53 @@ static void edac_ue_error(struct mem_ctl_info *mci,
                              msg, label, location, detail);
        }
 
-       edac_inc_ue_error(mci, enable_per_layer_report, pos);
+       edac_inc_ue_error(mci, enable_per_layer_report, pos, error_count);
 }
 
 #define OTHER_LABEL " or "
+
+/**
+ * edac_mc_handle_error - reports a memory event to userspace
+ *
+ * @type:              severity of the error (CE/UE/Fatal)
+ * @mci:               a struct mem_ctl_info pointer
+ * @error_count:       Number of errors of the same type
+ * @page_frame_number: mem page where the error occurred
+ * @offset_in_page:    offset of the error inside the page
+ * @syndrome:          ECC syndrome
+ * @top_layer:         Memory layer[0] position
+ * @mid_layer:         Memory layer[1] position
+ * @low_layer:         Memory layer[2] position
+ * @msg:               Message meaningful to the end users that
+ *                     explains the event
+ * @other_detail:      Technical details about the event that
+ *                     may help hardware manufacturers and
+ *                     EDAC developers to analyse the event
+ */
 void edac_mc_handle_error(const enum hw_event_mc_err_type type,
                          struct mem_ctl_info *mci,
+                         const u16 error_count,
                          const unsigned long page_frame_number,
                          const unsigned long offset_in_page,
                          const unsigned long syndrome,
-                         const int layer0,
-                         const int layer1,
-                         const int layer2,
+                         const int top_layer,
+                         const int mid_layer,
+                         const int low_layer,
                          const char *msg,
-                         const char *other_detail,
-                         const void *mcelog)
+                         const char *other_detail)
 {
        /* FIXME: too much for stack: move it to some pre-alocated area */
        char detail[80], location[80];
        char label[(EDAC_MC_LABEL_LEN + 1 + sizeof(OTHER_LABEL)) * mci->tot_dimms];
        char *p;
        int row = -1, chan = -1;
-       int pos[EDAC_MAX_LAYERS] = { layer0, layer1, layer2 };
+       int pos[EDAC_MAX_LAYERS] = { top_layer, mid_layer, low_layer };
        int i;
-       u32 grain;
+       long grain;
        bool enable_per_layer_report = false;
+       u8 grain_bits;
 
-       debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
+       edac_dbg(3, "MC%d\n", mci->mc_idx);
 
        /*
         * Check if the event report is consistent and if the memory
@@ -1043,13 +1123,13 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type,
        p = label;
        *p = '\0';
        for (i = 0; i < mci->tot_dimms; i++) {
-               struct dimm_info *dimm = &mci->dimms[i];
+               struct dimm_info *dimm = mci->dimms[i];
 
-               if (layer0 >= 0 && layer0 != dimm->location[0])
+               if (top_layer >= 0 && top_layer != dimm->location[0])
                        continue;
-               if (layer1 >= 0 && layer1 != dimm->location[1])
+               if (mid_layer >= 0 && mid_layer != dimm->location[1])
                        continue;
-               if (layer2 >= 0 && layer2 != dimm->location[2])
+               if (low_layer >= 0 && low_layer != dimm->location[2])
                        continue;
 
                /* get the max grain, over the error match range */
@@ -1075,11 +1155,9 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type,
                         * get csrow/channel of the DIMM, in order to allow
                         * incrementing the compat API counters
                         */
-                       debugf4("%s: %s csrows map: (%d,%d)\n",
-                               __func__,
-                               mci->mem_is_per_rank ? "rank" : "dimm",
-                               dimm->csrow, dimm->cschannel);
-
+                       edac_dbg(4, "%s csrows map: (%d,%d)\n",
+                                mci->mem_is_per_rank ? "rank" : "dimm",
+                                dimm->csrow, dimm->cschannel);
                        if (row == -1)
                                row = dimm->csrow;
                        else if (row >= 0 && row != dimm->csrow)
@@ -1095,19 +1173,18 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type,
        if (!enable_per_layer_report) {
                strcpy(label, "any memory");
        } else {
-               debugf4("%s: csrow/channel to increment: (%d,%d)\n",
-                       __func__, row, chan);
+               edac_dbg(4, "csrow/channel to increment: (%d,%d)\n", row, chan);
                if (p == label)
                        strcpy(label, "unknown memory");
                if (type == HW_EVENT_ERR_CORRECTED) {
                        if (row >= 0) {
-                               mci->csrows[row].ce_count++;
+                               mci->csrows[row]->ce_count += error_count;
                                if (chan >= 0)
-                                       mci->csrows[row].channels[chan].ce_count++;
+                                       mci->csrows[row]->channels[chan]->ce_count += error_count;
                        }
                } else
                        if (row >= 0)
-                               mci->csrows[row].ue_count++;
+                               mci->csrows[row]->ue_count += error_count;
        }
 
        /* Fill the RAM location data */
@@ -1120,23 +1197,33 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type,
                             edac_layer_name[mci->layers[i].type],
                             pos[i]);
        }
+       if (p > location)
+               *(p - 1) = '\0';
+
+       /* Report the error via the trace interface */
+
+       grain_bits = fls_long(grain) + 1;
+       trace_mc_event(type, msg, label, error_count,
+                      mci->mc_idx, top_layer, mid_layer, low_layer,
+                      PAGES_TO_MiB(page_frame_number) | offset_in_page,
+                      grain_bits, syndrome, other_detail);
 
        /* Memory type dependent details about the error */
        if (type == HW_EVENT_ERR_CORRECTED) {
                snprintf(detail, sizeof(detail),
-                       "page:0x%lx offset:0x%lx grain:%d syndrome:0x%lx",
+                       "page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx",
                        page_frame_number, offset_in_page,
                        grain, syndrome);
-               edac_ce_error(mci, pos, msg, location, label, detail,
-                             other_detail, enable_per_layer_report,
+               edac_ce_error(mci, error_count, pos, msg, location, label,
+                             detail, other_detail, enable_per_layer_report,
                              page_frame_number, offset_in_page, grain);
        } else {
                snprintf(detail, sizeof(detail),
-                       "page:0x%lx offset:0x%lx grain:%d",
+                       "page:0x%lx offset:0x%lx grain:%ld",
                        page_frame_number, offset_in_page, grain);
 
-               edac_ue_error(mci, pos, msg, location, label, detail,
-                             other_detail, enable_per_layer_report);
+               edac_ue_error(mci, error_count, pos, msg, location, label,
+                             detail, other_detail, enable_per_layer_report);
        }
 }
 EXPORT_SYMBOL_GPL(edac_mc_handle_error);
index f6a29b0eedc8535bb33769ca81cf8f88abe602e8..ed0bc07b85039deb78c09812e461e0c41e3b3670 100644 (file)
@@ -7,17 +7,21 @@
  *
  * Written Doug Thompson <norsk5@xmission.com> www.softwarebitmaker.com
  *
+ * (c) 2012 - Mauro Carvalho Chehab <mchehab@redhat.com>
+ *     The entire API were re-written, and ported to use struct device
+ *
  */
 
 #include <linux/ctype.h>
 #include <linux/slab.h>
 #include <linux/edac.h>
 #include <linux/bug.h>
+#include <linux/pm_runtime.h>
+#include <linux/uaccess.h>
 
 #include "edac_core.h"
 #include "edac_module.h"
 
-
 /* MC EDAC Controls, setable by module parameter, and sysfs */
 static int edac_mc_log_ue = 1;
 static int edac_mc_log_ce = 1;
@@ -78,6 +82,8 @@ module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int,
                  &edac_mc_poll_msec, 0644);
 MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds");
 
+static struct device *mci_pdev;
+
 /*
  * various constants for Memory Controllers
  */
@@ -125,317 +131,526 @@ static const char *edac_caps[] = {
        [EDAC_S16ECD16ED] = "S16ECD16ED"
 };
 
-/* EDAC sysfs CSROW data structures and methods
+#ifdef CONFIG_EDAC_LEGACY_SYSFS
+/*
+ * EDAC sysfs CSROW data structures and methods
+ */
+
+#define to_csrow(k) container_of(k, struct csrow_info, dev)
+
+/*
+ * We need it to avoid namespace conflicts between the legacy API
+ * and the per-dimm/per-rank one
  */
+#define DEVICE_ATTR_LEGACY(_name, _mode, _show, _store) \
+       struct device_attribute dev_attr_legacy_##_name = __ATTR(_name, _mode, _show, _store)
+
+struct dev_ch_attribute {
+       struct device_attribute attr;
+       int channel;
+};
+
+#define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \
+       struct dev_ch_attribute dev_attr_legacy_##_name = \
+               { __ATTR(_name, _mode, _show, _store), (_var) }
+
+#define to_channel(k) (container_of(k, struct dev_ch_attribute, attr)->channel)
 
 /* Set of more default csrow<id> attribute show/store functions */
-static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data,
-                               int private)
+static ssize_t csrow_ue_count_show(struct device *dev,
+                                  struct device_attribute *mattr, char *data)
 {
+       struct csrow_info *csrow = to_csrow(dev);
+
        return sprintf(data, "%u\n", csrow->ue_count);
 }
 
-static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data,
-                               int private)
+static ssize_t csrow_ce_count_show(struct device *dev,
+                                  struct device_attribute *mattr, char *data)
 {
+       struct csrow_info *csrow = to_csrow(dev);
+
        return sprintf(data, "%u\n", csrow->ce_count);
 }
 
-static ssize_t csrow_size_show(struct csrow_info *csrow, char *data,
-                               int private)
+static ssize_t csrow_size_show(struct device *dev,
+                              struct device_attribute *mattr, char *data)
 {
+       struct csrow_info *csrow = to_csrow(dev);
        int i;
        u32 nr_pages = 0;
 
        for (i = 0; i < csrow->nr_channels; i++)
-               nr_pages += csrow->channels[i].dimm->nr_pages;
-
+               nr_pages += csrow->channels[i]->dimm->nr_pages;
        return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages));
 }
 
-static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data,
-                               int private)
+static ssize_t csrow_mem_type_show(struct device *dev,
+                                  struct device_attribute *mattr, char *data)
 {
-       return sprintf(data, "%s\n", mem_types[csrow->channels[0].dimm->mtype]);
+       struct csrow_info *csrow = to_csrow(dev);
+
+       return sprintf(data, "%s\n", mem_types[csrow->channels[0]->dimm->mtype]);
 }
 
-static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data,
-                               int private)
+static ssize_t csrow_dev_type_show(struct device *dev,
+                                  struct device_attribute *mattr, char *data)
 {
-       return sprintf(data, "%s\n", dev_types[csrow->channels[0].dimm->dtype]);
+       struct csrow_info *csrow = to_csrow(dev);
+
+       return sprintf(data, "%s\n", dev_types[csrow->channels[0]->dimm->dtype]);
 }
 
-static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data,
-                               int private)
+static ssize_t csrow_edac_mode_show(struct device *dev,
+                                   struct device_attribute *mattr,
+                                   char *data)
 {
-       return sprintf(data, "%s\n", edac_caps[csrow->channels[0].dimm->edac_mode]);
+       struct csrow_info *csrow = to_csrow(dev);
+
+       return sprintf(data, "%s\n", edac_caps[csrow->channels[0]->dimm->edac_mode]);
 }
 
 /* show/store functions for DIMM Label attributes */
-static ssize_t channel_dimm_label_show(struct csrow_info *csrow,
-                               char *data, int channel)
+static ssize_t channel_dimm_label_show(struct device *dev,
+                                      struct device_attribute *mattr,
+                                      char *data)
 {
+       struct csrow_info *csrow = to_csrow(dev);
+       unsigned chan = to_channel(mattr);
+       struct rank_info *rank = csrow->channels[chan];
+
        /* if field has not been initialized, there is nothing to send */
-       if (!csrow->channels[channel].dimm->label[0])
+       if (!rank->dimm->label[0])
                return 0;
 
        return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n",
-                       csrow->channels[channel].dimm->label);
+                       rank->dimm->label);
 }
 
-static ssize_t channel_dimm_label_store(struct csrow_info *csrow,
-                                       const char *data,
-                                       size_t count, int channel)
+static ssize_t channel_dimm_label_store(struct device *dev,
+                                       struct device_attribute *mattr,
+                                       const char *data, size_t count)
 {
+       struct csrow_info *csrow = to_csrow(dev);
+       unsigned chan = to_channel(mattr);
+       struct rank_info *rank = csrow->channels[chan];
+
        ssize_t max_size = 0;
 
        max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1);
-       strncpy(csrow->channels[channel].dimm->label, data, max_size);
-       csrow->channels[channel].dimm->label[max_size] = '\0';
+       strncpy(rank->dimm->label, data, max_size);
+       rank->dimm->label[max_size] = '\0';
 
        return max_size;
 }
 
 /* show function for dynamic chX_ce_count attribute */
-static ssize_t channel_ce_count_show(struct csrow_info *csrow,
-                               char *data, int channel)
+static ssize_t channel_ce_count_show(struct device *dev,
+                                    struct device_attribute *mattr, char *data)
 {
-       return sprintf(data, "%u\n", csrow->channels[channel].ce_count);
+       struct csrow_info *csrow = to_csrow(dev);
+       unsigned chan = to_channel(mattr);
+       struct rank_info *rank = csrow->channels[chan];
+
+       return sprintf(data, "%u\n", rank->ce_count);
 }
 
-/* csrow specific attribute structure */
-struct csrowdev_attribute {
-       struct attribute attr;
-        ssize_t(*show) (struct csrow_info *, char *, int);
-        ssize_t(*store) (struct csrow_info *, const char *, size_t, int);
-       int private;
-};
+/* cwrow<id>/attribute files */
+DEVICE_ATTR_LEGACY(size_mb, S_IRUGO, csrow_size_show, NULL);
+DEVICE_ATTR_LEGACY(dev_type, S_IRUGO, csrow_dev_type_show, NULL);
+DEVICE_ATTR_LEGACY(mem_type, S_IRUGO, csrow_mem_type_show, NULL);
+DEVICE_ATTR_LEGACY(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL);
+DEVICE_ATTR_LEGACY(ue_count, S_IRUGO, csrow_ue_count_show, NULL);
+DEVICE_ATTR_LEGACY(ce_count, S_IRUGO, csrow_ce_count_show, NULL);
 
-#define to_csrow(k) container_of(k, struct csrow_info, kobj)
-#define to_csrowdev_attr(a) container_of(a, struct csrowdev_attribute, attr)
+/* default attributes of the CSROW<id> object */
+static struct attribute *csrow_attrs[] = {
+       &dev_attr_legacy_dev_type.attr,
+       &dev_attr_legacy_mem_type.attr,
+       &dev_attr_legacy_edac_mode.attr,
+       &dev_attr_legacy_size_mb.attr,
+       &dev_attr_legacy_ue_count.attr,
+       &dev_attr_legacy_ce_count.attr,
+       NULL,
+};
 
-/* Set of show/store higher level functions for default csrow attributes */
-static ssize_t csrowdev_show(struct kobject *kobj,
-                       struct attribute *attr, char *buffer)
-{
-       struct csrow_info *csrow = to_csrow(kobj);
-       struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr);
+static struct attribute_group csrow_attr_grp = {
+       .attrs  = csrow_attrs,
+};
 
-       if (csrowdev_attr->show)
-               return csrowdev_attr->show(csrow,
-                                       buffer, csrowdev_attr->private);
-       return -EIO;
-}
+static const struct attribute_group *csrow_attr_groups[] = {
+       &csrow_attr_grp,
+       NULL
+};
 
-static ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr,
-                       const char *buffer, size_t count)
+static void csrow_attr_release(struct device *dev)
 {
-       struct csrow_info *csrow = to_csrow(kobj);
-       struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr);
-
-       if (csrowdev_attr->store)
-               return csrowdev_attr->store(csrow,
-                                       buffer,
-                                       count, csrowdev_attr->private);
-       return -EIO;
-}
+       struct csrow_info *csrow = container_of(dev, struct csrow_info, dev);
 
-static const struct sysfs_ops csrowfs_ops = {
-       .show = csrowdev_show,
-       .store = csrowdev_store
-};
+       edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev));
+       kfree(csrow);
+}
 
-#define CSROWDEV_ATTR(_name,_mode,_show,_store,_private)       \
-static struct csrowdev_attribute attr_##_name = {                      \
-       .attr = {.name = __stringify(_name), .mode = _mode },   \
-       .show   = _show,                                        \
-       .store  = _store,                                       \
-       .private = _private,                                    \
+static struct device_type csrow_attr_type = {
+       .groups         = csrow_attr_groups,
+       .release        = csrow_attr_release,
 };
 
-/* default cwrow<id>/attribute files */
-CSROWDEV_ATTR(size_mb, S_IRUGO, csrow_size_show, NULL, 0);
-CSROWDEV_ATTR(dev_type, S_IRUGO, csrow_dev_type_show, NULL, 0);
-CSROWDEV_ATTR(mem_type, S_IRUGO, csrow_mem_type_show, NULL, 0);
-CSROWDEV_ATTR(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL, 0);
-CSROWDEV_ATTR(ue_count, S_IRUGO, csrow_ue_count_show, NULL, 0);
-CSROWDEV_ATTR(ce_count, S_IRUGO, csrow_ce_count_show, NULL, 0);
+/*
+ * possible dynamic channel DIMM Label attribute files
+ *
+ */
 
-/* default attributes of the CSROW<id> object */
-static struct csrowdev_attribute *default_csrow_attr[] = {
-       &attr_dev_type,
-       &attr_mem_type,
-       &attr_edac_mode,
-       &attr_size_mb,
-       &attr_ue_count,
-       &attr_ce_count,
-       NULL,
-};
+#define EDAC_NR_CHANNELS       6
 
-/* possible dynamic channel DIMM Label attribute files */
-CSROWDEV_ATTR(ch0_dimm_label, S_IRUGO | S_IWUSR,
+DEVICE_CHANNEL(ch0_dimm_label, S_IRUGO | S_IWUSR,
        channel_dimm_label_show, channel_dimm_label_store, 0);
-CSROWDEV_ATTR(ch1_dimm_label, S_IRUGO | S_IWUSR,
+DEVICE_CHANNEL(ch1_dimm_label, S_IRUGO | S_IWUSR,
        channel_dimm_label_show, channel_dimm_label_store, 1);
-CSROWDEV_ATTR(ch2_dimm_label, S_IRUGO | S_IWUSR,
+DEVICE_CHANNEL(ch2_dimm_label, S_IRUGO | S_IWUSR,
        channel_dimm_label_show, channel_dimm_label_store, 2);
-CSROWDEV_ATTR(ch3_dimm_label, S_IRUGO | S_IWUSR,
+DEVICE_CHANNEL(ch3_dimm_label, S_IRUGO | S_IWUSR,
        channel_dimm_label_show, channel_dimm_label_store, 3);
-CSROWDEV_ATTR(ch4_dimm_label, S_IRUGO | S_IWUSR,
+DEVICE_CHANNEL(ch4_dimm_label, S_IRUGO | S_IWUSR,
        channel_dimm_label_show, channel_dimm_label_store, 4);
-CSROWDEV_ATTR(ch5_dimm_label, S_IRUGO | S_IWUSR,
+DEVICE_CHANNEL(ch5_dimm_label, S_IRUGO | S_IWUSR,
        channel_dimm_label_show, channel_dimm_label_store, 5);
 
 /* Total possible dynamic DIMM Label attribute file table */
-static struct csrowdev_attribute *dynamic_csrow_dimm_attr[] = {
-       &attr_ch0_dimm_label,
-       &attr_ch1_dimm_label,
-       &attr_ch2_dimm_label,
-       &attr_ch3_dimm_label,
-       &attr_ch4_dimm_label,
-       &attr_ch5_dimm_label
+static struct device_attribute *dynamic_csrow_dimm_attr[] = {
+       &dev_attr_legacy_ch0_dimm_label.attr,
+       &dev_attr_legacy_ch1_dimm_label.attr,
+       &dev_attr_legacy_ch2_dimm_label.attr,
+       &dev_attr_legacy_ch3_dimm_label.attr,
+       &dev_attr_legacy_ch4_dimm_label.attr,
+       &dev_attr_legacy_ch5_dimm_label.attr
 };
 
 /* possible dynamic channel ce_count attribute files */
-CSROWDEV_ATTR(ch0_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 0);
-CSROWDEV_ATTR(ch1_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 1);
-CSROWDEV_ATTR(ch2_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 2);
-CSROWDEV_ATTR(ch3_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 3);
-CSROWDEV_ATTR(ch4_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 4);
-CSROWDEV_ATTR(ch5_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 5);
+DEVICE_CHANNEL(ch0_ce_count, S_IRUGO | S_IWUSR,
+                  channel_ce_count_show, NULL, 0);
+DEVICE_CHANNEL(ch1_ce_count, S_IRUGO | S_IWUSR,
+                  channel_ce_count_show, NULL, 1);
+DEVICE_CHANNEL(ch2_ce_count, S_IRUGO | S_IWUSR,
+                  channel_ce_count_show, NULL, 2);
+DEVICE_CHANNEL(ch3_ce_count, S_IRUGO | S_IWUSR,
+                  channel_ce_count_show, NULL, 3);
+DEVICE_CHANNEL(ch4_ce_count, S_IRUGO | S_IWUSR,
+                  channel_ce_count_show, NULL, 4);
+DEVICE_CHANNEL(ch5_ce_count, S_IRUGO | S_IWUSR,
+                  channel_ce_count_show, NULL, 5);
 
 /* Total possible dynamic ce_count attribute file table */
-static struct csrowdev_attribute *dynamic_csrow_ce_count_attr[] = {
-       &attr_ch0_ce_count,
-       &attr_ch1_ce_count,
-       &attr_ch2_ce_count,
-       &attr_ch3_ce_count,
-       &attr_ch4_ce_count,
-       &attr_ch5_ce_count
+static struct device_attribute *dynamic_csrow_ce_count_attr[] = {
+       &dev_attr_legacy_ch0_ce_count.attr,
+       &dev_attr_legacy_ch1_ce_count.attr,
+       &dev_attr_legacy_ch2_ce_count.attr,
+       &dev_attr_legacy_ch3_ce_count.attr,
+       &dev_attr_legacy_ch4_ce_count.attr,
+       &dev_attr_legacy_ch5_ce_count.attr
 };
 
-#define EDAC_NR_CHANNELS       6
+static inline int nr_pages_per_csrow(struct csrow_info *csrow)
+{
+       int chan, nr_pages = 0;
+
+       for (chan = 0; chan < csrow->nr_channels; chan++)
+               nr_pages += csrow->channels[chan]->dimm->nr_pages;
+
+       return nr_pages;
+}
 
-/* Create dynamic CHANNEL files, indexed by 'chan',  under specifed CSROW */
-static int edac_create_channel_files(struct kobject *kobj, int chan)
+/* Create a CSROW object under specifed edac_mc_device */
+static int edac_create_csrow_object(struct mem_ctl_info *mci,
+                                   struct csrow_info *csrow, int index)
 {
-       int err = -ENODEV;
+       int err, chan;
+
+       if (csrow->nr_channels >= EDAC_NR_CHANNELS)
+               return -ENODEV;
+
+       csrow->dev.type = &csrow_attr_type;
+       csrow->dev.bus = &mci->bus;
+       device_initialize(&csrow->dev);
+       csrow->dev.parent = &mci->dev;
+       dev_set_name(&csrow->dev, "csrow%d", index);
+       dev_set_drvdata(&csrow->dev, csrow);
 
-       if (chan >= EDAC_NR_CHANNELS)
+       edac_dbg(0, "creating (virtual) csrow node %s\n",
+                dev_name(&csrow->dev));
+
+       err = device_add(&csrow->dev);
+       if (err < 0)
                return err;
 
-       /* create the DIMM label attribute file */
-       err = sysfs_create_file(kobj,
-                               (struct attribute *)
-                               dynamic_csrow_dimm_attr[chan]);
-
-       if (!err) {
-               /* create the CE Count attribute file */
-               err = sysfs_create_file(kobj,
-                                       (struct attribute *)
-                                       dynamic_csrow_ce_count_attr[chan]);
-       } else {
-               debugf1("%s()  dimm labels and ce_count files created",
-                       __func__);
+       for (chan = 0; chan < csrow->nr_channels; chan++) {
+               /* Only expose populated DIMMs */
+               if (!csrow->channels[chan]->dimm->nr_pages)
+                       continue;
+               err = device_create_file(&csrow->dev,
+                                        dynamic_csrow_dimm_attr[chan]);
+               if (err < 0)
+                       goto error;
+               err = device_create_file(&csrow->dev,
+                                        dynamic_csrow_ce_count_attr[chan]);
+               if (err < 0) {
+                       device_remove_file(&csrow->dev,
+                                          dynamic_csrow_dimm_attr[chan]);
+                       goto error;
+               }
+       }
+
+       return 0;
+
+error:
+       for (--chan; chan >= 0; chan--) {
+               device_remove_file(&csrow->dev,
+                                       dynamic_csrow_dimm_attr[chan]);
+               device_remove_file(&csrow->dev,
+                                          dynamic_csrow_ce_count_attr[chan]);
        }
+       put_device(&csrow->dev);
 
        return err;
 }
 
-/* No memory to release for this kobj */
-static void edac_csrow_instance_release(struct kobject *kobj)
+/* Create a CSROW object under specifed edac_mc_device */
+static int edac_create_csrow_objects(struct mem_ctl_info *mci)
 {
-       struct mem_ctl_info *mci;
-       struct csrow_info *cs;
+       int err, i, chan;
+       struct csrow_info *csrow;
+
+       for (i = 0; i < mci->nr_csrows; i++) {
+               csrow = mci->csrows[i];
+               if (!nr_pages_per_csrow(csrow))
+                       continue;
+               err = edac_create_csrow_object(mci, mci->csrows[i], i);
+               if (err < 0)
+                       goto error;
+       }
+       return 0;
 
-       debugf1("%s()\n", __func__);
+error:
+       for (--i; i >= 0; i--) {
+               csrow = mci->csrows[i];
+               if (!nr_pages_per_csrow(csrow))
+                       continue;
+               for (chan = csrow->nr_channels - 1; chan >= 0; chan--) {
+                       if (!csrow->channels[chan]->dimm->nr_pages)
+                               continue;
+                       device_remove_file(&csrow->dev,
+                                               dynamic_csrow_dimm_attr[chan]);
+                       device_remove_file(&csrow->dev,
+                                               dynamic_csrow_ce_count_attr[chan]);
+               }
+               put_device(&mci->csrows[i]->dev);
+       }
 
-       cs = container_of(kobj, struct csrow_info, kobj);
-       mci = cs->mci;
+       return err;
+}
 
-       kobject_put(&mci->edac_mci_kobj);
+static void edac_delete_csrow_objects(struct mem_ctl_info *mci)
+{
+       int i, chan;
+       struct csrow_info *csrow;
+
+       for (i = mci->nr_csrows - 1; i >= 0; i--) {
+               csrow = mci->csrows[i];
+               if (!nr_pages_per_csrow(csrow))
+                       continue;
+               for (chan = csrow->nr_channels - 1; chan >= 0; chan--) {
+                       if (!csrow->channels[chan]->dimm->nr_pages)
+                               continue;
+                       edac_dbg(1, "Removing csrow %d channel %d sysfs nodes\n",
+                                i, chan);
+                       device_remove_file(&csrow->dev,
+                                               dynamic_csrow_dimm_attr[chan]);
+                       device_remove_file(&csrow->dev,
+                                               dynamic_csrow_ce_count_attr[chan]);
+               }
+               put_device(&mci->csrows[i]->dev);
+               device_del(&mci->csrows[i]->dev);
+       }
 }
+#endif
 
-/* the kobj_type instance for a CSROW */
-static struct kobj_type ktype_csrow = {
-       .release = edac_csrow_instance_release,
-       .sysfs_ops = &csrowfs_ops,
-       .default_attrs = (struct attribute **)default_csrow_attr,
+/*
+ * Per-dimm (or per-rank) devices
+ */
+
+#define to_dimm(k) container_of(k, struct dimm_info, dev)
+
+/* show/store functions for DIMM Label attributes */
+static ssize_t dimmdev_location_show(struct device *dev,
+                                    struct device_attribute *mattr, char *data)
+{
+       struct dimm_info *dimm = to_dimm(dev);
+
+       return edac_dimm_info_location(dimm, data, PAGE_SIZE);
+}
+
+static ssize_t dimmdev_label_show(struct device *dev,
+                                 struct device_attribute *mattr, char *data)
+{
+       struct dimm_info *dimm = to_dimm(dev);
+
+       /* if field has not been initialized, there is nothing to send */
+       if (!dimm->label[0])
+               return 0;
+
+       return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", dimm->label);
+}
+
+static ssize_t dimmdev_label_store(struct device *dev,
+                                  struct device_attribute *mattr,
+                                  const char *data,
+                                  size_t count)
+{
+       struct dimm_info *dimm = to_dimm(dev);
+
+       ssize_t max_size = 0;
+
+       max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1);
+       strncpy(dimm->label, data, max_size);
+       dimm->label[max_size] = '\0';
+
+       return max_size;
+}
+
+static ssize_t dimmdev_size_show(struct device *dev,
+                                struct device_attribute *mattr, char *data)
+{
+       struct dimm_info *dimm = to_dimm(dev);
+
+       return sprintf(data, "%u\n", PAGES_TO_MiB(dimm->nr_pages));
+}
+
+static ssize_t dimmdev_mem_type_show(struct device *dev,
+                                    struct device_attribute *mattr, char *data)
+{
+       struct dimm_info *dimm = to_dimm(dev);
+
+       return sprintf(data, "%s\n", mem_types[dimm->mtype]);
+}
+
+static ssize_t dimmdev_dev_type_show(struct device *dev,
+                                    struct device_attribute *mattr, char *data)
+{
+       struct dimm_info *dimm = to_dimm(dev);
+
+       return sprintf(data, "%s\n", dev_types[dimm->dtype]);
+}
+
+static ssize_t dimmdev_edac_mode_show(struct device *dev,
+                                     struct device_attribute *mattr,
+                                     char *data)
+{
+       struct dimm_info *dimm = to_dimm(dev);
+
+       return sprintf(data, "%s\n", edac_caps[dimm->edac_mode]);
+}
+
+/* dimm/rank attribute files */
+static DEVICE_ATTR(dimm_label, S_IRUGO | S_IWUSR,
+                  dimmdev_label_show, dimmdev_label_store);
+static DEVICE_ATTR(dimm_location, S_IRUGO, dimmdev_location_show, NULL);
+static DEVICE_ATTR(size, S_IRUGO, dimmdev_size_show, NULL);
+static DEVICE_ATTR(dimm_mem_type, S_IRUGO, dimmdev_mem_type_show, NULL);
+static DEVICE_ATTR(dimm_dev_type, S_IRUGO, dimmdev_dev_type_show, NULL);
+static DEVICE_ATTR(dimm_edac_mode, S_IRUGO, dimmdev_edac_mode_show, NULL);
+
+/* attributes of the dimm<id>/rank<id> object */
+static struct attribute *dimm_attrs[] = {
+       &dev_attr_dimm_label.attr,
+       &dev_attr_dimm_location.attr,
+       &dev_attr_size.attr,
+       &dev_attr_dimm_mem_type.attr,
+       &dev_attr_dimm_dev_type.attr,
+       &dev_attr_dimm_edac_mode.attr,
+       NULL,
 };
 
-/* Create a CSROW object under specifed edac_mc_device */
-static int edac_create_csrow_object(struct mem_ctl_info *mci,
-                                       struct csrow_info *csrow, int index)
+static struct attribute_group dimm_attr_grp = {
+       .attrs  = dimm_attrs,
+};
+
+static const struct attribute_group *dimm_attr_groups[] = {
+       &dimm_attr_grp,
+       NULL
+};
+
+static void dimm_attr_release(struct device *dev)
 {
-       struct kobject *kobj_mci = &mci->edac_mci_kobj;
-       struct kobject *kobj;
-       int chan;
-       int err;
+       struct dimm_info *dimm = container_of(dev, struct dimm_info, dev);
 
-       /* generate ..../edac/mc/mc<id>/csrow<index>   */
-       memset(&csrow->kobj, 0, sizeof(csrow->kobj));
-       csrow->mci = mci;       /* include container up link */
+       edac_dbg(1, "Releasing dimm device %s\n", dev_name(dev));
+       kfree(dimm);
+}
 
-       /* bump the mci instance's kobject's ref count */
-       kobj = kobject_get(&mci->edac_mci_kobj);
-       if (!kobj) {
-               err = -ENODEV;
-               goto err_out;
-       }
+static struct device_type dimm_attr_type = {
+       .groups         = dimm_attr_groups,
+       .release        = dimm_attr_release,
+};
+
+/* Create a DIMM object under specifed memory controller device */
+static int edac_create_dimm_object(struct mem_ctl_info *mci,
+                                  struct dimm_info *dimm,
+                                  int index)
+{
+       int err;
+       dimm->mci = mci;
 
-       /* Instanstiate the csrow object */
-       err = kobject_init_and_add(&csrow->kobj, &ktype_csrow, kobj_mci,
-                                  "csrow%d", index);
-       if (err)
-               goto err_release_top_kobj;
+       dimm->dev.type = &dimm_attr_type;
+       dimm->dev.bus = &mci->bus;
+       device_initialize(&dimm->dev);
 
-       /* At this point, to release a csrow kobj, one must
-        * call the kobject_put and allow that tear down
-        * to work the releasing
-        */
+       dimm->dev.parent = &mci->dev;
+       if (mci->mem_is_per_rank)
+               dev_set_name(&dimm->dev, "rank%d", index);
+       else
+               dev_set_name(&dimm->dev, "dimm%d", index);
+       dev_set_drvdata(&dimm->dev, dimm);
+       pm_runtime_forbid(&mci->dev);
 
-       /* Create the dyanmic attribute files on this csrow,
-        * namely, the DIMM labels and the channel ce_count
-        */
-       for (chan = 0; chan < csrow->nr_channels; chan++) {
-               err = edac_create_channel_files(&csrow->kobj, chan);
-               if (err) {
-                       /* special case the unregister here */
-                       kobject_put(&csrow->kobj);
-                       goto err_out;
-               }
-       }
-       kobject_uevent(&csrow->kobj, KOBJ_ADD);
-       return 0;
+       err =  device_add(&dimm->dev);
 
-       /* error unwind stack */
-err_release_top_kobj:
-       kobject_put(&mci->edac_mci_kobj);
+       edac_dbg(0, "creating rank/dimm device %s\n", dev_name(&dimm->dev));
 
-err_out:
        return err;
 }
 
-/* default sysfs methods and data structures for the main MCI kobject */
+/*
+ * Memory controller device
+ */
+
+#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
 
-static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci,
+static ssize_t mci_reset_counters_store(struct device *dev,
+                                       struct device_attribute *mattr,
                                        const char *data, size_t count)
 {
-       int row, chan;
-
-       mci->ue_noinfo_count = 0;
-       mci->ce_noinfo_count = 0;
+       struct mem_ctl_info *mci = to_mci(dev);
+       int cnt, row, chan, i;
        mci->ue_mc = 0;
        mci->ce_mc = 0;
+       mci->ue_noinfo_count = 0;
+       mci->ce_noinfo_count = 0;
 
        for (row = 0; row < mci->nr_csrows; row++) {
-               struct csrow_info *ri = &mci->csrows[row];
+               struct csrow_info *ri = mci->csrows[row];
 
                ri->ue_count = 0;
                ri->ce_count = 0;
 
                for (chan = 0; chan < ri->nr_channels; chan++)
-                       ri->channels[chan].ce_count = 0;
+                       ri->channels[chan]->ce_count = 0;
+       }
+
+       cnt = 1;
+       for (i = 0; i < mci->n_layers; i++) {
+               cnt *= mci->layers[i].size;
+               memset(mci->ce_per_layer[i], 0, cnt * sizeof(u32));
+               memset(mci->ue_per_layer[i], 0, cnt * sizeof(u32));
        }
 
        mci->start_time = jiffies;
@@ -451,9 +666,11 @@ static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci,
  * Negative value still means that an error has occurred while setting
  * the scrub rate.
  */
-static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci,
+static ssize_t mci_sdram_scrub_rate_store(struct device *dev,
+                                         struct device_attribute *mattr,
                                          const char *data, size_t count)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        unsigned long bandwidth = 0;
        int new_bw = 0;
 
@@ -476,8 +693,11 @@ static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci,
 /*
  * ->get_sdram_scrub_rate() return value semantics same as above.
  */
-static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_sdram_scrub_rate_show(struct device *dev,
+                                        struct device_attribute *mattr,
+                                        char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        int bandwidth = 0;
 
        if (!mci->get_sdram_scrub_rate)
@@ -493,45 +713,72 @@ static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data)
 }
 
 /* default attribute files for the MCI object */
-static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_ue_count_show(struct device *dev,
+                                struct device_attribute *mattr,
+                                char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
+
        return sprintf(data, "%d\n", mci->ue_mc);
 }
 
-static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_ce_count_show(struct device *dev,
+                                struct device_attribute *mattr,
+                                char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
+
        return sprintf(data, "%d\n", mci->ce_mc);
 }
 
-static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_ce_noinfo_show(struct device *dev,
+                                 struct device_attribute *mattr,
+                                 char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
+
        return sprintf(data, "%d\n", mci->ce_noinfo_count);
 }
 
-static ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_ue_noinfo_show(struct device *dev,
+                                 struct device_attribute *mattr,
+                                 char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
+
        return sprintf(data, "%d\n", mci->ue_noinfo_count);
 }
 
-static ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_seconds_show(struct device *dev,
+                               struct device_attribute *mattr,
+                               char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
+
        return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ);
 }
 
-static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_ctl_name_show(struct device *dev,
+                                struct device_attribute *mattr,
+                                char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
+
        return sprintf(data, "%s\n", mci->ctl_name);
 }
 
-static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_size_mb_show(struct device *dev,
+                               struct device_attribute *mattr,
+                               char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        int total_pages = 0, csrow_idx, j;
 
        for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) {
-               struct csrow_info *csrow = &mci->csrows[csrow_idx];
+               struct csrow_info *csrow = mci->csrows[csrow_idx];
 
                for (j = 0; j < csrow->nr_channels; j++) {
-                       struct dimm_info *dimm = csrow->channels[j].dimm;
+                       struct dimm_info *dimm = csrow->channels[j]->dimm;
 
                        total_pages += dimm->nr_pages;
                }
@@ -540,361 +787,187 @@ static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data)
        return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages));
 }
 
-#define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj)
-#define to_mcidev_attr(a) container_of(a,struct mcidev_sysfs_attribute,attr)
-
-/* MCI show/store functions for top most object */
-static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr,
-                       char *buffer)
+static ssize_t mci_max_location_show(struct device *dev,
+                                    struct device_attribute *mattr,
+                                    char *data)
 {
-       struct mem_ctl_info *mem_ctl_info = to_mci(kobj);
-       struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr);
-
-       debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info);
+       struct mem_ctl_info *mci = to_mci(dev);
+       int i;
+       char *p = data;
 
-       if (mcidev_attr->show)
-               return mcidev_attr->show(mem_ctl_info, buffer);
+       for (i = 0; i < mci->n_layers; i++) {
+               p += sprintf(p, "%s %d ",
+                            edac_layer_name[mci->layers[i].type],
+                            mci->layers[i].size - 1);
+       }
 
-       return -EIO;
+       return p - data;
 }
 
-static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr,
-                       const char *buffer, size_t count)
+#ifdef CONFIG_EDAC_DEBUG
+static ssize_t edac_fake_inject_write(struct file *file,
+                                     const char __user *data,
+                                     size_t count, loff_t *ppos)
 {
-       struct mem_ctl_info *mem_ctl_info = to_mci(kobj);
-       struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr);
-
-       debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info);
-
-       if (mcidev_attr->store)
-               return mcidev_attr->store(mem_ctl_info, buffer, count);
+       struct device *dev = file->private_data;
+       struct mem_ctl_info *mci = to_mci(dev);
+       static enum hw_event_mc_err_type type;
+       u16 errcount = mci->fake_inject_count;
+
+       if (!errcount)
+               errcount = 1;
+
+       type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED
+                                  : HW_EVENT_ERR_CORRECTED;
+
+       printk(KERN_DEBUG
+              "Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n",
+               errcount,
+               (type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE",
+               errcount > 1 ? "s" : "",
+               mci->fake_inject_layer[0],
+               mci->fake_inject_layer[1],
+               mci->fake_inject_layer[2]
+              );
+       edac_mc_handle_error(type, mci, errcount, 0, 0, 0,
+                            mci->fake_inject_layer[0],
+                            mci->fake_inject_layer[1],
+                            mci->fake_inject_layer[2],
+                            "FAKE ERROR", "for EDAC testing only");
 
-       return -EIO;
+       return count;
 }
 
-/* Intermediate show/store table */
-static const struct sysfs_ops mci_ops = {
-       .show = mcidev_show,
-       .store = mcidev_store
-};
+static int debugfs_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
 
-#define MCIDEV_ATTR(_name,_mode,_show,_store)                  \
-static struct mcidev_sysfs_attribute mci_attr_##_name = {                      \
-       .attr = {.name = __stringify(_name), .mode = _mode },   \
-       .show   = _show,                                        \
-       .store  = _store,                                       \
+static const struct file_operations debug_fake_inject_fops = {
+       .open = debugfs_open,
+       .write = edac_fake_inject_write,
+       .llseek = generic_file_llseek,
 };
+#endif
 
 /* default Control file */
-MCIDEV_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
+DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
 
 /* default Attribute files */
-MCIDEV_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL);
-MCIDEV_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL);
-MCIDEV_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL);
-MCIDEV_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL);
-MCIDEV_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL);
-MCIDEV_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL);
-MCIDEV_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL);
+DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL);
+DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL);
+DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL);
+DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL);
+DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL);
+DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL);
+DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL);
+DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL);
 
 /* memory scrubber attribute file */
-MCIDEV_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show,
+DEVICE_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show,
        mci_sdram_scrub_rate_store);
 
-static struct mcidev_sysfs_attribute *mci_attr[] = {
-       &mci_attr_reset_counters,
-       &mci_attr_mc_name,
-       &mci_attr_size_mb,
-       &mci_attr_seconds_since_reset,
-       &mci_attr_ue_noinfo_count,
-       &mci_attr_ce_noinfo_count,
-       &mci_attr_ue_count,
-       &mci_attr_ce_count,
-       &mci_attr_sdram_scrub_rate,
+static struct attribute *mci_attrs[] = {
+       &dev_attr_reset_counters.attr,
+       &dev_attr_mc_name.attr,
+       &dev_attr_size_mb.attr,
+       &dev_attr_seconds_since_reset.attr,
+       &dev_attr_ue_noinfo_count.attr,
+       &dev_attr_ce_noinfo_count.attr,
+       &dev_attr_ue_count.attr,
+       &dev_attr_ce_count.attr,
+       &dev_attr_sdram_scrub_rate.attr,
+       &dev_attr_max_location.attr,
        NULL
 };
 
+static struct attribute_group mci_attr_grp = {
+       .attrs  = mci_attrs,
+};
 
-/*
- * Release of a MC controlling instance
- *
- *     each MC control instance has the following resources upon entry:
- *             a) a ref count on the top memctl kobj
- *             b) a ref count on this module
- *
- *     this function must decrement those ref counts and then
- *     issue a free on the instance's memory
- */
-static void edac_mci_control_release(struct kobject *kobj)
-{
-       struct mem_ctl_info *mci;
-
-       mci = to_mci(kobj);
+static const struct attribute_group *mci_attr_groups[] = {
+       &mci_attr_grp,
+       NULL
+};
 
-       debugf0("%s() mci instance idx=%d releasing\n", __func__, mci->mc_idx);
+static void mci_attr_release(struct device *dev)
+{
+       struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev);
 
-       /* decrement the module ref count */
-       module_put(mci->owner);
+       edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev));
+       kfree(mci);
 }
 
-static struct kobj_type ktype_mci = {
-       .release = edac_mci_control_release,
-       .sysfs_ops = &mci_ops,
-       .default_attrs = (struct attribute **)mci_attr,
+static struct device_type mci_attr_type = {
+       .groups         = mci_attr_groups,
+       .release        = mci_attr_release,
 };
 
-/* EDAC memory controller sysfs kset:
- *     /sys/devices/system/edac/mc
- */
-static struct kset *mc_kset;
+#ifdef CONFIG_EDAC_DEBUG
+static struct dentry *edac_debugfs;
 
-/*
- * edac_mc_register_sysfs_main_kobj
- *
- *     setups and registers the main kobject for each mci
- */
-int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci)
+int __init edac_debugfs_init(void)
 {
-       struct kobject *kobj_mci;
-       int err;
-
-       debugf1("%s()\n", __func__);
-
-       kobj_mci = &mci->edac_mci_kobj;
-
-       /* Init the mci's kobject */
-       memset(kobj_mci, 0, sizeof(*kobj_mci));
-
-       /* Record which module 'owns' this control structure
-        * and bump the ref count of the module
-        */
-       mci->owner = THIS_MODULE;
-
-       /* bump ref count on this module */
-       if (!try_module_get(mci->owner)) {
-               err = -ENODEV;
-               goto fail_out;
-       }
-
-       /* this instance become part of the mc_kset */
-       kobj_mci->kset = mc_kset;
-
-       /* register the mc<id> kobject to the mc_kset */
-       err = kobject_init_and_add(kobj_mci, &ktype_mci, NULL,
-                                  "mc%d", mci->mc_idx);
-       if (err) {
-               debugf1("%s()Failed to register '.../edac/mc%d'\n",
-                       __func__, mci->mc_idx);
-               goto kobj_reg_fail;
+       edac_debugfs = debugfs_create_dir("edac", NULL);
+       if (IS_ERR(edac_debugfs)) {
+               edac_debugfs = NULL;
+               return -ENOMEM;
        }
-       kobject_uevent(kobj_mci, KOBJ_ADD);
-
-       /* At this point, to 'free' the control struct,
-        * edac_mc_unregister_sysfs_main_kobj() must be used
-        */
-
-       debugf1("%s() Registered '.../edac/mc%d' kobject\n",
-               __func__, mci->mc_idx);
-
        return 0;
-
-       /* Error exit stack */
-
-kobj_reg_fail:
-       module_put(mci->owner);
-
-fail_out:
-       return err;
-}
-
-/*
- * edac_mc_register_sysfs_main_kobj
- *
- *     tears down and the main mci kobject from the mc_kset
- */
-void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci)
-{
-       debugf1("%s()\n", __func__);
-
-       /* delete the kobj from the mc_kset */
-       kobject_put(&mci->edac_mci_kobj);
-}
-
-#define EDAC_DEVICE_SYMLINK    "device"
-
-#define grp_to_mci(k) (container_of(k, struct mcidev_sysfs_group_kobj, kobj)->mci)
-
-/* MCI show/store functions for top most object */
-static ssize_t inst_grp_show(struct kobject *kobj, struct attribute *attr,
-                       char *buffer)
-{
-       struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj);
-       struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr);
-
-       debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info);
-
-       if (mcidev_attr->show)
-               return mcidev_attr->show(mem_ctl_info, buffer);
-
-       return -EIO;
 }
 
-static ssize_t inst_grp_store(struct kobject *kobj, struct attribute *attr,
-                       const char *buffer, size_t count)
+void __exit edac_debugfs_exit(void)
 {
-       struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj);
-       struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr);
-
-       debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info);
-
-       if (mcidev_attr->store)
-               return mcidev_attr->store(mem_ctl_info, buffer, count);
-
-       return -EIO;
+       debugfs_remove(edac_debugfs);
 }
 
-/* No memory to release for this kobj */
-static void edac_inst_grp_release(struct kobject *kobj)
+int edac_create_debug_nodes(struct mem_ctl_info *mci)
 {
-       struct mcidev_sysfs_group_kobj *grp;
-       struct mem_ctl_info *mci;
-
-       debugf1("%s()\n", __func__);
-
-       grp = container_of(kobj, struct mcidev_sysfs_group_kobj, kobj);
-       mci = grp->mci;
-}
-
-/* Intermediate show/store table */
-static struct sysfs_ops inst_grp_ops = {
-       .show = inst_grp_show,
-       .store = inst_grp_store
-};
-
-/* the kobj_type instance for a instance group */
-static struct kobj_type ktype_inst_grp = {
-       .release = edac_inst_grp_release,
-       .sysfs_ops = &inst_grp_ops,
-};
-
+       struct dentry *d, *parent;
+       char name[80];
+       int i;
 
-/*
- * edac_create_mci_instance_attributes
- *     create MC driver specific attributes bellow an specified kobj
- * This routine calls itself recursively, in order to create an entire
- * object tree.
- */
-static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci,
-                               const struct mcidev_sysfs_attribute *sysfs_attrib,
-                               struct kobject *kobj)
-{
-       int err;
+       if (!edac_debugfs)
+               return -ENODEV;
 
-       debugf4("%s()\n", __func__);
-
-       while (sysfs_attrib) {
-               debugf4("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib);
-               if (sysfs_attrib->grp) {
-                       struct mcidev_sysfs_group_kobj *grp_kobj;
-
-                       grp_kobj = kzalloc(sizeof(*grp_kobj), GFP_KERNEL);
-                       if (!grp_kobj)
-                               return -ENOMEM;
-
-                       grp_kobj->grp = sysfs_attrib->grp;
-                       grp_kobj->mci = mci;
-                       list_add_tail(&grp_kobj->list, &mci->grp_kobj_list);
-
-                       debugf0("%s() grp %s, mci %p\n", __func__,
-                               sysfs_attrib->grp->name, mci);
-
-                       err = kobject_init_and_add(&grp_kobj->kobj,
-                                               &ktype_inst_grp,
-                                               &mci->edac_mci_kobj,
-                                               sysfs_attrib->grp->name);
-                       if (err < 0) {
-                               printk(KERN_ERR "kobject_init_and_add failed: %d\n", err);
-                               return err;
-                       }
-                       err = edac_create_mci_instance_attributes(mci,
-                                       grp_kobj->grp->mcidev_attr,
-                                       &grp_kobj->kobj);
-
-                       if (err < 0)
-                               return err;
-               } else if (sysfs_attrib->attr.name) {
-                       debugf4("%s() file %s\n", __func__,
-                               sysfs_attrib->attr.name);
-
-                       err = sysfs_create_file(kobj, &sysfs_attrib->attr);
-                       if (err < 0) {
-                               printk(KERN_ERR "sysfs_create_file failed: %d\n", err);
-                               return err;
-                       }
-               } else
-                       break;
-
-               sysfs_attrib++;
+       d = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
+       if (!d)
+               return -ENOMEM;
+       parent = d;
+
+       for (i = 0; i < mci->n_layers; i++) {
+               sprintf(name, "fake_inject_%s",
+                            edac_layer_name[mci->layers[i].type]);
+               d = debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent,
+                                     &mci->fake_inject_layer[i]);
+               if (!d)
+                       goto nomem;
        }
 
-       return 0;
-}
+       d = debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
+                               &mci->fake_inject_ue);
+       if (!d)
+               goto nomem;
 
-/*
- * edac_remove_mci_instance_attributes
- *     remove MC driver specific attributes at the topmost level
- *     directory of this mci instance.
- */
-static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci,
-                               const struct mcidev_sysfs_attribute *sysfs_attrib,
-                               struct kobject *kobj, int count)
-{
-       struct mcidev_sysfs_group_kobj *grp_kobj, *tmp;
+       d = debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent,
+                               &mci->fake_inject_count);
+       if (!d)
+               goto nomem;
 
-       debugf1("%s()\n", __func__);
-
-       /*
-        * loop if there are attributes and until we hit a NULL entry
-        * Remove first all the attributes
-        */
-       while (sysfs_attrib) {
-               debugf4("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib);
-               if (sysfs_attrib->grp) {
-                       debugf4("%s() seeking for group %s\n",
-                               __func__, sysfs_attrib->grp->name);
-                       list_for_each_entry(grp_kobj,
-                                           &mci->grp_kobj_list, list) {
-                               debugf4("%s() grp_kobj->grp = %p\n",__func__, grp_kobj->grp);
-                               if (grp_kobj->grp == sysfs_attrib->grp) {
-                                       edac_remove_mci_instance_attributes(mci,
-                                                   grp_kobj->grp->mcidev_attr,
-                                                   &grp_kobj->kobj, count + 1);
-                                       debugf4("%s() group %s\n", __func__,
-                                               sysfs_attrib->grp->name);
-                                       kobject_put(&grp_kobj->kobj);
-                               }
-                       }
-                       debugf4("%s() end of seeking for group %s\n",
-                               __func__, sysfs_attrib->grp->name);
-               } else if (sysfs_attrib->attr.name) {
-                       debugf4("%s() file %s\n", __func__,
-                               sysfs_attrib->attr.name);
-                       sysfs_remove_file(kobj, &sysfs_attrib->attr);
-               } else
-                       break;
-               sysfs_attrib++;
-       }
+       d = debugfs_create_file("fake_inject", S_IWUSR, parent,
+                               &mci->dev,
+                               &debug_fake_inject_fops);
+       if (!d)
+               goto nomem;
 
-       /* Remove the group objects */
-       if (count)
-               return;
-       list_for_each_entry_safe(grp_kobj, tmp,
-                                &mci->grp_kobj_list, list) {
-               list_del(&grp_kobj->list);
-               kfree(grp_kobj);
-       }
+       mci->debugfs = parent;
+       return 0;
+nomem:
+       debugfs_remove(mci->debugfs);
+       return -ENOMEM;
 }
-
+#endif
 
 /*
  * Create a new Memory Controller kobject instance,
@@ -906,77 +979,87 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci,
  */
 int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
 {
-       int i, j;
-       int err;
-       struct csrow_info *csrow;
-       struct kobject *kobj_mci = &mci->edac_mci_kobj;
+       int i, err;
 
-       debugf0("%s() idx=%d\n", __func__, mci->mc_idx);
-
-       INIT_LIST_HEAD(&mci->grp_kobj_list);
+       /*
+        * The memory controller needs its own bus, in order to avoid
+        * namespace conflicts at /sys/bus/edac.
+        */
+       mci->bus.name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx);
+       if (!mci->bus.name)
+               return -ENOMEM;
+       edac_dbg(0, "creating bus %s\n", mci->bus.name);
+       err = bus_register(&mci->bus);
+       if (err < 0)
+               return err;
 
-       /* create a symlink for the device */
-       err = sysfs_create_link(kobj_mci, &mci->dev->kobj,
-                               EDAC_DEVICE_SYMLINK);
-       if (err) {
-               debugf1("%s() failure to create symlink\n", __func__);
-               goto fail0;
+       /* get the /sys/devices/system/edac subsys reference */
+       mci->dev.type = &mci_attr_type;
+       device_initialize(&mci->dev);
+
+       mci->dev.parent = mci_pdev;
+       mci->dev.bus = &mci->bus;
+       dev_set_name(&mci->dev, "mc%d", mci->mc_idx);
+       dev_set_drvdata(&mci->dev, mci);
+       pm_runtime_forbid(&mci->dev);
+
+       edac_dbg(0, "creating device %s\n", dev_name(&mci->dev));
+       err = device_add(&mci->dev);
+       if (err < 0) {
+               bus_unregister(&mci->bus);
+               kfree(mci->bus.name);
+               return err;
        }
 
-       /* If the low level driver desires some attributes,
-        * then create them now for the driver.
+       /*
+        * Create the dimm/rank devices
         */
-       if (mci->mc_driver_sysfs_attributes) {
-               err = edac_create_mci_instance_attributes(mci,
-                                       mci->mc_driver_sysfs_attributes,
-                                       &mci->edac_mci_kobj);
+       for (i = 0; i < mci->tot_dimms; i++) {
+               struct dimm_info *dimm = mci->dimms[i];
+               /* Only expose populated DIMMs */
+               if (dimm->nr_pages == 0)
+                       continue;
+#ifdef CONFIG_EDAC_DEBUG
+               edac_dbg(1, "creating dimm%d, located at ", i);
+               if (edac_debug_level >= 1) {
+                       int lay;
+                       for (lay = 0; lay < mci->n_layers; lay++)
+                               printk(KERN_CONT "%s %d ",
+                                       edac_layer_name[mci->layers[lay].type],
+                                       dimm->location[lay]);
+                       printk(KERN_CONT "\n");
+               }
+#endif
+               err = edac_create_dimm_object(mci, dimm, i);
                if (err) {
-                       debugf1("%s() failure to create mci attributes\n",
-                               __func__);
-                       goto fail0;
+                       edac_dbg(1, "failure: create dimm %d obj\n", i);
+                       goto fail;
                }
        }
 
-       /* Make directories for each CSROW object under the mc<id> kobject
-        */
-       for (i = 0; i < mci->nr_csrows; i++) {
-               int nr_pages = 0;
-
-               csrow = &mci->csrows[i];
-               for (j = 0; j < csrow->nr_channels; j++)
-                       nr_pages += csrow->channels[j].dimm->nr_pages;
-
-               if (nr_pages > 0) {
-                       err = edac_create_csrow_object(mci, csrow, i);
-                       if (err) {
-                               debugf1("%s() failure: create csrow %d obj\n",
-                                       __func__, i);
-                               goto fail1;
-                       }
-               }
-       }
+#ifdef CONFIG_EDAC_LEGACY_SYSFS
+       err = edac_create_csrow_objects(mci);
+       if (err < 0)
+               goto fail;
+#endif
 
+#ifdef CONFIG_EDAC_DEBUG
+       edac_create_debug_nodes(mci);
+#endif
        return 0;
 
-fail1:
+fail:
        for (i--; i >= 0; i--) {
-               int nr_pages = 0;
-
-               csrow = &mci->csrows[i];
-               for (j = 0; j < csrow->nr_channels; j++)
-                       nr_pages += csrow->channels[j].dimm->nr_pages;
-               if (nr_pages > 0)
-                       kobject_put(&mci->csrows[i].kobj);
+               struct dimm_info *dimm = mci->dimms[i];
+               if (dimm->nr_pages == 0)
+                       continue;
+               put_device(&dimm->dev);
+               device_del(&dimm->dev);
        }
-
-       /* remove the mci instance's attributes, if any */
-       edac_remove_mci_instance_attributes(mci,
-               mci->mc_driver_sysfs_attributes, &mci->edac_mci_kobj, 0);
-
-       /* remove the symlink */
-       sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK);
-
-fail0:
+       put_device(&mci->dev);
+       device_del(&mci->dev);
+       bus_unregister(&mci->bus);
+       kfree(mci->bus.name);
        return err;
 }
 
@@ -985,98 +1068,84 @@ fail0:
  */
 void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
 {
-       struct csrow_info *csrow;
-       int i, j;
-
-       debugf0("%s()\n", __func__);
-
-       /* remove all csrow kobjects */
-       debugf4("%s()  unregister this mci kobj\n", __func__);
-       for (i = 0; i < mci->nr_csrows; i++) {
-               int nr_pages = 0;
-
-               csrow = &mci->csrows[i];
-               for (j = 0; j < csrow->nr_channels; j++)
-                       nr_pages += csrow->channels[j].dimm->nr_pages;
-               if (nr_pages > 0) {
-                       debugf0("%s()  unreg csrow-%d\n", __func__, i);
-                       kobject_put(&mci->csrows[i].kobj);
-               }
-       }
+       int i;
 
-       /* remove this mci instance's attribtes */
-       if (mci->mc_driver_sysfs_attributes) {
-               debugf4("%s()  unregister mci private attributes\n", __func__);
-               edac_remove_mci_instance_attributes(mci,
-                                               mci->mc_driver_sysfs_attributes,
-                                               &mci->edac_mci_kobj, 0);
+       edac_dbg(0, "\n");
+
+#ifdef CONFIG_EDAC_DEBUG
+       debugfs_remove(mci->debugfs);
+#endif
+#ifdef CONFIG_EDAC_LEGACY_SYSFS
+       edac_delete_csrow_objects(mci);
+#endif
+
+       for (i = 0; i < mci->tot_dimms; i++) {
+               struct dimm_info *dimm = mci->dimms[i];
+               if (dimm->nr_pages == 0)
+                       continue;
+               edac_dbg(0, "removing device %s\n", dev_name(&dimm->dev));
+               put_device(&dimm->dev);
+               device_del(&dimm->dev);
        }
-
-       /* remove the symlink */
-       debugf4("%s()  remove_link\n", __func__);
-       sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK);
-
-       /* unregister this instance's kobject */
-       debugf4("%s()  remove_mci_instance\n", __func__);
-       kobject_put(&mci->edac_mci_kobj);
 }
 
+void edac_unregister_sysfs(struct mem_ctl_info *mci)
+{
+       edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev));
+       put_device(&mci->dev);
+       device_del(&mci->dev);
+       bus_unregister(&mci->bus);
+       kfree(mci->bus.name);
+}
 
+static void mc_attr_release(struct device *dev)
+{
+       /*
+        * There's no container structure here, as this is just the mci
+        * parent device, used to create the /sys/devices/mc sysfs node.
+        * So, there are no attributes on it.
+        */
+       edac_dbg(1, "Releasing device %s\n", dev_name(dev));
+       kfree(dev);
+}
 
-
+static struct device_type mc_attr_type = {
+       .release        = mc_attr_release,
+};
 /*
- * edac_setup_sysfs_mc_kset(void)
- *
- * Initialize the mc_kset for the 'mc' entry
- *     This requires creating the top 'mc' directory with a kset
- *     and its controls/attributes.
- *
- *     To this 'mc' kset, instance 'mci' will be grouped as children.
- *
- * Return:  0 SUCCESS
- *         !0 FAILURE error code
+ * Init/exit code for the module. Basically, creates/removes /sys/class/rc
  */
-int edac_sysfs_setup_mc_kset(void)
+int __init edac_mc_sysfs_init(void)
 {
-       int err = -EINVAL;
        struct bus_type *edac_subsys;
-
-       debugf1("%s()\n", __func__);
+       int err;
 
        /* get the /sys/devices/system/edac subsys reference */
        edac_subsys = edac_get_sysfs_subsys();
        if (edac_subsys == NULL) {
-               debugf1("%s() no edac_subsys error=%d\n", __func__, err);
-               goto fail_out;
+               edac_dbg(1, "no edac_subsys\n");
+               return -EINVAL;
        }
 
-       /* Init the MC's kobject */
-       mc_kset = kset_create_and_add("mc", NULL, &edac_subsys->dev_root->kobj);
-       if (!mc_kset) {
-               err = -ENOMEM;
-               debugf1("%s() Failed to register '.../edac/mc'\n", __func__);
-               goto fail_kset;
-       }
+       mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL);
 
-       debugf1("%s() Registered '.../edac/mc' kobject\n", __func__);
+       mci_pdev->bus = edac_subsys;
+       mci_pdev->type = &mc_attr_type;
+       device_initialize(mci_pdev);
+       dev_set_name(mci_pdev, "mc");
 
-       return 0;
+       err = device_add(mci_pdev);
+       if (err < 0)
+               return err;
 
-fail_kset:
-       edac_put_sysfs_subsys();
+       edac_dbg(0, "device %s created\n", dev_name(mci_pdev));
 
-fail_out:
-       return err;
+       return 0;
 }
 
-/*
- * edac_sysfs_teardown_mc_kset
- *
- *     deconstruct the mc_ket for memory controllers
- */
-void edac_sysfs_teardown_mc_kset(void)
+void __exit edac_mc_sysfs_exit(void)
 {
-       kset_unregister(mc_kset);
+       put_device(mci_pdev);
+       device_del(mci_pdev);
        edac_put_sysfs_subsys();
 }
-
index 5ddaa86d6a6e86ef8ed0671070168d7541cd7073..58a28d838f37bfef26450bacdf11c8a338f6dcc7 100644 (file)
@@ -15,7 +15,7 @@
 #include "edac_core.h"
 #include "edac_module.h"
 
-#define EDAC_VERSION "Ver: 2.1.0"
+#define EDAC_VERSION "Ver: 3.0.0"
 
 #ifdef CONFIG_EDAC_DEBUG
 /* Values of 0 to 4 will generate output */
@@ -90,26 +90,21 @@ static int __init edac_init(void)
         */
        edac_pci_clear_parity_errors();
 
-       /*
-        * now set up the mc_kset under the edac class object
-        */
-       err = edac_sysfs_setup_mc_kset();
+       err = edac_mc_sysfs_init();
        if (err)
                goto error;
 
+       edac_debugfs_init();
+
        /* Setup/Initialize the workq for this core */
        err = edac_workqueue_setup();
        if (err) {
                edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n");
-               goto workq_fail;
+               goto error;
        }
 
        return 0;
 
-       /* Error teardown stack */
-workq_fail:
-       edac_sysfs_teardown_mc_kset();
-
 error:
        return err;
 }
@@ -120,11 +115,12 @@ error:
  */
 static void __exit edac_exit(void)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /* tear down the various subsystems */
        edac_workqueue_teardown();
-       edac_sysfs_teardown_mc_kset();
+       edac_mc_sysfs_exit();
+       edac_debugfs_exit();
 }
 
 /*
index 0ea7d14cb930748e75aadbb18e48e4616fdc315e..3d139c6e7fe325719b7ddaf4b38127f5895f8bb8 100644 (file)
  *
  * edac_mc objects
  */
-extern int edac_sysfs_setup_mc_kset(void);
-extern void edac_sysfs_teardown_mc_kset(void);
-extern int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci);
-extern void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci);
+       /* on edac_mc_sysfs.c */
+int edac_mc_sysfs_init(void);
+void edac_mc_sysfs_exit(void);
 extern int edac_create_sysfs_mci_device(struct mem_ctl_info *mci);
 extern void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci);
+void edac_unregister_sysfs(struct mem_ctl_info *mci);
 extern int edac_get_log_ue(void);
 extern int edac_get_log_ce(void);
 extern int edac_get_panic_on_ue(void);
@@ -34,6 +34,10 @@ extern int edac_mc_get_panic_on_ue(void);
 extern int edac_get_poll_msec(void);
 extern int edac_mc_get_poll_msec(void);
 
+unsigned edac_dimm_info_location(struct dimm_info *dimm, char *buf,
+                                unsigned len);
+
+       /* on edac_device.c */
 extern int edac_device_register_sysfs_main_kobj(
                                struct edac_device_ctl_info *edac_dev);
 extern void edac_device_unregister_sysfs_main_kobj(
@@ -52,6 +56,20 @@ extern void edac_mc_reset_delay_period(int value);
 
 extern void *edac_align_ptr(void **p, unsigned size, int n_elems);
 
+/*
+ * EDAC debugfs functions
+ */
+#ifdef CONFIG_EDAC_DEBUG
+int edac_debugfs_init(void);
+void edac_debugfs_exit(void);
+#else
+static inline int edac_debugfs_init(void)
+{
+       return -ENODEV;
+}
+static inline void edac_debugfs_exit(void) {}
+#endif
+
 /*
  * EDAC PCI functions
  */
index f1ac866498864dfbfc8e73ad091860d790142a68..ee87ef972ead7667a60a1359e9347e9ebaf60bda 100644 (file)
@@ -45,7 +45,7 @@ struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt,
        void *p = NULL, *pvt;
        unsigned int size;
 
-       debugf1("%s()\n", __func__);
+       edac_dbg(1, "\n");
 
        pci = edac_align_ptr(&p, sizeof(*pci), 1);
        pvt = edac_align_ptr(&p, 1, sz_pvt);
@@ -80,7 +80,7 @@ EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info);
  */
 void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci)
 {
-       debugf1("%s()\n", __func__);
+       edac_dbg(1, "\n");
 
        edac_pci_remove_sysfs(pci);
 }
@@ -97,7 +97,7 @@ static struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev)
        struct edac_pci_ctl_info *pci;
        struct list_head *item;
 
-       debugf1("%s()\n", __func__);
+       edac_dbg(1, "\n");
 
        list_for_each(item, &edac_pci_list) {
                pci = list_entry(item, struct edac_pci_ctl_info, link);
@@ -122,7 +122,7 @@ static int add_edac_pci_to_global_list(struct edac_pci_ctl_info *pci)
        struct list_head *item, *insert_before;
        struct edac_pci_ctl_info *rover;
 
-       debugf1("%s()\n", __func__);
+       edac_dbg(1, "\n");
 
        insert_before = &edac_pci_list;
 
@@ -226,7 +226,7 @@ static void edac_pci_workq_function(struct work_struct *work_req)
        int msec;
        unsigned long delay;
 
-       debugf3("%s() checking\n", __func__);
+       edac_dbg(3, "checking\n");
 
        mutex_lock(&edac_pci_ctls_mutex);
 
@@ -261,7 +261,7 @@ static void edac_pci_workq_function(struct work_struct *work_req)
 static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci,
                                 unsigned int msec)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function);
        queue_delayed_work(edac_workqueue, &pci->work,
@@ -276,7 +276,7 @@ static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci)
 {
        int status;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        status = cancel_delayed_work(&pci->work);
        if (status == 0)
@@ -293,7 +293,7 @@ static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci)
 void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci,
                                 unsigned long value)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        edac_pci_workq_teardown(pci);
 
@@ -333,7 +333,7 @@ EXPORT_SYMBOL_GPL(edac_pci_alloc_index);
  */
 int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        pci->pci_idx = edac_idx;
        pci->start_time = jiffies;
@@ -393,7 +393,7 @@ struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev)
 {
        struct edac_pci_ctl_info *pci;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        mutex_lock(&edac_pci_ctls_mutex);
 
@@ -430,7 +430,7 @@ EXPORT_SYMBOL_GPL(edac_pci_del_device);
  */
 static void edac_pci_generic_check(struct edac_pci_ctl_info *pci)
 {
-       debugf4("%s()\n", __func__);
+       edac_dbg(4, "\n");
        edac_pci_do_parity_check();
 }
 
@@ -475,7 +475,7 @@ struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev,
        pdata->edac_idx = edac_pci_idx++;
 
        if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
-               debugf3("%s(): failed edac_pci_add_device()\n", __func__);
+               edac_dbg(3, "failed edac_pci_add_device()\n");
                edac_pci_free_ctl_info(pci);
                return NULL;
        }
@@ -491,7 +491,7 @@ EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl);
  */
 void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci)
 {
-       debugf0("%s() pci mod=%s\n", __func__, pci->mod_name);
+       edac_dbg(0, "pci mod=%s\n", pci->mod_name);
 
        edac_pci_del_device(pci->dev);
        edac_pci_free_ctl_info(pci);
index 97f5064e39924deb504fc54922fec0941aa043ca..e164c555a337fb3818817dbef13f1673532ac260 100644 (file)
@@ -78,7 +78,7 @@ static void edac_pci_instance_release(struct kobject *kobj)
 {
        struct edac_pci_ctl_info *pci;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /* Form pointer to containing struct, the pci control struct */
        pci = to_instance(kobj);
@@ -161,7 +161,7 @@ static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx)
        struct kobject *main_kobj;
        int err;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /* First bump the ref count on the top main kobj, which will
         * track the number of PCI instances we have, and thus nest
@@ -177,14 +177,13 @@ static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx)
        err = kobject_init_and_add(&pci->kobj, &ktype_pci_instance,
                                   edac_pci_top_main_kobj, "pci%d", idx);
        if (err != 0) {
-               debugf2("%s() failed to register instance pci%d\n",
-                       __func__, idx);
+               edac_dbg(2, "failed to register instance pci%d\n", idx);
                kobject_put(edac_pci_top_main_kobj);
                goto error_out;
        }
 
        kobject_uevent(&pci->kobj, KOBJ_ADD);
-       debugf1("%s() Register instance 'pci%d' kobject\n", __func__, idx);
+       edac_dbg(1, "Register instance 'pci%d' kobject\n", idx);
 
        return 0;
 
@@ -201,7 +200,7 @@ error_out:
 static void edac_pci_unregister_sysfs_instance_kobj(
                        struct edac_pci_ctl_info *pci)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /* Unregister the instance kobject and allow its release
         * function release the main reference count and then
@@ -317,7 +316,7 @@ static struct edac_pci_dev_attribute *edac_pci_attr[] = {
  */
 static void edac_pci_release_main_kobj(struct kobject *kobj)
 {
-       debugf0("%s() here to module_put(THIS_MODULE)\n", __func__);
+       edac_dbg(0, "here to module_put(THIS_MODULE)\n");
 
        kfree(kobj);
 
@@ -345,7 +344,7 @@ static int edac_pci_main_kobj_setup(void)
        int err;
        struct bus_type *edac_subsys;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /* check and count if we have already created the main kobject */
        if (atomic_inc_return(&edac_pci_sysfs_refcount) != 1)
@@ -356,7 +355,7 @@ static int edac_pci_main_kobj_setup(void)
         */
        edac_subsys = edac_get_sysfs_subsys();
        if (edac_subsys == NULL) {
-               debugf1("%s() no edac_subsys\n", __func__);
+               edac_dbg(1, "no edac_subsys\n");
                err = -ENODEV;
                goto decrement_count_fail;
        }
@@ -366,14 +365,14 @@ static int edac_pci_main_kobj_setup(void)
         * level main kobj for EDAC PCI
         */
        if (!try_module_get(THIS_MODULE)) {
-               debugf1("%s() try_module_get() failed\n", __func__);
+               edac_dbg(1, "try_module_get() failed\n");
                err = -ENODEV;
                goto mod_get_fail;
        }
 
        edac_pci_top_main_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
        if (!edac_pci_top_main_kobj) {
-               debugf1("Failed to allocate\n");
+               edac_dbg(1, "Failed to allocate\n");
                err = -ENOMEM;
                goto kzalloc_fail;
        }
@@ -383,7 +382,7 @@ static int edac_pci_main_kobj_setup(void)
                                   &ktype_edac_pci_main_kobj,
                                   &edac_subsys->dev_root->kobj, "pci");
        if (err) {
-               debugf1("Failed to register '.../edac/pci'\n");
+               edac_dbg(1, "Failed to register '.../edac/pci'\n");
                goto kobject_init_and_add_fail;
        }
 
@@ -392,7 +391,7 @@ static int edac_pci_main_kobj_setup(void)
         * must be used, for resources to be cleaned up properly
         */
        kobject_uevent(edac_pci_top_main_kobj, KOBJ_ADD);
-       debugf1("Registered '.../edac/pci' kobject\n");
+       edac_dbg(1, "Registered '.../edac/pci' kobject\n");
 
        return 0;
 
@@ -421,15 +420,14 @@ decrement_count_fail:
  */
 static void edac_pci_main_kobj_teardown(void)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /* Decrement the count and only if no more controller instances
         * are connected perform the unregisteration of the top level
         * main kobj
         */
        if (atomic_dec_return(&edac_pci_sysfs_refcount) == 0) {
-               debugf0("%s() called kobject_put on main kobj\n",
-                       __func__);
+               edac_dbg(0, "called kobject_put on main kobj\n");
                kobject_put(edac_pci_top_main_kobj);
        }
        edac_put_sysfs_subsys();
@@ -446,7 +444,7 @@ int edac_pci_create_sysfs(struct edac_pci_ctl_info *pci)
        int err;
        struct kobject *edac_kobj = &pci->kobj;
 
-       debugf0("%s() idx=%d\n", __func__, pci->pci_idx);
+       edac_dbg(0, "idx=%d\n", pci->pci_idx);
 
        /* create the top main EDAC PCI kobject, IF needed */
        err = edac_pci_main_kobj_setup();
@@ -460,8 +458,7 @@ int edac_pci_create_sysfs(struct edac_pci_ctl_info *pci)
 
        err = sysfs_create_link(edac_kobj, &pci->dev->kobj, EDAC_PCI_SYMLINK);
        if (err) {
-               debugf0("%s() sysfs_create_link() returned err= %d\n",
-                       __func__, err);
+               edac_dbg(0, "sysfs_create_link() returned err= %d\n", err);
                goto symlink_fail;
        }
 
@@ -484,7 +481,7 @@ unregister_cleanup:
  */
 void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci)
 {
-       debugf0("%s() index=%d\n", __func__, pci->pci_idx);
+       edac_dbg(0, "index=%d\n", pci->pci_idx);
 
        /* Remove the symlink */
        sysfs_remove_link(&pci->kobj, EDAC_PCI_SYMLINK);
@@ -496,7 +493,7 @@ void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci)
         * if this 'pci' is the last instance.
         * If it is, the main kobject will be unregistered as a result
         */
-       debugf0("%s() calling edac_pci_main_kobj_teardown()\n", __func__);
+       edac_dbg(0, "calling edac_pci_main_kobj_teardown()\n");
        edac_pci_main_kobj_teardown();
 }
 
@@ -572,7 +569,7 @@ static void edac_pci_dev_parity_test(struct pci_dev *dev)
 
        local_irq_restore(flags);
 
-       debugf4("PCI STATUS= 0x%04x %s\n", status, dev_name(&dev->dev));
+       edac_dbg(4, "PCI STATUS= 0x%04x %s\n", status, dev_name(&dev->dev));
 
        /* check the status reg for errors on boards NOT marked as broken
         * if broken, we cannot trust any of the status bits
@@ -603,13 +600,15 @@ static void edac_pci_dev_parity_test(struct pci_dev *dev)
        }
 
 
-       debugf4("PCI HEADER TYPE= 0x%02x %s\n", header_type, dev_name(&dev->dev));
+       edac_dbg(4, "PCI HEADER TYPE= 0x%02x %s\n",
+                header_type, dev_name(&dev->dev));
 
        if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
                /* On bridges, need to examine secondary status register  */
                status = get_pci_parity_status(dev, 1);
 
-               debugf4("PCI SEC_STATUS= 0x%04x %s\n", status, dev_name(&dev->dev));
+               edac_dbg(4, "PCI SEC_STATUS= 0x%04x %s\n",
+                        status, dev_name(&dev->dev));
 
                /* check the secondary status reg for errors,
                 * on NOT broken boards
@@ -671,7 +670,7 @@ void edac_pci_do_parity_check(void)
 {
        int before_count;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        /* if policy has PCI check off, leave now */
        if (!check_pci_errors)
diff --git a/drivers/edac/highbank_l2_edac.c b/drivers/edac/highbank_l2_edac.c
new file mode 100644 (file)
index 0000000..e599b00
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2011-2012 Calxeda, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/edac.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+
+#include "edac_core.h"
+#include "edac_module.h"
+
+#define SR_CLR_SB_ECC_INTR     0x0
+#define SR_CLR_DB_ECC_INTR     0x4
+
+struct hb_l2_drvdata {
+       void __iomem *base;
+       int sb_irq;
+       int db_irq;
+};
+
+static irqreturn_t highbank_l2_err_handler(int irq, void *dev_id)
+{
+       struct edac_device_ctl_info *dci = dev_id;
+       struct hb_l2_drvdata *drvdata = dci->pvt_info;
+
+       if (irq == drvdata->sb_irq) {
+               writel(1, drvdata->base + SR_CLR_SB_ECC_INTR);
+               edac_device_handle_ce(dci, 0, 0, dci->ctl_name);
+       }
+       if (irq == drvdata->db_irq) {
+               writel(1, drvdata->base + SR_CLR_DB_ECC_INTR);
+               edac_device_handle_ue(dci, 0, 0, dci->ctl_name);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int __devinit highbank_l2_err_probe(struct platform_device *pdev)
+{
+       struct edac_device_ctl_info *dci;
+       struct hb_l2_drvdata *drvdata;
+       struct resource *r;
+       int res = 0;
+
+       dci = edac_device_alloc_ctl_info(sizeof(*drvdata), "cpu",
+               1, "L", 1, 2, NULL, 0, 0);
+       if (!dci)
+               return -ENOMEM;
+
+       drvdata = dci->pvt_info;
+       dci->dev = &pdev->dev;
+       platform_set_drvdata(pdev, dci);
+
+       if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL))
+               return -ENOMEM;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               dev_err(&pdev->dev, "Unable to get mem resource\n");
+               res = -ENODEV;
+               goto err;
+       }
+
+       if (!devm_request_mem_region(&pdev->dev, r->start,
+                                    resource_size(r), dev_name(&pdev->dev))) {
+               dev_err(&pdev->dev, "Error while requesting mem region\n");
+               res = -EBUSY;
+               goto err;
+       }
+
+       drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+       if (!drvdata->base) {
+               dev_err(&pdev->dev, "Unable to map regs\n");
+               res = -ENOMEM;
+               goto err;
+       }
+
+       drvdata->db_irq = platform_get_irq(pdev, 0);
+       res = devm_request_irq(&pdev->dev, drvdata->db_irq,
+                              highbank_l2_err_handler,
+                              0, dev_name(&pdev->dev), dci);
+       if (res < 0)
+               goto err;
+
+       drvdata->sb_irq = platform_get_irq(pdev, 1);
+       res = devm_request_irq(&pdev->dev, drvdata->sb_irq,
+                              highbank_l2_err_handler,
+                              0, dev_name(&pdev->dev), dci);
+       if (res < 0)
+               goto err;
+
+       dci->mod_name = dev_name(&pdev->dev);
+       dci->dev_name = dev_name(&pdev->dev);
+
+       if (edac_device_add_device(dci))
+               goto err;
+
+       devres_close_group(&pdev->dev, NULL);
+       return 0;
+err:
+       devres_release_group(&pdev->dev, NULL);
+       edac_device_free_ctl_info(dci);
+       return res;
+}
+
+static int highbank_l2_err_remove(struct platform_device *pdev)
+{
+       struct edac_device_ctl_info *dci = platform_get_drvdata(pdev);
+
+       edac_device_del_device(&pdev->dev);
+       edac_device_free_ctl_info(dci);
+       return 0;
+}
+
+static const struct of_device_id hb_l2_err_of_match[] = {
+       { .compatible = "calxeda,hb-sregs-l2-ecc", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, hb_l2_err_of_match);
+
+static struct platform_driver highbank_l2_edac_driver = {
+       .probe = highbank_l2_err_probe,
+       .remove = highbank_l2_err_remove,
+       .driver = {
+               .name = "hb_l2_edac",
+               .of_match_table = hb_l2_err_of_match,
+       },
+};
+
+module_platform_driver(highbank_l2_edac_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Calxeda, Inc.");
+MODULE_DESCRIPTION("EDAC Driver for Calxeda Highbank L2 Cache");
diff --git a/drivers/edac/highbank_mc_edac.c b/drivers/edac/highbank_mc_edac.c
new file mode 100644 (file)
index 0000000..c769f47
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2011-2012 Calxeda, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/edac.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/uaccess.h>
+
+#include "edac_core.h"
+#include "edac_module.h"
+
+/* DDR Ctrlr Error Registers */
+#define HB_DDR_ECC_OPT                 0x128
+#define HB_DDR_ECC_U_ERR_ADDR          0x130
+#define HB_DDR_ECC_U_ERR_STAT          0x134
+#define HB_DDR_ECC_U_ERR_DATAL         0x138
+#define HB_DDR_ECC_U_ERR_DATAH         0x13c
+#define HB_DDR_ECC_C_ERR_ADDR          0x140
+#define HB_DDR_ECC_C_ERR_STAT          0x144
+#define HB_DDR_ECC_C_ERR_DATAL         0x148
+#define HB_DDR_ECC_C_ERR_DATAH         0x14c
+#define HB_DDR_ECC_INT_STATUS          0x180
+#define HB_DDR_ECC_INT_ACK             0x184
+#define HB_DDR_ECC_U_ERR_ID            0x424
+#define HB_DDR_ECC_C_ERR_ID            0x428
+
+#define HB_DDR_ECC_INT_STAT_CE         0x8
+#define HB_DDR_ECC_INT_STAT_DOUBLE_CE  0x10
+#define HB_DDR_ECC_INT_STAT_UE         0x20
+#define HB_DDR_ECC_INT_STAT_DOUBLE_UE  0x40
+
+#define HB_DDR_ECC_OPT_MODE_MASK       0x3
+#define HB_DDR_ECC_OPT_FWC             0x100
+#define HB_DDR_ECC_OPT_XOR_SHIFT       16
+
+struct hb_mc_drvdata {
+       void __iomem *mc_vbase;
+};
+
+static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id)
+{
+       struct mem_ctl_info *mci = dev_id;
+       struct hb_mc_drvdata *drvdata = mci->pvt_info;
+       u32 status, err_addr;
+
+       /* Read the interrupt status register */
+       status = readl(drvdata->mc_vbase + HB_DDR_ECC_INT_STATUS);
+
+       if (status & HB_DDR_ECC_INT_STAT_UE) {
+               err_addr = readl(drvdata->mc_vbase + HB_DDR_ECC_U_ERR_ADDR);
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
+                                    err_addr >> PAGE_SHIFT,
+                                    err_addr & ~PAGE_MASK, 0,
+                                    0, 0, -1,
+                                    mci->ctl_name, "");
+       }
+       if (status & HB_DDR_ECC_INT_STAT_CE) {
+               u32 syndrome = readl(drvdata->mc_vbase + HB_DDR_ECC_C_ERR_STAT);
+               syndrome = (syndrome >> 8) & 0xff;
+               err_addr = readl(drvdata->mc_vbase + HB_DDR_ECC_C_ERR_ADDR);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
+                                    err_addr >> PAGE_SHIFT,
+                                    err_addr & ~PAGE_MASK, syndrome,
+                                    0, 0, -1,
+                                    mci->ctl_name, "");
+       }
+
+       /* clear the error, clears the interrupt */
+       writel(status, drvdata->mc_vbase + HB_DDR_ECC_INT_ACK);
+       return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_EDAC_DEBUG
+static ssize_t highbank_mc_err_inject_write(struct file *file,
+                                     const char __user *data,
+                                     size_t count, loff_t *ppos)
+{
+       struct mem_ctl_info *mci = file->private_data;
+       struct hb_mc_drvdata *pdata = mci->pvt_info;
+       char buf[32];
+       size_t buf_size;
+       u32 reg;
+       u8 synd;
+
+       buf_size = min(count, (sizeof(buf)-1));
+       if (copy_from_user(buf, data, buf_size))
+               return -EFAULT;
+       buf[buf_size] = 0;
+
+       if (!kstrtou8(buf, 16, &synd)) {
+               reg = readl(pdata->mc_vbase + HB_DDR_ECC_OPT);
+               reg &= HB_DDR_ECC_OPT_MODE_MASK;
+               reg |= (synd << HB_DDR_ECC_OPT_XOR_SHIFT) | HB_DDR_ECC_OPT_FWC;
+               writel(reg, pdata->mc_vbase + HB_DDR_ECC_OPT);
+       }
+
+       return count;
+}
+
+static int debugfs_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static const struct file_operations highbank_mc_debug_inject_fops = {
+       .open = debugfs_open,
+       .write = highbank_mc_err_inject_write,
+       .llseek = generic_file_llseek,
+};
+
+static void __devinit highbank_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
+{
+       if (mci->debugfs)
+               debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
+                                   &highbank_mc_debug_inject_fops);
+;
+}
+#else
+static void __devinit highbank_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
+{}
+#endif
+
+static int __devinit highbank_mc_probe(struct platform_device *pdev)
+{
+       struct edac_mc_layer layers[2];
+       struct mem_ctl_info *mci;
+       struct hb_mc_drvdata *drvdata;
+       struct dimm_info *dimm;
+       struct resource *r;
+       u32 control;
+       int irq;
+       int res = 0;
+
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = 1;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = 1;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
+                           sizeof(struct hb_mc_drvdata));
+       if (!mci)
+               return -ENOMEM;
+
+       mci->pdev = &pdev->dev;
+       drvdata = mci->pvt_info;
+       platform_set_drvdata(pdev, mci);
+
+       if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL))
+               return -ENOMEM;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               dev_err(&pdev->dev, "Unable to get mem resource\n");
+               res = -ENODEV;
+               goto err;
+       }
+
+       if (!devm_request_mem_region(&pdev->dev, r->start,
+                                    resource_size(r), dev_name(&pdev->dev))) {
+               dev_err(&pdev->dev, "Error while requesting mem region\n");
+               res = -EBUSY;
+               goto err;
+       }
+
+       drvdata->mc_vbase = devm_ioremap(&pdev->dev,
+                                         r->start, resource_size(r));
+       if (!drvdata->mc_vbase) {
+               dev_err(&pdev->dev, "Unable to map regs\n");
+               res = -ENOMEM;
+               goto err;
+       }
+
+       control = readl(drvdata->mc_vbase + HB_DDR_ECC_OPT) & 0x3;
+       if (!control || (control == 0x2)) {
+               dev_err(&pdev->dev, "No ECC present, or ECC disabled\n");
+               res = -ENODEV;
+               goto err;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       res = devm_request_irq(&pdev->dev, irq, highbank_mc_err_handler,
+                              0, dev_name(&pdev->dev), mci);
+       if (res < 0) {
+               dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
+               goto err;
+       }
+
+       mci->mtype_cap = MEM_FLAG_DDR3;
+       mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
+       mci->edac_cap = EDAC_FLAG_SECDED;
+       mci->mod_name = dev_name(&pdev->dev);
+       mci->mod_ver = "1";
+       mci->ctl_name = dev_name(&pdev->dev);
+       mci->scrub_mode = SCRUB_SW_SRC;
+
+       /* Only a single 4GB DIMM is supported */
+       dimm = *mci->dimms;
+       dimm->nr_pages = (~0UL >> PAGE_SHIFT) + 1;
+       dimm->grain = 8;
+       dimm->dtype = DEV_X8;
+       dimm->mtype = MEM_DDR3;
+       dimm->edac_mode = EDAC_SECDED;
+
+       res = edac_mc_add_mc(mci);
+       if (res < 0)
+               goto err;
+
+       highbank_mc_create_debugfs_nodes(mci);
+
+       devres_close_group(&pdev->dev, NULL);
+       return 0;
+err:
+       devres_release_group(&pdev->dev, NULL);
+       edac_mc_free(mci);
+       return res;
+}
+
+static int highbank_mc_remove(struct platform_device *pdev)
+{
+       struct mem_ctl_info *mci = platform_get_drvdata(pdev);
+
+       edac_mc_del_mc(&pdev->dev);
+       edac_mc_free(mci);
+       return 0;
+}
+
+static const struct of_device_id hb_ddr_ctrl_of_match[] = {
+       { .compatible = "calxeda,hb-ddr-ctrl", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, hb_ddr_ctrl_of_match);
+
+static struct platform_driver highbank_mc_edac_driver = {
+       .probe = highbank_mc_probe,
+       .remove = highbank_mc_remove,
+       .driver = {
+               .name = "hb_mc_edac",
+               .of_match_table = hb_ddr_ctrl_of_match,
+       },
+};
+
+module_platform_driver(highbank_mc_edac_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Calxeda, Inc.");
+MODULE_DESCRIPTION("EDAC Driver for Calxeda Highbank");
index 8ad1744faacd9559f6f4ab66eb1bf1b960fd4ee0..d3d19cc4e9a1a48eccb84820dbe8df519955cc96 100644 (file)
@@ -194,7 +194,7 @@ static void i3000_get_error_info(struct mem_ctl_info *mci,
 {
        struct pci_dev *pdev;
 
-       pdev = to_pci_dev(mci->dev);
+       pdev = to_pci_dev(mci->pdev);
 
        /*
         * This is a mess because there is no atomic way to read all the
@@ -236,7 +236,7 @@ static int i3000_process_error_info(struct mem_ctl_info *mci,
        int row, multi_chan, channel;
        unsigned long pfn, offset;
 
-       multi_chan = mci->csrows[0].nr_channels - 1;
+       multi_chan = mci->csrows[0]->nr_channels - 1;
 
        if (!(info->errsts & I3000_ERRSTS_BITS))
                return 0;
@@ -245,9 +245,9 @@ static int i3000_process_error_info(struct mem_ctl_info *mci,
                return 1;
 
        if ((info->errsts ^ info->errsts2) & I3000_ERRSTS_BITS) {
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
                                     -1, -1, -1,
-                                    "UE overwrote CE", "", NULL);
+                                    "UE overwrote CE", "");
                info->errsts = info->errsts2;
        }
 
@@ -258,15 +258,15 @@ static int i3000_process_error_info(struct mem_ctl_info *mci,
        row = edac_mc_find_csrow_by_page(mci, pfn);
 
        if (info->errsts & I3000_ERRSTS_UE)
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                     pfn, offset, 0,
                                     row, -1, -1,
-                                    "i3000 UE", "", NULL);
+                                    "i3000 UE", "");
        else
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                     pfn, offset, info->derrsyn,
                                     row, multi_chan ? channel : 0, -1,
-                                    "i3000 CE", "", NULL);
+                                    "i3000 CE", "");
 
        return 1;
 }
@@ -275,7 +275,7 @@ static void i3000_check(struct mem_ctl_info *mci)
 {
        struct i3000_error_info info;
 
-       debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
+       edac_dbg(1, "MC%d\n", mci->mc_idx);
        i3000_get_error_info(mci, &info);
        i3000_process_error_info(mci, &info, 1);
 }
@@ -322,7 +322,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
        unsigned long mchbar;
        void __iomem *window;
 
-       debugf0("MC: %s()\n", __func__);
+       edac_dbg(0, "MC:\n");
 
        pci_read_config_dword(pdev, I3000_MCHBAR, (u32 *) & mchbar);
        mchbar &= I3000_MCHBAR_MASK;
@@ -366,9 +366,9 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
        if (!mci)
                return -ENOMEM;
 
-       debugf3("MC: %s(): init mci\n", __func__);
+       edac_dbg(3, "MC: init mci\n");
 
-       mci->dev = &pdev->dev;
+       mci->pdev = &pdev->dev;
        mci->mtype_cap = MEM_FLAG_DDR2;
 
        mci->edac_ctl_cap = EDAC_FLAG_SECDED;
@@ -393,14 +393,13 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
        for (last_cumul_size = i = 0; i < mci->nr_csrows; i++) {
                u8 value;
                u32 cumul_size;
-               struct csrow_info *csrow = &mci->csrows[i];
+               struct csrow_info *csrow = mci->csrows[i];
 
                value = drb[i];
                cumul_size = value << (I3000_DRB_SHIFT - PAGE_SHIFT);
                if (interleaved)
                        cumul_size <<= 1;
-               debugf3("MC: %s(): (%d) cumul_size 0x%x\n",
-                       __func__, i, cumul_size);
+               edac_dbg(3, "MC: (%d) cumul_size 0x%x\n", i, cumul_size);
                if (cumul_size == last_cumul_size)
                        continue;
 
@@ -410,7 +409,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
                last_cumul_size = cumul_size;
 
                for (j = 0; j < nr_channels; j++) {
-                       struct dimm_info *dimm = csrow->channels[j].dimm;
+                       struct dimm_info *dimm = csrow->channels[j]->dimm;
 
                        dimm->nr_pages = nr_pages / nr_channels;
                        dimm->grain = I3000_DEAP_GRAIN;
@@ -429,7 +428,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
 
        rc = -ENODEV;
        if (edac_mc_add_mc(mci)) {
-               debugf3("MC: %s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(3, "MC: failed edac_mc_add_mc()\n");
                goto fail;
        }
 
@@ -445,7 +444,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
        }
 
        /* get this far and it's successful */
-       debugf3("MC: %s(): success\n", __func__);
+       edac_dbg(3, "MC: success\n");
        return 0;
 
 fail:
@@ -461,7 +460,7 @@ static int __devinit i3000_init_one(struct pci_dev *pdev,
 {
        int rc;
 
-       debugf0("MC: %s()\n", __func__);
+       edac_dbg(0, "MC:\n");
 
        if (pci_enable_device(pdev) < 0)
                return -EIO;
@@ -477,7 +476,7 @@ static void __devexit i3000_remove_one(struct pci_dev *pdev)
 {
        struct mem_ctl_info *mci;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        if (i3000_pci)
                edac_pci_release_generic_ctl(i3000_pci);
@@ -511,7 +510,7 @@ static int __init i3000_init(void)
 {
        int pci_rc;
 
-       debugf3("MC: %s()\n", __func__);
+       edac_dbg(3, "MC:\n");
 
        /* Ensure that the OPSTATE is set correctly for POLL or NMI */
        opstate_init();
@@ -525,14 +524,14 @@ static int __init i3000_init(void)
                mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
                                        PCI_DEVICE_ID_INTEL_3000_HB, NULL);
                if (!mci_pdev) {
-                       debugf0("i3000 pci_get_device fail\n");
+                       edac_dbg(0, "i3000 pci_get_device fail\n");
                        pci_rc = -ENODEV;
                        goto fail1;
                }
 
                pci_rc = i3000_init_one(mci_pdev, i3000_pci_tbl);
                if (pci_rc < 0) {
-                       debugf0("i3000 init fail\n");
+                       edac_dbg(0, "i3000 init fail\n");
                        pci_rc = -ENODEV;
                        goto fail1;
                }
@@ -552,7 +551,7 @@ fail0:
 
 static void __exit i3000_exit(void)
 {
-       debugf3("MC: %s()\n", __func__);
+       edac_dbg(3, "MC:\n");
 
        pci_unregister_driver(&i3000_driver);
        if (!i3000_registered) {
index bbe43ef718238c72d159affd4d2f80e22141f88f..47180a08edad28c7c95fea0cd51e9b7b15172f27 100644 (file)
@@ -110,10 +110,10 @@ static int how_many_channels(struct pci_dev *pdev)
 
        pci_read_config_byte(pdev, I3200_CAPID0 + 8, &capid0_8b);
        if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */
-               debugf0("In single channel mode.\n");
+               edac_dbg(0, "In single channel mode\n");
                return 1;
        } else {
-               debugf0("In dual channel mode.\n");
+               edac_dbg(0, "In dual channel mode\n");
                return 2;
        }
 }
@@ -159,7 +159,7 @@ static void i3200_clear_error_info(struct mem_ctl_info *mci)
 {
        struct pci_dev *pdev;
 
-       pdev = to_pci_dev(mci->dev);
+       pdev = to_pci_dev(mci->pdev);
 
        /*
         * Clear any error bits.
@@ -176,7 +176,7 @@ static void i3200_get_and_clear_error_info(struct mem_ctl_info *mci,
        struct i3200_priv *priv = mci->pvt_info;
        void __iomem *window = priv->window;
 
-       pdev = to_pci_dev(mci->dev);
+       pdev = to_pci_dev(mci->pdev);
 
        /*
         * This is a mess because there is no atomic way to read all the
@@ -218,25 +218,25 @@ static void i3200_process_error_info(struct mem_ctl_info *mci,
                return;
 
        if ((info->errsts ^ info->errsts2) & I3200_ERRSTS_BITS) {
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
-                                    -1, -1, -1, "UE overwrote CE", "", NULL);
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
+                                    -1, -1, -1, "UE overwrote CE", "");
                info->errsts = info->errsts2;
        }
 
        for (channel = 0; channel < nr_channels; channel++) {
                log = info->eccerrlog[channel];
                if (log & I3200_ECCERRLOG_UE) {
-                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                             0, 0, 0,
                                             eccerrlog_row(channel, log),
                                             -1, -1,
-                                            "i3000 UE", "", NULL);
+                                            "i3000 UE", "");
                } else if (log & I3200_ECCERRLOG_CE) {
-                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                             0, 0, eccerrlog_syndrome(log),
                                             eccerrlog_row(channel, log),
                                             -1, -1,
-                                            "i3000 UE", "", NULL);
+                                            "i3000 UE", "");
                }
        }
 }
@@ -245,7 +245,7 @@ static void i3200_check(struct mem_ctl_info *mci)
 {
        struct i3200_error_info info;
 
-       debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
+       edac_dbg(1, "MC%d\n", mci->mc_idx);
        i3200_get_and_clear_error_info(mci, &info);
        i3200_process_error_info(mci, &info);
 }
@@ -332,7 +332,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
        void __iomem *window;
        struct i3200_priv *priv;
 
-       debugf0("MC: %s()\n", __func__);
+       edac_dbg(0, "MC:\n");
 
        window = i3200_map_mchbar(pdev);
        if (!window)
@@ -352,9 +352,9 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
        if (!mci)
                return -ENOMEM;
 
-       debugf3("MC: %s(): init mci\n", __func__);
+       edac_dbg(3, "MC: init mci\n");
 
-       mci->dev = &pdev->dev;
+       mci->pdev = &pdev->dev;
        mci->mtype_cap = MEM_FLAG_DDR2;
 
        mci->edac_ctl_cap = EDAC_FLAG_SECDED;
@@ -379,7 +379,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
         */
        for (i = 0; i < mci->nr_csrows; i++) {
                unsigned long nr_pages;
-               struct csrow_info *csrow = &mci->csrows[i];
+               struct csrow_info *csrow = mci->csrows[i];
 
                nr_pages = drb_to_nr_pages(drbs, stacked,
                        i / I3200_RANKS_PER_CHANNEL,
@@ -389,7 +389,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
                        continue;
 
                for (j = 0; j < nr_channels; j++) {
-                       struct dimm_info *dimm = csrow->channels[j].dimm;
+                       struct dimm_info *dimm = csrow->channels[j]->dimm;
 
                        dimm->nr_pages = nr_pages / nr_channels;
                        dimm->grain = nr_pages << PAGE_SHIFT;
@@ -403,12 +403,12 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
 
        rc = -ENODEV;
        if (edac_mc_add_mc(mci)) {
-               debugf3("MC: %s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(3, "MC: failed edac_mc_add_mc()\n");
                goto fail;
        }
 
        /* get this far and it's successful */
-       debugf3("MC: %s(): success\n", __func__);
+       edac_dbg(3, "MC: success\n");
        return 0;
 
 fail:
@@ -424,7 +424,7 @@ static int __devinit i3200_init_one(struct pci_dev *pdev,
 {
        int rc;
 
-       debugf0("MC: %s()\n", __func__);
+       edac_dbg(0, "MC:\n");
 
        if (pci_enable_device(pdev) < 0)
                return -EIO;
@@ -441,7 +441,7 @@ static void __devexit i3200_remove_one(struct pci_dev *pdev)
        struct mem_ctl_info *mci;
        struct i3200_priv *priv;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        mci = edac_mc_del_mc(&pdev->dev);
        if (!mci)
@@ -475,7 +475,7 @@ static int __init i3200_init(void)
 {
        int pci_rc;
 
-       debugf3("MC: %s()\n", __func__);
+       edac_dbg(3, "MC:\n");
 
        /* Ensure that the OPSTATE is set correctly for POLL or NMI */
        opstate_init();
@@ -489,14 +489,14 @@ static int __init i3200_init(void)
                mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
                                PCI_DEVICE_ID_INTEL_3200_HB, NULL);
                if (!mci_pdev) {
-                       debugf0("i3200 pci_get_device fail\n");
+                       edac_dbg(0, "i3200 pci_get_device fail\n");
                        pci_rc = -ENODEV;
                        goto fail1;
                }
 
                pci_rc = i3200_init_one(mci_pdev, i3200_pci_tbl);
                if (pci_rc < 0) {
-                       debugf0("i3200 init fail\n");
+                       edac_dbg(0, "i3200 init fail\n");
                        pci_rc = -ENODEV;
                        goto fail1;
                }
@@ -516,7 +516,7 @@ fail0:
 
 static void __exit i3200_exit(void)
 {
-       debugf3("MC: %s()\n", __func__);
+       edac_dbg(3, "MC:\n");
 
        pci_unregister_driver(&i3200_driver);
        if (!i3200_registered) {
index 11ea835f155a840dc2ae3048ca67b904e1385801..39c63757c2a14fd7bc988c23d7ee6be2e84d0a06 100644 (file)
 #define CHANNELS_PER_BRANCH    2
 #define MAX_BRANCHES           2
 
-/* Defines to extract the vaious fields from the
+/* Defines to extract the various fields from the
  *     MTRx - Memory Technology Registers
  */
 #define MTR_DIMMS_PRESENT(mtr)         ((mtr) & (0x1 << 8))
 #define MTR_DIMM_COLS(mtr)             ((mtr) & 0x3)
 #define MTR_DIMM_COLS_ADDR_BITS(mtr)   (MTR_DIMM_COLS(mtr) + 10)
 
-#ifdef CONFIG_EDAC_DEBUG
-static char *numrow_toString[] = {
-       "8,192 - 13 rows",
-       "16,384 - 14 rows",
-       "32,768 - 15 rows",
-       "reserved"
-};
-
-static char *numcol_toString[] = {
-       "1,024 - 10 columns",
-       "2,048 - 11 columns",
-       "4,096 - 12 columns",
-       "reserved"
-};
-#endif
-
 /* enables the report of miscellaneous messages as CE errors - default off */
 static int misc_messages;
 
@@ -344,7 +328,13 @@ struct i5000_pvt {
        struct pci_dev *branch_1;       /* 22.0 */
 
        u16 tolm;               /* top of low memory */
-       u64 ambase;             /* AMB BAR */
+       union {
+               u64 ambase;             /* AMB BAR */
+               struct {
+                       u32 ambase_bottom;
+                       u32 ambase_top;
+               } u __packed;
+       };
 
        u16 mir0, mir1, mir2;
 
@@ -494,10 +484,9 @@ static void i5000_process_fatal_error_info(struct mem_ctl_info *mci,
        ras = NREC_RAS(info->nrecmemb);
        cas = NREC_CAS(info->nrecmemb);
 
-       debugf0("\t\tCSROW= %d  Channel= %d "
-               "(DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
-               rank, channel, bank,
-               rdwr ? "Write" : "Read", ras, cas);
+       edac_dbg(0, "\t\tCSROW= %d  Channel= %d (DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
+                rank, channel, bank,
+                rdwr ? "Write" : "Read", ras, cas);
 
        /* Only 1 bit will be on */
        switch (allErrors) {
@@ -536,10 +525,10 @@ static void i5000_process_fatal_error_info(struct mem_ctl_info *mci,
                 bank, ras, cas, allErrors, specific);
 
        /* Call the helper to output message */
-       edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 0, 0, 0,
+       edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 1, 0, 0, 0,
                             channel >> 1, channel & 1, rank,
                             rdwr ? "Write error" : "Read error",
-                            msg, NULL);
+                            msg);
 }
 
 /*
@@ -574,7 +563,7 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
        /* ONLY ONE of the possible error bits will be set, as per the docs */
        ue_errors = allErrors & FERR_NF_UNCORRECTABLE;
        if (ue_errors) {
-               debugf0("\tUncorrected bits= 0x%x\n", ue_errors);
+               edac_dbg(0, "\tUncorrected bits= 0x%x\n", ue_errors);
 
                branch = EXTRACT_FBDCHAN_INDX(info->ferr_nf_fbd);
 
@@ -590,11 +579,9 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
                ras = NREC_RAS(info->nrecmemb);
                cas = NREC_CAS(info->nrecmemb);
 
-               debugf0
-                       ("\t\tCSROW= %d  Channels= %d,%d  (Branch= %d "
-                       "DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
-                       rank, channel, channel + 1, branch >> 1, bank,
-                       rdwr ? "Write" : "Read", ras, cas);
+               edac_dbg(0, "\t\tCSROW= %d  Channels= %d,%d  (Branch= %d DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
+                        rank, channel, channel + 1, branch >> 1, bank,
+                        rdwr ? "Write" : "Read", ras, cas);
 
                switch (ue_errors) {
                case FERR_NF_M12ERR:
@@ -637,16 +624,16 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
                         rank, bank, ras, cas, ue_errors, specific);
 
                /* Call the helper to output message */
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
                                channel >> 1, -1, rank,
                                rdwr ? "Write error" : "Read error",
-                               msg, NULL);
+                               msg);
        }
 
        /* Check correctable errors */
        ce_errors = allErrors & FERR_NF_CORRECTABLE;
        if (ce_errors) {
-               debugf0("\tCorrected bits= 0x%x\n", ce_errors);
+               edac_dbg(0, "\tCorrected bits= 0x%x\n", ce_errors);
 
                branch = EXTRACT_FBDCHAN_INDX(info->ferr_nf_fbd);
 
@@ -664,10 +651,9 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
                ras = REC_RAS(info->recmemb);
                cas = REC_CAS(info->recmemb);
 
-               debugf0("\t\tCSROW= %d Channel= %d  (Branch %d "
-                       "DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
-                       rank, channel, branch >> 1, bank,
-                       rdwr ? "Write" : "Read", ras, cas);
+               edac_dbg(0, "\t\tCSROW= %d Channel= %d  (Branch %d DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
+                        rank, channel, branch >> 1, bank,
+                        rdwr ? "Write" : "Read", ras, cas);
 
                switch (ce_errors) {
                case FERR_NF_M17ERR:
@@ -692,10 +678,10 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
                         specific);
 
                /* Call the helper to output message */
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0,
                                channel >> 1, channel % 2, rank,
                                rdwr ? "Write error" : "Read error",
-                               msg, NULL);
+                               msg);
        }
 
        if (!misc_messages)
@@ -738,9 +724,9 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
                         "Err=%#x (%s)", misc_errors, specific);
 
                /* Call the helper to output message */
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0,
                                branch >> 1, -1, -1,
-                               "Misc error", msg, NULL);
+                               "Misc error", msg);
        }
 }
 
@@ -779,7 +765,7 @@ static void i5000_clear_error(struct mem_ctl_info *mci)
 static void i5000_check_error(struct mem_ctl_info *mci)
 {
        struct i5000_error_info info;
-       debugf4("MC%d: %s: %s()\n", mci->mc_idx, __FILE__, __func__);
+       edac_dbg(4, "MC%d\n", mci->mc_idx);
        i5000_get_error_info(mci, &info);
        i5000_process_error_info(mci, &info, 1);
 }
@@ -850,15 +836,16 @@ static int i5000_get_devices(struct mem_ctl_info *mci, int dev_idx)
 
        pvt->fsb_error_regs = pdev;
 
-       debugf1("System Address, processor bus- PCI Bus ID: %s  %x:%x\n",
-               pci_name(pvt->system_address),
-               pvt->system_address->vendor, pvt->system_address->device);
-       debugf1("Branchmap, control and errors - PCI Bus ID: %s  %x:%x\n",
-               pci_name(pvt->branchmap_werrors),
-               pvt->branchmap_werrors->vendor, pvt->branchmap_werrors->device);
-       debugf1("FSB Error Regs - PCI Bus ID: %s  %x:%x\n",
-               pci_name(pvt->fsb_error_regs),
-               pvt->fsb_error_regs->vendor, pvt->fsb_error_regs->device);
+       edac_dbg(1, "System Address, processor bus- PCI Bus ID: %s  %x:%x\n",
+                pci_name(pvt->system_address),
+                pvt->system_address->vendor, pvt->system_address->device);
+       edac_dbg(1, "Branchmap, control and errors - PCI Bus ID: %s  %x:%x\n",
+                pci_name(pvt->branchmap_werrors),
+                pvt->branchmap_werrors->vendor,
+                pvt->branchmap_werrors->device);
+       edac_dbg(1, "FSB Error Regs - PCI Bus ID: %s  %x:%x\n",
+                pci_name(pvt->fsb_error_regs),
+                pvt->fsb_error_regs->vendor, pvt->fsb_error_regs->device);
 
        pdev = NULL;
        pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
@@ -981,16 +968,25 @@ static void decode_mtr(int slot_row, u16 mtr)
 
        ans = MTR_DIMMS_PRESENT(mtr);
 
-       debugf2("\tMTR%d=0x%x:  DIMMs are %s\n", slot_row, mtr,
-               ans ? "Present" : "NOT Present");
+       edac_dbg(2, "\tMTR%d=0x%x:  DIMMs are %sPresent\n",
+                slot_row, mtr, ans ? "" : "NOT ");
        if (!ans)
                return;
 
-       debugf2("\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
-       debugf2("\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr));
-       debugf2("\t\tNUMRANK: %s\n", MTR_DIMM_RANK(mtr) ? "double" : "single");
-       debugf2("\t\tNUMROW: %s\n", numrow_toString[MTR_DIMM_ROWS(mtr)]);
-       debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]);
+       edac_dbg(2, "\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
+       edac_dbg(2, "\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr));
+       edac_dbg(2, "\t\tNUMRANK: %s\n",
+                MTR_DIMM_RANK(mtr) ? "double" : "single");
+       edac_dbg(2, "\t\tNUMROW: %s\n",
+                MTR_DIMM_ROWS(mtr) == 0 ? "8,192 - 13 rows" :
+                MTR_DIMM_ROWS(mtr) == 1 ? "16,384 - 14 rows" :
+                MTR_DIMM_ROWS(mtr) == 2 ? "32,768 - 15 rows" :
+                "reserved");
+       edac_dbg(2, "\t\tNUMCOL: %s\n",
+                MTR_DIMM_COLS(mtr) == 0 ? "1,024 - 10 columns" :
+                MTR_DIMM_COLS(mtr) == 1 ? "2,048 - 11 columns" :
+                MTR_DIMM_COLS(mtr) == 2 ? "4,096 - 12 columns" :
+                "reserved");
 }
 
 static void handle_channel(struct i5000_pvt *pvt, int slot, int channel,
@@ -1061,7 +1057,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt)
                                "--------------------------------");
                        p += n;
                        space -= n;
-                       debugf2("%s\n", mem_buffer);
+                       edac_dbg(2, "%s\n", mem_buffer);
                        p = mem_buffer;
                        space = PAGE_SIZE;
                }
@@ -1082,7 +1078,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt)
                }
                p += n;
                space -= n;
-               debugf2("%s\n", mem_buffer);
+               edac_dbg(2, "%s\n", mem_buffer);
                p = mem_buffer;
                space = PAGE_SIZE;
        }
@@ -1092,7 +1088,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt)
                "--------------------------------");
        p += n;
        space -= n;
-       debugf2("%s\n", mem_buffer);
+       edac_dbg(2, "%s\n", mem_buffer);
        p = mem_buffer;
        space = PAGE_SIZE;
 
@@ -1105,7 +1101,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt)
                p += n;
                space -= n;
        }
-       debugf2("%s\n", mem_buffer);
+       edac_dbg(2, "%s\n", mem_buffer);
        p = mem_buffer;
        space = PAGE_SIZE;
 
@@ -1118,7 +1114,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt)
        }
 
        /* output the last message and free buffer */
-       debugf2("%s\n", mem_buffer);
+       edac_dbg(2, "%s\n", mem_buffer);
        kfree(mem_buffer);
 }
 
@@ -1141,24 +1137,25 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci)
        pvt = mci->pvt_info;
 
        pci_read_config_dword(pvt->system_address, AMBASE,
-                       (u32 *) & pvt->ambase);
+                       &pvt->u.ambase_bottom);
        pci_read_config_dword(pvt->system_address, AMBASE + sizeof(u32),
-                       ((u32 *) & pvt->ambase) + sizeof(u32));
+                       &pvt->u.ambase_top);
 
        maxdimmperch = pvt->maxdimmperch;
        maxch = pvt->maxch;
 
-       debugf2("AMBASE= 0x%lx  MAXCH= %d  MAX-DIMM-Per-CH= %d\n",
-               (long unsigned int)pvt->ambase, pvt->maxch, pvt->maxdimmperch);
+       edac_dbg(2, "AMBASE= 0x%lx  MAXCH= %d  MAX-DIMM-Per-CH= %d\n",
+                (long unsigned int)pvt->ambase, pvt->maxch, pvt->maxdimmperch);
 
        /* Get the Branch Map regs */
        pci_read_config_word(pvt->branchmap_werrors, TOLM, &pvt->tolm);
        pvt->tolm >>= 12;
-       debugf2("\nTOLM (number of 256M regions) =%u (0x%x)\n", pvt->tolm,
-               pvt->tolm);
+       edac_dbg(2, "TOLM (number of 256M regions) =%u (0x%x)\n",
+                pvt->tolm, pvt->tolm);
 
        actual_tolm = pvt->tolm << 28;
-       debugf2("Actual TOLM byte addr=%u (0x%x)\n", actual_tolm, actual_tolm);
+       edac_dbg(2, "Actual TOLM byte addr=%u (0x%x)\n",
+                actual_tolm, actual_tolm);
 
        pci_read_config_word(pvt->branchmap_werrors, MIR0, &pvt->mir0);
        pci_read_config_word(pvt->branchmap_werrors, MIR1, &pvt->mir1);
@@ -1168,15 +1165,18 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci)
        limit = (pvt->mir0 >> 4) & 0x0FFF;
        way0 = pvt->mir0 & 0x1;
        way1 = pvt->mir0 & 0x2;
-       debugf2("MIR0: limit= 0x%x  WAY1= %u  WAY0= %x\n", limit, way1, way0);
+       edac_dbg(2, "MIR0: limit= 0x%x  WAY1= %u  WAY0= %x\n",
+                limit, way1, way0);
        limit = (pvt->mir1 >> 4) & 0x0FFF;
        way0 = pvt->mir1 & 0x1;
        way1 = pvt->mir1 & 0x2;
-       debugf2("MIR1: limit= 0x%x  WAY1= %u  WAY0= %x\n", limit, way1, way0);
+       edac_dbg(2, "MIR1: limit= 0x%x  WAY1= %u  WAY0= %x\n",
+                limit, way1, way0);
        limit = (pvt->mir2 >> 4) & 0x0FFF;
        way0 = pvt->mir2 & 0x1;
        way1 = pvt->mir2 & 0x2;
-       debugf2("MIR2: limit= 0x%x  WAY1= %u  WAY0= %x\n", limit, way1, way0);
+       edac_dbg(2, "MIR2: limit= 0x%x  WAY1= %u  WAY0= %x\n",
+                limit, way1, way0);
 
        /* Get the MTR[0-3] regs */
        for (slot_row = 0; slot_row < NUM_MTRS; slot_row++) {
@@ -1185,31 +1185,31 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci)
                pci_read_config_word(pvt->branch_0, where,
                                &pvt->b0_mtr[slot_row]);
 
-               debugf2("MTR%d where=0x%x B0 value=0x%x\n", slot_row, where,
-                       pvt->b0_mtr[slot_row]);
+               edac_dbg(2, "MTR%d where=0x%x B0 value=0x%x\n",
+                        slot_row, where, pvt->b0_mtr[slot_row]);
 
                if (pvt->maxch >= CHANNELS_PER_BRANCH) {
                        pci_read_config_word(pvt->branch_1, where,
                                        &pvt->b1_mtr[slot_row]);
-                       debugf2("MTR%d where=0x%x B1 value=0x%x\n", slot_row,
-                               where, pvt->b1_mtr[slot_row]);
+                       edac_dbg(2, "MTR%d where=0x%x B1 value=0x%x\n",
+                                slot_row, where, pvt->b1_mtr[slot_row]);
                } else {
                        pvt->b1_mtr[slot_row] = 0;
                }
        }
 
        /* Read and dump branch 0's MTRs */
-       debugf2("\nMemory Technology Registers:\n");
-       debugf2("   Branch 0:\n");
+       edac_dbg(2, "Memory Technology Registers:\n");
+       edac_dbg(2, "   Branch 0:\n");
        for (slot_row = 0; slot_row < NUM_MTRS; slot_row++) {
                decode_mtr(slot_row, pvt->b0_mtr[slot_row]);
        }
        pci_read_config_word(pvt->branch_0, AMB_PRESENT_0,
                        &pvt->b0_ambpresent0);
-       debugf2("\t\tAMB-Branch 0-present0 0x%x:\n", pvt->b0_ambpresent0);
+       edac_dbg(2, "\t\tAMB-Branch 0-present0 0x%x:\n", pvt->b0_ambpresent0);
        pci_read_config_word(pvt->branch_0, AMB_PRESENT_1,
                        &pvt->b0_ambpresent1);
-       debugf2("\t\tAMB-Branch 0-present1 0x%x:\n", pvt->b0_ambpresent1);
+       edac_dbg(2, "\t\tAMB-Branch 0-present1 0x%x:\n", pvt->b0_ambpresent1);
 
        /* Only if we have 2 branchs (4 channels) */
        if (pvt->maxch < CHANNELS_PER_BRANCH) {
@@ -1217,18 +1217,18 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci)
                pvt->b1_ambpresent1 = 0;
        } else {
                /* Read and dump  branch 1's MTRs */
-               debugf2("   Branch 1:\n");
+               edac_dbg(2, "   Branch 1:\n");
                for (slot_row = 0; slot_row < NUM_MTRS; slot_row++) {
                        decode_mtr(slot_row, pvt->b1_mtr[slot_row]);
                }
                pci_read_config_word(pvt->branch_1, AMB_PRESENT_0,
                                &pvt->b1_ambpresent0);
-               debugf2("\t\tAMB-Branch 1-present0 0x%x:\n",
-                       pvt->b1_ambpresent0);
+               edac_dbg(2, "\t\tAMB-Branch 1-present0 0x%x:\n",
+                        pvt->b1_ambpresent0);
                pci_read_config_word(pvt->branch_1, AMB_PRESENT_1,
                                &pvt->b1_ambpresent1);
-               debugf2("\t\tAMB-Branch 1-present1 0x%x:\n",
-                       pvt->b1_ambpresent1);
+               edac_dbg(2, "\t\tAMB-Branch 1-present1 0x%x:\n",
+                        pvt->b1_ambpresent1);
        }
 
        /* Go and determine the size of each DIMM and place in an
@@ -1363,10 +1363,9 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx)
        int num_channels;
        int num_dimms_per_channel;
 
-       debugf0("MC: %s: %s(), pdev bus %u dev=0x%x fn=0x%x\n",
-               __FILE__, __func__,
-               pdev->bus->number,
-               PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+       edac_dbg(0, "MC: pdev bus %u dev=0x%x fn=0x%x\n",
+                pdev->bus->number,
+                PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
 
        /* We only are looking for func 0 of the set */
        if (PCI_FUNC(pdev->devfn) != 0)
@@ -1388,8 +1387,8 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx)
        i5000_get_dimm_and_channel_counts(pdev, &num_dimms_per_channel,
                                        &num_channels);
 
-       debugf0("MC: %s(): Number of Branches=2 Channels= %d  DIMMS= %d\n",
-               __func__, num_channels, num_dimms_per_channel);
+       edac_dbg(0, "MC: Number of Branches=2 Channels= %d  DIMMS= %d\n",
+                num_channels, num_dimms_per_channel);
 
        /* allocate a new MC control structure */
 
@@ -1406,10 +1405,9 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx)
        if (mci == NULL)
                return -ENOMEM;
 
-       kobject_get(&mci->edac_mci_kobj);
-       debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci);
+       edac_dbg(0, "MC: mci = %p\n", mci);
 
-       mci->dev = &pdev->dev;  /* record ptr  to the generic device */
+       mci->pdev = &pdev->dev; /* record ptr  to the generic device */
 
        pvt = mci->pvt_info;
        pvt->system_address = pdev;     /* Record this device in our private */
@@ -1439,19 +1437,16 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx)
        /* initialize the MC control structure 'csrows' table
         * with the mapping and control information */
        if (i5000_init_csrows(mci)) {
-               debugf0("MC: Setting mci->edac_cap to EDAC_FLAG_NONE\n"
-                       "    because i5000_init_csrows() returned nonzero "
-                       "value\n");
+               edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i5000_init_csrows() returned nonzero value\n");
                mci->edac_cap = EDAC_FLAG_NONE; /* no csrows found */
        } else {
-               debugf1("MC: Enable error reporting now\n");
+               edac_dbg(1, "MC: Enable error reporting now\n");
                i5000_enable_error_reporting(mci);
        }
 
        /* add this new MC control structure to EDAC's list of MCs */
        if (edac_mc_add_mc(mci)) {
-               debugf0("MC: %s: %s(): failed edac_mc_add_mc()\n",
-                       __FILE__, __func__);
+               edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
                /* FIXME: perhaps some code should go here that disables error
                 * reporting if we just enabled it
                 */
@@ -1479,7 +1474,6 @@ fail1:
        i5000_put_devices(mci);
 
 fail0:
-       kobject_put(&mci->edac_mci_kobj);
        edac_mc_free(mci);
        return -ENODEV;
 }
@@ -1496,7 +1490,7 @@ static int __devinit i5000_init_one(struct pci_dev *pdev,
 {
        int rc;
 
-       debugf0("MC: %s: %s()\n", __FILE__, __func__);
+       edac_dbg(0, "MC:\n");
 
        /* wake up device */
        rc = pci_enable_device(pdev);
@@ -1515,7 +1509,7 @@ static void __devexit i5000_remove_one(struct pci_dev *pdev)
 {
        struct mem_ctl_info *mci;
 
-       debugf0("%s: %s()\n", __FILE__, __func__);
+       edac_dbg(0, "\n");
 
        if (i5000_pci)
                edac_pci_release_generic_ctl(i5000_pci);
@@ -1525,7 +1519,6 @@ static void __devexit i5000_remove_one(struct pci_dev *pdev)
 
        /* retrieve references to resources, and free those resources */
        i5000_put_devices(mci);
-       kobject_put(&mci->edac_mci_kobj);
        edac_mc_free(mci);
 }
 
@@ -1562,7 +1555,7 @@ static int __init i5000_init(void)
 {
        int pci_rc;
 
-       debugf2("MC: %s: %s()\n", __FILE__, __func__);
+       edac_dbg(2, "MC:\n");
 
        /* Ensure that the OPSTATE is set correctly for POLL or NMI */
        opstate_init();
@@ -1578,7 +1571,7 @@ static int __init i5000_init(void)
  */
 static void __exit i5000_exit(void)
 {
-       debugf2("MC: %s: %s()\n", __FILE__, __func__);
+       edac_dbg(2, "MC:\n");
        pci_unregister_driver(&i5000_driver);
 }
 
index e9e7c2a29dc389d9462f50e5b1d17367b9a7d194..c4b5e5f868e85ea1e60b0a301d0a7bdc68585a2e 100644 (file)
@@ -431,10 +431,10 @@ static void i5100_handle_ce(struct mem_ctl_info *mci,
                 "bank %u, cas %u, ras %u\n",
                 bank, cas, ras);
 
-       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                             0, 0, syndrome,
                             chan, rank, -1,
-                            msg, detail, NULL);
+                            msg, detail);
 }
 
 static void i5100_handle_ue(struct mem_ctl_info *mci,
@@ -453,10 +453,10 @@ static void i5100_handle_ue(struct mem_ctl_info *mci,
                 "bank %u, cas %u, ras %u\n",
                 bank, cas, ras);
 
-       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                             0, 0, syndrome,
                             chan, rank, -1,
-                            msg, detail, NULL);
+                            msg, detail);
 }
 
 static void i5100_read_log(struct mem_ctl_info *mci, int chan,
@@ -859,8 +859,8 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
                                i5100_rank_to_slot(mci, chan, rank));
                }
 
-               debugf2("dimm channel %d, rank %d, size %ld\n",
-                       chan, rank, (long)PAGES_TO_MiB(npages));
+               edac_dbg(2, "dimm channel %d, rank %d, size %ld\n",
+                        chan, rank, (long)PAGES_TO_MiB(npages));
        }
 }
 
@@ -943,7 +943,7 @@ static int __devinit i5100_init_one(struct pci_dev *pdev,
                goto bail_disable_ch1;
        }
 
-       mci->dev = &pdev->dev;
+       mci->pdev = &pdev->dev;
 
        priv = mci->pvt_info;
        priv->ranksperchan = ranksperch;
index 6640c29e1885a814d8f7e69318541d1afd666037..277246998b805024517f0641841996e8bfd85d29 100644 (file)
@@ -300,24 +300,6 @@ static inline int extract_fbdchan_indx(u32 x)
        return (x>>28) & 0x3;
 }
 
-#ifdef CONFIG_EDAC_DEBUG
-/* MTR NUMROW */
-static const char *numrow_toString[] = {
-       "8,192 - 13 rows",
-       "16,384 - 14 rows",
-       "32,768 - 15 rows",
-       "65,536 - 16 rows"
-};
-
-/* MTR NUMCOL */
-static const char *numcol_toString[] = {
-       "1,024 - 10 columns",
-       "2,048 - 11 columns",
-       "4,096 - 12 columns",
-       "reserved"
-};
-#endif
-
 /* Device name and register DID (Device ID) */
 struct i5400_dev_info {
        const char *ctl_name;   /* name for this device */
@@ -345,7 +327,13 @@ struct i5400_pvt {
        struct pci_dev *branch_1;               /* 22.0 */
 
        u16 tolm;                               /* top of low memory */
-       u64 ambase;                             /* AMB BAR */
+       union {
+               u64 ambase;                             /* AMB BAR */
+               struct {
+                       u32 ambase_bottom;
+                       u32 ambase_top;
+               } u __packed;
+       };
 
        u16 mir0, mir1;
 
@@ -560,10 +548,9 @@ static void i5400_proccess_non_recoverable_info(struct mem_ctl_info *mci,
        ras = nrec_ras(info);
        cas = nrec_cas(info);
 
-       debugf0("\t\tDIMM= %d  Channels= %d,%d  (Branch= %d "
-               "DRAM Bank= %d Buffer ID = %d rdwr= %s ras= %d cas= %d)\n",
-               rank, channel, channel + 1, branch >> 1, bank,
-               buf_id, rdwr_str(rdwr), ras, cas);
+       edac_dbg(0, "\t\tDIMM= %d  Channels= %d,%d  (Branch= %d DRAM Bank= %d Buffer ID = %d rdwr= %s ras= %d cas= %d)\n",
+                rank, channel, channel + 1, branch >> 1, bank,
+                buf_id, rdwr_str(rdwr), ras, cas);
 
        /* Only 1 bit will be on */
        errnum = find_first_bit(&allErrors, ARRAY_SIZE(error_name));
@@ -573,10 +560,10 @@ static void i5400_proccess_non_recoverable_info(struct mem_ctl_info *mci,
                 "Bank=%d Buffer ID = %d RAS=%d CAS=%d Err=0x%lx (%s)",
                 bank, buf_id, ras, cas, allErrors, error_name[errnum]);
 
-       edac_mc_handle_error(tp_event, mci, 0, 0, 0,
+       edac_mc_handle_error(tp_event, mci, 1, 0, 0, 0,
                             branch >> 1, -1, rank,
                             rdwr ? "Write error" : "Read error",
-                            msg, NULL);
+                            msg);
 }
 
 /*
@@ -613,7 +600,7 @@ static void i5400_process_nonfatal_error_info(struct mem_ctl_info *mci,
 
        /* Correctable errors */
        if (allErrors & ERROR_NF_CORRECTABLE) {
-               debugf0("\tCorrected bits= 0x%lx\n", allErrors);
+               edac_dbg(0, "\tCorrected bits= 0x%lx\n", allErrors);
 
                branch = extract_fbdchan_indx(info->ferr_nf_fbd);
 
@@ -634,10 +621,9 @@ static void i5400_process_nonfatal_error_info(struct mem_ctl_info *mci,
                /* Only 1 bit will be on */
                errnum = find_first_bit(&allErrors, ARRAY_SIZE(error_name));
 
-               debugf0("\t\tDIMM= %d Channel= %d  (Branch %d "
-                       "DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
-                       rank, channel, branch >> 1, bank,
-                       rdwr_str(rdwr), ras, cas);
+               edac_dbg(0, "\t\tDIMM= %d Channel= %d  (Branch %d DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
+                        rank, channel, branch >> 1, bank,
+                        rdwr_str(rdwr), ras, cas);
 
                /* Form out message */
                snprintf(msg, sizeof(msg),
@@ -646,10 +632,10 @@ static void i5400_process_nonfatal_error_info(struct mem_ctl_info *mci,
                         branch >> 1, bank, rdwr_str(rdwr), ras, cas,
                         allErrors, error_name[errnum]);
 
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0,
                                     branch >> 1, channel % 2, rank,
                                     rdwr ? "Write error" : "Read error",
-                                    msg, NULL);
+                                    msg);
 
                return;
        }
@@ -700,7 +686,7 @@ static void i5400_clear_error(struct mem_ctl_info *mci)
 static void i5400_check_error(struct mem_ctl_info *mci)
 {
        struct i5400_error_info info;
-       debugf4("MC%d: %s: %s()\n", mci->mc_idx, __FILE__, __func__);
+       edac_dbg(4, "MC%d\n", mci->mc_idx);
        i5400_get_error_info(mci, &info);
        i5400_process_error_info(mci, &info);
 }
@@ -786,15 +772,16 @@ static int i5400_get_devices(struct mem_ctl_info *mci, int dev_idx)
        }
        pvt->fsb_error_regs = pdev;
 
-       debugf1("System Address, processor bus- PCI Bus ID: %s  %x:%x\n",
-               pci_name(pvt->system_address),
-               pvt->system_address->vendor, pvt->system_address->device);
-       debugf1("Branchmap, control and errors - PCI Bus ID: %s  %x:%x\n",
-               pci_name(pvt->branchmap_werrors),
-               pvt->branchmap_werrors->vendor, pvt->branchmap_werrors->device);
-       debugf1("FSB Error Regs - PCI Bus ID: %s  %x:%x\n",
-               pci_name(pvt->fsb_error_regs),
-               pvt->fsb_error_regs->vendor, pvt->fsb_error_regs->device);
+       edac_dbg(1, "System Address, processor bus- PCI Bus ID: %s  %x:%x\n",
+                pci_name(pvt->system_address),
+                pvt->system_address->vendor, pvt->system_address->device);
+       edac_dbg(1, "Branchmap, control and errors - PCI Bus ID: %s  %x:%x\n",
+                pci_name(pvt->branchmap_werrors),
+                pvt->branchmap_werrors->vendor,
+                pvt->branchmap_werrors->device);
+       edac_dbg(1, "FSB Error Regs - PCI Bus ID: %s  %x:%x\n",
+                pci_name(pvt->fsb_error_regs),
+                pvt->fsb_error_regs->vendor, pvt->fsb_error_regs->device);
 
        pvt->branch_0 = pci_get_device(PCI_VENDOR_ID_INTEL,
                                       PCI_DEVICE_ID_INTEL_5400_FBD0, NULL);
@@ -882,8 +869,8 @@ static int determine_mtr(struct i5400_pvt *pvt, int dimm, int channel)
        n = dimm;
 
        if (n >= DIMMS_PER_CHANNEL) {
-               debugf0("ERROR: trying to access an invalid dimm: %d\n",
-                       dimm);
+               edac_dbg(0, "ERROR: trying to access an invalid dimm: %d\n",
+                        dimm);
                return 0;
        }
 
@@ -903,20 +890,29 @@ static void decode_mtr(int slot_row, u16 mtr)
 
        ans = MTR_DIMMS_PRESENT(mtr);
 
-       debugf2("\tMTR%d=0x%x:  DIMMs are %s\n", slot_row, mtr,
-               ans ? "Present" : "NOT Present");
+       edac_dbg(2, "\tMTR%d=0x%x:  DIMMs are %sPresent\n",
+                slot_row, mtr, ans ? "" : "NOT ");
        if (!ans)
                return;
 
-       debugf2("\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
-
-       debugf2("\t\tELECTRICAL THROTTLING is %s\n",
-               MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled");
-
-       debugf2("\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr));
-       debugf2("\t\tNUMRANK: %s\n", MTR_DIMM_RANK(mtr) ? "double" : "single");
-       debugf2("\t\tNUMROW: %s\n", numrow_toString[MTR_DIMM_ROWS(mtr)]);
-       debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]);
+       edac_dbg(2, "\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
+
+       edac_dbg(2, "\t\tELECTRICAL THROTTLING is %s\n",
+                MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled");
+
+       edac_dbg(2, "\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr));
+       edac_dbg(2, "\t\tNUMRANK: %s\n",
+                MTR_DIMM_RANK(mtr) ? "double" : "single");
+       edac_dbg(2, "\t\tNUMROW: %s\n",
+                MTR_DIMM_ROWS(mtr) == 0 ? "8,192 - 13 rows" :
+                MTR_DIMM_ROWS(mtr) == 1 ? "16,384 - 14 rows" :
+                MTR_DIMM_ROWS(mtr) == 2 ? "32,768 - 15 rows" :
+                "65,536 - 16 rows");
+       edac_dbg(2, "\t\tNUMCOL: %s\n",
+                MTR_DIMM_COLS(mtr) == 0 ? "1,024 - 10 columns" :
+                MTR_DIMM_COLS(mtr) == 1 ? "2,048 - 11 columns" :
+                MTR_DIMM_COLS(mtr) == 2 ? "4,096 - 12 columns" :
+                "reserved");
 }
 
 static void handle_channel(struct i5400_pvt *pvt, int dimm, int channel,
@@ -989,7 +985,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
                                        "-------------------------------");
                        p += n;
                        space -= n;
-                       debugf2("%s\n", mem_buffer);
+                       edac_dbg(2, "%s\n", mem_buffer);
                        p = mem_buffer;
                        space = PAGE_SIZE;
                }
@@ -1004,7 +1000,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
                        p += n;
                        space -= n;
                }
-               debugf2("%s\n", mem_buffer);
+               edac_dbg(2, "%s\n", mem_buffer);
                p = mem_buffer;
                space = PAGE_SIZE;
        }
@@ -1014,7 +1010,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
                        "-------------------------------");
        p += n;
        space -= n;
-       debugf2("%s\n", mem_buffer);
+       edac_dbg(2, "%s\n", mem_buffer);
        p = mem_buffer;
        space = PAGE_SIZE;
 
@@ -1029,7 +1025,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
        }
 
        space -= n;
-       debugf2("%s\n", mem_buffer);
+       edac_dbg(2, "%s\n", mem_buffer);
        p = mem_buffer;
        space = PAGE_SIZE;
 
@@ -1042,7 +1038,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
        }
 
        /* output the last message and free buffer */
-       debugf2("%s\n", mem_buffer);
+       edac_dbg(2, "%s\n", mem_buffer);
        kfree(mem_buffer);
 }
 
@@ -1065,25 +1061,25 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
        pvt = mci->pvt_info;
 
        pci_read_config_dword(pvt->system_address, AMBASE,
-                       (u32 *) &pvt->ambase);
+                       &pvt->u.ambase_bottom);
        pci_read_config_dword(pvt->system_address, AMBASE + sizeof(u32),
-                       ((u32 *) &pvt->ambase) + sizeof(u32));
+                       &pvt->u.ambase_top);
 
        maxdimmperch = pvt->maxdimmperch;
        maxch = pvt->maxch;
 
-       debugf2("AMBASE= 0x%lx  MAXCH= %d  MAX-DIMM-Per-CH= %d\n",
-               (long unsigned int)pvt->ambase, pvt->maxch, pvt->maxdimmperch);
+       edac_dbg(2, "AMBASE= 0x%lx  MAXCH= %d  MAX-DIMM-Per-CH= %d\n",
+                (long unsigned int)pvt->ambase, pvt->maxch, pvt->maxdimmperch);
 
        /* Get the Branch Map regs */
        pci_read_config_word(pvt->branchmap_werrors, TOLM, &pvt->tolm);
        pvt->tolm >>= 12;
-       debugf2("\nTOLM (number of 256M regions) =%u (0x%x)\n", pvt->tolm,
-               pvt->tolm);
+       edac_dbg(2, "\nTOLM (number of 256M regions) =%u (0x%x)\n",
+                pvt->tolm, pvt->tolm);
 
        actual_tolm = (u32) ((1000l * pvt->tolm) >> (30 - 28));
-       debugf2("Actual TOLM byte addr=%u.%03u GB (0x%x)\n",
-               actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28);
+       edac_dbg(2, "Actual TOLM byte addr=%u.%03u GB (0x%x)\n",
+                actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28);
 
        pci_read_config_word(pvt->branchmap_werrors, MIR0, &pvt->mir0);
        pci_read_config_word(pvt->branchmap_werrors, MIR1, &pvt->mir1);
@@ -1092,11 +1088,13 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
        limit = (pvt->mir0 >> 4) & 0x0fff;
        way0 = pvt->mir0 & 0x1;
        way1 = pvt->mir0 & 0x2;
-       debugf2("MIR0: limit= 0x%x  WAY1= %u  WAY0= %x\n", limit, way1, way0);
+       edac_dbg(2, "MIR0: limit= 0x%x  WAY1= %u  WAY0= %x\n",
+                limit, way1, way0);
        limit = (pvt->mir1 >> 4) & 0xfff;
        way0 = pvt->mir1 & 0x1;
        way1 = pvt->mir1 & 0x2;
-       debugf2("MIR1: limit= 0x%x  WAY1= %u  WAY0= %x\n", limit, way1, way0);
+       edac_dbg(2, "MIR1: limit= 0x%x  WAY1= %u  WAY0= %x\n",
+                limit, way1, way0);
 
        /* Get the set of MTR[0-3] regs by each branch */
        for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++) {
@@ -1106,8 +1104,8 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
                pci_read_config_word(pvt->branch_0, where,
                                &pvt->b0_mtr[slot_row]);
 
-               debugf2("MTR%d where=0x%x B0 value=0x%x\n", slot_row, where,
-                       pvt->b0_mtr[slot_row]);
+               edac_dbg(2, "MTR%d where=0x%x B0 value=0x%x\n",
+                        slot_row, where, pvt->b0_mtr[slot_row]);
 
                if (pvt->maxch < CHANNELS_PER_BRANCH) {
                        pvt->b1_mtr[slot_row] = 0;
@@ -1117,22 +1115,22 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
                /* Branch 1 set of MTR registers */
                pci_read_config_word(pvt->branch_1, where,
                                &pvt->b1_mtr[slot_row]);
-               debugf2("MTR%d where=0x%x B1 value=0x%x\n", slot_row, where,
-                       pvt->b1_mtr[slot_row]);
+               edac_dbg(2, "MTR%d where=0x%x B1 value=0x%x\n",
+                        slot_row, where, pvt->b1_mtr[slot_row]);
        }
 
        /* Read and dump branch 0's MTRs */
-       debugf2("\nMemory Technology Registers:\n");
-       debugf2("   Branch 0:\n");
+       edac_dbg(2, "Memory Technology Registers:\n");
+       edac_dbg(2, "   Branch 0:\n");
        for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++)
                decode_mtr(slot_row, pvt->b0_mtr[slot_row]);
 
        pci_read_config_word(pvt->branch_0, AMBPRESENT_0,
                        &pvt->b0_ambpresent0);
-       debugf2("\t\tAMB-Branch 0-present0 0x%x:\n", pvt->b0_ambpresent0);
+       edac_dbg(2, "\t\tAMB-Branch 0-present0 0x%x:\n", pvt->b0_ambpresent0);
        pci_read_config_word(pvt->branch_0, AMBPRESENT_1,
                        &pvt->b0_ambpresent1);
-       debugf2("\t\tAMB-Branch 0-present1 0x%x:\n", pvt->b0_ambpresent1);
+       edac_dbg(2, "\t\tAMB-Branch 0-present1 0x%x:\n", pvt->b0_ambpresent1);
 
        /* Only if we have 2 branchs (4 channels) */
        if (pvt->maxch < CHANNELS_PER_BRANCH) {
@@ -1140,18 +1138,18 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
                pvt->b1_ambpresent1 = 0;
        } else {
                /* Read and dump  branch 1's MTRs */
-               debugf2("   Branch 1:\n");
+               edac_dbg(2, "   Branch 1:\n");
                for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++)
                        decode_mtr(slot_row, pvt->b1_mtr[slot_row]);
 
                pci_read_config_word(pvt->branch_1, AMBPRESENT_0,
                                &pvt->b1_ambpresent0);
-               debugf2("\t\tAMB-Branch 1-present0 0x%x:\n",
-                       pvt->b1_ambpresent0);
+               edac_dbg(2, "\t\tAMB-Branch 1-present0 0x%x:\n",
+                        pvt->b1_ambpresent0);
                pci_read_config_word(pvt->branch_1, AMBPRESENT_1,
                                &pvt->b1_ambpresent1);
-               debugf2("\t\tAMB-Branch 1-present1 0x%x:\n",
-                       pvt->b1_ambpresent1);
+               edac_dbg(2, "\t\tAMB-Branch 1-present1 0x%x:\n",
+                        pvt->b1_ambpresent1);
        }
 
        /* Go and determine the size of each DIMM and place in an
@@ -1203,10 +1201,9 @@ static int i5400_init_dimms(struct mem_ctl_info *mci)
 
                        size_mb =  pvt->dimm_info[slot][channel].megabytes;
 
-                       debugf2("%s: dimm%zd (branch %d channel %d slot %d): %d.%03d GB\n",
-                               __func__, dimm - mci->dimms,
-                               channel / 2, channel % 2, slot,
-                               size_mb / 1000, size_mb % 1000);
+                       edac_dbg(2, "dimm (branch %d channel %d slot %d): %d.%03d GB\n",
+                                channel / 2, channel % 2, slot,
+                                size_mb / 1000, size_mb % 1000);
 
                        dimm->nr_pages = size_mb << 8;
                        dimm->grain = 8;
@@ -1227,7 +1224,7 @@ static int i5400_init_dimms(struct mem_ctl_info *mci)
         * With such single-DIMM mode, the SDCC algorithm degrades to SECDEC+.
         */
        if (ndimms == 1)
-               mci->dimms[0].edac_mode = EDAC_SECDED;
+               mci->dimms[0]->edac_mode = EDAC_SECDED;
 
        return (ndimms == 0);
 }
@@ -1270,10 +1267,9 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
        if (dev_idx >= ARRAY_SIZE(i5400_devs))
                return -EINVAL;
 
-       debugf0("MC: %s: %s(), pdev bus %u dev=0x%x fn=0x%x\n",
-               __FILE__, __func__,
-               pdev->bus->number,
-               PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+       edac_dbg(0, "MC: pdev bus %u dev=0x%x fn=0x%x\n",
+                pdev->bus->number,
+                PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
 
        /* We only are looking for func 0 of the set */
        if (PCI_FUNC(pdev->devfn) != 0)
@@ -1297,9 +1293,9 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
        if (mci == NULL)
                return -ENOMEM;
 
-       debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci);
+       edac_dbg(0, "MC: mci = %p\n", mci);
 
-       mci->dev = &pdev->dev;  /* record ptr  to the generic device */
+       mci->pdev = &pdev->dev; /* record ptr  to the generic device */
 
        pvt = mci->pvt_info;
        pvt->system_address = pdev;     /* Record this device in our private */
@@ -1329,19 +1325,16 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
        /* initialize the MC control structure 'dimms' table
         * with the mapping and control information */
        if (i5400_init_dimms(mci)) {
-               debugf0("MC: Setting mci->edac_cap to EDAC_FLAG_NONE\n"
-                       "    because i5400_init_dimms() returned nonzero "
-                       "value\n");
+               edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i5400_init_dimms() returned nonzero value\n");
                mci->edac_cap = EDAC_FLAG_NONE; /* no dimms found */
        } else {
-               debugf1("MC: Enable error reporting now\n");
+               edac_dbg(1, "MC: Enable error reporting now\n");
                i5400_enable_error_reporting(mci);
        }
 
        /* add this new MC control structure to EDAC's list of MCs */
        if (edac_mc_add_mc(mci)) {
-               debugf0("MC: %s: %s(): failed edac_mc_add_mc()\n",
-                       __FILE__, __func__);
+               edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
                /* FIXME: perhaps some code should go here that disables error
                 * reporting if we just enabled it
                 */
@@ -1385,7 +1378,7 @@ static int __devinit i5400_init_one(struct pci_dev *pdev,
 {
        int rc;
 
-       debugf0("MC: %s: %s()\n", __FILE__, __func__);
+       edac_dbg(0, "MC:\n");
 
        /* wake up device */
        rc = pci_enable_device(pdev);
@@ -1404,7 +1397,7 @@ static void __devexit i5400_remove_one(struct pci_dev *pdev)
 {
        struct mem_ctl_info *mci;
 
-       debugf0("%s: %s()\n", __FILE__, __func__);
+       edac_dbg(0, "\n");
 
        if (i5400_pci)
                edac_pci_release_generic_ctl(i5400_pci);
@@ -1450,7 +1443,7 @@ static int __init i5400_init(void)
 {
        int pci_rc;
 
-       debugf2("MC: %s: %s()\n", __FILE__, __func__);
+       edac_dbg(2, "MC:\n");
 
        /* Ensure that the OPSTATE is set correctly for POLL or NMI */
        opstate_init();
@@ -1466,7 +1459,7 @@ static int __init i5400_init(void)
  */
 static void __exit i5400_exit(void)
 {
-       debugf2("MC: %s: %s()\n", __FILE__, __func__);
+       edac_dbg(2, "MC:\n");
        pci_unregister_driver(&i5400_driver);
 }
 
index 97c22fd650eec1953dfba7b3c773c1ddf082d700..a09d0667f72acb4aca4dda012b0d172a927182eb 100644 (file)
@@ -182,24 +182,6 @@ static const u16 mtr_regs[MAX_SLOTS] = {
 #define MTR_DIMM_COLS(mtr)             ((mtr) & 0x3)
 #define MTR_DIMM_COLS_ADDR_BITS(mtr)   (MTR_DIMM_COLS(mtr) + 10)
 
-#ifdef CONFIG_EDAC_DEBUG
-/* MTR NUMROW */
-static const char *numrow_toString[] = {
-       "8,192 - 13 rows",
-       "16,384 - 14 rows",
-       "32,768 - 15 rows",
-       "65,536 - 16 rows"
-};
-
-/* MTR NUMCOL */
-static const char *numcol_toString[] = {
-       "1,024 - 10 columns",
-       "2,048 - 11 columns",
-       "4,096 - 12 columns",
-       "reserved"
-};
-#endif
-
 /************************************************
  * i7300 Register definitions for error detection
  ************************************************/
@@ -467,10 +449,10 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci)
                         "Bank=%d RAS=%d CAS=%d Err=0x%lx (%s))",
                         bank, ras, cas, errors, specific);
 
-               edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 0, 0, 0,
+               edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 1, 0, 0, 0,
                                     branch, -1, rank,
                                     is_wr ? "Write error" : "Read error",
-                                    pvt->tmp_prt_buffer, NULL);
+                                    pvt->tmp_prt_buffer);
 
        }
 
@@ -513,11 +495,11 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci)
                         "DRAM-Bank=%d RAS=%d CAS=%d, Err=0x%lx (%s))",
                         bank, ras, cas, errors, specific);
 
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0,
                                     syndrome,
                                     branch >> 1, channel % 2, rank,
                                     is_wr ? "Write error" : "Read error",
-                                    pvt->tmp_prt_buffer, NULL);
+                                    pvt->tmp_prt_buffer);
        }
        return;
 }
@@ -614,9 +596,8 @@ static int decode_mtr(struct i7300_pvt *pvt,
        mtr = pvt->mtr[slot][branch];
        ans = MTR_DIMMS_PRESENT(mtr) ? 1 : 0;
 
-       debugf2("\tMTR%d CH%d: DIMMs are %s (mtr)\n",
-               slot, channel,
-               ans ? "Present" : "NOT Present");
+       edac_dbg(2, "\tMTR%d CH%d: DIMMs are %sPresent (mtr)\n",
+                slot, channel, ans ? "" : "NOT ");
 
        /* Determine if there is a DIMM present in this DIMM slot */
        if (!ans)
@@ -638,16 +619,25 @@ static int decode_mtr(struct i7300_pvt *pvt,
 
        dinfo->megabytes = 1 << addrBits;
 
-       debugf2("\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
-
-       debugf2("\t\tELECTRICAL THROTTLING is %s\n",
-               MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled");
-
-       debugf2("\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr));
-       debugf2("\t\tNUMRANK: %s\n", MTR_DIMM_RANKS(mtr) ? "double" : "single");
-       debugf2("\t\tNUMROW: %s\n", numrow_toString[MTR_DIMM_ROWS(mtr)]);
-       debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]);
-       debugf2("\t\tSIZE: %d MB\n", dinfo->megabytes);
+       edac_dbg(2, "\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
+
+       edac_dbg(2, "\t\tELECTRICAL THROTTLING is %s\n",
+                MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled");
+
+       edac_dbg(2, "\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr));
+       edac_dbg(2, "\t\tNUMRANK: %s\n",
+                MTR_DIMM_RANKS(mtr) ? "double" : "single");
+       edac_dbg(2, "\t\tNUMROW: %s\n",
+                MTR_DIMM_ROWS(mtr) == 0 ? "8,192 - 13 rows" :
+                MTR_DIMM_ROWS(mtr) == 1 ? "16,384 - 14 rows" :
+                MTR_DIMM_ROWS(mtr) == 2 ? "32,768 - 15 rows" :
+                "65,536 - 16 rows");
+       edac_dbg(2, "\t\tNUMCOL: %s\n",
+                MTR_DIMM_COLS(mtr) == 0 ? "1,024 - 10 columns" :
+                MTR_DIMM_COLS(mtr) == 1 ? "2,048 - 11 columns" :
+                MTR_DIMM_COLS(mtr) == 2 ? "4,096 - 12 columns" :
+                "reserved");
+       edac_dbg(2, "\t\tSIZE: %d MB\n", dinfo->megabytes);
 
        /*
         * The type of error detection actually depends of the
@@ -663,9 +653,9 @@ static int decode_mtr(struct i7300_pvt *pvt,
        dimm->mtype = MEM_FB_DDR2;
        if (IS_SINGLE_MODE(pvt->mc_settings_a)) {
                dimm->edac_mode = EDAC_SECDED;
-               debugf2("\t\tECC code is 8-byte-over-32-byte SECDED+ code\n");
+               edac_dbg(2, "\t\tECC code is 8-byte-over-32-byte SECDED+ code\n");
        } else {
-               debugf2("\t\tECC code is on Lockstep mode\n");
+               edac_dbg(2, "\t\tECC code is on Lockstep mode\n");
                if (MTR_DRAM_WIDTH(mtr) == 8)
                        dimm->edac_mode = EDAC_S8ECD8ED;
                else
@@ -674,9 +664,9 @@ static int decode_mtr(struct i7300_pvt *pvt,
 
        /* ask what device type on this row */
        if (MTR_DRAM_WIDTH(mtr) == 8) {
-               debugf2("\t\tScrub algorithm for x8 is on %s mode\n",
-                       IS_SCRBALGO_ENHANCED(pvt->mc_settings) ?
-                                           "enhanced" : "normal");
+               edac_dbg(2, "\t\tScrub algorithm for x8 is on %s mode\n",
+                        IS_SCRBALGO_ENHANCED(pvt->mc_settings) ?
+                        "enhanced" : "normal");
 
                dimm->dtype = DEV_X8;
        } else
@@ -710,14 +700,14 @@ static void print_dimm_size(struct i7300_pvt *pvt)
                p += n;
                space -= n;
        }
-       debugf2("%s\n", pvt->tmp_prt_buffer);
+       edac_dbg(2, "%s\n", pvt->tmp_prt_buffer);
        p = pvt->tmp_prt_buffer;
        space = PAGE_SIZE;
        n = snprintf(p, space, "-------------------------------"
                               "------------------------------");
        p += n;
        space -= n;
-       debugf2("%s\n", pvt->tmp_prt_buffer);
+       edac_dbg(2, "%s\n", pvt->tmp_prt_buffer);
        p = pvt->tmp_prt_buffer;
        space = PAGE_SIZE;
 
@@ -733,7 +723,7 @@ static void print_dimm_size(struct i7300_pvt *pvt)
                        space -= n;
                }
 
-               debugf2("%s\n", pvt->tmp_prt_buffer);
+               edac_dbg(2, "%s\n", pvt->tmp_prt_buffer);
                p = pvt->tmp_prt_buffer;
                space = PAGE_SIZE;
        }
@@ -742,7 +732,7 @@ static void print_dimm_size(struct i7300_pvt *pvt)
                               "------------------------------");
        p += n;
        space -= n;
-       debugf2("%s\n", pvt->tmp_prt_buffer);
+       edac_dbg(2, "%s\n", pvt->tmp_prt_buffer);
        p = pvt->tmp_prt_buffer;
        space = PAGE_SIZE;
 #endif
@@ -765,7 +755,7 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
 
        pvt = mci->pvt_info;
 
-       debugf2("Memory Technology Registers:\n");
+       edac_dbg(2, "Memory Technology Registers:\n");
 
        /* Get the AMB present registers for the four channels */
        for (branch = 0; branch < MAX_BRANCHES; branch++) {
@@ -774,15 +764,15 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
                pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch],
                                     AMBPRESENT_0,
                                &pvt->ambpresent[channel]);
-               debugf2("\t\tAMB-present CH%d = 0x%x:\n",
-                       channel, pvt->ambpresent[channel]);
+               edac_dbg(2, "\t\tAMB-present CH%d = 0x%x:\n",
+                        channel, pvt->ambpresent[channel]);
 
                channel = to_channel(1, branch);
                pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch],
                                     AMBPRESENT_1,
                                &pvt->ambpresent[channel]);
-               debugf2("\t\tAMB-present CH%d = 0x%x:\n",
-                       channel, pvt->ambpresent[channel]);
+               edac_dbg(2, "\t\tAMB-present CH%d = 0x%x:\n",
+                        channel, pvt->ambpresent[channel]);
        }
 
        /* Get the set of MTR[0-7] regs by each branch */
@@ -824,12 +814,11 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
 static void decode_mir(int mir_no, u16 mir[MAX_MIR])
 {
        if (mir[mir_no] & 3)
-               debugf2("MIR%d: limit= 0x%x Branch(es) that participate:"
-                       " %s %s\n",
-                       mir_no,
-                       (mir[mir_no] >> 4) & 0xfff,
-                       (mir[mir_no] & 1) ? "B0" : "",
-                       (mir[mir_no] & 2) ? "B1" : "");
+               edac_dbg(2, "MIR%d: limit= 0x%x Branch(es) that participate: %s %s\n",
+                        mir_no,
+                        (mir[mir_no] >> 4) & 0xfff,
+                        (mir[mir_no] & 1) ? "B0" : "",
+                        (mir[mir_no] & 2) ? "B1" : "");
 }
 
 /**
@@ -849,17 +838,17 @@ static int i7300_get_mc_regs(struct mem_ctl_info *mci)
        pci_read_config_dword(pvt->pci_dev_16_0_fsb_ctlr, AMBASE,
                        (u32 *) &pvt->ambase);
 
-       debugf2("AMBASE= 0x%lx\n", (long unsigned int)pvt->ambase);
+       edac_dbg(2, "AMBASE= 0x%lx\n", (long unsigned int)pvt->ambase);
 
        /* Get the Branch Map regs */
        pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, TOLM, &pvt->tolm);
        pvt->tolm >>= 12;
-       debugf2("TOLM (number of 256M regions) =%u (0x%x)\n", pvt->tolm,
-               pvt->tolm);
+       edac_dbg(2, "TOLM (number of 256M regions) =%u (0x%x)\n",
+                pvt->tolm, pvt->tolm);
 
        actual_tolm = (u32) ((1000l * pvt->tolm) >> (30 - 28));
-       debugf2("Actual TOLM byte addr=%u.%03u GB (0x%x)\n",
-               actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28);
+       edac_dbg(2, "Actual TOLM byte addr=%u.%03u GB (0x%x)\n",
+                actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28);
 
        /* Get memory controller settings */
        pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, MC_SETTINGS,
@@ -868,15 +857,15 @@ static int i7300_get_mc_regs(struct mem_ctl_info *mci)
                             &pvt->mc_settings_a);
 
        if (IS_SINGLE_MODE(pvt->mc_settings_a))
-               debugf0("Memory controller operating on single mode\n");
+               edac_dbg(0, "Memory controller operating on single mode\n");
        else
-               debugf0("Memory controller operating on %s mode\n",
-               IS_MIRRORED(pvt->mc_settings) ? "mirrored" : "non-mirrored");
+               edac_dbg(0, "Memory controller operating on %smirrored mode\n",
+                        IS_MIRRORED(pvt->mc_settings) ? "" : "non-");
 
-       debugf0("Error detection is %s\n",
-               IS_ECC_ENABLED(pvt->mc_settings) ? "enabled" : "disabled");
-       debugf0("Retry is %s\n",
-               IS_RETRY_ENABLED(pvt->mc_settings) ? "enabled" : "disabled");
+       edac_dbg(0, "Error detection is %s\n",
+                IS_ECC_ENABLED(pvt->mc_settings) ? "enabled" : "disabled");
+       edac_dbg(0, "Retry is %s\n",
+                IS_RETRY_ENABLED(pvt->mc_settings) ? "enabled" : "disabled");
 
        /* Get Memory Interleave Range registers */
        pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR0,
@@ -970,18 +959,18 @@ static int __devinit i7300_get_devices(struct mem_ctl_info *mci)
                }
        }
 
-       debugf1("System Address, processor bus- PCI Bus ID: %s  %x:%x\n",
-               pci_name(pvt->pci_dev_16_0_fsb_ctlr),
-               pvt->pci_dev_16_0_fsb_ctlr->vendor,
-               pvt->pci_dev_16_0_fsb_ctlr->device);
-       debugf1("Branchmap, control and errors - PCI Bus ID: %s  %x:%x\n",
-               pci_name(pvt->pci_dev_16_1_fsb_addr_map),
-               pvt->pci_dev_16_1_fsb_addr_map->vendor,
-               pvt->pci_dev_16_1_fsb_addr_map->device);
-       debugf1("FSB Error Regs - PCI Bus ID: %s  %x:%x\n",
-               pci_name(pvt->pci_dev_16_2_fsb_err_regs),
-               pvt->pci_dev_16_2_fsb_err_regs->vendor,
-               pvt->pci_dev_16_2_fsb_err_regs->device);
+       edac_dbg(1, "System Address, processor bus- PCI Bus ID: %s  %x:%x\n",
+                pci_name(pvt->pci_dev_16_0_fsb_ctlr),
+                pvt->pci_dev_16_0_fsb_ctlr->vendor,
+                pvt->pci_dev_16_0_fsb_ctlr->device);
+       edac_dbg(1, "Branchmap, control and errors - PCI Bus ID: %s  %x:%x\n",
+                pci_name(pvt->pci_dev_16_1_fsb_addr_map),
+                pvt->pci_dev_16_1_fsb_addr_map->vendor,
+                pvt->pci_dev_16_1_fsb_addr_map->device);
+       edac_dbg(1, "FSB Error Regs - PCI Bus ID: %s  %x:%x\n",
+                pci_name(pvt->pci_dev_16_2_fsb_err_regs),
+                pvt->pci_dev_16_2_fsb_err_regs->vendor,
+                pvt->pci_dev_16_2_fsb_err_regs->device);
 
        pvt->pci_dev_2x_0_fbd_branch[0] = pci_get_device(PCI_VENDOR_ID_INTEL,
                                            PCI_DEVICE_ID_INTEL_I7300_MCH_FB0,
@@ -1032,10 +1021,9 @@ static int __devinit i7300_init_one(struct pci_dev *pdev,
        if (rc == -EIO)
                return rc;
 
-       debugf0("MC: " __FILE__ ": %s(), pdev bus %u dev=0x%x fn=0x%x\n",
-               __func__,
-               pdev->bus->number,
-               PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+       edac_dbg(0, "MC: pdev bus %u dev=0x%x fn=0x%x\n",
+                pdev->bus->number,
+                PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
 
        /* We only are looking for func 0 of the set */
        if (PCI_FUNC(pdev->devfn) != 0)
@@ -1055,9 +1043,9 @@ static int __devinit i7300_init_one(struct pci_dev *pdev,
        if (mci == NULL)
                return -ENOMEM;
 
-       debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
+       edac_dbg(0, "MC: mci = %p\n", mci);
 
-       mci->dev = &pdev->dev;  /* record ptr  to the generic device */
+       mci->pdev = &pdev->dev; /* record ptr  to the generic device */
 
        pvt = mci->pvt_info;
        pvt->pci_dev_16_0_fsb_ctlr = pdev;      /* Record this device in our private */
@@ -1088,19 +1076,16 @@ static int __devinit i7300_init_one(struct pci_dev *pdev,
        /* initialize the MC control structure 'csrows' table
         * with the mapping and control information */
        if (i7300_get_mc_regs(mci)) {
-               debugf0("MC: Setting mci->edac_cap to EDAC_FLAG_NONE\n"
-                       "    because i7300_init_csrows() returned nonzero "
-                       "value\n");
+               edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i7300_init_csrows() returned nonzero value\n");
                mci->edac_cap = EDAC_FLAG_NONE; /* no csrows found */
        } else {
-               debugf1("MC: Enable error reporting now\n");
+               edac_dbg(1, "MC: Enable error reporting now\n");
                i7300_enable_error_reporting(mci);
        }
 
        /* add this new MC control structure to EDAC's list of MCs */
        if (edac_mc_add_mc(mci)) {
-               debugf0("MC: " __FILE__
-                       ": %s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
                /* FIXME: perhaps some code should go here that disables error
                 * reporting if we just enabled it
                 */
@@ -1142,7 +1127,7 @@ static void __devexit i7300_remove_one(struct pci_dev *pdev)
        struct mem_ctl_info *mci;
        char *tmp;
 
-       debugf0(__FILE__ ": %s()\n", __func__);
+       edac_dbg(0, "\n");
 
        if (i7300_pci)
                edac_pci_release_generic_ctl(i7300_pci);
@@ -1189,7 +1174,7 @@ static int __init i7300_init(void)
 {
        int pci_rc;
 
-       debugf2("MC: " __FILE__ ": %s()\n", __func__);
+       edac_dbg(2, "\n");
 
        /* Ensure that the OPSTATE is set correctly for POLL or NMI */
        opstate_init();
@@ -1204,7 +1189,7 @@ static int __init i7300_init(void)
  */
 static void __exit i7300_exit(void)
 {
-       debugf2("MC: " __FILE__ ": %s()\n", __func__);
+       edac_dbg(2, "\n");
        pci_unregister_driver(&i7300_driver);
 }
 
index a499c7ed820ae62d8fc489478ef8522e756d833b..3672101023bd8d44c7fb136601a72a0290a9e936 100644 (file)
@@ -248,6 +248,8 @@ struct i7core_dev {
 };
 
 struct i7core_pvt {
+       struct device *addrmatch_dev, *chancounts_dev;
+
        struct pci_dev  *pci_noncore;
        struct pci_dev  *pci_mcr[MAX_MCR_FUNC + 1];
        struct pci_dev  *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
@@ -514,29 +516,28 @@ static int get_dimm_config(struct mem_ctl_info *mci)
        pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
        pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
 
-       debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
-               pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status,
-               pvt->info.max_dod, pvt->info.ch_map);
+       edac_dbg(0, "QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
+                pvt->i7core_dev->socket, pvt->info.mc_control,
+                pvt->info.mc_status, pvt->info.max_dod, pvt->info.ch_map);
 
        if (ECC_ENABLED(pvt)) {
-               debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
+               edac_dbg(0, "ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
                if (ECCx8(pvt))
                        mode = EDAC_S8ECD8ED;
                else
                        mode = EDAC_S4ECD4ED;
        } else {
-               debugf0("ECC disabled\n");
+               edac_dbg(0, "ECC disabled\n");
                mode = EDAC_NONE;
        }
 
        /* FIXME: need to handle the error codes */
-       debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
-               "x%x x 0x%x\n",
-               numdimms(pvt->info.max_dod),
-               numrank(pvt->info.max_dod >> 2),
-               numbank(pvt->info.max_dod >> 4),
-               numrow(pvt->info.max_dod >> 6),
-               numcol(pvt->info.max_dod >> 9));
+       edac_dbg(0, "DOD Max limits: DIMMS: %d, %d-ranked, %d-banked x%x x 0x%x\n",
+                numdimms(pvt->info.max_dod),
+                numrank(pvt->info.max_dod >> 2),
+                numbank(pvt->info.max_dod >> 4),
+                numrow(pvt->info.max_dod >> 6),
+                numcol(pvt->info.max_dod >> 9));
 
        for (i = 0; i < NUM_CHANS; i++) {
                u32 data, dimm_dod[3], value[8];
@@ -545,11 +546,11 @@ static int get_dimm_config(struct mem_ctl_info *mci)
                        continue;
 
                if (!CH_ACTIVE(pvt, i)) {
-                       debugf0("Channel %i is not active\n", i);
+                       edac_dbg(0, "Channel %i is not active\n", i);
                        continue;
                }
                if (CH_DISABLED(pvt, i)) {
-                       debugf0("Channel %i is disabled\n", i);
+                       edac_dbg(0, "Channel %i is disabled\n", i);
                        continue;
                }
 
@@ -580,15 +581,14 @@ static int get_dimm_config(struct mem_ctl_info *mci)
                pci_read_config_dword(pvt->pci_ch[i][1],
                                MC_DOD_CH_DIMM2, &dimm_dod[2]);
 
-               debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
-                       "%s%s%s%cDIMMs\n",
-                       i,
-                       RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
-                       data,
-                       pvt->channel[i].is_3dimms_present ? "3DIMMS " : "",
-                       pvt->channel[i].is_3dimms_present ? "SINGLE_4R " : "",
-                       pvt->channel[i].has_4rank ? "HAS_4R " : "",
-                       (data & REGISTERED_DIMM) ? 'R' : 'U');
+               edac_dbg(0, "Ch%d phy rd%d, wr%d (0x%08x): %s%s%s%cDIMMs\n",
+                        i,
+                        RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
+                        data,
+                        pvt->channel[i].is_3dimms_present ? "3DIMMS " : "",
+                        pvt->channel[i].is_3dimms_present ? "SINGLE_4R " : "",
+                        pvt->channel[i].has_4rank ? "HAS_4R " : "",
+                        (data & REGISTERED_DIMM) ? 'R' : 'U');
 
                for (j = 0; j < 3; j++) {
                        u32 banks, ranks, rows, cols;
@@ -607,11 +607,10 @@ static int get_dimm_config(struct mem_ctl_info *mci)
                        /* DDR3 has 8 I/O banks */
                        size = (rows * cols * banks * ranks) >> (20 - 3);
 
-                       debugf0("\tdimm %d %d Mb offset: %x, "
-                               "bank: %d, rank: %d, row: %#x, col: %#x\n",
-                               j, size,
-                               RANKOFFSET(dimm_dod[j]),
-                               banks, ranks, rows, cols);
+                       edac_dbg(0, "\tdimm %d %d Mb offset: %x, bank: %d, rank: %d, row: %#x, col: %#x\n",
+                                j, size,
+                                RANKOFFSET(dimm_dod[j]),
+                                banks, ranks, rows, cols);
 
                        npages = MiB_TO_PAGES(size);
 
@@ -647,12 +646,12 @@ static int get_dimm_config(struct mem_ctl_info *mci)
                pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
                pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
                pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
-               debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
+               edac_dbg(1, "\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
                for (j = 0; j < 8; j++)
-                       debugf1("\t\t%#x\t%#x\t%#x\n",
-                               (value[j] >> 27) & 0x1,
-                               (value[j] >> 24) & 0x7,
-                               (value[j] & ((1 << 24) - 1)));
+                       edac_dbg(1, "\t\t%#x\t%#x\t%#x\n",
+                                (value[j] >> 27) & 0x1,
+                                (value[j] >> 24) & 0x7,
+                                (value[j] & ((1 << 24) - 1)));
        }
 
        return 0;
@@ -662,6 +661,8 @@ static int get_dimm_config(struct mem_ctl_info *mci)
                        Error insertion routines
  ****************************************************************************/
 
+#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
+
 /* The i7core has independent error injection features per channel.
    However, to have a simpler code, we don't allow enabling error injection
    on more than one channel.
@@ -691,9 +692,11 @@ static int disable_inject(const struct mem_ctl_info *mci)
  *     bit 0 - refers to the lower 32-byte half cacheline
  *     bit 1 - refers to the upper 32-byte half cacheline
  */
-static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
+static ssize_t i7core_inject_section_store(struct device *dev,
+                                          struct device_attribute *mattr,
                                           const char *data, size_t count)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct i7core_pvt *pvt = mci->pvt_info;
        unsigned long value;
        int rc;
@@ -709,9 +712,11 @@ static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
        return count;
 }
 
-static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
-                                             char *data)
+static ssize_t i7core_inject_section_show(struct device *dev,
+                                         struct device_attribute *mattr,
+                                         char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct i7core_pvt *pvt = mci->pvt_info;
        return sprintf(data, "0x%08x\n", pvt->inject.section);
 }
@@ -724,10 +729,12 @@ static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
  *     bit 1 - inject ECC error
  *     bit 2 - inject parity error
  */
-static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
+static ssize_t i7core_inject_type_store(struct device *dev,
+                                       struct device_attribute *mattr,
                                        const char *data, size_t count)
 {
-       struct i7core_pvt *pvt = mci->pvt_info;
+       struct mem_ctl_info *mci = to_mci(dev);
+struct i7core_pvt *pvt = mci->pvt_info;
        unsigned long value;
        int rc;
 
@@ -742,10 +749,13 @@ static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
        return count;
 }
 
-static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
-                                             char *data)
+static ssize_t i7core_inject_type_show(struct device *dev,
+                                      struct device_attribute *mattr,
+                                      char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct i7core_pvt *pvt = mci->pvt_info;
+
        return sprintf(data, "0x%08x\n", pvt->inject.type);
 }
 
@@ -759,9 +769,11 @@ static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
  *   23:16 and 31:24). Flipping bits in two symbol pairs will cause an
  *   uncorrectable error to be injected.
  */
-static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
-                                       const char *data, size_t count)
+static ssize_t i7core_inject_eccmask_store(struct device *dev,
+                                          struct device_attribute *mattr,
+                                          const char *data, size_t count)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct i7core_pvt *pvt = mci->pvt_info;
        unsigned long value;
        int rc;
@@ -777,10 +789,13 @@ static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
        return count;
 }
 
-static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
-                                             char *data)
+static ssize_t i7core_inject_eccmask_show(struct device *dev,
+                                         struct device_attribute *mattr,
+                                         char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct i7core_pvt *pvt = mci->pvt_info;
+
        return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
 }
 
@@ -797,14 +812,16 @@ static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
 
 #define DECLARE_ADDR_MATCH(param, limit)                       \
 static ssize_t i7core_inject_store_##param(                    \
-               struct mem_ctl_info *mci,                       \
-               const char *data, size_t count)                 \
+       struct device *dev,                                     \
+       struct device_attribute *mattr,                         \
+       const char *data, size_t count)                         \
 {                                                              \
+       struct mem_ctl_info *mci = to_mci(dev);                 \
        struct i7core_pvt *pvt;                                 \
        long value;                                             \
        int rc;                                                 \
                                                                \
-       debugf1("%s()\n", __func__);                            \
+       edac_dbg(1, "\n");                                      \
        pvt = mci->pvt_info;                                    \
                                                                \
        if (pvt->inject.enable)                                 \
@@ -824,13 +841,15 @@ static ssize_t i7core_inject_store_##param(                       \
 }                                                              \
                                                                \
 static ssize_t i7core_inject_show_##param(                     \
-               struct mem_ctl_info *mci,                       \
-               char *data)                                     \
+       struct device *dev,                                     \
+       struct device_attribute *mattr,                         \
+       char *data)                                             \
 {                                                              \
+       struct mem_ctl_info *mci = to_mci(dev);                 \
        struct i7core_pvt *pvt;                                 \
                                                                \
        pvt = mci->pvt_info;                                    \
-       debugf1("%s() pvt=%p\n", __func__, pvt);                \
+       edac_dbg(1, "pvt=%p\n", pvt);                           \
        if (pvt->inject.param < 0)                              \
                return sprintf(data, "any\n");                  \
        else                                                    \
@@ -838,14 +857,9 @@ static ssize_t i7core_inject_show_##param(                 \
 }
 
 #define ATTR_ADDR_MATCH(param)                                 \
-       {                                                       \
-               .attr = {                                       \
-                       .name = #param,                         \
-                       .mode = (S_IRUGO | S_IWUSR)             \
-               },                                              \
-               .show  = i7core_inject_show_##param,            \
-               .store = i7core_inject_store_##param,           \
-       }
+       static DEVICE_ATTR(param, S_IRUGO | S_IWUSR,            \
+                   i7core_inject_show_##param,                 \
+                   i7core_inject_store_##param)
 
 DECLARE_ADDR_MATCH(channel, 3);
 DECLARE_ADDR_MATCH(dimm, 3);
@@ -854,14 +868,21 @@ DECLARE_ADDR_MATCH(bank, 32);
 DECLARE_ADDR_MATCH(page, 0x10000);
 DECLARE_ADDR_MATCH(col, 0x4000);
 
+ATTR_ADDR_MATCH(channel);
+ATTR_ADDR_MATCH(dimm);
+ATTR_ADDR_MATCH(rank);
+ATTR_ADDR_MATCH(bank);
+ATTR_ADDR_MATCH(page);
+ATTR_ADDR_MATCH(col);
+
 static int write_and_test(struct pci_dev *dev, const int where, const u32 val)
 {
        u32 read;
        int count;
 
-       debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
-               dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
-               where, val);
+       edac_dbg(0, "setting pci %02x:%02x.%x reg=%02x value=%08x\n",
+                dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
+                where, val);
 
        for (count = 0; count < 10; count++) {
                if (count)
@@ -899,9 +920,11 @@ static int write_and_test(struct pci_dev *dev, const int where, const u32 val)
  *    is reliable enough to check if the MC is using the
  *    three channels. However, this is not clear at the datasheet.
  */
-static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
-                                      const char *data, size_t count)
+static ssize_t i7core_inject_enable_store(struct device *dev,
+                                         struct device_attribute *mattr,
+                                         const char *data, size_t count)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct i7core_pvt *pvt = mci->pvt_info;
        u32 injectmask;
        u64 mask = 0;
@@ -994,17 +1017,18 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
        pci_write_config_dword(pvt->pci_noncore,
                               MC_CFG_CONTROL, 8);
 
-       debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
-               " inject 0x%08x\n",
-               mask, pvt->inject.eccmask, injectmask);
+       edac_dbg(0, "Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n",
+                mask, pvt->inject.eccmask, injectmask);
 
 
        return count;
 }
 
-static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
-                                       char *data)
+static ssize_t i7core_inject_enable_show(struct device *dev,
+                                        struct device_attribute *mattr,
+                                        char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct i7core_pvt *pvt = mci->pvt_info;
        u32 injectmask;
 
@@ -1014,7 +1038,7 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
        pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
                               MC_CHANNEL_ERROR_INJECT, &injectmask);
 
-       debugf0("Inject error read: 0x%018x\n", injectmask);
+       edac_dbg(0, "Inject error read: 0x%018x\n", injectmask);
 
        if (injectmask & 0x0c)
                pvt->inject.enable = 1;
@@ -1024,12 +1048,14 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
 
 #define DECLARE_COUNTER(param)                                 \
 static ssize_t i7core_show_counter_##param(                    \
-               struct mem_ctl_info *mci,                       \
-               char *data)                                     \
+       struct device *dev,                                     \
+       struct device_attribute *mattr,                         \
+       char *data)                                             \
 {                                                              \
+       struct mem_ctl_info *mci = to_mci(dev);                 \
        struct i7core_pvt *pvt = mci->pvt_info;                 \
                                                                \
-       debugf1("%s() \n", __func__);                           \
+       edac_dbg(1, "\n");                                      \
        if (!pvt->ce_count_available || (pvt->is_registered))   \
                return sprintf(data, "data unavailable\n");     \
        return sprintf(data, "%lu\n",                           \
@@ -1037,121 +1063,179 @@ static ssize_t i7core_show_counter_##param(                   \
 }
 
 #define ATTR_COUNTER(param)                                    \
-       {                                                       \
-               .attr = {                                       \
-                       .name = __stringify(udimm##param),      \
-                       .mode = (S_IRUGO | S_IWUSR)             \
-               },                                              \
-               .show  = i7core_show_counter_##param            \
-       }
+       static DEVICE_ATTR(udimm##param, S_IRUGO | S_IWUSR,     \
+                   i7core_show_counter_##param,                \
+                   NULL)
 
 DECLARE_COUNTER(0);
 DECLARE_COUNTER(1);
 DECLARE_COUNTER(2);
 
+ATTR_COUNTER(0);
+ATTR_COUNTER(1);
+ATTR_COUNTER(2);
+
 /*
- * Sysfs struct
+ * inject_addrmatch device sysfs struct
  */
 
-static const struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
-       ATTR_ADDR_MATCH(channel),
-       ATTR_ADDR_MATCH(dimm),
-       ATTR_ADDR_MATCH(rank),
-       ATTR_ADDR_MATCH(bank),
-       ATTR_ADDR_MATCH(page),
-       ATTR_ADDR_MATCH(col),
-       { } /* End of list */
+static struct attribute *i7core_addrmatch_attrs[] = {
+       &dev_attr_channel.attr,
+       &dev_attr_dimm.attr,
+       &dev_attr_rank.attr,
+       &dev_attr_bank.attr,
+       &dev_attr_page.attr,
+       &dev_attr_col.attr,
+       NULL
 };
 
-static const struct mcidev_sysfs_group i7core_inject_addrmatch = {
-       .name  = "inject_addrmatch",
-       .mcidev_attr = i7core_addrmatch_attrs,
+static struct attribute_group addrmatch_grp = {
+       .attrs  = i7core_addrmatch_attrs,
 };
 
-static const struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
-       ATTR_COUNTER(0),
-       ATTR_COUNTER(1),
-       ATTR_COUNTER(2),
-       { .attr = { .name = NULL } }
+static const struct attribute_group *addrmatch_groups[] = {
+       &addrmatch_grp,
+       NULL
 };
 
-static const struct mcidev_sysfs_group i7core_udimm_counters = {
-       .name  = "all_channel_counts",
-       .mcidev_attr = i7core_udimm_counters_attrs,
+static void addrmatch_release(struct device *device)
+{
+       edac_dbg(1, "Releasing device %s\n", dev_name(device));
+       kfree(device);
+}
+
+static struct device_type addrmatch_type = {
+       .groups         = addrmatch_groups,
+       .release        = addrmatch_release,
 };
 
-static const struct mcidev_sysfs_attribute i7core_sysfs_rdimm_attrs[] = {
-       {
-               .attr = {
-                       .name = "inject_section",
-                       .mode = (S_IRUGO | S_IWUSR)
-               },
-               .show  = i7core_inject_section_show,
-               .store = i7core_inject_section_store,
-       }, {
-               .attr = {
-                       .name = "inject_type",
-                       .mode = (S_IRUGO | S_IWUSR)
-               },
-               .show  = i7core_inject_type_show,
-               .store = i7core_inject_type_store,
-       }, {
-               .attr = {
-                       .name = "inject_eccmask",
-                       .mode = (S_IRUGO | S_IWUSR)
-               },
-               .show  = i7core_inject_eccmask_show,
-               .store = i7core_inject_eccmask_store,
-       }, {
-               .grp = &i7core_inject_addrmatch,
-       }, {
-               .attr = {
-                       .name = "inject_enable",
-                       .mode = (S_IRUGO | S_IWUSR)
-               },
-               .show  = i7core_inject_enable_show,
-               .store = i7core_inject_enable_store,
-       },
-       { }     /* End of list */
+/*
+ * all_channel_counts sysfs struct
+ */
+
+static struct attribute *i7core_udimm_counters_attrs[] = {
+       &dev_attr_udimm0.attr,
+       &dev_attr_udimm1.attr,
+       &dev_attr_udimm2.attr,
+       NULL
 };
 
-static const struct mcidev_sysfs_attribute i7core_sysfs_udimm_attrs[] = {
-       {
-               .attr = {
-                       .name = "inject_section",
-                       .mode = (S_IRUGO | S_IWUSR)
-               },
-               .show  = i7core_inject_section_show,
-               .store = i7core_inject_section_store,
-       }, {
-               .attr = {
-                       .name = "inject_type",
-                       .mode = (S_IRUGO | S_IWUSR)
-               },
-               .show  = i7core_inject_type_show,
-               .store = i7core_inject_type_store,
-       }, {
-               .attr = {
-                       .name = "inject_eccmask",
-                       .mode = (S_IRUGO | S_IWUSR)
-               },
-               .show  = i7core_inject_eccmask_show,
-               .store = i7core_inject_eccmask_store,
-       }, {
-               .grp = &i7core_inject_addrmatch,
-       }, {
-               .attr = {
-                       .name = "inject_enable",
-                       .mode = (S_IRUGO | S_IWUSR)
-               },
-               .show  = i7core_inject_enable_show,
-               .store = i7core_inject_enable_store,
-       }, {
-               .grp = &i7core_udimm_counters,
-       },
-       { }     /* End of list */
+static struct attribute_group all_channel_counts_grp = {
+       .attrs  = i7core_udimm_counters_attrs,
 };
 
+static const struct attribute_group *all_channel_counts_groups[] = {
+       &all_channel_counts_grp,
+       NULL
+};
+
+static void all_channel_counts_release(struct device *device)
+{
+       edac_dbg(1, "Releasing device %s\n", dev_name(device));
+       kfree(device);
+}
+
+static struct device_type all_channel_counts_type = {
+       .groups         = all_channel_counts_groups,
+       .release        = all_channel_counts_release,
+};
+
+/*
+ * inject sysfs attributes
+ */
+
+static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR,
+                  i7core_inject_section_show, i7core_inject_section_store);
+
+static DEVICE_ATTR(inject_type, S_IRUGO | S_IWUSR,
+                  i7core_inject_type_show, i7core_inject_type_store);
+
+
+static DEVICE_ATTR(inject_eccmask, S_IRUGO | S_IWUSR,
+                  i7core_inject_eccmask_show, i7core_inject_eccmask_store);
+
+static DEVICE_ATTR(inject_enable, S_IRUGO | S_IWUSR,
+                  i7core_inject_enable_show, i7core_inject_enable_store);
+
+static int i7core_create_sysfs_devices(struct mem_ctl_info *mci)
+{
+       struct i7core_pvt *pvt = mci->pvt_info;
+       int rc;
+
+       rc = device_create_file(&mci->dev, &dev_attr_inject_section);
+       if (rc < 0)
+               return rc;
+       rc = device_create_file(&mci->dev, &dev_attr_inject_type);
+       if (rc < 0)
+               return rc;
+       rc = device_create_file(&mci->dev, &dev_attr_inject_eccmask);
+       if (rc < 0)
+               return rc;
+       rc = device_create_file(&mci->dev, &dev_attr_inject_enable);
+       if (rc < 0)
+               return rc;
+
+       pvt->addrmatch_dev = kzalloc(sizeof(*pvt->addrmatch_dev), GFP_KERNEL);
+       if (!pvt->addrmatch_dev)
+               return rc;
+
+       pvt->addrmatch_dev->type = &addrmatch_type;
+       pvt->addrmatch_dev->bus = mci->dev.bus;
+       device_initialize(pvt->addrmatch_dev);
+       pvt->addrmatch_dev->parent = &mci->dev;
+       dev_set_name(pvt->addrmatch_dev, "inject_addrmatch");
+       dev_set_drvdata(pvt->addrmatch_dev, mci);
+
+       edac_dbg(1, "creating %s\n", dev_name(pvt->addrmatch_dev));
+
+       rc = device_add(pvt->addrmatch_dev);
+       if (rc < 0)
+               return rc;
+
+       if (!pvt->is_registered) {
+               pvt->chancounts_dev = kzalloc(sizeof(*pvt->chancounts_dev),
+                                             GFP_KERNEL);
+               if (!pvt->chancounts_dev) {
+                       put_device(pvt->addrmatch_dev);
+                       device_del(pvt->addrmatch_dev);
+                       return rc;
+               }
+
+               pvt->chancounts_dev->type = &all_channel_counts_type;
+               pvt->chancounts_dev->bus = mci->dev.bus;
+               device_initialize(pvt->chancounts_dev);
+               pvt->chancounts_dev->parent = &mci->dev;
+               dev_set_name(pvt->chancounts_dev, "all_channel_counts");
+               dev_set_drvdata(pvt->chancounts_dev, mci);
+
+               edac_dbg(1, "creating %s\n", dev_name(pvt->chancounts_dev));
+
+               rc = device_add(pvt->chancounts_dev);
+               if (rc < 0)
+                       return rc;
+       }
+       return 0;
+}
+
+static void i7core_delete_sysfs_devices(struct mem_ctl_info *mci)
+{
+       struct i7core_pvt *pvt = mci->pvt_info;
+
+       edac_dbg(1, "\n");
+
+       device_remove_file(&mci->dev, &dev_attr_inject_section);
+       device_remove_file(&mci->dev, &dev_attr_inject_type);
+       device_remove_file(&mci->dev, &dev_attr_inject_eccmask);
+       device_remove_file(&mci->dev, &dev_attr_inject_enable);
+
+       if (!pvt->is_registered) {
+               put_device(pvt->chancounts_dev);
+               device_del(pvt->chancounts_dev);
+       }
+       put_device(pvt->addrmatch_dev);
+       device_del(pvt->addrmatch_dev);
+}
+
 /****************************************************************************
        Device initialization routines: put/get, init/exit
  ****************************************************************************/
@@ -1164,14 +1248,14 @@ static void i7core_put_devices(struct i7core_dev *i7core_dev)
 {
        int i;
 
-       debugf0(__FILE__ ": %s()\n", __func__);
+       edac_dbg(0, "\n");
        for (i = 0; i < i7core_dev->n_devs; i++) {
                struct pci_dev *pdev = i7core_dev->pdev[i];
                if (!pdev)
                        continue;
-               debugf0("Removing dev %02x:%02x.%d\n",
-                       pdev->bus->number,
-                       PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+               edac_dbg(0, "Removing dev %02x:%02x.%d\n",
+                        pdev->bus->number,
+                        PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
                pci_dev_put(pdev);
        }
 }
@@ -1214,12 +1298,12 @@ static unsigned i7core_pci_lastbus(void)
 
        while ((b = pci_find_next_bus(b)) != NULL) {
                bus = b->number;
-               debugf0("Found bus %d\n", bus);
+               edac_dbg(0, "Found bus %d\n", bus);
                if (bus > last_bus)
                        last_bus = bus;
        }
 
-       debugf0("Last bus %d\n", last_bus);
+       edac_dbg(0, "Last bus %d\n", last_bus);
 
        return last_bus;
 }
@@ -1326,10 +1410,10 @@ static int i7core_get_onedevice(struct pci_dev **prev,
                return -ENODEV;
        }
 
-       debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
-               socket, bus, dev_descr->dev,
-               dev_descr->func,
-               PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
+       edac_dbg(0, "Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
+                socket, bus, dev_descr->dev,
+                dev_descr->func,
+                PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
 
        /*
         * As stated on drivers/pci/search.c, the reference count for
@@ -1427,13 +1511,13 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
                                family = "unknown";
                                pvt->enable_scrub = false;
                        }
-                       debugf0("Detected a processor type %s\n", family);
+                       edac_dbg(0, "Detected a processor type %s\n", family);
                } else
                        goto error;
 
-               debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
-                       PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
-                       pdev, i7core_dev->socket);
+               edac_dbg(0, "Associated fn %d.%d, dev = %p, socket %d\n",
+                        PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+                        pdev, i7core_dev->socket);
 
                if (PCI_SLOT(pdev->devfn) == 3 &&
                        PCI_FUNC(pdev->devfn) == 2)
@@ -1452,18 +1536,6 @@ error:
 /****************************************************************************
                        Error check routines
  ****************************************************************************/
-static void i7core_rdimm_update_errcount(struct mem_ctl_info *mci,
-                                     const int chan,
-                                     const int dimm,
-                                     const int add)
-{
-       int i;
-
-       for (i = 0; i < add; i++) {
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0,
-                                    chan, dimm, -1, "error", "", NULL);
-       }
-}
 
 static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
                                         const int chan,
@@ -1502,12 +1574,17 @@ static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
 
        /*updated the edac core */
        if (add0 != 0)
-               i7core_rdimm_update_errcount(mci, chan, 0, add0);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add0,
+                                    0, 0, 0,
+                                    chan, 0, -1, "error", "");
        if (add1 != 0)
-               i7core_rdimm_update_errcount(mci, chan, 1, add1);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add1,
+                                    0, 0, 0,
+                                    chan, 1, -1, "error", "");
        if (add2 != 0)
-               i7core_rdimm_update_errcount(mci, chan, 2, add2);
-
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add2,
+                                    0, 0, 0,
+                                    chan, 2, -1, "error", "");
 }
 
 static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
@@ -1530,8 +1607,8 @@ static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
        pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
                                                                &rcv[2][1]);
        for (i = 0 ; i < 3; i++) {
-               debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
-                       (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
+               edac_dbg(3, "MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
+                        (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
                /*if the channel has 3 dimms*/
                if (pvt->channel[i].dimms > 2) {
                        new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
@@ -1562,7 +1639,7 @@ static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
        int new0, new1, new2;
 
        if (!pvt->pci_mcr[4]) {
-               debugf0("%s MCR registers not found\n", __func__);
+               edac_dbg(0, "MCR registers not found\n");
                return;
        }
 
@@ -1626,7 +1703,7 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci,
                                    const struct mce *m)
 {
        struct i7core_pvt *pvt = mci->pvt_info;
-       char *type, *optype, *err, msg[80];
+       char *type, *optype, *err;
        enum hw_event_mc_err_type tp_event;
        unsigned long error = m->status & 0x1ff0000l;
        bool uncorrected_error = m->mcgstatus & 1ll << 61;
@@ -1704,20 +1781,18 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci,
                err = "unknown";
        }
 
-       snprintf(msg, sizeof(msg), "count=%d %s", core_err_cnt, optype);
-
        /*
         * Call the helper to output message
         * FIXME: what to do if core_err_cnt > 1? Currently, it generates
         * only one event
         */
        if (uncorrected_error || !pvt->is_registered)
-               edac_mc_handle_error(tp_event, mci,
+               edac_mc_handle_error(tp_event, mci, core_err_cnt,
                                     m->addr >> PAGE_SHIFT,
                                     m->addr & ~PAGE_MASK,
                                     syndrome,
                                     channel, dimm, -1,
-                                    err, msg, m);
+                                    err, optype);
 }
 
 /*
@@ -2094,8 +2169,7 @@ static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
        struct i7core_pvt *pvt;
 
        if (unlikely(!mci || !mci->pvt_info)) {
-               debugf0("MC: " __FILE__ ": %s(): dev = %p\n",
-                       __func__, &i7core_dev->pdev[0]->dev);
+               edac_dbg(0, "MC: dev = %p\n", &i7core_dev->pdev[0]->dev);
 
                i7core_printk(KERN_ERR, "Couldn't find mci handler\n");
                return;
@@ -2103,8 +2177,7 @@ static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
 
        pvt = mci->pvt_info;
 
-       debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
-               __func__, mci, &i7core_dev->pdev[0]->dev);
+       edac_dbg(0, "MC: mci = %p, dev = %p\n", mci, &i7core_dev->pdev[0]->dev);
 
        /* Disable scrubrate setting */
        if (pvt->enable_scrub)
@@ -2114,9 +2187,10 @@ static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
        i7core_pci_ctl_release(pvt);
 
        /* Remove MC sysfs nodes */
-       edac_mc_del_mc(mci->dev);
+       i7core_delete_sysfs_devices(mci);
+       edac_mc_del_mc(mci->pdev);
 
-       debugf1("%s: free mci struct\n", mci->ctl_name);
+       edac_dbg(1, "%s: free mci struct\n", mci->ctl_name);
        kfree(mci->ctl_name);
        edac_mc_free(mci);
        i7core_dev->mci = NULL;
@@ -2142,8 +2216,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
        if (unlikely(!mci))
                return -ENOMEM;
 
-       debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
-               __func__, mci, &i7core_dev->pdev[0]->dev);
+       edac_dbg(0, "MC: mci = %p, dev = %p\n", mci, &i7core_dev->pdev[0]->dev);
 
        pvt = mci->pvt_info;
        memset(pvt, 0, sizeof(*pvt));
@@ -2172,15 +2245,11 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
        if (unlikely(rc < 0))
                goto fail0;
 
-       if (pvt->is_registered)
-               mci->mc_driver_sysfs_attributes = i7core_sysfs_rdimm_attrs;
-       else
-               mci->mc_driver_sysfs_attributes = i7core_sysfs_udimm_attrs;
 
        /* Get dimm basic config */
        get_dimm_config(mci);
        /* record ptr to the generic device */
-       mci->dev = &i7core_dev->pdev[0]->dev;
+       mci->pdev = &i7core_dev->pdev[0]->dev;
        /* Set the function pointer to an actual operation function */
        mci->edac_check = i7core_check_error;
 
@@ -2190,8 +2259,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
 
        /* add this new MC control structure to EDAC's list of MCs */
        if (unlikely(edac_mc_add_mc(mci))) {
-               debugf0("MC: " __FILE__
-                       ": %s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
                /* FIXME: perhaps some code should go here that disables error
                 * reporting if we just enabled it
                 */
@@ -2199,6 +2267,12 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
                rc = -EINVAL;
                goto fail0;
        }
+       if (i7core_create_sysfs_devices(mci)) {
+               edac_dbg(0, "MC: failed to create sysfs nodes\n");
+               edac_mc_del_mc(mci->pdev);
+               rc = -EINVAL;
+               goto fail0;
+       }
 
        /* Default error mask is any memory */
        pvt->inject.channel = 0;
@@ -2298,7 +2372,7 @@ static void __devexit i7core_remove(struct pci_dev *pdev)
 {
        struct i7core_dev *i7core_dev;
 
-       debugf0(__FILE__ ": %s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /*
         * we have a trouble here: pdev value for removal will be wrong, since
@@ -2347,7 +2421,7 @@ static int __init i7core_init(void)
 {
        int pci_rc;
 
-       debugf2("MC: " __FILE__ ": %s()\n", __func__);
+       edac_dbg(2, "\n");
 
        /* Ensure that the OPSTATE is set correctly for POLL or NMI */
        opstate_init();
@@ -2374,7 +2448,7 @@ static int __init i7core_init(void)
  */
 static void __exit i7core_exit(void)
 {
-       debugf2("MC: " __FILE__ ": %s()\n", __func__);
+       edac_dbg(2, "\n");
        pci_unregister_driver(&i7core_driver);
        mce_unregister_decode_chain(&i7_mce_dec);
 }
index 52072c28a8a652466f31ed8be165b591098143ca..90f303db5d1dfd0d6ee0b4d77a0e1b899834fa68 100644 (file)
@@ -124,7 +124,7 @@ static void i82443bxgx_edacmc_get_error_info(struct mem_ctl_info *mci,
                                *info)
 {
        struct pci_dev *pdev;
-       pdev = to_pci_dev(mci->dev);
+       pdev = to_pci_dev(mci->pdev);
        pci_read_config_dword(pdev, I82443BXGX_EAP, &info->eap);
        if (info->eap & I82443BXGX_EAP_OFFSET_SBE)
                /* Clear error to allow next error to be reported [p.61] */
@@ -156,19 +156,19 @@ static int i82443bxgx_edacmc_process_error_info(struct mem_ctl_info *mci,
        if (info->eap & I82443BXGX_EAP_OFFSET_SBE) {
                error_found = 1;
                if (handle_errors)
-                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                             page, pageoffset, 0,
                                             edac_mc_find_csrow_by_page(mci, page),
-                                            0, -1, mci->ctl_name, "", NULL);
+                                            0, -1, mci->ctl_name, "");
        }
 
        if (info->eap & I82443BXGX_EAP_OFFSET_MBE) {
                error_found = 1;
                if (handle_errors)
-                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                             page, pageoffset, 0,
                                             edac_mc_find_csrow_by_page(mci, page),
-                                            0, -1, mci->ctl_name, "", NULL);
+                                            0, -1, mci->ctl_name, "");
        }
 
        return error_found;
@@ -178,7 +178,7 @@ static void i82443bxgx_edacmc_check(struct mem_ctl_info *mci)
 {
        struct i82443bxgx_edacmc_error_info info;
 
-       debugf1("MC%d: %s: %s()\n", mci->mc_idx, __FILE__, __func__);
+       edac_dbg(1, "MC%d\n", mci->mc_idx);
        i82443bxgx_edacmc_get_error_info(mci, &info);
        i82443bxgx_edacmc_process_error_info(mci, &info, 1);
 }
@@ -197,18 +197,17 @@ static void i82443bxgx_init_csrows(struct mem_ctl_info *mci,
        pci_read_config_byte(pdev, I82443BXGX_DRAMC, &dramc);
        row_high_limit_last = 0;
        for (index = 0; index < mci->nr_csrows; index++) {
-               csrow = &mci->csrows[index];
-               dimm = csrow->channels[0].dimm;
+               csrow = mci->csrows[index];
+               dimm = csrow->channels[0]->dimm;
 
                pci_read_config_byte(pdev, I82443BXGX_DRB + index, &drbar);
-               debugf1("MC%d: %s: %s() Row=%d DRB = %#0x\n",
-                       mci->mc_idx, __FILE__, __func__, index, drbar);
+               edac_dbg(1, "MC%d: Row=%d DRB = %#0x\n",
+                        mci->mc_idx, index, drbar);
                row_high_limit = ((u32) drbar << 23);
                /* find the DRAM Chip Select Base address and mask */
-               debugf1("MC%d: %s: %s() Row=%d, "
-                       "Boundary Address=%#0x, Last = %#0x\n",
-                       mci->mc_idx, __FILE__, __func__, index, row_high_limit,
-                       row_high_limit_last);
+               edac_dbg(1, "MC%d: Row=%d, Boundary Address=%#0x, Last = %#0x\n",
+                        mci->mc_idx, index, row_high_limit,
+                        row_high_limit_last);
 
                /* 440GX goes to 2GB, represented with a DRB of 0. */
                if (row_high_limit_last && !row_high_limit)
@@ -241,7 +240,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
        enum mem_type mtype;
        enum edac_type edac_mode;
 
-       debugf0("MC: %s: %s()\n", __FILE__, __func__);
+       edac_dbg(0, "MC:\n");
 
        /* Something is really hosed if PCI config space reads from
         * the MC aren't working.
@@ -259,8 +258,8 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
        if (mci == NULL)
                return -ENOMEM;
 
-       debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci);
-       mci->dev = &pdev->dev;
+       edac_dbg(0, "MC: mci = %p\n", mci);
+       mci->pdev = &pdev->dev;
        mci->mtype_cap = MEM_FLAG_EDO | MEM_FLAG_SDR | MEM_FLAG_RDR;
        mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
        pci_read_config_byte(pdev, I82443BXGX_DRAMC, &dramc);
@@ -275,8 +274,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
                mtype = MEM_RDR;
                break;
        default:
-               debugf0("Unknown/reserved DRAM type value "
-                       "in DRAMC register!\n");
+               edac_dbg(0, "Unknown/reserved DRAM type value in DRAMC register!\n");
                mtype = -MEM_UNKNOWN;
        }
 
@@ -305,8 +303,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
                edac_mode = EDAC_SECDED;
                break;
        default:
-               debugf0("%s(): Unknown/reserved ECC state "
-                       "in NBXCFG register!\n", __func__);
+               edac_dbg(0, "Unknown/reserved ECC state in NBXCFG register!\n");
                edac_mode = EDAC_UNKNOWN;
                break;
        }
@@ -330,7 +327,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
        mci->ctl_page_to_phys = NULL;
 
        if (edac_mc_add_mc(mci)) {
-               debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(3, "failed edac_mc_add_mc()\n");
                goto fail;
        }
 
@@ -345,7 +342,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
                        __func__);
        }
 
-       debugf3("MC: %s: %s(): success\n", __FILE__, __func__);
+       edac_dbg(3, "MC: success\n");
        return 0;
 
 fail:
@@ -361,7 +358,7 @@ static int __devinit i82443bxgx_edacmc_init_one(struct pci_dev *pdev,
 {
        int rc;
 
-       debugf0("MC: %s: %s()\n", __FILE__, __func__);
+       edac_dbg(0, "MC:\n");
 
        /* don't need to call pci_enable_device() */
        rc = i82443bxgx_edacmc_probe1(pdev, ent->driver_data);
@@ -376,7 +373,7 @@ static void __devexit i82443bxgx_edacmc_remove_one(struct pci_dev *pdev)
 {
        struct mem_ctl_info *mci;
 
-       debugf0("%s: %s()\n", __FILE__, __func__);
+       edac_dbg(0, "\n");
 
        if (i82443bxgx_pci)
                edac_pci_release_generic_ctl(i82443bxgx_pci);
@@ -428,7 +425,7 @@ static int __init i82443bxgx_edacmc_init(void)
                        id = &i82443bxgx_pci_tbl[i];
                }
                if (!mci_pdev) {
-                       debugf0("i82443bxgx pci_get_device fail\n");
+                       edac_dbg(0, "i82443bxgx pci_get_device fail\n");
                        pci_rc = -ENODEV;
                        goto fail1;
                }
@@ -436,7 +433,7 @@ static int __init i82443bxgx_edacmc_init(void)
                pci_rc = i82443bxgx_edacmc_init_one(mci_pdev, i82443bxgx_pci_tbl);
 
                if (pci_rc < 0) {
-                       debugf0("i82443bxgx init fail\n");
+                       edac_dbg(0, "i82443bxgx init fail\n");
                        pci_rc = -ENODEV;
                        goto fail1;
                }
index 08045059d10bcc64a06318c9fcdd7826e7ea83fc..1faa749715131c1e19b34134d6c01626fb34dd7a 100644 (file)
@@ -67,7 +67,7 @@ static void i82860_get_error_info(struct mem_ctl_info *mci,
 {
        struct pci_dev *pdev;
 
-       pdev = to_pci_dev(mci->dev);
+       pdev = to_pci_dev(mci->pdev);
 
        /*
         * This is a mess because there is no atomic way to read all the
@@ -109,25 +109,25 @@ static int i82860_process_error_info(struct mem_ctl_info *mci,
                return 1;
 
        if ((info->errsts ^ info->errsts2) & 0x0003) {
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
-                                    -1, -1, -1, "UE overwrote CE", "", NULL);
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
+                                    -1, -1, -1, "UE overwrote CE", "");
                info->errsts = info->errsts2;
        }
 
        info->eap >>= PAGE_SHIFT;
        row = edac_mc_find_csrow_by_page(mci, info->eap);
-       dimm = mci->csrows[row].channels[0].dimm;
+       dimm = mci->csrows[row]->channels[0]->dimm;
 
        if (info->errsts & 0x0002)
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                     info->eap, 0, 0,
                                     dimm->location[0], dimm->location[1], -1,
-                                    "i82860 UE", "", NULL);
+                                    "i82860 UE", "");
        else
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                     info->eap, 0, info->derrsyn,
                                     dimm->location[0], dimm->location[1], -1,
-                                    "i82860 CE", "", NULL);
+                                    "i82860 CE", "");
 
        return 1;
 }
@@ -136,7 +136,7 @@ static void i82860_check(struct mem_ctl_info *mci)
 {
        struct i82860_error_info info;
 
-       debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
+       edac_dbg(1, "MC%d\n", mci->mc_idx);
        i82860_get_error_info(mci, &info);
        i82860_process_error_info(mci, &info, 1);
 }
@@ -161,14 +161,13 @@ static void i82860_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev)
         * in all eight rows.
         */
        for (index = 0; index < mci->nr_csrows; index++) {
-               csrow = &mci->csrows[index];
-               dimm = csrow->channels[0].dimm;
+               csrow = mci->csrows[index];
+               dimm = csrow->channels[0]->dimm;
 
                pci_read_config_word(pdev, I82860_GBA + index * 2, &value);
                cumul_size = (value & I82860_GBA_MASK) <<
                        (I82860_GBA_SHIFT - PAGE_SHIFT);
-               debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
-                       cumul_size);
+               edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size);
 
                if (cumul_size == last_cumul_size)
                        continue;       /* not populated */
@@ -210,8 +209,8 @@ static int i82860_probe1(struct pci_dev *pdev, int dev_idx)
        if (!mci)
                return -ENOMEM;
 
-       debugf3("%s(): init mci\n", __func__);
-       mci->dev = &pdev->dev;
+       edac_dbg(3, "init mci\n");
+       mci->pdev = &pdev->dev;
        mci->mtype_cap = MEM_FLAG_DDR;
        mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
        /* I"m not sure about this but I think that all RDRAM is SECDED */
@@ -229,7 +228,7 @@ static int i82860_probe1(struct pci_dev *pdev, int dev_idx)
         * type of memory controller.  The ID is therefore hardcoded to 0.
         */
        if (edac_mc_add_mc(mci)) {
-               debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(3, "failed edac_mc_add_mc()\n");
                goto fail;
        }
 
@@ -245,7 +244,7 @@ static int i82860_probe1(struct pci_dev *pdev, int dev_idx)
        }
 
        /* get this far and it's successful */
-       debugf3("%s(): success\n", __func__);
+       edac_dbg(3, "success\n");
 
        return 0;
 
@@ -260,7 +259,7 @@ static int __devinit i82860_init_one(struct pci_dev *pdev,
 {
        int rc;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
        i82860_printk(KERN_INFO, "i82860 init one\n");
 
        if (pci_enable_device(pdev) < 0)
@@ -278,7 +277,7 @@ static void __devexit i82860_remove_one(struct pci_dev *pdev)
 {
        struct mem_ctl_info *mci;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        if (i82860_pci)
                edac_pci_release_generic_ctl(i82860_pci);
@@ -311,7 +310,7 @@ static int __init i82860_init(void)
 {
        int pci_rc;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        /* Ensure that the OPSTATE is set correctly for POLL or NMI */
        opstate_init();
@@ -324,7 +323,7 @@ static int __init i82860_init(void)
                                        PCI_DEVICE_ID_INTEL_82860_0, NULL);
 
                if (mci_pdev == NULL) {
-                       debugf0("860 pci_get_device fail\n");
+                       edac_dbg(0, "860 pci_get_device fail\n");
                        pci_rc = -ENODEV;
                        goto fail1;
                }
@@ -332,7 +331,7 @@ static int __init i82860_init(void)
                pci_rc = i82860_init_one(mci_pdev, i82860_pci_tbl);
 
                if (pci_rc < 0) {
-                       debugf0("860 init fail\n");
+                       edac_dbg(0, "860 init fail\n");
                        pci_rc = -ENODEV;
                        goto fail1;
                }
@@ -352,7 +351,7 @@ fail0:
 
 static void __exit i82860_exit(void)
 {
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        pci_unregister_driver(&i82860_driver);
 
index b613e31c16e5de18f47a8a335690cfc4c487a752..3e416b1a6b53680b3f5d78f3a579fb04439caa62 100644 (file)
@@ -189,7 +189,7 @@ static void i82875p_get_error_info(struct mem_ctl_info *mci,
 {
        struct pci_dev *pdev;
 
-       pdev = to_pci_dev(mci->dev);
+       pdev = to_pci_dev(mci->pdev);
 
        /*
         * This is a mess because there is no atomic way to read all the
@@ -227,7 +227,7 @@ static int i82875p_process_error_info(struct mem_ctl_info *mci,
 {
        int row, multi_chan;
 
-       multi_chan = mci->csrows[0].nr_channels - 1;
+       multi_chan = mci->csrows[0]->nr_channels - 1;
 
        if (!(info->errsts & 0x0081))
                return 0;
@@ -236,9 +236,9 @@ static int i82875p_process_error_info(struct mem_ctl_info *mci,
                return 1;
 
        if ((info->errsts ^ info->errsts2) & 0x0081) {
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
                                     -1, -1, -1,
-                                    "UE overwrote CE", "", NULL);
+                                    "UE overwrote CE", "");
                info->errsts = info->errsts2;
        }
 
@@ -246,15 +246,15 @@ static int i82875p_process_error_info(struct mem_ctl_info *mci,
        row = edac_mc_find_csrow_by_page(mci, info->eap);
 
        if (info->errsts & 0x0080)
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                     info->eap, 0, 0,
                                     row, -1, -1,
-                                    "i82875p UE", "", NULL);
+                                    "i82875p UE", "");
        else
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                     info->eap, 0, info->derrsyn,
                                     row, multi_chan ? (info->des & 0x1) : 0,
-                                    -1, "i82875p CE", "", NULL);
+                                    -1, "i82875p CE", "");
 
        return 1;
 }
@@ -263,7 +263,7 @@ static void i82875p_check(struct mem_ctl_info *mci)
 {
        struct i82875p_error_info info;
 
-       debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
+       edac_dbg(1, "MC%d\n", mci->mc_idx);
        i82875p_get_error_info(mci, &info);
        i82875p_process_error_info(mci, &info, 1);
 }
@@ -367,12 +367,11 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci,
         */
 
        for (index = 0; index < mci->nr_csrows; index++) {
-               csrow = &mci->csrows[index];
+               csrow = mci->csrows[index];
 
                value = readb(ovrfl_window + I82875P_DRB + index);
                cumul_size = value << (I82875P_DRB_SHIFT - PAGE_SHIFT);
-               debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
-                       cumul_size);
+               edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size);
                if (cumul_size == last_cumul_size)
                        continue;       /* not populated */
 
@@ -382,7 +381,7 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci,
                last_cumul_size = cumul_size;
 
                for (j = 0; j < nr_chans; j++) {
-                       dimm = csrow->channels[j].dimm;
+                       dimm = csrow->channels[j]->dimm;
 
                        dimm->nr_pages = nr_pages / nr_chans;
                        dimm->grain = 1 << 12;  /* I82875P_EAP has 4KiB reolution */
@@ -405,7 +404,7 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx)
        u32 nr_chans;
        struct i82875p_error_info discard;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        ovrfl_pdev = pci_get_device(PCI_VEND_DEV(INTEL, 82875_6), NULL);
 
@@ -426,11 +425,8 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx)
                goto fail0;
        }
 
-       /* Keeps mci available after edac_mc_del_mc() till edac_mc_free() */
-       kobject_get(&mci->edac_mci_kobj);
-
-       debugf3("%s(): init mci\n", __func__);
-       mci->dev = &pdev->dev;
+       edac_dbg(3, "init mci\n");
+       mci->pdev = &pdev->dev;
        mci->mtype_cap = MEM_FLAG_DDR;
        mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
        mci->edac_cap = EDAC_FLAG_UNKNOWN;
@@ -440,7 +436,7 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx)
        mci->dev_name = pci_name(pdev);
        mci->edac_check = i82875p_check;
        mci->ctl_page_to_phys = NULL;
-       debugf3("%s(): init pvt\n", __func__);
+       edac_dbg(3, "init pvt\n");
        pvt = (struct i82875p_pvt *)mci->pvt_info;
        pvt->ovrfl_pdev = ovrfl_pdev;
        pvt->ovrfl_window = ovrfl_window;
@@ -451,7 +447,7 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx)
         * type of memory controller.  The ID is therefore hardcoded to 0.
         */
        if (edac_mc_add_mc(mci)) {
-               debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(3, "failed edac_mc_add_mc()\n");
                goto fail1;
        }
 
@@ -467,11 +463,10 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx)
        }
 
        /* get this far and it's successful */
-       debugf3("%s(): success\n", __func__);
+       edac_dbg(3, "success\n");
        return 0;
 
 fail1:
-       kobject_put(&mci->edac_mci_kobj);
        edac_mc_free(mci);
 
 fail0:
@@ -489,7 +484,7 @@ static int __devinit i82875p_init_one(struct pci_dev *pdev,
 {
        int rc;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
        i82875p_printk(KERN_INFO, "i82875p init one\n");
 
        if (pci_enable_device(pdev) < 0)
@@ -508,7 +503,7 @@ static void __devexit i82875p_remove_one(struct pci_dev *pdev)
        struct mem_ctl_info *mci;
        struct i82875p_pvt *pvt = NULL;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        if (i82875p_pci)
                edac_pci_release_generic_ctl(i82875p_pci);
@@ -554,7 +549,7 @@ static int __init i82875p_init(void)
 {
        int pci_rc;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        /* Ensure that the OPSTATE is set correctly for POLL or NMI */
        opstate_init();
@@ -569,7 +564,7 @@ static int __init i82875p_init(void)
                                        PCI_DEVICE_ID_INTEL_82875_0, NULL);
 
                if (!mci_pdev) {
-                       debugf0("875p pci_get_device fail\n");
+                       edac_dbg(0, "875p pci_get_device fail\n");
                        pci_rc = -ENODEV;
                        goto fail1;
                }
@@ -577,7 +572,7 @@ static int __init i82875p_init(void)
                pci_rc = i82875p_init_one(mci_pdev, i82875p_pci_tbl);
 
                if (pci_rc < 0) {
-                       debugf0("875p init fail\n");
+                       edac_dbg(0, "875p init fail\n");
                        pci_rc = -ENODEV;
                        goto fail1;
                }
@@ -597,7 +592,7 @@ fail0:
 
 static void __exit i82875p_exit(void)
 {
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        i82875p_remove_one(mci_pdev);
        pci_dev_put(mci_pdev);
index 433332c7cdbabe3bf54fbce0e9622403af29869c..069e26c11c4f761997bbf2afb6b9bf1bf2d6b039 100644 (file)
@@ -241,7 +241,7 @@ static void i82975x_get_error_info(struct mem_ctl_info *mci,
 {
        struct pci_dev *pdev;
 
-       pdev = to_pci_dev(mci->dev);
+       pdev = to_pci_dev(mci->pdev);
 
        /*
         * This is a mess because there is no atomic way to read all the
@@ -288,8 +288,8 @@ static int i82975x_process_error_info(struct mem_ctl_info *mci,
                return 1;
 
        if ((info->errsts ^ info->errsts2) & 0x0003) {
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
-                                    -1, -1, -1, "UE overwrote CE", "", NULL);
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
+                                    -1, -1, -1, "UE overwrote CE", "");
                info->errsts = info->errsts2;
        }
 
@@ -308,21 +308,21 @@ static int i82975x_process_error_info(struct mem_ctl_info *mci,
                        (info->xeap & 1) ? 1 : 0, info->eap, (unsigned int) page);
                return 0;
        }
-       chan = (mci->csrows[row].nr_channels == 1) ? 0 : info->eap & 1;
+       chan = (mci->csrows[row]->nr_channels == 1) ? 0 : info->eap & 1;
        offst = info->eap
                        & ((1 << PAGE_SHIFT) -
-                          (1 << mci->csrows[row].channels[chan].dimm->grain));
+                          (1 << mci->csrows[row]->channels[chan]->dimm->grain));
 
        if (info->errsts & 0x0002)
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                     page, offst, 0,
                                     row, -1, -1,
-                                    "i82975x UE", "", NULL);
+                                    "i82975x UE", "");
        else
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                     page, offst, info->derrsyn,
                                     row, chan ? chan : 0, -1,
-                                    "i82975x CE", "", NULL);
+                                    "i82975x CE", "");
 
        return 1;
 }
@@ -331,7 +331,7 @@ static void i82975x_check(struct mem_ctl_info *mci)
 {
        struct i82975x_error_info info;
 
-       debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
+       edac_dbg(1, "MC%d\n", mci->mc_idx);
        i82975x_get_error_info(mci, &info);
        i82975x_process_error_info(mci, &info, 1);
 }
@@ -394,7 +394,7 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
         */
 
        for (index = 0; index < mci->nr_csrows; index++) {
-               csrow = &mci->csrows[index];
+               csrow = mci->csrows[index];
 
                value = readb(mch_window + I82975X_DRB + index +
                                        ((index >= 4) ? 0x80 : 0));
@@ -406,8 +406,7 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
                 */
                if (csrow->nr_channels > 1)
                        cumul_size <<= 1;
-               debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
-                       cumul_size);
+               edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size);
 
                nr_pages = cumul_size - last_cumul_size;
                if (!nr_pages)
@@ -421,10 +420,10 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
                 */
                dtype = i82975x_dram_type(mch_window, index);
                for (chan = 0; chan < csrow->nr_channels; chan++) {
-                       dimm = mci->csrows[index].channels[chan].dimm;
+                       dimm = mci->csrows[index]->channels[chan]->dimm;
 
                        dimm->nr_pages = nr_pages / csrow->nr_channels;
-                       strncpy(csrow->channels[chan].dimm->label,
+                       strncpy(csrow->channels[chan]->dimm->label,
                                        labels[(index >> 1) + (chan * 2)],
                                        EDAC_MC_LABEL_LEN);
                        dimm->grain = 1 << 7;   /* 128Byte cache-line resolution */
@@ -489,11 +488,11 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx)
        u8 c1drb[4];
 #endif
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        pci_read_config_dword(pdev, I82975X_MCHBAR, &mchbar);
        if (!(mchbar & 1)) {
-               debugf3("%s(): failed, MCHBAR disabled!\n", __func__);
+               edac_dbg(3, "failed, MCHBAR disabled!\n");
                goto fail0;
        }
        mchbar &= 0xffffc000;   /* bits 31:14 used for 16K window */
@@ -558,8 +557,8 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx)
                goto fail1;
        }
 
-       debugf3("%s(): init mci\n", __func__);
-       mci->dev = &pdev->dev;
+       edac_dbg(3, "init mci\n");
+       mci->pdev = &pdev->dev;
        mci->mtype_cap = MEM_FLAG_DDR2;
        mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
        mci->edac_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
@@ -569,7 +568,7 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx)
        mci->dev_name = pci_name(pdev);
        mci->edac_check = i82975x_check;
        mci->ctl_page_to_phys = NULL;
-       debugf3("%s(): init pvt\n", __func__);
+       edac_dbg(3, "init pvt\n");
        pvt = (struct i82975x_pvt *) mci->pvt_info;
        pvt->mch_window = mch_window;
        i82975x_init_csrows(mci, pdev, mch_window);
@@ -578,12 +577,12 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx)
 
        /* finalize this instance of memory controller with edac core */
        if (edac_mc_add_mc(mci)) {
-               debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(3, "failed edac_mc_add_mc()\n");
                goto fail2;
        }
 
        /* get this far and it's successful */
-       debugf3("%s(): success\n", __func__);
+       edac_dbg(3, "success\n");
        return 0;
 
 fail2:
@@ -601,7 +600,7 @@ static int __devinit i82975x_init_one(struct pci_dev *pdev,
 {
        int rc;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        if (pci_enable_device(pdev) < 0)
                return -EIO;
@@ -619,7 +618,7 @@ static void __devexit i82975x_remove_one(struct pci_dev *pdev)
        struct mem_ctl_info *mci;
        struct i82975x_pvt *pvt;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        mci = edac_mc_del_mc(&pdev->dev);
        if (mci  == NULL)
@@ -655,7 +654,7 @@ static int __init i82975x_init(void)
 {
        int pci_rc;
 
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        /* Ensure that the OPSTATE is set correctly for POLL or NMI */
        opstate_init();
@@ -669,7 +668,7 @@ static int __init i82975x_init(void)
                                PCI_DEVICE_ID_INTEL_82975_0, NULL);
 
                if (!mci_pdev) {
-                       debugf0("i82975x pci_get_device fail\n");
+                       edac_dbg(0, "i82975x pci_get_device fail\n");
                        pci_rc = -ENODEV;
                        goto fail1;
                }
@@ -677,7 +676,7 @@ static int __init i82975x_init(void)
                pci_rc = i82975x_init_one(mci_pdev, i82975x_pci_tbl);
 
                if (pci_rc < 0) {
-                       debugf0("i82975x init fail\n");
+                       edac_dbg(0, "i82975x init fail\n");
                        pci_rc = -ENODEV;
                        goto fail1;
                }
@@ -697,7 +696,7 @@ fail0:
 
 static void __exit i82975x_exit(void)
 {
-       debugf3("%s()\n", __func__);
+       edac_dbg(3, "\n");
 
        pci_unregister_driver(&i82975x_driver);
 
index 0e374625f6f894a20272df947ffe2f3b7620422f..a1e791ec25d38514b7c47ae27bd2cab0198ded68 100644 (file)
@@ -49,34 +49,45 @@ static u32 orig_hid1[2];
 
 /************************ MC SYSFS parts ***********************************/
 
-static ssize_t mpc85xx_mc_inject_data_hi_show(struct mem_ctl_info *mci,
+#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
+
+static ssize_t mpc85xx_mc_inject_data_hi_show(struct device *dev,
+                                             struct device_attribute *mattr,
                                              char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
        return sprintf(data, "0x%08x",
                       in_be32(pdata->mc_vbase +
                               MPC85XX_MC_DATA_ERR_INJECT_HI));
 }
 
-static ssize_t mpc85xx_mc_inject_data_lo_show(struct mem_ctl_info *mci,
+static ssize_t mpc85xx_mc_inject_data_lo_show(struct device *dev,
+                                             struct device_attribute *mattr,
                                              char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
        return sprintf(data, "0x%08x",
                       in_be32(pdata->mc_vbase +
                               MPC85XX_MC_DATA_ERR_INJECT_LO));
 }
 
-static ssize_t mpc85xx_mc_inject_ctrl_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mpc85xx_mc_inject_ctrl_show(struct device *dev,
+                                          struct device_attribute *mattr,
+                                          char *data)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
        return sprintf(data, "0x%08x",
                       in_be32(pdata->mc_vbase + MPC85XX_MC_ECC_ERR_INJECT));
 }
 
-static ssize_t mpc85xx_mc_inject_data_hi_store(struct mem_ctl_info *mci,
+static ssize_t mpc85xx_mc_inject_data_hi_store(struct device *dev,
+                                              struct device_attribute *mattr,
                                               const char *data, size_t count)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
        if (isdigit(*data)) {
                out_be32(pdata->mc_vbase + MPC85XX_MC_DATA_ERR_INJECT_HI,
@@ -86,9 +97,11 @@ static ssize_t mpc85xx_mc_inject_data_hi_store(struct mem_ctl_info *mci,
        return 0;
 }
 
-static ssize_t mpc85xx_mc_inject_data_lo_store(struct mem_ctl_info *mci,
+static ssize_t mpc85xx_mc_inject_data_lo_store(struct device *dev,
+                                              struct device_attribute *mattr,
                                               const char *data, size_t count)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
        if (isdigit(*data)) {
                out_be32(pdata->mc_vbase + MPC85XX_MC_DATA_ERR_INJECT_LO,
@@ -98,9 +111,11 @@ static ssize_t mpc85xx_mc_inject_data_lo_store(struct mem_ctl_info *mci,
        return 0;
 }
 
-static ssize_t mpc85xx_mc_inject_ctrl_store(struct mem_ctl_info *mci,
-                                           const char *data, size_t count)
+static ssize_t mpc85xx_mc_inject_ctrl_store(struct device *dev,
+                                              struct device_attribute *mattr,
+                                              const char *data, size_t count)
 {
+       struct mem_ctl_info *mci = to_mci(dev);
        struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
        if (isdigit(*data)) {
                out_be32(pdata->mc_vbase + MPC85XX_MC_ECC_ERR_INJECT,
@@ -110,38 +125,35 @@ static ssize_t mpc85xx_mc_inject_ctrl_store(struct mem_ctl_info *mci,
        return 0;
 }
 
-static struct mcidev_sysfs_attribute mpc85xx_mc_sysfs_attributes[] = {
-       {
-        .attr = {
-                 .name = "inject_data_hi",
-                 .mode = (S_IRUGO | S_IWUSR)
-                 },
-        .show = mpc85xx_mc_inject_data_hi_show,
-        .store = mpc85xx_mc_inject_data_hi_store},
-       {
-        .attr = {
-                 .name = "inject_data_lo",
-                 .mode = (S_IRUGO | S_IWUSR)
-                 },
-        .show = mpc85xx_mc_inject_data_lo_show,
-        .store = mpc85xx_mc_inject_data_lo_store},
-       {
-        .attr = {
-                 .name = "inject_ctrl",
-                 .mode = (S_IRUGO | S_IWUSR)
-                 },
-        .show = mpc85xx_mc_inject_ctrl_show,
-        .store = mpc85xx_mc_inject_ctrl_store},
+DEVICE_ATTR(inject_data_hi, S_IRUGO | S_IWUSR,
+           mpc85xx_mc_inject_data_hi_show, mpc85xx_mc_inject_data_hi_store);
+DEVICE_ATTR(inject_data_lo, S_IRUGO | S_IWUSR,
+           mpc85xx_mc_inject_data_lo_show, mpc85xx_mc_inject_data_lo_store);
+DEVICE_ATTR(inject_ctrl, S_IRUGO | S_IWUSR,
+           mpc85xx_mc_inject_ctrl_show, mpc85xx_mc_inject_ctrl_store);
 
-       /* End of list */
-       {
-        .attr = {.name = NULL}
-        }
-};
+static int mpc85xx_create_sysfs_attributes(struct mem_ctl_info *mci)
+{
+       int rc;
+
+       rc = device_create_file(&mci->dev, &dev_attr_inject_data_hi);
+       if (rc < 0)
+               return rc;
+       rc = device_create_file(&mci->dev, &dev_attr_inject_data_lo);
+       if (rc < 0)
+               return rc;
+       rc = device_create_file(&mci->dev, &dev_attr_inject_ctrl);
+       if (rc < 0)
+               return rc;
 
-static void mpc85xx_set_mc_sysfs_attributes(struct mem_ctl_info *mci)
+       return 0;
+}
+
+static void mpc85xx_remove_sysfs_attributes(struct mem_ctl_info *mci)
 {
-       mci->mc_driver_sysfs_attributes = mpc85xx_mc_sysfs_attributes;
+       device_remove_file(&mci->dev, &dev_attr_inject_data_hi);
+       device_remove_file(&mci->dev, &dev_attr_inject_data_lo);
+       device_remove_file(&mci->dev, &dev_attr_inject_ctrl);
 }
 
 /**************************** PCI Err device ***************************/
@@ -268,7 +280,7 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *op)
        out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, ~0);
 
        if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
-               debugf3("%s(): failed edac_pci_add_device()\n", __func__);
+               edac_dbg(3, "failed edac_pci_add_device()\n");
                goto err;
        }
 
@@ -291,7 +303,7 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *op)
        }
 
        devres_remove_group(&op->dev, mpc85xx_pci_err_probe);
-       debugf3("%s(): success\n", __func__);
+       edac_dbg(3, "success\n");
        printk(KERN_INFO EDAC_MOD_STR " PCI err registered\n");
 
        return 0;
@@ -309,7 +321,7 @@ static int mpc85xx_pci_err_remove(struct platform_device *op)
        struct edac_pci_ctl_info *pci = dev_get_drvdata(&op->dev);
        struct mpc85xx_pci_pdata *pdata = pci->pvt_info;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR,
                 orig_pci_err_cap_dr);
@@ -570,7 +582,7 @@ static int __devinit mpc85xx_l2_err_probe(struct platform_device *op)
        pdata->edac_idx = edac_dev_idx++;
 
        if (edac_device_add_device(edac_dev) > 0) {
-               debugf3("%s(): failed edac_device_add_device()\n", __func__);
+               edac_dbg(3, "failed edac_device_add_device()\n");
                goto err;
        }
 
@@ -598,7 +610,7 @@ static int __devinit mpc85xx_l2_err_probe(struct platform_device *op)
 
        devres_remove_group(&op->dev, mpc85xx_l2_err_probe);
 
-       debugf3("%s(): success\n", __func__);
+       edac_dbg(3, "success\n");
        printk(KERN_INFO EDAC_MOD_STR " L2 err registered\n");
 
        return 0;
@@ -616,7 +628,7 @@ static int mpc85xx_l2_err_remove(struct platform_device *op)
        struct edac_device_ctl_info *edac_dev = dev_get_drvdata(&op->dev);
        struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        if (edac_op_state == EDAC_OPSTATE_INT) {
                out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINTEN, 0);
@@ -813,7 +825,7 @@ static void mpc85xx_mc_check(struct mem_ctl_info *mci)
        pfn = err_addr >> PAGE_SHIFT;
 
        for (row_index = 0; row_index < mci->nr_csrows; row_index++) {
-               csrow = &mci->csrows[row_index];
+               csrow = mci->csrows[row_index];
                if ((pfn >= csrow->first_page) && (pfn <= csrow->last_page))
                        break;
        }
@@ -854,16 +866,16 @@ static void mpc85xx_mc_check(struct mem_ctl_info *mci)
                mpc85xx_mc_printk(mci, KERN_ERR, "PFN out of range!\n");
 
        if (err_detect & DDR_EDE_SBE)
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                     pfn, err_addr & ~PAGE_MASK, syndrome,
                                     row_index, 0, -1,
-                                    mci->ctl_name, "", NULL);
+                                    mci->ctl_name, "");
 
        if (err_detect & DDR_EDE_MBE)
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                     pfn, err_addr & ~PAGE_MASK, syndrome,
                                     row_index, 0, -1,
-                                    mci->ctl_name, "", NULL);
+                                    mci->ctl_name, "");
 
        out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, err_detect);
 }
@@ -933,8 +945,8 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci)
                u32 start;
                u32 end;
 
-               csrow = &mci->csrows[index];
-               dimm = csrow->channels[0].dimm;
+               csrow = mci->csrows[index];
+               dimm = csrow->channels[0]->dimm;
 
                cs_bnds = in_be32(pdata->mc_vbase + MPC85XX_MC_CS_BNDS_0 +
                                  (index * MPC85XX_MC_CS_BNDS_OFS));
@@ -990,9 +1002,9 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op)
        pdata = mci->pvt_info;
        pdata->name = "mpc85xx_mc_err";
        pdata->irq = NO_IRQ;
-       mci->dev = &op->dev;
+       mci->pdev = &op->dev;
        pdata->edac_idx = edac_mc_idx++;
-       dev_set_drvdata(mci->dev, mci);
+       dev_set_drvdata(mci->pdev, mci);
        mci->ctl_name = pdata->name;
        mci->dev_name = pdata->name;
 
@@ -1026,7 +1038,7 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op)
                goto err;
        }
 
-       debugf3("%s(): init mci\n", __func__);
+       edac_dbg(3, "init mci\n");
        mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_RDDR2 |
            MEM_FLAG_DDR | MEM_FLAG_DDR2;
        mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
@@ -1041,8 +1053,6 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op)
 
        mci->scrub_mode = SCRUB_SW_SRC;
 
-       mpc85xx_set_mc_sysfs_attributes(mci);
-
        mpc85xx_init_csrows(mci);
 
        /* store the original error disable bits */
@@ -1054,7 +1064,13 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op)
        out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, ~0);
 
        if (edac_mc_add_mc(mci)) {
-               debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(3, "failed edac_mc_add_mc()\n");
+               goto err;
+       }
+
+       if (mpc85xx_create_sysfs_attributes(mci)) {
+               edac_mc_del_mc(mci->pdev);
+               edac_dbg(3, "failed edac_mc_add_mc()\n");
                goto err;
        }
 
@@ -1088,7 +1104,7 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op)
        }
 
        devres_remove_group(&op->dev, mpc85xx_mc_err_probe);
-       debugf3("%s(): success\n", __func__);
+       edac_dbg(3, "success\n");
        printk(KERN_INFO EDAC_MOD_STR " MC err registered\n");
 
        return 0;
@@ -1106,7 +1122,7 @@ static int mpc85xx_mc_err_remove(struct platform_device *op)
        struct mem_ctl_info *mci = dev_get_drvdata(&op->dev);
        struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        if (edac_op_state == EDAC_OPSTATE_INT) {
                out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_INT_EN, 0);
@@ -1117,6 +1133,7 @@ static int mpc85xx_mc_err_remove(struct platform_device *op)
                 orig_ddr_err_disable);
        out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_SBE, orig_ddr_err_sbe);
 
+       mpc85xx_remove_sysfs_attributes(mci);
        edac_mc_del_mc(&op->dev);
        edac_mc_free(mci);
        return 0;
index b0bb5a3d2527698c1f4659997fc6950526e5e215..2b315c2edc3cac14c5247e33a398283fd098b26d 100644 (file)
@@ -169,7 +169,7 @@ static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev)
                 MV64X60_PCIx_ERR_MASK_VAL);
 
        if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
-               debugf3("%s(): failed edac_pci_add_device()\n", __func__);
+               edac_dbg(3, "failed edac_pci_add_device()\n");
                goto err;
        }
 
@@ -194,7 +194,7 @@ static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev)
        devres_remove_group(&pdev->dev, mv64x60_pci_err_probe);
 
        /* get this far and it's successful */
-       debugf3("%s(): success\n", __func__);
+       edac_dbg(3, "success\n");
 
        return 0;
 
@@ -210,7 +210,7 @@ static int mv64x60_pci_err_remove(struct platform_device *pdev)
 {
        struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev);
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        edac_pci_del_device(&pdev->dev);
 
@@ -336,7 +336,7 @@ static int __devinit mv64x60_sram_err_probe(struct platform_device *pdev)
        pdata->edac_idx = edac_dev_idx++;
 
        if (edac_device_add_device(edac_dev) > 0) {
-               debugf3("%s(): failed edac_device_add_device()\n", __func__);
+               edac_dbg(3, "failed edac_device_add_device()\n");
                goto err;
        }
 
@@ -363,7 +363,7 @@ static int __devinit mv64x60_sram_err_probe(struct platform_device *pdev)
        devres_remove_group(&pdev->dev, mv64x60_sram_err_probe);
 
        /* get this far and it's successful */
-       debugf3("%s(): success\n", __func__);
+       edac_dbg(3, "success\n");
 
        return 0;
 
@@ -379,7 +379,7 @@ static int mv64x60_sram_err_remove(struct platform_device *pdev)
 {
        struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev);
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        edac_device_del_device(&pdev->dev);
        edac_device_free_ctl_info(edac_dev);
@@ -531,7 +531,7 @@ static int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev)
        pdata->edac_idx = edac_dev_idx++;
 
        if (edac_device_add_device(edac_dev) > 0) {
-               debugf3("%s(): failed edac_device_add_device()\n", __func__);
+               edac_dbg(3, "failed edac_device_add_device()\n");
                goto err;
        }
 
@@ -558,7 +558,7 @@ static int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev)
        devres_remove_group(&pdev->dev, mv64x60_cpu_err_probe);
 
        /* get this far and it's successful */
-       debugf3("%s(): success\n", __func__);
+       edac_dbg(3, "success\n");
 
        return 0;
 
@@ -574,7 +574,7 @@ static int mv64x60_cpu_err_remove(struct platform_device *pdev)
 {
        struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev);
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        edac_device_del_device(&pdev->dev);
        edac_device_free_ctl_info(edac_dev);
@@ -611,17 +611,17 @@ static void mv64x60_mc_check(struct mem_ctl_info *mci)
 
        /* first bit clear in ECC Err Reg, 1 bit error, correctable by HW */
        if (!(reg & 0x1))
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                     err_addr >> PAGE_SHIFT,
                                     err_addr & PAGE_MASK, syndrome,
                                     0, 0, -1,
-                                    mci->ctl_name, "", NULL);
+                                    mci->ctl_name, "");
        else    /* 2 bit error, UE */
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                     err_addr >> PAGE_SHIFT,
                                     err_addr & PAGE_MASK, 0,
                                     0, 0, -1,
-                                    mci->ctl_name, "", NULL);
+                                    mci->ctl_name, "");
 
        /* clear the error */
        out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0);
@@ -670,8 +670,8 @@ static void mv64x60_init_csrows(struct mem_ctl_info *mci,
 
        ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG);
 
-       csrow = &mci->csrows[0];
-       dimm = csrow->channels[0].dimm;
+       csrow = mci->csrows[0];
+       dimm = csrow->channels[0]->dimm;
 
        dimm->nr_pages = pdata->total_mem >> PAGE_SHIFT;
        dimm->grain = 8;
@@ -724,7 +724,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev)
        }
 
        pdata = mci->pvt_info;
-       mci->dev = &pdev->dev;
+       mci->pdev = &pdev->dev;
        platform_set_drvdata(pdev, mci);
        pdata->name = "mv64x60_mc_err";
        pdata->irq = NO_IRQ;
@@ -766,7 +766,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev)
                goto err2;
        }
 
-       debugf3("%s(): init mci\n", __func__);
+       edac_dbg(3, "init mci\n");
        mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR;
        mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
        mci->edac_cap = EDAC_FLAG_SECDED;
@@ -790,7 +790,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev)
        out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL, ctl);
 
        if (edac_mc_add_mc(mci)) {
-               debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(3, "failed edac_mc_add_mc()\n");
                goto err;
        }
 
@@ -815,7 +815,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev)
        }
 
        /* get this far and it's successful */
-       debugf3("%s(): success\n", __func__);
+       edac_dbg(3, "success\n");
 
        return 0;
 
@@ -831,7 +831,7 @@ static int mv64x60_mc_err_remove(struct platform_device *pdev)
 {
        struct mem_ctl_info *mci = platform_get_drvdata(pdev);
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        edac_mc_del_mc(&pdev->dev);
        edac_mc_free(mci);
index b095a906a994bc7b092362f21ed9f31eecf8d288..2d35b78ada3c819a5879d9357eadbb5ec2da00e7 100644 (file)
@@ -74,7 +74,7 @@ static int system_mmc_id;
 
 static u32 pasemi_edac_get_error_info(struct mem_ctl_info *mci)
 {
-       struct pci_dev *pdev = to_pci_dev(mci->dev);
+       struct pci_dev *pdev = to_pci_dev(mci->pdev);
        u32 tmp;
 
        pci_read_config_dword(pdev, MCDEBUG_ERRSTA,
@@ -95,7 +95,7 @@ static u32 pasemi_edac_get_error_info(struct mem_ctl_info *mci)
 
 static void pasemi_edac_process_error_info(struct mem_ctl_info *mci, u32 errsta)
 {
-       struct pci_dev *pdev = to_pci_dev(mci->dev);
+       struct pci_dev *pdev = to_pci_dev(mci->pdev);
        u32 errlog1a;
        u32 cs;
 
@@ -110,16 +110,16 @@ static void pasemi_edac_process_error_info(struct mem_ctl_info *mci, u32 errsta)
        /* uncorrectable/multi-bit errors */
        if (errsta & (MCDEBUG_ERRSTA_MBE_STATUS |
                      MCDEBUG_ERRSTA_RFL_STATUS)) {
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
-                                    mci->csrows[cs].first_page, 0, 0,
-                                    cs, 0, -1, mci->ctl_name, "", NULL);
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
+                                    mci->csrows[cs]->first_page, 0, 0,
+                                    cs, 0, -1, mci->ctl_name, "");
        }
 
        /* correctable/single-bit errors */
        if (errsta & MCDEBUG_ERRSTA_SBE_STATUS)
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
-                                    mci->csrows[cs].first_page, 0, 0,
-                                    cs, 0, -1, mci->ctl_name, "", NULL);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
+                                    mci->csrows[cs]->first_page, 0, 0,
+                                    cs, 0, -1, mci->ctl_name, "");
 }
 
 static void pasemi_edac_check(struct mem_ctl_info *mci)
@@ -141,8 +141,8 @@ static int pasemi_edac_init_csrows(struct mem_ctl_info *mci,
        int index;
 
        for (index = 0; index < mci->nr_csrows; index++) {
-               csrow = &mci->csrows[index];
-               dimm = csrow->channels[0].dimm;
+               csrow = mci->csrows[index];
+               dimm = csrow->channels[0]->dimm;
 
                pci_read_config_dword(pdev,
                                      MCDRAM_RANKCFG + (index * 12),
@@ -225,7 +225,7 @@ static int __devinit pasemi_edac_probe(struct pci_dev *pdev,
                MCCFG_ERRCOR_ECC_GEN_EN |
                MCCFG_ERRCOR_ECC_CRR_EN;
 
-       mci->dev = &pdev->dev;
+       mci->pdev = &pdev->dev;
        mci->mtype_cap = MEM_FLAG_DDR | MEM_FLAG_RDDR;
        mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
        mci->edac_cap = (errcor & MCCFG_ERRCOR_ECC_GEN_EN) ?
index f3f9fed06ad7d34ec8e3607f8141da6256e88e49..bf09576359911c51f8d548b04605387c77466a1e 100644 (file)
@@ -727,10 +727,10 @@ ppc4xx_edac_handle_ce(struct mem_ctl_info *mci,
 
        for (row = 0; row < mci->nr_csrows; row++)
                if (ppc4xx_edac_check_bank_error(status, row))
-                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                             0, 0, 0,
                                             row, 0, -1,
-                                            message, "", NULL);
+                                            message, "");
 }
 
 /**
@@ -758,10 +758,10 @@ ppc4xx_edac_handle_ue(struct mem_ctl_info *mci,
 
        for (row = 0; row < mci->nr_csrows; row++)
                if (ppc4xx_edac_check_bank_error(status, row))
-                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                             page, offset, 0,
                                             row, 0, -1,
-                                            message, "", NULL);
+                                            message, "");
 }
 
 /**
@@ -1027,9 +1027,9 @@ ppc4xx_edac_mc_init(struct mem_ctl_info *mci,
 
        /* Initial driver pointers and private data */
 
-       mci->dev                = &op->dev;
+       mci->pdev               = &op->dev;
 
-       dev_set_drvdata(mci->dev, mci);
+       dev_set_drvdata(mci->pdev, mci);
 
        pdata                   = mci->pvt_info;
 
@@ -1334,7 +1334,7 @@ static int __devinit ppc4xx_edac_probe(struct platform_device *op)
        return 0;
 
  fail1:
-       edac_mc_del_mc(mci->dev);
+       edac_mc_del_mc(mci->pdev);
 
  fail:
        edac_mc_free(mci);
@@ -1368,7 +1368,7 @@ ppc4xx_edac_remove(struct platform_device *op)
 
        dcr_unmap(pdata->dcr_host, SDRAM_DCR_RESOURCE_LEN);
 
-       edac_mc_del_mc(mci->dev);
+       edac_mc_del_mc(mci->pdev);
        edac_mc_free(mci);
 
        return 0;
index e1cacd164f316d3821e750f106a82540e536d794..f854debd553306a6eeae079067c2a35bc6a5988d 100644 (file)
@@ -140,7 +140,7 @@ static void r82600_get_error_info(struct mem_ctl_info *mci,
 {
        struct pci_dev *pdev;
 
-       pdev = to_pci_dev(mci->dev);
+       pdev = to_pci_dev(mci->pdev);
        pci_read_config_dword(pdev, R82600_EAP, &info->eapr);
 
        if (info->eapr & BIT(0))
@@ -179,11 +179,11 @@ static int r82600_process_error_info(struct mem_ctl_info *mci,
                error_found = 1;
 
                if (handle_errors)
-                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                             page, 0, syndrome,
                                             edac_mc_find_csrow_by_page(mci, page),
                                             0, -1,
-                                            mci->ctl_name, "", NULL);
+                                            mci->ctl_name, "");
        }
 
        if (info->eapr & BIT(1)) {      /* UE? */
@@ -191,11 +191,11 @@ static int r82600_process_error_info(struct mem_ctl_info *mci,
 
                if (handle_errors)
                        /* 82600 doesn't give enough info */
-                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                             page, 0, 0,
                                             edac_mc_find_csrow_by_page(mci, page),
                                             0, -1,
-                                            mci->ctl_name, "", NULL);
+                                            mci->ctl_name, "");
        }
 
        return error_found;
@@ -205,7 +205,7 @@ static void r82600_check(struct mem_ctl_info *mci)
 {
        struct r82600_error_info info;
 
-       debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
+       edac_dbg(1, "MC%d\n", mci->mc_idx);
        r82600_get_error_info(mci, &info);
        r82600_process_error_info(mci, &info, 1);
 }
@@ -230,19 +230,19 @@ static void r82600_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
        row_high_limit_last = 0;
 
        for (index = 0; index < mci->nr_csrows; index++) {
-               csrow = &mci->csrows[index];
-               dimm = csrow->channels[0].dimm;
+               csrow = mci->csrows[index];
+               dimm = csrow->channels[0]->dimm;
 
                /* find the DRAM Chip Select Base address and mask */
                pci_read_config_byte(pdev, R82600_DRBA + index, &drbar);
 
-               debugf1("%s() Row=%d DRBA = %#0x\n", __func__, index, drbar);
+               edac_dbg(1, "Row=%d DRBA = %#0x\n", index, drbar);
 
                row_high_limit = ((u32) drbar << 24);
 /*             row_high_limit = ((u32)drbar << 24) | 0xffffffUL; */
 
-               debugf1("%s() Row=%d, Boundary Address=%#0x, Last = %#0x\n",
-                       __func__, index, row_high_limit, row_high_limit_last);
+               edac_dbg(1, "Row=%d, Boundary Address=%#0x, Last = %#0x\n",
+                        index, row_high_limit, row_high_limit_last);
 
                /* Empty row [p.57] */
                if (row_high_limit == row_high_limit_last)
@@ -277,14 +277,13 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx)
        u32 sdram_refresh_rate;
        struct r82600_error_info discard;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
        pci_read_config_byte(pdev, R82600_DRAMC, &dramcr);
        pci_read_config_dword(pdev, R82600_EAP, &eapr);
        scrub_disabled = eapr & BIT(31);
        sdram_refresh_rate = dramcr & (BIT(0) | BIT(1));
-       debugf2("%s(): sdram refresh rate = %#0x\n", __func__,
-               sdram_refresh_rate);
-       debugf2("%s(): DRAMC register = %#0x\n", __func__, dramcr);
+       edac_dbg(2, "sdram refresh rate = %#0x\n", sdram_refresh_rate);
+       edac_dbg(2, "DRAMC register = %#0x\n", dramcr);
        layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
        layers[0].size = R82600_NR_CSROWS;
        layers[0].is_virt_csrow = true;
@@ -295,8 +294,8 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx)
        if (mci == NULL)
                return -ENOMEM;
 
-       debugf0("%s(): mci = %p\n", __func__, mci);
-       mci->dev = &pdev->dev;
+       edac_dbg(0, "mci = %p\n", mci);
+       mci->pdev = &pdev->dev;
        mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR;
        mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
        /* FIXME try to work out if the chip leads have been used for COM2
@@ -311,8 +310,8 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx)
 
        if (ecc_enabled(dramcr)) {
                if (scrub_disabled)
-                       debugf3("%s(): mci = %p - Scrubbing disabled! EAP: "
-                               "%#0x\n", __func__, mci, eapr);
+                       edac_dbg(3, "mci = %p - Scrubbing disabled! EAP: %#0x\n",
+                                mci, eapr);
        } else
                mci->edac_cap = EDAC_FLAG_NONE;
 
@@ -329,15 +328,14 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx)
         * type of memory controller.  The ID is therefore hardcoded to 0.
         */
        if (edac_mc_add_mc(mci)) {
-               debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(3, "failed edac_mc_add_mc()\n");
                goto fail;
        }
 
        /* get this far and it's successful */
 
        if (disable_hardware_scrub) {
-               debugf3("%s(): Disabling Hardware Scrub (scrub on error)\n",
-                       __func__);
+               edac_dbg(3, "Disabling Hardware Scrub (scrub on error)\n");
                pci_write_bits32(pdev, R82600_EAP, BIT(31), BIT(31));
        }
 
@@ -352,7 +350,7 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx)
                        __func__);
        }
 
-       debugf3("%s(): success\n", __func__);
+       edac_dbg(3, "success\n");
        return 0;
 
 fail:
@@ -364,7 +362,7 @@ fail:
 static int __devinit r82600_init_one(struct pci_dev *pdev,
                                const struct pci_device_id *ent)
 {
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /* don't need to call pci_enable_device() */
        return r82600_probe1(pdev, ent->driver_data);
@@ -374,7 +372,7 @@ static void __devexit r82600_remove_one(struct pci_dev *pdev)
 {
        struct mem_ctl_info *mci;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        if (r82600_pci)
                edac_pci_release_generic_ctl(r82600_pci);
index 36ad17e79d6183e3f21bf63f3266dc169f3941a0..f3b1f9fafa4b20b4f40917bcbab827ad760515df 100644 (file)
@@ -381,8 +381,8 @@ static inline int numrank(u32 mtr)
        int ranks = (1 << RANK_CNT_BITS(mtr));
 
        if (ranks > 4) {
-               debugf0("Invalid number of ranks: %d (max = 4) raw value = %x (%04x)",
-                       ranks, (unsigned int)RANK_CNT_BITS(mtr), mtr);
+               edac_dbg(0, "Invalid number of ranks: %d (max = 4) raw value = %x (%04x)\n",
+                        ranks, (unsigned int)RANK_CNT_BITS(mtr), mtr);
                return -EINVAL;
        }
 
@@ -394,8 +394,8 @@ static inline int numrow(u32 mtr)
        int rows = (RANK_WIDTH_BITS(mtr) + 12);
 
        if (rows < 13 || rows > 18) {
-               debugf0("Invalid number of rows: %d (should be between 14 and 17) raw value = %x (%04x)",
-                       rows, (unsigned int)RANK_WIDTH_BITS(mtr), mtr);
+               edac_dbg(0, "Invalid number of rows: %d (should be between 14 and 17) raw value = %x (%04x)\n",
+                        rows, (unsigned int)RANK_WIDTH_BITS(mtr), mtr);
                return -EINVAL;
        }
 
@@ -407,8 +407,8 @@ static inline int numcol(u32 mtr)
        int cols = (COL_WIDTH_BITS(mtr) + 10);
 
        if (cols > 12) {
-               debugf0("Invalid number of cols: %d (max = 4) raw value = %x (%04x)",
-                       cols, (unsigned int)COL_WIDTH_BITS(mtr), mtr);
+               edac_dbg(0, "Invalid number of cols: %d (max = 4) raw value = %x (%04x)\n",
+                        cols, (unsigned int)COL_WIDTH_BITS(mtr), mtr);
                return -EINVAL;
        }
 
@@ -475,8 +475,8 @@ static struct pci_dev *get_pdev_slot_func(u8 bus, unsigned slot,
 
                if (PCI_SLOT(sbridge_dev->pdev[i]->devfn) == slot &&
                    PCI_FUNC(sbridge_dev->pdev[i]->devfn) == func) {
-                       debugf1("Associated %02x.%02x.%d with %p\n",
-                               bus, slot, func, sbridge_dev->pdev[i]);
+                       edac_dbg(1, "Associated %02x.%02x.%d with %p\n",
+                                bus, slot, func, sbridge_dev->pdev[i]);
                        return sbridge_dev->pdev[i];
                }
        }
@@ -523,45 +523,45 @@ static int get_dimm_config(struct mem_ctl_info *mci)
 
        pci_read_config_dword(pvt->pci_br, SAD_CONTROL, &reg);
        pvt->sbridge_dev->node_id = NODE_ID(reg);
-       debugf0("mc#%d: Node ID: %d, source ID: %d\n",
-               pvt->sbridge_dev->mc,
-               pvt->sbridge_dev->node_id,
-               pvt->sbridge_dev->source_id);
+       edac_dbg(0, "mc#%d: Node ID: %d, source ID: %d\n",
+                pvt->sbridge_dev->mc,
+                pvt->sbridge_dev->node_id,
+                pvt->sbridge_dev->source_id);
 
        pci_read_config_dword(pvt->pci_ras, RASENABLES, &reg);
        if (IS_MIRROR_ENABLED(reg)) {
-               debugf0("Memory mirror is enabled\n");
+               edac_dbg(0, "Memory mirror is enabled\n");
                pvt->is_mirrored = true;
        } else {
-               debugf0("Memory mirror is disabled\n");
+               edac_dbg(0, "Memory mirror is disabled\n");
                pvt->is_mirrored = false;
        }
 
        pci_read_config_dword(pvt->pci_ta, MCMTR, &pvt->info.mcmtr);
        if (IS_LOCKSTEP_ENABLED(pvt->info.mcmtr)) {
-               debugf0("Lockstep is enabled\n");
+               edac_dbg(0, "Lockstep is enabled\n");
                mode = EDAC_S8ECD8ED;
                pvt->is_lockstep = true;
        } else {
-               debugf0("Lockstep is disabled\n");
+               edac_dbg(0, "Lockstep is disabled\n");
                mode = EDAC_S4ECD4ED;
                pvt->is_lockstep = false;
        }
        if (IS_CLOSE_PG(pvt->info.mcmtr)) {
-               debugf0("address map is on closed page mode\n");
+               edac_dbg(0, "address map is on closed page mode\n");
                pvt->is_close_pg = true;
        } else {
-               debugf0("address map is on open page mode\n");
+               edac_dbg(0, "address map is on open page mode\n");
                pvt->is_close_pg = false;
        }
 
        pci_read_config_dword(pvt->pci_ddrio, RANK_CFG_A, &reg);
        if (IS_RDIMM_ENABLED(reg)) {
                /* FIXME: Can also be LRDIMM */
-               debugf0("Memory is registered\n");
+               edac_dbg(0, "Memory is registered\n");
                mtype = MEM_RDDR3;
        } else {
-               debugf0("Memory is unregistered\n");
+               edac_dbg(0, "Memory is unregistered\n");
                mtype = MEM_DDR3;
        }
 
@@ -576,7 +576,7 @@ static int get_dimm_config(struct mem_ctl_info *mci)
                                       i, j, 0);
                        pci_read_config_dword(pvt->pci_tad[i],
                                              mtr_regs[j], &mtr);
-                       debugf4("Channel #%d  MTR%d = %x\n", i, j, mtr);
+                       edac_dbg(4, "Channel #%d  MTR%d = %x\n", i, j, mtr);
                        if (IS_DIMM_PRESENT(mtr)) {
                                pvt->channel[i].dimms++;
 
@@ -588,10 +588,10 @@ static int get_dimm_config(struct mem_ctl_info *mci)
                                size = (rows * cols * banks * ranks) >> (20 - 3);
                                npages = MiB_TO_PAGES(size);
 
-                               debugf0("mc#%d: channel %d, dimm %d, %d Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
-                                       pvt->sbridge_dev->mc, i, j,
-                                       size, npages,
-                                       banks, ranks, rows, cols);
+                               edac_dbg(0, "mc#%d: channel %d, dimm %d, %d Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
+                                        pvt->sbridge_dev->mc, i, j,
+                                        size, npages,
+                                        banks, ranks, rows, cols);
 
                                dimm->nr_pages = npages;
                                dimm->grain = 32;
@@ -629,8 +629,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
        tmp_mb = (1 + pvt->tolm) >> 20;
 
        mb = div_u64_rem(tmp_mb, 1000, &kb);
-       debugf0("TOLM: %u.%03u GB (0x%016Lx)\n",
-               mb, kb, (u64)pvt->tolm);
+       edac_dbg(0, "TOLM: %u.%03u GB (0x%016Lx)\n", mb, kb, (u64)pvt->tolm);
 
        /* Address range is already 45:25 */
        pci_read_config_dword(pvt->pci_sad1, TOHM,
@@ -639,8 +638,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
        tmp_mb = (1 + pvt->tohm) >> 20;
 
        mb = div_u64_rem(tmp_mb, 1000, &kb);
-       debugf0("TOHM: %u.%03u GB (0x%016Lx)",
-               mb, kb, (u64)pvt->tohm);
+       edac_dbg(0, "TOHM: %u.%03u GB (0x%016Lx)", mb, kb, (u64)pvt->tohm);
 
        /*
         * Step 2) Get SAD range and SAD Interleave list
@@ -663,13 +661,13 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
 
                tmp_mb = (limit + 1) >> 20;
                mb = div_u64_rem(tmp_mb, 1000, &kb);
-               debugf0("SAD#%d %s up to %u.%03u GB (0x%016Lx) %s reg=0x%08x\n",
-                       n_sads,
-                       get_dram_attr(reg),
-                       mb, kb,
-                       ((u64)tmp_mb) << 20L,
-                       INTERLEAVE_MODE(reg) ? "Interleave: 8:6" : "Interleave: [8:6]XOR[18:16]",
-                       reg);
+               edac_dbg(0, "SAD#%d %s up to %u.%03u GB (0x%016Lx) Interleave: %s reg=0x%08x\n",
+                        n_sads,
+                        get_dram_attr(reg),
+                        mb, kb,
+                        ((u64)tmp_mb) << 20L,
+                        INTERLEAVE_MODE(reg) ? "8:6" : "[8:6]XOR[18:16]",
+                        reg);
                prv = limit;
 
                pci_read_config_dword(pvt->pci_sad0, interleave_list[n_sads],
@@ -679,8 +677,8 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
                        if (j > 0 && sad_interl == sad_pkg(reg, j))
                                break;
 
-                       debugf0("SAD#%d, interleave #%d: %d\n",
-                       n_sads, j, sad_pkg(reg, j));
+                       edac_dbg(0, "SAD#%d, interleave #%d: %d\n",
+                                n_sads, j, sad_pkg(reg, j));
                }
        }
 
@@ -697,16 +695,16 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
                tmp_mb = (limit + 1) >> 20;
 
                mb = div_u64_rem(tmp_mb, 1000, &kb);
-               debugf0("TAD#%d: up to %u.%03u GB (0x%016Lx), socket interleave %d, memory interleave %d, TGT: %d, %d, %d, %d, reg=0x%08x\n",
-                       n_tads, mb, kb,
-                       ((u64)tmp_mb) << 20L,
-                       (u32)TAD_SOCK(reg),
-                       (u32)TAD_CH(reg),
-                       (u32)TAD_TGT0(reg),
-                       (u32)TAD_TGT1(reg),
-                       (u32)TAD_TGT2(reg),
-                       (u32)TAD_TGT3(reg),
-                       reg);
+               edac_dbg(0, "TAD#%d: up to %u.%03u GB (0x%016Lx), socket interleave %d, memory interleave %d, TGT: %d, %d, %d, %d, reg=0x%08x\n",
+                        n_tads, mb, kb,
+                        ((u64)tmp_mb) << 20L,
+                        (u32)TAD_SOCK(reg),
+                        (u32)TAD_CH(reg),
+                        (u32)TAD_TGT0(reg),
+                        (u32)TAD_TGT1(reg),
+                        (u32)TAD_TGT2(reg),
+                        (u32)TAD_TGT3(reg),
+                        reg);
                prv = limit;
        }
 
@@ -722,11 +720,11 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
                                              &reg);
                        tmp_mb = TAD_OFFSET(reg) >> 20;
                        mb = div_u64_rem(tmp_mb, 1000, &kb);
-                       debugf0("TAD CH#%d, offset #%d: %u.%03u GB (0x%016Lx), reg=0x%08x\n",
-                               i, j,
-                               mb, kb,
-                               ((u64)tmp_mb) << 20L,
-                               reg);
+                       edac_dbg(0, "TAD CH#%d, offset #%d: %u.%03u GB (0x%016Lx), reg=0x%08x\n",
+                                i, j,
+                                mb, kb,
+                                ((u64)tmp_mb) << 20L,
+                                reg);
                }
        }
 
@@ -747,12 +745,12 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
                        tmp_mb = RIR_LIMIT(reg) >> 20;
                        rir_way = 1 << RIR_WAY(reg);
                        mb = div_u64_rem(tmp_mb, 1000, &kb);
-                       debugf0("CH#%d RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d, reg=0x%08x\n",
-                               i, j,
-                               mb, kb,
-                               ((u64)tmp_mb) << 20L,
-                               rir_way,
-                               reg);
+                       edac_dbg(0, "CH#%d RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d, reg=0x%08x\n",
+                                i, j,
+                                mb, kb,
+                                ((u64)tmp_mb) << 20L,
+                                rir_way,
+                                reg);
 
                        for (k = 0; k < rir_way; k++) {
                                pci_read_config_dword(pvt->pci_tad[i],
@@ -761,12 +759,12 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
                                tmp_mb = RIR_OFFSET(reg) << 6;
 
                                mb = div_u64_rem(tmp_mb, 1000, &kb);
-                               debugf0("CH#%d RIR#%d INTL#%d, offset %u.%03u GB (0x%016Lx), tgt: %d, reg=0x%08x\n",
-                                       i, j, k,
-                                       mb, kb,
-                                       ((u64)tmp_mb) << 20L,
-                                       (u32)RIR_RNK_TGT(reg),
-                                       reg);
+                               edac_dbg(0, "CH#%d RIR#%d INTL#%d, offset %u.%03u GB (0x%016Lx), tgt: %d, reg=0x%08x\n",
+                                        i, j, k,
+                                        mb, kb,
+                                        ((u64)tmp_mb) << 20L,
+                                        (u32)RIR_RNK_TGT(reg),
+                                        reg);
                        }
                }
        }
@@ -853,16 +851,16 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
                if (sad_way > 0 && sad_interl == sad_pkg(reg, sad_way))
                        break;
                sad_interleave[sad_way] = sad_pkg(reg, sad_way);
-               debugf0("SAD interleave #%d: %d\n",
-                       sad_way, sad_interleave[sad_way]);
+               edac_dbg(0, "SAD interleave #%d: %d\n",
+                        sad_way, sad_interleave[sad_way]);
        }
-       debugf0("mc#%d: Error detected on SAD#%d: address 0x%016Lx < 0x%016Lx, Interleave [%d:6]%s\n",
-               pvt->sbridge_dev->mc,
-               n_sads,
-               addr,
-               limit,
-               sad_way + 7,
-               interleave_mode ? "" : "XOR[18:16]");
+       edac_dbg(0, "mc#%d: Error detected on SAD#%d: address 0x%016Lx < 0x%016Lx, Interleave [%d:6]%s\n",
+                pvt->sbridge_dev->mc,
+                n_sads,
+                addr,
+                limit,
+                sad_way + 7,
+                interleave_mode ? "" : "XOR[18:16]");
        if (interleave_mode)
                idx = ((addr >> 6) ^ (addr >> 16)) & 7;
        else
@@ -884,8 +882,8 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
                return -EINVAL;
        }
        *socket = sad_interleave[idx];
-       debugf0("SAD interleave index: %d (wayness %d) = CPU socket %d\n",
-               idx, sad_way, *socket);
+       edac_dbg(0, "SAD interleave index: %d (wayness %d) = CPU socket %d\n",
+                idx, sad_way, *socket);
 
        /*
         * Move to the proper node structure, in order to access the
@@ -972,16 +970,16 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
 
        offset = TAD_OFFSET(tad_offset);
 
-       debugf0("TAD#%d: address 0x%016Lx < 0x%016Lx, socket interleave %d, channel interleave %d (offset 0x%08Lx), index %d, base ch: %d, ch mask: 0x%02lx\n",
-               n_tads,
-               addr,
-               limit,
-               (u32)TAD_SOCK(reg),
-               ch_way,
-               offset,
-               idx,
-               base_ch,
-               *channel_mask);
+       edac_dbg(0, "TAD#%d: address 0x%016Lx < 0x%016Lx, socket interleave %d, channel interleave %d (offset 0x%08Lx), index %d, base ch: %d, ch mask: 0x%02lx\n",
+                n_tads,
+                addr,
+                limit,
+                (u32)TAD_SOCK(reg),
+                ch_way,
+                offset,
+                idx,
+                base_ch,
+                *channel_mask);
 
        /* Calculate channel address */
        /* Remove the TAD offset */
@@ -1017,11 +1015,11 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
 
                limit = RIR_LIMIT(reg);
                mb = div_u64_rem(limit >> 20, 1000, &kb);
-               debugf0("RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d\n",
-                       n_rir,
-                       mb, kb,
-                       limit,
-                       1 << RIR_WAY(reg));
+               edac_dbg(0, "RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d\n",
+                        n_rir,
+                        mb, kb,
+                        limit,
+                        1 << RIR_WAY(reg));
                if  (ch_addr <= limit)
                        break;
        }
@@ -1042,12 +1040,12 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
                              &reg);
        *rank = RIR_RNK_TGT(reg);
 
-       debugf0("RIR#%d: channel address 0x%08Lx < 0x%08Lx, RIR interleave %d, index %d\n",
-               n_rir,
-               ch_addr,
-               limit,
-               rir_way,
-               idx);
+       edac_dbg(0, "RIR#%d: channel address 0x%08Lx < 0x%08Lx, RIR interleave %d, index %d\n",
+                n_rir,
+                ch_addr,
+                limit,
+                rir_way,
+                idx);
 
        return 0;
 }
@@ -1064,14 +1062,14 @@ static void sbridge_put_devices(struct sbridge_dev *sbridge_dev)
 {
        int i;
 
-       debugf0(__FILE__ ": %s()\n", __func__);
+       edac_dbg(0, "\n");
        for (i = 0; i < sbridge_dev->n_devs; i++) {
                struct pci_dev *pdev = sbridge_dev->pdev[i];
                if (!pdev)
                        continue;
-               debugf0("Removing dev %02x:%02x.%d\n",
-                       pdev->bus->number,
-                       PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+               edac_dbg(0, "Removing dev %02x:%02x.%d\n",
+                        pdev->bus->number,
+                        PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
                pci_dev_put(pdev);
        }
 }
@@ -1177,10 +1175,9 @@ static int sbridge_get_onedevice(struct pci_dev **prev,
                return -ENODEV;
        }
 
-       debugf0("Detected dev %02x:%d.%d PCI ID %04x:%04x\n",
-               bus, dev_descr->dev,
-               dev_descr->func,
-               PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
+       edac_dbg(0, "Detected dev %02x:%d.%d PCI ID %04x:%04x\n",
+                bus, dev_descr->dev, dev_descr->func,
+                PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
 
        /*
         * As stated on drivers/pci/search.c, the reference count for
@@ -1297,10 +1294,10 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
                        goto error;
                }
 
-               debugf0("Associated PCI %02x.%02d.%d with dev = %p\n",
-                       sbridge_dev->bus,
-                       PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
-                       pdev);
+               edac_dbg(0, "Associated PCI %02x.%02d.%d with dev = %p\n",
+                        sbridge_dev->bus,
+                        PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+                        pdev);
        }
 
        /* Check if everything were registered */
@@ -1435,8 +1432,7 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
         * to the group of dimm's where the error may be happening.
         */
        snprintf(msg, sizeof(msg),
-                "count:%d%s%s area:%s err_code:%04x:%04x socket:%d channel_mask:%ld rank:%d",
-                core_err_cnt,
+                "%s%s area:%s err_code:%04x:%04x socket:%d channel_mask:%ld rank:%d",
                 overflow ? " OVERFLOW" : "",
                 (uncorrected_error && recoverable) ? " recoverable" : "",
                 area_type,
@@ -1445,20 +1441,20 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
                 channel_mask,
                 rank);
 
-       debugf0("%s", msg);
+       edac_dbg(0, "%s\n", msg);
 
        /* FIXME: need support for channel mask */
 
        /* Call the helper to output message */
-       edac_mc_handle_error(tp_event, mci,
+       edac_mc_handle_error(tp_event, mci, core_err_cnt,
                             m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, 0,
                             channel, dimm, -1,
-                            optype, msg, m);
+                            optype, msg);
        return;
 err_parsing:
-       edac_mc_handle_error(tp_event, mci, 0, 0, 0,
+       edac_mc_handle_error(tp_event, mci, core_err_cnt, 0, 0, 0,
                             -1, -1, -1,
-                            msg, "", m);
+                            msg, "");
 
 }
 
@@ -1592,8 +1588,7 @@ static void sbridge_unregister_mci(struct sbridge_dev *sbridge_dev)
        struct sbridge_pvt *pvt;
 
        if (unlikely(!mci || !mci->pvt_info)) {
-               debugf0("MC: " __FILE__ ": %s(): dev = %p\n",
-                       __func__, &sbridge_dev->pdev[0]->dev);
+               edac_dbg(0, "MC: dev = %p\n", &sbridge_dev->pdev[0]->dev);
 
                sbridge_printk(KERN_ERR, "Couldn't find mci handler\n");
                return;
@@ -1601,13 +1596,13 @@ static void sbridge_unregister_mci(struct sbridge_dev *sbridge_dev)
 
        pvt = mci->pvt_info;
 
-       debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
-               __func__, mci, &sbridge_dev->pdev[0]->dev);
+       edac_dbg(0, "MC: mci = %p, dev = %p\n",
+                mci, &sbridge_dev->pdev[0]->dev);
 
        /* Remove MC sysfs nodes */
-       edac_mc_del_mc(mci->dev);
+       edac_mc_del_mc(mci->pdev);
 
-       debugf1("%s: free mci struct\n", mci->ctl_name);
+       edac_dbg(1, "%s: free mci struct\n", mci->ctl_name);
        kfree(mci->ctl_name);
        edac_mc_free(mci);
        sbridge_dev->mci = NULL;
@@ -1638,8 +1633,8 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev)
        if (unlikely(!mci))
                return -ENOMEM;
 
-       debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
-               __func__, mci, &sbridge_dev->pdev[0]->dev);
+       edac_dbg(0, "MC: mci = %p, dev = %p\n",
+                mci, &sbridge_dev->pdev[0]->dev);
 
        pvt = mci->pvt_info;
        memset(pvt, 0, sizeof(*pvt));
@@ -1670,12 +1665,11 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev)
        get_memory_layout(mci);
 
        /* record ptr to the generic device */
-       mci->dev = &sbridge_dev->pdev[0]->dev;
+       mci->pdev = &sbridge_dev->pdev[0]->dev;
 
        /* add this new MC control structure to EDAC's list of MCs */
        if (unlikely(edac_mc_add_mc(mci))) {
-               debugf0("MC: " __FILE__
-                       ": %s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
                rc = -EINVAL;
                goto fail0;
        }
@@ -1722,7 +1716,8 @@ static int __devinit sbridge_probe(struct pci_dev *pdev,
        mc = 0;
 
        list_for_each_entry(sbridge_dev, &sbridge_edac_list, list) {
-               debugf0("Registering MC#%d (%d of %d)\n", mc, mc + 1, num_mc);
+               edac_dbg(0, "Registering MC#%d (%d of %d)\n",
+                        mc, mc + 1, num_mc);
                sbridge_dev->mc = mc++;
                rc = sbridge_register_mci(sbridge_dev);
                if (unlikely(rc < 0))
@@ -1752,7 +1747,7 @@ static void __devexit sbridge_remove(struct pci_dev *pdev)
 {
        struct sbridge_dev *sbridge_dev;
 
-       debugf0(__FILE__ ": %s()\n", __func__);
+       edac_dbg(0, "\n");
 
        /*
         * we have a trouble here: pdev value for removal will be wrong, since
@@ -1801,7 +1796,7 @@ static int __init sbridge_init(void)
 {
        int pci_rc;
 
-       debugf2("MC: " __FILE__ ": %s()\n", __func__);
+       edac_dbg(2, "\n");
 
        /* Ensure that the OPSTATE is set correctly for POLL or NMI */
        opstate_init();
@@ -1825,7 +1820,7 @@ static int __init sbridge_init(void)
  */
 static void __exit sbridge_exit(void)
 {
-       debugf2("MC: " __FILE__ ": %s()\n", __func__);
+       edac_dbg(2, "\n");
        pci_unregister_driver(&sbridge_driver);
        mce_unregister_decode_chain(&sbridge_mce_dec);
 }
index 7bb4614730db846445909d460a5b5a0953d8ab50..1e904b7b79a042671c83bb10e07547bfcf3eee12 100644 (file)
@@ -69,12 +69,12 @@ static void tile_edac_check(struct mem_ctl_info *mci)
 
        /* Check if the current error count is different from the saved one. */
        if (mem_error.sbe_count != priv->ce_count) {
-               dev_dbg(mci->dev, "ECC CE err on node %d\n", priv->node);
+               dev_dbg(mci->pdev, "ECC CE err on node %d\n", priv->node);
                priv->ce_count = mem_error.sbe_count;
-               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                     0, 0, 0,
                                     0, 0, -1,
-                                    mci->ctl_name, "", NULL);
+                                    mci->ctl_name, "");
        }
 }
 
@@ -84,10 +84,10 @@ static void tile_edac_check(struct mem_ctl_info *mci)
  */
 static int __devinit tile_edac_init_csrows(struct mem_ctl_info *mci)
 {
-       struct csrow_info       *csrow = &mci->csrows[0];
+       struct csrow_info       *csrow = mci->csrows[0];
        struct tile_edac_priv   *priv = mci->pvt_info;
        struct mshim_mem_info   mem_info;
-       struct dimm_info *dimm = csrow->channels[0].dimm;
+       struct dimm_info *dimm = csrow->channels[0]->dimm;
 
        if (hv_dev_pread(priv->hv_devhdl, 0, (HV_VirtAddr)&mem_info,
                sizeof(struct mshim_mem_info), MSHIM_MEM_INFO_OFF) !=
@@ -149,7 +149,7 @@ static int __devinit tile_edac_mc_probe(struct platform_device *pdev)
        priv->node = pdev->id;
        priv->hv_devhdl = hv_devhdl;
 
-       mci->dev = &pdev->dev;
+       mci->pdev = &pdev->dev;
        mci->mtype_cap = MEM_FLAG_DDR2;
        mci->edac_ctl_cap = EDAC_FLAG_SECDED;
 
index 1ac7962d63eadcd5ba8ddd17ae58b96b9a062e2e..08a992693e62ed8b7e994782ce824945001f220a 100644 (file)
@@ -103,10 +103,10 @@ static int how_many_channel(struct pci_dev *pdev)
 
        pci_read_config_byte(pdev, X38_CAPID0 + 8, &capid0_8b);
        if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */
-               debugf0("In single channel mode.\n");
+               edac_dbg(0, "In single channel mode\n");
                x38_channel_num = 1;
        } else {
-               debugf0("In dual channel mode.\n");
+               edac_dbg(0, "In dual channel mode\n");
                x38_channel_num = 2;
        }
 
@@ -151,7 +151,7 @@ static void x38_clear_error_info(struct mem_ctl_info *mci)
 {
        struct pci_dev *pdev;
 
-       pdev = to_pci_dev(mci->dev);
+       pdev = to_pci_dev(mci->pdev);
 
        /*
         * Clear any error bits.
@@ -172,7 +172,7 @@ static void x38_get_and_clear_error_info(struct mem_ctl_info *mci,
        struct pci_dev *pdev;
        void __iomem *window = mci->pvt_info;
 
-       pdev = to_pci_dev(mci->dev);
+       pdev = to_pci_dev(mci->pdev);
 
        /*
         * This is a mess because there is no atomic way to read all the
@@ -215,26 +215,26 @@ static void x38_process_error_info(struct mem_ctl_info *mci,
                return;
 
        if ((info->errsts ^ info->errsts2) & X38_ERRSTS_BITS) {
-               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
                                     -1, -1, -1,
-                                    "UE overwrote CE", "", NULL);
+                                    "UE overwrote CE", "");
                info->errsts = info->errsts2;
        }
 
        for (channel = 0; channel < x38_channel_num; channel++) {
                log = info->eccerrlog[channel];
                if (log & X38_ECCERRLOG_UE) {
-                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
                                             0, 0, 0,
                                             eccerrlog_row(channel, log),
                                             -1, -1,
-                                            "x38 UE", "", NULL);
+                                            "x38 UE", "");
                } else if (log & X38_ECCERRLOG_CE) {
-                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
                                             0, 0, eccerrlog_syndrome(log),
                                             eccerrlog_row(channel, log),
                                             -1, -1,
-                                            "x38 CE", "", NULL);
+                                            "x38 CE", "");
                }
        }
 }
@@ -243,7 +243,7 @@ static void x38_check(struct mem_ctl_info *mci)
 {
        struct x38_error_info info;
 
-       debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
+       edac_dbg(1, "MC%d\n", mci->mc_idx);
        x38_get_and_clear_error_info(mci, &info);
        x38_process_error_info(mci, &info);
 }
@@ -331,7 +331,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
        bool stacked;
        void __iomem *window;
 
-       debugf0("MC: %s()\n", __func__);
+       edac_dbg(0, "MC:\n");
 
        window = x38_map_mchbar(pdev);
        if (!window)
@@ -352,9 +352,9 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
        if (!mci)
                return -ENOMEM;
 
-       debugf3("MC: %s(): init mci\n", __func__);
+       edac_dbg(3, "MC: init mci\n");
 
-       mci->dev = &pdev->dev;
+       mci->pdev = &pdev->dev;
        mci->mtype_cap = MEM_FLAG_DDR2;
 
        mci->edac_ctl_cap = EDAC_FLAG_SECDED;
@@ -378,7 +378,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
         */
        for (i = 0; i < mci->nr_csrows; i++) {
                unsigned long nr_pages;
-               struct csrow_info *csrow = &mci->csrows[i];
+               struct csrow_info *csrow = mci->csrows[i];
 
                nr_pages = drb_to_nr_pages(drbs, stacked,
                        i / X38_RANKS_PER_CHANNEL,
@@ -388,7 +388,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
                        continue;
 
                for (j = 0; j < x38_channel_num; j++) {
-                       struct dimm_info *dimm = csrow->channels[j].dimm;
+                       struct dimm_info *dimm = csrow->channels[j]->dimm;
 
                        dimm->nr_pages = nr_pages / x38_channel_num;
                        dimm->grain = nr_pages << PAGE_SHIFT;
@@ -402,12 +402,12 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
 
        rc = -ENODEV;
        if (edac_mc_add_mc(mci)) {
-               debugf3("MC: %s(): failed edac_mc_add_mc()\n", __func__);
+               edac_dbg(3, "MC: failed edac_mc_add_mc()\n");
                goto fail;
        }
 
        /* get this far and it's successful */
-       debugf3("MC: %s(): success\n", __func__);
+       edac_dbg(3, "MC: success\n");
        return 0;
 
 fail:
@@ -423,7 +423,7 @@ static int __devinit x38_init_one(struct pci_dev *pdev,
 {
        int rc;
 
-       debugf0("MC: %s()\n", __func__);
+       edac_dbg(0, "MC:\n");
 
        if (pci_enable_device(pdev) < 0)
                return -EIO;
@@ -439,7 +439,7 @@ static void __devexit x38_remove_one(struct pci_dev *pdev)
 {
        struct mem_ctl_info *mci;
 
-       debugf0("%s()\n", __func__);
+       edac_dbg(0, "\n");
 
        mci = edac_mc_del_mc(&pdev->dev);
        if (!mci)
@@ -472,7 +472,7 @@ static int __init x38_init(void)
 {
        int pci_rc;
 
-       debugf3("MC: %s()\n", __func__);
+       edac_dbg(3, "MC:\n");
 
        /* Ensure that the OPSTATE is set correctly for POLL or NMI */
        opstate_init();
@@ -486,14 +486,14 @@ static int __init x38_init(void)
                mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
                                        PCI_DEVICE_ID_INTEL_X38_HB, NULL);
                if (!mci_pdev) {
-                       debugf0("x38 pci_get_device fail\n");
+                       edac_dbg(0, "x38 pci_get_device fail\n");
                        pci_rc = -ENODEV;
                        goto fail1;
                }
 
                pci_rc = x38_init_one(mci_pdev, x38_pci_tbl);
                if (pci_rc < 0) {
-                       debugf0("x38 init fail\n");
+                       edac_dbg(0, "x38 init fail\n");
                        pci_rc = -ENODEV;
                        goto fail1;
                }
@@ -513,7 +513,7 @@ fail0:
 
 static void __exit x38_exit(void)
 {
-       debugf3("MC: %s()\n", __func__);
+       edac_dbg(3, "MC:\n");
 
        pci_unregister_driver(&x38_driver);
        if (!x38_registered) {
index 16716356d1fee89dc4344c08bd0d11b57b87bf85..e175c8ed4ec47667fd9f09514ed2801b51e2c21d 100644 (file)
@@ -33,7 +33,7 @@ config EXTCON_MAX77693
 
 config EXTCON_MAX8997
        tristate "MAX8997 EXTCON Support"
-       depends on MFD_MAX8997
+       depends on MFD_MAX8997 && IRQ_DOMAIN
        help
          If you say yes here you get support for the MUIC device of
          Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory
index a4ed30bd9a4182c26168bd3bbc81b206641739a2..ef9090a4271ddc00ee6d735c2c4b48051e5c09f3 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/mfd/max8997.h>
 #include <linux/mfd/max8997-private.h>
 #include <linux/extcon.h>
+#include <linux/irqdomain.h>
 
 #define        DEV_NAME                        "max8997-muic"
 
@@ -77,6 +78,7 @@
 struct max8997_muic_irq {
        unsigned int irq;
        const char *name;
+       unsigned int virq;
 };
 
 static struct max8997_muic_irq muic_irqs[] = {
@@ -343,12 +345,10 @@ static void max8997_muic_irq_work(struct work_struct *work)
 {
        struct max8997_muic_info *info = container_of(work,
                        struct max8997_muic_info, irq_work);
-       struct max8997_dev *max8997 = i2c_get_clientdata(info->muic);
        u8 status[2];
        u8 adc, chg_type;
-
-       int irq_type = info->irq - max8997->irq_base;
-       int ret;
+       int irq_type = 0;
+       int i, ret;
 
        mutex_lock(&info->mutex);
 
@@ -363,6 +363,10 @@ static void max8997_muic_irq_work(struct work_struct *work)
        dev_dbg(info->dev, "%s: STATUS1:0x%x, 2:0x%x\n", __func__,
                        status[0], status[1]);
 
+       for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++)
+               if (info->irq == muic_irqs[i].virq)
+                       irq_type = muic_irqs[i].irq;
+
        switch (irq_type) {
        case MAX8997_MUICIRQ_ADC:
                adc = status[0] & STATUS1_ADC_MASK;
@@ -448,11 +452,15 @@ static int __devinit max8997_muic_probe(struct platform_device *pdev)
 
        for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
                struct max8997_muic_irq *muic_irq = &muic_irqs[i];
+               int virq = 0;
+
+               virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq);
+               if (!virq)
+                       goto err_irq;
+               muic_irq->virq = virq;
 
-               ret = request_threaded_irq(pdata->irq_base + muic_irq->irq,
-                               NULL, max8997_muic_irq_handler,
-                               0, muic_irq->name,
-                               info);
+               ret = request_threaded_irq(virq, NULL,max8997_muic_irq_handler,
+                               0, muic_irq->name, info);
                if (ret) {
                        dev_err(&pdev->dev,
                                "failed: irq request (IRQ: %d,"
@@ -496,7 +504,7 @@ err_extcon:
        kfree(info->edev);
 err_irq:
        while (--i >= 0)
-               free_irq(pdata->irq_base + muic_irqs[i].irq, info);
+               free_irq(muic_irqs[i].virq, info);
        kfree(info);
 err_kfree:
        return ret;
@@ -505,11 +513,10 @@ err_kfree:
 static int __devexit max8997_muic_remove(struct platform_device *pdev)
 {
        struct max8997_muic_info *info = platform_get_drvdata(pdev);
-       struct max8997_dev *max8997 = i2c_get_clientdata(info->muic);
        int i;
 
        for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
-               free_irq(max8997->irq_base + muic_irqs[i].irq, info);
+               free_irq(muic_irqs[i].virq, info);
        cancel_work_sync(&info->irq_work);
 
        extcon_dev_unregister(info->edev);
index 4d460ef871610d6309a47561bf0c531344e2b7cd..7a05fd24d68b9c126719ac320620dee9343f49a4 100644 (file)
@@ -398,6 +398,14 @@ static ssize_t guid_show(struct device *dev,
        return ret;
 }
 
+static ssize_t is_local_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       struct fw_device *device = fw_device(dev);
+
+       return sprintf(buf, "%u\n", device->is_local);
+}
+
 static int units_sprintf(char *buf, const u32 *directory)
 {
        struct fw_csr_iterator ci;
@@ -447,6 +455,7 @@ static ssize_t units_show(struct device *dev,
 static struct device_attribute fw_device_attributes[] = {
        __ATTR_RO(config_rom),
        __ATTR_RO(guid),
+       __ATTR_RO(is_local),
        __ATTR_RO(units),
        __ATTR_NULL,
 };
index 8382e27e9a271877c98d24ff6268f9d578738b47..38c0aa60b2cb1a53da46547e5bc188a87db1f18b 100644 (file)
@@ -146,7 +146,7 @@ EXPORT_SYMBOL(fw_iso_buffer_destroy);
 /* Convert DMA address to offset into virtually contiguous buffer. */
 size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed)
 {
-       int i;
+       size_t i;
        dma_addr_t address;
        ssize_t offset;
 
index 780708dc6e25f39a9cd181e3a1c0f075ad8f96ee..87d6f2d2f02d015a030cad4001fd59bebeeea9a4 100644 (file)
@@ -525,9 +525,10 @@ const struct fw_address_region fw_high_memory_region =
        { .start = 0x000100000000ULL, .end = 0xffffe0000000ULL,  };
 EXPORT_SYMBOL(fw_high_memory_region);
 
-#if 0
-const struct fw_address_region fw_low_memory_region =
+static const struct fw_address_region low_memory_region =
        { .start = 0x000000000000ULL, .end = 0x000100000000ULL,  };
+
+#if 0
 const struct fw_address_region fw_private_region =
        { .start = 0xffffe0000000ULL, .end = 0xfffff0000000ULL,  };
 const struct fw_address_region fw_csr_region =
@@ -1198,6 +1199,23 @@ static struct fw_address_handler registers = {
        .address_callback       = handle_registers,
 };
 
+static void handle_low_memory(struct fw_card *card, struct fw_request *request,
+               int tcode, int destination, int source, int generation,
+               unsigned long long offset, void *payload, size_t length,
+               void *callback_data)
+{
+       /*
+        * This catches requests not handled by the physical DMA unit,
+        * i.e., wrong transaction types or unauthorized source nodes.
+        */
+       fw_send_response(card, request, RCODE_TYPE_ERROR);
+}
+
+static struct fw_address_handler low_memory = {
+       .length                 = 0x000100000000ULL,
+       .address_callback       = handle_low_memory,
+};
+
 MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>");
 MODULE_DESCRIPTION("Core IEEE1394 transaction logic");
 MODULE_LICENSE("GPL");
@@ -1259,6 +1277,7 @@ static int __init fw_core_init(void)
 
        fw_core_add_address_handler(&topology_map, &topology_map_region);
        fw_core_add_address_handler(&registers, &registers_region);
+       fw_core_add_address_handler(&low_memory, &low_memory_region);
        fw_core_add_descriptor(&vendor_id_descriptor);
        fw_core_add_descriptor(&model_id_descriptor);
 
index c1af05e834b610e98acba423c6e811239c9d908e..c788dbdaf3bc6b51f779079909e339d26b063e25 100644 (file)
@@ -191,6 +191,7 @@ struct fw_ohci {
        unsigned quirks;
        unsigned int pri_req_max;
        u32 bus_time;
+       bool bus_time_running;
        bool is_root;
        bool csr_state_setclear_abdicate;
        int n_ir;
@@ -1726,6 +1727,13 @@ static u32 update_bus_time(struct fw_ohci *ohci)
 {
        u32 cycle_time_seconds = get_cycle_time(ohci) >> 25;
 
+       if (unlikely(!ohci->bus_time_running)) {
+               reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_cycle64Seconds);
+               ohci->bus_time = (lower_32_bits(get_seconds()) & ~0x7f) |
+                                (cycle_time_seconds & 0x40);
+               ohci->bus_time_running = true;
+       }
+
        if ((ohci->bus_time & 0x40) != (cycle_time_seconds & 0x40))
                ohci->bus_time += 0x40;
 
@@ -2213,7 +2221,7 @@ static int ohci_enable(struct fw_card *card,
 {
        struct fw_ohci *ohci = fw_ohci(card);
        struct pci_dev *dev = to_pci_dev(card->device);
-       u32 lps, seconds, version, irqs;
+       u32 lps, version, irqs;
        int i, ret;
 
        if (software_reset(ohci)) {
@@ -2269,9 +2277,12 @@ static int ohci_enable(struct fw_card *card,
                  (OHCI1394_MAX_PHYS_RESP_RETRIES << 8) |
                  (200 << 16));
 
-       seconds = lower_32_bits(get_seconds());
-       reg_write(ohci, OHCI1394_IsochronousCycleTimer, seconds << 25);
-       ohci->bus_time = seconds & ~0x3f;
+       ohci->bus_time_running = false;
+
+       for (i = 0; i < 32; i++)
+               if (ohci->ir_context_support & (1 << i))
+                       reg_write(ohci, OHCI1394_IsoRcvContextControlClear(i),
+                                 IR_CONTEXT_MULTI_CHANNEL_MODE);
 
        version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff;
        if (version >= OHCI_VERSION_1_1) {
@@ -2369,7 +2380,6 @@ static int ohci_enable(struct fw_card *card,
                OHCI1394_postedWriteErr |
                OHCI1394_selfIDComplete |
                OHCI1394_regAccessFail |
-               OHCI1394_cycle64Seconds |
                OHCI1394_cycleInconsistent |
                OHCI1394_unrecoverableError |
                OHCI1394_cycleTooLong |
@@ -2658,7 +2668,8 @@ static void ohci_write_csr(struct fw_card *card, int csr_offset, u32 value)
 
        case CSR_BUS_TIME:
                spin_lock_irqsave(&ohci->lock, flags);
-               ohci->bus_time = (ohci->bus_time & 0x7f) | (value & ~0x7f);
+               ohci->bus_time = (update_bus_time(ohci) & 0x40) |
+                                (value & ~0x7f);
                spin_unlock_irqrestore(&ohci->lock, flags);
                break;
 
@@ -3539,6 +3550,13 @@ static int __devinit pci_probe(struct pci_dev *dev,
 
        INIT_WORK(&ohci->bus_reset_work, bus_reset_work);
 
+       if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM) ||
+           pci_resource_len(dev, 0) < OHCI1394_REGISTER_SIZE) {
+               dev_err(&dev->dev, "invalid MMIO resource\n");
+               err = -ENXIO;
+               goto fail_disable;
+       }
+
        err = pci_request_region(dev, 0, ohci_driver_name);
        if (err) {
                dev_err(&dev->dev, "MMIO resource unavailable\n");
index 153980be4ee64462f12a97563c0143fc59b3eb82..b298158cb9224dd24eb65c5c00d2733b5d6cfc98 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/dmi.h>
 #include <linux/efi.h>
 #include <linux/bootmem.h>
+#include <linux/random.h>
 #include <asm/dmi.h>
 
 /*
@@ -111,6 +112,8 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *,
 
        dmi_table(buf, dmi_len, dmi_num, decode, NULL);
 
+       add_device_randomness(buf, dmi_len);
+
        dmi_iounmap(buf, dmi_len);
        return 0;
 }
index adc07102a20d0caef9fc98290f25be6c5b2b6a3d..c1cdc9236666f60ee6891206f5a1f90bba2c4a0f 100644 (file)
@@ -98,7 +98,7 @@ static LIST_HEAD(map_entries);
 /**
  * firmware_map_add_entry() - Does the real work to add a firmware memmap entry.
  * @start: Start of the memory range.
- * @end:   End of the memory range (inclusive).
+ * @end:   End of the memory range (exclusive).
  * @type:  Type of the memory range.
  * @entry: Pre-allocated (either kmalloc() or bootmem allocator), uninitialised
  *         entry.
@@ -113,7 +113,7 @@ static int firmware_map_add_entry(u64 start, u64 end,
        BUG_ON(start > end);
 
        entry->start = start;
-       entry->end = end;
+       entry->end = end - 1;
        entry->type = type;
        INIT_LIST_HEAD(&entry->list);
        kobject_init(&entry->kobj, &memmap_ktype);
@@ -148,7 +148,7 @@ static int add_sysfs_fw_map_entry(struct firmware_map_entry *entry)
  * firmware_map_add_hotplug() - Adds a firmware mapping entry when we do
  * memory hotplug.
  * @start: Start of the memory range.
- * @end:   End of the memory range (inclusive).
+ * @end:   End of the memory range (exclusive)
  * @type:  Type of the memory range.
  *
  * Adds a firmware mapping entry. This function is for memory hotplug, it is
@@ -175,7 +175,7 @@ int __meminit firmware_map_add_hotplug(u64 start, u64 end, const char *type)
 /**
  * firmware_map_add_early() - Adds a firmware mapping entry.
  * @start: Start of the memory range.
- * @end:   End of the memory range (inclusive).
+ * @end:   End of the memory range.
  * @type:  Type of the memory range.
  *
  * Adds a firmware mapping entry. This function uses the bootmem allocator
index 51e0e2d8fac6345859e1c7083c7a3c86a9b5add3..a330492e06f9597e9d8abcf82561b248bee1753c 100644 (file)
@@ -95,7 +95,7 @@ efi_setup_pcdp_console(char *cmdline)
        if (efi.hcdp == EFI_INVALID_TABLE_ADDR)
                return -ENODEV;
 
-       pcdp = ioremap(efi.hcdp, 4096);
+       pcdp = early_ioremap(efi.hcdp, 4096);
        printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, efi.hcdp);
 
        if (strstr(cmdline, "console=hcdp")) {
@@ -131,6 +131,6 @@ efi_setup_pcdp_console(char *cmdline)
        }
 
 out:
-       iounmap(pcdp);
+       early_iounmap(pcdp, 4096);
        return rc;
 }
index 502b5ea43f4fae28db5a9eb65f3c37d0daf52e69..b16c8a72a2e241a8a47c524564a02c48a5389b35 100644 (file)
@@ -597,6 +597,13 @@ config GPIO_AB8500
        help
          Select this to enable the AB8500 IC GPIO driver
 
+config GPIO_TPS6586X
+       bool "TPS6586X GPIO"
+       depends on MFD_TPS6586X
+       help
+         Select this option to enable GPIO driver for the TPS6586X
+         chip family.
+
 config GPIO_TPS65910
        bool "TPS65910 GPIO"
        depends on MFD_TPS65910
index d37048105a87328c96f5b56a4bb5f1abf1812011..153caceeb0530df7a7e5932421b7f2d48fee3780 100644 (file)
@@ -63,6 +63,7 @@ obj-$(CONFIG_GPIO_TC3589X)    += gpio-tc3589x.o
 obj-$(CONFIG_ARCH_TEGRA)       += gpio-tegra.o
 obj-$(CONFIG_GPIO_TIMBERDALE)  += gpio-timberdale.o
 obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o
+obj-$(CONFIG_GPIO_TPS6586X)    += gpio-tps6586x.o
 obj-$(CONFIG_GPIO_TPS65910)    += gpio-tps65910.o
 obj-$(CONFIG_GPIO_TPS65912)    += gpio-tps65912.o
 obj-$(CONFIG_GPIO_TWL4030)     += gpio-twl4030.o
diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c
new file mode 100644 (file)
index 0000000..2526b3b
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * TI TPS6586x GPIO driver
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ * Author: Laxman dewangan <ldewangan@nvidia.com>
+ *
+ * Based on tps6586x.c
+ * Copyright (c) 2010 CompuLab Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/tps6586x.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+/* GPIO control registers */
+#define TPS6586X_GPIOSET1      0x5d
+#define TPS6586X_GPIOSET2      0x5e
+
+struct tps6586x_gpio {
+       struct gpio_chip gpio_chip;
+       struct device *parent;
+};
+
+static inline struct tps6586x_gpio *to_tps6586x_gpio(struct gpio_chip *chip)
+{
+       return container_of(chip, struct tps6586x_gpio, gpio_chip);
+}
+
+static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+       struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc);
+       uint8_t val;
+       int ret;
+
+       ret = tps6586x_read(tps6586x_gpio->parent, TPS6586X_GPIOSET2, &val);
+       if (ret)
+               return ret;
+
+       return !!(val & (1 << offset));
+}
+
+static void tps6586x_gpio_set(struct gpio_chip *gc, unsigned offset,
+                             int value)
+{
+       struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc);
+
+       tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET2,
+                       value << offset, 1 << offset);
+}
+
+static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
+                               int value)
+{
+       struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc);
+       uint8_t val, mask;
+
+       tps6586x_gpio_set(gc, offset, value);
+
+       val = 0x1 << (offset * 2);
+       mask = 0x3 << (offset * 2);
+
+       return tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET1,
+                               val, mask);
+}
+
+static int __devinit tps6586x_gpio_probe(struct platform_device *pdev)
+{
+       struct tps6586x_platform_data *pdata;
+       struct tps6586x_gpio *tps6586x_gpio;
+       int ret;
+
+       pdata = dev_get_platdata(pdev->dev.parent);
+       tps6586x_gpio = devm_kzalloc(&pdev->dev,
+                               sizeof(*tps6586x_gpio), GFP_KERNEL);
+       if (!tps6586x_gpio) {
+               dev_err(&pdev->dev, "Could not allocate tps6586x_gpio\n");
+               return -ENOMEM;
+       }
+
+       tps6586x_gpio->parent = pdev->dev.parent;
+
+       tps6586x_gpio->gpio_chip.owner = THIS_MODULE;
+       tps6586x_gpio->gpio_chip.label = pdev->name;
+       tps6586x_gpio->gpio_chip.dev = &pdev->dev;
+       tps6586x_gpio->gpio_chip.ngpio = 4;
+       tps6586x_gpio->gpio_chip.can_sleep = 1;
+
+       /* FIXME: add handling of GPIOs as dedicated inputs */
+       tps6586x_gpio->gpio_chip.direction_output = tps6586x_gpio_output;
+       tps6586x_gpio->gpio_chip.set    = tps6586x_gpio_set;
+       tps6586x_gpio->gpio_chip.get    = tps6586x_gpio_get;
+
+#ifdef CONFIG_OF_GPIO
+       tps6586x_gpio->gpio_chip.of_node = pdev->dev.parent->of_node;
+#endif
+       if (pdata && pdata->gpio_base)
+               tps6586x_gpio->gpio_chip.base = pdata->gpio_base;
+       else
+               tps6586x_gpio->gpio_chip.base = -1;
+
+       ret = gpiochip_add(&tps6586x_gpio->gpio_chip);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, tps6586x_gpio);
+
+       return ret;
+}
+
+static int __devexit tps6586x_gpio_remove(struct platform_device *pdev)
+{
+       struct tps6586x_gpio *tps6586x_gpio = platform_get_drvdata(pdev);
+
+       return gpiochip_remove(&tps6586x_gpio->gpio_chip);
+}
+
+static struct platform_driver tps6586x_gpio_driver = {
+       .driver.name    = "tps6586x-gpio",
+       .driver.owner   = THIS_MODULE,
+       .probe          = tps6586x_gpio_probe,
+       .remove         = __devexit_p(tps6586x_gpio_remove),
+};
+
+static int __init tps6586x_gpio_init(void)
+{
+       return platform_driver_register(&tps6586x_gpio_driver);
+}
+subsys_initcall(tps6586x_gpio_init);
+
+static void __exit tps6586x_gpio_exit(void)
+{
+       platform_driver_unregister(&tps6586x_gpio_driver);
+}
+module_exit(tps6586x_gpio_exit);
+
+MODULE_ALIAS("platform:tps6586x-gpio");
+MODULE_DESCRIPTION("GPIO interface for TPS6586X PMIC");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL");
index bf791fa0e50d06311b98379aa550a2444051b191..d9568198c3008b1518096c92ee5ac54f77a70676 100644 (file)
@@ -196,7 +196,8 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
        return ret;
 }
 
-struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector)
+static struct drm_encoder *exynos_drm_best_encoder(
+                                               struct drm_connector *connector)
 {
        struct drm_device *dev = connector->dev;
        struct exynos_drm_connector *exynos_connector =
index eaf630dc5dba951e78f8a68c2d0bcc1b25ae40b3..84dd099eae3b93cfa49e733d42353e1a0d0e12e4 100644 (file)
@@ -33,7 +33,6 @@
 #include "exynos_drm_fbdev.h"
 
 static LIST_HEAD(exynos_drm_subdrv_list);
-static struct drm_device *drm_dev;
 
 static int exynos_drm_subdrv_probe(struct drm_device *dev,
                                        struct exynos_drm_subdrv *subdrv)
@@ -120,8 +119,6 @@ int exynos_drm_device_register(struct drm_device *dev)
        if (!dev)
                return -EINVAL;
 
-       drm_dev = dev;
-
        list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
                subdrv->drm_dev = dev;
                err = exynos_drm_subdrv_probe(dev, subdrv);
@@ -149,8 +146,6 @@ int exynos_drm_device_unregister(struct drm_device *dev)
        list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list)
                exynos_drm_subdrv_remove(dev, subdrv);
 
-       drm_dev = NULL;
-
        return 0;
 }
 EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
index 32a34c85899bb08aa0cf95d492830af66af6041c..abb1e2f8227f3304a88b475bcc3190dec81be8c8 100644 (file)
 #include "drmP.h"
 #include "drm_crtc_helper.h"
 
-#include "exynos_drm_crtc.h"
 #include "exynos_drm_drv.h"
-#include "exynos_drm_fb.h"
 #include "exynos_drm_encoder.h"
-#include "exynos_drm_gem.h"
+#include "exynos_drm_plane.h"
 
 #define to_exynos_crtc(x)      container_of(x, struct exynos_drm_crtc,\
                                drm_crtc)
 
+enum exynos_crtc_mode {
+       CRTC_MODE_NORMAL,       /* normal mode */
+       CRTC_MODE_BLANK,        /* The private plane of crtc is blank */
+};
+
 /*
  * Exynos specific crtc structure.
  *
  * @drm_crtc: crtc object.
- * @overlay: contain information common to display controller and hdmi and
- *     contents of this overlay object would be copied to sub driver size.
+ * @drm_plane: pointer of private plane object for this crtc
  * @pipe: a crtc index created at load() with a new crtc object creation
  *     and the crtc object would be set to private->crtc array
  *     to get a crtc object corresponding to this pipe from private->crtc
  *     we can refer to the crtc to current hardware interrupt occured through
  *     this pipe value.
  * @dpms: store the crtc dpms value
+ * @mode: store the crtc mode value
  */
 struct exynos_drm_crtc {
        struct drm_crtc                 drm_crtc;
-       struct exynos_drm_overlay       overlay;
+       struct drm_plane                *plane;
        unsigned int                    pipe;
        unsigned int                    dpms;
+       enum exynos_crtc_mode           mode;
 };
 
-static void exynos_drm_crtc_apply(struct drm_crtc *crtc)
-{
-       struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
-       struct exynos_drm_overlay *overlay = &exynos_crtc->overlay;
-
-       exynos_drm_fn_encoder(crtc, overlay,
-                       exynos_drm_encoder_crtc_mode_set);
-       exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
-                       exynos_drm_encoder_crtc_commit);
-}
-
-int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
-                             struct drm_framebuffer *fb,
-                             struct drm_display_mode *mode,
-                             struct exynos_drm_crtc_pos *pos)
-{
-       struct exynos_drm_gem_buf *buffer;
-       unsigned int actual_w;
-       unsigned int actual_h;
-       int nr = exynos_drm_format_num_buffers(fb->pixel_format);
-       int i;
-
-       for (i = 0; i < nr; i++) {
-               buffer = exynos_drm_fb_buffer(fb, i);
-               if (!buffer) {
-                       DRM_LOG_KMS("buffer is null\n");
-                       return -EFAULT;
-               }
-
-               overlay->dma_addr[i] = buffer->dma_addr;
-               overlay->vaddr[i] = buffer->kvaddr;
-
-               DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n",
-                               i, (unsigned long)overlay->vaddr[i],
-                               (unsigned long)overlay->dma_addr[i]);
-       }
-
-       actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w);
-       actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h);
-
-       /* set drm framebuffer data. */
-       overlay->fb_x = pos->fb_x;
-       overlay->fb_y = pos->fb_y;
-       overlay->fb_width = fb->width;
-       overlay->fb_height = fb->height;
-       overlay->src_width = pos->src_w;
-       overlay->src_height = pos->src_h;
-       overlay->bpp = fb->bits_per_pixel;
-       overlay->pitch = fb->pitches[0];
-       overlay->pixel_format = fb->pixel_format;
-
-       /* set overlay range to be displayed. */
-       overlay->crtc_x = pos->crtc_x;
-       overlay->crtc_y = pos->crtc_y;
-       overlay->crtc_width = actual_w;
-       overlay->crtc_height = actual_h;
-
-       /* set drm mode data. */
-       overlay->mode_width = mode->hdisplay;
-       overlay->mode_height = mode->vdisplay;
-       overlay->refresh = mode->vrefresh;
-       overlay->scan_flag = mode->flags;
-
-       DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)",
-                       overlay->crtc_x, overlay->crtc_y,
-                       overlay->crtc_width, overlay->crtc_height);
-
-       return 0;
-}
-
-static int exynos_drm_crtc_update(struct drm_crtc *crtc)
-{
-       struct exynos_drm_crtc *exynos_crtc;
-       struct exynos_drm_overlay *overlay;
-       struct exynos_drm_crtc_pos pos;
-       struct drm_display_mode *mode = &crtc->mode;
-       struct drm_framebuffer *fb = crtc->fb;
-
-       if (!mode || !fb)
-               return -EINVAL;
-
-       exynos_crtc = to_exynos_crtc(crtc);
-       overlay = &exynos_crtc->overlay;
-
-       memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos));
-
-       /* it means the offset of framebuffer to be displayed. */
-       pos.fb_x = crtc->x;
-       pos.fb_y = crtc->y;
-
-       /* OSD position to be displayed. */
-       pos.crtc_x = 0;
-       pos.crtc_y = 0;
-       pos.crtc_w = fb->width - crtc->x;
-       pos.crtc_h = fb->height - crtc->y;
-       pos.src_w = pos.crtc_w;
-       pos.src_h = pos.crtc_h;
-
-       return exynos_drm_overlay_update(overlay, crtc->fb, mode, &pos);
-}
-
 static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
 {
        struct drm_device *dev = crtc->dev;
@@ -175,23 +78,8 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
 
        mutex_lock(&dev->struct_mutex);
 
-       switch (mode) {
-       case DRM_MODE_DPMS_ON:
-               exynos_drm_fn_encoder(crtc, &mode,
-                               exynos_drm_encoder_crtc_dpms);
-               exynos_crtc->dpms = mode;
-               break;
-       case DRM_MODE_DPMS_STANDBY:
-       case DRM_MODE_DPMS_SUSPEND:
-       case DRM_MODE_DPMS_OFF:
-               exynos_drm_fn_encoder(crtc, &mode,
-                               exynos_drm_encoder_crtc_dpms);
-               exynos_crtc->dpms = mode;
-               break;
-       default:
-               DRM_ERROR("unspecified mode %d\n", mode);
-               break;
-       }
+       exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms);
+       exynos_crtc->dpms = mode;
 
        mutex_unlock(&dev->struct_mutex);
 }
@@ -209,30 +97,8 @@ static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       /*
-        * when set_crtc is requested from user or at booting time,
-        * crtc->commit would be called without dpms call so if dpms is
-        * no power on then crtc->dpms should be called
-        * with DRM_MODE_DPMS_ON for the hardware power to be on.
-        */
-       if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) {
-               int mode = DRM_MODE_DPMS_ON;
-
-               /*
-                * enable hardware(power on) to all encoders hdmi connected
-                * to current crtc.
-                */
-               exynos_drm_crtc_dpms(crtc, mode);
-               /*
-                * enable dma to all encoders connected to current crtc and
-                * lcd panel.
-                */
-               exynos_drm_fn_encoder(crtc, &mode,
-                                       exynos_drm_encoder_dpms_from_crtc);
-       }
-
-       exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
-                       exynos_drm_encoder_crtc_commit);
+       exynos_plane_commit(exynos_crtc->plane);
+       exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON);
 }
 
 static bool
@@ -251,31 +117,61 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
                          struct drm_display_mode *adjusted_mode, int x, int y,
                          struct drm_framebuffer *old_fb)
 {
+       struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+       struct drm_plane *plane = exynos_crtc->plane;
+       unsigned int crtc_w;
+       unsigned int crtc_h;
+       int pipe = exynos_crtc->pipe;
+       int ret;
+
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
+       exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+
        /*
         * copy the mode data adjusted by mode_fixup() into crtc->mode
         * so that hardware can be seet to proper mode.
         */
        memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
 
-       return exynos_drm_crtc_update(crtc);
+       crtc_w = crtc->fb->width - x;
+       crtc_h = crtc->fb->height - y;
+
+       ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
+                                   x, y, crtc_w, crtc_h);
+       if (ret)
+               return ret;
+
+       plane->crtc = crtc;
+       plane->fb = crtc->fb;
+
+       exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe);
+
+       return 0;
 }
 
 static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
                                          struct drm_framebuffer *old_fb)
 {
+       struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+       struct drm_plane *plane = exynos_crtc->plane;
+       unsigned int crtc_w;
+       unsigned int crtc_h;
        int ret;
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       ret = exynos_drm_crtc_update(crtc);
+       crtc_w = crtc->fb->width - x;
+       crtc_h = crtc->fb->height - y;
+
+       ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
+                                   x, y, crtc_w, crtc_h);
        if (ret)
                return ret;
 
-       exynos_drm_crtc_apply(crtc);
+       exynos_drm_crtc_commit(crtc);
 
-       return ret;
+       return 0;
 }
 
 static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc)
@@ -284,6 +180,16 @@ static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc)
        /* drm framework doesn't check NULL */
 }
 
+static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
+{
+       struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_OFF);
+       exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
 static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
        .dpms           = exynos_drm_crtc_dpms,
        .prepare        = exynos_drm_crtc_prepare,
@@ -292,6 +198,7 @@ static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
        .mode_set       = exynos_drm_crtc_mode_set,
        .mode_set_base  = exynos_drm_crtc_mode_set_base,
        .load_lut       = exynos_drm_crtc_load_lut,
+       .disable        = exynos_drm_crtc_disable,
 };
 
 static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
@@ -327,7 +234,8 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
                                &dev_priv->pageflip_event_list);
 
                crtc->fb = fb;
-               ret = exynos_drm_crtc_update(crtc);
+               ret = exynos_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y,
+                                                   NULL);
                if (ret) {
                        crtc->fb = old_fb;
                        drm_vblank_put(dev, exynos_crtc->pipe);
@@ -335,14 +243,6 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
 
                        goto out;
                }
-
-               /*
-                * the values related to a buffer of the drm framebuffer
-                * to be applied should be set at here. because these values
-                * first, are set to shadow registers and then to
-                * real registers at vsync front porch period.
-                */
-               exynos_drm_crtc_apply(crtc);
        }
 out:
        mutex_unlock(&dev->struct_mutex);
@@ -362,18 +262,73 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
        kfree(exynos_crtc);
 }
 
+static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
+                                       struct drm_property *property,
+                                       uint64_t val)
+{
+       struct drm_device *dev = crtc->dev;
+       struct exynos_drm_private *dev_priv = dev->dev_private;
+       struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+
+       DRM_DEBUG_KMS("%s\n", __func__);
+
+       if (property == dev_priv->crtc_mode_property) {
+               enum exynos_crtc_mode mode = val;
+
+               if (mode == exynos_crtc->mode)
+                       return 0;
+
+               exynos_crtc->mode = mode;
+
+               switch (mode) {
+               case CRTC_MODE_NORMAL:
+                       exynos_drm_crtc_commit(crtc);
+                       break;
+               case CRTC_MODE_BLANK:
+                       exynos_plane_dpms(exynos_crtc->plane,
+                                         DRM_MODE_DPMS_OFF);
+                       break;
+               default:
+                       break;
+               }
+
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
 static struct drm_crtc_funcs exynos_crtc_funcs = {
        .set_config     = drm_crtc_helper_set_config,
        .page_flip      = exynos_drm_crtc_page_flip,
        .destroy        = exynos_drm_crtc_destroy,
+       .set_property   = exynos_drm_crtc_set_property,
+};
+
+static const struct drm_prop_enum_list mode_names[] = {
+       { CRTC_MODE_NORMAL, "normal" },
+       { CRTC_MODE_BLANK, "blank" },
 };
 
-struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev,
-               struct drm_crtc *crtc)
+static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc)
 {
-       struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct exynos_drm_private *dev_priv = dev->dev_private;
+       struct drm_property *prop;
 
-       return &exynos_crtc->overlay;
+       DRM_DEBUG_KMS("%s\n", __func__);
+
+       prop = dev_priv->crtc_mode_property;
+       if (!prop) {
+               prop = drm_property_create_enum(dev, 0, "mode", mode_names,
+                                               ARRAY_SIZE(mode_names));
+               if (!prop)
+                       return;
+
+               dev_priv->crtc_mode_property = prop;
+       }
+
+       drm_object_attach_property(&crtc->base, prop, 0);
 }
 
 int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
@@ -392,7 +347,12 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
 
        exynos_crtc->pipe = nr;
        exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
-       exynos_crtc->overlay.zpos = DEFAULT_ZPOS;
+       exynos_crtc->plane = exynos_plane_init(dev, 1 << nr, true);
+       if (!exynos_crtc->plane) {
+               kfree(exynos_crtc);
+               return -ENOMEM;
+       }
+
        crtc = &exynos_crtc->drm_crtc;
 
        private->crtc[nr] = crtc;
@@ -400,6 +360,8 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
        drm_crtc_init(dev, crtc, &exynos_crtc_funcs);
        drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
 
+       exynos_drm_crtc_attach_mode_property(crtc);
+
        return 0;
 }
 
index 16b8e2195a0de2f1b8778b697e707f50f527843f..6bae8d8c250ec7d6cd84c85268d99cf5f40669b6 100644 (file)
 #ifndef _EXYNOS_DRM_CRTC_H_
 #define _EXYNOS_DRM_CRTC_H_
 
-struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev,
-               struct drm_crtc *crtc);
 int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr);
 int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
 void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);
 
-/*
- * Exynos specific crtc postion structure.
- *
- * @fb_x: offset x on a framebuffer to be displyed
- *     - the unit is screen coordinates.
- * @fb_y: offset y on a framebuffer to be displayed
- *     - the unit is screen coordinates.
- * @src_w: width of source area to be displayed from a framebuffer.
- * @src_h: height of source area to be displayed from a framebuffer.
- * @crtc_x: offset x on hardware screen.
- * @crtc_y: offset y on hardware screen.
- * @crtc_w: width of hardware screen.
- * @crtc_h: height of hardware screen.
- */
-struct exynos_drm_crtc_pos {
-       unsigned int fb_x;
-       unsigned int fb_y;
-       unsigned int src_w;
-       unsigned int src_h;
-       unsigned int crtc_x;
-       unsigned int crtc_y;
-       unsigned int crtc_w;
-       unsigned int crtc_h;
-};
-
-int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
-                             struct drm_framebuffer *fb,
-                             struct drm_display_mode *mode,
-                             struct exynos_drm_crtc_pos *pos);
 #endif
index 274909271c36de62e18d10b81fe01a58b598fc61..613bf8a5d9b268331779b0f60f43f52a047269de 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "drmP.h"
 #include "drm.h"
+#include "exynos_drm.h"
 #include "exynos_drm_drv.h"
 #include "exynos_drm_gem.h"
 
@@ -86,6 +87,10 @@ static struct sg_table *
        npages = buf->size / buf->page_size;
 
        sgt = exynos_pages_to_sg(buf->pages, npages, buf->page_size);
+       if (!sgt) {
+               DRM_DEBUG_PRIME("exynos_pages_to_sg returned NULL!\n");
+               goto err_unlock;
+       }
        nents = dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir);
 
        DRM_DEBUG_PRIME("npages = %d buffer size = 0x%lx page_size = 0x%lx\n",
@@ -186,7 +191,7 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
        struct exynos_drm_gem_obj *exynos_gem_obj;
        struct exynos_drm_gem_buf *buffer;
        struct page *page;
-       int ret, i = 0;
+       int ret;
 
        DRM_DEBUG_PRIME("%s\n", __FILE__);
 
@@ -210,7 +215,7 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
 
 
        sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
-       if (IS_ERR(sgt)) {
+       if (IS_ERR_OR_NULL(sgt)) {
                ret = PTR_ERR(sgt);
                goto err_buf_detach;
        }
@@ -236,13 +241,25 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
        }
 
        sgl = sgt->sgl;
-       buffer->dma_addr = sg_dma_address(sgl);
 
-       while (i < sgt->nents) {
-               buffer->pages[i] = sg_page(sgl);
-               buffer->size += sg_dma_len(sgl);
-               sgl = sg_next(sgl);
-               i++;
+       if (sgt->nents == 1) {
+               buffer->dma_addr = sg_dma_address(sgt->sgl);
+               buffer->size = sg_dma_len(sgt->sgl);
+
+               /* always physically continuous memory if sgt->nents is 1. */
+               exynos_gem_obj->flags |= EXYNOS_BO_CONTIG;
+       } else {
+               unsigned int i = 0;
+
+               buffer->dma_addr = sg_dma_address(sgl);
+               while (i < sgt->nents) {
+                       buffer->pages[i] = sg_page(sgl);
+                       buffer->size += sg_dma_len(sgl);
+                       sgl = sg_next(sgl);
+                       i++;
+               }
+
+               exynos_gem_obj->flags |= EXYNOS_BO_NONCONTIG;
        }
 
        exynos_gem_obj->buffer = buffer;
index d6de2e07fa034ea33d796cd6ad97c5902e369924..ebacec6f1e48efef646700c630f02813007eaac0 100644 (file)
@@ -85,8 +85,11 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
        }
 
        for (nr = 0; nr < MAX_PLANE; nr++) {
-               ret = exynos_plane_init(dev, nr);
-               if (ret)
+               struct drm_plane *plane;
+               unsigned int possible_crtcs = (1 << MAX_CRTC) - 1;
+
+               plane = exynos_plane_init(dev, possible_crtcs, false);
+               if (!plane)
                        goto err_crtc;
        }
 
@@ -221,8 +224,6 @@ static struct drm_ioctl_desc exynos_ioctls[] = {
                        exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH),
        DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET,
                        exynos_drm_gem_get_ioctl, DRM_UNLOCKED),
-       DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, exynos_plane_set_zpos_ioctl,
-                       DRM_UNLOCKED | DRM_AUTH),
        DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION,
                        vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH),
        DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER,
index 277653d5fda0704dcbad615b1ec1d64f7bedc5f7..e22704b249d75782def8345dca75b5e51d60715d 100644 (file)
@@ -59,12 +59,14 @@ enum exynos_drm_output_type {
  *
  * @mode_set: copy drm overlay info to hw specific overlay info.
  * @commit: apply hardware specific overlay data to registers.
+ * @enable: enable hardware specific overlay.
  * @disable: disable hardware specific overlay.
  */
 struct exynos_drm_overlay_ops {
        void (*mode_set)(struct device *subdrv_dev,
                         struct exynos_drm_overlay *overlay);
        void (*commit)(struct device *subdrv_dev, int zpos);
+       void (*enable)(struct device *subdrv_dev, int zpos);
        void (*disable)(struct device *subdrv_dev, int zpos);
 };
 
@@ -235,6 +237,8 @@ struct exynos_drm_private {
         * this array is used to be aware of which crtc did it request vblank.
         */
        struct drm_crtc *crtc[MAX_CRTC];
+       struct drm_property *plane_zpos_property;
+       struct drm_property *crtc_mode_property;
 };
 
 /*
index 4a13a747f5d4930d06d495503190ee80ea4d532d..2c037cd7d2d44aee526779636d5f8e999c4adf5d 100644 (file)
@@ -30,7 +30,6 @@
 #include "drm_crtc_helper.h"
 
 #include "exynos_drm_drv.h"
-#include "exynos_drm_crtc.h"
 #include "exynos_drm_encoder.h"
 
 #define to_exynos_encoder(x)   container_of(x, struct exynos_drm_encoder,\
@@ -136,21 +135,16 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
        struct drm_connector *connector;
        struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
        struct exynos_drm_manager_ops *manager_ops = manager->ops;
-       struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
-       struct exynos_drm_overlay *overlay = get_exynos_drm_overlay(dev,
-                                               encoder->crtc);
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
+       exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-               if (connector->encoder == encoder) {
+               if (connector->encoder == encoder)
                        if (manager_ops && manager_ops->mode_set)
                                manager_ops->mode_set(manager->dev,
                                                        adjusted_mode);
-
-                       if (overlay_ops && overlay_ops->mode_set)
-                               overlay_ops->mode_set(manager->dev, overlay);
-               }
        }
 }
 
@@ -310,8 +304,8 @@ void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data)
        struct exynos_drm_manager_ops *manager_ops = manager->ops;
        int crtc = *(int *)data;
 
-       if (manager->pipe == -1)
-               manager->pipe = crtc;
+       if (manager->pipe != crtc)
+               return;
 
        if (manager_ops->enable_vblank)
                manager_ops->enable_vblank(manager->dev);
@@ -324,34 +318,41 @@ void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data)
        struct exynos_drm_manager_ops *manager_ops = manager->ops;
        int crtc = *(int *)data;
 
-       if (manager->pipe == -1)
-               manager->pipe = crtc;
+       if (manager->pipe != crtc)
+               return;
 
        if (manager_ops->disable_vblank)
                manager_ops->disable_vblank(manager->dev);
 }
 
-void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder,
-                                         void *data)
+void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data)
 {
-       struct exynos_drm_manager *manager =
-               to_exynos_encoder(encoder)->manager;
-       struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
-       int zpos = DEFAULT_ZPOS;
+       struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
+       struct exynos_drm_manager *manager = exynos_encoder->manager;
+       struct exynos_drm_manager_ops *manager_ops = manager->ops;
+       int mode = *(int *)data;
 
-       if (data)
-               zpos = *(int *)data;
+       DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       if (overlay_ops && overlay_ops->commit)
-               overlay_ops->commit(manager->dev, zpos);
+       if (manager_ops && manager_ops->dpms)
+               manager_ops->dpms(manager->dev, mode);
+
+       /*
+        * if this condition is ok then it means that the crtc is already
+        * detached from encoder and last function for detaching is properly
+        * done, so clear pipe from manager to prevent repeated call.
+        */
+       if (mode > DRM_MODE_DPMS_ON) {
+               if (!encoder->crtc)
+                       manager->pipe = -1;
+       }
 }
 
-void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data)
+void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data)
 {
        struct exynos_drm_manager *manager =
                to_exynos_encoder(encoder)->manager;
-       int crtc = *(int *)data;
-       int zpos = DEFAULT_ZPOS;
+       int pipe = *(int *)data;
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
@@ -359,76 +360,62 @@ void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data)
         * when crtc is detached from encoder, this pipe is used
         * to select manager operation
         */
-       manager->pipe = crtc;
-
-       exynos_drm_encoder_crtc_plane_commit(encoder, &zpos);
+       manager->pipe = pipe;
 }
 
-void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder, void *data)
+void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data)
 {
-       struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
-       int mode = *(int *)data;
+       struct exynos_drm_manager *manager =
+               to_exynos_encoder(encoder)->manager;
+       struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+       struct exynos_drm_overlay *overlay = data;
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       exynos_drm_encoder_dpms(encoder, mode);
-
-       exynos_encoder->dpms = mode;
+       if (overlay_ops && overlay_ops->mode_set)
+               overlay_ops->mode_set(manager->dev, overlay);
 }
 
-void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data)
+void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data)
 {
-       struct drm_device *dev = encoder->dev;
-       struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
-       struct exynos_drm_manager *manager = exynos_encoder->manager;
-       struct exynos_drm_manager_ops *manager_ops = manager->ops;
-       struct drm_connector *connector;
-       int mode = *(int *)data;
+       struct exynos_drm_manager *manager =
+               to_exynos_encoder(encoder)->manager;
+       struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+       int zpos = DEFAULT_ZPOS;
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       if (manager_ops && manager_ops->dpms)
-               manager_ops->dpms(manager->dev, mode);
-
-       /*
-        * set current dpms mode to the connector connected to
-        * current encoder. connector->dpms would be checked
-        * at drm_helper_connector_dpms()
-        */
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head)
-               if (connector->encoder == encoder)
-                       connector->dpms = mode;
+       if (data)
+               zpos = *(int *)data;
 
-       /*
-        * if this condition is ok then it means that the crtc is already
-        * detached from encoder and last function for detaching is properly
-        * done, so clear pipe from manager to prevent repeated call.
-        */
-       if (mode > DRM_MODE_DPMS_ON) {
-               if (!encoder->crtc)
-                       manager->pipe = -1;
-       }
+       if (overlay_ops && overlay_ops->commit)
+               overlay_ops->commit(manager->dev, zpos);
 }
 
-void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data)
+void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data)
 {
        struct exynos_drm_manager *manager =
                to_exynos_encoder(encoder)->manager;
        struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
-       struct exynos_drm_overlay *overlay = data;
+       int zpos = DEFAULT_ZPOS;
 
-       if (overlay_ops && overlay_ops->mode_set)
-               overlay_ops->mode_set(manager->dev, overlay);
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       if (data)
+               zpos = *(int *)data;
+
+       if (overlay_ops && overlay_ops->enable)
+               overlay_ops->enable(manager->dev, zpos);
 }
 
-void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data)
+void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data)
 {
        struct exynos_drm_manager *manager =
                to_exynos_encoder(encoder)->manager;
        struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
        int zpos = DEFAULT_ZPOS;
 
-       DRM_DEBUG_KMS("\n");
+       DRM_DEBUG_KMS("%s\n", __FILE__);
 
        if (data)
                zpos = *(int *)data;
index eb7d2316847edff9e4faa2940f241e9a19f8739c..6470d9ddf5a15affa3291673422b72e06b456063 100644 (file)
@@ -40,13 +40,11 @@ void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
                            void (*fn)(struct drm_encoder *, void *));
 void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data);
 void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder,
-                                         void *data);
-void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder,
-                                       void *data);
 void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data);
+void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data);
+void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data);
+void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data);
+void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data);
+void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data);
 
 #endif
index 29fdbfeb43cb79645d034da2d7f8a7aba27dbe5b..a68d2b313f03cab1d42fae0d362159f6a4f1ba26 100644 (file)
@@ -78,7 +78,6 @@ struct fimd_context {
        struct drm_crtc                 *crtc;
        struct clk                      *bus_clk;
        struct clk                      *lcd_clk;
-       struct resource                 *regs_res;
        void __iomem                    *regs;
        struct fimd_win_data            win_data[WINDOWS_NR];
        unsigned int                    clkdiv;
@@ -813,7 +812,7 @@ static int __devinit fimd_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
        if (!ctx)
                return -ENOMEM;
 
@@ -838,33 +837,26 @@ static int __devinit fimd_probe(struct platform_device *pdev)
                goto err_clk;
        }
 
-       ctx->regs_res = request_mem_region(res->start, resource_size(res),
-                                          dev_name(dev));
-       if (!ctx->regs_res) {
-               dev_err(dev, "failed to claim register region\n");
-               ret = -ENOENT;
-               goto err_clk;
-       }
-
-       ctx->regs = ioremap(res->start, resource_size(res));
+       ctx->regs = devm_request_and_ioremap(&pdev->dev, res);
        if (!ctx->regs) {
                dev_err(dev, "failed to map registers\n");
                ret = -ENXIO;
-               goto err_req_region_io;
+               goto err_clk;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (!res) {
                dev_err(dev, "irq request failed.\n");
-               goto err_req_region_irq;
+               goto err_clk;
        }
 
        ctx->irq = res->start;
 
-       ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx);
-       if (ret < 0) {
+       ret = devm_request_irq(&pdev->dev, ctx->irq, fimd_irq_handler,
+                                                       0, "drm_fimd", ctx);
+       if (ret) {
                dev_err(dev, "irq request failed.\n");
-               goto err_req_irq;
+               goto err_clk;
        }
 
        ctx->vidcon0 = pdata->vidcon0;
@@ -899,14 +891,6 @@ static int __devinit fimd_probe(struct platform_device *pdev)
 
        return 0;
 
-err_req_irq:
-err_req_region_irq:
-       iounmap(ctx->regs);
-
-err_req_region_io:
-       release_resource(ctx->regs_res);
-       kfree(ctx->regs_res);
-
 err_clk:
        clk_disable(ctx->lcd_clk);
        clk_put(ctx->lcd_clk);
@@ -916,7 +900,6 @@ err_bus_clk:
        clk_put(ctx->bus_clk);
 
 err_clk_get:
-       kfree(ctx);
        return ret;
 }
 
@@ -944,13 +927,6 @@ out:
        clk_put(ctx->lcd_clk);
        clk_put(ctx->bus_clk);
 
-       iounmap(ctx->regs);
-       release_resource(ctx->regs_res);
-       kfree(ctx->regs_res);
-       free_irq(ctx->irq, ctx);
-
-       kfree(ctx);
-
        return 0;
 }
 
index 5c8b683029ea64c0938c1054aa1157c4eb94e694..f9efde40c097b819af24d41ff32162f3191ea632 100644 (file)
@@ -99,25 +99,17 @@ out:
 struct page **exynos_gem_get_pages(struct drm_gem_object *obj,
                                                gfp_t gfpmask)
 {
-       struct inode *inode;
-       struct address_space *mapping;
        struct page *p, **pages;
        int i, npages;
 
-       /* This is the shared memory object that backs the GEM resource */
-       inode = obj->filp->f_path.dentry->d_inode;
-       mapping = inode->i_mapping;
-
        npages = obj->size >> PAGE_SHIFT;
 
        pages = drm_malloc_ab(npages, sizeof(struct page *));
        if (pages == NULL)
                return ERR_PTR(-ENOMEM);
 
-       gfpmask |= mapping_gfp_mask(mapping);
-
        for (i = 0; i < npages; i++) {
-               p = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
+               p = alloc_page(gfpmask);
                if (IS_ERR(p))
                        goto fail;
                pages[i] = p;
@@ -126,31 +118,22 @@ struct page **exynos_gem_get_pages(struct drm_gem_object *obj,
        return pages;
 
 fail:
-       while (i--)
-               page_cache_release(pages[i]);
+       while (--i)
+               __free_page(pages[i]);
 
        drm_free_large(pages);
        return ERR_PTR(PTR_ERR(p));
 }
 
 static void exynos_gem_put_pages(struct drm_gem_object *obj,
-                                       struct page **pages,
-                                       bool dirty, bool accessed)
+                                       struct page **pages)
 {
-       int i, npages;
+       int npages;
 
        npages = obj->size >> PAGE_SHIFT;
 
-       for (i = 0; i < npages; i++) {
-               if (dirty)
-                       set_page_dirty(pages[i]);
-
-               if (accessed)
-                       mark_page_accessed(pages[i]);
-
-               /* Undo the reference we took when populating the table */
-               page_cache_release(pages[i]);
-       }
+       while (--npages >= 0)
+               __free_page(pages[npages]);
 
        drm_free_large(pages);
 }
@@ -189,7 +172,7 @@ static int exynos_drm_gem_get_pages(struct drm_gem_object *obj)
                return -EINVAL;
        }
 
-       pages = exynos_gem_get_pages(obj, GFP_KERNEL);
+       pages = exynos_gem_get_pages(obj, GFP_HIGHUSER_MOVABLE);
        if (IS_ERR(pages)) {
                DRM_ERROR("failed to get pages.\n");
                return PTR_ERR(pages);
@@ -230,7 +213,7 @@ err1:
        kfree(buf->sgt);
        buf->sgt = NULL;
 err:
-       exynos_gem_put_pages(obj, pages, true, false);
+       exynos_gem_put_pages(obj, pages);
        return ret;
 
 }
@@ -248,7 +231,7 @@ static void exynos_drm_gem_put_pages(struct drm_gem_object *obj)
        kfree(buf->sgt);
        buf->sgt = NULL;
 
-       exynos_gem_put_pages(obj, buf->pages, true, false);
+       exynos_gem_put_pages(obj, buf->pages);
        buf->pages = NULL;
 
        /* add some codes for UNCACHED type here. TODO */
@@ -291,11 +274,21 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
        if (!buf->pages)
                return;
 
+       /*
+        * do not release memory region from exporter.
+        *
+        * the region will be released by exporter
+        * once dmabuf's refcount becomes 0.
+        */
+       if (obj->import_attach)
+               goto out;
+
        if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG)
                exynos_drm_gem_put_pages(obj);
        else
                exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, buf);
 
+out:
        exynos_drm_fini_buf(obj->dev, buf);
        exynos_gem_obj->buffer = NULL;
 
@@ -668,7 +661,7 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
         *      with DRM_IOCTL_MODE_CREATE_DUMB command.
         */
 
-       args->pitch = args->width * args->bpp >> 3;
+       args->pitch = args->width * ((args->bpp + 7) / 8);
        args->size = PAGE_ALIGN(args->pitch * args->height);
 
        exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
index 14d038b6cb02ffe6c35e3350d612018a3fe9f241..085b2a5d5f70fab9925aafbc8c561c05040521e7 100644 (file)
@@ -63,7 +63,8 @@ struct exynos_drm_gem_buf {
  *     by user request or at framebuffer creation.
  *     continuous memory region allocated by user request
  *     or at framebuffer creation.
- * @size: total memory size to physically non-continuous memory region.
+ * @size: size requested from user, in bytes and this size is aligned
+ *     in page unit.
  * @flags: indicate memory type to allocated buffer and cache attruibute.
  *
  * P.S. this object would be transfered to user as kms_bo.handle so
index c4c6525d46532392414902079ec5aa18efdedfbc..b89829e5043a59a67152c5cafc241265f0183f12 100644 (file)
 #include "drmP.h"
 
 #include "exynos_drm.h"
-#include "exynos_drm_crtc.h"
 #include "exynos_drm_drv.h"
 #include "exynos_drm_encoder.h"
+#include "exynos_drm_fb.h"
+#include "exynos_drm_gem.h"
+
+#define to_exynos_plane(x)     container_of(x, struct exynos_plane, base)
 
 struct exynos_plane {
        struct drm_plane                base;
@@ -30,6 +33,108 @@ static const uint32_t formats[] = {
        DRM_FORMAT_NV12MT,
 };
 
+int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
+                         struct drm_framebuffer *fb, int crtc_x, int crtc_y,
+                         unsigned int crtc_w, unsigned int crtc_h,
+                         uint32_t src_x, uint32_t src_y,
+                         uint32_t src_w, uint32_t src_h)
+{
+       struct exynos_plane *exynos_plane = to_exynos_plane(plane);
+       struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
+       unsigned int actual_w;
+       unsigned int actual_h;
+       int nr;
+       int i;
+
+       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+       nr = exynos_drm_format_num_buffers(fb->pixel_format);
+       for (i = 0; i < nr; i++) {
+               struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i);
+
+               if (!buffer) {
+                       DRM_LOG_KMS("buffer is null\n");
+                       return -EFAULT;
+               }
+
+               overlay->dma_addr[i] = buffer->dma_addr;
+               overlay->vaddr[i] = buffer->kvaddr;
+
+               DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n",
+                               i, (unsigned long)overlay->vaddr[i],
+                               (unsigned long)overlay->dma_addr[i]);
+       }
+
+       actual_w = min((unsigned)(crtc->mode.hdisplay - crtc_x), crtc_w);
+       actual_h = min((unsigned)(crtc->mode.vdisplay - crtc_y), crtc_h);
+
+       /* set drm framebuffer data. */
+       overlay->fb_x = src_x;
+       overlay->fb_y = src_y;
+       overlay->fb_width = fb->width;
+       overlay->fb_height = fb->height;
+       overlay->src_width = src_w;
+       overlay->src_height = src_h;
+       overlay->bpp = fb->bits_per_pixel;
+       overlay->pitch = fb->pitches[0];
+       overlay->pixel_format = fb->pixel_format;
+
+       /* set overlay range to be displayed. */
+       overlay->crtc_x = crtc_x;
+       overlay->crtc_y = crtc_y;
+       overlay->crtc_width = actual_w;
+       overlay->crtc_height = actual_h;
+
+       /* set drm mode data. */
+       overlay->mode_width = crtc->mode.hdisplay;
+       overlay->mode_height = crtc->mode.vdisplay;
+       overlay->refresh = crtc->mode.vrefresh;
+       overlay->scan_flag = crtc->mode.flags;
+
+       DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)",
+                       overlay->crtc_x, overlay->crtc_y,
+                       overlay->crtc_width, overlay->crtc_height);
+
+       exynos_drm_fn_encoder(crtc, overlay, exynos_drm_encoder_plane_mode_set);
+
+       return 0;
+}
+
+void exynos_plane_commit(struct drm_plane *plane)
+{
+       struct exynos_plane *exynos_plane = to_exynos_plane(plane);
+       struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
+
+       exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
+                       exynos_drm_encoder_plane_commit);
+}
+
+void exynos_plane_dpms(struct drm_plane *plane, int mode)
+{
+       struct exynos_plane *exynos_plane = to_exynos_plane(plane);
+       struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
+
+       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+       if (mode == DRM_MODE_DPMS_ON) {
+               if (exynos_plane->enabled)
+                       return;
+
+               exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
+                               exynos_drm_encoder_plane_enable);
+
+               exynos_plane->enabled = true;
+       } else {
+               if (!exynos_plane->enabled)
+                       return;
+
+               exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
+                               exynos_drm_encoder_plane_disable);
+
+               exynos_plane->enabled = false;
+       }
+}
+
 static int
 exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
                     struct drm_framebuffer *fb, int crtc_x, int crtc_y,
@@ -37,64 +142,37 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
                     uint32_t src_x, uint32_t src_y,
                     uint32_t src_w, uint32_t src_h)
 {
-       struct exynos_plane *exynos_plane =
-               container_of(plane, struct exynos_plane, base);
-       struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
-       struct exynos_drm_crtc_pos pos;
        int ret;
 
        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
-       memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos));
-       pos.crtc_x = crtc_x;
-       pos.crtc_y = crtc_y;
-       pos.crtc_w = crtc_w;
-       pos.crtc_h = crtc_h;
-
-       /* considering 16.16 fixed point of source values */
-       pos.fb_x = src_x >> 16;
-       pos.fb_y = src_y >> 16;
-       pos.src_w = src_w >> 16;
-       pos.src_h = src_h >> 16;
-
-       ret = exynos_drm_overlay_update(overlay, fb, &crtc->mode, &pos);
+       ret = exynos_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y,
+                       crtc_w, crtc_h, src_x >> 16, src_y >> 16,
+                       src_w >> 16, src_h >> 16);
        if (ret < 0)
                return ret;
 
-       exynos_drm_fn_encoder(crtc, overlay,
-                       exynos_drm_encoder_crtc_mode_set);
-       exynos_drm_fn_encoder(crtc, &overlay->zpos,
-                       exynos_drm_encoder_crtc_plane_commit);
+       plane->crtc = crtc;
+       plane->fb = crtc->fb;
 
-       exynos_plane->enabled = true;
+       exynos_plane_commit(plane);
+       exynos_plane_dpms(plane, DRM_MODE_DPMS_ON);
 
        return 0;
 }
 
 static int exynos_disable_plane(struct drm_plane *plane)
 {
-       struct exynos_plane *exynos_plane =
-               container_of(plane, struct exynos_plane, base);
-       struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
-
        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
-       if (!exynos_plane->enabled)
-               return 0;
-
-       exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
-                       exynos_drm_encoder_crtc_disable);
-
-       exynos_plane->enabled = false;
-       exynos_plane->overlay.zpos = DEFAULT_ZPOS;
+       exynos_plane_dpms(plane, DRM_MODE_DPMS_OFF);
 
        return 0;
 }
 
 static void exynos_plane_destroy(struct drm_plane *plane)
 {
-       struct exynos_plane *exynos_plane =
-               container_of(plane, struct exynos_plane, base);
+       struct exynos_plane *exynos_plane = to_exynos_plane(plane);
 
        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
@@ -103,69 +181,79 @@ static void exynos_plane_destroy(struct drm_plane *plane)
        kfree(exynos_plane);
 }
 
+static int exynos_plane_set_property(struct drm_plane *plane,
+                                    struct drm_property *property,
+                                    uint64_t val)
+{
+       struct drm_device *dev = plane->dev;
+       struct exynos_plane *exynos_plane = to_exynos_plane(plane);
+       struct exynos_drm_private *dev_priv = dev->dev_private;
+
+       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+       if (property == dev_priv->plane_zpos_property) {
+               exynos_plane->overlay.zpos = val;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
 static struct drm_plane_funcs exynos_plane_funcs = {
        .update_plane   = exynos_update_plane,
        .disable_plane  = exynos_disable_plane,
        .destroy        = exynos_plane_destroy,
+       .set_property   = exynos_plane_set_property,
 };
 
-int exynos_plane_init(struct drm_device *dev, unsigned int nr)
+static void exynos_plane_attach_zpos_property(struct drm_plane *plane)
 {
-       struct exynos_plane *exynos_plane;
-       uint32_t possible_crtcs;
+       struct drm_device *dev = plane->dev;
+       struct exynos_drm_private *dev_priv = dev->dev_private;
+       struct drm_property *prop;
 
-       exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL);
-       if (!exynos_plane)
-               return -ENOMEM;
+       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
-       /* all CRTCs are available */
-       possible_crtcs = (1 << MAX_CRTC) - 1;
+       prop = dev_priv->plane_zpos_property;
+       if (!prop) {
+               prop = drm_property_create_range(dev, 0, "zpos", 0,
+                                                MAX_PLANE - 1);
+               if (!prop)
+                       return;
 
-       exynos_plane->overlay.zpos = DEFAULT_ZPOS;
+               dev_priv->plane_zpos_property = prop;
+       }
 
-       return drm_plane_init(dev, &exynos_plane->base, possible_crtcs,
-                             &exynos_plane_funcs, formats, ARRAY_SIZE(formats),
-                             false);
+       drm_object_attach_property(&plane->base, prop, 0);
 }
 
-int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data,
-                               struct drm_file *file_priv)
+struct drm_plane *exynos_plane_init(struct drm_device *dev,
+                                   unsigned int possible_crtcs, bool priv)
 {
-       struct drm_exynos_plane_set_zpos *zpos_req = data;
-       struct drm_mode_object *obj;
-       struct drm_plane *plane;
        struct exynos_plane *exynos_plane;
-       int ret = 0;
+       int err;
 
        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       if (zpos_req->zpos < 0 || zpos_req->zpos >= MAX_PLANE) {
-               if (zpos_req->zpos != DEFAULT_ZPOS) {
-                       DRM_ERROR("zpos not within limits\n");
-                       return -EINVAL;
-               }
+       exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL);
+       if (!exynos_plane) {
+               DRM_ERROR("failed to allocate plane\n");
+               return NULL;
        }
 
-       mutex_lock(&dev->mode_config.mutex);
-
-       obj = drm_mode_object_find(dev, zpos_req->plane_id,
-                       DRM_MODE_OBJECT_PLANE);
-       if (!obj) {
-               DRM_DEBUG_KMS("Unknown plane ID %d\n",
-                             zpos_req->plane_id);
-               ret = -EINVAL;
-               goto out;
+       err = drm_plane_init(dev, &exynos_plane->base, possible_crtcs,
+                             &exynos_plane_funcs, formats, ARRAY_SIZE(formats),
+                             priv);
+       if (err) {
+               DRM_ERROR("failed to initialize plane\n");
+               kfree(exynos_plane);
+               return NULL;
        }
 
-       plane = obj_to_plane(obj);
-       exynos_plane = container_of(plane, struct exynos_plane, base);
-
-       exynos_plane->overlay.zpos = zpos_req->zpos;
+       if (priv)
+               exynos_plane->overlay.zpos = DEFAULT_ZPOS;
+       else
+               exynos_plane_attach_zpos_property(&exynos_plane->base);
 
-out:
-       mutex_unlock(&dev->mode_config.mutex);
-       return ret;
+       return &exynos_plane->base;
 }
index 16b71f8217e7c121a083adf673859c6447e115cb..88312458580d459946cd67a1f794a13929254374 100644 (file)
@@ -9,6 +9,12 @@
  *
  */
 
-int exynos_plane_init(struct drm_device *dev, unsigned int nr);
-int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data,
-                               struct drm_file *file_priv);
+int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
+                         struct drm_framebuffer *fb, int crtc_x, int crtc_y,
+                         unsigned int crtc_w, unsigned int crtc_h,
+                         uint32_t src_x, uint32_t src_y,
+                         uint32_t src_w, uint32_t src_h);
+void exynos_plane_commit(struct drm_plane *plane);
+void exynos_plane_dpms(struct drm_plane *plane, int mode);
+struct drm_plane *exynos_plane_init(struct drm_device *dev,
+                                   unsigned int possible_crtcs, bool priv);
index 7b9c153dceb610776f59711d4c5c3c303ff21228..bb1550c4dd57db37554954537ef19c61d4e513ce 100644 (file)
@@ -85,8 +85,6 @@ static const char fake_edid_info[] = {
        0x00, 0x00, 0x00, 0x06
 };
 
-static void vidi_fake_vblank_handler(struct work_struct *work);
-
 static bool vidi_display_is_connected(struct device *dev)
 {
        struct vidi_context *ctx = get_vidi_context(dev);
@@ -531,6 +529,16 @@ static int vidi_store_connection(struct device *dev,
        if (ctx->connected > 1)
                return -EINVAL;
 
+       /* use fake edid data for test. */
+       if (!ctx->raw_edid)
+               ctx->raw_edid = (struct edid *)fake_edid_info;
+
+       /* if raw_edid isn't same as fake data then it can't be tested. */
+       if (ctx->raw_edid != (struct edid *)fake_edid_info) {
+               DRM_DEBUG_KMS("edid data is not fake data.\n");
+               return -EINVAL;
+       }
+
        DRM_DEBUG_KMS("requested connection.\n");
 
        drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
@@ -549,6 +557,8 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
        struct exynos_drm_manager *manager;
        struct exynos_drm_display_ops *display_ops;
        struct drm_exynos_vidi_connection *vidi = data;
+       struct edid *raw_edid;
+       int edid_len;
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
@@ -557,11 +567,6 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
                return -EINVAL;
        }
 
-       if (!vidi->edid) {
-               DRM_DEBUG_KMS("edid data is null.\n");
-               return -EINVAL;
-       }
-
        if (vidi->connection > 1) {
                DRM_DEBUG_KMS("connection should be 0 or 1.\n");
                return -EINVAL;
@@ -588,8 +593,30 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
                return -EINVAL;
        }
 
-       if (vidi->connection)
-               ctx->raw_edid = (struct edid *)vidi->edid;
+       if (vidi->connection) {
+               if (!vidi->edid) {
+                       DRM_DEBUG_KMS("edid data is null.\n");
+                       return -EINVAL;
+               }
+               raw_edid = (struct edid *)(uint32_t)vidi->edid;
+               edid_len = (1 + raw_edid->extensions) * EDID_LENGTH;
+               ctx->raw_edid = kzalloc(edid_len, GFP_KERNEL);
+               if (!ctx->raw_edid) {
+                       DRM_DEBUG_KMS("failed to allocate raw_edid.\n");
+                       return -ENOMEM;
+               }
+               memcpy(ctx->raw_edid, raw_edid, edid_len);
+       } else {
+               /*
+                * with connection = 0, free raw_edid
+                * only if raw edid data isn't same as fake data.
+                */
+               if (ctx->raw_edid && ctx->raw_edid !=
+                               (struct edid *)fake_edid_info) {
+                       kfree(ctx->raw_edid);
+                       ctx->raw_edid = NULL;
+               }
+       }
 
        ctx->connected = vidi->connection;
        drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
@@ -614,9 +641,6 @@ static int __devinit vidi_probe(struct platform_device *pdev)
 
        INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
 
-       /* for test */
-       ctx->raw_edid = (struct edid *)fake_edid_info;
-
        subdrv = &ctx->subdrv;
        subdrv->dev = dev;
        subdrv->manager = &vidi_manager;
@@ -644,6 +668,11 @@ static int __devexit vidi_remove(struct platform_device *pdev)
 
        exynos_drm_subdrv_unregister(&ctx->subdrv);
 
+       if (ctx->raw_edid != (struct edid *)fake_edid_info) {
+               kfree(ctx->raw_edid);
+               ctx->raw_edid = NULL;
+       }
+
        kfree(ctx);
 
        return 0;
index 066bde3f19c4fb97642b178c27b072519a4c1b10..409e2ec1207c3ddbe4ecae65f9fae1498fcd6410 100644 (file)
@@ -63,7 +63,6 @@ struct hdmi_context {
        bool                            dvi_mode;
        struct mutex                    hdmi_mutex;
 
-       struct resource                 *regs_res;
        void __iomem                    *regs;
        unsigned int                    external_irq;
        unsigned int                    internal_irq;
@@ -2280,16 +2279,17 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL);
+       drm_hdmi_ctx = devm_kzalloc(&pdev->dev, sizeof(*drm_hdmi_ctx),
+                                                               GFP_KERNEL);
        if (!drm_hdmi_ctx) {
                DRM_ERROR("failed to allocate common hdmi context.\n");
                return -ENOMEM;
        }
 
-       hdata = kzalloc(sizeof(struct hdmi_context), GFP_KERNEL);
+       hdata = devm_kzalloc(&pdev->dev, sizeof(struct hdmi_context),
+                                                               GFP_KERNEL);
        if (!hdata) {
                DRM_ERROR("out of memory\n");
-               kfree(drm_hdmi_ctx);
                return -ENOMEM;
        }
 
@@ -2318,26 +2318,18 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
                goto err_resource;
        }
 
-       hdata->regs_res = request_mem_region(res->start, resource_size(res),
-                                          dev_name(dev));
-       if (!hdata->regs_res) {
-               DRM_ERROR("failed to claim register region\n");
-               ret = -ENOENT;
-               goto err_resource;
-       }
-
-       hdata->regs = ioremap(res->start, resource_size(res));
+       hdata->regs = devm_request_and_ioremap(&pdev->dev, res);
        if (!hdata->regs) {
                DRM_ERROR("failed to map registers\n");
                ret = -ENXIO;
-               goto err_req_region;
+               goto err_resource;
        }
 
        /* DDC i2c driver */
        if (i2c_add_driver(&ddc_driver)) {
                DRM_ERROR("failed to register ddc i2c driver\n");
                ret = -ENOENT;
-               goto err_iomap;
+               goto err_resource;
        }
 
        hdata->ddc_port = hdmi_ddc;
@@ -2398,16 +2390,9 @@ err_hdmiphy:
        i2c_del_driver(&hdmiphy_driver);
 err_ddc:
        i2c_del_driver(&ddc_driver);
-err_iomap:
-       iounmap(hdata->regs);
-err_req_region:
-       release_mem_region(hdata->regs_res->start,
-                       resource_size(hdata->regs_res));
 err_resource:
        hdmi_resources_cleanup(hdata);
 err_data:
-       kfree(hdata);
-       kfree(drm_hdmi_ctx);
        return ret;
 }
 
@@ -2425,18 +2410,11 @@ static int __devexit hdmi_remove(struct platform_device *pdev)
 
        hdmi_resources_cleanup(hdata);
 
-       iounmap(hdata->regs);
-
-       release_mem_region(hdata->regs_res->start,
-                       resource_size(hdata->regs_res));
-
        /* hdmiphy i2c driver */
        i2c_del_driver(&hdmiphy_driver);
        /* DDC i2c driver */
        i2c_del_driver(&ddc_driver);
 
-       kfree(hdata);
-
        return 0;
 }
 
index e2147a2ddcecab72570db6f3055aabdb18fe85ea..30fcc12f81dd943802031936dcfc9ea81b8f1548 100644 (file)
@@ -956,7 +956,8 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
 
        clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
 
-       mixer_res->mixer_regs = ioremap(res->start, resource_size(res));
+       mixer_res->mixer_regs = devm_ioremap(&pdev->dev, res->start,
+                                                       resource_size(res));
        if (mixer_res->mixer_regs == NULL) {
                dev_err(dev, "register mapping failed.\n");
                ret = -ENXIO;
@@ -967,38 +968,34 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
        if (res == NULL) {
                dev_err(dev, "get memory resource failed.\n");
                ret = -ENXIO;
-               goto fail_mixer_regs;
+               goto fail;
        }
 
-       mixer_res->vp_regs = ioremap(res->start, resource_size(res));
+       mixer_res->vp_regs = devm_ioremap(&pdev->dev, res->start,
+                                                       resource_size(res));
        if (mixer_res->vp_regs == NULL) {
                dev_err(dev, "register mapping failed.\n");
                ret = -ENXIO;
-               goto fail_mixer_regs;
+               goto fail;
        }
 
        res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq");
        if (res == NULL) {
                dev_err(dev, "get interrupt resource failed.\n");
                ret = -ENXIO;
-               goto fail_vp_regs;
+               goto fail;
        }
 
-       ret = request_irq(res->start, mixer_irq_handler, 0, "drm_mixer", ctx);
+       ret = devm_request_irq(&pdev->dev, res->start, mixer_irq_handler,
+                                                       0, "drm_mixer", ctx);
        if (ret) {
                dev_err(dev, "request interrupt failed.\n");
-               goto fail_vp_regs;
+               goto fail;
        }
        mixer_res->irq = res->start;
 
        return 0;
 
-fail_vp_regs:
-       iounmap(mixer_res->vp_regs);
-
-fail_mixer_regs:
-       iounmap(mixer_res->mixer_regs);
-
 fail:
        if (!IS_ERR_OR_NULL(mixer_res->sclk_dac))
                clk_put(mixer_res->sclk_dac);
@@ -1013,16 +1010,6 @@ fail:
        return ret;
 }
 
-static void mixer_resources_cleanup(struct mixer_context *ctx)
-{
-       struct mixer_resources *res = &ctx->mixer_res;
-
-       free_irq(res->irq, ctx);
-
-       iounmap(res->vp_regs);
-       iounmap(res->mixer_regs);
-}
-
 static int __devinit mixer_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -1032,16 +1019,16 @@ static int __devinit mixer_probe(struct platform_device *pdev)
 
        dev_info(dev, "probe start\n");
 
-       drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL);
+       drm_hdmi_ctx = devm_kzalloc(&pdev->dev, sizeof(*drm_hdmi_ctx),
+                                                               GFP_KERNEL);
        if (!drm_hdmi_ctx) {
                DRM_ERROR("failed to allocate common hdmi context.\n");
                return -ENOMEM;
        }
 
-       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
        if (!ctx) {
                DRM_ERROR("failed to alloc mixer context.\n");
-               kfree(drm_hdmi_ctx);
                return -ENOMEM;
        }
 
@@ -1072,17 +1059,10 @@ fail:
 
 static int mixer_remove(struct platform_device *pdev)
 {
-       struct device *dev = &pdev->dev;
-       struct exynos_drm_hdmi_context *drm_hdmi_ctx =
-                                       platform_get_drvdata(pdev);
-       struct mixer_context *ctx = drm_hdmi_ctx->ctx;
-
-       dev_info(dev, "remove successful\n");
+       dev_info(&pdev->dev, "remove successful\n");
 
        pm_runtime_disable(&pdev->dev);
 
-       mixer_resources_cleanup(ctx);
-
        return 0;
 }
 
index 500844f04f93abb668f7ce57b0d841f776c074c7..60ea284407cea4d1f62db43a72b3e6cdbea47e31 100644 (file)
@@ -1928,6 +1928,7 @@ static const struct hid_device_id hid_ignore_list[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_RADIOSHARK) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101) },
index 41c34f21bd001ee00864c596bb217db4647bde7c..1dcb76ff51e340a2215d735bdde23e5c370bade0 100644 (file)
 #define USB_VENDOR_ID_GRIFFIN          0x077d
 #define USB_DEVICE_ID_POWERMATE                0x0410
 #define USB_DEVICE_ID_SOUNDKNOB                0x04AA
+#define USB_DEVICE_ID_RADIOSHARK       0x627a
 
 #define USB_VENDOR_ID_GTCO             0x078c
 #define USB_DEVICE_ID_GTCO_90          0x0090
index a220e5746d67df8922f7421b2ee0b174d6ae949b..4748086eaaf26ad41bd3b2fb0c7e40be4a34e054 100644 (file)
@@ -545,8 +545,7 @@ static int vmbus_bus_init(int irq)
        if (ret)
                goto err_cleanup;
 
-       ret = request_irq(irq, vmbus_isr, IRQF_SAMPLE_RANDOM,
-                       driver_name, hv_acpi_dev);
+       ret = request_irq(irq, vmbus_isr, 0, driver_name, hv_acpi_dev);
 
        if (ret != 0) {
                pr_err("Unable to request IRQ %d\n",
index 563c02904ddf0ab9704d76f2ab78ae6e3e364cb0..23ab3c496b0505bb1ed3c2f0e43dfe2ef1a139a8 100644 (file)
@@ -927,6 +927,8 @@ static int acpi_power_meter_remove(struct acpi_device *device, int type)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+
 static int acpi_power_meter_resume(struct device *dev)
 {
        struct acpi_power_meter_resource *resource;
@@ -944,6 +946,8 @@ static int acpi_power_meter_resume(struct device *dev)
        return 0;
 }
 
+#endif /* CONFIG_PM_SLEEP */
+
 static SIMPLE_DEV_PM_OPS(acpi_power_meter_pm, NULL, acpi_power_meter_resume);
 
 static struct acpi_driver acpi_power_meter_driver = {
index 4d937a18fadb5cd6cc07bcbe8e4c32aadfb37ee0..282708860517e8de41e2f86c3aeb55a118aaca17 100644 (file)
@@ -55,9 +55,9 @@
 
 /* wait up to 32 ms for a status change. */
 #define APPLESMC_MIN_WAIT      0x0010
+#define APPLESMC_RETRY_WAIT    0x0100
 #define APPLESMC_MAX_WAIT      0x8000
 
-#define APPLESMC_STATUS_MASK   0x0f
 #define APPLESMC_READ_CMD      0x10
 #define APPLESMC_WRITE_CMD     0x11
 #define APPLESMC_GET_KEY_BY_INDEX_CMD  0x12
@@ -162,51 +162,68 @@ static unsigned int key_at_index;
 static struct workqueue_struct *applesmc_led_wq;
 
 /*
- * __wait_status - Wait up to 32ms for the status port to get a certain value
- * (masked with 0x0f), returning zero if the value is obtained.  Callers must
+ * wait_read - Wait for a byte to appear on SMC port. Callers must
  * hold applesmc_lock.
  */
-static int __wait_status(u8 val)
+static int wait_read(void)
 {
+       u8 status;
        int us;
-
-       val = val & APPLESMC_STATUS_MASK;
-
        for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
                udelay(us);
-               if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val)
+               status = inb(APPLESMC_CMD_PORT);
+               /* read: wait for smc to settle */
+               if (status & 0x01)
                        return 0;
        }
 
+       pr_warn("wait_read() fail: 0x%02x\n", status);
        return -EIO;
 }
 
 /*
- * special treatment of command port - on newer macbooks, it seems necessary
- * to resend the command byte before polling the status again. Callers must
- * hold applesmc_lock.
+ * send_byte - Write to SMC port, retrying when necessary. Callers
+ * must hold applesmc_lock.
  */
-static int send_command(u8 cmd)
+static int send_byte(u8 cmd, u16 port)
 {
+       u8 status;
        int us;
+
+       outb(cmd, port);
        for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
-               outb(cmd, APPLESMC_CMD_PORT);
                udelay(us);
-               if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c)
+               status = inb(APPLESMC_CMD_PORT);
+               /* write: wait for smc to settle */
+               if (status & 0x02)
+                       continue;
+               /* ready: cmd accepted, return */
+               if (status & 0x04)
                        return 0;
+               /* timeout: give up */
+               if (us << 1 == APPLESMC_MAX_WAIT)
+                       break;
+               /* busy: long wait and resend */
+               udelay(APPLESMC_RETRY_WAIT);
+               outb(cmd, port);
        }
+
+       pr_warn("send_byte(0x%02x, 0x%04x) fail: 0x%02x\n", cmd, port, status);
        return -EIO;
 }
 
+static int send_command(u8 cmd)
+{
+       return send_byte(cmd, APPLESMC_CMD_PORT);
+}
+
 static int send_argument(const char *key)
 {
        int i;
 
-       for (i = 0; i < 4; i++) {
-               outb(key[i], APPLESMC_DATA_PORT);
-               if (__wait_status(0x04))
+       for (i = 0; i < 4; i++)
+               if (send_byte(key[i], APPLESMC_DATA_PORT))
                        return -EIO;
-       }
        return 0;
 }
 
@@ -219,11 +236,14 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
                return -EIO;
        }
 
-       outb(len, APPLESMC_DATA_PORT);
+       if (send_byte(len, APPLESMC_DATA_PORT)) {
+               pr_warn("%.4s: read len fail\n", key);
+               return -EIO;
+       }
 
        for (i = 0; i < len; i++) {
-               if (__wait_status(0x05)) {
-                       pr_warn("%.4s: read data fail\n", key);
+               if (wait_read()) {
+                       pr_warn("%.4s: read data[%d] fail\n", key, i);
                        return -EIO;
                }
                buffer[i] = inb(APPLESMC_DATA_PORT);
@@ -241,14 +261,16 @@ static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len)
                return -EIO;
        }
 
-       outb(len, APPLESMC_DATA_PORT);
+       if (send_byte(len, APPLESMC_DATA_PORT)) {
+               pr_warn("%.4s: write len fail\n", key);
+               return -EIO;
+       }
 
        for (i = 0; i < len; i++) {
-               if (__wait_status(0x04)) {
+               if (send_byte(buffer[i], APPLESMC_DATA_PORT)) {
                        pr_warn("%s: write data fail\n", key);
                        return -EIO;
                }
-               outb(buffer[i], APPLESMC_DATA_PORT);
        }
 
        return 0;
index 637c51c11b44ca774057404b6e5326a4ba1de31d..faa16f80db9cf652bbc75aaa7e5fd0e73b788ca4 100644 (file)
@@ -793,7 +793,7 @@ static struct notifier_block coretemp_cpu_notifier __refdata = {
        .notifier_call = coretemp_cpu_callback,
 };
 
-static const struct x86_cpu_id coretemp_ids[] = {
+static const struct x86_cpu_id __initconst coretemp_ids[] = {
        { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTHERM },
        {}
 };
index e72ba5d2a8248e04ebf4bf65662007e57b960ca3..e21e43c13156911989267fb555d24e5b98ddb67e 100644 (file)
@@ -57,7 +57,7 @@ static const unsigned short normal_i2c[] = {
 #define JC42_CFG_EVENT_LOCK    (1 << 7)
 #define JC42_CFG_SHUTDOWN      (1 << 8)
 #define JC42_CFG_HYST_SHIFT    9
-#define JC42_CFG_HYST_MASK     0x03
+#define JC42_CFG_HYST_MASK     (0x03 << 9)
 
 /* Capabilities */
 #define JC42_CAP_RANGE         (1 << 2)
@@ -287,8 +287,8 @@ static ssize_t show_temp_crit_hyst(struct device *dev,
                return PTR_ERR(data);
 
        temp = jc42_temp_from_reg(data->temp_crit);
-       hyst = jc42_hysteresis[(data->config >> JC42_CFG_HYST_SHIFT)
-                              & JC42_CFG_HYST_MASK];
+       hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
+                              >> JC42_CFG_HYST_SHIFT];
        return sprintf(buf, "%d\n", temp - hyst);
 }
 
@@ -302,8 +302,8 @@ static ssize_t show_temp_max_hyst(struct device *dev,
                return PTR_ERR(data);
 
        temp = jc42_temp_from_reg(data->temp_max);
-       hyst = jc42_hysteresis[(data->config >> JC42_CFG_HYST_SHIFT)
-                              & JC42_CFG_HYST_MASK];
+       hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
+                              >> JC42_CFG_HYST_SHIFT];
        return sprintf(buf, "%d\n", temp - hyst);
 }
 
@@ -362,8 +362,7 @@ static ssize_t set_temp_crit_hyst(struct device *dev,
        }
 
        mutex_lock(&data->update_lock);
-       data->config = (data->config
-                       & ~(JC42_CFG_HYST_MASK << JC42_CFG_HYST_SHIFT))
+       data->config = (data->config & ~JC42_CFG_HYST_MASK)
          | (hyst << JC42_CFG_HYST_SHIFT);
        err = i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG,
                                           data->config);
@@ -535,9 +534,16 @@ static int jc42_remove(struct i2c_client *client)
        struct jc42_data *data = i2c_get_clientdata(client);
        hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &jc42_group);
-       if (data->config != data->orig_config)
-               i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG,
-                                            data->orig_config);
+
+       /* Restore original configuration except hysteresis */
+       if ((data->config & ~JC42_CFG_HYST_MASK) !=
+           (data->orig_config & ~JC42_CFG_HYST_MASK)) {
+               int config;
+
+               config = (data->orig_config & ~JC42_CFG_HYST_MASK)
+                 | (data->config & JC42_CFG_HYST_MASK);
+               i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, config);
+       }
        return 0;
 }
 
index 8689664ef03cd86866aae1d868aacc55e10a95d9..ee4ebc198a9448e868f45af079e8f3d5f51968ae 100644 (file)
@@ -309,7 +309,7 @@ static struct notifier_block via_cputemp_cpu_notifier __refdata = {
        .notifier_call = via_cputemp_cpu_callback,
 };
 
-static const struct x86_cpu_id cputemp_ids[] = {
+static const struct x86_cpu_id __initconst cputemp_ids[] = {
        { X86_VENDOR_CENTAUR, 6, 0xa, }, /* C7 A */
        { X86_VENDOR_CENTAUR, 6, 0xd, }, /* C7 D */
        { X86_VENDOR_CENTAUR, 6, 0xf, }, /* Nano */
index 2e7530a4e7b8cdb37cc47cedef24a0da3d5cec12..b4aaa1bd6728503b629acea078f3021fc148115a 100644 (file)
@@ -462,7 +462,7 @@ config I2C_MPC
 
 config I2C_MV64XXX
        tristate "Marvell mv64xxx I2C Controller"
-       depends on (MV64X60 || PLAT_ORION) && EXPERIMENTAL
+       depends on (MV64X60 || PLAT_ORION)
        help
          If you say yes to this option, support will be included for the
          built-in I2C interface on the Marvell 64xxx line of host bridges.
@@ -483,10 +483,11 @@ config I2C_MXS
 
 config I2C_NOMADIK
        tristate "ST-Ericsson Nomadik/Ux500 I2C Controller"
-       depends on PLAT_NOMADIK
+       depends on ARM_AMBA
        help
          If you say yes to this option, support will be included for the
-         I2C interface from ST-Ericsson's Nomadik and Ux500 architectures.
+         I2C interface from ST-Ericsson's Nomadik and Ux500 architectures,
+         as well as the STA2X11 PCIe I/O HUB.
 
 config I2C_NUC900
        tristate "NUC900 I2C Driver"
index 1679deef9c890131a951ee5cb8d50217ecb9bcf5..e24484beef078d9e3069b32066cd9c865ed25f22 100644 (file)
@@ -279,30 +279,31 @@ static int __devexit at91_i2c_remove(struct platform_device *pdev)
 
 /* NOTE: could save a few mA by keeping clock off outside of at91_xfer... */
 
-static int at91_i2c_suspend(struct platform_device *pdev, pm_message_t mesg)
+static int at91_i2c_suspend(struct device *dev)
 {
        clk_disable(twi_clk);
        return 0;
 }
 
-static int at91_i2c_resume(struct platform_device *pdev)
+static int at91_i2c_resume(struct device *dev)
 {
        return clk_enable(twi_clk);
 }
 
+static SIMPLE_DEV_PM_OPS(at91_i2c_pm, at91_i2c_suspend, at91_i2c_resume);
+#define AT91_I2C_PM    (&at91_i2c_pm)
+
 #else
-#define at91_i2c_suspend       NULL
-#define at91_i2c_resume                NULL
+#define AT91_I2C_PM    NULL
 #endif
 
 static struct platform_driver at91_i2c_driver = {
        .probe          = at91_i2c_probe,
        .remove         = __devexit_p(at91_i2c_remove),
-       .suspend        = at91_i2c_suspend,
-       .resume         = at91_i2c_resume,
        .driver         = {
                .name   = "at91_i2c",
                .owner  = THIS_MODULE,
+               .pm     = AT91_I2C_PM,
        },
 };
 
index cdb59e5b23f749aa81153eb6619269d4f248eb61..0cf780fd6ef12578b6640b82abb952e4d0d3585e 100644 (file)
@@ -25,6 +25,7 @@
 #include <asm/blackfin.h>
 #include <asm/portmux.h>
 #include <asm/irq.h>
+#include <asm/bfin_twi.h>
 
 /* SMBus mode*/
 #define TWI_I2C_MODE_STANDARD          1
 #define TWI_I2C_MODE_COMBINED          3
 #define TWI_I2C_MODE_REPEAT            4
 
-struct bfin_twi_iface {
-       int                     irq;
-       spinlock_t              lock;
-       char                    read_write;
-       u8                      command;
-       u8                      *transPtr;
-       int                     readNum;
-       int                     writeNum;
-       int                     cur_mode;
-       int                     manual_stop;
-       int                     result;
-       struct i2c_adapter      adap;
-       struct completion       complete;
-       struct i2c_msg          *pmsg;
-       int                     msg_num;
-       int                     cur_msg;
-       u16                     saved_clkdiv;
-       u16                     saved_control;
-       void __iomem            *regs_base;
-};
-
-
-#define DEFINE_TWI_REG(reg, off) \
-static inline u16 read_##reg(struct bfin_twi_iface *iface) \
-       { return bfin_read16(iface->regs_base + (off)); } \
-static inline void write_##reg(struct bfin_twi_iface *iface, u16 v) \
-       { bfin_write16(iface->regs_base + (off), v); }
-
-DEFINE_TWI_REG(CLKDIV, 0x00)
-DEFINE_TWI_REG(CONTROL, 0x04)
-DEFINE_TWI_REG(SLAVE_CTL, 0x08)
-DEFINE_TWI_REG(SLAVE_STAT, 0x0C)
-DEFINE_TWI_REG(SLAVE_ADDR, 0x10)
-DEFINE_TWI_REG(MASTER_CTL, 0x14)
-DEFINE_TWI_REG(MASTER_STAT, 0x18)
-DEFINE_TWI_REG(MASTER_ADDR, 0x1C)
-DEFINE_TWI_REG(INT_STAT, 0x20)
-DEFINE_TWI_REG(INT_MASK, 0x24)
-DEFINE_TWI_REG(FIFO_CTL, 0x28)
-DEFINE_TWI_REG(FIFO_STAT, 0x2C)
-DEFINE_TWI_REG(XMT_DATA8, 0x80)
-DEFINE_TWI_REG(XMT_DATA16, 0x84)
-DEFINE_TWI_REG(RCV_DATA8, 0x88)
-DEFINE_TWI_REG(RCV_DATA16, 0x8C)
-
-static const u16 pin_req[2][3] = {
-       {P_TWI0_SCL, P_TWI0_SDA, 0},
-       {P_TWI1_SCL, P_TWI1_SDA, 0},
-};
-
 static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
                                        unsigned short twi_int_status)
 {
@@ -99,7 +50,7 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
                 */
                else if (iface->cur_mode == TWI_I2C_MODE_COMBINED)
                        write_MASTER_CTL(iface,
-                               read_MASTER_CTL(iface) | MDIR | RSTART);
+                               read_MASTER_CTL(iface) | MDIR);
                else if (iface->manual_stop)
                        write_MASTER_CTL(iface,
                                read_MASTER_CTL(iface) | STOP);
@@ -107,10 +58,10 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
                         iface->cur_msg + 1 < iface->msg_num) {
                        if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
                                write_MASTER_CTL(iface,
-                                       read_MASTER_CTL(iface) | RSTART | MDIR);
+                                       read_MASTER_CTL(iface) | MDIR);
                        else
                                write_MASTER_CTL(iface,
-                                       (read_MASTER_CTL(iface) | RSTART) & ~MDIR);
+                                       read_MASTER_CTL(iface) & ~MDIR);
                }
        }
        if (twi_int_status & RCVSERV) {
@@ -130,17 +81,25 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
                        }
                        iface->transPtr++;
                        iface->readNum--;
-               } else if (iface->manual_stop) {
-                       write_MASTER_CTL(iface,
-                               read_MASTER_CTL(iface) | STOP);
-               } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
-                          iface->cur_msg + 1 < iface->msg_num) {
-                       if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
-                               write_MASTER_CTL(iface,
-                                       read_MASTER_CTL(iface) | RSTART | MDIR);
-                       else
+               }
+
+               if (iface->readNum == 0) {
+                       if (iface->manual_stop) {
+                               /* Temporary workaround to avoid possible bus stall -
+                                * Flush FIFO before issuing the STOP condition
+                                */
+                               read_RCV_DATA16(iface);
                                write_MASTER_CTL(iface,
-                                       (read_MASTER_CTL(iface) | RSTART) & ~MDIR);
+                                       read_MASTER_CTL(iface) | STOP);
+                       } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
+                                       iface->cur_msg + 1 < iface->msg_num) {
+                               if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
+                                       write_MASTER_CTL(iface,
+                                               read_MASTER_CTL(iface) | MDIR);
+                               else
+                                       write_MASTER_CTL(iface,
+                                               read_MASTER_CTL(iface) & ~MDIR);
+                       }
                }
        }
        if (twi_int_status & MERR) {
@@ -193,7 +152,8 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
                return;
        }
        if (twi_int_status & MCOMP) {
-               if ((read_MASTER_CTL(iface) & MEN) == 0 &&
+               if (twi_int_status & (XMTSERV | RCVSERV) &&
+                       (read_MASTER_CTL(iface) & MEN) == 0 &&
                        (iface->cur_mode == TWI_I2C_MODE_REPEAT ||
                        iface->cur_mode == TWI_I2C_MODE_COMBINED)) {
                        iface->result = -1;
@@ -221,7 +181,7 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
                        write_MASTER_CTL(iface,
                                read_MASTER_CTL(iface) & ~RSTART);
                } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
-                               iface->cur_msg+1 < iface->msg_num) {
+                               iface->cur_msg + 1 < iface->msg_num) {
                        iface->cur_msg++;
                        iface->transPtr = iface->pmsg[iface->cur_msg].buf;
                        iface->writeNum = iface->readNum =
@@ -241,27 +201,29 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
                                }
                        }
 
-                       if (iface->pmsg[iface->cur_msg].len <= 255)
-                                       write_MASTER_CTL(iface,
+                       if (iface->pmsg[iface->cur_msg].len <= 255) {
+                               write_MASTER_CTL(iface,
                                        (read_MASTER_CTL(iface) &
                                        (~(0xff << 6))) |
-                               (iface->pmsg[iface->cur_msg].len << 6));
-                       else {
+                                       (iface->pmsg[iface->cur_msg].len << 6));
+                               iface->manual_stop = 0;
+                       } else {
                                write_MASTER_CTL(iface,
                                        (read_MASTER_CTL(iface) |
                                        (0xff << 6)));
                                iface->manual_stop = 1;
                        }
-                       /* remove restart bit and enable master receive */
-                       write_MASTER_CTL(iface,
-                               read_MASTER_CTL(iface) & ~RSTART);
+                       /* remove restart bit before last message */
+                       if (iface->cur_msg + 1 == iface->msg_num)
+                               write_MASTER_CTL(iface,
+                                       read_MASTER_CTL(iface) & ~RSTART);
                } else {
                        iface->result = 1;
                        write_INT_MASK(iface, 0);
                        write_MASTER_CTL(iface, 0);
                }
+               complete(&iface->complete);
        }
-       complete(&iface->complete);
 }
 
 /* Interrupt handler */
@@ -298,8 +260,8 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
        if (!(read_CONTROL(iface) & TWI_ENA))
                return -ENXIO;
 
-       while (read_MASTER_STAT(iface) & BUSBUSY)
-               yield();
+       if (read_MASTER_STAT(iface) & BUSBUSY)
+               return -EAGAIN;
 
        iface->pmsg = msgs;
        iface->msg_num = num;
@@ -311,7 +273,8 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
                return -EINVAL;
        }
 
-       iface->cur_mode = TWI_I2C_MODE_REPEAT;
+       if (iface->msg_num > 1)
+               iface->cur_mode = TWI_I2C_MODE_REPEAT;
        iface->manual_stop = 0;
        iface->transPtr = pmsg->buf;
        iface->writeNum = iface->readNum = pmsg->len;
@@ -356,6 +319,7 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
 
        /* Master enable */
        write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
+               (iface->msg_num > 1 ? RSTART : 0) |
                ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
                ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
        SSYNC();
@@ -398,8 +362,8 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
        if (!(read_CONTROL(iface) & TWI_ENA))
                return -ENXIO;
 
-       while (read_MASTER_STAT(iface) & BUSBUSY)
-               yield();
+       if (read_MASTER_STAT(iface) & BUSBUSY)
+               return -EAGAIN;
 
        iface->writeNum = 0;
        iface->readNum = 0;
@@ -520,7 +484,7 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
                else
                        write_MASTER_CTL(iface, 0x1 << 6);
                /* Master enable */
-               write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
+               write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | RSTART |
                        ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
                break;
        default:
@@ -611,9 +575,9 @@ static struct i2c_algorithm bfin_twi_algorithm = {
        .functionality = bfin_twi_functionality,
 };
 
-static int i2c_bfin_twi_suspend(struct platform_device *pdev, pm_message_t state)
+static int i2c_bfin_twi_suspend(struct device *dev)
 {
-       struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
+       struct bfin_twi_iface *iface = dev_get_drvdata(dev);
 
        iface->saved_clkdiv = read_CLKDIV(iface);
        iface->saved_control = read_CONTROL(iface);
@@ -626,14 +590,14 @@ static int i2c_bfin_twi_suspend(struct platform_device *pdev, pm_message_t state
        return 0;
 }
 
-static int i2c_bfin_twi_resume(struct platform_device *pdev)
+static int i2c_bfin_twi_resume(struct device *dev)
 {
-       struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
+       struct bfin_twi_iface *iface = dev_get_drvdata(dev);
 
        int rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
-               0, pdev->name, iface);
+               0, to_platform_device(dev)->name, iface);
        if (rc) {
-               dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq);
+               dev_err(dev, "Can't get IRQ %d !\n", iface->irq);
                return -ENODEV;
        }
 
@@ -646,6 +610,9 @@ static int i2c_bfin_twi_resume(struct platform_device *pdev)
        return 0;
 }
 
+static SIMPLE_DEV_PM_OPS(i2c_bfin_twi_pm,
+                        i2c_bfin_twi_suspend, i2c_bfin_twi_resume);
+
 static int i2c_bfin_twi_probe(struct platform_device *pdev)
 {
        struct bfin_twi_iface *iface;
@@ -695,7 +662,8 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
        p_adap->timeout = 5 * HZ;
        p_adap->retries = 3;
 
-       rc = peripheral_request_list(pin_req[pdev->id], "i2c-bfin-twi");
+       rc = peripheral_request_list((unsigned short *)pdev->dev.platform_data,
+                                       "i2c-bfin-twi");
        if (rc) {
                dev_err(&pdev->dev, "Can't setup pin mux!\n");
                goto out_error_pin_mux;
@@ -742,7 +710,7 @@ out_error_add_adapter:
        free_irq(iface->irq, iface);
 out_error_req_irq:
 out_error_no_irq:
-       peripheral_free_list(pin_req[pdev->id]);
+       peripheral_free_list((unsigned short *)pdev->dev.platform_data);
 out_error_pin_mux:
        iounmap(iface->regs_base);
 out_error_ioremap:
@@ -760,7 +728,7 @@ static int i2c_bfin_twi_remove(struct platform_device *pdev)
 
        i2c_del_adapter(&(iface->adap));
        free_irq(iface->irq, iface);
-       peripheral_free_list(pin_req[pdev->id]);
+       peripheral_free_list((unsigned short *)pdev->dev.platform_data);
        iounmap(iface->regs_base);
        kfree(iface);
 
@@ -770,11 +738,10 @@ static int i2c_bfin_twi_remove(struct platform_device *pdev)
 static struct platform_driver i2c_bfin_twi_driver = {
        .probe          = i2c_bfin_twi_probe,
        .remove         = i2c_bfin_twi_remove,
-       .suspend        = i2c_bfin_twi_suspend,
-       .resume         = i2c_bfin_twi_resume,
        .driver         = {
                .name   = "i2c-bfin-twi",
                .owner  = THIS_MODULE,
+               .pm     = &i2c_bfin_twi_pm,
        },
 };
 
index 370031ac8200d4765606437b1ffc8b61967dac12..0722f869465c3ba6e8904aa13c161abed412dbc9 100644 (file)
@@ -117,10 +117,8 @@ static u16 __initdata i2c_clk_div[50][2] = {
 
 struct imx_i2c_struct {
        struct i2c_adapter      adapter;
-       struct resource         *res;
        struct clk              *clk;
        void __iomem            *base;
-       int                     irq;
        wait_queue_head_t       queue;
        unsigned long           i2csr;
        unsigned int            disable_delay;
@@ -472,9 +470,8 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
        struct imxi2c_platform_data *pdata = pdev->dev.platform_data;
        struct pinctrl *pinctrl;
        void __iomem *base;
-       resource_size_t res_size;
-       int irq, bitrate;
-       int ret;
+       int irq, ret;
+       u32 bitrate;
 
        dev_dbg(&pdev->dev, "<%s>\n", __func__);
 
@@ -489,25 +486,15 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
                return -ENOENT;
        }
 
-       res_size = resource_size(res);
-
-       if (!request_mem_region(res->start, res_size, DRIVER_NAME)) {
-               dev_err(&pdev->dev, "request_mem_region failed\n");
+       base = devm_request_and_ioremap(&pdev->dev, res);
+       if (!base)
                return -EBUSY;
-       }
-
-       base = ioremap(res->start, res_size);
-       if (!base) {
-               dev_err(&pdev->dev, "ioremap failed\n");
-               ret = -EIO;
-               goto fail1;
-       }
 
-       i2c_imx = kzalloc(sizeof(struct imx_i2c_struct), GFP_KERNEL);
+       i2c_imx = devm_kzalloc(&pdev->dev, sizeof(struct imx_i2c_struct),
+                               GFP_KERNEL);
        if (!i2c_imx) {
                dev_err(&pdev->dev, "can't allocate interface\n");
-               ret = -ENOMEM;
-               goto fail2;
+               return -ENOMEM;
        }
 
        /* Setup i2c_imx driver structure */
@@ -517,29 +504,27 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
        i2c_imx->adapter.dev.parent     = &pdev->dev;
        i2c_imx->adapter.nr             = pdev->id;
        i2c_imx->adapter.dev.of_node    = pdev->dev.of_node;
-       i2c_imx->irq                    = irq;
        i2c_imx->base                   = base;
-       i2c_imx->res                    = res;
 
        pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
        if (IS_ERR(pinctrl)) {
-               ret = PTR_ERR(pinctrl);
-               goto fail3;
+               dev_err(&pdev->dev, "can't get/select pinctrl\n");
+               return PTR_ERR(pinctrl);
        }
 
        /* Get I2C clock */
-       i2c_imx->clk = clk_get(&pdev->dev, "i2c_clk");
+       i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(i2c_imx->clk)) {
-               ret = PTR_ERR(i2c_imx->clk);
                dev_err(&pdev->dev, "can't get I2C clock\n");
-               goto fail3;
+               return PTR_ERR(i2c_imx->clk);
        }
 
        /* Request IRQ */
-       ret = request_irq(i2c_imx->irq, i2c_imx_isr, 0, pdev->name, i2c_imx);
+       ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0,
+                               pdev->name, i2c_imx);
        if (ret) {
-               dev_err(&pdev->dev, "can't claim irq %d\n", i2c_imx->irq);
-               goto fail4;
+               dev_err(&pdev->dev, "can't claim irq %d\n", irq);
+               return ret;
        }
 
        /* Init queue */
@@ -564,7 +549,7 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
        ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
        if (ret < 0) {
                dev_err(&pdev->dev, "registration failed\n");
-               goto fail5;
+               return ret;
        }
 
        of_i2c_register_devices(&i2c_imx->adapter);
@@ -572,28 +557,16 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
        /* Set up platform driver data */
        platform_set_drvdata(pdev, i2c_imx);
 
-       dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", i2c_imx->irq);
+       dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
        dev_dbg(&i2c_imx->adapter.dev, "device resources from 0x%x to 0x%x\n",
-               i2c_imx->res->start, i2c_imx->res->end);
-       dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x \n",
-               res_size, i2c_imx->res->start);
+               res->start, res->end);
+       dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x\n",
+               resource_size(res), res->start);
        dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
                i2c_imx->adapter.name);
        dev_dbg(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
 
        return 0;   /* Return OK */
-
-fail5:
-       free_irq(i2c_imx->irq, i2c_imx);
-fail4:
-       clk_put(i2c_imx->clk);
-fail3:
-       kfree(i2c_imx);
-fail2:
-       iounmap(base);
-fail1:
-       release_mem_region(res->start, resource_size(res));
-       return ret; /* Return error number */
 }
 
 static int __exit i2c_imx_remove(struct platform_device *pdev)
@@ -605,20 +578,12 @@ static int __exit i2c_imx_remove(struct platform_device *pdev)
        i2c_del_adapter(&i2c_imx->adapter);
        platform_set_drvdata(pdev, NULL);
 
-       /* free interrupt */
-       free_irq(i2c_imx->irq, i2c_imx);
-
        /* setup chip registers to defaults */
        writeb(0, i2c_imx->base + IMX_I2C_IADR);
        writeb(0, i2c_imx->base + IMX_I2C_IFDR);
        writeb(0, i2c_imx->base + IMX_I2C_I2CR);
        writeb(0, i2c_imx->base + IMX_I2C_I2SR);
 
-       clk_put(i2c_imx->clk);
-
-       iounmap(i2c_imx->base);
-       release_mem_region(i2c_imx->res->start, resource_size(i2c_imx->res));
-       kfree(i2c_imx);
        return 0;
 }
 
index 4f44a33017b061db314e051f9df882d4dee6a9e0..2e9d56719e997e5318246fc93c4240c7dec6c96e 100644 (file)
 #include <linux/mv643xx_i2c.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_i2c.h>
+#include <linux/clk.h>
+#include <linux/err.h>
 
 /* Register defines */
 #define        MV64XXX_I2C_REG_SLAVE_ADDR                      0x00
@@ -98,6 +103,9 @@ struct mv64xxx_i2c_data {
        int                     rc;
        u32                     freq_m;
        u32                     freq_n;
+#if defined(CONFIG_HAVE_CLK)
+       struct clk              *clk;
+#endif
        wait_queue_head_t       waitq;
        spinlock_t              lock;
        struct i2c_msg          *msg;
@@ -521,6 +529,82 @@ mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
        drv_data->reg_base_p = 0;
 }
 
+#ifdef CONFIG_OF
+static int __devinit
+mv64xxx_calc_freq(const int tclk, const int n, const int m)
+{
+       return tclk / (10 * (m + 1) * (2 << n));
+}
+
+static bool __devinit
+mv64xxx_find_baud_factors(const u32 req_freq, const u32 tclk, u32 *best_n,
+                         u32 *best_m)
+{
+       int freq, delta, best_delta = INT_MAX;
+       int m, n;
+
+       for (n = 0; n <= 7; n++)
+               for (m = 0; m <= 15; m++) {
+                       freq = mv64xxx_calc_freq(tclk, n, m);
+                       delta = req_freq - freq;
+                       if (delta >= 0 && delta < best_delta) {
+                               *best_m = m;
+                               *best_n = n;
+                               best_delta = delta;
+                       }
+                       if (best_delta == 0)
+                               return true;
+               }
+       if (best_delta == INT_MAX)
+               return false;
+       return true;
+}
+
+static int __devinit
+mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
+                 struct device_node *np)
+{
+       u32 bus_freq, tclk;
+       int rc = 0;
+
+       /* CLK is mandatory when using DT to describe the i2c bus. We
+        * need to know tclk in order to calculate bus clock
+        * factors.
+        */
+#if !defined(CONFIG_HAVE_CLK)
+       /* Have OF but no CLK */
+       return -ENODEV;
+#else
+       if (IS_ERR(drv_data->clk)) {
+               rc = -ENODEV;
+               goto out;
+       }
+       tclk = clk_get_rate(drv_data->clk);
+       of_property_read_u32(np, "clock-frequency", &bus_freq);
+       if (!mv64xxx_find_baud_factors(bus_freq, tclk,
+                                      &drv_data->freq_n, &drv_data->freq_m)) {
+               rc = -EINVAL;
+               goto out;
+       }
+       drv_data->irq = irq_of_parse_and_map(np, 0);
+
+       /* Its not yet defined how timeouts will be specified in device tree.
+        * So hard code the value to 1 second.
+        */
+       drv_data->adapter.timeout = HZ;
+out:
+       return rc;
+#endif
+}
+#else /* CONFIG_OF */
+static int __devinit
+mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
+                 struct device_node *np)
+{
+       return -ENODEV;
+}
+#endif /* CONFIG_OF */
+
 static int __devinit
 mv64xxx_i2c_probe(struct platform_device *pd)
 {
@@ -528,7 +612,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
        struct mv64xxx_i2c_pdata        *pdata = pd->dev.platform_data;
        int     rc;
 
-       if ((pd->id != 0) || !pdata)
+       if ((!pdata && !pd->dev.of_node))
                return -ENODEV;
 
        drv_data = kzalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
@@ -546,19 +630,35 @@ mv64xxx_i2c_probe(struct platform_device *pd)
        init_waitqueue_head(&drv_data->waitq);
        spin_lock_init(&drv_data->lock);
 
-       drv_data->freq_m = pdata->freq_m;
-       drv_data->freq_n = pdata->freq_n;
-       drv_data->irq = platform_get_irq(pd, 0);
+#if defined(CONFIG_HAVE_CLK)
+       /* Not all platforms have a clk */
+       drv_data->clk = clk_get(&pd->dev, NULL);
+       if (!IS_ERR(drv_data->clk)) {
+               clk_prepare(drv_data->clk);
+               clk_enable(drv_data->clk);
+       }
+#endif
+       if (pdata) {
+               drv_data->freq_m = pdata->freq_m;
+               drv_data->freq_n = pdata->freq_n;
+               drv_data->irq = platform_get_irq(pd, 0);
+               drv_data->adapter.timeout = msecs_to_jiffies(pdata->timeout);
+       } else if (pd->dev.of_node) {
+               rc = mv64xxx_of_config(drv_data, pd->dev.of_node);
+               if (rc)
+                       goto exit_unmap_regs;
+       }
        if (drv_data->irq < 0) {
                rc = -ENXIO;
                goto exit_unmap_regs;
        }
+
        drv_data->adapter.dev.parent = &pd->dev;
        drv_data->adapter.algo = &mv64xxx_i2c_algo;
        drv_data->adapter.owner = THIS_MODULE;
        drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
-       drv_data->adapter.timeout = msecs_to_jiffies(pdata->timeout);
        drv_data->adapter.nr = pd->id;
+       drv_data->adapter.dev.of_node = pd->dev.of_node;
        platform_set_drvdata(pd, drv_data);
        i2c_set_adapdata(&drv_data->adapter, drv_data);
 
@@ -577,11 +677,20 @@ mv64xxx_i2c_probe(struct platform_device *pd)
                goto exit_free_irq;
        }
 
+       of_i2c_register_devices(&drv_data->adapter);
+
        return 0;
 
        exit_free_irq:
                free_irq(drv_data->irq, drv_data);
        exit_unmap_regs:
+#if defined(CONFIG_HAVE_CLK)
+       /* Not all platforms have a clk */
+       if (!IS_ERR(drv_data->clk)) {
+               clk_disable(drv_data->clk);
+               clk_unprepare(drv_data->clk);
+       }
+#endif
                mv64xxx_i2c_unmap_regs(drv_data);
        exit_kfree:
                kfree(drv_data);
@@ -597,17 +706,31 @@ mv64xxx_i2c_remove(struct platform_device *dev)
        rc = i2c_del_adapter(&drv_data->adapter);
        free_irq(drv_data->irq, drv_data);
        mv64xxx_i2c_unmap_regs(drv_data);
+#if defined(CONFIG_HAVE_CLK)
+       /* Not all platforms have a clk */
+       if (!IS_ERR(drv_data->clk)) {
+               clk_disable(drv_data->clk);
+               clk_unprepare(drv_data->clk);
+       }
+#endif
        kfree(drv_data);
 
        return rc;
 }
 
+static const struct of_device_id mv64xxx_i2c_of_match_table[] __devinitdata = {
+       { .compatible = "marvell,mv64xxx-i2c", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table);
+
 static struct platform_driver mv64xxx_i2c_driver = {
        .probe  = mv64xxx_i2c_probe,
        .remove = __devexit_p(mv64xxx_i2c_remove),
        .driver = {
                .owner  = THIS_MODULE,
                .name   = MV64XXX_I2C_CTLR_NAME,
+               .of_match_table = of_match_ptr(mv64xxx_i2c_of_match_table),
        },
 };
 
index 04eb441b6ce1945b8375b81cb7c7cd89cf172f7c..088c5c1ed17dfe831c4345ee8f02dd0ef4e1c82d 100644 (file)
 #define MXS_I2C_CTRL0_DIRECTION                        0x00010000
 #define MXS_I2C_CTRL0_XFER_COUNT(v)            ((v) & 0x0000FFFF)
 
+#define MXS_I2C_TIMING0                (0x10)
+#define MXS_I2C_TIMING1                (0x20)
+#define MXS_I2C_TIMING2                (0x30)
+
 #define MXS_I2C_CTRL1          (0x40)
 #define MXS_I2C_CTRL1_SET      (0x44)
 #define MXS_I2C_CTRL1_CLR      (0x48)
 #define MXS_CMD_I2C_READ       (MXS_I2C_CTRL0_SEND_NAK_ON_LAST | \
                                 MXS_I2C_CTRL0_MASTER_MODE)
 
+struct mxs_i2c_speed_config {
+       uint32_t        timing0;
+       uint32_t        timing1;
+       uint32_t        timing2;
+};
+
+/*
+ * Timing values for the default 24MHz clock supplied into the i2c block.
+ *
+ * The bus can operate at 95kHz or at 400kHz with the following timing
+ * register configurations. The 100kHz mode isn't present because it's
+ * values are not stated in the i.MX233/i.MX28 datasheet. The 95kHz mode
+ * shall be close enough replacement. Therefore when the bus is configured
+ * for 100kHz operation, 95kHz timing settings are actually loaded.
+ *
+ * For details, see i.MX233 [25.4.2 - 25.4.4] and i.MX28 [27.5.2 - 27.5.4].
+ */
+static const struct mxs_i2c_speed_config mxs_i2c_95kHz_config = {
+       .timing0        = 0x00780030,
+       .timing1        = 0x00800030,
+       .timing2        = 0x00300030,
+};
+
+static const struct mxs_i2c_speed_config mxs_i2c_400kHz_config = {
+       .timing0        = 0x000f0007,
+       .timing1        = 0x001f000f,
+       .timing2        = 0x00300030,
+};
+
 /**
  * struct mxs_i2c_dev - per device, private MXS-I2C data
  *
@@ -112,11 +145,17 @@ struct mxs_i2c_dev {
        struct completion cmd_complete;
        u32 cmd_err;
        struct i2c_adapter adapter;
+       const struct mxs_i2c_speed_config *speed;
 };
 
 static void mxs_i2c_reset(struct mxs_i2c_dev *i2c)
 {
        stmp_reset_block(i2c->regs);
+
+       writel(i2c->speed->timing0, i2c->regs + MXS_I2C_TIMING0);
+       writel(i2c->speed->timing1, i2c->regs + MXS_I2C_TIMING1);
+       writel(i2c->speed->timing2, i2c->regs + MXS_I2C_TIMING2);
+
        writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET);
        writel(MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE,
                        i2c->regs + MXS_I2C_QUEUECTRL_SET);
@@ -193,7 +232,7 @@ static int mxs_i2c_wait_for_data(struct mxs_i2c_dev *i2c)
 
 static int mxs_i2c_finish_read(struct mxs_i2c_dev *i2c, u8 *buf, int len)
 {
-       u32 data;
+       u32 uninitialized_var(data);
        int i;
 
        for (i = 0; i < len; i++) {
@@ -319,6 +358,28 @@ static const struct i2c_algorithm mxs_i2c_algo = {
        .functionality = mxs_i2c_func,
 };
 
+static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c)
+{
+       uint32_t speed;
+       struct device *dev = i2c->dev;
+       struct device_node *node = dev->of_node;
+       int ret;
+
+       if (!node)
+               return -EINVAL;
+
+       i2c->speed = &mxs_i2c_95kHz_config;
+       ret = of_property_read_u32(node, "clock-frequency", &speed);
+       if (ret)
+               dev_warn(dev, "No I2C speed selected, using 100kHz\n");
+       else if (speed == 400000)
+               i2c->speed = &mxs_i2c_400kHz_config;
+       else if (speed != 100000)
+               dev_warn(dev, "Unsupported I2C speed selected, using 100kHz\n");
+
+       return 0;
+}
+
 static int __devinit mxs_i2c_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -358,6 +419,11 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev)
                return err;
 
        i2c->dev = dev;
+
+       err = mxs_i2c_get_ofdata(i2c);
+       if (err)
+               return err;
+
        platform_set_drvdata(pdev, i2c);
 
        /* Do reset to enforce correct startup after pinmuxing */
index a92440dbef078ba18c6c5561a74f7f8265ba72c4..5e6f1eed4f83f233da76202c70d7564f773d3e1c 100644 (file)
@@ -14,7 +14,8 @@
  */
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/platform_device.h>
+#include <linux/amba/bus.h>
+#include <linux/atomic.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/i2c.h>
@@ -23,8 +24,7 @@
 #include <linux/io.h>
 #include <linux/regulator/consumer.h>
 #include <linux/pm_runtime.h>
-
-#include <plat/i2c.h>
+#include <linux/platform_data/i2c-nomadik.h>
 
 #define DRIVER_NAME "nmk-i2c"
 
@@ -136,7 +136,7 @@ struct i2c_nmk_client {
 
 /**
  * struct nmk_i2c_dev - private data structure of the controller.
- * @pdev: parent platform device.
+ * @adev: parent amba device.
  * @adap: corresponding I2C adapter.
  * @irq: interrupt line for the controller.
  * @virtbase: virtual io memory area.
@@ -150,7 +150,7 @@ struct i2c_nmk_client {
  * @busy: Busy doing transfer.
  */
 struct nmk_i2c_dev {
-       struct platform_device          *pdev;
+       struct amba_device              *adev;
        struct i2c_adapter              adap;
        int                             irq;
        void __iomem                    *virtbase;
@@ -217,7 +217,7 @@ static int flush_i2c_fifo(struct nmk_i2c_dev *dev)
                }
        }
 
-       dev_err(&dev->pdev->dev,
+       dev_err(&dev->adev->dev,
                "flushing operation timed out giving up after %d attempts",
                LOOP_ATTEMPTS);
 
@@ -276,15 +276,32 @@ exit:
 /**
  * load_i2c_mcr_reg() - load the MCR register
  * @dev: private data of controller
+ * @flags: message flags
  */
-static u32 load_i2c_mcr_reg(struct nmk_i2c_dev *dev)
+static u32 load_i2c_mcr_reg(struct nmk_i2c_dev *dev, u16 flags)
 {
        u32 mcr = 0;
+       unsigned short slave_adr_3msb_bits;
 
-       /* 7-bit address transaction */
-       mcr |= GEN_MASK(1, I2C_MCR_AM, 12);
        mcr |= GEN_MASK(dev->cli.slave_adr, I2C_MCR_A7, 1);
 
+       if (unlikely(flags & I2C_M_TEN)) {
+               /* 10-bit address transaction */
+               mcr |= GEN_MASK(2, I2C_MCR_AM, 12);
+               /*
+                * Get the top 3 bits.
+                * EA10 represents extended address in MCR. This includes
+                * the extension (MSB bits) of the 7 bit address loaded
+                * in A7
+                */
+               slave_adr_3msb_bits = (dev->cli.slave_adr >> 7) & 0x7;
+
+               mcr |= GEN_MASK(slave_adr_3msb_bits, I2C_MCR_EA10, 8);
+       } else {
+               /* 7-bit address transaction */
+               mcr |= GEN_MASK(1, I2C_MCR_AM, 12);
+       }
+
        /* start byte procedure not applied */
        mcr |= GEN_MASK(0, I2C_MCR_SB, 11);
 
@@ -364,7 +381,7 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
         * and high speed (up to 3.4 Mb/s)
         */
        if (dev->cfg.sm > I2C_FREQ_MODE_FAST) {
-               dev_err(&dev->pdev->dev,
+               dev_err(&dev->adev->dev,
                        "do not support this mode defaulting to std. mode\n");
                brcr2 = i2c_clk/(100000 * 2) & 0xffff;
                writel((brcr1 | brcr2), dev->virtbase + I2C_BRCR);
@@ -381,19 +398,20 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
 /**
  * read_i2c() - Read from I2C client device
  * @dev: private data of I2C Driver
+ * @flags: message flags
  *
  * This function reads from i2c client device when controller is in
  * master mode. There is a completion timeout. If there is no transfer
  * before timeout error is returned.
  */
-static int read_i2c(struct nmk_i2c_dev *dev)
+static int read_i2c(struct nmk_i2c_dev *dev, u16 flags)
 {
        u32 status = 0;
        u32 mcr;
        u32 irq_mask = 0;
        int timeout;
 
-       mcr = load_i2c_mcr_reg(dev);
+       mcr = load_i2c_mcr_reg(dev, flags);
        writel(mcr, dev->virtbase + I2C_MCR);
 
        /* load the current CR value */
@@ -423,7 +441,7 @@ static int read_i2c(struct nmk_i2c_dev *dev)
                &dev->xfer_complete, dev->adap.timeout);
 
        if (timeout < 0) {
-               dev_err(&dev->pdev->dev,
+               dev_err(&dev->adev->dev,
                        "wait_for_completion_timeout "
                        "returned %d waiting for event\n", timeout);
                status = timeout;
@@ -431,7 +449,7 @@ static int read_i2c(struct nmk_i2c_dev *dev)
 
        if (timeout == 0) {
                /* Controller timed out */
-               dev_err(&dev->pdev->dev, "read from slave 0x%x timed out\n",
+               dev_err(&dev->adev->dev, "read from slave 0x%x timed out\n",
                                dev->cli.slave_adr);
                status = -ETIMEDOUT;
        }
@@ -459,17 +477,18 @@ static void fill_tx_fifo(struct nmk_i2c_dev *dev, int no_bytes)
 /**
  * write_i2c() - Write data to I2C client.
  * @dev: private data of I2C Driver
+ * @flags: message flags
  *
  * This function writes data to I2C client
  */
-static int write_i2c(struct nmk_i2c_dev *dev)
+static int write_i2c(struct nmk_i2c_dev *dev, u16 flags)
 {
        u32 status = 0;
        u32 mcr;
        u32 irq_mask = 0;
        int timeout;
 
-       mcr = load_i2c_mcr_reg(dev);
+       mcr = load_i2c_mcr_reg(dev, flags);
 
        writel(mcr, dev->virtbase + I2C_MCR);
 
@@ -510,7 +529,7 @@ static int write_i2c(struct nmk_i2c_dev *dev)
                &dev->xfer_complete, dev->adap.timeout);
 
        if (timeout < 0) {
-               dev_err(&dev->pdev->dev,
+               dev_err(&dev->adev->dev,
                        "wait_for_completion_timeout "
                        "returned %d waiting for event\n", timeout);
                status = timeout;
@@ -518,7 +537,7 @@ static int write_i2c(struct nmk_i2c_dev *dev)
 
        if (timeout == 0) {
                /* Controller timed out */
-               dev_err(&dev->pdev->dev, "write to slave 0x%x timed out\n",
+               dev_err(&dev->adev->dev, "write to slave 0x%x timed out\n",
                                dev->cli.slave_adr);
                status = -ETIMEDOUT;
        }
@@ -538,11 +557,11 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags)
        if (flags & I2C_M_RD) {
                /* read operation */
                dev->cli.operation = I2C_READ;
-               status = read_i2c(dev);
+               status = read_i2c(dev, flags);
        } else {
                /* write operation */
                dev->cli.operation = I2C_WRITE;
-               status = write_i2c(dev);
+               status = write_i2c(dev, flags);
        }
 
        if (status || (dev->result)) {
@@ -557,7 +576,7 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags)
                if (((i2c_sr >> 2) & 0x3) == 0x3) {
                        /* get the abort cause */
                        cause = (i2c_sr >> 4) & 0x7;
-                       dev_err(&dev->pdev->dev, "%s\n",
+                       dev_err(&dev->adev->dev, "%s\n",
                                cause >= ARRAY_SIZE(abort_causes) ?
                                "unknown reason" :
                                abort_causes[cause]);
@@ -630,7 +649,7 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
 
        if (dev->regulator)
                regulator_enable(dev->regulator);
-       pm_runtime_get_sync(&dev->pdev->dev);
+       pm_runtime_get_sync(&dev->adev->dev);
 
        clk_enable(dev->clk);
 
@@ -644,13 +663,6 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
                setup_i2c_controller(dev);
 
                for (i = 0; i < num_msgs; i++) {
-                       if (unlikely(msgs[i].flags & I2C_M_TEN)) {
-                               dev_err(&dev->pdev->dev,
-                                       "10 bit addressing not supported\n");
-
-                               status = -EINVAL;
-                               goto out;
-                       }
                        dev->cli.slave_adr      = msgs[i].addr;
                        dev->cli.buffer         = msgs[i].buf;
                        dev->cli.count          = msgs[i].len;
@@ -667,7 +679,7 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
 
 out:
        clk_disable(dev->clk);
-       pm_runtime_put_sync(&dev->pdev->dev);
+       pm_runtime_put_sync(&dev->adev->dev);
        if (dev->regulator)
                regulator_disable(dev->regulator);
 
@@ -790,7 +802,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
 
                if (dev->cli.count) {
                        dev->result = -EIO;
-                       dev_err(&dev->pdev->dev,
+                       dev_err(&dev->adev->dev,
                                "%lu bytes still remain to be xfered\n",
                                dev->cli.count);
                        (void) init_hw(dev);
@@ -834,7 +846,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
                dev->result = -EIO;
                (void) init_hw(dev);
 
-               dev_err(&dev->pdev->dev, "Tx Fifo Over run\n");
+               dev_err(&dev->adev->dev, "Tx Fifo Over run\n");
                complete(&dev->xfer_complete);
 
                break;
@@ -847,10 +859,10 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
        case I2C_IT_RFSE:
        case I2C_IT_WTSR:
        case I2C_IT_STD:
-               dev_err(&dev->pdev->dev, "unhandled Interrupt\n");
+               dev_err(&dev->adev->dev, "unhandled Interrupt\n");
                break;
        default:
-               dev_err(&dev->pdev->dev, "spurious Interrupt..\n");
+               dev_err(&dev->adev->dev, "spurious Interrupt..\n");
                break;
        }
 
@@ -861,8 +873,8 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
 #ifdef CONFIG_PM
 static int nmk_i2c_suspend(struct device *dev)
 {
-       struct platform_device *pdev = to_platform_device(dev);
-       struct nmk_i2c_dev *nmk_i2c = platform_get_drvdata(pdev);
+       struct amba_device *adev = to_amba_device(dev);
+       struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev);
 
        if (nmk_i2c->busy)
                return -EBUSY;
@@ -891,7 +903,7 @@ static const struct dev_pm_ops nmk_i2c_pm = {
 
 static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap)
 {
-       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;
 }
 
 static const struct i2c_algorithm nmk_i2c_algo = {
@@ -899,78 +911,69 @@ static const struct i2c_algorithm nmk_i2c_algo = {
        .functionality  = nmk_i2c_functionality
 };
 
-static int __devinit nmk_i2c_probe(struct platform_device *pdev)
+static atomic_t adapter_id = ATOMIC_INIT(0);
+
+static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
 {
        int ret = 0;
-       struct resource *res;
        struct nmk_i2c_controller *pdata =
-                       pdev->dev.platform_data;
+                       adev->dev.platform_data;
        struct nmk_i2c_dev      *dev;
        struct i2c_adapter *adap;
 
+       if (!pdata) {
+               dev_warn(&adev->dev, "no platform data\n");
+               return -ENODEV;
+       }
        dev = kzalloc(sizeof(struct nmk_i2c_dev), GFP_KERNEL);
        if (!dev) {
-               dev_err(&pdev->dev, "cannot allocate memory\n");
+               dev_err(&adev->dev, "cannot allocate memory\n");
                ret = -ENOMEM;
                goto err_no_mem;
        }
        dev->busy = false;
-       dev->pdev = pdev;
-       platform_set_drvdata(pdev, dev);
+       dev->adev = adev;
+       amba_set_drvdata(adev, dev);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               ret = -ENOENT;
-               goto err_no_resource;
-       }
-
-       if (request_mem_region(res->start, resource_size(res),
-               DRIVER_NAME "I/O region") == NULL) {
-               ret = -EBUSY;
-               goto err_no_region;
-       }
-
-       dev->virtbase = ioremap(res->start, resource_size(res));
+       dev->virtbase = ioremap(adev->res.start, resource_size(&adev->res));
        if (!dev->virtbase) {
                ret = -ENOMEM;
                goto err_no_ioremap;
        }
 
-       dev->irq = platform_get_irq(pdev, 0);
+       dev->irq = adev->irq[0];
        ret = request_irq(dev->irq, i2c_irq_handler, 0,
                                DRIVER_NAME, dev);
        if (ret) {
-               dev_err(&pdev->dev, "cannot claim the irq %d\n", dev->irq);
+               dev_err(&adev->dev, "cannot claim the irq %d\n", dev->irq);
                goto err_irq;
        }
 
-       dev->regulator = regulator_get(&pdev->dev, "v-i2c");
+       dev->regulator = regulator_get(&adev->dev, "v-i2c");
        if (IS_ERR(dev->regulator)) {
-               dev_warn(&pdev->dev, "could not get i2c regulator\n");
+               dev_warn(&adev->dev, "could not get i2c regulator\n");
                dev->regulator = NULL;
        }
 
-       pm_suspend_ignore_children(&pdev->dev, true);
-       pm_runtime_enable(&pdev->dev);
+       pm_suspend_ignore_children(&adev->dev, true);
 
-       dev->clk = clk_get(&pdev->dev, NULL);
+       dev->clk = clk_get(&adev->dev, NULL);
        if (IS_ERR(dev->clk)) {
-               dev_err(&pdev->dev, "could not get i2c clock\n");
+               dev_err(&adev->dev, "could not get i2c clock\n");
                ret = PTR_ERR(dev->clk);
                goto err_no_clk;
        }
 
        adap = &dev->adap;
-       adap->dev.parent = &pdev->dev;
+       adap->dev.parent = &adev->dev;
        adap->owner     = THIS_MODULE;
        adap->class     = I2C_CLASS_HWMON | I2C_CLASS_SPD;
        adap->algo      = &nmk_i2c_algo;
        adap->timeout   = msecs_to_jiffies(pdata->timeout);
+       adap->nr = atomic_read(&adapter_id);
        snprintf(adap->name, sizeof(adap->name),
-                "Nomadik I2C%d at %lx", pdev->id, (unsigned long)res->start);
-
-       /* fetch the controller id */
-       adap->nr        = pdev->id;
+                "Nomadik I2C%d at %pR", adap->nr, &adev->res);
+       atomic_inc(&adapter_id);
 
        /* fetch the controller configuration from machine */
        dev->cfg.clk_freq = pdata->clk_freq;
@@ -981,16 +984,18 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
 
        i2c_set_adapdata(adap, dev);
 
-       dev_info(&pdev->dev,
+       dev_info(&adev->dev,
                 "initialize %s on virtual base %p\n",
                 adap->name, dev->virtbase);
 
        ret = i2c_add_numbered_adapter(adap);
        if (ret) {
-               dev_err(&pdev->dev, "failed to add adapter\n");
+               dev_err(&adev->dev, "failed to add adapter\n");
                goto err_add_adap;
        }
 
+       pm_runtime_put(&adev->dev);
+
        return 0;
 
  err_add_adap:
@@ -998,25 +1003,21 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
  err_no_clk:
        if (dev->regulator)
                regulator_put(dev->regulator);
-       pm_runtime_disable(&pdev->dev);
        free_irq(dev->irq, dev);
  err_irq:
        iounmap(dev->virtbase);
  err_no_ioremap:
-       release_mem_region(res->start, resource_size(res));
- err_no_region:
-       platform_set_drvdata(pdev, NULL);
- err_no_resource:
+       amba_set_drvdata(adev, NULL);
        kfree(dev);
  err_no_mem:
 
        return ret;
 }
 
-static int __devexit nmk_i2c_remove(struct platform_device *pdev)
+static int nmk_i2c_remove(struct amba_device *adev)
 {
-       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       struct nmk_i2c_dev *dev = platform_get_drvdata(pdev);
+       struct resource *res = &adev->res;
+       struct nmk_i2c_dev *dev = amba_get_drvdata(adev);
 
        i2c_del_adapter(&dev->adap);
        flush_i2c_fifo(dev);
@@ -1031,31 +1032,46 @@ static int __devexit nmk_i2c_remove(struct platform_device *pdev)
        clk_put(dev->clk);
        if (dev->regulator)
                regulator_put(dev->regulator);
-       pm_runtime_disable(&pdev->dev);
-       platform_set_drvdata(pdev, NULL);
+       pm_runtime_disable(&adev->dev);
+       amba_set_drvdata(adev, NULL);
        kfree(dev);
 
        return 0;
 }
 
-static struct platform_driver nmk_i2c_driver = {
-       .driver = {
+static struct amba_id nmk_i2c_ids[] = {
+       {
+               .id     = 0x00180024,
+               .mask   = 0x00ffffff,
+       },
+       {
+               .id     = 0x00380024,
+               .mask   = 0x00ffffff,
+       },
+       {},
+};
+
+MODULE_DEVICE_TABLE(amba, nmk_i2c_ids);
+
+static struct amba_driver nmk_i2c_driver = {
+       .drv = {
                .owner = THIS_MODULE,
                .name = DRIVER_NAME,
                .pm = &nmk_i2c_pm,
        },
+       .id_table = nmk_i2c_ids,
        .probe = nmk_i2c_probe,
-       .remove = __devexit_p(nmk_i2c_remove),
+       .remove = nmk_i2c_remove,
 };
 
 static int __init nmk_i2c_init(void)
 {
-       return platform_driver_register(&nmk_i2c_driver);
+       return amba_driver_register(&nmk_i2c_driver);
 }
 
 static void __exit nmk_i2c_exit(void)
 {
-       platform_driver_unregister(&nmk_i2c_driver);
+       amba_driver_unregister(&nmk_i2c_driver);
 }
 
 subsys_initcall(nmk_i2c_init);
@@ -1064,4 +1080,3 @@ module_exit(nmk_i2c_exit);
 MODULE_AUTHOR("Sachin Verma, Srinidhi KASAGAR");
 MODULE_DESCRIPTION("Nomadik/Ux500 I2C driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRIVER_NAME);
index 75194c579b6d78f419f8886de4d028a2447e08a4..bffd5501ac2ddd5516f29dfb08bb12add1e196fe 100644 (file)
  */
 
 /*
- * Device tree configuration:
- *
- * Required properties:
- * - compatible      : "opencores,i2c-ocores"
- * - reg             : bus address start and address range size of device
- * - interrupts      : interrupt number
- * - regstep         : size of device registers in bytes
- * - clock-frequency : frequency of bus clock in Hz
- * 
- * Example:
- *
- *  i2c0: ocores@a0000000 {
- *              compatible = "opencores,i2c-ocores";
- *              reg = <0xa0000000 0x8>;
- *              interrupts = <10>;
- *
- *              regstep = <1>;
- *              clock-frequency = <20000000>;
- *
- * -- Devices connected on this I2C bus get
- * -- defined here; address- and size-cells
- * -- apply to these child devices
- *
- *              #address-cells = <1>;
- *              #size-cells = <0>;
- *
- *              dummy@60 {
- *                     compatible = "dummy";
- *                     reg = <60>;
- *              };
- *  };
- *
+ * This driver can be used from the device tree, see
+ *     Documentation/devicetree/bindings/i2c/ocore-i2c.txt
  */
-
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/io.h>
 #include <linux/of_i2c.h>
+#include <linux/log2.h>
 
 struct ocores_i2c {
        void __iomem *base;
-       int regstep;
+       u32 reg_shift;
+       u32 reg_io_width;
        wait_queue_head_t wait;
        struct i2c_adapter adap;
        struct i2c_msg *msg;
@@ -102,12 +73,22 @@ struct ocores_i2c {
 
 static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value)
 {
-       iowrite8(value, i2c->base + reg * i2c->regstep);
+       if (i2c->reg_io_width == 4)
+               iowrite32(value, i2c->base + (reg << i2c->reg_shift));
+       else if (i2c->reg_io_width == 2)
+               iowrite16(value, i2c->base + (reg << i2c->reg_shift));
+       else
+               iowrite8(value, i2c->base + (reg << i2c->reg_shift));
 }
 
 static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg)
 {
-       return ioread8(i2c->base + reg * i2c->regstep);
+       if (i2c->reg_io_width == 4)
+               return ioread32(i2c->base + (reg << i2c->reg_shift));
+       else if (i2c->reg_io_width == 2)
+               return ioread16(i2c->base + (reg << i2c->reg_shift));
+       else
+               return ioread8(i2c->base + (reg << i2c->reg_shift));
 }
 
 static void ocores_process(struct ocores_i2c *i2c)
@@ -247,26 +228,35 @@ static struct i2c_adapter ocores_adapter = {
 };
 
 #ifdef CONFIG_OF
-static int ocores_i2c_of_probe(struct platform_devicepdev,
-                               struct ocores_i2ci2c)
+static int ocores_i2c_of_probe(struct platform_device *pdev,
+                               struct ocores_i2c *i2c)
 {
-       const __be32* val;
-
-       val = of_get_property(pdev->dev.of_node, "regstep", NULL);
-       if (!val) {
-               dev_err(&pdev->dev, "Missing required parameter 'regstep'");
-               return -ENODEV;
+       struct device_node *np = pdev->dev.of_node;
+       u32 val;
+
+       if (of_property_read_u32(np, "reg-shift", &i2c->reg_shift)) {
+               /* no 'reg-shift', check for deprecated 'regstep' */
+               if (!of_property_read_u32(np, "regstep", &val)) {
+                       if (!is_power_of_2(val)) {
+                               dev_err(&pdev->dev, "invalid regstep %d\n",
+                                       val);
+                               return -EINVAL;
+                       }
+                       i2c->reg_shift = ilog2(val);
+                       dev_warn(&pdev->dev,
+                               "regstep property deprecated, use reg-shift\n");
+               }
        }
-       i2c->regstep = be32_to_cpup(val);
 
-       val = of_get_property(pdev->dev.of_node, "clock-frequency", NULL);
-       if (!val) {
+       if (of_property_read_u32(np, "clock-frequency", &val)) {
                dev_err(&pdev->dev,
-                       "Missing required parameter 'clock-frequency'");
+                       "Missing required parameter 'clock-frequency'\n");
                return -ENODEV;
        }
-       i2c->clock_khz = be32_to_cpup(val) / 1000;
+       i2c->clock_khz = val / 1000;
 
+       of_property_read_u32(pdev->dev.of_node, "reg-io-width",
+                               &i2c->reg_io_width);
        return 0;
 }
 #else
@@ -308,7 +298,8 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev)
 
        pdata = pdev->dev.platform_data;
        if (pdata) {
-               i2c->regstep = pdata->regstep;
+               i2c->reg_shift = pdata->reg_shift;
+               i2c->reg_io_width = pdata->reg_io_width;
                i2c->clock_khz = pdata->clock_khz;
        } else {
                ret = ocores_i2c_of_probe(pdev, i2c);
@@ -316,6 +307,9 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev)
                        return ret;
        }
 
+       if (i2c->reg_io_width == 0)
+               i2c->reg_io_width = 1; /* Set to default value */
+
        ocores_init(i2c);
 
        init_waitqueue_head(&i2c->wait);
@@ -351,7 +345,7 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int __devexit ocores_i2c_remove(struct platform_devicepdev)
+static int __devexit ocores_i2c_remove(struct platform_device *pdev)
 {
        struct ocores_i2c *i2c = platform_get_drvdata(pdev);
 
@@ -367,9 +361,9 @@ static int __devexit ocores_i2c_remove(struct platform_device* pdev)
 }
 
 #ifdef CONFIG_PM
-static int ocores_i2c_suspend(struct platform_device *pdev, pm_message_t state)
+static int ocores_i2c_suspend(struct device *dev)
 {
-       struct ocores_i2c *i2c = platform_get_drvdata(pdev);
+       struct ocores_i2c *i2c = dev_get_drvdata(dev);
        u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
 
        /* make sure the device is disabled */
@@ -378,17 +372,19 @@ static int ocores_i2c_suspend(struct platform_device *pdev, pm_message_t state)
        return 0;
 }
 
-static int ocores_i2c_resume(struct platform_device *pdev)
+static int ocores_i2c_resume(struct device *dev)
 {
-       struct ocores_i2c *i2c = platform_get_drvdata(pdev);
+       struct ocores_i2c *i2c = dev_get_drvdata(dev);
 
        ocores_init(i2c);
 
        return 0;
 }
+
+static SIMPLE_DEV_PM_OPS(ocores_i2c_pm, ocores_i2c_suspend, ocores_i2c_resume);
+#define OCORES_I2C_PM  (&ocores_i2c_pm)
 #else
-#define ocores_i2c_suspend     NULL
-#define ocores_i2c_resume      NULL
+#define OCORES_I2C_PM  NULL
 #endif
 
 static struct of_device_id ocores_i2c_match[] = {
@@ -400,12 +396,11 @@ MODULE_DEVICE_TABLE(of, ocores_i2c_match);
 static struct platform_driver ocores_i2c_driver = {
        .probe   = ocores_i2c_probe,
        .remove  = __devexit_p(ocores_i2c_remove),
-       .suspend = ocores_i2c_suspend,
-       .resume  = ocores_i2c_resume,
        .driver  = {
                .owner = THIS_MODULE,
                .name = "ocores-i2c",
                .of_match_table = ocores_i2c_match,
+               .pm = OCORES_I2C_PM,
        },
 };
 
index ee139a598814ea46da91fc494d6d2ded770ddef4..f44c83549fe5a4c5380eb8e18f5d19faae77c9c7 100644 (file)
@@ -2,7 +2,7 @@
  * (C) Copyright 2009-2010
  * Nokia Siemens Networks, michael.lawnick.ext@nsn.com
  *
- * Portions Copyright (C) 2010 Cavium Networks, Inc.
+ * Portions Copyright (C) 2010, 2011 Cavium Networks, Inc.
  *
  * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors.
  *
  * warranty of any kind, whether express or implied.
  */
 
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of_i2c.h>
+#include <linux/delay.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/init.h>
-
-#include <linux/io.h>
 #include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of.h>
 
 #include <asm/octeon/octeon.h>
 
@@ -65,7 +66,7 @@ struct octeon_i2c {
        wait_queue_head_t queue;
        struct i2c_adapter adap;
        int irq;
-       int twsi_freq;
+       u32 twsi_freq;
        int sys_freq;
        resource_size_t twsi_phys;
        void __iomem *twsi_base;
@@ -121,10 +122,8 @@ static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg)
  */
 static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data)
 {
-       u64 tmp;
-
        __raw_writeq(data, i2c->twsi_base + TWSI_INT);
-       tmp = __raw_readq(i2c->twsi_base + TWSI_INT);
+       __raw_readq(i2c->twsi_base + TWSI_INT);
 }
 
 /**
@@ -515,7 +514,6 @@ static int __devinit octeon_i2c_probe(struct platform_device *pdev)
 {
        int irq, result = 0;
        struct octeon_i2c *i2c;
-       struct octeon_i2c_data *i2c_data;
        struct resource *res_mem;
 
        /* All adaptors have an irq.  */
@@ -523,86 +521,90 @@ static int __devinit octeon_i2c_probe(struct platform_device *pdev)
        if (irq < 0)
                return irq;
 
-       i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
+       i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
        if (!i2c) {
                dev_err(&pdev->dev, "kzalloc failed\n");
                result = -ENOMEM;
                goto out;
        }
        i2c->dev = &pdev->dev;
-       i2c_data = pdev->dev.platform_data;
 
        res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
        if (res_mem == NULL) {
                dev_err(i2c->dev, "found no memory resource\n");
                result = -ENXIO;
-               goto fail_region;
+               goto out;
        }
+       i2c->twsi_phys = res_mem->start;
+       i2c->regsize = resource_size(res_mem);
 
-       if (i2c_data == NULL) {
-               dev_err(i2c->dev, "no I2C frequency data\n");
+       /*
+        * "clock-rate" is a legacy binding, the official binding is
+        * "clock-frequency".  Try the official one first and then
+        * fall back if it doesn't exist.
+        */
+       if (of_property_read_u32(pdev->dev.of_node,
+                                "clock-frequency", &i2c->twsi_freq) &&
+           of_property_read_u32(pdev->dev.of_node,
+                                "clock-rate", &i2c->twsi_freq)) {
+               dev_err(i2c->dev,
+                       "no I2C 'clock-rate' or 'clock-frequency' property\n");
                result = -ENXIO;
-               goto fail_region;
+               goto out;
        }
 
-       i2c->twsi_phys = res_mem->start;
-       i2c->regsize = resource_size(res_mem);
-       i2c->twsi_freq = i2c_data->i2c_freq;
-       i2c->sys_freq = i2c_data->sys_freq;
+       i2c->sys_freq = octeon_get_io_clock_rate();
 
-       if (!request_mem_region(i2c->twsi_phys, i2c->regsize, res_mem->name)) {
+       if (!devm_request_mem_region(&pdev->dev, i2c->twsi_phys, i2c->regsize,
+                                     res_mem->name)) {
                dev_err(i2c->dev, "request_mem_region failed\n");
-               goto fail_region;
+               goto out;
        }
-       i2c->twsi_base = ioremap(i2c->twsi_phys, i2c->regsize);
+       i2c->twsi_base = devm_ioremap(&pdev->dev, i2c->twsi_phys, i2c->regsize);
 
        init_waitqueue_head(&i2c->queue);
 
        i2c->irq = irq;
 
-       result = request_irq(i2c->irq, octeon_i2c_isr, 0, DRV_NAME, i2c);
+       result = devm_request_irq(&pdev->dev, i2c->irq,
+                                 octeon_i2c_isr, 0, DRV_NAME, i2c);
        if (result < 0) {
                dev_err(i2c->dev, "failed to attach interrupt\n");
-               goto fail_irq;
+               goto out;
        }
 
        result = octeon_i2c_initlowlevel(i2c);
        if (result) {
                dev_err(i2c->dev, "init low level failed\n");
-               goto  fail_add;
+               goto  out;
        }
 
        result = octeon_i2c_setclock(i2c);
        if (result) {
                dev_err(i2c->dev, "clock init failed\n");
-               goto  fail_add;
+               goto  out;
        }
 
        i2c->adap = octeon_i2c_ops;
        i2c->adap.dev.parent = &pdev->dev;
-       i2c->adap.nr = pdev->id >= 0 ? pdev->id : 0;
+       i2c->adap.dev.of_node = pdev->dev.of_node;
        i2c_set_adapdata(&i2c->adap, i2c);
        platform_set_drvdata(pdev, i2c);
 
-       result = i2c_add_numbered_adapter(&i2c->adap);
+       result = i2c_add_adapter(&i2c->adap);
        if (result < 0) {
                dev_err(i2c->dev, "failed to add adapter\n");
                goto fail_add;
        }
-
        dev_info(i2c->dev, "version %s\n", DRV_VERSION);
 
-       return result;
+       of_i2c_register_devices(&i2c->adap);
+
+       return 0;
 
 fail_add:
        platform_set_drvdata(pdev, NULL);
-       free_irq(i2c->irq, i2c);
-fail_irq:
-       iounmap(i2c->twsi_base);
-       release_mem_region(i2c->twsi_phys, i2c->regsize);
-fail_region:
-       kfree(i2c);
 out:
        return result;
 };
@@ -613,19 +615,24 @@ static int __devexit octeon_i2c_remove(struct platform_device *pdev)
 
        i2c_del_adapter(&i2c->adap);
        platform_set_drvdata(pdev, NULL);
-       free_irq(i2c->irq, i2c);
-       iounmap(i2c->twsi_base);
-       release_mem_region(i2c->twsi_phys, i2c->regsize);
-       kfree(i2c);
        return 0;
 };
 
+static struct of_device_id octeon_i2c_match[] = {
+       {
+               .compatible = "cavium,octeon-3860-twsi",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, octeon_i2c_match);
+
 static struct platform_driver octeon_i2c_driver = {
        .probe          = octeon_i2c_probe,
        .remove         = __devexit_p(octeon_i2c_remove),
        .driver         = {
                .owner  = THIS_MODULE,
                .name   = DRV_NAME,
+               .of_match_table = octeon_i2c_match,
        },
 };
 
@@ -635,4 +642,3 @@ MODULE_AUTHOR("Michael Lawnick <michael.lawnick.ext@nsn.com>");
 MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_VERSION);
-MODULE_ALIAS("platform:" DRV_NAME);
index c2148332de0f641fdffcdd395741785cee93af77..6849635b268a81fe8f85d380d2d80d16ba6ab2b1 100644 (file)
@@ -49,8 +49,8 @@
 
 /* I2C controller revisions present on specific hardware */
 #define OMAP_I2C_REV_ON_2430           0x36
-#define OMAP_I2C_REV_ON_3430           0x3C
-#define OMAP_I2C_REV_ON_3530_4430      0x40
+#define OMAP_I2C_REV_ON_3430_3530      0x3C
+#define OMAP_I2C_REV_ON_3630_4430      0x40
 
 /* timeout waiting for the controller to respond */
 #define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000))
@@ -173,7 +173,7 @@ enum {
 
 /* Errata definitions */
 #define I2C_OMAP_ERRATA_I207           (1 << 0)
-#define I2C_OMAP3_1P153                        (1 << 1)
+#define I2C_OMAP_ERRATA_I462           (1 << 1)
 
 struct omap_i2c_dev {
        struct device           *dev;
@@ -269,47 +269,6 @@ static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg)
                                (i2c_dev->regs[reg] << i2c_dev->reg_shift));
 }
 
-static void omap_i2c_unidle(struct omap_i2c_dev *dev)
-{
-       if (dev->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) {
-               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);
-       }
-
-       /*
-        * Don't write to this register if the IE state is 0 as it can
-        * cause deadlock.
-        */
-       if (dev->iestate)
-               omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
-}
-
-static void omap_i2c_idle(struct omap_i2c_dev *dev)
-{
-       u16 iv;
-
-       dev->iestate = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
-       if (dev->dtrev == OMAP_I2C_IP_VERSION_2)
-               omap_i2c_write_reg(dev, OMAP_I2C_IP_V2_IRQENABLE_CLR, 1);
-       else
-               omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0);
-
-       if (dev->rev < OMAP_I2C_OMAP1_REV_2) {
-               iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); /* Read clears */
-       } else {
-               omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, dev->iestate);
-
-               /* Flush posted write */
-               omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
-       }
-}
-
 static int omap_i2c_init(struct omap_i2c_dev *dev)
 {
        u16 psc = 0, scll = 0, sclh = 0, buf = 0;
@@ -346,7 +305,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
                        omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG,
                                           SYSC_AUTOIDLE_MASK);
 
-               } else if (dev->rev >= OMAP_I2C_REV_ON_3430) {
+               } else if (dev->rev >= OMAP_I2C_REV_ON_3430_3530) {
                        dev->syscstate = SYSC_AUTOIDLE_MASK;
                        dev->syscstate |= SYSC_ENAWAKEUP_MASK;
                        dev->syscstate |= (SYSC_IDLEMODE_SMART <<
@@ -468,11 +427,6 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
        /* Take the I2C module out of reset: */
        omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
 
-       dev->errata = 0;
-
-       if (dev->flags & OMAP_I2C_FLAG_APPLY_ERRATA_I207)
-               dev->errata |= I2C_OMAP_ERRATA_I207;
-
        /* Enable interrupts */
        dev->iestate = (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY |
                        OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK |
@@ -514,7 +468,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
                             struct i2c_msg *msg, int stop)
 {
        struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
-       int r;
+       unsigned long timeout;
        u16 w;
 
        dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
@@ -536,7 +490,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
        w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR;
        omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, w);
 
-       init_completion(&dev->cmd_complete);
+       INIT_COMPLETION(dev->cmd_complete);
        dev->cmd_err = 0;
 
        w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT;
@@ -584,12 +538,10 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
         * REVISIT: We should abort the transfer on signals, but the bus goes
         * into arbitration and we're currently unable to recover from it.
         */
-       r = wait_for_completion_timeout(&dev->cmd_complete,
-                                       OMAP_I2C_TIMEOUT);
+       timeout = wait_for_completion_timeout(&dev->cmd_complete,
+                                               OMAP_I2C_TIMEOUT);
        dev->buf_len = 0;
-       if (r < 0)
-               return r;
-       if (r == 0) {
+       if (timeout == 0) {
                dev_err(dev->dev, "controller timed out\n");
                omap_i2c_init(dev);
                return -ETIMEDOUT;
@@ -630,7 +582,9 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
        int i;
        int r;
 
-       pm_runtime_get_sync(dev->dev);
+       r = pm_runtime_get_sync(dev->dev);
+       if (IS_ERR_VALUE(r))
+               return r;
 
        r = omap_i2c_wait_for_bb(dev);
        if (r < 0)
@@ -767,11 +721,11 @@ omap_i2c_omap1_isr(int this_irq, void *dev_id)
 #endif
 
 /*
- * OMAP3430 Errata 1.153: When an XRDY/XDR is hit, wait for XUDF before writing
+ * OMAP3430 Errata i462: When an XRDY/XDR is hit, wait for XUDF before writing
  * data to DATA_REG. Otherwise some data bytes can be lost while transferring
  * them from the memory to the I2C interface.
  */
-static int errata_omap3_1p153(struct omap_i2c_dev *dev, u16 *stat, int *err)
+static int errata_omap3_i462(struct omap_i2c_dev *dev, u16 *stat, int *err)
 {
        unsigned long timeout = 10000;
 
@@ -779,7 +733,6 @@ static int errata_omap3_1p153(struct omap_i2c_dev *dev, u16 *stat, int *err)
                if (*stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) {
                        omap_i2c_ack_stat(dev, *stat & (OMAP_I2C_STAT_XRDY |
                                                        OMAP_I2C_STAT_XDR));
-                       *err |= OMAP_I2C_STAT_XUDF;
                        return -ETIMEDOUT;
                }
 
@@ -792,6 +745,7 @@ static int errata_omap3_1p153(struct omap_i2c_dev *dev, u16 *stat, int *err)
                return 0;
        }
 
+       *err |= OMAP_I2C_STAT_XUDF;
        return 0;
 }
 
@@ -930,8 +884,8 @@ complete:
                                        break;
                                }
 
-                               if ((dev->errata & I2C_OMAP3_1P153) &&
-                                   errata_omap3_1p153(dev, &stat, &err))
+                               if ((dev->errata & I2C_OMAP_ERRATA_I462) &&
+                                   errata_omap3_i462(dev, &stat, &err))
                                        goto complete;
 
                                omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
@@ -1048,6 +1002,7 @@ omap_i2c_probe(struct platform_device *pdev)
        }
 
        platform_set_drvdata(pdev, dev);
+       init_completion(&dev->cmd_complete);
 
        dev->reg_shift = (dev->flags >> OMAP_I2C_FLAG_BUS_SHIFT__SHIFT) & 3;
 
@@ -1057,12 +1012,19 @@ omap_i2c_probe(struct platform_device *pdev)
                dev->regs = (u8 *)reg_map_ip_v1;
 
        pm_runtime_enable(dev->dev);
-       pm_runtime_get_sync(dev->dev);
+       r = pm_runtime_get_sync(dev->dev);
+       if (IS_ERR_VALUE(r))
+               goto err_free_mem;
 
        dev->rev = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff;
 
-       if (dev->rev <= OMAP_I2C_REV_ON_3430)
-               dev->errata |= I2C_OMAP3_1P153;
+       dev->errata = 0;
+
+       if (dev->flags & OMAP_I2C_FLAG_APPLY_ERRATA_I207)
+               dev->errata |= I2C_OMAP_ERRATA_I207;
+
+       if (dev->rev <= OMAP_I2C_REV_ON_3430_3530)
+               dev->errata |= I2C_OMAP_ERRATA_I462;
 
        if (!(dev->flags & OMAP_I2C_FLAG_NO_FIFO)) {
                u16 s;
@@ -1079,7 +1041,7 @@ omap_i2c_probe(struct platform_device *pdev)
 
                dev->fifo_size = (dev->fifo_size / 2);
 
-               if (dev->rev >= OMAP_I2C_REV_ON_3530_4430)
+               if (dev->rev >= OMAP_I2C_REV_ON_3630_4430)
                        dev->b_hw = 0; /* Disable hardware fixes */
                else
                        dev->b_hw = 1; /* Enable hardware fixes */
@@ -1095,7 +1057,7 @@ omap_i2c_probe(struct platform_device *pdev)
 
        isr = (dev->rev < OMAP_I2C_OMAP1_REV_2) ? omap_i2c_omap1_isr :
                                                                   omap_i2c_isr;
-       r = request_irq(dev->irq, isr, 0, pdev->name, dev);
+       r = request_irq(dev->irq, isr, IRQF_NO_SUSPEND, pdev->name, dev);
 
        if (r) {
                dev_err(dev->dev, "failure requesting irq %i\n", dev->irq);
@@ -1105,8 +1067,6 @@ omap_i2c_probe(struct platform_device *pdev)
        dev_info(dev->dev, "bus %d rev%d.%d.%d at %d kHz\n", pdev->id,
                 dev->dtrev, dev->rev >> 4, dev->rev & 0xf, dev->speed);
 
-       pm_runtime_put(dev->dev);
-
        adap = &dev->adapter;
        i2c_set_adapdata(adap, dev);
        adap->owner = THIS_MODULE;
@@ -1126,6 +1086,8 @@ omap_i2c_probe(struct platform_device *pdev)
 
        of_i2c_register_devices(adap);
 
+       pm_runtime_put(dev->dev);
+
        return 0;
 
 err_free_irq:
@@ -1134,6 +1096,7 @@ err_unuse_clocks:
        omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
        pm_runtime_put(dev->dev);
        iounmap(dev->base);
+       pm_runtime_disable(&pdev->dev);
 err_free_mem:
        platform_set_drvdata(pdev, NULL);
        kfree(dev);
@@ -1143,17 +1106,23 @@ err_release_region:
        return r;
 }
 
-static int
-omap_i2c_remove(struct platform_device *pdev)
+static int __devexit omap_i2c_remove(struct platform_device *pdev)
 {
        struct omap_i2c_dev     *dev = platform_get_drvdata(pdev);
        struct resource         *mem;
+       int ret;
 
        platform_set_drvdata(pdev, NULL);
 
        free_irq(dev->irq, dev);
        i2c_del_adapter(&dev->adapter);
+       ret = pm_runtime_get_sync(&pdev->dev);
+       if (IS_ERR_VALUE(ret))
+               return ret;
+
        omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
+       pm_runtime_put(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
        iounmap(dev->base);
        kfree(dev);
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1161,13 +1130,26 @@ omap_i2c_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
 #ifdef CONFIG_PM_RUNTIME
 static int omap_i2c_runtime_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct omap_i2c_dev *_dev = platform_get_drvdata(pdev);
+       u16 iv;
+
+       _dev->iestate = omap_i2c_read_reg(_dev, OMAP_I2C_IE_REG);
+
+       omap_i2c_write_reg(_dev, OMAP_I2C_IE_REG, 0);
 
-       omap_i2c_idle(_dev);
+       if (_dev->rev < OMAP_I2C_OMAP1_REV_2) {
+               iv = omap_i2c_read_reg(_dev, OMAP_I2C_IV_REG); /* Read clears */
+       } else {
+               omap_i2c_write_reg(_dev, OMAP_I2C_STAT_REG, _dev->iestate);
+
+               /* Flush posted write */
+               omap_i2c_read_reg(_dev, OMAP_I2C_STAT_REG);
+       }
 
        return 0;
 }
@@ -1177,23 +1159,40 @@ static int omap_i2c_runtime_resume(struct device *dev)
        struct platform_device *pdev = to_platform_device(dev);
        struct omap_i2c_dev *_dev = platform_get_drvdata(pdev);
 
-       omap_i2c_unidle(_dev);
+       if (_dev->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) {
+               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);
+       }
+
+       /*
+        * Don't write to this register if the IE state is 0 as it can
+        * cause deadlock.
+        */
+       if (_dev->iestate)
+               omap_i2c_write_reg(_dev, OMAP_I2C_IE_REG, _dev->iestate);
 
        return 0;
 }
+#endif /* CONFIG_PM_RUNTIME */
 
 static struct dev_pm_ops omap_i2c_pm_ops = {
-       .runtime_suspend = omap_i2c_runtime_suspend,
-       .runtime_resume = omap_i2c_runtime_resume,
+       SET_RUNTIME_PM_OPS(omap_i2c_runtime_suspend,
+                          omap_i2c_runtime_resume, NULL)
 };
 #define OMAP_I2C_PM_OPS (&omap_i2c_pm_ops)
 #else
 #define OMAP_I2C_PM_OPS NULL
-#endif
+#endif /* CONFIG_PM */
 
 static struct platform_driver omap_i2c_driver = {
        .probe          = omap_i2c_probe,
-       .remove         = omap_i2c_remove,
+       .remove         = __devexit_p(omap_i2c_remove),
        .driver         = {
                .name   = "omap_i2c",
                .owner  = THIS_MODULE,
index 07b7447ecbc9102c7721e27727ab7d25223f35d8..3d71395ae1f763d47ea11f33ba55eeab6ef7c174 100644 (file)
@@ -306,8 +306,7 @@ static int __devinit pmcmsptwi_probe(struct platform_device *pldev)
        pmcmsptwi_data.irq = platform_get_irq(pldev, 0);
        if (pmcmsptwi_data.irq) {
                rc = request_irq(pmcmsptwi_data.irq, &pmcmsptwi_interrupt,
-                       IRQF_SHARED | IRQF_SAMPLE_RANDOM,
-                       pldev->name, &pmcmsptwi_data);
+                                IRQF_SHARED, pldev->name, &pmcmsptwi_data);
                if (rc == 0) {
                        /*
                         * Enable 'DONE' interrupt only.
index 99389d2eae515deaf4a3fee7983bf0ba38ffdd7b..5d54416770b01e7816cc85cd7dcbf403bf407442 100644 (file)
@@ -587,25 +587,27 @@ static struct i2c_algorithm pnx_algorithm = {
 };
 
 #ifdef CONFIG_PM
-static int i2c_pnx_controller_suspend(struct platform_device *pdev,
-                                     pm_message_t state)
+static int i2c_pnx_controller_suspend(struct device *dev)
 {
-       struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev);
+       struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev);
 
        clk_disable(alg_data->clk);
 
        return 0;
 }
 
-static int i2c_pnx_controller_resume(struct platform_device *pdev)
+static int i2c_pnx_controller_resume(struct device *dev)
 {
-       struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev);
+       struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev);
 
        return clk_enable(alg_data->clk);
 }
+
+static SIMPLE_DEV_PM_OPS(i2c_pnx_pm,
+                        i2c_pnx_controller_suspend, i2c_pnx_controller_resume);
+#define PNX_I2C_PM     (&i2c_pnx_pm)
 #else
-#define i2c_pnx_controller_suspend     NULL
-#define i2c_pnx_controller_resume      NULL
+#define PNX_I2C_PM     NULL
 #endif
 
 static int __devinit i2c_pnx_probe(struct platform_device *pdev)
@@ -783,11 +785,10 @@ static struct platform_driver i2c_pnx_driver = {
                .name = "pnx-i2c",
                .owner = THIS_MODULE,
                .of_match_table = of_match_ptr(i2c_pnx_of_match),
+               .pm = PNX_I2C_PM,
        },
        .probe = i2c_pnx_probe,
        .remove = __devexit_p(i2c_pnx_remove),
-       .suspend = i2c_pnx_controller_suspend,
-       .resume = i2c_pnx_controller_resume,
 };
 
 static int __init i2c_adap_pnx_init(void)
index 93709fbe30eb4c248527fa528bce2ae278807b8e..d8515be00b98a8b325620fca2069a06905a06969 100644 (file)
@@ -254,7 +254,7 @@ static int __devexit puv3_i2c_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM
-static int puv3_i2c_suspend(struct platform_device *dev, pm_message_t state)
+static int puv3_i2c_suspend(struct device *dev)
 {
        int poll_count;
        /* Disable the IIC */
@@ -267,23 +267,20 @@ static int puv3_i2c_suspend(struct platform_device *dev, pm_message_t state)
        return 0;
 }
 
-static int puv3_i2c_resume(struct platform_device *dev)
-{
-       return 0 ;
-}
+static SIMPLE_DEV_PM_OPS(puv3_i2c_pm, puv3_i2c_suspend, NULL);
+#define PUV3_I2C_PM    (&puv3_i2c_pm)
+
 #else
-#define puv3_i2c_suspend NULL
-#define puv3_i2c_resume NULL
+#define PUV3_I2C_PM    NULL
 #endif
 
 static struct platform_driver puv3_i2c_driver = {
        .probe          = puv3_i2c_probe,
        .remove         = __devexit_p(puv3_i2c_remove),
-       .suspend        = puv3_i2c_suspend,
-       .resume         = puv3_i2c_resume,
        .driver         = {
                .name   = "PKUnity-v3-I2C",
                .owner  = THIS_MODULE,
+               .pm     = PUV3_I2C_PM,
        }
 };
 
index a997c7d3f95dec538c68dfc8948906b87395df2a..1034d93fb838d0b31774bb172af1e3cb6123bad1 100644 (file)
 
 #include <asm/irq.h>
 
-#ifndef CONFIG_HAVE_CLK
-#define clk_get(dev, id)       NULL
-#define clk_put(clk)           do { } while (0)
-#define clk_disable(clk)       do { } while (0)
-#define clk_enable(clk)                do { } while (0)
-#endif
-
 struct pxa_reg_layout {
        u32 ibmr;
        u32 idbr;
index 01959154572d88f0eb954327759fc66bcf9da5c8..5ae3b0236bd325443cb508d12ca6438d1190276b 100644 (file)
@@ -122,7 +122,7 @@ static inline unsigned int s3c24xx_get_device_quirks(struct platform_device *pde
 {
        if (pdev->dev.of_node) {
                const struct of_device_id *match;
-               match = of_match_node(&s3c24xx_i2c_match, pdev->dev.of_node);
+               match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node);
                return (unsigned int)match->data;
        }
 
@@ -609,7 +609,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
 
                if (ret != -EAGAIN) {
                        clk_disable(i2c->clk);
-                       pm_runtime_put_sync(&adap->dev);
+                       pm_runtime_put(&adap->dev);
                        return ret;
                }
 
@@ -619,7 +619,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
        }
 
        clk_disable(i2c->clk);
-       pm_runtime_put_sync(&adap->dev);
+       pm_runtime_put(&adap->dev);
        return -EREMOTEIO;
 }
 
index 4d44af181f377f2d886fee0d42afb13265a6d32b..580a0c04cb42d6b3b16716b57f5f352ecaa314c1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2009 ST-Ericsson AB
+ * Copyright (C) 2007-2012 ST-Ericsson AB
  * License terms: GNU General Public License (GPL) version 2
  * ST DDC I2C master mode driver, used in e.g. U300 series platforms.
  * Author: Linus Walleij <linus.walleij@stericsson.com>
@@ -139,8 +139,6 @@ module_param(scl_frequency, uint,  0644);
  * struct stu300_dev - the stu300 driver state holder
  * @pdev: parent platform device
  * @adapter: corresponding I2C adapter
- * @phybase: location of I/O area in memory
- * @physize: size of I/O area in memory
  * @clk: hardware block clock
  * @irq: assigned interrupt line
  * @cmd_issue_lock: this locks the following cmd_ variables
@@ -155,8 +153,6 @@ module_param(scl_frequency, uint,  0644);
 struct stu300_dev {
        struct platform_device  *pdev;
        struct i2c_adapter      adapter;
-       resource_size_t         phybase;
-       resource_size_t         physize;
        void __iomem            *virtbase;
        struct clk              *clk;
        int                     irq;
@@ -873,64 +869,44 @@ stu300_probe(struct platform_device *pdev)
        int ret = 0;
        char clk_name[] = "I2C0";
 
-       dev = kzalloc(sizeof(struct stu300_dev), GFP_KERNEL);
+       dev = devm_kzalloc(&pdev->dev, sizeof(struct stu300_dev), GFP_KERNEL);
        if (!dev) {
                dev_err(&pdev->dev, "could not allocate device struct\n");
-               ret = -ENOMEM;
-               goto err_no_devmem;
+               return -ENOMEM;
        }
 
        bus_nr = pdev->id;
        clk_name[3] += (char)bus_nr;
-       dev->clk = clk_get(&pdev->dev, clk_name);
+       dev->clk = devm_clk_get(&pdev->dev, clk_name);
        if (IS_ERR(dev->clk)) {
-               ret = PTR_ERR(dev->clk);
                dev_err(&pdev->dev, "could not retrieve i2c bus clock\n");
-               goto err_no_clk;
+               return PTR_ERR(dev->clk);
        }
 
        dev->pdev = pdev;
-       platform_set_drvdata(pdev, dev);
-
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               ret = -ENOENT;
-               goto err_no_resource;
-       }
-
-       dev->phybase = res->start;
-       dev->physize = resource_size(res);
-
-       if (request_mem_region(dev->phybase, dev->physize,
-                              NAME " I/O Area") == NULL) {
-               ret = -EBUSY;
-               goto err_no_ioregion;
-       }
+       if (!res)
+               return -ENOENT;
 
-       dev->virtbase = ioremap(dev->phybase, dev->physize);
+       dev->virtbase = devm_request_and_ioremap(&pdev->dev, res);
        dev_dbg(&pdev->dev, "initialize bus device I2C%d on virtual "
                "base %p\n", bus_nr, dev->virtbase);
-       if (!dev->virtbase) {
-               ret = -ENOMEM;
-               goto err_no_ioremap;
-       }
+       if (!dev->virtbase)
+               return -ENOMEM;
 
        dev->irq = platform_get_irq(pdev, 0);
-       if (request_irq(dev->irq, stu300_irh, 0,
-                       NAME, dev)) {
-               ret = -EIO;
-               goto err_no_irq;
-       }
+       ret = devm_request_irq(&pdev->dev, dev->irq, stu300_irh, 0, NAME, dev);
+       if (ret < 0)
+               return ret;
 
        dev->speed = scl_frequency;
 
-       clk_enable(dev->clk);
+       clk_prepare_enable(dev->clk);
        ret = stu300_init_hw(dev);
        clk_disable(dev->clk);
-
        if (ret != 0) {
                dev_err(&dev->pdev->dev, "error initializing hardware.\n");
-               goto err_init_hw;
+               return -EIO;
        }
 
        /* IRQ event handling initialization */
@@ -952,57 +928,43 @@ stu300_probe(struct platform_device *pdev)
        /* i2c device drivers may be active on return from add_adapter() */
        ret = i2c_add_numbered_adapter(adap);
        if (ret) {
-               dev_err(&dev->pdev->dev, "failure adding ST Micro DDC "
+               dev_err(&pdev->dev, "failure adding ST Micro DDC "
                       "I2C adapter\n");
-               goto err_add_adapter;
+               return ret;
        }
-       return 0;
 
- err_add_adapter:
- err_init_hw:
-       free_irq(dev->irq, dev);
- err_no_irq:
-       iounmap(dev->virtbase);
- err_no_ioremap:
-       release_mem_region(dev->phybase, dev->physize);
- err_no_ioregion:
-       platform_set_drvdata(pdev, NULL);
- err_no_resource:
-       clk_put(dev->clk);
- err_no_clk:
-       kfree(dev);
- err_no_devmem:
-       dev_err(&pdev->dev, "failed to add " NAME " adapter: %d\n",
-               pdev->id);
-       return ret;
+       platform_set_drvdata(pdev, dev);
+       return 0;
 }
 
 #ifdef CONFIG_PM
-static int stu300_suspend(struct platform_device *pdev, pm_message_t state)
+static int stu300_suspend(struct device *device)
 {
-       struct stu300_dev *dev = platform_get_drvdata(pdev);
+       struct stu300_dev *dev = dev_get_drvdata(device);
 
        /* Turn off everything */
        stu300_wr8(0x00, dev->virtbase + I2C_CR);
        return 0;
 }
 
-static int stu300_resume(struct platform_device *pdev)
+static int stu300_resume(struct device *device)
 {
        int ret = 0;
-       struct stu300_dev *dev = platform_get_drvdata(pdev);
+       struct stu300_dev *dev = dev_get_drvdata(device);
 
        clk_enable(dev->clk);
        ret = stu300_init_hw(dev);
        clk_disable(dev->clk);
 
        if (ret != 0)
-               dev_err(&pdev->dev, "error re-initializing hardware.\n");
+               dev_err(device, "error re-initializing hardware.\n");
        return ret;
 }
+
+static SIMPLE_DEV_PM_OPS(stu300_pm, stu300_suspend, stu300_resume);
+#define STU300_I2C_PM  (&stu300_pm)
 #else
-#define stu300_suspend NULL
-#define stu300_resume NULL
+#define STU300_I2C_PM  NULL
 #endif
 
 static int __exit
@@ -1013,12 +975,7 @@ stu300_remove(struct platform_device *pdev)
        i2c_del_adapter(&dev->adapter);
        /* Turn off everything */
        stu300_wr8(0x00, dev->virtbase + I2C_CR);
-       free_irq(dev->irq, dev);
-       iounmap(dev->virtbase);
-       release_mem_region(dev->phybase, dev->physize);
-       clk_put(dev->clk);
        platform_set_drvdata(pdev, NULL);
-       kfree(dev);
        return 0;
 }
 
@@ -1026,10 +983,9 @@ static struct platform_driver stu300_i2c_driver = {
        .driver = {
                .name   = NAME,
                .owner  = THIS_MODULE,
+               .pm     = STU300_I2C_PM,
        },
        .remove         = __exit_p(stu300_remove),
-       .suspend        = stu300_suspend,
-       .resume         = stu300_resume,
 
 };
 
index 3da7ee3eb505e0816d809093958f0259816c3450..66eb53fac2022363c3cf90fa96d0ad6358234acc 100644 (file)
 #define I2C_HEADER_10BIT_ADDR                  (1<<18)
 #define I2C_HEADER_IE_ENABLE                   (1<<17)
 #define I2C_HEADER_REPEAT_START                        (1<<16)
+#define I2C_HEADER_CONTINUE_XFER               (1<<15)
 #define I2C_HEADER_MASTER_ADDR_SHIFT           12
 #define I2C_HEADER_SLAVE_ADDR_SHIFT            1
+/*
+ * msg_end_type: The bus control which need to be send at end of transfer.
+ * @MSG_END_STOP: Send stop pulse at end of transfer.
+ * @MSG_END_REPEAT_START: Send repeat start at end of transfer.
+ * @MSG_END_CONTINUE: The following on message is coming and so do not send
+ *             stop or repeat start.
+ */
+enum msg_end_type {
+       MSG_END_STOP,
+       MSG_END_REPEAT_START,
+       MSG_END_CONTINUE,
+};
 
 /**
  * struct tegra_i2c_dev        - per device i2c context
  * @adapter: core i2c layer adapter information
  * @clk: clock reference for i2c controller
  * @i2c_clk: clock reference for i2c bus
- * @iomem: memory resource for registers
  * @base: ioremapped registers cookie
  * @cont_id: i2c controller id, used for for packet header
  * @irq: irq number of transfer complete interrupt
@@ -124,7 +136,6 @@ struct tegra_i2c_dev {
        struct i2c_adapter adapter;
        struct clk *clk;
        struct clk *i2c_clk;
-       struct resource *iomem;
        void __iomem *base;
        int cont_id;
        int irq;
@@ -165,6 +176,10 @@ static void i2c_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
        unsigned long reg)
 {
        writel(val, i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
+
+       /* Read back register to make sure that register writes completed */
+       if (reg != I2C_TX_FIFO)
+               readl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
 }
 
 static u32 i2c_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg)
@@ -449,7 +464,7 @@ err:
 }
 
 static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
-       struct i2c_msg *msg, int stop)
+       struct i2c_msg *msg, enum msg_end_type end_state)
 {
        u32 packet_header;
        u32 int_mask;
@@ -476,7 +491,9 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
        i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
 
        packet_header = I2C_HEADER_IE_ENABLE;
-       if (!stop)
+       if (end_state == MSG_END_CONTINUE)
+               packet_header |= I2C_HEADER_CONTINUE_XFER;
+       else if (end_state == MSG_END_REPEAT_START)
                packet_header |= I2C_HEADER_REPEAT_START;
        if (msg->flags & I2C_M_TEN) {
                packet_header |= msg->addr;
@@ -548,8 +565,14 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
 
        clk_prepare_enable(i2c_dev->clk);
        for (i = 0; i < num; i++) {
-               int stop = (i == (num - 1)) ? 1  : 0;
-               ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], stop);
+               enum msg_end_type end_type = MSG_END_STOP;
+               if (i < (num - 1)) {
+                       if (msgs[i + 1].flags & I2C_M_NOSTART)
+                               end_type = MSG_END_CONTINUE;
+                       else
+                               end_type = MSG_END_REPEAT_START;
+               }
+               ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type);
                if (ret)
                        break;
        }
@@ -559,7 +582,8 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
 
 static u32 tegra_i2c_func(struct i2c_adapter *adap)
 {
-       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
+               I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART;
 }
 
 static const struct i2c_algorithm tegra_i2c_algo = {
@@ -572,7 +596,6 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
        struct tegra_i2c_dev *i2c_dev;
        struct tegra_i2c_platform_data *pdata = pdev->dev.platform_data;
        struct resource *res;
-       struct resource *iomem;
        struct clk *clk;
        struct clk *i2c_clk;
        const unsigned int *prop;
@@ -585,50 +608,41 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "no mem resource\n");
                return -EINVAL;
        }
-       iomem = request_mem_region(res->start, resource_size(res), pdev->name);
-       if (!iomem) {
-               dev_err(&pdev->dev, "I2C region already claimed\n");
-               return -EBUSY;
-       }
 
-       base = ioremap(iomem->start, resource_size(iomem));
+       base = devm_request_and_ioremap(&pdev->dev, res);
        if (!base) {
-               dev_err(&pdev->dev, "Cannot ioremap I2C region\n");
-               return -ENOMEM;
+               dev_err(&pdev->dev, "Cannot request/ioremap I2C registers\n");
+               return -EADDRNOTAVAIL;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (!res) {
                dev_err(&pdev->dev, "no irq resource\n");
-               ret = -EINVAL;
-               goto err_iounmap;
+               return -EINVAL;
        }
        irq = res->start;
 
-       clk = clk_get(&pdev->dev, NULL);
+       clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(clk)) {
                dev_err(&pdev->dev, "missing controller clock");
-               ret = PTR_ERR(clk);
-               goto err_release_region;
+               return PTR_ERR(clk);
        }
 
-       i2c_clk = clk_get(&pdev->dev, "i2c");
+       i2c_clk = devm_clk_get(&pdev->dev, "i2c");
        if (IS_ERR(i2c_clk)) {
                dev_err(&pdev->dev, "missing bus clock");
-               ret = PTR_ERR(i2c_clk);
-               goto err_clk_put;
+               return PTR_ERR(i2c_clk);
        }
 
-       i2c_dev = kzalloc(sizeof(struct tegra_i2c_dev), GFP_KERNEL);
+       i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
        if (!i2c_dev) {
-               ret = -ENOMEM;
-               goto err_i2c_clk_put;
+               dev_err(&pdev->dev, "Could not allocate struct tegra_i2c_dev");
+               return -ENOMEM;
        }
 
        i2c_dev->base = base;
        i2c_dev->clk = clk;
        i2c_dev->i2c_clk = i2c_clk;
-       i2c_dev->iomem = iomem;
        i2c_dev->adapter.algo = &tegra_i2c_algo;
        i2c_dev->irq = irq;
        i2c_dev->cont_id = pdev->id;
@@ -657,13 +671,14 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
        ret = tegra_i2c_init(i2c_dev);
        if (ret) {
                dev_err(&pdev->dev, "Failed to initialize i2c controller");
-               goto err_free;
+               return ret;
        }
 
-       ret = request_irq(i2c_dev->irq, tegra_i2c_isr, 0, pdev->name, i2c_dev);
+       ret = devm_request_irq(&pdev->dev, i2c_dev->irq,
+                       tegra_i2c_isr, 0, pdev->name, i2c_dev);
        if (ret) {
                dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq);
-               goto err_free;
+               return ret;
        }
 
        clk_prepare_enable(i2c_dev->i2c_clk);
@@ -681,45 +696,26 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
        ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
        if (ret) {
                dev_err(&pdev->dev, "Failed to add I2C adapter\n");
-               goto err_free_irq;
+               clk_disable_unprepare(i2c_dev->i2c_clk);
+               return ret;
        }
 
        of_i2c_register_devices(&i2c_dev->adapter);
 
        return 0;
-err_free_irq:
-       free_irq(i2c_dev->irq, i2c_dev);
-err_free:
-       kfree(i2c_dev);
-err_i2c_clk_put:
-       clk_put(i2c_clk);
-err_clk_put:
-       clk_put(clk);
-err_release_region:
-       release_mem_region(iomem->start, resource_size(iomem));
-err_iounmap:
-       iounmap(base);
-       return ret;
 }
 
 static int __devexit tegra_i2c_remove(struct platform_device *pdev)
 {
        struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
        i2c_del_adapter(&i2c_dev->adapter);
-       free_irq(i2c_dev->irq, i2c_dev);
-       clk_put(i2c_dev->i2c_clk);
-       clk_put(i2c_dev->clk);
-       release_mem_region(i2c_dev->iomem->start,
-               resource_size(i2c_dev->iomem));
-       iounmap(i2c_dev->base);
-       kfree(i2c_dev);
        return 0;
 }
 
 #ifdef CONFIG_PM
-static int tegra_i2c_suspend(struct platform_device *pdev, pm_message_t state)
+static int tegra_i2c_suspend(struct device *dev)
 {
-       struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+       struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
 
        i2c_lock_adapter(&i2c_dev->adapter);
        i2c_dev->is_suspended = true;
@@ -728,9 +724,9 @@ static int tegra_i2c_suspend(struct platform_device *pdev, pm_message_t state)
        return 0;
 }
 
-static int tegra_i2c_resume(struct platform_device *pdev)
+static int tegra_i2c_resume(struct device *dev)
 {
-       struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+       struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
        int ret;
 
        i2c_lock_adapter(&i2c_dev->adapter);
@@ -748,6 +744,11 @@ static int tegra_i2c_resume(struct platform_device *pdev)
 
        return 0;
 }
+
+static SIMPLE_DEV_PM_OPS(tegra_i2c_pm, tegra_i2c_suspend, tegra_i2c_resume);
+#define TEGRA_I2C_PM   (&tegra_i2c_pm)
+#else
+#define TEGRA_I2C_PM   NULL
 #endif
 
 #if defined(CONFIG_OF)
@@ -758,21 +759,16 @@ static const struct of_device_id tegra_i2c_of_match[] __devinitconst = {
        {},
 };
 MODULE_DEVICE_TABLE(of, tegra_i2c_of_match);
-#else
-#define tegra_i2c_of_match NULL
 #endif
 
 static struct platform_driver tegra_i2c_driver = {
        .probe   = tegra_i2c_probe,
        .remove  = __devexit_p(tegra_i2c_remove),
-#ifdef CONFIG_PM
-       .suspend = tegra_i2c_suspend,
-       .resume  = tegra_i2c_resume,
-#endif
        .driver  = {
                .name  = "tegra-i2c",
                .owner = THIS_MODULE,
-               .of_match_table = tegra_i2c_of_match,
+               .of_match_table = of_match_ptr(tegra_i2c_of_match),
+               .pm    = TEGRA_I2C_PM,
        },
 };
 
index 26488aa893d5e5dc62ac2d01c332dd2d4ca9d2a1..2efa56c5ff2c32d10ff3018def5bc077b8492e4e 100644 (file)
@@ -1311,6 +1311,37 @@ module_exit(i2c_exit);
  * ----------------------------------------------------
  */
 
+/**
+ * __i2c_transfer - unlocked flavor of i2c_transfer
+ * @adap: Handle to I2C bus
+ * @msgs: One or more messages to execute before STOP is issued to
+ *     terminate the operation; each message begins with a START.
+ * @num: Number of messages to be executed.
+ *
+ * Returns negative errno, else the number of messages executed.
+ *
+ * Adapter lock must be held when calling this function. No debug logging
+ * takes place. adap->algo->master_xfer existence isn't checked.
+ */
+int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+       unsigned long orig_jiffies;
+       int ret, try;
+
+       /* Retry automatically on arbitration loss */
+       orig_jiffies = jiffies;
+       for (ret = 0, try = 0; try <= adap->retries; try++) {
+               ret = adap->algo->master_xfer(adap, msgs, num);
+               if (ret != -EAGAIN)
+                       break;
+               if (time_after(jiffies, orig_jiffies + adap->timeout))
+                       break;
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(__i2c_transfer);
+
 /**
  * i2c_transfer - execute a single or combined I2C message
  * @adap: Handle to I2C bus
@@ -1325,8 +1356,7 @@ module_exit(i2c_exit);
  */
 int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 {
-       unsigned long orig_jiffies;
-       int ret, try;
+       int ret;
 
        /* REVISIT the fault reporting model here is weak:
         *
@@ -1364,15 +1394,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
                        i2c_lock_adapter(adap);
                }
 
-               /* Retry automatically on arbitration loss */
-               orig_jiffies = jiffies;
-               for (ret = 0, try = 0; try <= adap->retries; try++) {
-                       ret = adap->algo->master_xfer(adap, msgs, num);
-                       if (ret != -EAGAIN)
-                               break;
-                       if (time_after(jiffies, orig_jiffies + adap->timeout))
-                               break;
-               }
+               ret = __i2c_transfer(adap, msgs, num);
                i2c_unlock_adapter(adap);
 
                return ret;
index 5a335b5447c668856da82fbdb98a49c0b28e89e1..7172559ce0c1486042a0d5396d8898a10cd31663 100644 (file)
@@ -3064,10 +3064,7 @@ static int cma_join_ib_multicast(struct rdma_id_private *id_priv,
                                                id_priv->id.port_num, &rec,
                                                comp_mask, GFP_KERNEL,
                                                cma_ib_mc_handler, mc);
-       if (IS_ERR(mc->multicast.ib))
-               return PTR_ERR(mc->multicast.ib);
-
-       return 0;
+       return PTR_RET(mc->multicast.ib);
 }
 
 static void iboe_mcast_work_handler(struct work_struct *work)
index 893cb879462cfe584ea26c314b838bdcd3e3a41f..6bf8504228957eb8341342c16cfa9b88a234c8bd 100644 (file)
@@ -1002,23 +1002,18 @@ static ssize_t ucma_set_option(struct ucma_file *file, const char __user *inbuf,
        if (IS_ERR(ctx))
                return PTR_ERR(ctx);
 
-       optval = kmalloc(cmd.optlen, GFP_KERNEL);
-       if (!optval) {
-               ret = -ENOMEM;
-               goto out1;
-       }
-
-       if (copy_from_user(optval, (void __user *) (unsigned long) cmd.optval,
-                          cmd.optlen)) {
-               ret = -EFAULT;
-               goto out2;
+       optval = memdup_user((void __user *) (unsigned long) cmd.optval,
+                            cmd.optlen);
+       if (IS_ERR(optval)) {
+               ret = PTR_ERR(optval);
+               goto out;
        }
 
        ret = ucma_set_option_level(ctx, cmd.level, cmd.optname, optval,
                                    cmd.optlen);
-out2:
        kfree(optval);
-out1:
+
+out:
        ucma_put_ctx(ctx);
        return ret;
 }
index b2f9784beb4ad88a34243388563b230182f16db8..cb5b7f7d4d3876904bb6b515273747c37787465d 100644 (file)
@@ -893,7 +893,9 @@ static int ocrdma_check_qp_params(struct ib_pd *ibpd, struct ocrdma_dev *dev,
        /* verify consumer QPs are not trying to use GSI QP's CQ */
        if ((attrs->qp_type != IB_QPT_GSI) && (dev->gsi_qp_created)) {
                if ((dev->gsi_sqcq == get_ocrdma_cq(attrs->send_cq)) ||
-                   (dev->gsi_sqcq == get_ocrdma_cq(attrs->send_cq))) {
+                   (dev->gsi_sqcq == get_ocrdma_cq(attrs->recv_cq)) ||
+                   (dev->gsi_rqcq == get_ocrdma_cq(attrs->send_cq)) ||
+                   (dev->gsi_rqcq == get_ocrdma_cq(attrs->recv_cq))) {
                        ocrdma_err("%s(%d) Consumer QP cannot use GSI CQs.\n",
                                   __func__, dev->id);
                        return -EINVAL;
index 6e19ec844d9982122014df601ed64a975056502e..7b1b86690024d5e7a19b8c332c0270e74ae55f83 100644 (file)
@@ -656,6 +656,11 @@ struct qib_pportdata {
        /* 16 congestion entries with each entry corresponding to a SL */
        struct ib_cc_congestion_entry_shadow *congestion_entries;
 
+       /* Maximum number of congestion control entries that the agent expects
+        * the manager to send.
+        */
+       u16 cc_supported_table_entries;
+
        /* Total number of congestion control table entries */
        u16 total_cct_entry;
 
@@ -667,11 +672,6 @@ struct qib_pportdata {
 
        /* CA's max number of 64 entry units in the congestion control table */
        u8 cc_max_table_entries;
-
-       /* Maximum number of congestion control entries that the agent expects
-        * the manager to send.
-        */
-       u8 cc_supported_table_entries;
 };
 
 /* Observers. Not to be taken lightly, possibly not to ship. */
index 86df632ea6121f94382071de8e0a17258a361a7c..ca43901ed861b8d2bc31c70a9e570d9db3d2f8c1 100644 (file)
@@ -92,6 +92,8 @@ enum {
        IPOIB_STOP_REAPER         = 7,
        IPOIB_FLAG_ADMIN_CM       = 9,
        IPOIB_FLAG_UMCAST         = 10,
+       IPOIB_STOP_NEIGH_GC       = 11,
+       IPOIB_NEIGH_TBL_FLUSH     = 12,
 
        IPOIB_MAX_BACKOFF_SECONDS = 16,
 
@@ -260,6 +262,20 @@ struct ipoib_ethtool_st {
        u16     max_coalesced_frames;
 };
 
+struct ipoib_neigh_hash {
+       struct ipoib_neigh __rcu      **buckets;
+       struct rcu_head                 rcu;
+       u32                             mask;
+       u32                             size;
+};
+
+struct ipoib_neigh_table {
+       struct ipoib_neigh_hash __rcu  *htbl;
+       rwlock_t                        rwlock;
+       atomic_t                        entries;
+       struct completion               flushed;
+};
+
 /*
  * Device private locking: network stack tx_lock protects members used
  * in TX fast path, lock protects everything else.  lock nests inside
@@ -279,6 +295,8 @@ struct ipoib_dev_priv {
        struct rb_root  path_tree;
        struct list_head path_list;
 
+       struct ipoib_neigh_table ntbl;
+
        struct ipoib_mcast *broadcast;
        struct list_head multicast_list;
        struct rb_root multicast_tree;
@@ -291,7 +309,7 @@ struct ipoib_dev_priv {
        struct work_struct flush_heavy;
        struct work_struct restart_task;
        struct delayed_work ah_reap_task;
-
+       struct delayed_work neigh_reap_task;
        struct ib_device *ca;
        u8                port;
        u16               pkey;
@@ -377,13 +395,16 @@ struct ipoib_neigh {
 #ifdef CONFIG_INFINIBAND_IPOIB_CM
        struct ipoib_cm_tx *cm;
 #endif
-       union ib_gid        dgid;
+       u8     daddr[INFINIBAND_ALEN];
        struct sk_buff_head queue;
 
-       struct neighbour   *neighbour;
        struct net_device *dev;
 
        struct list_head    list;
+       struct ipoib_neigh __rcu *hnext;
+       struct rcu_head     rcu;
+       atomic_t            refcnt;
+       unsigned long       alive;
 };
 
 #define IPOIB_UD_MTU(ib_mtu)           (ib_mtu - IPOIB_ENCAP_LEN)
@@ -394,21 +415,17 @@ static inline int ipoib_ud_need_sg(unsigned int ib_mtu)
        return IPOIB_UD_BUF_SIZE(ib_mtu) > PAGE_SIZE;
 }
 
-/*
- * We stash a pointer to our private neighbour information after our
- * hardware address in neigh->ha.  The ALIGN() expression here makes
- * sure that this pointer is stored aligned so that an unaligned
- * load is not needed to dereference it.
- */
-static inline struct ipoib_neigh **to_ipoib_neigh(struct neighbour *neigh)
+void ipoib_neigh_dtor(struct ipoib_neigh *neigh);
+static inline void ipoib_neigh_put(struct ipoib_neigh *neigh)
 {
-       return (void*) neigh + ALIGN(offsetof(struct neighbour, ha) +
-                                    INFINIBAND_ALEN, sizeof(void *));
+       if (atomic_dec_and_test(&neigh->refcnt))
+               ipoib_neigh_dtor(neigh);
 }
-
-struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neigh,
+struct ipoib_neigh *ipoib_neigh_get(struct net_device *dev, u8 *daddr);
+struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
                                      struct net_device *dev);
-void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh);
+void ipoib_neigh_free(struct ipoib_neigh *neigh);
+void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid);
 
 extern struct workqueue_struct *ipoib_workqueue;
 
@@ -425,7 +442,6 @@ static inline void ipoib_put_ah(struct ipoib_ah *ah)
 {
        kref_put(&ah->ref, ipoib_free_ah);
 }
-
 int ipoib_open(struct net_device *dev);
 int ipoib_add_pkey_attr(struct net_device *dev);
 int ipoib_add_umcast_attr(struct net_device *dev);
@@ -455,7 +471,7 @@ void ipoib_dev_cleanup(struct net_device *dev);
 
 void ipoib_mcast_join_task(struct work_struct *work);
 void ipoib_mcast_carrier_on_task(struct work_struct *work);
-void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb);
+void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb);
 
 void ipoib_mcast_restart_task(struct work_struct *work);
 int ipoib_mcast_start_thread(struct net_device *dev);
@@ -517,10 +533,10 @@ static inline int ipoib_cm_admin_enabled(struct net_device *dev)
                test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
 }
 
-static inline int ipoib_cm_enabled(struct net_device *dev, struct neighbour *n)
+static inline int ipoib_cm_enabled(struct net_device *dev, u8 *hwaddr)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
-       return IPOIB_CM_SUPPORTED(n->ha) &&
+       return IPOIB_CM_SUPPORTED(hwaddr) &&
                test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
 }
 
@@ -575,7 +591,7 @@ static inline int ipoib_cm_admin_enabled(struct net_device *dev)
 {
        return 0;
 }
-static inline int ipoib_cm_enabled(struct net_device *dev, struct neighbour *n)
+static inline int ipoib_cm_enabled(struct net_device *dev, u8 *hwaddr)
 
 {
        return 0;
index 6d66ab0dd92a7ea0032d0e43a69ea38cceb21041..95ecf4eadf5f75949f6a7e14acfa01a50fbdc0cd 100644 (file)
@@ -811,9 +811,7 @@ void ipoib_cm_handle_tx_wc(struct net_device *dev, struct ib_wc *wc)
                if (neigh) {
                        neigh->cm = NULL;
                        list_del(&neigh->list);
-                       if (neigh->ah)
-                               ipoib_put_ah(neigh->ah);
-                       ipoib_neigh_free(dev, neigh);
+                       ipoib_neigh_free(neigh);
 
                        tx->neigh = NULL;
                }
@@ -1230,9 +1228,7 @@ static int ipoib_cm_tx_handler(struct ib_cm_id *cm_id,
                if (neigh) {
                        neigh->cm = NULL;
                        list_del(&neigh->list);
-                       if (neigh->ah)
-                               ipoib_put_ah(neigh->ah);
-                       ipoib_neigh_free(dev, neigh);
+                       ipoib_neigh_free(neigh);
 
                        tx->neigh = NULL;
                }
@@ -1279,7 +1275,7 @@ void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx)
                list_move(&tx->list, &priv->cm.reap_list);
                queue_work(ipoib_workqueue, &priv->cm.reap_task);
                ipoib_dbg(priv, "Reap connection for gid %pI6\n",
-                         tx->neigh->dgid.raw);
+                         tx->neigh->daddr + 4);
                tx->neigh = NULL;
        }
 }
@@ -1304,7 +1300,7 @@ static void ipoib_cm_tx_start(struct work_struct *work)
                p = list_entry(priv->cm.start_list.next, typeof(*p), list);
                list_del_init(&p->list);
                neigh = p->neigh;
-               qpn = IPOIB_QPN(neigh->neighbour->ha);
+               qpn = IPOIB_QPN(neigh->daddr);
                memcpy(&pathrec, &p->path->pathrec, sizeof pathrec);
 
                spin_unlock_irqrestore(&priv->lock, flags);
@@ -1320,9 +1316,7 @@ static void ipoib_cm_tx_start(struct work_struct *work)
                        if (neigh) {
                                neigh->cm = NULL;
                                list_del(&neigh->list);
-                               if (neigh->ah)
-                                       ipoib_put_ah(neigh->ah);
-                               ipoib_neigh_free(dev, neigh);
+                               ipoib_neigh_free(neigh);
                        }
                        list_del(&p->list);
                        kfree(p);
index bbee4b2d7a13b46b847a56f8f1538de8fe5ec9be..97920b77a5d0abe2f378ed060aa8243a2c5c33e7 100644 (file)
@@ -46,7 +46,8 @@
 #include <linux/ip.h>
 #include <linux/in.h>
 
-#include <net/dst.h>
+#include <linux/jhash.h>
+#include <net/arp.h>
 
 MODULE_AUTHOR("Roland Dreier");
 MODULE_DESCRIPTION("IP-over-InfiniBand net driver");
@@ -84,6 +85,7 @@ struct ib_sa_client ipoib_sa_client;
 
 static void ipoib_add_one(struct ib_device *device);
 static void ipoib_remove_one(struct ib_device *device);
+static void ipoib_neigh_reclaim(struct rcu_head *rp);
 
 static struct ib_client ipoib_client = {
        .name   = "ipoib",
@@ -264,30 +266,15 @@ static int __path_add(struct net_device *dev, struct ipoib_path *path)
 
 static void path_free(struct net_device *dev, struct ipoib_path *path)
 {
-       struct ipoib_dev_priv *priv = netdev_priv(dev);
-       struct ipoib_neigh *neigh, *tn;
        struct sk_buff *skb;
-       unsigned long flags;
 
        while ((skb = __skb_dequeue(&path->queue)))
                dev_kfree_skb_irq(skb);
 
-       spin_lock_irqsave(&priv->lock, flags);
-
-       list_for_each_entry_safe(neigh, tn, &path->neigh_list, list) {
-               /*
-                * It's safe to call ipoib_put_ah() inside priv->lock
-                * here, because we know that path->ah will always
-                * hold one more reference, so ipoib_put_ah() will
-                * never do more than decrement the ref count.
-                */
-               if (neigh->ah)
-                       ipoib_put_ah(neigh->ah);
-
-               ipoib_neigh_free(dev, neigh);
-       }
+       ipoib_dbg(netdev_priv(dev), "path_free\n");
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+       /* remove all neigh connected to this path */
+       ipoib_del_neighs_by_gid(dev, path->pathrec.dgid.raw);
 
        if (path->ah)
                ipoib_put_ah(path->ah);
@@ -458,19 +445,15 @@ static void path_rec_completion(int status,
                        }
                        kref_get(&path->ah->ref);
                        neigh->ah = path->ah;
-                       memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw,
-                              sizeof(union ib_gid));
 
-                       if (ipoib_cm_enabled(dev, neigh->neighbour)) {
+                       if (ipoib_cm_enabled(dev, neigh->daddr)) {
                                if (!ipoib_cm_get(neigh))
                                        ipoib_cm_set(neigh, ipoib_cm_create_tx(dev,
                                                                               path,
                                                                               neigh));
                                if (!ipoib_cm_get(neigh)) {
                                        list_del(&neigh->list);
-                                       if (neigh->ah)
-                                               ipoib_put_ah(neigh->ah);
-                                       ipoib_neigh_free(dev, neigh);
+                                       ipoib_neigh_free(neigh);
                                        continue;
                                }
                        }
@@ -555,15 +538,15 @@ static int path_rec_start(struct net_device *dev,
        return 0;
 }
 
-/* called with rcu_read_lock */
-static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_device *dev)
+static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
+                          struct net_device *dev)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
        struct ipoib_path *path;
        struct ipoib_neigh *neigh;
        unsigned long flags;
 
-       neigh = ipoib_neigh_alloc(n, skb->dev);
+       neigh = ipoib_neigh_alloc(daddr, dev);
        if (!neigh) {
                ++dev->stats.tx_dropped;
                dev_kfree_skb_any(skb);
@@ -572,9 +555,9 @@ static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_
 
        spin_lock_irqsave(&priv->lock, flags);
 
-       path = __path_find(dev, n->ha + 4);
+       path = __path_find(dev, daddr + 4);
        if (!path) {
-               path = path_rec_create(dev, n->ha + 4);
+               path = path_rec_create(dev, daddr + 4);
                if (!path)
                        goto err_path;
 
@@ -586,17 +569,13 @@ static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_
        if (path->ah) {
                kref_get(&path->ah->ref);
                neigh->ah = path->ah;
-               memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw,
-                      sizeof(union ib_gid));
 
-               if (ipoib_cm_enabled(dev, neigh->neighbour)) {
+               if (ipoib_cm_enabled(dev, neigh->daddr)) {
                        if (!ipoib_cm_get(neigh))
                                ipoib_cm_set(neigh, ipoib_cm_create_tx(dev, path, neigh));
                        if (!ipoib_cm_get(neigh)) {
                                list_del(&neigh->list);
-                               if (neigh->ah)
-                                       ipoib_put_ah(neigh->ah);
-                               ipoib_neigh_free(dev, neigh);
+                               ipoib_neigh_free(neigh);
                                goto err_drop;
                        }
                        if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE)
@@ -608,7 +587,8 @@ static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_
                        }
                } else {
                        spin_unlock_irqrestore(&priv->lock, flags);
-                       ipoib_send(dev, skb, path->ah, IPOIB_QPN(n->ha));
+                       ipoib_send(dev, skb, path->ah, IPOIB_QPN(daddr));
+                       ipoib_neigh_put(neigh);
                        return;
                }
        } else {
@@ -621,35 +601,20 @@ static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_
        }
 
        spin_unlock_irqrestore(&priv->lock, flags);
+       ipoib_neigh_put(neigh);
        return;
 
 err_list:
        list_del(&neigh->list);
 
 err_path:
-       ipoib_neigh_free(dev, neigh);
+       ipoib_neigh_free(neigh);
 err_drop:
        ++dev->stats.tx_dropped;
        dev_kfree_skb_any(skb);
 
        spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-/* called with rcu_read_lock */
-static void ipoib_path_lookup(struct sk_buff *skb, struct neighbour *n, struct net_device *dev)
-{
-       struct ipoib_dev_priv *priv = netdev_priv(skb->dev);
-
-       /* Look up path record for unicasts */
-       if (n->ha[4] != 0xff) {
-               neigh_add_path(skb, n, dev);
-               return;
-       }
-
-       /* Add in the P_Key for multicasts */
-       n->ha[8] = (priv->pkey >> 8) & 0xff;
-       n->ha[9] = priv->pkey & 0xff;
-       ipoib_mcast_send(dev, n->ha + 4, skb);
+       ipoib_neigh_put(neigh);
 }
 
 static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
@@ -710,96 +675,80 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
        struct ipoib_neigh *neigh;
-       struct neighbour *n = NULL;
+       struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb;
+       struct ipoib_header *header;
        unsigned long flags;
 
-       rcu_read_lock();
-       if (likely(skb_dst(skb))) {
-               n = dst_neigh_lookup_skb(skb_dst(skb), skb);
-               if (!n) {
+       header = (struct ipoib_header *) skb->data;
+
+       if (unlikely(cb->hwaddr[4] == 0xff)) {
+               /* multicast, arrange "if" according to probability */
+               if ((header->proto != htons(ETH_P_IP)) &&
+                   (header->proto != htons(ETH_P_IPV6)) &&
+                   (header->proto != htons(ETH_P_ARP)) &&
+                   (header->proto != htons(ETH_P_RARP))) {
+                       /* ethertype not supported by IPoIB */
                        ++dev->stats.tx_dropped;
                        dev_kfree_skb_any(skb);
-                       goto unlock;
+                       return NETDEV_TX_OK;
                }
+               /* Add in the P_Key for multicast*/
+               cb->hwaddr[8] = (priv->pkey >> 8) & 0xff;
+               cb->hwaddr[9] = priv->pkey & 0xff;
+
+               neigh = ipoib_neigh_get(dev, cb->hwaddr);
+               if (likely(neigh))
+                       goto send_using_neigh;
+               ipoib_mcast_send(dev, cb->hwaddr, skb);
+               return NETDEV_TX_OK;
        }
-       if (likely(n)) {
-               if (unlikely(!*to_ipoib_neigh(n))) {
-                       ipoib_path_lookup(skb, n, dev);
-                       goto unlock;
-               }
-
-               neigh = *to_ipoib_neigh(n);
 
-               if (unlikely((memcmp(&neigh->dgid.raw,
-                                    n->ha + 4,
-                                    sizeof(union ib_gid))) ||
-                            (neigh->dev != dev))) {
-                       spin_lock_irqsave(&priv->lock, flags);
-                       /*
-                        * It's safe to call ipoib_put_ah() inside
-                        * priv->lock here, because we know that
-                        * path->ah will always hold one more reference,
-                        * so ipoib_put_ah() will never do more than
-                        * decrement the ref count.
-                        */
-                       if (neigh->ah)
-                               ipoib_put_ah(neigh->ah);
-                       list_del(&neigh->list);
-                       ipoib_neigh_free(dev, neigh);
-                       spin_unlock_irqrestore(&priv->lock, flags);
-                       ipoib_path_lookup(skb, n, dev);
-                       goto unlock;
+       /* unicast, arrange "switch" according to probability */
+       switch (header->proto) {
+       case htons(ETH_P_IP):
+       case htons(ETH_P_IPV6):
+               neigh = ipoib_neigh_get(dev, cb->hwaddr);
+               if (unlikely(!neigh)) {
+                       neigh_add_path(skb, cb->hwaddr, dev);
+                       return NETDEV_TX_OK;
                }
+               break;
+       case htons(ETH_P_ARP):
+       case htons(ETH_P_RARP):
+               /* for unicast ARP and RARP should always perform path find */
+               unicast_arp_send(skb, dev, cb);
+               return NETDEV_TX_OK;
+       default:
+               /* ethertype not supported by IPoIB */
+               ++dev->stats.tx_dropped;
+               dev_kfree_skb_any(skb);
+               return NETDEV_TX_OK;
+       }
 
-               if (ipoib_cm_get(neigh)) {
-                       if (ipoib_cm_up(neigh)) {
-                               ipoib_cm_send(dev, skb, ipoib_cm_get(neigh));
-                               goto unlock;
-                       }
-               } else if (neigh->ah) {
-                       ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(n->ha));
-                       goto unlock;
+send_using_neigh:
+       /* note we now hold a ref to neigh */
+       if (ipoib_cm_get(neigh)) {
+               if (ipoib_cm_up(neigh)) {
+                       ipoib_cm_send(dev, skb, ipoib_cm_get(neigh));
+                       goto unref;
                }
+       } else if (neigh->ah) {
+               ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(cb->hwaddr));
+               goto unref;
+       }
 
-               if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
-                       spin_lock_irqsave(&priv->lock, flags);
-                       __skb_queue_tail(&neigh->queue, skb);
-                       spin_unlock_irqrestore(&priv->lock, flags);
-               } else {
-                       ++dev->stats.tx_dropped;
-                       dev_kfree_skb_any(skb);
-               }
+       if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+               spin_lock_irqsave(&priv->lock, flags);
+               __skb_queue_tail(&neigh->queue, skb);
+               spin_unlock_irqrestore(&priv->lock, flags);
        } else {
-               struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb;
-
-               if (cb->hwaddr[4] == 0xff) {
-                       /* Add in the P_Key for multicast*/
-                       cb->hwaddr[8] = (priv->pkey >> 8) & 0xff;
-                       cb->hwaddr[9] = priv->pkey & 0xff;
+               ++dev->stats.tx_dropped;
+               dev_kfree_skb_any(skb);
+       }
 
-                       ipoib_mcast_send(dev, cb->hwaddr + 4, skb);
-               } else {
-                       /* unicast GID -- should be ARP or RARP reply */
-
-                       if ((be16_to_cpup((__be16 *) skb->data) != ETH_P_ARP) &&
-                           (be16_to_cpup((__be16 *) skb->data) != ETH_P_RARP)) {
-                               ipoib_warn(priv, "Unicast, no %s: type %04x, QPN %06x %pI6\n",
-                                          skb_dst(skb) ? "neigh" : "dst",
-                                          be16_to_cpup((__be16 *) skb->data),
-                                          IPOIB_QPN(cb->hwaddr),
-                                          cb->hwaddr + 4);
-                               dev_kfree_skb_any(skb);
-                               ++dev->stats.tx_dropped;
-                               goto unlock;
-                       }
+unref:
+       ipoib_neigh_put(neigh);
 
-                       unicast_arp_send(skb, dev, cb);
-               }
-       }
-unlock:
-       if (n)
-               neigh_release(n);
-       rcu_read_unlock();
        return NETDEV_TX_OK;
 }
 
@@ -821,6 +770,7 @@ static int ipoib_hard_header(struct sk_buff *skb,
                             const void *daddr, const void *saddr, unsigned len)
 {
        struct ipoib_header *header;
+       struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb;
 
        header = (struct ipoib_header *) skb_push(skb, sizeof *header);
 
@@ -828,14 +778,11 @@ static int ipoib_hard_header(struct sk_buff *skb,
        header->reserved = 0;
 
        /*
-        * If we don't have a dst_entry structure, stuff the
+        * we don't rely on dst_entry structure,  always stuff the
         * destination address into skb->cb so we can figure out where
         * to send the packet later.
         */
-       if (!skb_dst(skb)) {
-               struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb;
-               memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN);
-       }
+       memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN);
 
        return 0;
 }
@@ -852,86 +799,438 @@ static void ipoib_set_mcast_list(struct net_device *dev)
        queue_work(ipoib_workqueue, &priv->restart_task);
 }
 
-static void ipoib_neigh_cleanup(struct neighbour *n)
+static u32 ipoib_addr_hash(struct ipoib_neigh_hash *htbl, u8 *daddr)
 {
-       struct ipoib_neigh *neigh;
-       struct ipoib_dev_priv *priv = netdev_priv(n->dev);
+       /*
+        * Use only the address parts that contributes to spreading
+        * The subnet prefix is not used as one can not connect to
+        * same remote port (GUID) using the same remote QPN via two
+        * different subnets.
+        */
+        /* qpn octets[1:4) & port GUID octets[12:20) */
+       u32 *daddr_32 = (u32 *) daddr;
+       u32 hv;
+
+       hv = jhash_3words(daddr_32[3], daddr_32[4], 0xFFFFFF & daddr_32[0], 0);
+       return hv & htbl->mask;
+}
+
+struct ipoib_neigh *ipoib_neigh_get(struct net_device *dev, u8 *daddr)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_neigh_table *ntbl = &priv->ntbl;
+       struct ipoib_neigh_hash *htbl;
+       struct ipoib_neigh *neigh = NULL;
+       u32 hash_val;
+
+       rcu_read_lock_bh();
+
+       htbl = rcu_dereference_bh(ntbl->htbl);
+
+       if (!htbl)
+               goto out_unlock;
+
+       hash_val = ipoib_addr_hash(htbl, daddr);
+       for (neigh = rcu_dereference_bh(htbl->buckets[hash_val]);
+            neigh != NULL;
+            neigh = rcu_dereference_bh(neigh->hnext)) {
+               if (memcmp(daddr, neigh->daddr, INFINIBAND_ALEN) == 0) {
+                       /* found, take one ref on behalf of the caller */
+                       if (!atomic_inc_not_zero(&neigh->refcnt)) {
+                               /* deleted */
+                               neigh = NULL;
+                               goto out_unlock;
+                       }
+                       neigh->alive = jiffies;
+                       goto out_unlock;
+               }
+       }
+
+out_unlock:
+       rcu_read_unlock_bh();
+       return neigh;
+}
+
+static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
+{
+       struct ipoib_neigh_table *ntbl = &priv->ntbl;
+       struct ipoib_neigh_hash *htbl;
+       unsigned long neigh_obsolete;
+       unsigned long dt;
        unsigned long flags;
-       struct ipoib_ah *ah = NULL;
+       int i;
 
-       neigh = *to_ipoib_neigh(n);
-       if (neigh)
-               priv = netdev_priv(neigh->dev);
-       else
+       if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
                return;
-       ipoib_dbg(priv,
-                 "neigh_cleanup for %06x %pI6\n",
-                 IPOIB_QPN(n->ha),
-                 n->ha + 4);
 
-       spin_lock_irqsave(&priv->lock, flags);
+       write_lock_bh(&ntbl->rwlock);
+
+       htbl = rcu_dereference_protected(ntbl->htbl,
+                                        lockdep_is_held(&ntbl->rwlock));
+
+       if (!htbl)
+               goto out_unlock;
+
+       /* neigh is obsolete if it was idle for two GC periods */
+       dt = 2 * arp_tbl.gc_interval;
+       neigh_obsolete = jiffies - dt;
+       /* handle possible race condition */
+       if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
+               goto out_unlock;
+
+       for (i = 0; i < htbl->size; i++) {
+               struct ipoib_neigh *neigh;
+               struct ipoib_neigh __rcu **np = &htbl->buckets[i];
+
+               while ((neigh = rcu_dereference_protected(*np,
+                                                         lockdep_is_held(&ntbl->rwlock))) != NULL) {
+                       /* was the neigh idle for two GC periods */
+                       if (time_after(neigh_obsolete, neigh->alive)) {
+                               rcu_assign_pointer(*np,
+                                                  rcu_dereference_protected(neigh->hnext,
+                                                                            lockdep_is_held(&ntbl->rwlock)));
+                               /* remove from path/mc list */
+                               spin_lock_irqsave(&priv->lock, flags);
+                               list_del(&neigh->list);
+                               spin_unlock_irqrestore(&priv->lock, flags);
+                               call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
+                       } else {
+                               np = &neigh->hnext;
+                       }
 
-       if (neigh->ah)
-               ah = neigh->ah;
-       list_del(&neigh->list);
-       ipoib_neigh_free(n->dev, neigh);
+               }
+       }
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+out_unlock:
+       write_unlock_bh(&ntbl->rwlock);
+}
 
-       if (ah)
-               ipoib_put_ah(ah);
+static void ipoib_reap_neigh(struct work_struct *work)
+{
+       struct ipoib_dev_priv *priv =
+               container_of(work, struct ipoib_dev_priv, neigh_reap_task.work);
+
+       __ipoib_reap_neigh(priv);
+
+       if (!test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
+               queue_delayed_work(ipoib_workqueue, &priv->neigh_reap_task,
+                                  arp_tbl.gc_interval);
 }
 
-struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neighbour,
+
+static struct ipoib_neigh *ipoib_neigh_ctor(u8 *daddr,
                                      struct net_device *dev)
 {
        struct ipoib_neigh *neigh;
 
-       neigh = kmalloc(sizeof *neigh, GFP_ATOMIC);
+       neigh = kzalloc(sizeof *neigh, GFP_ATOMIC);
        if (!neigh)
                return NULL;
 
-       neigh->neighbour = neighbour;
        neigh->dev = dev;
-       memset(&neigh->dgid.raw, 0, sizeof (union ib_gid));
-       *to_ipoib_neigh(neighbour) = neigh;
+       memcpy(&neigh->daddr, daddr, sizeof(neigh->daddr));
        skb_queue_head_init(&neigh->queue);
+       INIT_LIST_HEAD(&neigh->list);
        ipoib_cm_set(neigh, NULL);
+       /* one ref on behalf of the caller */
+       atomic_set(&neigh->refcnt, 1);
+
+       return neigh;
+}
+
+struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
+                                     struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_neigh_table *ntbl = &priv->ntbl;
+       struct ipoib_neigh_hash *htbl;
+       struct ipoib_neigh *neigh;
+       u32 hash_val;
+
+       write_lock_bh(&ntbl->rwlock);
+
+       htbl = rcu_dereference_protected(ntbl->htbl,
+                                        lockdep_is_held(&ntbl->rwlock));
+       if (!htbl) {
+               neigh = NULL;
+               goto out_unlock;
+       }
+
+       /* need to add a new neigh, but maybe some other thread succeeded?
+        * recalc hash, maybe hash resize took place so we do a search
+        */
+       hash_val = ipoib_addr_hash(htbl, daddr);
+       for (neigh = rcu_dereference_protected(htbl->buckets[hash_val],
+                                              lockdep_is_held(&ntbl->rwlock));
+            neigh != NULL;
+            neigh = rcu_dereference_protected(neigh->hnext,
+                                              lockdep_is_held(&ntbl->rwlock))) {
+               if (memcmp(daddr, neigh->daddr, INFINIBAND_ALEN) == 0) {
+                       /* found, take one ref on behalf of the caller */
+                       if (!atomic_inc_not_zero(&neigh->refcnt)) {
+                               /* deleted */
+                               neigh = NULL;
+                               break;
+                       }
+                       neigh->alive = jiffies;
+                       goto out_unlock;
+               }
+       }
+
+       neigh = ipoib_neigh_ctor(daddr, dev);
+       if (!neigh)
+               goto out_unlock;
+
+       /* one ref on behalf of the hash table */
+       atomic_inc(&neigh->refcnt);
+       neigh->alive = jiffies;
+       /* put in hash */
+       rcu_assign_pointer(neigh->hnext,
+                          rcu_dereference_protected(htbl->buckets[hash_val],
+                                                    lockdep_is_held(&ntbl->rwlock)));
+       rcu_assign_pointer(htbl->buckets[hash_val], neigh);
+       atomic_inc(&ntbl->entries);
+
+out_unlock:
+       write_unlock_bh(&ntbl->rwlock);
 
        return neigh;
 }
 
-void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh)
+void ipoib_neigh_dtor(struct ipoib_neigh *neigh)
 {
+       /* neigh reference count was dropprd to zero */
+       struct net_device *dev = neigh->dev;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
        struct sk_buff *skb;
-       *to_ipoib_neigh(neigh->neighbour) = NULL;
+       if (neigh->ah)
+               ipoib_put_ah(neigh->ah);
        while ((skb = __skb_dequeue(&neigh->queue))) {
                ++dev->stats.tx_dropped;
                dev_kfree_skb_any(skb);
        }
        if (ipoib_cm_get(neigh))
                ipoib_cm_destroy_tx(ipoib_cm_get(neigh));
+       ipoib_dbg(netdev_priv(dev),
+                 "neigh free for %06x %pI6\n",
+                 IPOIB_QPN(neigh->daddr),
+                 neigh->daddr + 4);
        kfree(neigh);
+       if (atomic_dec_and_test(&priv->ntbl.entries)) {
+               if (test_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags))
+                       complete(&priv->ntbl.flushed);
+       }
+}
+
+static void ipoib_neigh_reclaim(struct rcu_head *rp)
+{
+       /* Called as a result of removal from hash table */
+       struct ipoib_neigh *neigh = container_of(rp, struct ipoib_neigh, rcu);
+       /* note TX context may hold another ref */
+       ipoib_neigh_put(neigh);
 }
 
-static int ipoib_neigh_setup_dev(struct net_device *dev, struct neigh_parms *parms)
+void ipoib_neigh_free(struct ipoib_neigh *neigh)
 {
-       parms->neigh_cleanup = ipoib_neigh_cleanup;
+       struct net_device *dev = neigh->dev;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_neigh_table *ntbl = &priv->ntbl;
+       struct ipoib_neigh_hash *htbl;
+       struct ipoib_neigh __rcu **np;
+       struct ipoib_neigh *n;
+       u32 hash_val;
+
+       write_lock_bh(&ntbl->rwlock);
+
+       htbl = rcu_dereference_protected(ntbl->htbl,
+                                       lockdep_is_held(&ntbl->rwlock));
+       if (!htbl)
+               goto out_unlock;
+
+       hash_val = ipoib_addr_hash(htbl, neigh->daddr);
+       np = &htbl->buckets[hash_val];
+       for (n = rcu_dereference_protected(*np,
+                                           lockdep_is_held(&ntbl->rwlock));
+            n != NULL;
+            n = rcu_dereference_protected(neigh->hnext,
+                                       lockdep_is_held(&ntbl->rwlock))) {
+               if (n == neigh) {
+                       /* found */
+                       rcu_assign_pointer(*np,
+                                          rcu_dereference_protected(neigh->hnext,
+                                                                    lockdep_is_held(&ntbl->rwlock)));
+                       call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
+                       goto out_unlock;
+               } else {
+                       np = &n->hnext;
+               }
+       }
+
+out_unlock:
+       write_unlock_bh(&ntbl->rwlock);
+
+}
+
+static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
+{
+       struct ipoib_neigh_table *ntbl = &priv->ntbl;
+       struct ipoib_neigh_hash *htbl;
+       struct ipoib_neigh **buckets;
+       u32 size;
+
+       clear_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
+       ntbl->htbl = NULL;
+       rwlock_init(&ntbl->rwlock);
+       htbl = kzalloc(sizeof(*htbl), GFP_KERNEL);
+       if (!htbl)
+               return -ENOMEM;
+       set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+       size = roundup_pow_of_two(arp_tbl.gc_thresh3);
+       buckets = kzalloc(size * sizeof(*buckets), GFP_KERNEL);
+       if (!buckets) {
+               kfree(htbl);
+               return -ENOMEM;
+       }
+       htbl->size = size;
+       htbl->mask = (size - 1);
+       htbl->buckets = buckets;
+       ntbl->htbl = htbl;
+       atomic_set(&ntbl->entries, 0);
+
+       /* start garbage collection */
+       clear_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+       queue_delayed_work(ipoib_workqueue, &priv->neigh_reap_task,
+                          arp_tbl.gc_interval);
 
        return 0;
 }
 
+static void neigh_hash_free_rcu(struct rcu_head *head)
+{
+       struct ipoib_neigh_hash *htbl = container_of(head,
+                                                   struct ipoib_neigh_hash,
+                                                   rcu);
+       struct ipoib_neigh __rcu **buckets = htbl->buckets;
+
+       kfree(buckets);
+       kfree(htbl);
+}
+
+void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_neigh_table *ntbl = &priv->ntbl;
+       struct ipoib_neigh_hash *htbl;
+       unsigned long flags;
+       int i;
+
+       /* remove all neigh connected to a given path or mcast */
+       write_lock_bh(&ntbl->rwlock);
+
+       htbl = rcu_dereference_protected(ntbl->htbl,
+                                        lockdep_is_held(&ntbl->rwlock));
+
+       if (!htbl)
+               goto out_unlock;
+
+       for (i = 0; i < htbl->size; i++) {
+               struct ipoib_neigh *neigh;
+               struct ipoib_neigh __rcu **np = &htbl->buckets[i];
+
+               while ((neigh = rcu_dereference_protected(*np,
+                                                         lockdep_is_held(&ntbl->rwlock))) != NULL) {
+                       /* delete neighs belong to this parent */
+                       if (!memcmp(gid, neigh->daddr + 4, sizeof (union ib_gid))) {
+                               rcu_assign_pointer(*np,
+                                                  rcu_dereference_protected(neigh->hnext,
+                                                                            lockdep_is_held(&ntbl->rwlock)));
+                               /* remove from parent list */
+                               spin_lock_irqsave(&priv->lock, flags);
+                               list_del(&neigh->list);
+                               spin_unlock_irqrestore(&priv->lock, flags);
+                               call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
+                       } else {
+                               np = &neigh->hnext;
+                       }
+
+               }
+       }
+out_unlock:
+       write_unlock_bh(&ntbl->rwlock);
+}
+
+static void ipoib_flush_neighs(struct ipoib_dev_priv *priv)
+{
+       struct ipoib_neigh_table *ntbl = &priv->ntbl;
+       struct ipoib_neigh_hash *htbl;
+       unsigned long flags;
+       int i;
+
+       write_lock_bh(&ntbl->rwlock);
+
+       htbl = rcu_dereference_protected(ntbl->htbl,
+                                       lockdep_is_held(&ntbl->rwlock));
+       if (!htbl)
+               goto out_unlock;
+
+       for (i = 0; i < htbl->size; i++) {
+               struct ipoib_neigh *neigh;
+               struct ipoib_neigh __rcu **np = &htbl->buckets[i];
+
+               while ((neigh = rcu_dereference_protected(*np,
+                                                         lockdep_is_held(&ntbl->rwlock))) != NULL) {
+                       rcu_assign_pointer(*np,
+                                          rcu_dereference_protected(neigh->hnext,
+                                                                    lockdep_is_held(&ntbl->rwlock)));
+                       /* remove from path/mc list */
+                       spin_lock_irqsave(&priv->lock, flags);
+                       list_del(&neigh->list);
+                       spin_unlock_irqrestore(&priv->lock, flags);
+                       call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
+               }
+       }
+
+       rcu_assign_pointer(ntbl->htbl, NULL);
+       call_rcu(&htbl->rcu, neigh_hash_free_rcu);
+
+out_unlock:
+       write_unlock_bh(&ntbl->rwlock);
+}
+
+static void ipoib_neigh_hash_uninit(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       int stopped;
+
+       ipoib_dbg(priv, "ipoib_neigh_hash_uninit\n");
+       init_completion(&priv->ntbl.flushed);
+       set_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
+
+       /* Stop GC if called at init fail need to cancel work */
+       stopped = test_and_set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+       if (!stopped)
+               cancel_delayed_work(&priv->neigh_reap_task);
+
+       if (atomic_read(&priv->ntbl.entries)) {
+               ipoib_flush_neighs(priv);
+               wait_for_completion(&priv->ntbl.flushed);
+       }
+}
+
+
 int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
 
+       if (ipoib_neigh_hash_init(priv) < 0)
+               goto out;
        /* Allocate RX/TX "rings" to hold queued skbs */
        priv->rx_ring = kzalloc(ipoib_recvq_size * sizeof *priv->rx_ring,
                                GFP_KERNEL);
        if (!priv->rx_ring) {
                printk(KERN_WARNING "%s: failed to allocate RX ring (%d entries)\n",
                       ca->name, ipoib_recvq_size);
-               goto out;
+               goto out_neigh_hash_cleanup;
        }
 
        priv->tx_ring = vzalloc(ipoib_sendq_size * sizeof *priv->tx_ring);
@@ -954,6 +1253,8 @@ out_tx_ring_cleanup:
 out_rx_ring_cleanup:
        kfree(priv->rx_ring);
 
+out_neigh_hash_cleanup:
+       ipoib_neigh_hash_uninit(dev);
 out:
        return -ENOMEM;
 }
@@ -966,6 +1267,9 @@ void ipoib_dev_cleanup(struct net_device *dev)
 
        /* Delete any child interfaces first */
        list_for_each_entry_safe(cpriv, tcpriv, &priv->child_intfs, list) {
+               /* Stop GC on child */
+               set_bit(IPOIB_STOP_NEIGH_GC, &cpriv->flags);
+               cancel_delayed_work(&cpriv->neigh_reap_task);
                unregister_netdev(cpriv->dev);
                ipoib_dev_cleanup(cpriv->dev);
                free_netdev(cpriv->dev);
@@ -978,6 +1282,8 @@ void ipoib_dev_cleanup(struct net_device *dev)
 
        priv->rx_ring = NULL;
        priv->tx_ring = NULL;
+
+       ipoib_neigh_hash_uninit(dev);
 }
 
 static const struct header_ops ipoib_header_ops = {
@@ -992,7 +1298,6 @@ static const struct net_device_ops ipoib_netdev_ops = {
        .ndo_start_xmit          = ipoib_start_xmit,
        .ndo_tx_timeout          = ipoib_timeout,
        .ndo_set_rx_mode         = ipoib_set_mcast_list,
-       .ndo_neigh_setup         = ipoib_neigh_setup_dev,
 };
 
 static void ipoib_setup(struct net_device *dev)
@@ -1041,6 +1346,7 @@ static void ipoib_setup(struct net_device *dev)
        INIT_WORK(&priv->flush_heavy,   ipoib_ib_dev_flush_heavy);
        INIT_WORK(&priv->restart_task, ipoib_mcast_restart_task);
        INIT_DELAYED_WORK(&priv->ah_reap_task, ipoib_reap_ah);
+       INIT_DELAYED_WORK(&priv->neigh_reap_task, ipoib_reap_neigh);
 }
 
 struct ipoib_dev_priv *ipoib_intf_alloc(const char *name)
@@ -1281,6 +1587,9 @@ sysfs_failed:
 
 register_failed:
        ib_unregister_event_handler(&priv->event_handler);
+       /* Stop GC if started before flush */
+       set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+       cancel_delayed_work(&priv->neigh_reap_task);
        flush_workqueue(ipoib_workqueue);
 
 event_failed:
@@ -1347,6 +1656,9 @@ static void ipoib_remove_one(struct ib_device *device)
                dev_change_flags(priv->dev, priv->dev->flags & ~IFF_UP);
                rtnl_unlock();
 
+               /* Stop GC */
+               set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+               cancel_delayed_work(&priv->neigh_reap_task);
                flush_workqueue(ipoib_workqueue);
 
                unregister_netdev(priv->dev);
index 7cecb16d3d48c20e8237b1c1acdc173931a78979..13f4aa7593c834f2ee1475c6471cad8e0bcd12f8 100644 (file)
@@ -69,28 +69,13 @@ struct ipoib_mcast_iter {
 static void ipoib_mcast_free(struct ipoib_mcast *mcast)
 {
        struct net_device *dev = mcast->dev;
-       struct ipoib_dev_priv *priv = netdev_priv(dev);
-       struct ipoib_neigh *neigh, *tmp;
        int tx_dropped = 0;
 
        ipoib_dbg_mcast(netdev_priv(dev), "deleting multicast group %pI6\n",
                        mcast->mcmember.mgid.raw);
 
-       spin_lock_irq(&priv->lock);
-
-       list_for_each_entry_safe(neigh, tmp, &mcast->neigh_list, list) {
-               /*
-                * It's safe to call ipoib_put_ah() inside priv->lock
-                * here, because we know that mcast->ah will always
-                * hold one more reference, so ipoib_put_ah() will
-                * never do more than decrement the ref count.
-                */
-               if (neigh->ah)
-                       ipoib_put_ah(neigh->ah);
-               ipoib_neigh_free(dev, neigh);
-       }
-
-       spin_unlock_irq(&priv->lock);
+       /* remove all neigh connected to this mcast */
+       ipoib_del_neighs_by_gid(dev, mcast->mcmember.mgid.raw);
 
        if (mcast->ah)
                ipoib_put_ah(mcast->ah);
@@ -655,17 +640,12 @@ static int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast)
        return 0;
 }
 
-void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb)
+void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
-       struct dst_entry *dst = skb_dst(skb);
        struct ipoib_mcast *mcast;
-       struct neighbour *n;
        unsigned long flags;
-
-       n = NULL;
-       if (dst)
-               n = dst_neigh_lookup_skb(dst, skb);
+       void *mgid = daddr + 4;
 
        spin_lock_irqsave(&priv->lock, flags);
 
@@ -721,28 +701,29 @@ void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb)
 
 out:
        if (mcast && mcast->ah) {
-               if (n) {
-                       if (!*to_ipoib_neigh(n)) {
-                               struct ipoib_neigh *neigh;
-
-                               neigh = ipoib_neigh_alloc(n, skb->dev);
-                               if (neigh) {
-                                       kref_get(&mcast->ah->ref);
-                                       neigh->ah       = mcast->ah;
-                                       list_add_tail(&neigh->list,
-                                                     &mcast->neigh_list);
-                               }
+               struct ipoib_neigh *neigh;
+
+               spin_unlock_irqrestore(&priv->lock, flags);
+               neigh = ipoib_neigh_get(dev, daddr);
+               spin_lock_irqsave(&priv->lock, flags);
+               if (!neigh) {
+                       spin_unlock_irqrestore(&priv->lock, flags);
+                       neigh = ipoib_neigh_alloc(daddr, dev);
+                       spin_lock_irqsave(&priv->lock, flags);
+                       if (neigh) {
+                               kref_get(&mcast->ah->ref);
+                               neigh->ah       = mcast->ah;
+                               list_add_tail(&neigh->list, &mcast->neigh_list);
                        }
-                       neigh_release(n);
                }
                spin_unlock_irqrestore(&priv->lock, flags);
                ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN);
+               if (neigh)
+                       ipoib_neigh_put(neigh);
                return;
        }
 
 unlock:
-       if (n)
-               neigh_release(n);
        spin_unlock_irqrestore(&priv->lock, flags);
 }
 
diff --git a/drivers/input/misc/88pm80x_onkey.c b/drivers/input/misc/88pm80x_onkey.c
new file mode 100644 (file)
index 0000000..7f26e7b
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Marvell 88PM80x ONKEY driver
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ * Qiao Zhou <zhouqiao@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define PM800_LONG_ONKEY_EN            (1 << 0)
+#define PM800_LONG_KEY_DELAY           (8)     /* 1 .. 16 seconds */
+#define PM800_LONKEY_PRESS_TIME                ((PM800_LONG_KEY_DELAY-1) << 4)
+#define PM800_LONKEY_PRESS_TIME_MASK   (0xF0)
+#define PM800_SW_PDOWN                 (1 << 5)
+
+struct pm80x_onkey_info {
+       struct input_dev *idev;
+       struct pm80x_chip *pm80x;
+       struct regmap *map;
+       int irq;
+};
+
+/* 88PM80x gives us an interrupt when ONKEY is held */
+static irqreturn_t pm80x_onkey_handler(int irq, void *data)
+{
+       struct pm80x_onkey_info *info = data;
+       int ret = 0;
+       unsigned int val;
+
+       ret = regmap_read(info->map, PM800_STATUS_1, &val);
+       if (ret < 0) {
+               dev_err(info->idev->dev.parent, "failed to read status: %d\n", ret);
+               return IRQ_NONE;
+       }
+       val &= PM800_ONKEY_STS1;
+
+       input_report_key(info->idev, KEY_POWER, val);
+       input_sync(info->idev);
+
+       return IRQ_HANDLED;
+}
+
+static SIMPLE_DEV_PM_OPS(pm80x_onkey_pm_ops, pm80x_dev_suspend,
+                        pm80x_dev_resume);
+
+static int __devinit pm80x_onkey_probe(struct platform_device *pdev)
+{
+
+       struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+       struct pm80x_onkey_info *info;
+       int err;
+
+       info = kzalloc(sizeof(struct pm80x_onkey_info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->pm80x = chip;
+
+       info->irq = platform_get_irq(pdev, 0);
+       if (info->irq < 0) {
+               dev_err(&pdev->dev, "No IRQ resource!\n");
+               err = -EINVAL;
+               goto out;
+       }
+
+       info->map = info->pm80x->regmap;
+       if (!info->map) {
+               dev_err(&pdev->dev, "no regmap!\n");
+               err = -EINVAL;
+               goto out;
+       }
+
+       info->idev = input_allocate_device();
+       if (!info->idev) {
+               dev_err(&pdev->dev, "Failed to allocate input dev\n");
+               err = -ENOMEM;
+               goto out;
+       }
+
+       info->idev->name = "88pm80x_on";
+       info->idev->phys = "88pm80x_on/input0";
+       info->idev->id.bustype = BUS_I2C;
+       info->idev->dev.parent = &pdev->dev;
+       info->idev->evbit[0] = BIT_MASK(EV_KEY);
+       __set_bit(KEY_POWER, info->idev->keybit);
+
+       err = pm80x_request_irq(info->pm80x, info->irq, pm80x_onkey_handler,
+                                           IRQF_ONESHOT, "onkey", info);
+       if (err < 0) {
+               dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n",
+                       info->irq, err);
+               goto out_reg;
+       }
+
+       err = input_register_device(info->idev);
+       if (err) {
+               dev_err(&pdev->dev, "Can't register input device: %d\n", err);
+               goto out_irq;
+       }
+
+       platform_set_drvdata(pdev, info);
+
+       /* Enable long onkey detection */
+       regmap_update_bits(info->map, PM800_RTC_MISC4, PM800_LONG_ONKEY_EN,
+                          PM800_LONG_ONKEY_EN);
+       /* Set 8-second interval */
+       regmap_update_bits(info->map, PM800_RTC_MISC3,
+                          PM800_LONKEY_PRESS_TIME_MASK,
+                          PM800_LONKEY_PRESS_TIME);
+
+       device_init_wakeup(&pdev->dev, 1);
+       return 0;
+
+out_irq:
+       pm80x_free_irq(info->pm80x, info->irq, info);
+out_reg:
+       input_free_device(info->idev);
+out:
+       kfree(info);
+       return err;
+}
+
+static int __devexit pm80x_onkey_remove(struct platform_device *pdev)
+{
+       struct pm80x_onkey_info *info = platform_get_drvdata(pdev);
+
+       device_init_wakeup(&pdev->dev, 0);
+       pm80x_free_irq(info->pm80x, info->irq, info);
+       input_unregister_device(info->idev);
+       kfree(info);
+       return 0;
+}
+
+static struct platform_driver pm80x_onkey_driver = {
+       .driver = {
+                  .name = "88pm80x-onkey",
+                  .owner = THIS_MODULE,
+                  .pm = &pm80x_onkey_pm_ops,
+                  },
+       .probe = pm80x_onkey_probe,
+       .remove = __devexit_p(pm80x_onkey_remove),
+};
+
+module_platform_driver(pm80x_onkey_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Marvell 88PM80x ONKEY driver");
+MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
+MODULE_ALIAS("platform:88pm80x-onkey");
index 7faf4a7fcaa9219d278b67b75a77a6c10081f678..7c0f1ecfdd7a3ff88fed02671e1cf0fba5847b24 100644 (file)
@@ -22,6 +22,16 @@ config INPUT_88PM860X_ONKEY
          To compile this driver as a module, choose M here: the module
          will be called 88pm860x_onkey.
 
+config INPUT_88PM80X_ONKEY
+       tristate "88PM80x ONKEY support"
+       depends on MFD_88PM800
+       help
+         Support the ONKEY of Marvell 88PM80x PMICs as an input device
+         reporting power button status.
+
+         To compile this driver as a module, choose M here: the module
+         will be called 88pm80x_onkey.
+
 config INPUT_AB8500_PONKEY
        tristate "AB8500 Pon (PowerOn) Key"
        depends on AB8500_CORE
index f55cdf4916fae2d717a3828099f130c71bbffd02..83fe6f5b77d120e9b3194325f0fc83879a474a0a 100644 (file)
@@ -5,6 +5,7 @@
 # Each configuration option enables a list of files.
 
 obj-$(CONFIG_INPUT_88PM860X_ONKEY)     += 88pm860x_onkey.o
+obj-$(CONFIG_INPUT_88PM80X_ONKEY)      += 88pm80x_onkey.o
 obj-$(CONFIG_INPUT_AB8500_PONKEY)      += ab8500-ponkey.o
 obj-$(CONFIG_INPUT_AD714X)             += ad714x.o
 obj-$(CONFIG_INPUT_AD714X_I2C)         += ad714x-i2c.o
index 84ec691c05aa983751492a8dff29c5ca87b27ad2..f06231b7cab1ef762d7ad5db073b3a6b64d39987 100644 (file)
@@ -74,8 +74,8 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev)
 
        ponkey->idev = input;
        ponkey->ab8500 = ab8500;
-       ponkey->irq_dbf = irq_dbf;
-       ponkey->irq_dbr = irq_dbr;
+       ponkey->irq_dbf = ab8500_irq_get_virq(ab8500, irq_dbf);
+       ponkey->irq_dbr = ab8500_irq_get_virq(ab8500, irq_dbr);
 
        input->name = "AB8500 POn(PowerOn) Key";
        input->dev.parent = &pdev->dev;
index d5b390f75c9a97f38e147bb0a7a111cf7a9e15b3..14eaecea2b70660f7377c0a3196809e28c406d4a 100644 (file)
  * Note that newer firmware allows querying device for maximum useable
  * coordinates.
  */
+#define XMIN 0
+#define XMAX 6143
+#define YMIN 0
+#define YMAX 6143
 #define XMIN_NOMINAL 1472
 #define XMAX_NOMINAL 5472
 #define YMIN_NOMINAL 1408
 #define YMAX_NOMINAL 4448
 
+/* Size in bits of absolute position values reported by the hardware */
+#define ABS_POS_BITS 13
+
+/*
+ * Any position values from the hardware above the following limits are
+ * treated as "wrapped around negative" values that have been truncated to
+ * the 13-bit reporting range of the hardware. These are just reasonable
+ * guesses and can be adjusted if hardware is found that operates outside
+ * of these parameters.
+ */
+#define X_MAX_POSITIVE (((1 << ABS_POS_BITS) + XMAX) / 2)
+#define Y_MAX_POSITIVE (((1 << ABS_POS_BITS) + YMAX) / 2)
 
 /*****************************************************************************
  *     Stuff we need even when we do not want native Synaptics support
@@ -588,6 +604,12 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
                hw->right = (buf[0] & 0x02) ? 1 : 0;
        }
 
+       /* Convert wrap-around values to negative */
+       if (hw->x > X_MAX_POSITIVE)
+               hw->x -= 1 << ABS_POS_BITS;
+       if (hw->y > Y_MAX_POSITIVE)
+               hw->y -= 1 << ABS_POS_BITS;
+
        return 0;
 }
 
index 09a089996ded673788e8302b7854e2a4669960d8..d7a7e54f64651227ca893f20ba729b72bb2c6d2d 100644 (file)
@@ -878,7 +878,7 @@ static int __init hp_sdc_init(void)
 #endif
 
        errstr = "IRQ not available for";
-       if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED|IRQF_SAMPLE_RANDOM,
+       if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED,
                        "HP SDC", &hp_sdc))
                goto err1;
 
index 6533f44be5bd6636f22457f9ca32eaa7f404505c..002041975de9c902f193f2448865d051f0ce98dc 100644 (file)
@@ -464,7 +464,7 @@ static void wacom_intuos_general(struct wacom_wac *wacom)
                t = (data[6] << 2) | ((data[7] >> 6) & 3);
                if ((features->type >= INTUOS4S && features->type <= INTUOS4L) ||
                     (features->type >= INTUOS5S && features->type <= INTUOS5L) ||
-                   features->type == WACOM_21UX2 || features->type == WACOM_24HD) {
+                   (features->type >= WACOM_21UX2 && features->type <= WACOM_24HD)) {
                        t = (t << 1) | (data[1] & 1);
                }
                input_report_abs(input, ABS_PRESSURE, t);
@@ -614,7 +614,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
                                input_report_abs(input, ABS_MISC, 0);
                        }
                } else {
-                       if (features->type == WACOM_21UX2) {
+                       if (features->type == WACOM_21UX2 || features->type == WACOM_22HD) {
                                input_report_key(input, BTN_0, (data[5] & 0x01));
                                input_report_key(input, BTN_1, (data[6] & 0x01));
                                input_report_key(input, BTN_2, (data[6] & 0x02));
@@ -633,6 +633,12 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
                                input_report_key(input, BTN_Z, (data[8] & 0x20));
                                input_report_key(input, BTN_BASE, (data[8] & 0x40));
                                input_report_key(input, BTN_BASE2, (data[8] & 0x80));
+
+                               if (features->type == WACOM_22HD) {
+                                       input_report_key(input, KEY_PROG1, data[9] & 0x01);
+                                       input_report_key(input, KEY_PROG2, data[9] & 0x02);
+                                       input_report_key(input, KEY_PROG3, data[9] & 0x04);
+                               }
                        } else {
                                input_report_key(input, BTN_0, (data[5] & 0x01));
                                input_report_key(input, BTN_1, (data[5] & 0x02));
@@ -1231,6 +1237,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
        case CINTIQ:
        case WACOM_BEE:
        case WACOM_21UX2:
+       case WACOM_22HD:
        case WACOM_24HD:
                sync = wacom_intuos_irq(wacom_wac);
                break;
@@ -1432,6 +1439,12 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
                wacom_setup_cintiq(wacom_wac);
                break;
 
+       case WACOM_22HD:
+               __set_bit(KEY_PROG1, input_dev->keybit);
+               __set_bit(KEY_PROG2, input_dev->keybit);
+               __set_bit(KEY_PROG3, input_dev->keybit);
+               /* fall through */
+
        case WACOM_21UX2:
                __set_bit(BTN_A, input_dev->keybit);
                __set_bit(BTN_B, input_dev->keybit);
@@ -1858,6 +1871,9 @@ static const struct wacom_features wacom_features_0xF0 =
 static const struct wacom_features wacom_features_0xCC =
        { "Wacom Cintiq 21UX2",   WACOM_PKGLEN_INTUOS,    87200, 65600, 2047,
          63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xFA =
+       { "Wacom Cintiq 22HD",    WACOM_PKGLEN_INTUOS,    95840, 54260, 2047,
+         63, WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
 static const struct wacom_features wacom_features_0x90 =
        { "Wacom ISDv4 90",       WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  255,
          0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -2075,6 +2091,7 @@ const struct usb_device_id wacom_ids[] = {
        { USB_DEVICE_WACOM(0xEF) },
        { USB_DEVICE_WACOM(0x47) },
        { USB_DEVICE_WACOM(0xF4) },
+       { USB_DEVICE_WACOM(0xFA) },
        { USB_DEVICE_LENOVO(0x6004) },
        { }
 };
index bd5d37b28714917e0a352ea94b54912151bd398b..96c185cc301eb95bf062440e59c49114458c0028 100644 (file)
@@ -73,8 +73,9 @@ enum {
        INTUOS5S,
        INTUOS5,
        INTUOS5L,
-       WACOM_24HD,
        WACOM_21UX2,
+       WACOM_22HD,
+       WACOM_24HD,
        CINTIQ,
        WACOM_BEE,
        WACOM_MO,
index 73bd2f6b82ec7fe2943fd5adcee4d5226979e5c0..1ba232cbc09d4e8744e1a827c3939ad7292e85a6 100644 (file)
@@ -472,6 +472,19 @@ config TOUCHSCREEN_PENMOUNT
          To compile this driver as a module, choose M here: the
          module will be called penmount.
 
+config TOUCHSCREEN_EDT_FT5X06
+       tristate "EDT FocalTech FT5x06 I2C Touchscreen support"
+       depends on I2C
+       help
+         Say Y here if you have an EDT "Polytouch" touchscreen based
+         on the FocalTech FT5x06 family of controllers connected to
+         your system.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called edt-ft5x06.
+
 config TOUCHSCREEN_MIGOR
        tristate "Renesas MIGO-R touchscreen"
        depends on SH_MIGOR && I2C
index 5920c60f999d674184c67d3c4590f0989ed1c502..178eb128d90fe24fd6b851eea5c94abd685db322 100644 (file)
@@ -24,6 +24,7 @@ obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI)  += cyttsp_spi.o
 obj-$(CONFIG_TOUCHSCREEN_DA9034)       += da9034-ts.o
 obj-$(CONFIG_TOUCHSCREEN_DA9052)       += da9052_tsi.o
 obj-$(CONFIG_TOUCHSCREEN_DYNAPRO)      += dynapro.o
+obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06)   += edt-ft5x06.o
 obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE)    += hampshire.o
 obj-$(CONFIG_TOUCHSCREEN_GUNZE)                += gunze.o
 obj-$(CONFIG_TOUCHSCREEN_EETI)         += eeti_ts.o
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
new file mode 100644 (file)
index 0000000..9afc777
--- /dev/null
@@ -0,0 +1,898 @@
+/*
+ * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ * This is a driver for the EDT "Polytouch" family of touch controllers
+ * based on the FocalTech FT5x06 line of chips.
+ *
+ * Development of this driver has been sponsored by Glyn:
+ *    http://www.glyn.com/Products/Displays
+ */
+
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/input/mt.h>
+#include <linux/input/edt-ft5x06.h>
+
+#define MAX_SUPPORT_POINTS             5
+
+#define WORK_REGISTER_THRESHOLD                0x00
+#define WORK_REGISTER_REPORT_RATE      0x08
+#define WORK_REGISTER_GAIN             0x30
+#define WORK_REGISTER_OFFSET           0x31
+#define WORK_REGISTER_NUM_X            0x33
+#define WORK_REGISTER_NUM_Y            0x34
+
+#define WORK_REGISTER_OPMODE           0x3c
+#define FACTORY_REGISTER_OPMODE                0x01
+
+#define TOUCH_EVENT_DOWN               0x00
+#define TOUCH_EVENT_UP                 0x01
+#define TOUCH_EVENT_ON                 0x02
+#define TOUCH_EVENT_RESERVED           0x03
+
+#define EDT_NAME_LEN                   23
+#define EDT_SWITCH_MODE_RETRIES                10
+#define EDT_SWITCH_MODE_DELAY          5 /* msec */
+#define EDT_RAW_DATA_RETRIES           100
+#define EDT_RAW_DATA_DELAY             1 /* msec */
+
+struct edt_ft5x06_ts_data {
+       struct i2c_client *client;
+       struct input_dev *input;
+       u16 num_x;
+       u16 num_y;
+
+#if defined(CONFIG_DEBUG_FS)
+       struct dentry *debug_dir;
+       u8 *raw_buffer;
+       size_t raw_bufsize;
+#endif
+
+       struct mutex mutex;
+       bool factory_mode;
+       int threshold;
+       int gain;
+       int offset;
+       int report_rate;
+
+       char name[EDT_NAME_LEN];
+};
+
+static int edt_ft5x06_ts_readwrite(struct i2c_client *client,
+                                  u16 wr_len, u8 *wr_buf,
+                                  u16 rd_len, u8 *rd_buf)
+{
+       struct i2c_msg wrmsg[2];
+       int i = 0;
+       int ret;
+
+       if (wr_len) {
+               wrmsg[i].addr  = client->addr;
+               wrmsg[i].flags = 0;
+               wrmsg[i].len = wr_len;
+               wrmsg[i].buf = wr_buf;
+               i++;
+       }
+       if (rd_len) {
+               wrmsg[i].addr  = client->addr;
+               wrmsg[i].flags = I2C_M_RD;
+               wrmsg[i].len = rd_len;
+               wrmsg[i].buf = rd_buf;
+               i++;
+       }
+
+       ret = i2c_transfer(client->adapter, wrmsg, i);
+       if (ret < 0)
+               return ret;
+       if (ret != i)
+               return -EIO;
+
+       return 0;
+}
+
+static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata,
+                                   u8 *buf, int buflen)
+{
+       int i;
+       u8 crc = 0;
+
+       for (i = 0; i < buflen - 1; i++)
+               crc ^= buf[i];
+
+       if (crc != buf[buflen-1]) {
+               dev_err_ratelimited(&tsdata->client->dev,
+                                   "crc error: 0x%02x expected, got 0x%02x\n",
+                                   crc, buf[buflen-1]);
+               return false;
+       }
+
+       return true;
+}
+
+static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
+{
+       struct edt_ft5x06_ts_data *tsdata = dev_id;
+       struct device *dev = &tsdata->client->dev;
+       u8 cmd = 0xf9;
+       u8 rdbuf[26];
+       int i, type, x, y, id;
+       int error;
+
+       memset(rdbuf, 0, sizeof(rdbuf));
+
+       error = edt_ft5x06_ts_readwrite(tsdata->client,
+                                       sizeof(cmd), &cmd,
+                                       sizeof(rdbuf), rdbuf);
+       if (error) {
+               dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n",
+                                   error);
+               goto out;
+       }
+
+       if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != 26) {
+               dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n",
+                                   rdbuf[0], rdbuf[1], rdbuf[2]);
+               goto out;
+       }
+
+       if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, 26))
+               goto out;
+
+       for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
+               u8 *buf = &rdbuf[i * 4 + 5];
+               bool down;
+
+               type = buf[0] >> 6;
+               /* ignore Reserved events */
+               if (type == TOUCH_EVENT_RESERVED)
+                       continue;
+
+               x = ((buf[0] << 8) | buf[1]) & 0x0fff;
+               y = ((buf[2] << 8) | buf[3]) & 0x0fff;
+               id = (buf[2] >> 4) & 0x0f;
+               down = (type != TOUCH_EVENT_UP);
+
+               input_mt_slot(tsdata->input, id);
+               input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down);
+
+               if (!down)
+                       continue;
+
+               input_report_abs(tsdata->input, ABS_MT_POSITION_X, x);
+               input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y);
+       }
+
+       input_mt_report_pointer_emulation(tsdata->input, true);
+       input_sync(tsdata->input);
+
+out:
+       return IRQ_HANDLED;
+}
+
+static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
+                                    u8 addr, u8 value)
+{
+       u8 wrbuf[4];
+
+       wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
+       wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+       wrbuf[2] = value;
+       wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
+
+       return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL);
+}
+
+static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
+                                   u8 addr)
+{
+       u8 wrbuf[2], rdbuf[2];
+       int error;
+
+       wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
+       wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+       wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40;
+
+       error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, rdbuf);
+       if (error)
+               return error;
+
+       if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) {
+               dev_err(&tsdata->client->dev,
+                       "crc error: 0x%02x expected, got 0x%02x\n",
+                       wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], rdbuf[1]);
+               return -EIO;
+       }
+
+       return rdbuf[0];
+}
+
+struct edt_ft5x06_attribute {
+       struct device_attribute dattr;
+       size_t field_offset;
+       u8 limit_low;
+       u8 limit_high;
+       u8 addr;
+};
+
+#define EDT_ATTR(_field, _mode, _addr, _limit_low, _limit_high)                \
+       struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = {        \
+               .dattr = __ATTR(_field, _mode,                          \
+                               edt_ft5x06_setting_show,                \
+                               edt_ft5x06_setting_store),              \
+               .field_offset =                                         \
+                       offsetof(struct edt_ft5x06_ts_data, _field),    \
+               .limit_low = _limit_low,                                \
+               .limit_high = _limit_high,                              \
+               .addr = _addr,                                          \
+       }
+
+static ssize_t edt_ft5x06_setting_show(struct device *dev,
+                                      struct device_attribute *dattr,
+                                      char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+       struct edt_ft5x06_attribute *attr =
+                       container_of(dattr, struct edt_ft5x06_attribute, dattr);
+       u8 *field = (u8 *)((char *)tsdata + attr->field_offset);
+       int val;
+       size_t count = 0;
+       int error = 0;
+
+       mutex_lock(&tsdata->mutex);
+
+       if (tsdata->factory_mode) {
+               error = -EIO;
+               goto out;
+       }
+
+       val = edt_ft5x06_register_read(tsdata, attr->addr);
+       if (val < 0) {
+               error = val;
+               dev_err(&tsdata->client->dev,
+                       "Failed to fetch attribute %s, error %d\n",
+                       dattr->attr.name, error);
+               goto out;
+       }
+
+       if (val != *field) {
+               dev_warn(&tsdata->client->dev,
+                        "%s: read (%d) and stored value (%d) differ\n",
+                        dattr->attr.name, val, *field);
+               *field = val;
+       }
+
+       count = scnprintf(buf, PAGE_SIZE, "%d\n", val);
+out:
+       mutex_unlock(&tsdata->mutex);
+       return error ?: count;
+}
+
+static ssize_t edt_ft5x06_setting_store(struct device *dev,
+                                       struct device_attribute *dattr,
+                                       const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+       struct edt_ft5x06_attribute *attr =
+                       container_of(dattr, struct edt_ft5x06_attribute, dattr);
+       u8 *field = (u8 *)((char *)tsdata + attr->field_offset);
+       unsigned int val;
+       int error;
+
+       mutex_lock(&tsdata->mutex);
+
+       if (tsdata->factory_mode) {
+               error = -EIO;
+               goto out;
+       }
+
+       error = kstrtouint(buf, 0, &val);
+       if (error)
+               goto out;
+
+       if (val < attr->limit_low || val > attr->limit_high) {
+               error = -ERANGE;
+               goto out;
+       }
+
+       error = edt_ft5x06_register_write(tsdata, attr->addr, val);
+       if (error) {
+               dev_err(&tsdata->client->dev,
+                       "Failed to update attribute %s, error: %d\n",
+                       dattr->attr.name, error);
+               goto out;
+       }
+
+       *field = val;
+
+out:
+       mutex_unlock(&tsdata->mutex);
+       return error ?: count;
+}
+
+static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, 0, 31);
+static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, 0, 31);
+static EDT_ATTR(threshold, S_IWUSR | S_IRUGO,
+               WORK_REGISTER_THRESHOLD, 20, 80);
+static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO,
+               WORK_REGISTER_REPORT_RATE, 3, 14);
+
+static struct attribute *edt_ft5x06_attrs[] = {
+       &edt_ft5x06_attr_gain.dattr.attr,
+       &edt_ft5x06_attr_offset.dattr.attr,
+       &edt_ft5x06_attr_threshold.dattr.attr,
+       &edt_ft5x06_attr_report_rate.dattr.attr,
+       NULL
+};
+
+static const struct attribute_group edt_ft5x06_attr_group = {
+       .attrs = edt_ft5x06_attrs,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
+{
+       struct i2c_client *client = tsdata->client;
+       int retries = EDT_SWITCH_MODE_RETRIES;
+       int ret;
+       int error;
+
+       disable_irq(client->irq);
+
+       if (!tsdata->raw_buffer) {
+               tsdata->raw_bufsize = tsdata->num_x * tsdata->num_y *
+                                     sizeof(u16);
+               tsdata->raw_buffer = kzalloc(tsdata->raw_bufsize, GFP_KERNEL);
+               if (!tsdata->raw_buffer) {
+                       error = -ENOMEM;
+                       goto err_out;
+               }
+       }
+
+       /* mode register is 0x3c when in the work mode */
+       error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03);
+       if (error) {
+               dev_err(&client->dev,
+                       "failed to switch to factory mode, error %d\n", error);
+               goto err_out;
+       }
+
+       tsdata->factory_mode = true;
+       do {
+               mdelay(EDT_SWITCH_MODE_DELAY);
+               /* mode register is 0x01 when in factory mode */
+               ret = edt_ft5x06_register_read(tsdata, FACTORY_REGISTER_OPMODE);
+               if (ret == 0x03)
+                       break;
+       } while (--retries > 0);
+
+       if (retries == 0) {
+               dev_err(&client->dev, "not in factory mode after %dms.\n",
+                       EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
+               error = -EIO;
+               goto err_out;
+       }
+
+       return 0;
+
+err_out:
+       kfree(tsdata->raw_buffer);
+       tsdata->raw_buffer = NULL;
+       tsdata->factory_mode = false;
+       enable_irq(client->irq);
+
+       return error;
+}
+
+static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
+{
+       struct i2c_client *client = tsdata->client;
+       int retries = EDT_SWITCH_MODE_RETRIES;
+       int ret;
+       int error;
+
+       /* mode register is 0x01 when in the factory mode */
+       error = edt_ft5x06_register_write(tsdata, FACTORY_REGISTER_OPMODE, 0x1);
+       if (error) {
+               dev_err(&client->dev,
+                       "failed to switch to work mode, error: %d\n", error);
+               return error;
+       }
+
+       tsdata->factory_mode = false;
+
+       do {
+               mdelay(EDT_SWITCH_MODE_DELAY);
+               /* mode register is 0x01 when in factory mode */
+               ret = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OPMODE);
+               if (ret == 0x01)
+                       break;
+       } while (--retries > 0);
+
+       if (retries == 0) {
+               dev_err(&client->dev, "not in work mode after %dms.\n",
+                       EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
+               tsdata->factory_mode = true;
+               return -EIO;
+       }
+
+       if (tsdata->raw_buffer)
+               kfree(tsdata->raw_buffer);
+       tsdata->raw_buffer = NULL;
+
+       /* restore parameters */
+       edt_ft5x06_register_write(tsdata, WORK_REGISTER_THRESHOLD,
+                                 tsdata->threshold);
+       edt_ft5x06_register_write(tsdata, WORK_REGISTER_GAIN,
+                                 tsdata->gain);
+       edt_ft5x06_register_write(tsdata, WORK_REGISTER_OFFSET,
+                                 tsdata->offset);
+       edt_ft5x06_register_write(tsdata, WORK_REGISTER_REPORT_RATE,
+                                 tsdata->report_rate);
+
+       enable_irq(client->irq);
+
+       return 0;
+}
+
+static int edt_ft5x06_debugfs_mode_get(void *data, u64 *mode)
+{
+       struct edt_ft5x06_ts_data *tsdata = data;
+
+       *mode = tsdata->factory_mode;
+
+       return 0;
+};
+
+static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode)
+{
+       struct edt_ft5x06_ts_data *tsdata = data;
+       int retval = 0;
+
+       if (mode > 1)
+               return -ERANGE;
+
+       mutex_lock(&tsdata->mutex);
+
+       if (mode != tsdata->factory_mode) {
+               retval = mode ? edt_ft5x06_factory_mode(tsdata) :
+                               edt_ft5x06_work_mode(tsdata);
+       }
+
+       mutex_unlock(&tsdata->mutex);
+
+       return retval;
+};
+
+DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get,
+                       edt_ft5x06_debugfs_mode_set, "%llu\n");
+
+static int edt_ft5x06_debugfs_raw_data_open(struct inode *inode,
+                                           struct file *file)
+{
+       file->private_data = inode->i_private;
+
+       return 0;
+}
+
+static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
+                               char __user *buf, size_t count, loff_t *off)
+{
+       struct edt_ft5x06_ts_data *tsdata = file->private_data;
+       struct i2c_client *client = tsdata->client;
+       int retries  = EDT_RAW_DATA_RETRIES;
+       int val, i, error;
+       size_t read = 0;
+       int colbytes;
+       char wrbuf[3];
+       u8 *rdbuf;
+
+       if (*off < 0 || *off >= tsdata->raw_bufsize)
+               return 0;
+
+       mutex_lock(&tsdata->mutex);
+
+       if (!tsdata->factory_mode || !tsdata->raw_buffer) {
+               error = -EIO;
+               goto out;
+       }
+
+       error = edt_ft5x06_register_write(tsdata, 0x08, 0x01);
+       if (error) {
+               dev_dbg(&client->dev,
+                       "failed to write 0x08 register, error %d\n", error);
+               goto out;
+       }
+
+       do {
+               msleep(EDT_RAW_DATA_DELAY);
+               val = edt_ft5x06_register_read(tsdata, 0x08);
+               if (val < 1)
+                       break;
+       } while (--retries > 0);
+
+       if (val < 0) {
+               error = val;
+               dev_dbg(&client->dev,
+                       "failed to read 0x08 register, error %d\n", error);
+               goto out;
+       }
+
+       if (retries == 0) {
+               dev_dbg(&client->dev,
+                       "timed out waiting for register to settle\n");
+               error = -ETIMEDOUT;
+               goto out;
+       }
+
+       rdbuf = tsdata->raw_buffer;
+       colbytes = tsdata->num_y * sizeof(u16);
+
+       wrbuf[0] = 0xf5;
+       wrbuf[1] = 0x0e;
+       for (i = 0; i < tsdata->num_x; i++) {
+               wrbuf[2] = i;  /* column index */
+               error = edt_ft5x06_ts_readwrite(tsdata->client,
+                                               sizeof(wrbuf), wrbuf,
+                                               colbytes, rdbuf);
+               if (error)
+                       goto out;
+
+               rdbuf += colbytes;
+       }
+
+       read = min_t(size_t, count, tsdata->raw_bufsize - *off);
+       error = copy_to_user(buf, tsdata->raw_buffer + *off, read);
+       if (!error)
+               *off += read;
+out:
+       mutex_unlock(&tsdata->mutex);
+       return error ?: read;
+};
+
+
+static const struct file_operations debugfs_raw_data_fops = {
+       .open = edt_ft5x06_debugfs_raw_data_open,
+       .read = edt_ft5x06_debugfs_raw_data_read,
+};
+
+static void __devinit
+edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
+                             const char *debugfs_name)
+{
+       tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL);
+       if (!tsdata->debug_dir)
+               return;
+
+       debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x);
+       debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y);
+
+       debugfs_create_file("mode", S_IRUSR | S_IWUSR,
+                           tsdata->debug_dir, tsdata, &debugfs_mode_fops);
+       debugfs_create_file("raw_data", S_IRUSR,
+                           tsdata->debug_dir, tsdata, &debugfs_raw_data_fops);
+}
+
+static void __devexit
+edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
+{
+       if (tsdata->debug_dir)
+               debugfs_remove_recursive(tsdata->debug_dir);
+}
+
+#else
+
+static inline void
+edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
+                             const char *debugfs_name)
+{
+}
+
+static inline void
+edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
+{
+}
+
+#endif /* CONFIG_DEBUGFS */
+
+
+
+static int __devinit edt_ft5x06_ts_reset(struct i2c_client *client,
+                                        int reset_pin)
+{
+       int error;
+
+       if (gpio_is_valid(reset_pin)) {
+               /* this pulls reset down, enabling the low active reset */
+               error = gpio_request_one(reset_pin, GPIOF_OUT_INIT_LOW,
+                                        "edt-ft5x06 reset");
+               if (error) {
+                       dev_err(&client->dev,
+                               "Failed to request GPIO %d as reset pin, error %d\n",
+                               reset_pin, error);
+                       return error;
+               }
+
+               mdelay(50);
+               gpio_set_value(reset_pin, 1);
+               mdelay(100);
+       }
+
+       return 0;
+}
+
+static int __devinit edt_ft5x06_ts_identify(struct i2c_client *client,
+                                           char *model_name,
+                                           char *fw_version)
+{
+       u8 rdbuf[EDT_NAME_LEN];
+       char *p;
+       int error;
+
+       error = edt_ft5x06_ts_readwrite(client, 1, "\xbb",
+                                       EDT_NAME_LEN - 1, rdbuf);
+       if (error)
+               return error;
+
+       /* remove last '$' end marker */
+       rdbuf[EDT_NAME_LEN - 1] = '\0';
+       if (rdbuf[EDT_NAME_LEN - 2] == '$')
+               rdbuf[EDT_NAME_LEN - 2] = '\0';
+
+       /* look for Model/Version separator */
+       p = strchr(rdbuf, '*');
+       if (p)
+               *p++ = '\0';
+
+       strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN);
+       strlcpy(fw_version, p ? p : "", EDT_NAME_LEN);
+
+       return 0;
+}
+
+#define EDT_ATTR_CHECKSET(name, reg) \
+       if (pdata->name >= edt_ft5x06_attr_##name.limit_low &&          \
+           pdata->name <= edt_ft5x06_attr_##name.limit_high)           \
+               edt_ft5x06_register_write(tsdata, reg, pdata->name)
+
+static void __devinit
+edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata,
+                          const struct edt_ft5x06_platform_data *pdata)
+{
+       if (!pdata->use_parameters)
+               return;
+
+       /* pick up defaults from the platform data */
+       EDT_ATTR_CHECKSET(threshold, WORK_REGISTER_THRESHOLD);
+       EDT_ATTR_CHECKSET(gain, WORK_REGISTER_GAIN);
+       EDT_ATTR_CHECKSET(offset, WORK_REGISTER_OFFSET);
+       EDT_ATTR_CHECKSET(report_rate, WORK_REGISTER_REPORT_RATE);
+}
+
+static void __devinit
+edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
+{
+       tsdata->threshold = edt_ft5x06_register_read(tsdata,
+                                                    WORK_REGISTER_THRESHOLD);
+       tsdata->gain = edt_ft5x06_register_read(tsdata, WORK_REGISTER_GAIN);
+       tsdata->offset = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OFFSET);
+       tsdata->report_rate = edt_ft5x06_register_read(tsdata,
+                                               WORK_REGISTER_REPORT_RATE);
+       tsdata->num_x = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_X);
+       tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y);
+}
+
+static int __devinit edt_ft5x06_ts_probe(struct i2c_client *client,
+                                        const struct i2c_device_id *id)
+{
+       const struct edt_ft5x06_platform_data *pdata =
+                                               client->dev.platform_data;
+       struct edt_ft5x06_ts_data *tsdata;
+       struct input_dev *input;
+       int error;
+       char fw_version[EDT_NAME_LEN];
+
+       dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n");
+
+       if (!pdata) {
+               dev_err(&client->dev, "no platform data?\n");
+               return -EINVAL;
+       }
+
+       error = edt_ft5x06_ts_reset(client, pdata->reset_pin);
+       if (error)
+               return error;
+
+       if (gpio_is_valid(pdata->irq_pin)) {
+               error = gpio_request_one(pdata->irq_pin,
+                                        GPIOF_IN, "edt-ft5x06 irq");
+               if (error) {
+                       dev_err(&client->dev,
+                               "Failed to request GPIO %d, error %d\n",
+                               pdata->irq_pin, error);
+                       return error;
+               }
+       }
+
+       tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL);
+       input = input_allocate_device();
+       if (!tsdata || !input) {
+               dev_err(&client->dev, "failed to allocate driver data.\n");
+               error = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       mutex_init(&tsdata->mutex);
+       tsdata->client = client;
+       tsdata->input = input;
+       tsdata->factory_mode = false;
+
+       error = edt_ft5x06_ts_identify(client, tsdata->name, fw_version);
+       if (error) {
+               dev_err(&client->dev, "touchscreen probe failed\n");
+               goto err_free_mem;
+       }
+
+       edt_ft5x06_ts_get_defaults(tsdata, pdata);
+       edt_ft5x06_ts_get_parameters(tsdata);
+
+       dev_dbg(&client->dev,
+               "Model \"%s\", Rev. \"%s\", %dx%d sensors\n",
+               tsdata->name, fw_version, tsdata->num_x, tsdata->num_y);
+
+       input->name = tsdata->name;
+       input->id.bustype = BUS_I2C;
+       input->dev.parent = &client->dev;
+
+       __set_bit(EV_SYN, input->evbit);
+       __set_bit(EV_KEY, input->evbit);
+       __set_bit(EV_ABS, input->evbit);
+       __set_bit(BTN_TOUCH, input->keybit);
+       input_set_abs_params(input, ABS_X, 0, tsdata->num_x * 64 - 1, 0, 0);
+       input_set_abs_params(input, ABS_Y, 0, tsdata->num_y * 64 - 1, 0, 0);
+       input_set_abs_params(input, ABS_MT_POSITION_X,
+                            0, tsdata->num_x * 64 - 1, 0, 0);
+       input_set_abs_params(input, ABS_MT_POSITION_Y,
+                            0, tsdata->num_y * 64 - 1, 0, 0);
+       error = input_mt_init_slots(input, MAX_SUPPORT_POINTS);
+       if (error) {
+               dev_err(&client->dev, "Unable to init MT slots.\n");
+               goto err_free_mem;
+       }
+
+       input_set_drvdata(input, tsdata);
+       i2c_set_clientdata(client, tsdata);
+
+       error = request_threaded_irq(client->irq, NULL, edt_ft5x06_ts_isr,
+                                    IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                    client->name, tsdata);
+       if (error) {
+               dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
+               goto err_free_mem;
+       }
+
+       error = sysfs_create_group(&client->dev.kobj, &edt_ft5x06_attr_group);
+       if (error)
+               goto err_free_irq;
+
+       error = input_register_device(input);
+       if (error)
+               goto err_remove_attrs;
+
+       edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev));
+       device_init_wakeup(&client->dev, 1);
+
+       dev_dbg(&client->dev,
+               "EDT FT5x06 initialized: IRQ pin %d, Reset pin %d.\n",
+               pdata->irq_pin, pdata->reset_pin);
+
+       return 0;
+
+err_remove_attrs:
+       sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group);
+err_free_irq:
+       free_irq(client->irq, tsdata);
+err_free_mem:
+       input_free_device(input);
+       kfree(tsdata);
+
+       if (gpio_is_valid(pdata->irq_pin))
+               gpio_free(pdata->irq_pin);
+
+       return error;
+}
+
+static int __devexit edt_ft5x06_ts_remove(struct i2c_client *client)
+{
+       const struct edt_ft5x06_platform_data *pdata =
+                                               dev_get_platdata(&client->dev);
+       struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+
+       edt_ft5x06_ts_teardown_debugfs(tsdata);
+       sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group);
+
+       free_irq(client->irq, tsdata);
+       input_unregister_device(tsdata->input);
+
+       if (gpio_is_valid(pdata->irq_pin))
+               gpio_free(pdata->irq_pin);
+       if (gpio_is_valid(pdata->reset_pin))
+               gpio_free(pdata->reset_pin);
+
+       kfree(tsdata->raw_buffer);
+       kfree(tsdata);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int edt_ft5x06_ts_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+
+       if (device_may_wakeup(dev))
+               enable_irq_wake(client->irq);
+
+       return 0;
+}
+
+static int edt_ft5x06_ts_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+
+       if (device_may_wakeup(dev))
+               disable_irq_wake(client->irq);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,
+                        edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume);
+
+static const struct i2c_device_id edt_ft5x06_ts_id[] = {
+       { "edt-ft5x06", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
+
+static struct i2c_driver edt_ft5x06_ts_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "edt_ft5x06",
+               .pm = &edt_ft5x06_ts_pm_ops,
+       },
+       .id_table = edt_ft5x06_ts_id,
+       .probe    = edt_ft5x06_ts_probe,
+       .remove   = __devexit_p(edt_ft5x06_ts_remove),
+};
+
+module_i2c_driver(edt_ft5x06_ts_driver);
+
+MODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>");
+MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
index 5405ec644db3ee910df3765499300f11f7d1832c..baf2686aa8eb0cb027044e815dafa8b2aa6afae4 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/sched.h>
 #include "isdnloop.h"
 
-static char *revision = "$Revision: 1.11.6.7 $";
 static char *isdnloop_id = "loop0";
 
 MODULE_DESCRIPTION("ISDN4Linux: Pseudo Driver that simulates an ISDN card");
@@ -1494,17 +1493,6 @@ isdnloop_addcard(char *id1)
 static int __init
 isdnloop_init(void)
 {
-       char *p;
-       char rev[10];
-
-       if ((p = strchr(revision, ':'))) {
-               strcpy(rev, p + 1);
-               p = strchr(rev, '$');
-               *p = 0;
-       } else
-               strcpy(rev, " ??? ");
-       printk(KERN_NOTICE "isdnloop-ISDN-driver Rev%s\n", rev);
-
        if (isdnloop_id)
                return (isdnloop_addcard(isdnloop_id));
 
index 10f122a3a8566acc5a1046b4a9b35ef04ad45fab..1eee45b69b71617f9108a88b10085a56bef42c3c 100644 (file)
@@ -260,15 +260,6 @@ config DM_DEBUG_BLOCK_STACK_TRACING
 
          If unsure, say N.
 
-config DM_DEBUG_SPACE_MAPS
-       boolean "Extra validation for thin provisioning space maps"
-       depends on DM_THIN_PROVISIONING
-       ---help---
-         Enable this for messages that may help debug problems with the
-         space maps used by thin provisioning.
-
-          If unsure, say N.
-
 config DM_MIRROR
        tristate "Mirror target"
        depends on BLK_DEV_DM
index 3f06df59fd824022fd80a9c77551178660909eb6..664743d6a6cdb7961ea7555544185ea4cff06357 100644 (file)
@@ -42,21 +42,21 @@ struct convert_context {
        unsigned int offset_out;
        unsigned int idx_in;
        unsigned int idx_out;
-       sector_t sector;
-       atomic_t pending;
+       sector_t cc_sector;
+       atomic_t cc_pending;
 };
 
 /*
  * per bio private data
  */
 struct dm_crypt_io {
-       struct dm_target *target;
+       struct crypt_config *cc;
        struct bio *base_bio;
        struct work_struct work;
 
        struct convert_context ctx;
 
-       atomic_t pending;
+       atomic_t io_pending;
        int error;
        sector_t sector;
        struct dm_crypt_io *base_io;
@@ -109,9 +109,6 @@ enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID };
  */
 struct crypt_cpu {
        struct ablkcipher_request *req;
-       /* ESSIV: struct crypto_cipher *essiv_tfm */
-       void *iv_private;
-       struct crypto_ablkcipher *tfms[0];
 };
 
 /*
@@ -151,6 +148,10 @@ struct crypt_config {
         * per_cpu_ptr() only.
         */
        struct crypt_cpu __percpu *cpu;
+
+       /* ESSIV: struct crypto_cipher *essiv_tfm */
+       void *iv_private;
+       struct crypto_ablkcipher **tfms;
        unsigned tfms_count;
 
        /*
@@ -193,7 +194,7 @@ static struct crypt_cpu *this_crypt_config(struct crypt_config *cc)
  */
 static struct crypto_ablkcipher *any_tfm(struct crypt_config *cc)
 {
-       return __this_cpu_ptr(cc->cpu)->tfms[0];
+       return cc->tfms[0];
 }
 
 /*
@@ -258,7 +259,7 @@ static int crypt_iv_essiv_init(struct crypt_config *cc)
        struct hash_desc desc;
        struct scatterlist sg;
        struct crypto_cipher *essiv_tfm;
-       int err, cpu;
+       int err;
 
        sg_init_one(&sg, cc->key, cc->key_size);
        desc.tfm = essiv->hash_tfm;
@@ -268,14 +269,12 @@ static int crypt_iv_essiv_init(struct crypt_config *cc)
        if (err)
                return err;
 
-       for_each_possible_cpu(cpu) {
-               essiv_tfm = per_cpu_ptr(cc->cpu, cpu)->iv_private,
+       essiv_tfm = cc->iv_private;
 
-               err = crypto_cipher_setkey(essiv_tfm, essiv->salt,
-                                   crypto_hash_digestsize(essiv->hash_tfm));
-               if (err)
-                       return err;
-       }
+       err = crypto_cipher_setkey(essiv_tfm, essiv->salt,
+                           crypto_hash_digestsize(essiv->hash_tfm));
+       if (err)
+               return err;
 
        return 0;
 }
@@ -286,16 +285,14 @@ static int crypt_iv_essiv_wipe(struct crypt_config *cc)
        struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
        unsigned salt_size = crypto_hash_digestsize(essiv->hash_tfm);
        struct crypto_cipher *essiv_tfm;
-       int cpu, r, err = 0;
+       int r, err = 0;
 
        memset(essiv->salt, 0, salt_size);
 
-       for_each_possible_cpu(cpu) {
-               essiv_tfm = per_cpu_ptr(cc->cpu, cpu)->iv_private;
-               r = crypto_cipher_setkey(essiv_tfm, essiv->salt, salt_size);
-               if (r)
-                       err = r;
-       }
+       essiv_tfm = cc->iv_private;
+       r = crypto_cipher_setkey(essiv_tfm, essiv->salt, salt_size);
+       if (r)
+               err = r;
 
        return err;
 }
@@ -335,8 +332,6 @@ static struct crypto_cipher *setup_essiv_cpu(struct crypt_config *cc,
 
 static void crypt_iv_essiv_dtr(struct crypt_config *cc)
 {
-       int cpu;
-       struct crypt_cpu *cpu_cc;
        struct crypto_cipher *essiv_tfm;
        struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
 
@@ -346,15 +341,12 @@ static void crypt_iv_essiv_dtr(struct crypt_config *cc)
        kzfree(essiv->salt);
        essiv->salt = NULL;
 
-       for_each_possible_cpu(cpu) {
-               cpu_cc = per_cpu_ptr(cc->cpu, cpu);
-               essiv_tfm = cpu_cc->iv_private;
+       essiv_tfm = cc->iv_private;
 
-               if (essiv_tfm)
-                       crypto_free_cipher(essiv_tfm);
+       if (essiv_tfm)
+               crypto_free_cipher(essiv_tfm);
 
-               cpu_cc->iv_private = NULL;
-       }
+       cc->iv_private = NULL;
 }
 
 static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
@@ -363,7 +355,7 @@ static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
        struct crypto_cipher *essiv_tfm = NULL;
        struct crypto_hash *hash_tfm = NULL;
        u8 *salt = NULL;
-       int err, cpu;
+       int err;
 
        if (!opts) {
                ti->error = "Digest algorithm missing for ESSIV mode";
@@ -388,15 +380,13 @@ static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
        cc->iv_gen_private.essiv.salt = salt;
        cc->iv_gen_private.essiv.hash_tfm = hash_tfm;
 
-       for_each_possible_cpu(cpu) {
-               essiv_tfm = setup_essiv_cpu(cc, ti, salt,
-                                       crypto_hash_digestsize(hash_tfm));
-               if (IS_ERR(essiv_tfm)) {
-                       crypt_iv_essiv_dtr(cc);
-                       return PTR_ERR(essiv_tfm);
-               }
-               per_cpu_ptr(cc->cpu, cpu)->iv_private = essiv_tfm;
+       essiv_tfm = setup_essiv_cpu(cc, ti, salt,
+                               crypto_hash_digestsize(hash_tfm));
+       if (IS_ERR(essiv_tfm)) {
+               crypt_iv_essiv_dtr(cc);
+               return PTR_ERR(essiv_tfm);
        }
+       cc->iv_private = essiv_tfm;
 
        return 0;
 
@@ -410,7 +400,7 @@ bad:
 static int crypt_iv_essiv_gen(struct crypt_config *cc, u8 *iv,
                              struct dm_crypt_request *dmreq)
 {
-       struct crypto_cipher *essiv_tfm = this_crypt_config(cc)->iv_private;
+       struct crypto_cipher *essiv_tfm = cc->iv_private;
 
        memset(iv, 0, cc->iv_size);
        *(__le64 *)iv = cpu_to_le64(dmreq->iv_sector);
@@ -664,7 +654,7 @@ static void crypt_convert_init(struct crypt_config *cc,
        ctx->offset_out = 0;
        ctx->idx_in = bio_in ? bio_in->bi_idx : 0;
        ctx->idx_out = bio_out ? bio_out->bi_idx : 0;
-       ctx->sector = sector + cc->iv_offset;
+       ctx->cc_sector = sector + cc->iv_offset;
        init_completion(&ctx->restart);
 }
 
@@ -695,12 +685,12 @@ static int crypt_convert_block(struct crypt_config *cc,
        struct bio_vec *bv_out = bio_iovec_idx(ctx->bio_out, ctx->idx_out);
        struct dm_crypt_request *dmreq;
        u8 *iv;
-       int r = 0;
+       int r;
 
        dmreq = dmreq_of_req(cc, req);
        iv = iv_of_dmreq(cc, dmreq);
 
-       dmreq->iv_sector = ctx->sector;
+       dmreq->iv_sector = ctx->cc_sector;
        dmreq->ctx = ctx;
        sg_init_table(&dmreq->sg_in, 1);
        sg_set_page(&dmreq->sg_in, bv_in->bv_page, 1 << SECTOR_SHIFT,
@@ -749,12 +739,12 @@ static void crypt_alloc_req(struct crypt_config *cc,
                            struct convert_context *ctx)
 {
        struct crypt_cpu *this_cc = this_crypt_config(cc);
-       unsigned key_index = ctx->sector & (cc->tfms_count - 1);
+       unsigned key_index = ctx->cc_sector & (cc->tfms_count - 1);
 
        if (!this_cc->req)
                this_cc->req = mempool_alloc(cc->req_pool, GFP_NOIO);
 
-       ablkcipher_request_set_tfm(this_cc->req, this_cc->tfms[key_index]);
+       ablkcipher_request_set_tfm(this_cc->req, cc->tfms[key_index]);
        ablkcipher_request_set_callback(this_cc->req,
            CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
            kcryptd_async_done, dmreq_of_req(cc, this_cc->req));
@@ -769,14 +759,14 @@ static int crypt_convert(struct crypt_config *cc,
        struct crypt_cpu *this_cc = this_crypt_config(cc);
        int r;
 
-       atomic_set(&ctx->pending, 1);
+       atomic_set(&ctx->cc_pending, 1);
 
        while(ctx->idx_in < ctx->bio_in->bi_vcnt &&
              ctx->idx_out < ctx->bio_out->bi_vcnt) {
 
                crypt_alloc_req(cc, ctx);
 
-               atomic_inc(&ctx->pending);
+               atomic_inc(&ctx->cc_pending);
 
                r = crypt_convert_block(cc, ctx, this_cc->req);
 
@@ -788,19 +778,19 @@ static int crypt_convert(struct crypt_config *cc,
                        /* fall through*/
                case -EINPROGRESS:
                        this_cc->req = NULL;
-                       ctx->sector++;
+                       ctx->cc_sector++;
                        continue;
 
                /* sync */
                case 0:
-                       atomic_dec(&ctx->pending);
-                       ctx->sector++;
+                       atomic_dec(&ctx->cc_pending);
+                       ctx->cc_sector++;
                        cond_resched();
                        continue;
 
                /* error */
                default:
-                       atomic_dec(&ctx->pending);
+                       atomic_dec(&ctx->cc_pending);
                        return r;
                }
        }
@@ -811,7 +801,7 @@ static int crypt_convert(struct crypt_config *cc,
 static void dm_crypt_bio_destructor(struct bio *bio)
 {
        struct dm_crypt_io *io = bio->bi_private;
-       struct crypt_config *cc = io->target->private;
+       struct crypt_config *cc = io->cc;
 
        bio_free(bio, cc->bs);
 }
@@ -825,7 +815,7 @@ static void dm_crypt_bio_destructor(struct bio *bio)
 static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size,
                                      unsigned *out_of_pages)
 {
-       struct crypt_config *cc = io->target->private;
+       struct crypt_config *cc = io->cc;
        struct bio *clone;
        unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
        gfp_t gfp_mask = GFP_NOIO | __GFP_HIGHMEM;
@@ -884,26 +874,25 @@ static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone)
        }
 }
 
-static struct dm_crypt_io *crypt_io_alloc(struct dm_target *ti,
+static struct dm_crypt_io *crypt_io_alloc(struct crypt_config *cc,
                                          struct bio *bio, sector_t sector)
 {
-       struct crypt_config *cc = ti->private;
        struct dm_crypt_io *io;
 
        io = mempool_alloc(cc->io_pool, GFP_NOIO);
-       io->target = ti;
+       io->cc = cc;
        io->base_bio = bio;
        io->sector = sector;
        io->error = 0;
        io->base_io = NULL;
-       atomic_set(&io->pending, 0);
+       atomic_set(&io->io_pending, 0);
 
        return io;
 }
 
 static void crypt_inc_pending(struct dm_crypt_io *io)
 {
-       atomic_inc(&io->pending);
+       atomic_inc(&io->io_pending);
 }
 
 /*
@@ -913,12 +902,12 @@ static void crypt_inc_pending(struct dm_crypt_io *io)
  */
 static void crypt_dec_pending(struct dm_crypt_io *io)
 {
-       struct crypt_config *cc = io->target->private;
+       struct crypt_config *cc = io->cc;
        struct bio *base_bio = io->base_bio;
        struct dm_crypt_io *base_io = io->base_io;
        int error = io->error;
 
-       if (!atomic_dec_and_test(&io->pending))
+       if (!atomic_dec_and_test(&io->io_pending))
                return;
 
        mempool_free(io, cc->io_pool);
@@ -952,7 +941,7 @@ static void crypt_dec_pending(struct dm_crypt_io *io)
 static void crypt_endio(struct bio *clone, int error)
 {
        struct dm_crypt_io *io = clone->bi_private;
-       struct crypt_config *cc = io->target->private;
+       struct crypt_config *cc = io->cc;
        unsigned rw = bio_data_dir(clone);
 
        if (unlikely(!bio_flagged(clone, BIO_UPTODATE) && !error))
@@ -979,7 +968,7 @@ static void crypt_endio(struct bio *clone, int error)
 
 static void clone_init(struct dm_crypt_io *io, struct bio *clone)
 {
-       struct crypt_config *cc = io->target->private;
+       struct crypt_config *cc = io->cc;
 
        clone->bi_private = io;
        clone->bi_end_io  = crypt_endio;
@@ -990,7 +979,7 @@ static void clone_init(struct dm_crypt_io *io, struct bio *clone)
 
 static int kcryptd_io_read(struct dm_crypt_io *io, gfp_t gfp)
 {
-       struct crypt_config *cc = io->target->private;
+       struct crypt_config *cc = io->cc;
        struct bio *base_bio = io->base_bio;
        struct bio *clone;
 
@@ -1038,7 +1027,7 @@ static void kcryptd_io(struct work_struct *work)
 
 static void kcryptd_queue_io(struct dm_crypt_io *io)
 {
-       struct crypt_config *cc = io->target->private;
+       struct crypt_config *cc = io->cc;
 
        INIT_WORK(&io->work, kcryptd_io);
        queue_work(cc->io_queue, &io->work);
@@ -1047,7 +1036,7 @@ static void kcryptd_queue_io(struct dm_crypt_io *io)
 static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async)
 {
        struct bio *clone = io->ctx.bio_out;
-       struct crypt_config *cc = io->target->private;
+       struct crypt_config *cc = io->cc;
 
        if (unlikely(io->error < 0)) {
                crypt_free_buffer_pages(cc, clone);
@@ -1069,7 +1058,7 @@ static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async)
 
 static void kcryptd_crypt_write_convert(struct dm_crypt_io *io)
 {
-       struct crypt_config *cc = io->target->private;
+       struct crypt_config *cc = io->cc;
        struct bio *clone;
        struct dm_crypt_io *new_io;
        int crypt_finished;
@@ -1107,7 +1096,7 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io)
                if (r < 0)
                        io->error = -EIO;
 
-               crypt_finished = atomic_dec_and_test(&io->ctx.pending);
+               crypt_finished = atomic_dec_and_test(&io->ctx.cc_pending);
 
                /* Encryption was already finished, submit io now */
                if (crypt_finished) {
@@ -1135,7 +1124,7 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io)
                 * between fragments, so switch to a new dm_crypt_io structure.
                 */
                if (unlikely(!crypt_finished && remaining)) {
-                       new_io = crypt_io_alloc(io->target, io->base_bio,
+                       new_io = crypt_io_alloc(io->cc, io->base_bio,
                                                sector);
                        crypt_inc_pending(new_io);
                        crypt_convert_init(cc, &new_io->ctx, NULL,
@@ -1169,7 +1158,7 @@ static void kcryptd_crypt_read_done(struct dm_crypt_io *io)
 
 static void kcryptd_crypt_read_convert(struct dm_crypt_io *io)
 {
-       struct crypt_config *cc = io->target->private;
+       struct crypt_config *cc = io->cc;
        int r = 0;
 
        crypt_inc_pending(io);
@@ -1181,7 +1170,7 @@ static void kcryptd_crypt_read_convert(struct dm_crypt_io *io)
        if (r < 0)
                io->error = -EIO;
 
-       if (atomic_dec_and_test(&io->ctx.pending))
+       if (atomic_dec_and_test(&io->ctx.cc_pending))
                kcryptd_crypt_read_done(io);
 
        crypt_dec_pending(io);
@@ -1193,7 +1182,7 @@ static void kcryptd_async_done(struct crypto_async_request *async_req,
        struct dm_crypt_request *dmreq = async_req->data;
        struct convert_context *ctx = dmreq->ctx;
        struct dm_crypt_io *io = container_of(ctx, struct dm_crypt_io, ctx);
-       struct crypt_config *cc = io->target->private;
+       struct crypt_config *cc = io->cc;
 
        if (error == -EINPROGRESS) {
                complete(&ctx->restart);
@@ -1208,7 +1197,7 @@ static void kcryptd_async_done(struct crypto_async_request *async_req,
 
        mempool_free(req_of_dmreq(cc, dmreq), cc->req_pool);
 
-       if (!atomic_dec_and_test(&ctx->pending))
+       if (!atomic_dec_and_test(&ctx->cc_pending))
                return;
 
        if (bio_data_dir(io->base_bio) == READ)
@@ -1229,7 +1218,7 @@ static void kcryptd_crypt(struct work_struct *work)
 
 static void kcryptd_queue_crypt(struct dm_crypt_io *io)
 {
-       struct crypt_config *cc = io->target->private;
+       struct crypt_config *cc = io->cc;
 
        INIT_WORK(&io->work, kcryptd_crypt);
        queue_work(cc->crypt_queue, &io->work);
@@ -1241,7 +1230,6 @@ static void kcryptd_queue_crypt(struct dm_crypt_io *io)
 static int crypt_decode_key(u8 *key, char *hex, unsigned int size)
 {
        char buffer[3];
-       char *endp;
        unsigned int i;
 
        buffer[2] = '\0';
@@ -1250,9 +1238,7 @@ static int crypt_decode_key(u8 *key, char *hex, unsigned int size)
                buffer[0] = *hex++;
                buffer[1] = *hex++;
 
-               key[i] = (u8)simple_strtoul(buffer, &endp, 16);
-
-               if (endp != &buffer[2])
+               if (kstrtou8(buffer, 16, &key[i]))
                        return -EINVAL;
        }
 
@@ -1276,29 +1262,38 @@ static void crypt_encode_key(char *hex, u8 *key, unsigned int size)
        }
 }
 
-static void crypt_free_tfms(struct crypt_config *cc, int cpu)
+static void crypt_free_tfms(struct crypt_config *cc)
 {
-       struct crypt_cpu *cpu_cc = per_cpu_ptr(cc->cpu, cpu);
        unsigned i;
 
+       if (!cc->tfms)
+               return;
+
        for (i = 0; i < cc->tfms_count; i++)
-               if (cpu_cc->tfms[i] && !IS_ERR(cpu_cc->tfms[i])) {
-                       crypto_free_ablkcipher(cpu_cc->tfms[i]);
-                       cpu_cc->tfms[i] = NULL;
+               if (cc->tfms[i] && !IS_ERR(cc->tfms[i])) {
+                       crypto_free_ablkcipher(cc->tfms[i]);
+                       cc->tfms[i] = NULL;
                }
+
+       kfree(cc->tfms);
+       cc->tfms = NULL;
 }
 
-static int crypt_alloc_tfms(struct crypt_config *cc, int cpu, char *ciphermode)
+static int crypt_alloc_tfms(struct crypt_config *cc, char *ciphermode)
 {
-       struct crypt_cpu *cpu_cc = per_cpu_ptr(cc->cpu, cpu);
        unsigned i;
        int err;
 
+       cc->tfms = kmalloc(cc->tfms_count * sizeof(struct crypto_ablkcipher *),
+                          GFP_KERNEL);
+       if (!cc->tfms)
+               return -ENOMEM;
+
        for (i = 0; i < cc->tfms_count; i++) {
-               cpu_cc->tfms[i] = crypto_alloc_ablkcipher(ciphermode, 0, 0);
-               if (IS_ERR(cpu_cc->tfms[i])) {
-                       err = PTR_ERR(cpu_cc->tfms[i]);
-                       crypt_free_tfms(cc, cpu);
+               cc->tfms[i] = crypto_alloc_ablkcipher(ciphermode, 0, 0);
+               if (IS_ERR(cc->tfms[i])) {
+                       err = PTR_ERR(cc->tfms[i]);
+                       crypt_free_tfms(cc);
                        return err;
                }
        }
@@ -1309,15 +1304,14 @@ static int crypt_alloc_tfms(struct crypt_config *cc, int cpu, char *ciphermode)
 static int crypt_setkey_allcpus(struct crypt_config *cc)
 {
        unsigned subkey_size = cc->key_size >> ilog2(cc->tfms_count);
-       int cpu, err = 0, i, r;
-
-       for_each_possible_cpu(cpu) {
-               for (i = 0; i < cc->tfms_count; i++) {
-                       r = crypto_ablkcipher_setkey(per_cpu_ptr(cc->cpu, cpu)->tfms[i],
-                                                    cc->key + (i * subkey_size), subkey_size);
-                       if (r)
-                               err = r;
-               }
+       int err = 0, i, r;
+
+       for (i = 0; i < cc->tfms_count; i++) {
+               r = crypto_ablkcipher_setkey(cc->tfms[i],
+                                            cc->key + (i * subkey_size),
+                                            subkey_size);
+               if (r)
+                       err = r;
        }
 
        return err;
@@ -1379,9 +1373,10 @@ static void crypt_dtr(struct dm_target *ti)
                        cpu_cc = per_cpu_ptr(cc->cpu, cpu);
                        if (cpu_cc->req)
                                mempool_free(cpu_cc->req, cc->req_pool);
-                       crypt_free_tfms(cc, cpu);
                }
 
+       crypt_free_tfms(cc);
+
        if (cc->bs)
                bioset_free(cc->bs);
 
@@ -1414,7 +1409,7 @@ static int crypt_ctr_cipher(struct dm_target *ti,
        struct crypt_config *cc = ti->private;
        char *tmp, *cipher, *chainmode, *ivmode, *ivopts, *keycount;
        char *cipher_api = NULL;
-       int cpu, ret = -EINVAL;
+       int ret = -EINVAL;
        char dummy;
 
        /* Convert to crypto api definition? */
@@ -1455,8 +1450,7 @@ static int crypt_ctr_cipher(struct dm_target *ti,
        if (tmp)
                DMWARN("Ignoring unexpected additional cipher options");
 
-       cc->cpu = __alloc_percpu(sizeof(*(cc->cpu)) +
-                                cc->tfms_count * sizeof(*(cc->cpu->tfms)),
+       cc->cpu = __alloc_percpu(sizeof(*(cc->cpu)),
                                 __alignof__(struct crypt_cpu));
        if (!cc->cpu) {
                ti->error = "Cannot allocate per cpu state";
@@ -1489,12 +1483,10 @@ static int crypt_ctr_cipher(struct dm_target *ti,
        }
 
        /* Allocate cipher */
-       for_each_possible_cpu(cpu) {
-               ret = crypt_alloc_tfms(cc, cpu, cipher_api);
-               if (ret < 0) {
-                       ti->error = "Error allocating crypto tfm";
-                       goto bad;
-               }
+       ret = crypt_alloc_tfms(cc, cipher_api);
+       if (ret < 0) {
+               ti->error = "Error allocating crypto tfm";
+               goto bad;
        }
 
        /* Initialize and set key */
@@ -1702,7 +1694,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        }
 
        ti->num_flush_requests = 1;
-       ti->discard_zeroes_data_unsupported = 1;
+       ti->discard_zeroes_data_unsupported = true;
 
        return 0;
 
@@ -1715,7 +1707,7 @@ static int crypt_map(struct dm_target *ti, struct bio *bio,
                     union map_info *map_context)
 {
        struct dm_crypt_io *io;
-       struct crypt_config *cc;
+       struct crypt_config *cc = ti->private;
 
        /*
         * If bio is REQ_FLUSH or REQ_DISCARD, just bypass crypt queues.
@@ -1723,14 +1715,13 @@ static int crypt_map(struct dm_target *ti, struct bio *bio,
         * - for REQ_DISCARD caller must use flush if IO ordering matters
         */
        if (unlikely(bio->bi_rw & (REQ_FLUSH | REQ_DISCARD))) {
-               cc = ti->private;
                bio->bi_bdev = cc->dev->bdev;
                if (bio_sectors(bio))
                        bio->bi_sector = cc->start + dm_target_offset(ti, bio->bi_sector);
                return DM_MAPIO_REMAPPED;
        }
 
-       io = crypt_io_alloc(ti, bio, dm_target_offset(ti, bio->bi_sector));
+       io = crypt_io_alloc(cc, bio, dm_target_offset(ti, bio->bi_sector));
 
        if (bio_data_dir(io->base_bio) == READ) {
                if (kcryptd_io_read(io, GFP_NOWAIT))
@@ -1742,7 +1733,7 @@ static int crypt_map(struct dm_target *ti, struct bio *bio,
 }
 
 static int crypt_status(struct dm_target *ti, status_type_t type,
-                       char *result, unsigned int maxlen)
+                       unsigned status_flags, char *result, unsigned maxlen)
 {
        struct crypt_config *cc = ti->private;
        unsigned int sz = 0;
index 2dc22dddb2ae31abe4b5bb2371dfb8eaedfeec73..f53846f9ab502466be3208312e8cea518a8eff4d 100644 (file)
@@ -295,7 +295,7 @@ static int delay_map(struct dm_target *ti, struct bio *bio,
 }
 
 static int delay_status(struct dm_target *ti, status_type_t type,
-                       char *result, unsigned maxlen)
+                       unsigned status_flags, char *result, unsigned maxlen)
 {
        struct delay_c *dc = ti->private;
        int sz = 0;
index aa70f7d43a1ab87750cee933d244eb098cb8d92b..ebaa4f803eec3a08a0cd9fcd9a9a1b933618c50c 100644 (file)
@@ -142,24 +142,19 @@ EXPORT_SYMBOL(dm_exception_store_type_unregister);
 static int set_chunk_size(struct dm_exception_store *store,
                          const char *chunk_size_arg, char **error)
 {
-       unsigned long chunk_size_ulong;
-       char *value;
+       unsigned chunk_size;
 
-       chunk_size_ulong = simple_strtoul(chunk_size_arg, &value, 10);
-       if (*chunk_size_arg == '\0' || *value != '\0' ||
-           chunk_size_ulong > UINT_MAX) {
+       if (kstrtouint(chunk_size_arg, 10, &chunk_size)) {
                *error = "Invalid chunk size";
                return -EINVAL;
        }
 
-       if (!chunk_size_ulong) {
+       if (!chunk_size) {
                store->chunk_size = store->chunk_mask = store->chunk_shift = 0;
                return 0;
        }
 
-       return dm_exception_store_set_chunk_size(store,
-                                                (unsigned) chunk_size_ulong,
-                                                error);
+       return dm_exception_store_set_chunk_size(store, chunk_size, error);
 }
 
 int dm_exception_store_set_chunk_size(struct dm_exception_store *store,
index ac49c01f1a44bc6264ab50fcb1f7ba34bf271346..cc15543a6ad7088d712151df371a5b0b92d02dbe 100644 (file)
@@ -333,7 +333,7 @@ static int flakey_end_io(struct dm_target *ti, struct bio *bio,
 }
 
 static int flakey_status(struct dm_target *ti, status_type_t type,
-                        char *result, unsigned int maxlen)
+                        unsigned status_flags, char *result, unsigned maxlen)
 {
        unsigned sz = 0;
        struct flakey_c *fc = ti->private;
index a1a3e6df17b82624490952b0c91f94a9ea1e1f86..afd95986d09903a11652aafe8e90912db9f356ca 100644 (file)
@@ -1054,6 +1054,7 @@ static void retrieve_status(struct dm_table *table,
        char *outbuf, *outptr;
        status_type_t type;
        size_t remaining, len, used = 0;
+       unsigned status_flags = 0;
 
        outptr = outbuf = get_result_buffer(param, param_size, &len);
 
@@ -1090,7 +1091,9 @@ static void retrieve_status(struct dm_table *table,
 
                /* Get the status/table string from the target driver */
                if (ti->type->status) {
-                       if (ti->type->status(ti, type, outptr, remaining)) {
+                       if (param->flags & DM_NOFLUSH_FLAG)
+                               status_flags |= DM_STATUS_NOFLUSH_FLAG;
+                       if (ti->type->status(ti, type, status_flags, outptr, remaining)) {
                                param->flags |= DM_BUFFER_FULL_FLAG;
                                break;
                        }
index 3639eeab60421fb1829c2f70ee7bd7e193267463..1bf19a93eef05f00c68f3ad096dd46273991ef93 100644 (file)
@@ -96,7 +96,7 @@ static int linear_map(struct dm_target *ti, struct bio *bio,
 }
 
 static int linear_status(struct dm_target *ti, status_type_t type,
-                        char *result, unsigned int maxlen)
+                        unsigned status_flags, char *result, unsigned maxlen)
 {
        struct linear_c *lc = (struct linear_c *) ti->private;
 
index 65ebaebf502bdbc6e35f0cec1272589e2e5e000e..627d19186d5a1719f1780aaa21f7406edf6e99fd 100644 (file)
@@ -571,16 +571,6 @@ static void disk_dtr(struct dm_dirty_log *log)
        destroy_log_context(lc);
 }
 
-static int count_bits32(uint32_t *addr, unsigned size)
-{
-       int count = 0, i;
-
-       for (i = 0; i < size; i++) {
-               count += hweight32(*(addr+i));
-       }
-       return count;
-}
-
 static void fail_log_device(struct log_c *lc)
 {
        if (lc->log_dev_failed)
@@ -629,7 +619,8 @@ static int disk_resume(struct dm_dirty_log *log)
 
        /* copy clean across to sync */
        memcpy(lc->sync_bits, lc->clean_bits, size);
-       lc->sync_count = count_bits32(lc->clean_bits, lc->bitset_uint32_count);
+       lc->sync_count = memweight(lc->clean_bits,
+                               lc->bitset_uint32_count * sizeof(uint32_t));
        lc->sync_search = 0;
 
        /* set the correct number of regions in the header */
index 638dae048b4fada0633f2592538ee16535072b87..d8abb90a6c2fbecae99ae2de09ebc2f3d726731e 100644 (file)
@@ -85,6 +85,7 @@ struct multipath {
        unsigned queue_io:1;            /* Must we queue all I/O? */
        unsigned queue_if_no_path:1;    /* Queue I/O if last path fails? */
        unsigned saved_queue_if_no_path:1; /* Saved state during suspension */
+       unsigned retain_attached_hw_handler:1; /* If there's already a hw_handler present, don't change it. */
 
        unsigned pg_init_retries;       /* Number of times to retry pg_init */
        unsigned pg_init_count;         /* Number of times pg_init called */
@@ -568,6 +569,8 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
        int r;
        struct pgpath *p;
        struct multipath *m = ti->private;
+       struct request_queue *q = NULL;
+       const char *attached_handler_name;
 
        /* we need at least a path arg */
        if (as->argc < 1) {
@@ -586,13 +589,37 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
                goto bad;
        }
 
-       if (m->hw_handler_name) {
-               struct request_queue *q = bdev_get_queue(p->path.dev->bdev);
+       if (m->retain_attached_hw_handler || m->hw_handler_name)
+               q = bdev_get_queue(p->path.dev->bdev);
+
+       if (m->retain_attached_hw_handler) {
+               attached_handler_name = scsi_dh_attached_handler_name(q, GFP_KERNEL);
+               if (attached_handler_name) {
+                       /*
+                        * Reset hw_handler_name to match the attached handler
+                        * and clear any hw_handler_params associated with the
+                        * ignored handler.
+                        *
+                        * NB. This modifies the table line to show the actual
+                        * handler instead of the original table passed in.
+                        */
+                       kfree(m->hw_handler_name);
+                       m->hw_handler_name = attached_handler_name;
+
+                       kfree(m->hw_handler_params);
+                       m->hw_handler_params = NULL;
+               }
+       }
 
+       if (m->hw_handler_name) {
+               /*
+                * Increments scsi_dh reference, even when using an
+                * already-attached handler.
+                */
                r = scsi_dh_attach(q, m->hw_handler_name);
                if (r == -EBUSY) {
                        /*
-                        * Already attached to different hw_handler,
+                        * Already attached to different hw_handler:
                         * try to reattach with correct one.
                         */
                        scsi_dh_detach(q);
@@ -760,7 +787,7 @@ static int parse_features(struct dm_arg_set *as, struct multipath *m)
        const char *arg_name;
 
        static struct dm_arg _args[] = {
-               {0, 5, "invalid number of feature args"},
+               {0, 6, "invalid number of feature args"},
                {1, 50, "pg_init_retries must be between 1 and 50"},
                {0, 60000, "pg_init_delay_msecs must be between 0 and 60000"},
        };
@@ -781,6 +808,11 @@ static int parse_features(struct dm_arg_set *as, struct multipath *m)
                        continue;
                }
 
+               if (!strcasecmp(arg_name, "retain_attached_hw_handler")) {
+                       m->retain_attached_hw_handler = 1;
+                       continue;
+               }
+
                if (!strcasecmp(arg_name, "pg_init_retries") &&
                    (argc >= 1)) {
                        r = dm_read_arg(_args + 1, as, &m->pg_init_retries, &ti->error);
@@ -1346,7 +1378,7 @@ static void multipath_resume(struct dm_target *ti)
  *      num_paths num_selector_args [path_dev [selector_args]* ]+ ]+
  */
 static int multipath_status(struct dm_target *ti, status_type_t type,
-                           char *result, unsigned int maxlen)
+                           unsigned status_flags, char *result, unsigned maxlen)
 {
        int sz = 0;
        unsigned long flags;
@@ -1364,13 +1396,16 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
        else {
                DMEMIT("%u ", m->queue_if_no_path +
                              (m->pg_init_retries > 0) * 2 +
-                             (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) * 2);
+                             (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) * 2 +
+                             m->retain_attached_hw_handler);
                if (m->queue_if_no_path)
                        DMEMIT("queue_if_no_path ");
                if (m->pg_init_retries)
                        DMEMIT("pg_init_retries %u ", m->pg_init_retries);
                if (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT)
                        DMEMIT("pg_init_delay_msecs %u ", m->pg_init_delay_msecs);
+               if (m->retain_attached_hw_handler)
+                       DMEMIT("retain_attached_hw_handler ");
        }
 
        if (!m->hw_handler_name || type == STATUSTYPE_INFO)
@@ -1656,7 +1691,7 @@ out:
  *---------------------------------------------------------------*/
 static struct target_type multipath_target = {
        .name = "multipath",
-       .version = {1, 4, 0},
+       .version = {1, 5, 0},
        .module = THIS_MODULE,
        .ctr = multipath_ctr,
        .dtr = multipath_dtr,
index 017c34d78d61f7ca42cfc0221449b4b55ab40052..982e3e390c458ceadeab3b5f9618cf4e1bb5891e 100644 (file)
@@ -11,6 +11,7 @@
 #include "md.h"
 #include "raid1.h"
 #include "raid5.h"
+#include "raid10.h"
 #include "bitmap.h"
 
 #include <linux/device-mapper.h>
@@ -52,7 +53,10 @@ struct raid_dev {
 #define DMPF_MAX_RECOVERY_RATE 0x20
 #define DMPF_MAX_WRITE_BEHIND  0x40
 #define DMPF_STRIPE_CACHE      0x80
-#define DMPF_REGION_SIZE       0X100
+#define DMPF_REGION_SIZE       0x100
+#define DMPF_RAID10_COPIES     0x200
+#define DMPF_RAID10_FORMAT     0x400
+
 struct raid_set {
        struct dm_target *ti;
 
@@ -76,6 +80,7 @@ static struct raid_type {
        const unsigned algorithm;       /* RAID algorithm. */
 } raid_types[] = {
        {"raid1",    "RAID1 (mirroring)",               0, 2, 1, 0 /* NONE */},
+       {"raid10",   "RAID10 (striped mirrors)",        0, 2, 10, UINT_MAX /* Varies */},
        {"raid4",    "RAID4 (dedicated parity disk)",   1, 2, 5, ALGORITHM_PARITY_0},
        {"raid5_la", "RAID5 (left asymmetric)",         1, 2, 5, ALGORITHM_LEFT_ASYMMETRIC},
        {"raid5_ra", "RAID5 (right asymmetric)",        1, 2, 5, ALGORITHM_RIGHT_ASYMMETRIC},
@@ -86,6 +91,17 @@ static struct raid_type {
        {"raid6_nc", "RAID6 (N continue)",              2, 4, 6, ALGORITHM_ROTATING_N_CONTINUE}
 };
 
+static unsigned raid10_md_layout_to_copies(int layout)
+{
+       return layout & 0xFF;
+}
+
+static int raid10_format_to_md_layout(char *format, unsigned copies)
+{
+       /* 1 "far" copy, and 'copies' "near" copies */
+       return (1 << 8) | (copies & 0xFF);
+}
+
 static struct raid_type *get_raid_type(char *name)
 {
        int i;
@@ -101,20 +117,12 @@ static struct raid_set *context_alloc(struct dm_target *ti, struct raid_type *ra
 {
        unsigned i;
        struct raid_set *rs;
-       sector_t sectors_per_dev;
 
        if (raid_devs <= raid_type->parity_devs) {
                ti->error = "Insufficient number of devices";
                return ERR_PTR(-EINVAL);
        }
 
-       sectors_per_dev = ti->len;
-       if ((raid_type->level > 1) &&
-           sector_div(sectors_per_dev, (raid_devs - raid_type->parity_devs))) {
-               ti->error = "Target length not divisible by number of data devices";
-               return ERR_PTR(-EINVAL);
-       }
-
        rs = kzalloc(sizeof(*rs) + raid_devs * sizeof(rs->dev[0]), GFP_KERNEL);
        if (!rs) {
                ti->error = "Cannot allocate raid context";
@@ -128,7 +136,6 @@ static struct raid_set *context_alloc(struct dm_target *ti, struct raid_type *ra
        rs->md.raid_disks = raid_devs;
        rs->md.level = raid_type->level;
        rs->md.new_level = rs->md.level;
-       rs->md.dev_sectors = sectors_per_dev;
        rs->md.layout = raid_type->algorithm;
        rs->md.new_layout = rs->md.layout;
        rs->md.delta_disks = 0;
@@ -143,6 +150,7 @@ static struct raid_set *context_alloc(struct dm_target *ti, struct raid_type *ra
         *  rs->md.external
         *  rs->md.chunk_sectors
         *  rs->md.new_chunk_sectors
+        *  rs->md.dev_sectors
         */
 
        return rs;
@@ -347,12 +355,20 @@ static int validate_region_size(struct raid_set *rs, unsigned long region_size)
  *    [max_write_behind <sectors>]     See '-write-behind=' (man mdadm)
  *    [stripe_cache <sectors>]         Stripe cache size for higher RAIDs
  *    [region_size <sectors>]           Defines granularity of bitmap
+ *
+ * RAID10-only options:
+ *    [raid10_copies <# copies>]        Number of copies.  (Default: 2)
+ *    [raid10_format <near>]            Layout algorithm.  (Default: near)
  */
 static int parse_raid_params(struct raid_set *rs, char **argv,
                             unsigned num_raid_params)
 {
+       char *raid10_format = "near";
+       unsigned raid10_copies = 2;
        unsigned i, rebuild_cnt = 0;
        unsigned long value, region_size = 0;
+       sector_t sectors_per_dev = rs->ti->len;
+       sector_t max_io_len;
        char *key;
 
        /*
@@ -422,20 +438,53 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
                }
 
                key = argv[i++];
+
+               /* Parameters that take a string value are checked here. */
+               if (!strcasecmp(key, "raid10_format")) {
+                       if (rs->raid_type->level != 10) {
+                               rs->ti->error = "'raid10_format' is an invalid parameter for this RAID type";
+                               return -EINVAL;
+                       }
+                       if (strcmp("near", argv[i])) {
+                               rs->ti->error = "Invalid 'raid10_format' value given";
+                               return -EINVAL;
+                       }
+                       raid10_format = argv[i];
+                       rs->print_flags |= DMPF_RAID10_FORMAT;
+                       continue;
+               }
+
                if (strict_strtoul(argv[i], 10, &value) < 0) {
                        rs->ti->error = "Bad numerical argument given in raid params";
                        return -EINVAL;
                }
 
+               /* Parameters that take a numeric value are checked here */
                if (!strcasecmp(key, "rebuild")) {
                        rebuild_cnt++;
-                       if (((rs->raid_type->level != 1) &&
-                            (rebuild_cnt > rs->raid_type->parity_devs)) ||
-                           ((rs->raid_type->level == 1) &&
-                            (rebuild_cnt > (rs->md.raid_disks - 1)))) {
-                               rs->ti->error = "Too many rebuild devices specified for given RAID type";
+
+                       switch (rs->raid_type->level) {
+                       case 1:
+                               if (rebuild_cnt >= rs->md.raid_disks) {
+                                       rs->ti->error = "Too many rebuild devices specified";
+                                       return -EINVAL;
+                               }
+                               break;
+                       case 4:
+                       case 5:
+                       case 6:
+                               if (rebuild_cnt > rs->raid_type->parity_devs) {
+                                       rs->ti->error = "Too many rebuild devices specified for given RAID type";
+                                       return -EINVAL;
+                               }
+                               break;
+                       case 10:
+                       default:
+                               DMERR("The rebuild parameter is not supported for %s", rs->raid_type->name);
+                               rs->ti->error = "Rebuild not supported for this RAID type";
                                return -EINVAL;
                        }
+
                        if (value > rs->md.raid_disks) {
                                rs->ti->error = "Invalid rebuild index given";
                                return -EINVAL;
@@ -486,7 +535,8 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
                         */
                        value /= 2;
 
-                       if (rs->raid_type->level < 5) {
+                       if ((rs->raid_type->level != 5) &&
+                           (rs->raid_type->level != 6)) {
                                rs->ti->error = "Inappropriate argument: stripe_cache";
                                return -EINVAL;
                        }
@@ -511,6 +561,14 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
                } else if (!strcasecmp(key, "region_size")) {
                        rs->print_flags |= DMPF_REGION_SIZE;
                        region_size = value;
+               } else if (!strcasecmp(key, "raid10_copies") &&
+                          (rs->raid_type->level == 10)) {
+                       if ((value < 2) || (value > 0xFF)) {
+                               rs->ti->error = "Bad value for 'raid10_copies'";
+                               return -EINVAL;
+                       }
+                       rs->print_flags |= DMPF_RAID10_COPIES;
+                       raid10_copies = value;
                } else {
                        DMERR("Unable to parse RAID parameter: %s", key);
                        rs->ti->error = "Unable to parse RAID parameters";
@@ -522,14 +580,33 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
                return -EINVAL;
 
        if (rs->md.chunk_sectors)
-               rs->ti->split_io = rs->md.chunk_sectors;
+               max_io_len = rs->md.chunk_sectors;
        else
-               rs->ti->split_io = region_size;
+               max_io_len = region_size;
 
-       if (rs->md.chunk_sectors)
-               rs->ti->split_io = rs->md.chunk_sectors;
-       else
-               rs->ti->split_io = region_size;
+       if (dm_set_target_max_io_len(rs->ti, max_io_len))
+               return -EINVAL;
+
+       if (rs->raid_type->level == 10) {
+               if (raid10_copies > rs->md.raid_disks) {
+                       rs->ti->error = "Not enough devices to satisfy specification";
+                       return -EINVAL;
+               }
+
+               /* (Len * #mirrors) / #devices */
+               sectors_per_dev = rs->ti->len * raid10_copies;
+               sector_div(sectors_per_dev, rs->md.raid_disks);
+
+               rs->md.layout = raid10_format_to_md_layout(raid10_format,
+                                                          raid10_copies);
+               rs->md.new_layout = rs->md.layout;
+       } else if ((rs->raid_type->level > 1) &&
+                  sector_div(sectors_per_dev,
+                             (rs->md.raid_disks - rs->raid_type->parity_devs))) {
+               rs->ti->error = "Target length not divisible by number of data devices";
+               return -EINVAL;
+       }
+       rs->md.dev_sectors = sectors_per_dev;
 
        /* Assume there are no metadata devices until the drives are parsed */
        rs->md.persistent = 0;
@@ -552,6 +629,9 @@ static int raid_is_congested(struct dm_target_callbacks *cb, int bits)
        if (rs->raid_type->level == 1)
                return md_raid1_congested(&rs->md, bits);
 
+       if (rs->raid_type->level == 10)
+               return md_raid10_congested(&rs->md, bits);
+
        return md_raid5_congested(&rs->md, bits);
 }
 
@@ -870,6 +950,9 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
        case 6:
                redundancy = rs->raid_type->parity_devs;
                break;
+       case 10:
+               redundancy = raid10_md_layout_to_copies(mddev->layout) - 1;
+               break;
        default:
                ti->error = "Unknown RAID type";
                return -EINVAL;
@@ -1035,12 +1118,19 @@ static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv)
                goto bad;
        }
 
+       if (ti->len != rs->md.array_sectors) {
+               ti->error = "Array size does not match requested target length";
+               ret = -EINVAL;
+               goto size_mismatch;
+       }
        rs->callbacks.congested_fn = raid_is_congested;
        dm_table_add_target_callbacks(ti->table, &rs->callbacks);
 
        mddev_suspend(&rs->md);
        return 0;
 
+size_mismatch:
+       md_stop(&rs->md);
 bad:
        context_free(rs);
 
@@ -1067,7 +1157,7 @@ static int raid_map(struct dm_target *ti, struct bio *bio, union map_info *map_c
 }
 
 static int raid_status(struct dm_target *ti, status_type_t type,
-                      char *result, unsigned maxlen)
+                      unsigned status_flags, char *result, unsigned maxlen)
 {
        struct raid_set *rs = ti->private;
        unsigned raid_param_cnt = 1; /* at least 1 for chunksize */
@@ -1189,6 +1279,13 @@ static int raid_status(struct dm_target *ti, status_type_t type,
                        DMEMIT(" region_size %lu",
                               rs->md.bitmap_info.chunksize >> 9);
 
+               if (rs->print_flags & DMPF_RAID10_COPIES)
+                       DMEMIT(" raid10_copies %u",
+                              raid10_md_layout_to_copies(rs->md.layout));
+
+               if (rs->print_flags & DMPF_RAID10_FORMAT)
+                       DMEMIT(" raid10_format near");
+
                DMEMIT(" %d", rs->md.raid_disks);
                for (i = 0; i < rs->md.raid_disks; i++) {
                        if (rs->dev[i].meta_dev)
@@ -1263,7 +1360,7 @@ static void raid_resume(struct dm_target *ti)
 
 static struct target_type raid_target = {
        .name = "raid",
-       .version = {1, 2, 0},
+       .version = {1, 3, 0},
        .module = THIS_MODULE,
        .ctr = raid_ctr,
        .dtr = raid_dtr,
@@ -1290,6 +1387,8 @@ module_init(dm_raid_init);
 module_exit(dm_raid_exit);
 
 MODULE_DESCRIPTION(DM_NAME " raid4/5/6 target");
+MODULE_ALIAS("dm-raid1");
+MODULE_ALIAS("dm-raid10");
 MODULE_ALIAS("dm-raid4");
 MODULE_ALIAS("dm-raid5");
 MODULE_ALIAS("dm-raid6");
index b58b7a33914abd721e3508835a61afd699221dbf..bc5ddba8045b84a24e996235604a5db24d1d0e58 100644 (file)
@@ -1081,10 +1081,14 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        }
 
        ti->private = ms;
-       ti->split_io = dm_rh_get_region_size(ms->rh);
+
+       r = dm_set_target_max_io_len(ti, dm_rh_get_region_size(ms->rh));
+       if (r)
+               goto err_free_context;
+
        ti->num_flush_requests = 1;
        ti->num_discard_requests = 1;
-       ti->discard_zeroes_data_unsupported = 1;
+       ti->discard_zeroes_data_unsupported = true;
 
        ms->kmirrord_wq = alloc_workqueue("kmirrord",
                                          WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0);
@@ -1363,7 +1367,7 @@ static char device_status_char(struct mirror *m)
 
 
 static int mirror_status(struct dm_target *ti, status_type_t type,
-                        char *result, unsigned int maxlen)
+                        unsigned status_flags, char *result, unsigned maxlen)
 {
        unsigned int m, sz = 0;
        struct mirror_set *ms = (struct mirror_set *) ti->private;
index 6f758870fc19cf0e66db3055dab36ab96e038fee..a143921feaf6480119f77d4f8600aefc98c7b583 100644 (file)
@@ -691,7 +691,7 @@ static int dm_add_exception(void *context, chunk_t old, chunk_t new)
  * Return a minimum chunk size of all snapshots that have the specified origin.
  * Return zero if the origin has no snapshots.
  */
-static sector_t __minimum_chunk_size(struct origin *o)
+static uint32_t __minimum_chunk_size(struct origin *o)
 {
        struct dm_snapshot *snap;
        unsigned chunk_size = 0;
@@ -701,7 +701,7 @@ static sector_t __minimum_chunk_size(struct origin *o)
                        chunk_size = min_not_zero(chunk_size,
                                                  snap->store->chunk_size);
 
-       return chunk_size;
+       return (uint32_t) chunk_size;
 }
 
 /*
@@ -1172,7 +1172,10 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
                ti->error = "Chunk size not set";
                goto bad_read_metadata;
        }
-       ti->split_io = s->store->chunk_size;
+
+       r = dm_set_target_max_io_len(ti, s->store->chunk_size);
+       if (r)
+               goto bad_read_metadata;
 
        return 0;
 
@@ -1239,7 +1242,7 @@ static void __handover_exceptions(struct dm_snapshot *snap_src,
        snap_dest->store->snap = snap_dest;
        snap_src->store->snap = snap_src;
 
-       snap_dest->ti->split_io = snap_dest->store->chunk_size;
+       snap_dest->ti->max_io_len = snap_dest->store->chunk_size;
        snap_dest->valid = snap_src->valid;
 
        /*
@@ -1817,9 +1820,9 @@ static void snapshot_resume(struct dm_target *ti)
        up_write(&s->lock);
 }
 
-static sector_t get_origin_minimum_chunksize(struct block_device *bdev)
+static uint32_t get_origin_minimum_chunksize(struct block_device *bdev)
 {
-       sector_t min_chunksize;
+       uint32_t min_chunksize;
 
        down_read(&_origins_lock);
        min_chunksize = __minimum_chunk_size(__lookup_origin(bdev));
@@ -1838,15 +1841,15 @@ static void snapshot_merge_resume(struct dm_target *ti)
        snapshot_resume(ti);
 
        /*
-        * snapshot-merge acts as an origin, so set ti->split_io
+        * snapshot-merge acts as an origin, so set ti->max_io_len
         */
-       ti->split_io = get_origin_minimum_chunksize(s->origin->bdev);
+       ti->max_io_len = get_origin_minimum_chunksize(s->origin->bdev);
 
        start_merge(s);
 }
 
 static int snapshot_status(struct dm_target *ti, status_type_t type,
-                          char *result, unsigned int maxlen)
+                          unsigned status_flags, char *result, unsigned maxlen)
 {
        unsigned sz = 0;
        struct dm_snapshot *snap = ti->private;
@@ -2073,12 +2076,12 @@ static int origin_write_extent(struct dm_snapshot *merging_snap,
        struct origin *o;
 
        /*
-        * The origin's __minimum_chunk_size() got stored in split_io
+        * The origin's __minimum_chunk_size() got stored in max_io_len
         * by snapshot_merge_resume().
         */
        down_read(&_origins_lock);
        o = __lookup_origin(merging_snap->origin->bdev);
-       for (n = 0; n < size; n += merging_snap->ti->split_io)
+       for (n = 0; n < size; n += merging_snap->ti->max_io_len)
                if (__origin_write(&o->snapshots, sector + n, NULL) ==
                    DM_MAPIO_SUBMITTED)
                        must_wait = 1;
@@ -2138,18 +2141,18 @@ static int origin_map(struct dm_target *ti, struct bio *bio,
 }
 
 /*
- * Set the target "split_io" field to the minimum of all the snapshots'
+ * Set the target "max_io_len" field to the minimum of all the snapshots'
  * chunk sizes.
  */
 static void origin_resume(struct dm_target *ti)
 {
        struct dm_dev *dev = ti->private;
 
-       ti->split_io = get_origin_minimum_chunksize(dev->bdev);
+       ti->max_io_len = get_origin_minimum_chunksize(dev->bdev);
 }
 
-static int origin_status(struct dm_target *ti, status_type_t type, char *result,
-                        unsigned int maxlen)
+static int origin_status(struct dm_target *ti, status_type_t type,
+                        unsigned status_flags, char *result, unsigned maxlen)
 {
        struct dm_dev *dev = ti->private;
 
@@ -2176,7 +2179,6 @@ static int origin_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
                return max_size;
 
        bvm->bi_bdev = dev->bdev;
-       bvm->bi_sector = bvm->bi_sector;
 
        return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
 }
index 35c94ff24ad5867917ed715a8ce387fbb8d3b150..a087bf2a8d6666bbc699a44af28d003b4d2cfb88 100644 (file)
@@ -26,14 +26,12 @@ struct stripe {
 struct stripe_c {
        uint32_t stripes;
        int stripes_shift;
-       sector_t stripes_mask;
 
        /* The size of this target / num. stripes */
        sector_t stripe_width;
 
-       /* stripe chunk size */
-       uint32_t chunk_shift;
-       sector_t chunk_mask;
+       uint32_t chunk_size;
+       int chunk_size_shift;
 
        /* Needed for handling events */
        struct dm_target *ti;
@@ -91,7 +89,7 @@ static int get_stripe(struct dm_target *ti, struct stripe_c *sc,
 
 /*
  * Construct a striped mapping.
- * <number of stripes> <chunk size (2^^n)> [<dev_path> <offset>]+
+ * <number of stripes> <chunk size> [<dev_path> <offset>]+
  */
 static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 {
@@ -99,7 +97,6 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        sector_t width;
        uint32_t stripes;
        uint32_t chunk_size;
-       char *end;
        int r;
        unsigned int i;
 
@@ -108,34 +105,23 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
                return -EINVAL;
        }
 
-       stripes = simple_strtoul(argv[0], &end, 10);
-       if (!stripes || *end) {
+       if (kstrtouint(argv[0], 10, &stripes) || !stripes) {
                ti->error = "Invalid stripe count";
                return -EINVAL;
        }
 
-       chunk_size = simple_strtoul(argv[1], &end, 10);
-       if (*end) {
+       if (kstrtouint(argv[1], 10, &chunk_size) || !chunk_size) {
                ti->error = "Invalid chunk_size";
                return -EINVAL;
        }
 
-       /*
-        * chunk_size is a power of two
-        */
-       if (!is_power_of_2(chunk_size) ||
-           (chunk_size < (PAGE_SIZE >> SECTOR_SHIFT))) {
-               ti->error = "Invalid chunk size";
-               return -EINVAL;
-       }
-
-       if (ti->len & (chunk_size - 1)) {
+       width = ti->len;
+       if (sector_div(width, chunk_size)) {
                ti->error = "Target length not divisible by "
                    "chunk size";
                return -EINVAL;
        }
 
-       width = ti->len;
        if (sector_div(width, stripes)) {
                ti->error = "Target length not divisible by "
                    "number of stripes";
@@ -167,17 +153,21 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 
        if (stripes & (stripes - 1))
                sc->stripes_shift = -1;
-       else {
-               sc->stripes_shift = ffs(stripes) - 1;
-               sc->stripes_mask = ((sector_t) stripes) - 1;
-       }
+       else
+               sc->stripes_shift = __ffs(stripes);
+
+       r = dm_set_target_max_io_len(ti, chunk_size);
+       if (r)
+               return r;
 
-       ti->split_io = chunk_size;
        ti->num_flush_requests = stripes;
        ti->num_discard_requests = stripes;
 
-       sc->chunk_shift = ffs(chunk_size) - 1;
-       sc->chunk_mask = ((sector_t) chunk_size) - 1;
+       sc->chunk_size = chunk_size;
+       if (chunk_size & (chunk_size - 1))
+               sc->chunk_size_shift = -1;
+       else
+               sc->chunk_size_shift = __ffs(chunk_size);
 
        /*
         * Get the stripe destinations.
@@ -216,17 +206,29 @@ static void stripe_dtr(struct dm_target *ti)
 static void stripe_map_sector(struct stripe_c *sc, sector_t sector,
                              uint32_t *stripe, sector_t *result)
 {
-       sector_t offset = dm_target_offset(sc->ti, sector);
-       sector_t chunk = offset >> sc->chunk_shift;
+       sector_t chunk = dm_target_offset(sc->ti, sector);
+       sector_t chunk_offset;
+
+       if (sc->chunk_size_shift < 0)
+               chunk_offset = sector_div(chunk, sc->chunk_size);
+       else {
+               chunk_offset = chunk & (sc->chunk_size - 1);
+               chunk >>= sc->chunk_size_shift;
+       }
 
        if (sc->stripes_shift < 0)
                *stripe = sector_div(chunk, sc->stripes);
        else {
-               *stripe = chunk & sc->stripes_mask;
+               *stripe = chunk & (sc->stripes - 1);
                chunk >>= sc->stripes_shift;
        }
 
-       *result = (chunk << sc->chunk_shift) | (offset & sc->chunk_mask);
+       if (sc->chunk_size_shift < 0)
+               chunk *= sc->chunk_size;
+       else
+               chunk <<= sc->chunk_size_shift;
+
+       *result = chunk + chunk_offset;
 }
 
 static void stripe_map_range_sector(struct stripe_c *sc, sector_t sector,
@@ -237,9 +239,16 @@ static void stripe_map_range_sector(struct stripe_c *sc, sector_t sector,
        stripe_map_sector(sc, sector, &stripe, result);
        if (stripe == target_stripe)
                return;
-       *result &= ~sc->chunk_mask;                     /* round down */
+
+       /* round down */
+       sector = *result;
+       if (sc->chunk_size_shift < 0)
+               *result -= sector_div(sector, sc->chunk_size);
+       else
+               *result = sector & ~(sector_t)(sc->chunk_size - 1);
+
        if (target_stripe < stripe)
-               *result += sc->chunk_mask + 1;          /* next chunk */
+               *result += sc->chunk_size;              /* next chunk */
 }
 
 static int stripe_map_discard(struct stripe_c *sc, struct bio *bio,
@@ -302,8 +311,8 @@ static int stripe_map(struct dm_target *ti, struct bio *bio,
  *
  */
 
-static int stripe_status(struct dm_target *ti,
-                        status_type_t type, char *result, unsigned int maxlen)
+static int stripe_status(struct dm_target *ti, status_type_t type,
+                        unsigned status_flags, char *result, unsigned maxlen)
 {
        struct stripe_c *sc = (struct stripe_c *) ti->private;
        char buffer[sc->stripes + 1];
@@ -324,7 +333,7 @@ static int stripe_status(struct dm_target *ti,
 
        case STATUSTYPE_TABLE:
                DMEMIT("%d %llu", sc->stripes,
-                       (unsigned long long)sc->chunk_mask + 1);
+                       (unsigned long long)sc->chunk_size);
                for (i = 0; i < sc->stripes; i++)
                        DMEMIT(" %s %llu", sc->stripe[i].dev->name,
                            (unsigned long long)sc->stripe[i].physical_start);
@@ -391,7 +400,7 @@ static void stripe_io_hints(struct dm_target *ti,
                            struct queue_limits *limits)
 {
        struct stripe_c *sc = ti->private;
-       unsigned chunk_size = (sc->chunk_mask + 1) << 9;
+       unsigned chunk_size = sc->chunk_size << SECTOR_SHIFT;
 
        blk_limits_io_min(limits, chunk_size);
        blk_limits_io_opt(limits, chunk_size * sc->stripes);
@@ -419,7 +428,7 @@ static int stripe_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
 
 static struct target_type stripe_target = {
        .name   = "striped",
-       .version = {1, 4, 0},
+       .version = {1, 5, 0},
        .module = THIS_MODULE,
        .ctr    = stripe_ctr,
        .dtr    = stripe_dtr,
index 2e227fbf1622c075c6fc543f5b2d30fb2bf7dbd7..f90069029aaeed02ab6f4f61814afc92d13db2f0 100644 (file)
@@ -1319,6 +1319,9 @@ static bool dm_table_supports_flush(struct dm_table *t, unsigned flush)
                if (!ti->num_flush_requests)
                        continue;
 
+               if (ti->flush_supported)
+                       return 1;
+
                if (ti->type->iterate_devices &&
                    ti->type->iterate_devices(ti, device_flush_capable, &flush))
                        return 1;
index 3e2907f0bc462e261c05fab97cb0bff272649868..693e149e97271dbe3fa3b646cfac018533892835 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2011-2012 Red Hat, Inc.
  *
  * This file is released under the GPL.
  */
 #define THIN_METADATA_CACHE_SIZE 64
 #define SECTOR_TO_BLOCK_SHIFT 3
 
+/*
+ *  3 for btree insert +
+ *  2 for btree lookup used within space map
+ */
+#define THIN_MAX_CONCURRENT_LOCKS 5
+
 /* This should be plenty */
 #define SPACE_MAP_ROOT_SIZE 128
 
@@ -172,13 +178,20 @@ struct dm_pool_metadata {
 
        struct rw_semaphore root_lock;
        uint32_t time;
-       int need_commit;
        dm_block_t root;
        dm_block_t details_root;
        struct list_head thin_devices;
        uint64_t trans_id;
        unsigned long flags;
        sector_t data_block_size;
+       bool read_only:1;
+
+       /*
+        * Set if a transaction has to be aborted but the attempt to roll back
+        * to the previous (good) transaction failed.  The only pool metadata
+        * operation possible in this state is the closing of the device.
+        */
+       bool fail_io:1;
 };
 
 struct dm_thin_device {
@@ -187,7 +200,8 @@ struct dm_thin_device {
        dm_thin_id id;
 
        int open_count;
-       int changed;
+       bool changed:1;
+       bool aborted_with_changes:1;
        uint64_t mapped_blocks;
        uint64_t transaction_id;
        uint32_t creation_time;
@@ -338,7 +352,21 @@ static int subtree_equal(void *context, void *value1_le, void *value2_le)
 
 /*----------------------------------------------------------------*/
 
-static int superblock_all_zeroes(struct dm_block_manager *bm, int *result)
+static int superblock_lock_zero(struct dm_pool_metadata *pmd,
+                               struct dm_block **sblock)
+{
+       return dm_bm_write_lock_zero(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+                                    &sb_validator, sblock);
+}
+
+static int superblock_lock(struct dm_pool_metadata *pmd,
+                          struct dm_block **sblock)
+{
+       return dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+                               &sb_validator, sblock);
+}
+
+static int __superblock_all_zeroes(struct dm_block_manager *bm, int *result)
 {
        int r;
        unsigned i;
@@ -365,72 +393,9 @@ static int superblock_all_zeroes(struct dm_block_manager *bm, int *result)
        return dm_bm_unlock(b);
 }
 
-static int init_pmd(struct dm_pool_metadata *pmd,
-                   struct dm_block_manager *bm,
-                   dm_block_t nr_blocks, int create)
+static void __setup_btree_details(struct dm_pool_metadata *pmd)
 {
-       int r;
-       struct dm_space_map *sm, *data_sm;
-       struct dm_transaction_manager *tm;
-       struct dm_block *sblock;
-
-       if (create) {
-               r = dm_tm_create_with_sm(bm, THIN_SUPERBLOCK_LOCATION,
-                                        &sb_validator, &tm, &sm, &sblock);
-               if (r < 0) {
-                       DMERR("tm_create_with_sm failed");
-                       return r;
-               }
-
-               data_sm = dm_sm_disk_create(tm, nr_blocks);
-               if (IS_ERR(data_sm)) {
-                       DMERR("sm_disk_create failed");
-                       dm_tm_unlock(tm, sblock);
-                       r = PTR_ERR(data_sm);
-                       goto bad;
-               }
-       } else {
-               struct thin_disk_superblock *disk_super = NULL;
-               size_t space_map_root_offset =
-                       offsetof(struct thin_disk_superblock, metadata_space_map_root);
-
-               r = dm_tm_open_with_sm(bm, THIN_SUPERBLOCK_LOCATION,
-                                      &sb_validator, space_map_root_offset,
-                                      SPACE_MAP_ROOT_SIZE, &tm, &sm, &sblock);
-               if (r < 0) {
-                       DMERR("tm_open_with_sm failed");
-                       return r;
-               }
-
-               disk_super = dm_block_data(sblock);
-               data_sm = dm_sm_disk_open(tm, disk_super->data_space_map_root,
-                                         sizeof(disk_super->data_space_map_root));
-               if (IS_ERR(data_sm)) {
-                       DMERR("sm_disk_open failed");
-                       r = PTR_ERR(data_sm);
-                       goto bad;
-               }
-       }
-
-
-       r = dm_tm_unlock(tm, sblock);
-       if (r < 0) {
-               DMERR("couldn't unlock superblock");
-               goto bad_data_sm;
-       }
-
-       pmd->bm = bm;
-       pmd->metadata_sm = sm;
-       pmd->data_sm = data_sm;
-       pmd->tm = tm;
-       pmd->nb_tm = dm_tm_create_non_blocking_clone(tm);
-       if (!pmd->nb_tm) {
-               DMERR("could not create clone tm");
-               r = -ENOMEM;
-               goto bad_data_sm;
-       }
-
-       pmd->info.tm = tm;
+       pmd->info.tm = pmd->tm;
        pmd->info.levels = 2;
        pmd->info.value_type.context = pmd->data_sm;
        pmd->info.value_type.size = sizeof(__le64);
@@ -441,7 +406,7 @@ static int init_pmd(struct dm_pool_metadata *pmd,
        memcpy(&pmd->nb_info, &pmd->info, sizeof(pmd->nb_info));
        pmd->nb_info.tm = pmd->nb_tm;
 
-       pmd->tl_info.tm = tm;
+       pmd->tl_info.tm = pmd->tm;
        pmd->tl_info.levels = 1;
        pmd->tl_info.value_type.context = &pmd->info;
        pmd->tl_info.value_type.size = sizeof(__le64);
@@ -449,7 +414,7 @@ static int init_pmd(struct dm_pool_metadata *pmd,
        pmd->tl_info.value_type.dec = subtree_dec;
        pmd->tl_info.value_type.equal = subtree_equal;
 
-       pmd->bl_info.tm = tm;
+       pmd->bl_info.tm = pmd->tm;
        pmd->bl_info.levels = 1;
        pmd->bl_info.value_type.context = pmd->data_sm;
        pmd->bl_info.value_type.size = sizeof(__le64);
@@ -457,47 +422,265 @@ static int init_pmd(struct dm_pool_metadata *pmd,
        pmd->bl_info.value_type.dec = data_block_dec;
        pmd->bl_info.value_type.equal = data_block_equal;
 
-       pmd->details_info.tm = tm;
+       pmd->details_info.tm = pmd->tm;
        pmd->details_info.levels = 1;
        pmd->details_info.value_type.context = NULL;
        pmd->details_info.value_type.size = sizeof(struct disk_device_details);
        pmd->details_info.value_type.inc = NULL;
        pmd->details_info.value_type.dec = NULL;
        pmd->details_info.value_type.equal = NULL;
+}
 
-       pmd->root = 0;
+static int __write_initial_superblock(struct dm_pool_metadata *pmd)
+{
+       int r;
+       struct dm_block *sblock;
+       size_t metadata_len, data_len;
+       struct thin_disk_superblock *disk_super;
+       sector_t bdev_size = i_size_read(pmd->bdev->bd_inode) >> SECTOR_SHIFT;
 
-       init_rwsem(&pmd->root_lock);
-       pmd->time = 0;
-       pmd->need_commit = 0;
-       pmd->details_root = 0;
-       pmd->trans_id = 0;
-       pmd->flags = 0;
-       INIT_LIST_HEAD(&pmd->thin_devices);
+       if (bdev_size > THIN_METADATA_MAX_SECTORS)
+               bdev_size = THIN_METADATA_MAX_SECTORS;
+
+       r = dm_sm_root_size(pmd->metadata_sm, &metadata_len);
+       if (r < 0)
+               return r;
+
+       r = dm_sm_root_size(pmd->data_sm, &data_len);
+       if (r < 0)
+               return r;
+
+       r = dm_sm_commit(pmd->data_sm);
+       if (r < 0)
+               return r;
+
+       r = dm_tm_pre_commit(pmd->tm);
+       if (r < 0)
+               return r;
+
+       r = superblock_lock_zero(pmd, &sblock);
+       if (r)
+               return r;
+
+       disk_super = dm_block_data(sblock);
+       disk_super->flags = 0;
+       memset(disk_super->uuid, 0, sizeof(disk_super->uuid));
+       disk_super->magic = cpu_to_le64(THIN_SUPERBLOCK_MAGIC);
+       disk_super->version = cpu_to_le32(THIN_VERSION);
+       disk_super->time = 0;
+       disk_super->trans_id = 0;
+       disk_super->held_root = 0;
+
+       r = dm_sm_copy_root(pmd->metadata_sm, &disk_super->metadata_space_map_root,
+                           metadata_len);
+       if (r < 0)
+               goto bad_locked;
+
+       r = dm_sm_copy_root(pmd->data_sm, &disk_super->data_space_map_root,
+                           data_len);
+       if (r < 0)
+               goto bad_locked;
+
+       disk_super->data_mapping_root = cpu_to_le64(pmd->root);
+       disk_super->device_details_root = cpu_to_le64(pmd->details_root);
+       disk_super->metadata_block_size = cpu_to_le32(THIN_METADATA_BLOCK_SIZE >> SECTOR_SHIFT);
+       disk_super->metadata_nr_blocks = cpu_to_le64(bdev_size >> SECTOR_TO_BLOCK_SHIFT);
+       disk_super->data_block_size = cpu_to_le32(pmd->data_block_size);
+
+       return dm_tm_commit(pmd->tm, sblock);
+
+bad_locked:
+       dm_bm_unlock(sblock);
+       return r;
+}
+
+static int __format_metadata(struct dm_pool_metadata *pmd)
+{
+       int r;
+
+       r = dm_tm_create_with_sm(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+                                &pmd->tm, &pmd->metadata_sm);
+       if (r < 0) {
+               DMERR("tm_create_with_sm failed");
+               return r;
+       }
+
+       pmd->data_sm = dm_sm_disk_create(pmd->tm, 0);
+       if (IS_ERR(pmd->data_sm)) {
+               DMERR("sm_disk_create failed");
+               r = PTR_ERR(pmd->data_sm);
+               goto bad_cleanup_tm;
+       }
+
+       pmd->nb_tm = dm_tm_create_non_blocking_clone(pmd->tm);
+       if (!pmd->nb_tm) {
+               DMERR("could not create non-blocking clone tm");
+               r = -ENOMEM;
+               goto bad_cleanup_data_sm;
+       }
+
+       __setup_btree_details(pmd);
+
+       r = dm_btree_empty(&pmd->info, &pmd->root);
+       if (r < 0)
+               goto bad_cleanup_nb_tm;
+
+       r = dm_btree_empty(&pmd->details_info, &pmd->details_root);
+       if (r < 0) {
+               DMERR("couldn't create devices root");
+               goto bad_cleanup_nb_tm;
+       }
+
+       r = __write_initial_superblock(pmd);
+       if (r)
+               goto bad_cleanup_nb_tm;
 
        return 0;
 
-bad_data_sm:
-       dm_sm_destroy(data_sm);
-bad:
-       dm_tm_destroy(tm);
-       dm_sm_destroy(sm);
+bad_cleanup_nb_tm:
+       dm_tm_destroy(pmd->nb_tm);
+bad_cleanup_data_sm:
+       dm_sm_destroy(pmd->data_sm);
+bad_cleanup_tm:
+       dm_tm_destroy(pmd->tm);
+       dm_sm_destroy(pmd->metadata_sm);
+
+       return r;
+}
+
+static int __check_incompat_features(struct thin_disk_superblock *disk_super,
+                                    struct dm_pool_metadata *pmd)
+{
+       uint32_t features;
+
+       features = le32_to_cpu(disk_super->incompat_flags) & ~THIN_FEATURE_INCOMPAT_SUPP;
+       if (features) {
+               DMERR("could not access metadata due to unsupported optional features (%lx).",
+                     (unsigned long)features);
+               return -EINVAL;
+       }
+
+       /*
+        * Check for read-only metadata to skip the following RDWR checks.
+        */
+       if (get_disk_ro(pmd->bdev->bd_disk))
+               return 0;
+
+       features = le32_to_cpu(disk_super->compat_ro_flags) & ~THIN_FEATURE_COMPAT_RO_SUPP;
+       if (features) {
+               DMERR("could not access metadata RDWR due to unsupported optional features (%lx).",
+                     (unsigned long)features);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int __open_metadata(struct dm_pool_metadata *pmd)
+{
+       int r;
+       struct dm_block *sblock;
+       struct thin_disk_superblock *disk_super;
+
+       r = dm_bm_read_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+                           &sb_validator, &sblock);
+       if (r < 0) {
+               DMERR("couldn't read superblock");
+               return r;
+       }
+
+       disk_super = dm_block_data(sblock);
+
+       r = __check_incompat_features(disk_super, pmd);
+       if (r < 0)
+               goto bad_unlock_sblock;
+
+       r = dm_tm_open_with_sm(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+                              disk_super->metadata_space_map_root,
+                              sizeof(disk_super->metadata_space_map_root),
+                              &pmd->tm, &pmd->metadata_sm);
+       if (r < 0) {
+               DMERR("tm_open_with_sm failed");
+               goto bad_unlock_sblock;
+       }
+
+       pmd->data_sm = dm_sm_disk_open(pmd->tm, disk_super->data_space_map_root,
+                                      sizeof(disk_super->data_space_map_root));
+       if (IS_ERR(pmd->data_sm)) {
+               DMERR("sm_disk_open failed");
+               r = PTR_ERR(pmd->data_sm);
+               goto bad_cleanup_tm;
+       }
+
+       pmd->nb_tm = dm_tm_create_non_blocking_clone(pmd->tm);
+       if (!pmd->nb_tm) {
+               DMERR("could not create non-blocking clone tm");
+               r = -ENOMEM;
+               goto bad_cleanup_data_sm;
+       }
+
+       __setup_btree_details(pmd);
+       return dm_bm_unlock(sblock);
+
+bad_cleanup_data_sm:
+       dm_sm_destroy(pmd->data_sm);
+bad_cleanup_tm:
+       dm_tm_destroy(pmd->tm);
+       dm_sm_destroy(pmd->metadata_sm);
+bad_unlock_sblock:
+       dm_bm_unlock(sblock);
+
+       return r;
+}
+
+static int __open_or_format_metadata(struct dm_pool_metadata *pmd, bool format_device)
+{
+       int r, unformatted;
+
+       r = __superblock_all_zeroes(pmd->bm, &unformatted);
+       if (r)
+               return r;
+
+       if (unformatted)
+               return format_device ? __format_metadata(pmd) : -EPERM;
+
+       return __open_metadata(pmd);
+}
+
+static int __create_persistent_data_objects(struct dm_pool_metadata *pmd, bool format_device)
+{
+       int r;
+
+       pmd->bm = dm_block_manager_create(pmd->bdev, THIN_METADATA_BLOCK_SIZE,
+                                         THIN_METADATA_CACHE_SIZE,
+                                         THIN_MAX_CONCURRENT_LOCKS);
+       if (IS_ERR(pmd->bm)) {
+               DMERR("could not create block manager");
+               return PTR_ERR(pmd->bm);
+       }
+
+       r = __open_or_format_metadata(pmd, format_device);
+       if (r)
+               dm_block_manager_destroy(pmd->bm);
 
        return r;
 }
 
+static void __destroy_persistent_data_objects(struct dm_pool_metadata *pmd)
+{
+       dm_sm_destroy(pmd->data_sm);
+       dm_sm_destroy(pmd->metadata_sm);
+       dm_tm_destroy(pmd->nb_tm);
+       dm_tm_destroy(pmd->tm);
+       dm_block_manager_destroy(pmd->bm);
+}
+
 static int __begin_transaction(struct dm_pool_metadata *pmd)
 {
        int r;
-       u32 features;
        struct thin_disk_superblock *disk_super;
        struct dm_block *sblock;
 
-       /*
-        * __maybe_commit_transaction() resets these
-        */
-       WARN_ON(pmd->need_commit);
-
        /*
         * We re-read the superblock every time.  Shouldn't need to do this
         * really.
@@ -515,32 +698,8 @@ static int __begin_transaction(struct dm_pool_metadata *pmd)
        pmd->flags = le32_to_cpu(disk_super->flags);
        pmd->data_block_size = le32_to_cpu(disk_super->data_block_size);
 
-       features = le32_to_cpu(disk_super->incompat_flags) & ~THIN_FEATURE_INCOMPAT_SUPP;
-       if (features) {
-               DMERR("could not access metadata due to "
-                     "unsupported optional features (%lx).",
-                     (unsigned long)features);
-               r = -EINVAL;
-               goto out;
-       }
-
-       /*
-        * Check for read-only metadata to skip the following RDWR checks.
-        */
-       if (get_disk_ro(pmd->bdev->bd_disk))
-               goto out;
-
-       features = le32_to_cpu(disk_super->compat_ro_flags) & ~THIN_FEATURE_COMPAT_RO_SUPP;
-       if (features) {
-               DMERR("could not access metadata RDWR due to "
-                     "unsupported optional features (%lx).",
-                     (unsigned long)features);
-               r = -EINVAL;
-       }
-
-out:
        dm_bm_unlock(sblock);
-       return r;
+       return 0;
 }
 
 static int __write_changed_details(struct dm_pool_metadata *pmd)
@@ -573,8 +732,6 @@ static int __write_changed_details(struct dm_pool_metadata *pmd)
                        list_del(&td->list);
                        kfree(td);
                }
-
-               pmd->need_commit = 1;
        }
 
        return 0;
@@ -582,9 +739,6 @@ static int __write_changed_details(struct dm_pool_metadata *pmd)
 
 static int __commit_transaction(struct dm_pool_metadata *pmd)
 {
-       /*
-        * FIXME: Associated pool should be made read-only on failure.
-        */
        int r;
        size_t metadata_len, data_len;
        struct thin_disk_superblock *disk_super;
@@ -597,31 +751,27 @@ static int __commit_transaction(struct dm_pool_metadata *pmd)
 
        r = __write_changed_details(pmd);
        if (r < 0)
-               goto out;
-
-       if (!pmd->need_commit)
-               goto out;
+               return r;
 
        r = dm_sm_commit(pmd->data_sm);
        if (r < 0)
-               goto out;
+               return r;
 
        r = dm_tm_pre_commit(pmd->tm);
        if (r < 0)
-               goto out;
+               return r;
 
        r = dm_sm_root_size(pmd->metadata_sm, &metadata_len);
        if (r < 0)
-               goto out;
+               return r;
 
        r = dm_sm_root_size(pmd->data_sm, &data_len);
        if (r < 0)
-               goto out;
+               return r;
 
-       r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
-                            &sb_validator, &sblock);
+       r = superblock_lock(pmd, &sblock);
        if (r)
-               goto out;
+               return r;
 
        disk_super = dm_block_data(sblock);
        disk_super->time = cpu_to_le32(pmd->time);
@@ -640,12 +790,7 @@ static int __commit_transaction(struct dm_pool_metadata *pmd)
        if (r < 0)
                goto out_locked;
 
-       r = dm_tm_commit(pmd->tm, sblock);
-       if (!r)
-               pmd->need_commit = 0;
-
-out:
-       return r;
+       return dm_tm_commit(pmd->tm, sblock);
 
 out_locked:
        dm_bm_unlock(sblock);
@@ -653,15 +798,11 @@ out_locked:
 }
 
 struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
-                                              sector_t data_block_size)
+                                              sector_t data_block_size,
+                                              bool format_device)
 {
        int r;
-       struct thin_disk_superblock *disk_super;
        struct dm_pool_metadata *pmd;
-       sector_t bdev_size = i_size_read(bdev->bd_inode) >> SECTOR_SHIFT;
-       struct dm_block_manager *bm;
-       int create;
-       struct dm_block *sblock;
 
        pmd = kmalloc(sizeof(*pmd), GFP_KERNEL);
        if (!pmd) {
@@ -669,90 +810,28 @@ struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
                return ERR_PTR(-ENOMEM);
        }
 
-       /*
-        * Max hex locks:
-        *  3 for btree insert +
-        *  2 for btree lookup used within space map
-        */
-       bm = dm_block_manager_create(bdev, THIN_METADATA_BLOCK_SIZE,
-                                    THIN_METADATA_CACHE_SIZE, 5);
-       if (!bm) {
-               DMERR("could not create block manager");
-               kfree(pmd);
-               return ERR_PTR(-ENOMEM);
-       }
-
-       r = superblock_all_zeroes(bm, &create);
-       if (r) {
-               dm_block_manager_destroy(bm);
-               kfree(pmd);
-               return ERR_PTR(r);
-       }
-
+       init_rwsem(&pmd->root_lock);
+       pmd->time = 0;
+       INIT_LIST_HEAD(&pmd->thin_devices);
+       pmd->read_only = false;
+       pmd->fail_io = false;
+       pmd->bdev = bdev;
+       pmd->data_block_size = data_block_size;
 
-       r = init_pmd(pmd, bm, 0, create);
+       r = __create_persistent_data_objects(pmd, format_device);
        if (r) {
-               dm_block_manager_destroy(bm);
                kfree(pmd);
                return ERR_PTR(r);
        }
-       pmd->bdev = bdev;
-
-       if (!create) {
-               r = __begin_transaction(pmd);
-               if (r < 0)
-                       goto bad;
-               return pmd;
-       }
-
-       /*
-        * Create.
-        */
-       r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
-                            &sb_validator, &sblock);
-       if (r)
-               goto bad;
-
-       if (bdev_size > THIN_METADATA_MAX_SECTORS)
-               bdev_size = THIN_METADATA_MAX_SECTORS;
-
-       disk_super = dm_block_data(sblock);
-       disk_super->magic = cpu_to_le64(THIN_SUPERBLOCK_MAGIC);
-       disk_super->version = cpu_to_le32(THIN_VERSION);
-       disk_super->time = 0;
-       disk_super->metadata_block_size = cpu_to_le32(THIN_METADATA_BLOCK_SIZE >> SECTOR_SHIFT);
-       disk_super->metadata_nr_blocks = cpu_to_le64(bdev_size >> SECTOR_TO_BLOCK_SHIFT);
-       disk_super->data_block_size = cpu_to_le32(data_block_size);
-
-       r = dm_bm_unlock(sblock);
-       if (r < 0)
-               goto bad;
-
-       r = dm_btree_empty(&pmd->info, &pmd->root);
-       if (r < 0)
-               goto bad;
-
-       r = dm_btree_empty(&pmd->details_info, &pmd->details_root);
-       if (r < 0) {
-               DMERR("couldn't create devices root");
-               goto bad;
-       }
 
-       pmd->flags = 0;
-       pmd->need_commit = 1;
-       r = dm_pool_commit_metadata(pmd);
+       r = __begin_transaction(pmd);
        if (r < 0) {
-               DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
-                     __func__, r);
-               goto bad;
+               if (dm_pool_metadata_close(pmd) < 0)
+                       DMWARN("%s: dm_pool_metadata_close() failed.", __func__);
+               return ERR_PTR(r);
        }
 
        return pmd;
-
-bad:
-       if (dm_pool_metadata_close(pmd) < 0)
-               DMWARN("%s: dm_pool_metadata_close() failed.", __func__);
-       return ERR_PTR(r);
 }
 
 int dm_pool_metadata_close(struct dm_pool_metadata *pmd)
@@ -778,18 +857,17 @@ int dm_pool_metadata_close(struct dm_pool_metadata *pmd)
                return -EBUSY;
        }
 
-       r = __commit_transaction(pmd);
-       if (r < 0)
-               DMWARN("%s: __commit_transaction() failed, error = %d",
-                      __func__, r);
+       if (!pmd->read_only && !pmd->fail_io) {
+               r = __commit_transaction(pmd);
+               if (r < 0)
+                       DMWARN("%s: __commit_transaction() failed, error = %d",
+                              __func__, r);
+       }
 
-       dm_tm_destroy(pmd->tm);
-       dm_tm_destroy(pmd->nb_tm);
-       dm_block_manager_destroy(pmd->bm);
-       dm_sm_destroy(pmd->metadata_sm);
-       dm_sm_destroy(pmd->data_sm);
-       kfree(pmd);
+       if (!pmd->fail_io)
+               __destroy_persistent_data_objects(pmd);
 
+       kfree(pmd);
        return 0;
 }
 
@@ -850,6 +928,7 @@ static int __open_device(struct dm_pool_metadata *pmd,
        (*td)->id = dev;
        (*td)->open_count = 1;
        (*td)->changed = changed;
+       (*td)->aborted_with_changes = false;
        (*td)->mapped_blocks = le64_to_cpu(details_le.mapped_blocks);
        (*td)->transaction_id = le64_to_cpu(details_le.transaction_id);
        (*td)->creation_time = le32_to_cpu(details_le.creation_time);
@@ -911,10 +990,11 @@ static int __create_thin(struct dm_pool_metadata *pmd,
 
 int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_thin_id dev)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
-       r = __create_thin(pmd, dev);
+       if (!pmd->fail_io)
+               r = __create_thin(pmd, dev);
        up_write(&pmd->root_lock);
 
        return r;
@@ -1001,10 +1081,11 @@ int dm_pool_create_snap(struct dm_pool_metadata *pmd,
                                 dm_thin_id dev,
                                 dm_thin_id origin)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
-       r = __create_snap(pmd, dev, origin);
+       if (!pmd->fail_io)
+               r = __create_snap(pmd, dev, origin);
        up_write(&pmd->root_lock);
 
        return r;
@@ -1037,18 +1118,17 @@ static int __delete_device(struct dm_pool_metadata *pmd, dm_thin_id dev)
        if (r)
                return r;
 
-       pmd->need_commit = 1;
-
        return 0;
 }
 
 int dm_pool_delete_thin_device(struct dm_pool_metadata *pmd,
                               dm_thin_id dev)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
-       r = __delete_device(pmd, dev);
+       if (!pmd->fail_io)
+               r = __delete_device(pmd, dev);
        up_write(&pmd->root_lock);
 
        return r;
@@ -1058,28 +1138,40 @@ int dm_pool_set_metadata_transaction_id(struct dm_pool_metadata *pmd,
                                        uint64_t current_id,
                                        uint64_t new_id)
 {
+       int r = -EINVAL;
+
        down_write(&pmd->root_lock);
+
+       if (pmd->fail_io)
+               goto out;
+
        if (pmd->trans_id != current_id) {
-               up_write(&pmd->root_lock);
                DMERR("mismatched transaction id");
-               return -EINVAL;
+               goto out;
        }
 
        pmd->trans_id = new_id;
-       pmd->need_commit = 1;
+       r = 0;
+
+out:
        up_write(&pmd->root_lock);
 
-       return 0;
+       return r;
 }
 
 int dm_pool_get_metadata_transaction_id(struct dm_pool_metadata *pmd,
                                        uint64_t *result)
 {
+       int r = -EINVAL;
+
        down_read(&pmd->root_lock);
-       *result = pmd->trans_id;
+       if (!pmd->fail_io) {
+               *result = pmd->trans_id;
+               r = 0;
+       }
        up_read(&pmd->root_lock);
 
-       return 0;
+       return r;
 }
 
 static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
@@ -1108,8 +1200,6 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
 
                dm_tm_dec(pmd->tm, held_root);
                dm_tm_unlock(pmd->tm, copy);
-               pmd->need_commit = 1;
-
                return -EBUSY;
        }
 
@@ -1131,29 +1221,25 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
        /*
         * Write the held root into the superblock.
         */
-       r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
-                            &sb_validator, &sblock);
+       r = superblock_lock(pmd, &sblock);
        if (r) {
                dm_tm_dec(pmd->tm, held_root);
-               pmd->need_commit = 1;
                return r;
        }
 
        disk_super = dm_block_data(sblock);
        disk_super->held_root = cpu_to_le64(held_root);
        dm_bm_unlock(sblock);
-
-       pmd->need_commit = 1;
-
        return 0;
 }
 
 int dm_pool_reserve_metadata_snap(struct dm_pool_metadata *pmd)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
-       r = __reserve_metadata_snap(pmd);
+       if (!pmd->fail_io)
+               r = __reserve_metadata_snap(pmd);
        up_write(&pmd->root_lock);
 
        return r;
@@ -1166,15 +1252,13 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd)
        struct dm_block *sblock, *copy;
        dm_block_t held_root;
 
-       r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
-                            &sb_validator, &sblock);
+       r = superblock_lock(pmd, &sblock);
        if (r)
                return r;
 
        disk_super = dm_block_data(sblock);
        held_root = le64_to_cpu(disk_super->held_root);
        disk_super->held_root = cpu_to_le64(0);
-       pmd->need_commit = 1;
 
        dm_bm_unlock(sblock);
 
@@ -1197,10 +1281,11 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd)
 
 int dm_pool_release_metadata_snap(struct dm_pool_metadata *pmd)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
-       r = __release_metadata_snap(pmd);
+       if (!pmd->fail_io)
+               r = __release_metadata_snap(pmd);
        up_write(&pmd->root_lock);
 
        return r;
@@ -1227,10 +1312,11 @@ static int __get_metadata_snap(struct dm_pool_metadata *pmd,
 int dm_pool_get_metadata_snap(struct dm_pool_metadata *pmd,
                              dm_block_t *result)
 {
-       int r;
+       int r = -EINVAL;
 
        down_read(&pmd->root_lock);
-       r = __get_metadata_snap(pmd, result);
+       if (!pmd->fail_io)
+               r = __get_metadata_snap(pmd, result);
        up_read(&pmd->root_lock);
 
        return r;
@@ -1239,10 +1325,11 @@ int dm_pool_get_metadata_snap(struct dm_pool_metadata *pmd,
 int dm_pool_open_thin_device(struct dm_pool_metadata *pmd, dm_thin_id dev,
                             struct dm_thin_device **td)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
-       r = __open_device(pmd, dev, 0, td);
+       if (!pmd->fail_io)
+               r = __open_device(pmd, dev, 0, td);
        up_write(&pmd->root_lock);
 
        return r;
@@ -1262,7 +1349,7 @@ dm_thin_id dm_thin_dev_id(struct dm_thin_device *td)
        return td->id;
 }
 
-static int __snapshotted_since(struct dm_thin_device *td, uint32_t time)
+static bool __snapshotted_since(struct dm_thin_device *td, uint32_t time)
 {
        return td->snapshotted_time > time;
 }
@@ -1270,28 +1357,31 @@ static int __snapshotted_since(struct dm_thin_device *td, uint32_t time)
 int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
                       int can_block, struct dm_thin_lookup_result *result)
 {
-       int r;
+       int r = -EINVAL;
        uint64_t block_time = 0;
        __le64 value;
        struct dm_pool_metadata *pmd = td->pmd;
        dm_block_t keys[2] = { td->id, block };
+       struct dm_btree_info *info;
 
        if (can_block) {
                down_read(&pmd->root_lock);
-               r = dm_btree_lookup(&pmd->info, pmd->root, keys, &value);
-               if (!r)
-                       block_time = le64_to_cpu(value);
-               up_read(&pmd->root_lock);
-
-       } else if (down_read_trylock(&pmd->root_lock)) {
-               r = dm_btree_lookup(&pmd->nb_info, pmd->root, keys, &value);
-               if (!r)
-                       block_time = le64_to_cpu(value);
-               up_read(&pmd->root_lock);
-
-       } else
+               info = &pmd->info;
+       } else if (down_read_trylock(&pmd->root_lock))
+               info = &pmd->nb_info;
+       else
                return -EWOULDBLOCK;
 
+       if (pmd->fail_io)
+               goto out;
+
+       r = dm_btree_lookup(info, pmd->root, keys, &value);
+       if (!r)
+               block_time = le64_to_cpu(value);
+
+out:
+       up_read(&pmd->root_lock);
+
        if (!r) {
                dm_block_t exception_block;
                uint32_t exception_time;
@@ -1312,7 +1402,6 @@ static int __insert(struct dm_thin_device *td, dm_block_t block,
        struct dm_pool_metadata *pmd = td->pmd;
        dm_block_t keys[2] = { td->id, block };
 
-       pmd->need_commit = 1;
        value = cpu_to_le64(pack_block_time(data_block, pmd->time));
        __dm_bless_for_disk(&value);
 
@@ -1321,10 +1410,9 @@ static int __insert(struct dm_thin_device *td, dm_block_t block,
        if (r)
                return r;
 
-       if (inserted) {
+       td->changed = 1;
+       if (inserted)
                td->mapped_blocks++;
-               td->changed = 1;
-       }
 
        return 0;
 }
@@ -1332,10 +1420,11 @@ static int __insert(struct dm_thin_device *td, dm_block_t block,
 int dm_thin_insert_block(struct dm_thin_device *td, dm_block_t block,
                         dm_block_t data_block)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&td->pmd->root_lock);
-       r = __insert(td, block, data_block);
+       if (!td->pmd->fail_io)
+               r = __insert(td, block, data_block);
        up_write(&td->pmd->root_lock);
 
        return r;
@@ -1353,31 +1442,51 @@ static int __remove(struct dm_thin_device *td, dm_block_t block)
 
        td->mapped_blocks--;
        td->changed = 1;
-       pmd->need_commit = 1;
 
        return 0;
 }
 
 int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&td->pmd->root_lock);
-       r = __remove(td, block);
+       if (!td->pmd->fail_io)
+               r = __remove(td, block);
        up_write(&td->pmd->root_lock);
 
        return r;
 }
 
-int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result)
+bool dm_thin_changed_this_transaction(struct dm_thin_device *td)
 {
        int r;
 
-       down_write(&pmd->root_lock);
+       down_read(&td->pmd->root_lock);
+       r = td->changed;
+       up_read(&td->pmd->root_lock);
 
-       r = dm_sm_new_block(pmd->data_sm, result);
-       pmd->need_commit = 1;
+       return r;
+}
+
+bool dm_thin_aborted_changes(struct dm_thin_device *td)
+{
+       bool r;
 
+       down_read(&td->pmd->root_lock);
+       r = td->aborted_with_changes;
+       up_read(&td->pmd->root_lock);
+
+       return r;
+}
+
+int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result)
+{
+       int r = -EINVAL;
+
+       down_write(&pmd->root_lock);
+       if (!pmd->fail_io)
+               r = dm_sm_new_block(pmd->data_sm, result);
        up_write(&pmd->root_lock);
 
        return r;
@@ -1385,9 +1494,11 @@ int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result)
 
 int dm_pool_commit_metadata(struct dm_pool_metadata *pmd)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
+       if (pmd->fail_io)
+               goto out;
 
        r = __commit_transaction(pmd);
        if (r <= 0)
@@ -1402,12 +1513,41 @@ out:
        return r;
 }
 
+static void __set_abort_with_changes_flags(struct dm_pool_metadata *pmd)
+{
+       struct dm_thin_device *td;
+
+       list_for_each_entry(td, &pmd->thin_devices, list)
+               td->aborted_with_changes = td->changed;
+}
+
+int dm_pool_abort_metadata(struct dm_pool_metadata *pmd)
+{
+       int r = -EINVAL;
+
+       down_write(&pmd->root_lock);
+       if (pmd->fail_io)
+               goto out;
+
+       __set_abort_with_changes_flags(pmd);
+       __destroy_persistent_data_objects(pmd);
+       r = __create_persistent_data_objects(pmd, false);
+       if (r)
+               pmd->fail_io = true;
+
+out:
+       up_write(&pmd->root_lock);
+
+       return r;
+}
+
 int dm_pool_get_free_block_count(struct dm_pool_metadata *pmd, dm_block_t *result)
 {
-       int r;
+       int r = -EINVAL;
 
        down_read(&pmd->root_lock);
-       r = dm_sm_get_nr_free(pmd->data_sm, result);
+       if (!pmd->fail_io)
+               r = dm_sm_get_nr_free(pmd->data_sm, result);
        up_read(&pmd->root_lock);
 
        return r;
@@ -1416,10 +1556,11 @@ int dm_pool_get_free_block_count(struct dm_pool_metadata *pmd, dm_block_t *resul
 int dm_pool_get_free_metadata_block_count(struct dm_pool_metadata *pmd,
                                          dm_block_t *result)
 {
-       int r;
+       int r = -EINVAL;
 
        down_read(&pmd->root_lock);
-       r = dm_sm_get_nr_free(pmd->metadata_sm, result);
+       if (!pmd->fail_io)
+               r = dm_sm_get_nr_free(pmd->metadata_sm, result);
        up_read(&pmd->root_lock);
 
        return r;
@@ -1428,10 +1569,11 @@ int dm_pool_get_free_metadata_block_count(struct dm_pool_metadata *pmd,
 int dm_pool_get_metadata_dev_size(struct dm_pool_metadata *pmd,
                                  dm_block_t *result)
 {
-       int r;
+       int r = -EINVAL;
 
        down_read(&pmd->root_lock);
-       r = dm_sm_get_nr_blocks(pmd->metadata_sm, result);
+       if (!pmd->fail_io)
+               r = dm_sm_get_nr_blocks(pmd->metadata_sm, result);
        up_read(&pmd->root_lock);
 
        return r;
@@ -1448,10 +1590,11 @@ int dm_pool_get_data_block_size(struct dm_pool_metadata *pmd, sector_t *result)
 
 int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result)
 {
-       int r;
+       int r = -EINVAL;
 
        down_read(&pmd->root_lock);
-       r = dm_sm_get_nr_blocks(pmd->data_sm, result);
+       if (!pmd->fail_io)
+               r = dm_sm_get_nr_blocks(pmd->data_sm, result);
        up_read(&pmd->root_lock);
 
        return r;
@@ -1459,13 +1602,17 @@ int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result)
 
 int dm_thin_get_mapped_count(struct dm_thin_device *td, dm_block_t *result)
 {
+       int r = -EINVAL;
        struct dm_pool_metadata *pmd = td->pmd;
 
        down_read(&pmd->root_lock);
-       *result = td->mapped_blocks;
+       if (!pmd->fail_io) {
+               *result = td->mapped_blocks;
+               r = 0;
+       }
        up_read(&pmd->root_lock);
 
-       return 0;
+       return r;
 }
 
 static int __highest_block(struct dm_thin_device *td, dm_block_t *result)
@@ -1487,11 +1634,12 @@ static int __highest_block(struct dm_thin_device *td, dm_block_t *result)
 int dm_thin_get_highest_mapped_block(struct dm_thin_device *td,
                                     dm_block_t *result)
 {
-       int r;
+       int r = -EINVAL;
        struct dm_pool_metadata *pmd = td->pmd;
 
        down_read(&pmd->root_lock);
-       r = __highest_block(td, result);
+       if (!pmd->fail_io)
+               r = __highest_block(td, result);
        up_read(&pmd->root_lock);
 
        return r;
@@ -1514,20 +1662,25 @@ static int __resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
                return -EINVAL;
        }
 
-       r = dm_sm_extend(pmd->data_sm, new_count - old_count);
-       if (!r)
-               pmd->need_commit = 1;
-
-       return r;
+       return dm_sm_extend(pmd->data_sm, new_count - old_count);
 }
 
 int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
-       r = __resize_data_dev(pmd, new_count);
+       if (!pmd->fail_io)
+               r = __resize_data_dev(pmd, new_count);
        up_write(&pmd->root_lock);
 
        return r;
 }
+
+void dm_pool_metadata_read_only(struct dm_pool_metadata *pmd)
+{
+       down_write(&pmd->root_lock);
+       pmd->read_only = true;
+       dm_bm_set_read_only(pmd->bm);
+       up_write(&pmd->root_lock);
+}
index b88918ccdaf688e3bc5f10478023276893cc3521..0cecc3702885fb57452c013f83679fc437a393ca 100644 (file)
@@ -38,7 +38,8 @@ typedef uint64_t dm_thin_id;
  * Reopens or creates a new, empty metadata volume.
  */
 struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
-                                              sector_t data_block_size);
+                                              sector_t data_block_size,
+                                              bool format_device);
 
 int dm_pool_metadata_close(struct dm_pool_metadata *pmd);
 
@@ -78,6 +79,16 @@ int dm_pool_delete_thin_device(struct dm_pool_metadata *pmd,
  */
 int dm_pool_commit_metadata(struct dm_pool_metadata *pmd);
 
+/*
+ * Discards all uncommitted changes.  Rereads the superblock, rolling back
+ * to the last good transaction.  Thin devices remain open.
+ * dm_thin_aborted_changes() tells you if they had uncommitted changes.
+ *
+ * If this call fails it's only useful to call dm_pool_metadata_close().
+ * All other methods will fail with -EINVAL.
+ */
+int dm_pool_abort_metadata(struct dm_pool_metadata *pmd);
+
 /*
  * Set/get userspace transaction id.
  */
@@ -119,7 +130,7 @@ dm_thin_id dm_thin_dev_id(struct dm_thin_device *td);
 
 struct dm_thin_lookup_result {
        dm_block_t block;
-       int shared;
+       unsigned shared:1;
 };
 
 /*
@@ -147,6 +158,10 @@ int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block);
 /*
  * Queries.
  */
+bool dm_thin_changed_this_transaction(struct dm_thin_device *td);
+
+bool dm_thin_aborted_changes(struct dm_thin_device *td);
+
 int dm_thin_get_highest_mapped_block(struct dm_thin_device *td,
                                     dm_block_t *highest_mapped);
 
@@ -171,6 +186,12 @@ int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result);
  */
 int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_size);
 
+/*
+ * Flicks the underlying block manager into read only mode, so you know
+ * that nothing is changing.
+ */
+void dm_pool_metadata_read_only(struct dm_pool_metadata *pmd);
+
 /*----------------------------------------------------------------*/
 
 #endif
index 68694da0d21d0566a61649339cb3a4f3a3bbc943..af1fc3b2c2adbb365e247a2d45c1f7a94de9b6dd 100644 (file)
@@ -1,10 +1,11 @@
 /*
- * Copyright (C) 2011 Red Hat UK.
+ * Copyright (C) 2011-2012 Red Hat UK.
  *
  * This file is released under the GPL.
  */
 
 #include "dm-thin-metadata.h"
+#include "dm.h"
 
 #include <linux/device-mapper.h>
 #include <linux/dm-io.h>
@@ -19,7 +20,7 @@
 /*
  * Tunable constants
  */
-#define ENDIO_HOOK_POOL_SIZE 10240
+#define ENDIO_HOOK_POOL_SIZE 1024
 #define DEFERRED_SET_SIZE 64
 #define MAPPING_POOL_SIZE 1024
 #define PRISON_CELLS 1024
@@ -496,12 +497,27 @@ static void build_virtual_key(struct dm_thin_device *td, dm_block_t b,
  */
 struct dm_thin_new_mapping;
 
+/*
+ * The pool runs in 3 modes.  Ordered in degraded order for comparisons.
+ */
+enum pool_mode {
+       PM_WRITE,               /* metadata may be changed */
+       PM_READ_ONLY,           /* metadata may not be changed */
+       PM_FAIL,                /* all I/O fails */
+};
+
 struct pool_features {
+       enum pool_mode mode;
+
        unsigned zero_new_blocks:1;
        unsigned discard_enabled:1;
        unsigned discard_passdown:1;
 };
 
+struct thin_c;
+typedef void (*process_bio_fn)(struct thin_c *tc, struct bio *bio);
+typedef void (*process_mapping_fn)(struct dm_thin_new_mapping *m);
+
 struct pool {
        struct list_head list;
        struct dm_target *ti;   /* Only set if a pool target is bound */
@@ -510,10 +526,9 @@ struct pool {
        struct block_device *md_dev;
        struct dm_pool_metadata *pmd;
 
-       uint32_t sectors_per_block;
-       unsigned block_shift;
-       dm_block_t offset_mask;
        dm_block_t low_water_blocks;
+       uint32_t sectors_per_block;
+       int sectors_per_block_shift;
 
        struct pool_features pf;
        unsigned low_water_triggered:1; /* A dm event has been sent */
@@ -526,8 +541,8 @@ struct pool {
        struct work_struct worker;
        struct delayed_work waker;
 
-       unsigned ref_count;
        unsigned long last_commit_jiffies;
+       unsigned ref_count;
 
        spinlock_t lock;
        struct bio_list deferred_bios;
@@ -543,8 +558,17 @@ struct pool {
        struct dm_thin_new_mapping *next_mapping;
        mempool_t *mapping_pool;
        mempool_t *endio_hook_pool;
+
+       process_bio_fn process_bio;
+       process_bio_fn process_discard;
+
+       process_mapping_fn process_prepared_mapping;
+       process_mapping_fn process_prepared_discard;
 };
 
+static enum pool_mode get_pool_mode(struct pool *pool);
+static void set_pool_mode(struct pool *pool, enum pool_mode mode);
+
 /*
  * Target context for a pool.
  */
@@ -679,16 +703,28 @@ static void requeue_io(struct thin_c *tc)
 
 static dm_block_t get_bio_block(struct thin_c *tc, struct bio *bio)
 {
-       return bio->bi_sector >> tc->pool->block_shift;
+       sector_t block_nr = bio->bi_sector;
+
+       if (tc->pool->sectors_per_block_shift < 0)
+               (void) sector_div(block_nr, tc->pool->sectors_per_block);
+       else
+               block_nr >>= tc->pool->sectors_per_block_shift;
+
+       return block_nr;
 }
 
 static void remap(struct thin_c *tc, struct bio *bio, dm_block_t block)
 {
        struct pool *pool = tc->pool;
+       sector_t bi_sector = bio->bi_sector;
 
        bio->bi_bdev = tc->pool_dev->bdev;
-       bio->bi_sector = (block << pool->block_shift) +
-               (bio->bi_sector & pool->offset_mask);
+       if (tc->pool->sectors_per_block_shift < 0)
+               bio->bi_sector = (block * pool->sectors_per_block) +
+                                sector_div(bi_sector, pool->sectors_per_block);
+       else
+               bio->bi_sector = (block << pool->sectors_per_block_shift) |
+                               (bi_sector & (pool->sectors_per_block - 1));
 }
 
 static void remap_to_origin(struct thin_c *tc, struct bio *bio)
@@ -696,21 +732,39 @@ static void remap_to_origin(struct thin_c *tc, struct bio *bio)
        bio->bi_bdev = tc->origin_dev->bdev;
 }
 
+static int bio_triggers_commit(struct thin_c *tc, struct bio *bio)
+{
+       return (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) &&
+               dm_thin_changed_this_transaction(tc->td);
+}
+
 static void issue(struct thin_c *tc, struct bio *bio)
 {
        struct pool *pool = tc->pool;
        unsigned long flags;
 
+       if (!bio_triggers_commit(tc, bio)) {
+               generic_make_request(bio);
+               return;
+       }
+
        /*
-        * Batch together any FUA/FLUSH bios we find and then issue
-        * a single commit for them in process_deferred_bios().
+        * Complete bio with an error if earlier I/O caused changes to
+        * the metadata that can't be committed e.g, due to I/O errors
+        * on the metadata device.
         */
-       if (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) {
-               spin_lock_irqsave(&pool->lock, flags);
-               bio_list_add(&pool->deferred_flush_bios, bio);
-               spin_unlock_irqrestore(&pool->lock, flags);
-       } else
-               generic_make_request(bio);
+       if (dm_thin_aborted_changes(tc->td)) {
+               bio_io_error(bio);
+               return;
+       }
+
+       /*
+        * Batch together any bios that trigger commits and then issue a
+        * single commit for them in process_deferred_bios().
+        */
+       spin_lock_irqsave(&pool->lock, flags);
+       bio_list_add(&pool->deferred_flush_bios, bio);
+       spin_unlock_irqrestore(&pool->lock, flags);
 }
 
 static void remap_to_origin_and_issue(struct thin_c *tc, struct bio *bio)
@@ -847,6 +901,14 @@ static void cell_defer_except(struct thin_c *tc, struct dm_bio_prison_cell *cell
        wake_worker(pool);
 }
 
+static void process_prepared_mapping_fail(struct dm_thin_new_mapping *m)
+{
+       if (m->bio)
+               m->bio->bi_end_io = m->saved_bi_end_io;
+       cell_error(m->cell);
+       list_del(&m->list);
+       mempool_free(m, m->tc->pool->mapping_pool);
+}
 static void process_prepared_mapping(struct dm_thin_new_mapping *m)
 {
        struct thin_c *tc = m->tc;
@@ -859,7 +921,7 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m)
 
        if (m->err) {
                cell_error(m->cell);
-               return;
+               goto out;
        }
 
        /*
@@ -871,7 +933,7 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m)
        if (r) {
                DMERR("dm_thin_insert_block() failed");
                cell_error(m->cell);
-               return;
+               goto out;
        }
 
        /*
@@ -886,22 +948,25 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m)
        } else
                cell_defer(tc, m->cell, m->data_block);
 
+out:
        list_del(&m->list);
        mempool_free(m, tc->pool->mapping_pool);
 }
 
-static void process_prepared_discard(struct dm_thin_new_mapping *m)
+static void process_prepared_discard_fail(struct dm_thin_new_mapping *m)
 {
-       int r;
        struct thin_c *tc = m->tc;
 
-       r = dm_thin_remove_block(tc->td, m->virt_block);
-       if (r)
-               DMERR("dm_thin_remove_block() failed");
+       bio_io_error(m->bio);
+       cell_defer_except(tc, m->cell);
+       cell_defer_except(tc, m->cell2);
+       mempool_free(m, tc->pool->mapping_pool);
+}
+
+static void process_prepared_discard_passdown(struct dm_thin_new_mapping *m)
+{
+       struct thin_c *tc = m->tc;
 
-       /*
-        * Pass the discard down to the underlying device?
-        */
        if (m->pass_discard)
                remap_and_issue(tc, m->bio, m->data_block);
        else
@@ -912,8 +977,20 @@ static void process_prepared_discard(struct dm_thin_new_mapping *m)
        mempool_free(m, tc->pool->mapping_pool);
 }
 
+static void process_prepared_discard(struct dm_thin_new_mapping *m)
+{
+       int r;
+       struct thin_c *tc = m->tc;
+
+       r = dm_thin_remove_block(tc->td, m->virt_block);
+       if (r)
+               DMERR("dm_thin_remove_block() failed");
+
+       process_prepared_discard_passdown(m);
+}
+
 static void process_prepared(struct pool *pool, struct list_head *head,
-                            void (*fn)(struct dm_thin_new_mapping *))
+                            process_mapping_fn *fn)
 {
        unsigned long flags;
        struct list_head maps;
@@ -925,7 +1002,7 @@ static void process_prepared(struct pool *pool, struct list_head *head,
        spin_unlock_irqrestore(&pool->lock, flags);
 
        list_for_each_entry_safe(m, tmp, &maps, list)
-               fn(m);
+               (*fn)(m);
 }
 
 /*
@@ -933,9 +1010,7 @@ static void process_prepared(struct pool *pool, struct list_head *head,
  */
 static int io_overlaps_block(struct pool *pool, struct bio *bio)
 {
-       return !(bio->bi_sector & pool->offset_mask) &&
-               (bio->bi_size == (pool->sectors_per_block << SECTOR_SHIFT));
-
+       return bio->bi_size == (pool->sectors_per_block << SECTOR_SHIFT);
 }
 
 static int io_overwrites_block(struct pool *pool, struct bio *bio)
@@ -1093,6 +1168,35 @@ static void schedule_zero(struct thin_c *tc, dm_block_t virt_block,
        }
 }
 
+static int commit(struct pool *pool)
+{
+       int r;
+
+       r = dm_pool_commit_metadata(pool->pmd);
+       if (r)
+               DMERR("commit failed, error = %d", r);
+
+       return r;
+}
+
+/*
+ * A non-zero return indicates read_only or fail_io mode.
+ * Many callers don't care about the return value.
+ */
+static int commit_or_fallback(struct pool *pool)
+{
+       int r;
+
+       if (get_pool_mode(pool) != PM_WRITE)
+               return -EINVAL;
+
+       r = commit(pool);
+       if (r)
+               set_pool_mode(pool, PM_READ_ONLY);
+
+       return r;
+}
+
 static int alloc_data_block(struct thin_c *tc, dm_block_t *result)
 {
        int r;
@@ -1121,12 +1225,7 @@ static int alloc_data_block(struct thin_c *tc, dm_block_t *result)
                         * Try to commit to see if that will free up some
                         * more space.
                         */
-                       r = dm_pool_commit_metadata(pool->pmd);
-                       if (r) {
-                               DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
-                                     __func__, r);
-                               return r;
-                       }
+                       (void) commit_or_fallback(pool);
 
                        r = dm_pool_get_free_block_count(pool->pmd, &free_blocks);
                        if (r)
@@ -1218,7 +1317,7 @@ static void process_discard(struct thin_c *tc, struct bio *bio)
                         */
                        m = get_next_mapping(pool);
                        m->tc = tc;
-                       m->pass_discard = (!lookup_result.shared) & pool->pf.discard_passdown;
+                       m->pass_discard = (!lookup_result.shared) && pool->pf.discard_passdown;
                        m->virt_block = block;
                        m->data_block = lookup_result.block;
                        m->cell = cell;
@@ -1234,15 +1333,10 @@ static void process_discard(struct thin_c *tc, struct bio *bio)
                        }
                } else {
                        /*
-                        * This path is hit if people are ignoring
-                        * limits->discard_granularity.  It ignores any
-                        * part of the discard that is in a subsequent
-                        * block.
+                        * The DM core makes sure that the discard doesn't span
+                        * a block boundary.  So we submit the discard of a
+                        * partial block appropriately.
                         */
-                       sector_t offset = bio->bi_sector - (block << pool->block_shift);
-                       unsigned remaining = (pool->sectors_per_block - offset) << 9;
-                       bio->bi_size = min(bio->bi_size, remaining);
-
                        cell_release_singleton(cell, bio);
                        cell_release_singleton(cell2, bio);
                        if ((!lookup_result.shared) && pool->pf.discard_passdown)
@@ -1310,7 +1404,7 @@ static void process_shared_bio(struct thin_c *tc, struct bio *bio,
        if (bio_detain(pool->prison, &key, bio, &cell))
                return;
 
-       if (bio_data_dir(bio) == WRITE)
+       if (bio_data_dir(bio) == WRITE && bio->bi_size)
                break_sharing(tc, bio, block, &key, lookup_result, cell);
        else {
                struct dm_thin_endio_hook *h = dm_get_mapinfo(bio)->ptr;
@@ -1362,6 +1456,7 @@ static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block
 
        default:
                DMERR("%s: alloc_data_block() failed, error = %d", __func__, r);
+               set_pool_mode(tc->pool, PM_READ_ONLY);
                cell_error(cell);
                break;
        }
@@ -1419,6 +1514,49 @@ static void process_bio(struct thin_c *tc, struct bio *bio)
        }
 }
 
+static void process_bio_read_only(struct thin_c *tc, struct bio *bio)
+{
+       int r;
+       int rw = bio_data_dir(bio);
+       dm_block_t block = get_bio_block(tc, bio);
+       struct dm_thin_lookup_result lookup_result;
+
+       r = dm_thin_find_block(tc->td, block, 1, &lookup_result);
+       switch (r) {
+       case 0:
+               if (lookup_result.shared && (rw == WRITE) && bio->bi_size)
+                       bio_io_error(bio);
+               else
+                       remap_and_issue(tc, bio, lookup_result.block);
+               break;
+
+       case -ENODATA:
+               if (rw != READ) {
+                       bio_io_error(bio);
+                       break;
+               }
+
+               if (tc->origin_dev) {
+                       remap_to_origin_and_issue(tc, bio);
+                       break;
+               }
+
+               zero_fill_bio(bio);
+               bio_endio(bio, 0);
+               break;
+
+       default:
+               DMERR("dm_thin_find_block() failed, error = %d", r);
+               bio_io_error(bio);
+               break;
+       }
+}
+
+static void process_bio_fail(struct thin_c *tc, struct bio *bio)
+{
+       bio_io_error(bio);
+}
+
 static int need_commit_due_to_time(struct pool *pool)
 {
        return jiffies < pool->last_commit_jiffies ||
@@ -1430,7 +1568,6 @@ static void process_deferred_bios(struct pool *pool)
        unsigned long flags;
        struct bio *bio;
        struct bio_list bios;
-       int r;
 
        bio_list_init(&bios);
 
@@ -1457,9 +1594,9 @@ static void process_deferred_bios(struct pool *pool)
                }
 
                if (bio->bi_rw & REQ_DISCARD)
-                       process_discard(tc, bio);
+                       pool->process_discard(tc, bio);
                else
-                       process_bio(tc, bio);
+                       pool->process_bio(tc, bio);
        }
 
        /*
@@ -1475,10 +1612,7 @@ static void process_deferred_bios(struct pool *pool)
        if (bio_list_empty(&bios) && !need_commit_due_to_time(pool))
                return;
 
-       r = dm_pool_commit_metadata(pool->pmd);
-       if (r) {
-               DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
-                     __func__, r);
+       if (commit_or_fallback(pool)) {
                while ((bio = bio_list_pop(&bios)))
                        bio_io_error(bio);
                return;
@@ -1493,8 +1627,8 @@ static void do_worker(struct work_struct *ws)
 {
        struct pool *pool = container_of(ws, struct pool, worker);
 
-       process_prepared(pool, &pool->prepared_mappings, process_prepared_mapping);
-       process_prepared(pool, &pool->prepared_discards, process_prepared_discard);
+       process_prepared(pool, &pool->prepared_mappings, &pool->process_prepared_mapping);
+       process_prepared(pool, &pool->prepared_discards, &pool->process_prepared_discard);
        process_deferred_bios(pool);
 }
 
@@ -1511,6 +1645,52 @@ static void do_waker(struct work_struct *ws)
 
 /*----------------------------------------------------------------*/
 
+static enum pool_mode get_pool_mode(struct pool *pool)
+{
+       return pool->pf.mode;
+}
+
+static void set_pool_mode(struct pool *pool, enum pool_mode mode)
+{
+       int r;
+
+       pool->pf.mode = mode;
+
+       switch (mode) {
+       case PM_FAIL:
+               DMERR("switching pool to failure mode");
+               pool->process_bio = process_bio_fail;
+               pool->process_discard = process_bio_fail;
+               pool->process_prepared_mapping = process_prepared_mapping_fail;
+               pool->process_prepared_discard = process_prepared_discard_fail;
+               break;
+
+       case PM_READ_ONLY:
+               DMERR("switching pool to read-only mode");
+               r = dm_pool_abort_metadata(pool->pmd);
+               if (r) {
+                       DMERR("aborting transaction failed");
+                       set_pool_mode(pool, PM_FAIL);
+               } else {
+                       dm_pool_metadata_read_only(pool->pmd);
+                       pool->process_bio = process_bio_read_only;
+                       pool->process_discard = process_discard;
+                       pool->process_prepared_mapping = process_prepared_mapping_fail;
+                       pool->process_prepared_discard = process_prepared_discard_passdown;
+               }
+               break;
+
+       case PM_WRITE:
+               pool->process_bio = process_bio;
+               pool->process_discard = process_discard;
+               pool->process_prepared_mapping = process_prepared_mapping;
+               pool->process_prepared_discard = process_prepared_discard;
+               break;
+       }
+}
+
+/*----------------------------------------------------------------*/
+
 /*
  * Mapping functions.
  */
@@ -1556,6 +1736,12 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio,
        struct dm_thin_lookup_result result;
 
        map_context->ptr = thin_hook_bio(tc, bio);
+
+       if (get_pool_mode(tc->pool) == PM_FAIL) {
+               bio_io_error(bio);
+               return DM_MAPIO_SUBMITTED;
+       }
+
        if (bio->bi_rw & (REQ_DISCARD | REQ_FLUSH | REQ_FUA)) {
                thin_defer_bio(tc, bio);
                return DM_MAPIO_SUBMITTED;
@@ -1592,14 +1778,35 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio,
                break;
 
        case -ENODATA:
+               if (get_pool_mode(tc->pool) == PM_READ_ONLY) {
+                       /*
+                        * This block isn't provisioned, and we have no way
+                        * of doing so.  Just error it.
+                        */
+                       bio_io_error(bio);
+                       r = DM_MAPIO_SUBMITTED;
+                       break;
+               }
+               /* fall through */
+
+       case -EWOULDBLOCK:
                /*
                 * In future, the failed dm_thin_find_block above could
                 * provide the hint to load the metadata into cache.
                 */
-       case -EWOULDBLOCK:
                thin_defer_bio(tc, bio);
                r = DM_MAPIO_SUBMITTED;
                break;
+
+       default:
+               /*
+                * Must always call bio_io_error on failure.
+                * dm_thin_find_block can fail with -EINVAL if the
+                * pool is switched to fail-io mode.
+                */
+               bio_io_error(bio);
+               r = DM_MAPIO_SUBMITTED;
+               break;
        }
 
        return r;
@@ -1636,15 +1843,26 @@ static int bind_control_target(struct pool *pool, struct dm_target *ti)
 {
        struct pool_c *pt = ti->private;
 
+       /*
+        * We want to make sure that degraded pools are never upgraded.
+        */
+       enum pool_mode old_mode = pool->pf.mode;
+       enum pool_mode new_mode = pt->pf.mode;
+
+       if (old_mode > new_mode)
+               new_mode = old_mode;
+
        pool->ti = ti;
        pool->low_water_blocks = pt->low_water_blocks;
        pool->pf = pt->pf;
+       set_pool_mode(pool, new_mode);
 
        /*
         * If discard_passdown was enabled verify that the data device
         * supports discards.  Disable discard_passdown if not; otherwise
         * -EOPNOTSUPP will be returned.
         */
+       /* FIXME: pull this out into a sep fn. */
        if (pt->pf.discard_passdown) {
                struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
                if (!q || !blk_queue_discard(q)) {
@@ -1670,6 +1888,7 @@ static void unbind_control_target(struct pool *pool, struct dm_target *ti)
 /* Initialize pool features. */
 static void pool_features_init(struct pool_features *pf)
 {
+       pf->mode = PM_WRITE;
        pf->zero_new_blocks = 1;
        pf->discard_enabled = 1;
        pf->discard_passdown = 1;
@@ -1700,14 +1919,16 @@ static struct kmem_cache *_endio_hook_cache;
 
 static struct pool *pool_create(struct mapped_device *pool_md,
                                struct block_device *metadata_dev,
-                               unsigned long block_size, char **error)
+                               unsigned long block_size,
+                               int read_only, char **error)
 {
        int r;
        void *err_p;
        struct pool *pool;
        struct dm_pool_metadata *pmd;
+       bool format_device = read_only ? false : true;
 
-       pmd = dm_pool_metadata_open(metadata_dev, block_size);
+       pmd = dm_pool_metadata_open(metadata_dev, block_size, format_device);
        if (IS_ERR(pmd)) {
                *error = "Error creating metadata object";
                return (struct pool *)pmd;
@@ -1722,8 +1943,10 @@ static struct pool *pool_create(struct mapped_device *pool_md,
 
        pool->pmd = pmd;
        pool->sectors_per_block = block_size;
-       pool->block_shift = ffs(block_size) - 1;
-       pool->offset_mask = block_size - 1;
+       if (block_size & (block_size - 1))
+               pool->sectors_per_block_shift = -1;
+       else
+               pool->sectors_per_block_shift = __ffs(block_size);
        pool->low_water_blocks = 0;
        pool_features_init(&pool->pf);
        pool->prison = prison_create(PRISON_CELLS);
@@ -1822,25 +2045,29 @@ static void __pool_dec(struct pool *pool)
 
 static struct pool *__pool_find(struct mapped_device *pool_md,
                                struct block_device *metadata_dev,
-                               unsigned long block_size, char **error,
-                               int *created)
+                               unsigned long block_size, int read_only,
+                               char **error, int *created)
 {
        struct pool *pool = __pool_table_lookup_metadata_dev(metadata_dev);
 
        if (pool) {
-               if (pool->pool_md != pool_md)
+               if (pool->pool_md != pool_md) {
+                       *error = "metadata device already in use by a pool";
                        return ERR_PTR(-EBUSY);
+               }
                __pool_inc(pool);
 
        } else {
                pool = __pool_table_lookup(pool_md);
                if (pool) {
-                       if (pool->md_dev != metadata_dev)
+                       if (pool->md_dev != metadata_dev) {
+                               *error = "different pool cannot replace a pool";
                                return ERR_PTR(-EINVAL);
+                       }
                        __pool_inc(pool);
 
                } else {
-                       pool = pool_create(pool_md, metadata_dev, block_size, error);
+                       pool = pool_create(pool_md, metadata_dev, block_size, read_only, error);
                        *created = 1;
                }
        }
@@ -1891,19 +2118,23 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
                arg_name = dm_shift_arg(as);
                argc--;
 
-               if (!strcasecmp(arg_name, "skip_block_zeroing")) {
+               if (!strcasecmp(arg_name, "skip_block_zeroing"))
                        pf->zero_new_blocks = 0;
-                       continue;
-               } else if (!strcasecmp(arg_name, "ignore_discard")) {
+
+               else if (!strcasecmp(arg_name, "ignore_discard"))
                        pf->discard_enabled = 0;
-                       continue;
-               } else if (!strcasecmp(arg_name, "no_discard_passdown")) {
+
+               else if (!strcasecmp(arg_name, "no_discard_passdown"))
                        pf->discard_passdown = 0;
-                       continue;
-               }
 
-               ti->error = "Unrecognised pool feature requested";
-               r = -EINVAL;
+               else if (!strcasecmp(arg_name, "read_only"))
+                       pf->mode = PM_READ_ONLY;
+
+               else {
+                       ti->error = "Unrecognised pool feature requested";
+                       r = -EINVAL;
+                       break;
+               }
        }
 
        return r;
@@ -1967,7 +2198,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
        if (kstrtoul(argv[2], 10, &block_size) || !block_size ||
            block_size < DATA_DEV_BLOCK_SIZE_MIN_SECTORS ||
            block_size > DATA_DEV_BLOCK_SIZE_MAX_SECTORS ||
-           !is_power_of_2(block_size)) {
+           block_size & (DATA_DEV_BLOCK_SIZE_MIN_SECTORS - 1)) {
                ti->error = "Invalid block size";
                r = -EINVAL;
                goto out;
@@ -1996,7 +2227,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
        }
 
        pool = __pool_find(dm_table_get_md(ti->table), metadata_dev->bdev,
-                          block_size, &ti->error, &pool_created);
+                          block_size, pf.mode == PM_READ_ONLY, &ti->error, &pool_created);
        if (IS_ERR(pool)) {
                r = PTR_ERR(pool);
                goto out_free_pt;
@@ -2014,6 +2245,15 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
                goto out_flags_changed;
        }
 
+       /*
+        * The block layer requires discard_granularity to be a power of 2.
+        */
+       if (pf.discard_enabled && !is_power_of_2(block_size)) {
+               ti->error = "Discard support must be disabled when the block size is not a power of 2";
+               r = -EINVAL;
+               goto out_flags_changed;
+       }
+
        pt->pool = pool;
        pt->ti = ti;
        pt->metadata_dev = metadata_dev;
@@ -2033,7 +2273,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
                 * stacking of discard limits (this keeps the pool and
                 * thin devices' discard limits consistent).
                 */
-               ti->discards_supported = 1;
+               ti->discards_supported = true;
        }
        ti->private = pt;
 
@@ -2093,7 +2333,8 @@ static int pool_preresume(struct dm_target *ti)
        int r;
        struct pool_c *pt = ti->private;
        struct pool *pool = pt->pool;
-       dm_block_t data_size, sb_data_size;
+       sector_t data_size = ti->len;
+       dm_block_t sb_data_size;
 
        /*
         * Take control of the pool object.
@@ -2102,7 +2343,8 @@ static int pool_preresume(struct dm_target *ti)
        if (r)
                return r;
 
-       data_size = ti->len >> pool->block_shift;
+       (void) sector_div(data_size, pool->sectors_per_block);
+
        r = dm_pool_get_data_dev_size(pool->pmd, &sb_data_size);
        if (r) {
                DMERR("failed to retrieve data device size");
@@ -2111,22 +2353,19 @@ static int pool_preresume(struct dm_target *ti)
 
        if (data_size < sb_data_size) {
                DMERR("pool target too small, is %llu blocks (expected %llu)",
-                     data_size, sb_data_size);
+                     (unsigned long long)data_size, sb_data_size);
                return -EINVAL;
 
        } else if (data_size > sb_data_size) {
                r = dm_pool_resize_data_dev(pool->pmd, data_size);
                if (r) {
                        DMERR("failed to resize data device");
+                       /* FIXME Stricter than necessary: Rollback transaction instead here */
+                       set_pool_mode(pool, PM_READ_ONLY);
                        return r;
                }
 
-               r = dm_pool_commit_metadata(pool->pmd);
-               if (r) {
-                       DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
-                             __func__, r);
-                       return r;
-               }
+               (void) commit_or_fallback(pool);
        }
 
        return 0;
@@ -2149,19 +2388,12 @@ static void pool_resume(struct dm_target *ti)
 
 static void pool_postsuspend(struct dm_target *ti)
 {
-       int r;
        struct pool_c *pt = ti->private;
        struct pool *pool = pt->pool;
 
        cancel_delayed_work(&pool->waker);
        flush_workqueue(pool->wq);
-
-       r = dm_pool_commit_metadata(pool->pmd);
-       if (r < 0) {
-               DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
-                     __func__, r);
-               /* FIXME: invalidate device? error the next FUA or FLUSH bio ?*/
-       }
+       (void) commit_or_fallback(pool);
 }
 
 static int check_arg_count(unsigned argc, unsigned args_required)
@@ -2295,12 +2527,7 @@ static int process_reserve_metadata_snap_mesg(unsigned argc, char **argv, struct
        if (r)
                return r;
 
-       r = dm_pool_commit_metadata(pool->pmd);
-       if (r) {
-               DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
-                     __func__, r);
-               return r;
-       }
+       (void) commit_or_fallback(pool);
 
        r = dm_pool_reserve_metadata_snap(pool->pmd);
        if (r)
@@ -2361,25 +2588,41 @@ static int pool_message(struct dm_target *ti, unsigned argc, char **argv)
        else
                DMWARN("Unrecognised thin pool target message received: %s", argv[0]);
 
-       if (!r) {
-               r = dm_pool_commit_metadata(pool->pmd);
-               if (r)
-                       DMERR("%s message: dm_pool_commit_metadata() failed, error = %d",
-                             argv[0], r);
-       }
+       if (!r)
+               (void) commit_or_fallback(pool);
 
        return r;
 }
 
+static void emit_flags(struct pool_features *pf, char *result,
+                      unsigned sz, unsigned maxlen)
+{
+       unsigned count = !pf->zero_new_blocks + !pf->discard_enabled +
+               !pf->discard_passdown + (pf->mode == PM_READ_ONLY);
+       DMEMIT("%u ", count);
+
+       if (!pf->zero_new_blocks)
+               DMEMIT("skip_block_zeroing ");
+
+       if (!pf->discard_enabled)
+               DMEMIT("ignore_discard ");
+
+       if (!pf->discard_passdown)
+               DMEMIT("no_discard_passdown ");
+
+       if (pf->mode == PM_READ_ONLY)
+               DMEMIT("read_only ");
+}
+
 /*
  * Status line is:
  *    <transaction id> <used metadata sectors>/<total metadata sectors>
  *    <used data sectors>/<total data sectors> <held metadata root>
  */
 static int pool_status(struct dm_target *ti, status_type_t type,
-                      char *result, unsigned maxlen)
+                      unsigned status_flags, char *result, unsigned maxlen)
 {
-       int r, count;
+       int r;
        unsigned sz = 0;
        uint64_t transaction_id;
        dm_block_t nr_free_blocks_data;
@@ -2394,6 +2637,15 @@ static int pool_status(struct dm_target *ti, status_type_t type,
 
        switch (type) {
        case STATUSTYPE_INFO:
+               if (get_pool_mode(pool) == PM_FAIL) {
+                       DMEMIT("Fail");
+                       break;
+               }
+
+               /* Commit to ensure statistics aren't out-of-date */
+               if (!(status_flags & DM_STATUS_NOFLUSH_FLAG) && !dm_suspended(ti))
+                       (void) commit_or_fallback(pool);
+
                r = dm_pool_get_metadata_transaction_id(pool->pmd,
                                                        &transaction_id);
                if (r)
@@ -2429,9 +2681,19 @@ static int pool_status(struct dm_target *ti, status_type_t type,
                       (unsigned long long)nr_blocks_data);
 
                if (held_root)
-                       DMEMIT("%llu", held_root);
+                       DMEMIT("%llu ", held_root);
+               else
+                       DMEMIT("- ");
+
+               if (pool->pf.mode == PM_READ_ONLY)
+                       DMEMIT("ro ");
+               else
+                       DMEMIT("rw ");
+
+               if (pool->pf.discard_enabled && pool->pf.discard_passdown)
+                       DMEMIT("discard_passdown");
                else
-                       DMEMIT("-");
+                       DMEMIT("no_discard_passdown");
 
                break;
 
@@ -2441,20 +2703,7 @@ static int pool_status(struct dm_target *ti, status_type_t type,
                       format_dev_t(buf2, pt->data_dev->bdev->bd_dev),
                       (unsigned long)pool->sectors_per_block,
                       (unsigned long long)pt->low_water_blocks);
-
-               count = !pool->pf.zero_new_blocks + !pool->pf.discard_enabled +
-                       !pt->pf.discard_passdown;
-               DMEMIT("%u ", count);
-
-               if (!pool->pf.zero_new_blocks)
-                       DMEMIT("skip_block_zeroing ");
-
-               if (!pool->pf.discard_enabled)
-                       DMEMIT("ignore_discard ");
-
-               if (!pt->pf.discard_passdown)
-                       DMEMIT("no_discard_passdown ");
-
+               emit_flags(&pt->pf, result, sz, maxlen);
                break;
        }
 
@@ -2492,7 +2741,8 @@ static void set_discard_limits(struct pool *pool, struct queue_limits *limits)
 
        /*
         * This is just a hint, and not enforced.  We have to cope with
-        * bios that overlap 2 blocks.
+        * bios that cover a block partially.  A discard that spans a block
+        * boundary is not sent to this target.
         */
        limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
        limits->discard_zeroes_data = pool->pf.zero_new_blocks;
@@ -2513,7 +2763,7 @@ static struct target_type pool_target = {
        .name = "thin-pool",
        .features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
                    DM_TARGET_IMMUTABLE,
-       .version = {1, 2, 0},
+       .version = {1, 3, 0},
        .module = THIS_MODULE,
        .ctr = pool_ctr,
        .dtr = pool_dtr,
@@ -2618,20 +2868,31 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
        }
        __pool_inc(tc->pool);
 
+       if (get_pool_mode(tc->pool) == PM_FAIL) {
+               ti->error = "Couldn't open thin device, Pool is in fail mode";
+               goto bad_thin_open;
+       }
+
        r = dm_pool_open_thin_device(tc->pool->pmd, tc->dev_id, &tc->td);
        if (r) {
                ti->error = "Couldn't open thin internal device";
                goto bad_thin_open;
        }
 
-       ti->split_io = tc->pool->sectors_per_block;
+       r = dm_set_target_max_io_len(ti, tc->pool->sectors_per_block);
+       if (r)
+               goto bad_thin_open;
+
        ti->num_flush_requests = 1;
+       ti->flush_supported = true;
 
        /* In case the pool supports discards, pass them on. */
        if (tc->pool->pf.discard_enabled) {
-               ti->discards_supported = 1;
+               ti->discards_supported = true;
                ti->num_discard_requests = 1;
-               ti->discard_zeroes_data_unsupported = 1;
+               ti->discard_zeroes_data_unsupported = true;
+               /* Discard requests must be split on a block boundary */
+               ti->split_discard_requests = true;
        }
 
        dm_put(pool_md);
@@ -2712,7 +2973,7 @@ static void thin_postsuspend(struct dm_target *ti)
  * <nr mapped sectors> <highest mapped sector>
  */
 static int thin_status(struct dm_target *ti, status_type_t type,
-                      char *result, unsigned maxlen)
+                      unsigned status_flags, char *result, unsigned maxlen)
 {
        int r;
        ssize_t sz = 0;
@@ -2720,6 +2981,11 @@ static int thin_status(struct dm_target *ti, status_type_t type,
        char buf[BDEVNAME_SIZE];
        struct thin_c *tc = ti->private;
 
+       if (get_pool_mode(tc->pool) == PM_FAIL) {
+               DMEMIT("Fail");
+               return 0;
+       }
+
        if (!tc->td)
                DMEMIT("-");
        else {
@@ -2757,19 +3023,21 @@ static int thin_status(struct dm_target *ti, status_type_t type,
 static int thin_iterate_devices(struct dm_target *ti,
                                iterate_devices_callout_fn fn, void *data)
 {
-       dm_block_t blocks;
+       sector_t blocks;
        struct thin_c *tc = ti->private;
+       struct pool *pool = tc->pool;
 
        /*
         * We can't call dm_pool_get_data_dev_size() since that blocks.  So
         * we follow a more convoluted path through to the pool's target.
         */
-       if (!tc->pool->ti)
+       if (!pool->ti)
                return 0;       /* nothing is bound */
 
-       blocks = tc->pool->ti->len >> tc->pool->block_shift;
+       blocks = pool->ti->len;
+       (void) sector_div(blocks, pool->sectors_per_block);
        if (blocks)
-               return fn(ti, tc->pool_dev, 0, tc->pool->sectors_per_block * blocks, data);
+               return fn(ti, tc->pool_dev, 0, pool->sectors_per_block * blocks, data);
 
        return 0;
 }
@@ -2786,7 +3054,7 @@ static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
 
 static struct target_type thin_target = {
        .name = "thin",
-       .version = {1, 1, 0},
+       .version = {1, 3, 0},
        .module = THIS_MODULE,
        .ctr = thin_ctr,
        .dtr = thin_dtr,
index fa365d39b61281b887d456ddbb8c9141352c8acf..254d19268ad2fa0c7102014c997b75294450ba42 100644 (file)
@@ -515,7 +515,7 @@ static int verity_map(struct dm_target *ti, struct bio *bio,
  * Status: V (valid) or C (corruption found)
  */
 static int verity_status(struct dm_target *ti, status_type_t type,
-                        char *result, unsigned maxlen)
+                        unsigned status_flags, char *result, unsigned maxlen)
 {
        struct dm_verity *v = ti->private;
        unsigned sz = 0;
index e24143cc2040b7f3f8f90a63c3b1b2cfe2b21c24..4e09b6ff5b493403d4e51be0b4fee2d99643e276 100644 (file)
@@ -968,22 +968,41 @@ static sector_t max_io_len_target_boundary(sector_t sector, struct dm_target *ti
 static sector_t max_io_len(sector_t sector, struct dm_target *ti)
 {
        sector_t len = max_io_len_target_boundary(sector, ti);
+       sector_t offset, max_len;
 
        /*
-        * Does the target need to split even further ?
+        * Does the target need to split even further?
         */
-       if (ti->split_io) {
-               sector_t boundary;
-               sector_t offset = dm_target_offset(ti, sector);
-               boundary = ((offset + ti->split_io) & ~(ti->split_io - 1))
-                          - offset;
-               if (len > boundary)
-                       len = boundary;
+       if (ti->max_io_len) {
+               offset = dm_target_offset(ti, sector);
+               if (unlikely(ti->max_io_len & (ti->max_io_len - 1)))
+                       max_len = sector_div(offset, ti->max_io_len);
+               else
+                       max_len = offset & (ti->max_io_len - 1);
+               max_len = ti->max_io_len - max_len;
+
+               if (len > max_len)
+                       len = max_len;
        }
 
        return len;
 }
 
+int dm_set_target_max_io_len(struct dm_target *ti, sector_t len)
+{
+       if (len > UINT_MAX) {
+               DMERR("Specified maximum size of target IO (%llu) exceeds limit (%u)",
+                     (unsigned long long)len, UINT_MAX);
+               ti->error = "Maximum size of target IO is too large";
+               return -EINVAL;
+       }
+
+       ti->max_io_len = (uint32_t) len;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(dm_set_target_max_io_len);
+
 static void __map_bio(struct dm_target *ti, struct bio *clone,
                      struct dm_target_io *tio)
 {
@@ -1196,7 +1215,10 @@ static int __clone_and_map_discard(struct clone_info *ci)
                if (!ti->num_discard_requests)
                        return -EOPNOTSUPP;
 
-               len = min(ci->sector_count, max_io_len_target_boundary(ci->sector, ti));
+               if (!ti->split_discard_requests)
+                       len = min(ci->sector_count, max_io_len_target_boundary(ci->sector, ti));
+               else
+                       len = min(ci->sector_count, max_io_len(ci->sector, ti));
 
                __issue_target_requests(ci, ti, ti->num_discard_requests, len);
 
index b7dacd59d8d7534fbc73584609b5644a20ed4d84..52eef493d2669290eaa3f2a22e46717dac9aa1eb 100644 (file)
 #define DM_SUSPEND_LOCKFS_FLAG         (1 << 0)
 #define DM_SUSPEND_NOFLUSH_FLAG                (1 << 1)
 
+/*
+ * Status feature flags
+ */
+#define DM_STATUS_NOFLUSH_FLAG         (1 << 0)
+
 /*
  * Type of table and mapped_device's mempool
  */
index d5ab4493c8be656ecd28227994d7f7bbe06b8e2d..fcd098794d37d16c7b48b12c54b5ba6f5e6ba60e 100644 (file)
@@ -498,61 +498,13 @@ void md_flush_request(struct mddev *mddev, struct bio *bio)
 }
 EXPORT_SYMBOL(md_flush_request);
 
-/* Support for plugging.
- * This mirrors the plugging support in request_queue, but does not
- * require having a whole queue or request structures.
- * We allocate an md_plug_cb for each md device and each thread it gets
- * plugged on.  This links tot the private plug_handle structure in the
- * personality data where we keep a count of the number of outstanding
- * plugs so other code can see if a plug is active.
- */
-struct md_plug_cb {
-       struct blk_plug_cb cb;
-       struct mddev *mddev;
-};
-
-static void plugger_unplug(struct blk_plug_cb *cb)
-{
-       struct md_plug_cb *mdcb = container_of(cb, struct md_plug_cb, cb);
-       if (atomic_dec_and_test(&mdcb->mddev->plug_cnt))
-               md_wakeup_thread(mdcb->mddev->thread);
-       kfree(mdcb);
-}
-
-/* Check that an unplug wakeup will come shortly.
- * If not, wakeup the md thread immediately
- */
-int mddev_check_plugged(struct mddev *mddev)
+void md_unplug(struct blk_plug_cb *cb, bool from_schedule)
 {
-       struct blk_plug *plug = current->plug;
-       struct md_plug_cb *mdcb;
-
-       if (!plug)
-               return 0;
-
-       list_for_each_entry(mdcb, &plug->cb_list, cb.list) {
-               if (mdcb->cb.callback == plugger_unplug &&
-                   mdcb->mddev == mddev) {
-                       /* Already on the list, move to top */
-                       if (mdcb != list_first_entry(&plug->cb_list,
-                                                   struct md_plug_cb,
-                                                   cb.list))
-                               list_move(&mdcb->cb.list, &plug->cb_list);
-                       return 1;
-               }
-       }
-       /* Not currently on the callback list */
-       mdcb = kmalloc(sizeof(*mdcb), GFP_ATOMIC);
-       if (!mdcb)
-               return 0;
-
-       mdcb->mddev = mddev;
-       mdcb->cb.callback = plugger_unplug;
-       atomic_inc(&mddev->plug_cnt);
-       list_add(&mdcb->cb.list, &plug->cb_list);
-       return 1;
+       struct mddev *mddev = cb->data;
+       md_wakeup_thread(mddev->thread);
+       kfree(cb);
 }
-EXPORT_SYMBOL_GPL(mddev_check_plugged);
+EXPORT_SYMBOL(md_unplug);
 
 static inline struct mddev *mddev_get(struct mddev *mddev)
 {
@@ -602,7 +554,6 @@ void mddev_init(struct mddev *mddev)
        atomic_set(&mddev->active, 1);
        atomic_set(&mddev->openers, 0);
        atomic_set(&mddev->active_io, 0);
-       atomic_set(&mddev->plug_cnt, 0);
        spin_lock_init(&mddev->write_lock);
        atomic_set(&mddev->flush_pending, 0);
        init_waitqueue_head(&mddev->sb_wait);
@@ -3942,17 +3893,13 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len)
                break;
        case clear:
                /* stopping an active array */
-               if (atomic_read(&mddev->openers) > 0)
-                       return -EBUSY;
                err = do_md_stop(mddev, 0, NULL);
                break;
        case inactive:
                /* stopping an active array */
-               if (mddev->pers) {
-                       if (atomic_read(&mddev->openers) > 0)
-                               return -EBUSY;
+               if (mddev->pers)
                        err = do_md_stop(mddev, 2, NULL);
-               else
+               else
                        err = 0; /* already inactive */
                break;
        case suspended:
index 7b4a3c318cae437ecb2041c33b2bdcee2c66bf15..f385b038589d32313014e07af203b8f742bec4a7 100644 (file)
@@ -266,9 +266,6 @@ struct mddev {
        int                             new_chunk_sectors;
        int                             reshape_backwards;
 
-       atomic_t                        plug_cnt;       /* If device is expecting
-                                                        * more bios soon.
-                                                        */
        struct md_thread                *thread;        /* management thread */
        struct md_thread                *sync_thread;   /* doing resync or reconstruct */
        sector_t                        curr_resync;    /* last block scheduled */
@@ -630,6 +627,12 @@ extern struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask,
                                   struct mddev *mddev);
 extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs,
                                   struct mddev *mddev);
-extern int mddev_check_plugged(struct mddev *mddev);
 extern void md_trim_bio(struct bio *bio, int offset, int size);
+
+extern void md_unplug(struct blk_plug_cb *cb, bool from_schedule);
+static inline int mddev_check_plugged(struct mddev *mddev)
+{
+       return !!blk_check_plugged(md_unplug, mddev,
+                                  sizeof(struct blk_plug_cb));
+}
 #endif /* _MD_MD_H */
index cfa95f6622300ccbe7fcb953bbf2e811f171d527..d8e7cb767c1e608dc82d89b42ffa63fe445b8164 100644 (file)
@@ -1,7 +1,6 @@
 obj-$(CONFIG_DM_PERSISTENT_DATA) += dm-persistent-data.o
 dm-persistent-data-objs := \
        dm-block-manager.o \
-       dm-space-map-checker.o \
        dm-space-map-common.o \
        dm-space-map-disk.o \
        dm-space-map-metadata.o \
index 0317ecdc6e5399aff5e6430acc796c37dc025568..5ba277768d9914c3dd6364f3d990feef55b67c92 100644 (file)
@@ -325,11 +325,6 @@ static struct dm_buffer *to_buffer(struct dm_block *b)
        return (struct dm_buffer *) b;
 }
 
-static struct dm_bufio_client *to_bufio(struct dm_block_manager *bm)
-{
-       return (struct dm_bufio_client *) bm;
-}
-
 dm_block_t dm_block_location(struct dm_block *b)
 {
        return dm_bufio_get_block_number(to_buffer(b));
@@ -367,34 +362,60 @@ static void dm_block_manager_write_callback(struct dm_buffer *buf)
 /*----------------------------------------------------------------
  * Public interface
  *--------------------------------------------------------------*/
+struct dm_block_manager {
+       struct dm_bufio_client *bufio;
+       bool read_only:1;
+};
+
 struct dm_block_manager *dm_block_manager_create(struct block_device *bdev,
                                                 unsigned block_size,
                                                 unsigned cache_size,
                                                 unsigned max_held_per_thread)
 {
-       return (struct dm_block_manager *)
-               dm_bufio_client_create(bdev, block_size, max_held_per_thread,
-                                      sizeof(struct buffer_aux),
-                                      dm_block_manager_alloc_callback,
-                                      dm_block_manager_write_callback);
+       int r;
+       struct dm_block_manager *bm;
+
+       bm = kmalloc(sizeof(*bm), GFP_KERNEL);
+       if (!bm) {
+               r = -ENOMEM;
+               goto bad;
+       }
+
+       bm->bufio = dm_bufio_client_create(bdev, block_size, max_held_per_thread,
+                                          sizeof(struct buffer_aux),
+                                          dm_block_manager_alloc_callback,
+                                          dm_block_manager_write_callback);
+       if (IS_ERR(bm->bufio)) {
+               r = PTR_ERR(bm->bufio);
+               kfree(bm);
+               goto bad;
+       }
+
+       bm->read_only = false;
+
+       return bm;
+
+bad:
+       return ERR_PTR(r);
 }
 EXPORT_SYMBOL_GPL(dm_block_manager_create);
 
 void dm_block_manager_destroy(struct dm_block_manager *bm)
 {
-       return dm_bufio_client_destroy(to_bufio(bm));
+       dm_bufio_client_destroy(bm->bufio);
+       kfree(bm);
 }
 EXPORT_SYMBOL_GPL(dm_block_manager_destroy);
 
 unsigned dm_bm_block_size(struct dm_block_manager *bm)
 {
-       return dm_bufio_get_block_size(to_bufio(bm));
+       return dm_bufio_get_block_size(bm->bufio);
 }
 EXPORT_SYMBOL_GPL(dm_bm_block_size);
 
 dm_block_t dm_bm_nr_blocks(struct dm_block_manager *bm)
 {
-       return dm_bufio_get_device_size(to_bufio(bm));
+       return dm_bufio_get_device_size(bm->bufio);
 }
 
 static int dm_bm_validate_buffer(struct dm_block_manager *bm,
@@ -406,7 +427,7 @@ static int dm_bm_validate_buffer(struct dm_block_manager *bm,
                int r;
                if (!v)
                        return 0;
-               r = v->check(v, (struct dm_block *) buf, dm_bufio_get_block_size(to_bufio(bm)));
+               r = v->check(v, (struct dm_block *) buf, dm_bufio_get_block_size(bm->bufio));
                if (unlikely(r))
                        return r;
                aux->validator = v;
@@ -430,7 +451,7 @@ int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
        void *p;
        int r;
 
-       p = dm_bufio_read(to_bufio(bm), b, (struct dm_buffer **) result);
+       p = dm_bufio_read(bm->bufio, b, (struct dm_buffer **) result);
        if (unlikely(IS_ERR(p)))
                return PTR_ERR(p);
 
@@ -463,7 +484,10 @@ int dm_bm_write_lock(struct dm_block_manager *bm,
        void *p;
        int r;
 
-       p = dm_bufio_read(to_bufio(bm), b, (struct dm_buffer **) result);
+       if (bm->read_only)
+               return -EPERM;
+
+       p = dm_bufio_read(bm->bufio, b, (struct dm_buffer **) result);
        if (unlikely(IS_ERR(p)))
                return PTR_ERR(p);
 
@@ -496,7 +520,7 @@ int dm_bm_read_try_lock(struct dm_block_manager *bm,
        void *p;
        int r;
 
-       p = dm_bufio_get(to_bufio(bm), b, (struct dm_buffer **) result);
+       p = dm_bufio_get(bm->bufio, b, (struct dm_buffer **) result);
        if (unlikely(IS_ERR(p)))
                return PTR_ERR(p);
        if (unlikely(!p))
@@ -529,7 +553,10 @@ int dm_bm_write_lock_zero(struct dm_block_manager *bm,
        struct buffer_aux *aux;
        void *p;
 
-       p = dm_bufio_new(to_bufio(bm), b, (struct dm_buffer **) result);
+       if (bm->read_only)
+               return -EPERM;
+
+       p = dm_bufio_new(bm->bufio, b, (struct dm_buffer **) result);
        if (unlikely(IS_ERR(p)))
                return PTR_ERR(p);
 
@@ -547,6 +574,7 @@ int dm_bm_write_lock_zero(struct dm_block_manager *bm,
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(dm_bm_write_lock_zero);
 
 int dm_bm_unlock(struct dm_block *b)
 {
@@ -565,45 +593,30 @@ int dm_bm_unlock(struct dm_block *b)
 }
 EXPORT_SYMBOL_GPL(dm_bm_unlock);
 
-int dm_bm_unlock_move(struct dm_block *b, dm_block_t n)
-{
-       struct buffer_aux *aux;
-
-       aux = dm_bufio_get_aux_data(to_buffer(b));
-
-       if (aux->write_locked) {
-               dm_bufio_mark_buffer_dirty(to_buffer(b));
-               bl_up_write(&aux->lock);
-       } else
-               bl_up_read(&aux->lock);
-
-       dm_bufio_release_move(to_buffer(b), n);
-       return 0;
-}
-
 int dm_bm_flush_and_unlock(struct dm_block_manager *bm,
                           struct dm_block *superblock)
 {
        int r;
 
-       r = dm_bufio_write_dirty_buffers(to_bufio(bm));
-       if (unlikely(r))
-               return r;
-       r = dm_bufio_issue_flush(to_bufio(bm));
-       if (unlikely(r))
+       if (bm->read_only)
+               return -EPERM;
+
+       r = dm_bufio_write_dirty_buffers(bm->bufio);
+       if (unlikely(r)) {
+               dm_bm_unlock(superblock);
                return r;
+       }
 
        dm_bm_unlock(superblock);
 
-       r = dm_bufio_write_dirty_buffers(to_bufio(bm));
-       if (unlikely(r))
-               return r;
-       r = dm_bufio_issue_flush(to_bufio(bm));
-       if (unlikely(r))
-               return r;
+       return dm_bufio_write_dirty_buffers(bm->bufio);
+}
 
-       return 0;
+void dm_bm_set_read_only(struct dm_block_manager *bm)
+{
+       bm->read_only = true;
 }
+EXPORT_SYMBOL_GPL(dm_bm_set_read_only);
 
 u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor)
 {
index 924833d2dfa69f13944628eb220733a5500cd807..be5bff61be280562932906b1b182ef1d3775ea55 100644 (file)
@@ -96,14 +96,6 @@ int dm_bm_write_lock_zero(struct dm_block_manager *bm, dm_block_t b,
 
 int dm_bm_unlock(struct dm_block *b);
 
-/*
- * An optimisation; we often want to copy a block's contents to a new
- * block.  eg, as part of the shadowing operation.  It's far better for
- * bufio to do this move behind the scenes than hold 2 locks and memcpy the
- * data.
- */
-int dm_bm_unlock_move(struct dm_block *b, dm_block_t n);
-
 /*
  * It's a common idiom to have a superblock that should be committed last.
  *
@@ -116,6 +108,19 @@ int dm_bm_unlock_move(struct dm_block *b, dm_block_t n);
 int dm_bm_flush_and_unlock(struct dm_block_manager *bm,
                           struct dm_block *superblock);
 
+/*
+ * Switches the bm to a read only mode.  Once read-only mode
+ * has been entered the following functions will return -EPERM.
+ *
+ *   dm_bm_write_lock
+ *   dm_bm_write_lock_zero
+ *   dm_bm_flush_and_unlock
+ *
+ * Additionally you should not use dm_bm_unlock_move, however no error will
+ * be returned if you do.
+ */
+void dm_bm_set_read_only(struct dm_block_manager *bm);
+
 u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor);
 
 /*----------------------------------------------------------------*/
diff --git a/drivers/md/persistent-data/dm-space-map-checker.c b/drivers/md/persistent-data/dm-space-map-checker.c
deleted file mode 100644 (file)
index fc90c11..0000000
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This file is released under the GPL.
- */
-
-#include "dm-space-map-checker.h"
-
-#include <linux/device-mapper.h>
-#include <linux/export.h>
-#include <linux/vmalloc.h>
-
-#ifdef CONFIG_DM_DEBUG_SPACE_MAPS
-
-#define DM_MSG_PREFIX "space map checker"
-
-/*----------------------------------------------------------------*/
-
-struct count_array {
-       dm_block_t nr;
-       dm_block_t nr_free;
-
-       uint32_t *counts;
-};
-
-static int ca_get_count(struct count_array *ca, dm_block_t b, uint32_t *count)
-{
-       if (b >= ca->nr)
-               return -EINVAL;
-
-       *count = ca->counts[b];
-       return 0;
-}
-
-static int ca_count_more_than_one(struct count_array *ca, dm_block_t b, int *r)
-{
-       if (b >= ca->nr)
-               return -EINVAL;
-
-       *r = ca->counts[b] > 1;
-       return 0;
-}
-
-static int ca_set_count(struct count_array *ca, dm_block_t b, uint32_t count)
-{
-       uint32_t old_count;
-
-       if (b >= ca->nr)
-               return -EINVAL;
-
-       old_count = ca->counts[b];
-
-       if (!count && old_count)
-               ca->nr_free++;
-
-       else if (count && !old_count)
-               ca->nr_free--;
-
-       ca->counts[b] = count;
-       return 0;
-}
-
-static int ca_inc_block(struct count_array *ca, dm_block_t b)
-{
-       if (b >= ca->nr)
-               return -EINVAL;
-
-       ca_set_count(ca, b, ca->counts[b] + 1);
-       return 0;
-}
-
-static int ca_dec_block(struct count_array *ca, dm_block_t b)
-{
-       if (b >= ca->nr)
-               return -EINVAL;
-
-       BUG_ON(ca->counts[b] == 0);
-       ca_set_count(ca, b, ca->counts[b] - 1);
-       return 0;
-}
-
-static int ca_create(struct count_array *ca, struct dm_space_map *sm)
-{
-       int r;
-       dm_block_t nr_blocks;
-
-       r = dm_sm_get_nr_blocks(sm, &nr_blocks);
-       if (r)
-               return r;
-
-       ca->nr = nr_blocks;
-       ca->nr_free = nr_blocks;
-
-       if (!nr_blocks)
-               ca->counts = NULL;
-       else {
-               ca->counts = vzalloc(sizeof(*ca->counts) * nr_blocks);
-               if (!ca->counts)
-                       return -ENOMEM;
-       }
-
-       return 0;
-}
-
-static void ca_destroy(struct count_array *ca)
-{
-       vfree(ca->counts);
-}
-
-static int ca_load(struct count_array *ca, struct dm_space_map *sm)
-{
-       int r;
-       uint32_t count;
-       dm_block_t nr_blocks, i;
-
-       r = dm_sm_get_nr_blocks(sm, &nr_blocks);
-       if (r)
-               return r;
-
-       BUG_ON(ca->nr != nr_blocks);
-
-       DMWARN("Loading debug space map from disk.  This may take some time");
-       for (i = 0; i < nr_blocks; i++) {
-               r = dm_sm_get_count(sm, i, &count);
-               if (r) {
-                       DMERR("load failed");
-                       return r;
-               }
-
-               ca_set_count(ca, i, count);
-       }
-       DMWARN("Load complete");
-
-       return 0;
-}
-
-static int ca_extend(struct count_array *ca, dm_block_t extra_blocks)
-{
-       dm_block_t nr_blocks = ca->nr + extra_blocks;
-       uint32_t *counts = vzalloc(sizeof(*counts) * nr_blocks);
-       if (!counts)
-               return -ENOMEM;
-
-       if (ca->counts) {
-               memcpy(counts, ca->counts, sizeof(*counts) * ca->nr);
-               ca_destroy(ca);
-       }
-       ca->nr = nr_blocks;
-       ca->nr_free += extra_blocks;
-       ca->counts = counts;
-       return 0;
-}
-
-static int ca_commit(struct count_array *old, struct count_array *new)
-{
-       if (old->nr != new->nr) {
-               BUG_ON(old->nr > new->nr);
-               ca_extend(old, new->nr - old->nr);
-       }
-
-       BUG_ON(old->nr != new->nr);
-       old->nr_free = new->nr_free;
-       memcpy(old->counts, new->counts, sizeof(*old->counts) * old->nr);
-       return 0;
-}
-
-/*----------------------------------------------------------------*/
-
-struct sm_checker {
-       struct dm_space_map sm;
-
-       struct count_array old_counts;
-       struct count_array counts;
-
-       struct dm_space_map *real_sm;
-};
-
-static void sm_checker_destroy(struct dm_space_map *sm)
-{
-       struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
-
-       dm_sm_destroy(smc->real_sm);
-       ca_destroy(&smc->old_counts);
-       ca_destroy(&smc->counts);
-       kfree(smc);
-}
-
-static int sm_checker_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
-{
-       struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
-       int r = dm_sm_get_nr_blocks(smc->real_sm, count);
-       if (!r)
-               BUG_ON(smc->old_counts.nr != *count);
-       return r;
-}
-
-static int sm_checker_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
-{
-       struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
-       int r = dm_sm_get_nr_free(smc->real_sm, count);
-       if (!r) {
-               /*
-                * Slow, but we know it's correct.
-                */
-               dm_block_t b, n = 0;
-               for (b = 0; b < smc->old_counts.nr; b++)
-                       if (smc->old_counts.counts[b] == 0 &&
-                           smc->counts.counts[b] == 0)
-                               n++;
-
-               if (n != *count)
-                       DMERR("free block counts differ, checker %u, sm-disk:%u",
-                             (unsigned) n, (unsigned) *count);
-       }
-       return r;
-}
-
-static int sm_checker_new_block(struct dm_space_map *sm, dm_block_t *b)
-{
-       struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
-       int r = dm_sm_new_block(smc->real_sm, b);
-
-       if (!r) {
-               BUG_ON(*b >= smc->old_counts.nr);
-               BUG_ON(smc->old_counts.counts[*b] != 0);
-               BUG_ON(*b >= smc->counts.nr);
-               BUG_ON(smc->counts.counts[*b] != 0);
-               ca_set_count(&smc->counts, *b, 1);
-       }
-
-       return r;
-}
-
-static int sm_checker_inc_block(struct dm_space_map *sm, dm_block_t b)
-{
-       struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
-       int r = dm_sm_inc_block(smc->real_sm, b);
-       int r2 = ca_inc_block(&smc->counts, b);
-       BUG_ON(r != r2);
-       return r;
-}
-
-static int sm_checker_dec_block(struct dm_space_map *sm, dm_block_t b)
-{
-       struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
-       int r = dm_sm_dec_block(smc->real_sm, b);
-       int r2 = ca_dec_block(&smc->counts, b);
-       BUG_ON(r != r2);
-       return r;
-}
-
-static int sm_checker_get_count(struct dm_space_map *sm, dm_block_t b, uint32_t *result)
-{
-       struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
-       uint32_t result2 = 0;
-       int r = dm_sm_get_count(smc->real_sm, b, result);
-       int r2 = ca_get_count(&smc->counts, b, &result2);
-
-       BUG_ON(r != r2);
-       if (!r)
-               BUG_ON(*result != result2);
-       return r;
-}
-
-static int sm_checker_count_more_than_one(struct dm_space_map *sm, dm_block_t b, int *result)
-{
-       struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
-       int result2 = 0;
-       int r = dm_sm_count_is_more_than_one(smc->real_sm, b, result);
-       int r2 = ca_count_more_than_one(&smc->counts, b, &result2);
-
-       BUG_ON(r != r2);
-       if (!r)
-               BUG_ON(!(*result) && result2);
-       return r;
-}
-
-static int sm_checker_set_count(struct dm_space_map *sm, dm_block_t b, uint32_t count)
-{
-       struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
-       uint32_t old_rc;
-       int r = dm_sm_set_count(smc->real_sm, b, count);
-       int r2;
-
-       BUG_ON(b >= smc->counts.nr);
-       old_rc = smc->counts.counts[b];
-       r2 = ca_set_count(&smc->counts, b, count);
-       BUG_ON(r != r2);
-
-       return r;
-}
-
-static int sm_checker_commit(struct dm_space_map *sm)
-{
-       struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
-       int r;
-
-       r = dm_sm_commit(smc->real_sm);
-       if (r)
-               return r;
-
-       r = ca_commit(&smc->old_counts, &smc->counts);
-       if (r)
-               return r;
-
-       return 0;
-}
-
-static int sm_checker_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
-{
-       struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
-       int r = dm_sm_extend(smc->real_sm, extra_blocks);
-       if (r)
-               return r;
-
-       return ca_extend(&smc->counts, extra_blocks);
-}
-
-static int sm_checker_root_size(struct dm_space_map *sm, size_t *result)
-{
-       struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
-       return dm_sm_root_size(smc->real_sm, result);
-}
-
-static int sm_checker_copy_root(struct dm_space_map *sm, void *copy_to_here_le, size_t len)
-{
-       struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
-       return dm_sm_copy_root(smc->real_sm, copy_to_here_le, len);
-}
-
-/*----------------------------------------------------------------*/
-
-static struct dm_space_map ops_ = {
-       .destroy = sm_checker_destroy,
-       .get_nr_blocks = sm_checker_get_nr_blocks,
-       .get_nr_free = sm_checker_get_nr_free,
-       .inc_block = sm_checker_inc_block,
-       .dec_block = sm_checker_dec_block,
-       .new_block = sm_checker_new_block,
-       .get_count = sm_checker_get_count,
-       .count_is_more_than_one = sm_checker_count_more_than_one,
-       .set_count = sm_checker_set_count,
-       .commit = sm_checker_commit,
-       .extend = sm_checker_extend,
-       .root_size = sm_checker_root_size,
-       .copy_root = sm_checker_copy_root
-};
-
-struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm)
-{
-       int r;
-       struct sm_checker *smc;
-
-       if (IS_ERR_OR_NULL(sm))
-               return ERR_PTR(-EINVAL);
-
-       smc = kmalloc(sizeof(*smc), GFP_KERNEL);
-       if (!smc)
-               return ERR_PTR(-ENOMEM);
-
-       memcpy(&smc->sm, &ops_, sizeof(smc->sm));
-       r = ca_create(&smc->old_counts, sm);
-       if (r) {
-               kfree(smc);
-               return ERR_PTR(r);
-       }
-
-       r = ca_create(&smc->counts, sm);
-       if (r) {
-               ca_destroy(&smc->old_counts);
-               kfree(smc);
-               return ERR_PTR(r);
-       }
-
-       smc->real_sm = sm;
-
-       r = ca_load(&smc->counts, sm);
-       if (r) {
-               ca_destroy(&smc->counts);
-               ca_destroy(&smc->old_counts);
-               kfree(smc);
-               return ERR_PTR(r);
-       }
-
-       r = ca_commit(&smc->old_counts, &smc->counts);
-       if (r) {
-               ca_destroy(&smc->counts);
-               ca_destroy(&smc->old_counts);
-               kfree(smc);
-               return ERR_PTR(r);
-       }
-
-       return &smc->sm;
-}
-EXPORT_SYMBOL_GPL(dm_sm_checker_create);
-
-struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm)
-{
-       int r;
-       struct sm_checker *smc;
-
-       if (IS_ERR_OR_NULL(sm))
-               return ERR_PTR(-EINVAL);
-
-       smc = kmalloc(sizeof(*smc), GFP_KERNEL);
-       if (!smc)
-               return ERR_PTR(-ENOMEM);
-
-       memcpy(&smc->sm, &ops_, sizeof(smc->sm));
-       r = ca_create(&smc->old_counts, sm);
-       if (r) {
-               kfree(smc);
-               return ERR_PTR(r);
-       }
-
-       r = ca_create(&smc->counts, sm);
-       if (r) {
-               ca_destroy(&smc->old_counts);
-               kfree(smc);
-               return ERR_PTR(r);
-       }
-
-       smc->real_sm = sm;
-       return &smc->sm;
-}
-EXPORT_SYMBOL_GPL(dm_sm_checker_create_fresh);
-
-/*----------------------------------------------------------------*/
-
-#else
-
-struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm)
-{
-       return sm;
-}
-EXPORT_SYMBOL_GPL(dm_sm_checker_create);
-
-struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm)
-{
-       return sm;
-}
-EXPORT_SYMBOL_GPL(dm_sm_checker_create_fresh);
-
-/*----------------------------------------------------------------*/
-
-#endif
diff --git a/drivers/md/persistent-data/dm-space-map-checker.h b/drivers/md/persistent-data/dm-space-map-checker.h
deleted file mode 100644 (file)
index 444dccf..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This file is released under the GPL.
- */
-
-#ifndef SNAPSHOTS_SPACE_MAP_CHECKER_H
-#define SNAPSHOTS_SPACE_MAP_CHECKER_H
-
-#include "dm-space-map.h"
-
-/*----------------------------------------------------------------*/
-
-/*
- * This space map wraps a real on-disk space map, and verifies all of its
- * operations.  It uses a lot of memory, so only use if you have a specific
- * problem that you're debugging.
- *
- * Ownership of @sm passes.
- */
-struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm);
-struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm);
-
-/*----------------------------------------------------------------*/
-
-#endif
index ff3beed6ad2d93769e475539ecd60d29ffdfaf22..d77602d63c83f8b98e341dd07c1dd50c397986e4 100644 (file)
@@ -224,6 +224,7 @@ static int sm_ll_init(struct ll_disk *ll, struct dm_transaction_manager *tm)
        ll->nr_blocks = 0;
        ll->bitmap_root = 0;
        ll->ref_count_root = 0;
+       ll->bitmap_index_changed = false;
 
        return 0;
 }
@@ -476,7 +477,15 @@ int sm_ll_dec(struct ll_disk *ll, dm_block_t b, enum allocation_event *ev)
 
 int sm_ll_commit(struct ll_disk *ll)
 {
-       return ll->commit(ll);
+       int r = 0;
+
+       if (ll->bitmap_index_changed) {
+               r = ll->commit(ll);
+               if (!r)
+                       ll->bitmap_index_changed = false;
+       }
+
+       return r;
 }
 
 /*----------------------------------------------------------------*/
@@ -491,6 +500,7 @@ static int metadata_ll_load_ie(struct ll_disk *ll, dm_block_t index,
 static int metadata_ll_save_ie(struct ll_disk *ll, dm_block_t index,
                               struct disk_index_entry *ie)
 {
+       ll->bitmap_index_changed = true;
        memcpy(ll->mi_le.index + index, ie, sizeof(*ie));
        return 0;
 }
index 8f220821a9a9a336a175c1f748f20399abf7a14b..b3078d5eda0c335c2986b84128d3e5bde01e8b8e 100644 (file)
@@ -78,6 +78,7 @@ struct ll_disk {
        open_index_fn open_index;
        max_index_entries_fn max_entries;
        commit_fn commit;
+       bool bitmap_index_changed:1;
 };
 
 struct disk_sm_root {
index 3d0ed53328831627ab6ec79cb85946f36b0fe7a4..f6d29e614ab728c521b0ebc1f8a7f320e5099deb 100644 (file)
@@ -4,7 +4,6 @@
  * This file is released under the GPL.
  */
 
-#include "dm-space-map-checker.h"
 #include "dm-space-map-common.h"
 #include "dm-space-map-disk.h"
 #include "dm-space-map.h"
@@ -252,9 +251,8 @@ static struct dm_space_map ops = {
        .copy_root = sm_disk_copy_root
 };
 
-static struct dm_space_map *dm_sm_disk_create_real(
-       struct dm_transaction_manager *tm,
-       dm_block_t nr_blocks)
+struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm,
+                                      dm_block_t nr_blocks)
 {
        int r;
        struct sm_disk *smd;
@@ -285,27 +283,10 @@ bad:
        kfree(smd);
        return ERR_PTR(r);
 }
-
-struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm,
-                                      dm_block_t nr_blocks)
-{
-       struct dm_space_map *sm = dm_sm_disk_create_real(tm, nr_blocks);
-       struct dm_space_map *smc;
-
-       if (IS_ERR_OR_NULL(sm))
-               return sm;
-
-       smc = dm_sm_checker_create_fresh(sm);
-       if (IS_ERR(smc))
-               dm_sm_destroy(sm);
-
-       return smc;
-}
 EXPORT_SYMBOL_GPL(dm_sm_disk_create);
 
-static struct dm_space_map *dm_sm_disk_open_real(
-       struct dm_transaction_manager *tm,
-       void *root_le, size_t len)
+struct dm_space_map *dm_sm_disk_open(struct dm_transaction_manager *tm,
+                                    void *root_le, size_t len)
 {
        int r;
        struct sm_disk *smd;
@@ -332,13 +313,6 @@ bad:
        kfree(smd);
        return ERR_PTR(r);
 }
-
-struct dm_space_map *dm_sm_disk_open(struct dm_transaction_manager *tm,
-                                    void *root_le, size_t len)
-{
-       return dm_sm_checker_create(
-               dm_sm_disk_open_real(tm, root_le, len));
-}
 EXPORT_SYMBOL_GPL(dm_sm_disk_open);
 
 /*----------------------------------------------------------------*/
index e5604b32d91ff0017a5a625cfd8106fca92bfe19..d247a35da3c63030bca417b89482bb8fdf371cd0 100644 (file)
@@ -5,7 +5,6 @@
  */
 #include "dm-transaction-manager.h"
 #include "dm-space-map.h"
-#include "dm-space-map-checker.h"
 #include "dm-space-map-disk.h"
 #include "dm-space-map-metadata.h"
 #include "dm-persistent-data-internal.h"
@@ -220,13 +219,24 @@ static int __shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
        if (r < 0)
                return r;
 
-       r = dm_bm_unlock_move(orig_block, new);
-       if (r < 0) {
+       /*
+        * It would be tempting to use dm_bm_unlock_move here, but some
+        * code, such as the space maps, keeps using the old data structures
+        * secure in the knowledge they won't be changed until the next
+        * transaction.  Using unlock_move would force a synchronous read
+        * since the old block would no longer be in the cache.
+        */
+       r = dm_bm_write_lock_zero(tm->bm, new, v, result);
+       if (r) {
                dm_bm_unlock(orig_block);
                return r;
        }
 
-       return dm_bm_write_lock(tm->bm, new, v, result);
+       memcpy(dm_block_data(*result), dm_block_data(orig_block),
+              dm_bm_block_size(tm->bm));
+
+       dm_bm_unlock(orig_block);
+       return r;
 }
 
 int dm_tm_shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
@@ -311,98 +321,61 @@ struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm)
 
 static int dm_tm_create_internal(struct dm_block_manager *bm,
                                 dm_block_t sb_location,
-                                struct dm_block_validator *sb_validator,
-                                size_t root_offset, size_t root_max_len,
                                 struct dm_transaction_manager **tm,
                                 struct dm_space_map **sm,
-                                struct dm_block **sblock,
-                                int create)
+                                int create,
+                                void *sm_root, size_t sm_len)
 {
        int r;
-       struct dm_space_map *inner;
 
-       inner = dm_sm_metadata_init();
-       if (IS_ERR(inner))
-               return PTR_ERR(inner);
+       *sm = dm_sm_metadata_init();
+       if (IS_ERR(*sm))
+               return PTR_ERR(*sm);
 
-       *tm = dm_tm_create(bm, inner);
+       *tm = dm_tm_create(bm, *sm);
        if (IS_ERR(*tm)) {
-               dm_sm_destroy(inner);
+               dm_sm_destroy(*sm);
                return PTR_ERR(*tm);
        }
 
        if (create) {
-               r = dm_bm_write_lock_zero(dm_tm_get_bm(*tm), sb_location,
-                                         sb_validator, sblock);
-               if (r < 0) {
-                       DMERR("couldn't lock superblock");
-                       goto bad1;
-               }
-
-               r = dm_sm_metadata_create(inner, *tm, dm_bm_nr_blocks(bm),
+               r = dm_sm_metadata_create(*sm, *tm, dm_bm_nr_blocks(bm),
                                          sb_location);
                if (r) {
                        DMERR("couldn't create metadata space map");
-                       goto bad2;
-               }
-
-               *sm = dm_sm_checker_create(inner);
-               if (IS_ERR(*sm)) {
-                       r = PTR_ERR(*sm);
-                       goto bad2;
+                       goto bad;
                }
 
        } else {
-               r = dm_bm_write_lock(dm_tm_get_bm(*tm), sb_location,
-                                    sb_validator, sblock);
-               if (r < 0) {
-                       DMERR("couldn't lock superblock");
-                       goto bad1;
-               }
-
-               r = dm_sm_metadata_open(inner, *tm,
-                                       dm_block_data(*sblock) + root_offset,
-                                       root_max_len);
+               r = dm_sm_metadata_open(*sm, *tm, sm_root, sm_len);
                if (r) {
                        DMERR("couldn't open metadata space map");
-                       goto bad2;
-               }
-
-               *sm = dm_sm_checker_create(inner);
-               if (IS_ERR(*sm)) {
-                       r = PTR_ERR(*sm);
-                       goto bad2;
+                       goto bad;
                }
        }
 
        return 0;
 
-bad2:
-       dm_tm_unlock(*tm, *sblock);
-bad1:
+bad:
        dm_tm_destroy(*tm);
-       dm_sm_destroy(inner);
+       dm_sm_destroy(*sm);
        return r;
 }
 
 int dm_tm_create_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
-                        struct dm_block_validator *sb_validator,
                         struct dm_transaction_manager **tm,
-                        struct dm_space_map **sm, struct dm_block **sblock)
+                        struct dm_space_map **sm)
 {
-       return dm_tm_create_internal(bm, sb_location, sb_validator,
-                                    0, 0, tm, sm, sblock, 1);
+       return dm_tm_create_internal(bm, sb_location, tm, sm, 1, NULL, 0);
 }
 EXPORT_SYMBOL_GPL(dm_tm_create_with_sm);
 
 int dm_tm_open_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
-                      struct dm_block_validator *sb_validator,
-                      size_t root_offset, size_t root_max_len,
+                      void *sm_root, size_t root_len,
                       struct dm_transaction_manager **tm,
-                      struct dm_space_map **sm, struct dm_block **sblock)
+                      struct dm_space_map **sm)
 {
-       return dm_tm_create_internal(bm, sb_location, sb_validator, root_offset,
-                                    root_max_len, tm, sm, sblock, 0);
+       return dm_tm_create_internal(bm, sb_location, tm, sm, 0, sm_root, root_len);
 }
 EXPORT_SYMBOL_GPL(dm_tm_open_with_sm);
 
index 6da784871db45edc452dbcc8ba5dd59b61cb8440..b5b139076ca58034b38e80f06c4e5ac9f177f47d 100644 (file)
@@ -115,16 +115,17 @@ struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm);
  *
  * Returns a tm that has an open transaction to write the new disk sm.
  * Caller should store the new sm root and commit.
+ *
+ * The superblock location is passed so the metadata space map knows it
+ * shouldn't be used.
  */
 int dm_tm_create_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
-                        struct dm_block_validator *sb_validator,
                         struct dm_transaction_manager **tm,
-                        struct dm_space_map **sm, struct dm_block **sblock);
+                        struct dm_space_map **sm);
 
 int dm_tm_open_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
-                      struct dm_block_validator *sb_validator,
-                      size_t root_offset, size_t root_max_len,
+                      void *sm_root, size_t root_len,
                       struct dm_transaction_manager **tm,
-                      struct dm_space_map **sm, struct dm_block **sblock);
+                      struct dm_space_map **sm);
 
 #endif /* _LINUX_DM_TRANSACTION_MANAGER_H */
index cacd008d68644428914b39822417209cd00dddc8..9f7f8bee84423f1a7dd35cc33bda9874407af06e 100644 (file)
  */
 #define        NR_RAID1_BIOS 256
 
+/* when we get a read error on a read-only array, we redirect to another
+ * device without failing the first device, or trying to over-write to
+ * correct the read error.  To keep track of bad blocks on a per-bio
+ * level, we store IO_BLOCKED in the appropriate 'bios' pointer
+ */
+#define IO_BLOCKED ((struct bio *)1)
+/* When we successfully write to a known bad-block, we need to remove the
+ * bad-block marking which must be done from process context.  So we record
+ * the success by setting devs[n].bio to IO_MADE_GOOD
+ */
+#define IO_MADE_GOOD ((struct bio *)2)
+
+#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
+
 /* When there are this many requests queue to be written by
  * the raid1 thread, we become 'congested' to provide back-pressure
  * for writeback.
@@ -483,12 +497,14 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
        const sector_t this_sector = r1_bio->sector;
        int sectors;
        int best_good_sectors;
-       int start_disk;
-       int best_disk;
-       int i;
+       int best_disk, best_dist_disk, best_pending_disk;
+       int has_nonrot_disk;
+       int disk;
        sector_t best_dist;
+       unsigned int min_pending;
        struct md_rdev *rdev;
        int choose_first;
+       int choose_next_idle;
 
        rcu_read_lock();
        /*
@@ -499,26 +515,26 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
  retry:
        sectors = r1_bio->sectors;
        best_disk = -1;
+       best_dist_disk = -1;
        best_dist = MaxSector;
+       best_pending_disk = -1;
+       min_pending = UINT_MAX;
        best_good_sectors = 0;
+       has_nonrot_disk = 0;
+       choose_next_idle = 0;
 
        if (conf->mddev->recovery_cp < MaxSector &&
-           (this_sector + sectors >= conf->next_resync)) {
+           (this_sector + sectors >= conf->next_resync))
                choose_first = 1;
-               start_disk = 0;
-       } else {
+       else
                choose_first = 0;
-               start_disk = conf->last_used;
-       }
 
-       for (i = 0 ; i < conf->raid_disks * 2 ; i++) {
+       for (disk = 0 ; disk < conf->raid_disks * 2 ; disk++) {
                sector_t dist;
                sector_t first_bad;
                int bad_sectors;
-
-               int disk = start_disk + i;
-               if (disk >= conf->raid_disks * 2)
-                       disk -= conf->raid_disks * 2;
+               unsigned int pending;
+               bool nonrot;
 
                rdev = rcu_dereference(conf->mirrors[disk].rdev);
                if (r1_bio->bios[disk] == IO_BLOCKED
@@ -577,22 +593,77 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
                } else
                        best_good_sectors = sectors;
 
+               nonrot = blk_queue_nonrot(bdev_get_queue(rdev->bdev));
+               has_nonrot_disk |= nonrot;
+               pending = atomic_read(&rdev->nr_pending);
                dist = abs(this_sector - conf->mirrors[disk].head_position);
-               if (choose_first
-                   /* Don't change to another disk for sequential reads */
-                   || conf->next_seq_sect == this_sector
-                   || dist == 0
-                   /* If device is idle, use it */
-                   || atomic_read(&rdev->nr_pending) == 0) {
+               if (choose_first) {
+                       best_disk = disk;
+                       break;
+               }
+               /* Don't change to another disk for sequential reads */
+               if (conf->mirrors[disk].next_seq_sect == this_sector
+                   || dist == 0) {
+                       int opt_iosize = bdev_io_opt(rdev->bdev) >> 9;
+                       struct raid1_info *mirror = &conf->mirrors[disk];
+
+                       best_disk = disk;
+                       /*
+                        * If buffered sequential IO size exceeds optimal
+                        * iosize, check if there is idle disk. If yes, choose
+                        * the idle disk. read_balance could already choose an
+                        * idle disk before noticing it's a sequential IO in
+                        * this disk. This doesn't matter because this disk
+                        * will idle, next time it will be utilized after the
+                        * first disk has IO size exceeds optimal iosize. In
+                        * this way, iosize of the first disk will be optimal
+                        * iosize at least. iosize of the second disk might be
+                        * small, but not a big deal since when the second disk
+                        * starts IO, the first disk is likely still busy.
+                        */
+                       if (nonrot && opt_iosize > 0 &&
+                           mirror->seq_start != MaxSector &&
+                           mirror->next_seq_sect > opt_iosize &&
+                           mirror->next_seq_sect - opt_iosize >=
+                           mirror->seq_start) {
+                               choose_next_idle = 1;
+                               continue;
+                       }
+                       break;
+               }
+               /* If device is idle, use it */
+               if (pending == 0) {
                        best_disk = disk;
                        break;
                }
+
+               if (choose_next_idle)
+                       continue;
+
+               if (min_pending > pending) {
+                       min_pending = pending;
+                       best_pending_disk = disk;
+               }
+
                if (dist < best_dist) {
                        best_dist = dist;
-                       best_disk = disk;
+                       best_dist_disk = disk;
                }
        }
 
+       /*
+        * If all disks are rotational, choose the closest disk. If any disk is
+        * non-rotational, choose the disk with less pending request even the
+        * disk is rotational, which might/might not be optimal for raids with
+        * mixed ratation/non-rotational disks depending on workload.
+        */
+       if (best_disk == -1) {
+               if (has_nonrot_disk)
+                       best_disk = best_pending_disk;
+               else
+                       best_disk = best_dist_disk;
+       }
+
        if (best_disk >= 0) {
                rdev = rcu_dereference(conf->mirrors[best_disk].rdev);
                if (!rdev)
@@ -606,8 +677,11 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
                        goto retry;
                }
                sectors = best_good_sectors;
-               conf->next_seq_sect = this_sector + sectors;
-               conf->last_used = best_disk;
+
+               if (conf->mirrors[best_disk].next_seq_sect != this_sector)
+                       conf->mirrors[best_disk].seq_start = this_sector;
+
+               conf->mirrors[best_disk].next_seq_sect = this_sector + sectors;
        }
        rcu_read_unlock();
        *max_sectors = sectors;
@@ -873,7 +947,7 @@ do_sync_io:
 static void make_request(struct mddev *mddev, struct bio * bio)
 {
        struct r1conf *conf = mddev->private;
-       struct mirror_info *mirror;
+       struct raid1_info *mirror;
        struct r1bio *r1_bio;
        struct bio *read_bio;
        int i, disks;
@@ -1364,7 +1438,7 @@ static int raid1_add_disk(struct mddev *mddev, struct md_rdev *rdev)
        struct r1conf *conf = mddev->private;
        int err = -EEXIST;
        int mirror = 0;
-       struct mirror_info *p;
+       struct raid1_info *p;
        int first = 0;
        int last = conf->raid_disks - 1;
        struct request_queue *q = bdev_get_queue(rdev->bdev);
@@ -1433,7 +1507,7 @@ static int raid1_remove_disk(struct mddev *mddev, struct md_rdev *rdev)
        struct r1conf *conf = mddev->private;
        int err = 0;
        int number = rdev->raid_disk;
-       struct mirror_info *p = conf->mirrors+ number;
+       struct raid1_info *p = conf->mirrors + number;
 
        if (rdev != p->rdev)
                p = conf->mirrors + conf->raid_disks + number;
@@ -2173,8 +2247,7 @@ static void raid1d(struct mddev *mddev)
        blk_start_plug(&plug);
        for (;;) {
 
-               if (atomic_read(&mddev->plug_cnt) == 0)
-                       flush_pending_writes(conf);
+               flush_pending_writes(conf);
 
                spin_lock_irqsave(&conf->device_lock, flags);
                if (list_empty(head)) {
@@ -2371,6 +2444,18 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int *skipp
                                bio->bi_rw = READ;
                                bio->bi_end_io = end_sync_read;
                                read_targets++;
+                       } else if (!test_bit(WriteErrorSeen, &rdev->flags) &&
+                               test_bit(MD_RECOVERY_SYNC, &mddev->recovery) &&
+                               !test_bit(MD_RECOVERY_CHECK, &mddev->recovery)) {
+                               /*
+                                * The device is suitable for reading (InSync),
+                                * but has bad block(s) here. Let's try to correct them,
+                                * if we are doing resync or repair. Otherwise, leave
+                                * this device alone for this sync request.
+                                */
+                               bio->bi_rw = WRITE;
+                               bio->bi_end_io = end_sync_write;
+                               write_targets++;
                        }
                }
                if (bio->bi_end_io) {
@@ -2428,7 +2513,10 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int *skipp
                /* There is nowhere to write, so all non-sync
                 * drives must be failed - so we are finished
                 */
-               sector_t rv = max_sector - sector_nr;
+               sector_t rv;
+               if (min_bad > 0)
+                       max_sector = sector_nr + min_bad;
+               rv = max_sector - sector_nr;
                *skipped = 1;
                put_buf(r1_bio);
                return rv;
@@ -2521,7 +2609,7 @@ static struct r1conf *setup_conf(struct mddev *mddev)
 {
        struct r1conf *conf;
        int i;
-       struct mirror_info *disk;
+       struct raid1_info *disk;
        struct md_rdev *rdev;
        int err = -ENOMEM;
 
@@ -2529,7 +2617,7 @@ static struct r1conf *setup_conf(struct mddev *mddev)
        if (!conf)
                goto abort;
 
-       conf->mirrors = kzalloc(sizeof(struct mirror_info)
+       conf->mirrors = kzalloc(sizeof(struct raid1_info)
                                * mddev->raid_disks * 2,
                                 GFP_KERNEL);
        if (!conf->mirrors)
@@ -2572,6 +2660,7 @@ static struct r1conf *setup_conf(struct mddev *mddev)
                        mddev->merge_check_needed = 1;
 
                disk->head_position = 0;
+               disk->seq_start = MaxSector;
        }
        conf->raid_disks = mddev->raid_disks;
        conf->mddev = mddev;
@@ -2585,7 +2674,6 @@ static struct r1conf *setup_conf(struct mddev *mddev)
        conf->recovery_disabled = mddev->recovery_disabled - 1;
 
        err = -EIO;
-       conf->last_used = -1;
        for (i = 0; i < conf->raid_disks * 2; i++) {
 
                disk = conf->mirrors + i;
@@ -2611,19 +2699,9 @@ static struct r1conf *setup_conf(struct mddev *mddev)
                        if (disk->rdev &&
                            (disk->rdev->saved_raid_disk < 0))
                                conf->fullsync = 1;
-               } else if (conf->last_used < 0)
-                       /*
-                        * The first working device is used as a
-                        * starting point to read balancing.
-                        */
-                       conf->last_used = i;
+               }
        }
 
-       if (conf->last_used < 0) {
-               printk(KERN_ERR "md/raid1:%s: no operational mirrors\n",
-                      mdname(mddev));
-               goto abort;
-       }
        err = -ENOMEM;
        conf->thread = md_register_thread(raid1d, mddev, "raid1");
        if (!conf->thread) {
@@ -2798,7 +2876,7 @@ static int raid1_reshape(struct mddev *mddev)
         */
        mempool_t *newpool, *oldpool;
        struct pool_info *newpoolinfo;
-       struct mirror_info *newmirrors;
+       struct raid1_info *newmirrors;
        struct r1conf *conf = mddev->private;
        int cnt, raid_disks;
        unsigned long flags;
@@ -2841,7 +2919,7 @@ static int raid1_reshape(struct mddev *mddev)
                kfree(newpoolinfo);
                return -ENOMEM;
        }
-       newmirrors = kzalloc(sizeof(struct mirror_info) * raid_disks * 2,
+       newmirrors = kzalloc(sizeof(struct raid1_info) * raid_disks * 2,
                             GFP_KERNEL);
        if (!newmirrors) {
                kfree(newpoolinfo);
@@ -2880,7 +2958,6 @@ static int raid1_reshape(struct mddev *mddev)
        conf->raid_disks = mddev->raid_disks = raid_disks;
        mddev->delta_disks = 0;
 
-       conf->last_used = 0; /* just make sure it is in-range */
        lower_barrier(conf);
 
        set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
index 80ded139314cf8a649729ce8e22915ca11bbd17f..0ff3715fb7eba5ec4fed61a9922276b07b363aff 100644 (file)
@@ -1,9 +1,15 @@
 #ifndef _RAID1_H
 #define _RAID1_H
 
-struct mirror_info {
+struct raid1_info {
        struct md_rdev  *rdev;
        sector_t        head_position;
+
+       /* When choose the best device for a read (read_balance())
+        * we try to keep sequential reads one the same device
+        */
+       sector_t        next_seq_sect;
+       sector_t        seq_start;
 };
 
 /*
@@ -24,17 +30,11 @@ struct pool_info {
 
 struct r1conf {
        struct mddev            *mddev;
-       struct mirror_info      *mirrors;       /* twice 'raid_disks' to
+       struct raid1_info       *mirrors;       /* twice 'raid_disks' to
                                                 * allow for replacements.
                                                 */
        int                     raid_disks;
 
-       /* When choose the best device for a read (read_balance())
-        * we try to keep sequential reads one the same device
-        * using 'last_used' and 'next_seq_sect'
-        */
-       int                     last_used;
-       sector_t                next_seq_sect;
        /* During resync, read_balancing is only allowed on the part
         * of the array that has been resynced.  'next_resync' tells us
         * where that is.
@@ -135,20 +135,6 @@ struct r1bio {
        /* DO NOT PUT ANY NEW FIELDS HERE - bios array is contiguously alloced*/
 };
 
-/* when we get a read error on a read-only array, we redirect to another
- * device without failing the first device, or trying to over-write to
- * correct the read error.  To keep track of bad blocks on a per-bio
- * level, we store IO_BLOCKED in the appropriate 'bios' pointer
- */
-#define IO_BLOCKED ((struct bio *)1)
-/* When we successfully write to a known bad-block, we need to remove the
- * bad-block marking which must be done from process context.  So we record
- * the success by setting bios[n] to IO_MADE_GOOD
- */
-#define IO_MADE_GOOD ((struct bio *)2)
-
-#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
-
 /* bits for r1bio.state */
 #define        R1BIO_Uptodate  0
 #define        R1BIO_IsSync    1
index 8da6282254c3e822a27702c469b9afce720bda43..de5ed6fd8806c3c3db39bbd894fabb04734b4523 100644 (file)
  */
 #define        NR_RAID10_BIOS 256
 
-/* When there are this many requests queue to be written by
+/* when we get a read error on a read-only array, we redirect to another
+ * device without failing the first device, or trying to over-write to
+ * correct the read error.  To keep track of bad blocks on a per-bio
+ * level, we store IO_BLOCKED in the appropriate 'bios' pointer
+ */
+#define IO_BLOCKED ((struct bio *)1)
+/* When we successfully write to a known bad-block, we need to remove the
+ * bad-block marking which must be done from process context.  So we record
+ * the success by setting devs[n].bio to IO_MADE_GOOD
+ */
+#define IO_MADE_GOOD ((struct bio *)2)
+
+#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
+
+/* When there are this many requests queued to be written by
  * the raid10 thread, we become 'congested' to provide back-pressure
  * for writeback.
  */
@@ -717,7 +731,7 @@ static struct md_rdev *read_balance(struct r10conf *conf,
        int sectors = r10_bio->sectors;
        int best_good_sectors;
        sector_t new_distance, best_dist;
-       struct md_rdev *rdev, *best_rdev;
+       struct md_rdev *best_rdev, *rdev = NULL;
        int do_balance;
        int best_slot;
        struct geom *geo = &conf->geo;
@@ -839,9 +853,8 @@ retry:
        return rdev;
 }
 
-static int raid10_congested(void *data, int bits)
+int md_raid10_congested(struct mddev *mddev, int bits)
 {
-       struct mddev *mddev = data;
        struct r10conf *conf = mddev->private;
        int i, ret = 0;
 
@@ -849,8 +862,6 @@ static int raid10_congested(void *data, int bits)
            conf->pending_count >= max_queued_requests)
                return 1;
 
-       if (mddev_congested(mddev, bits))
-               return 1;
        rcu_read_lock();
        for (i = 0;
             (i < conf->geo.raid_disks || i < conf->prev.raid_disks)
@@ -866,6 +877,15 @@ static int raid10_congested(void *data, int bits)
        rcu_read_unlock();
        return ret;
 }
+EXPORT_SYMBOL_GPL(md_raid10_congested);
+
+static int raid10_congested(void *data, int bits)
+{
+       struct mddev *mddev = data;
+
+       return mddev_congested(mddev, bits) ||
+               md_raid10_congested(mddev, bits);
+}
 
 static void flush_pending_writes(struct r10conf *conf)
 {
@@ -1546,7 +1566,7 @@ static void error(struct mddev *mddev, struct md_rdev *rdev)
 static void print_conf(struct r10conf *conf)
 {
        int i;
-       struct mirror_info *tmp;
+       struct raid10_info *tmp;
 
        printk(KERN_DEBUG "RAID10 conf printout:\n");
        if (!conf) {
@@ -1580,7 +1600,7 @@ static int raid10_spare_active(struct mddev *mddev)
 {
        int i;
        struct r10conf *conf = mddev->private;
-       struct mirror_info *tmp;
+       struct raid10_info *tmp;
        int count = 0;
        unsigned long flags;
 
@@ -1655,7 +1675,7 @@ static int raid10_add_disk(struct mddev *mddev, struct md_rdev *rdev)
        else
                mirror = first;
        for ( ; mirror <= last ; mirror++) {
-               struct mirror_info *p = &conf->mirrors[mirror];
+               struct raid10_info *p = &conf->mirrors[mirror];
                if (p->recovery_disabled == mddev->recovery_disabled)
                        continue;
                if (p->rdev) {
@@ -1709,7 +1729,7 @@ static int raid10_remove_disk(struct mddev *mddev, struct md_rdev *rdev)
        int err = 0;
        int number = rdev->raid_disk;
        struct md_rdev **rdevp;
-       struct mirror_info *p = conf->mirrors + number;
+       struct raid10_info *p = conf->mirrors + number;
 
        print_conf(conf);
        if (rdev == p->rdev)
@@ -2660,8 +2680,7 @@ static void raid10d(struct mddev *mddev)
        blk_start_plug(&plug);
        for (;;) {
 
-               if (atomic_read(&mddev->plug_cnt) == 0)
-                       flush_pending_writes(conf);
+               flush_pending_writes(conf);
 
                spin_lock_irqsave(&conf->device_lock, flags);
                if (list_empty(head)) {
@@ -2876,7 +2895,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr,
                        sector_t sect;
                        int must_sync;
                        int any_working;
-                       struct mirror_info *mirror = &conf->mirrors[i];
+                       struct raid10_info *mirror = &conf->mirrors[i];
 
                        if ((mirror->rdev == NULL ||
                             test_bit(In_sync, &mirror->rdev->flags))
@@ -3388,7 +3407,7 @@ static struct r10conf *setup_conf(struct mddev *mddev)
                goto out;
 
        /* FIXME calc properly */
-       conf->mirrors = kzalloc(sizeof(struct mirror_info)*(mddev->raid_disks +
+       conf->mirrors = kzalloc(sizeof(struct raid10_info)*(mddev->raid_disks +
                                                            max(0,mddev->delta_disks)),
                                GFP_KERNEL);
        if (!conf->mirrors)
@@ -3452,7 +3471,7 @@ static int run(struct mddev *mddev)
 {
        struct r10conf *conf;
        int i, disk_idx, chunk_size;
-       struct mirror_info *disk;
+       struct raid10_info *disk;
        struct md_rdev *rdev;
        sector_t size;
        sector_t min_offset_diff = 0;
@@ -3472,12 +3491,14 @@ static int run(struct mddev *mddev)
        conf->thread = NULL;
 
        chunk_size = mddev->chunk_sectors << 9;
-       blk_queue_io_min(mddev->queue, chunk_size);
-       if (conf->geo.raid_disks % conf->geo.near_copies)
-               blk_queue_io_opt(mddev->queue, chunk_size * conf->geo.raid_disks);
-       else
-               blk_queue_io_opt(mddev->queue, chunk_size *
-                                (conf->geo.raid_disks / conf->geo.near_copies));
+       if (mddev->queue) {
+               blk_queue_io_min(mddev->queue, chunk_size);
+               if (conf->geo.raid_disks % conf->geo.near_copies)
+                       blk_queue_io_opt(mddev->queue, chunk_size * conf->geo.raid_disks);
+               else
+                       blk_queue_io_opt(mddev->queue, chunk_size *
+                                        (conf->geo.raid_disks / conf->geo.near_copies));
+       }
 
        rdev_for_each(rdev, mddev) {
                long long diff;
@@ -3511,8 +3532,9 @@ static int run(struct mddev *mddev)
                if (first || diff < min_offset_diff)
                        min_offset_diff = diff;
 
-               disk_stack_limits(mddev->gendisk, rdev->bdev,
-                                 rdev->data_offset << 9);
+               if (mddev->gendisk)
+                       disk_stack_limits(mddev->gendisk, rdev->bdev,
+                                         rdev->data_offset << 9);
 
                disk->head_position = 0;
        }
@@ -3575,22 +3597,22 @@ static int run(struct mddev *mddev)
        md_set_array_sectors(mddev, size);
        mddev->resync_max_sectors = size;
 
-       mddev->queue->backing_dev_info.congested_fn = raid10_congested;
-       mddev->queue->backing_dev_info.congested_data = mddev;
-
-       /* Calculate max read-ahead size.
-        * We need to readahead at least twice a whole stripe....
-        * maybe...
-        */
-       {
+       if (mddev->queue) {
                int stripe = conf->geo.raid_disks *
                        ((mddev->chunk_sectors << 9) / PAGE_SIZE);
+               mddev->queue->backing_dev_info.congested_fn = raid10_congested;
+               mddev->queue->backing_dev_info.congested_data = mddev;
+
+               /* Calculate max read-ahead size.
+                * We need to readahead at least twice a whole stripe....
+                * maybe...
+                */
                stripe /= conf->geo.near_copies;
                if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe)
                        mddev->queue->backing_dev_info.ra_pages = 2 * stripe;
+               blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec);
        }
 
-       blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec);
 
        if (md_integrity_register(mddev))
                goto out_free_conf;
@@ -3641,7 +3663,10 @@ static int stop(struct mddev *mddev)
        lower_barrier(conf);
 
        md_unregister_thread(&mddev->thread);
-       blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
+       if (mddev->queue)
+               /* the unplug fn references 'conf'*/
+               blk_sync_queue(mddev->queue);
+
        if (conf->r10bio_pool)
                mempool_destroy(conf->r10bio_pool);
        kfree(conf->mirrors);
@@ -3805,7 +3830,7 @@ static int raid10_check_reshape(struct mddev *mddev)
        if (mddev->delta_disks > 0) {
                /* allocate new 'mirrors' list */
                conf->mirrors_new = kzalloc(
-                       sizeof(struct mirror_info)
+                       sizeof(struct raid10_info)
                        *(mddev->raid_disks +
                          mddev->delta_disks),
                        GFP_KERNEL);
@@ -3930,7 +3955,7 @@ static int raid10_start_reshape(struct mddev *mddev)
        spin_lock_irq(&conf->device_lock);
        if (conf->mirrors_new) {
                memcpy(conf->mirrors_new, conf->mirrors,
-                      sizeof(struct mirror_info)*conf->prev.raid_disks);
+                      sizeof(struct raid10_info)*conf->prev.raid_disks);
                smp_mb();
                kfree(conf->mirrors_old); /* FIXME and elsewhere */
                conf->mirrors_old = conf->mirrors;
index 135b1b0a155438624b56ebadf42d8971463adc6a..007c2c68dd8369f2acbcd9ae8d3c155399d13674 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef _RAID10_H
 #define _RAID10_H
 
-struct mirror_info {
+struct raid10_info {
        struct md_rdev  *rdev, *replacement;
        sector_t        head_position;
        int             recovery_disabled;      /* matches
@@ -13,8 +13,8 @@ struct mirror_info {
 
 struct r10conf {
        struct mddev            *mddev;
-       struct mirror_info      *mirrors;
-       struct mirror_info      *mirrors_new, *mirrors_old;
+       struct raid10_info      *mirrors;
+       struct raid10_info      *mirrors_new, *mirrors_old;
        spinlock_t              device_lock;
 
        /* geometry */
@@ -123,20 +123,6 @@ struct r10bio {
        } devs[0];
 };
 
-/* when we get a read error on a read-only array, we redirect to another
- * device without failing the first device, or trying to over-write to
- * correct the read error.  To keep track of bad blocks on a per-bio
- * level, we store IO_BLOCKED in the appropriate 'bios' pointer
- */
-#define IO_BLOCKED ((struct bio*)1)
-/* When we successfully write to a known bad-block, we need to remove the
- * bad-block marking which must be done from process context.  So we record
- * the success by setting devs[n].bio to IO_MADE_GOOD
- */
-#define IO_MADE_GOOD ((struct bio *)2)
-
-#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
-
 /* bits for r10bio.state */
 enum r10bio_state {
        R10BIO_Uptodate,
@@ -159,4 +145,7 @@ enum r10bio_state {
  */
        R10BIO_Previous,
 };
+
+extern int md_raid10_congested(struct mddev *mddev, int bits);
+
 #endif
index 04348d76bb30fa8831964ea980ec2df912a45f92..87a2d0bdedd1187a695a4d7f25a6d2e5bc2164fe 100644 (file)
@@ -99,34 +99,40 @@ static inline struct bio *r5_next_bio(struct bio *bio, sector_t sector)
  * We maintain a biased count of active stripes in the bottom 16 bits of
  * bi_phys_segments, and a count of processed stripes in the upper 16 bits
  */
-static inline int raid5_bi_phys_segments(struct bio *bio)
+static inline int raid5_bi_processed_stripes(struct bio *bio)
 {
-       return bio->bi_phys_segments & 0xffff;
+       atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+       return (atomic_read(segments) >> 16) & 0xffff;
 }
 
-static inline int raid5_bi_hw_segments(struct bio *bio)
+static inline int raid5_dec_bi_active_stripes(struct bio *bio)
 {
-       return (bio->bi_phys_segments >> 16) & 0xffff;
+       atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+       return atomic_sub_return(1, segments) & 0xffff;
 }
 
-static inline int raid5_dec_bi_phys_segments(struct bio *bio)
+static inline void raid5_inc_bi_active_stripes(struct bio *bio)
 {
-       --bio->bi_phys_segments;
-       return raid5_bi_phys_segments(bio);
+       atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+       atomic_inc(segments);
 }
 
-static inline int raid5_dec_bi_hw_segments(struct bio *bio)
+static inline void raid5_set_bi_processed_stripes(struct bio *bio,
+       unsigned int cnt)
 {
-       unsigned short val = raid5_bi_hw_segments(bio);
+       atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+       int old, new;
 
-       --val;
-       bio->bi_phys_segments = (val << 16) | raid5_bi_phys_segments(bio);
-       return val;
+       do {
+               old = atomic_read(segments);
+               new = (old & 0xffff) | (cnt << 16);
+       } while (atomic_cmpxchg(segments, old, new) != old);
 }
 
-static inline void raid5_set_bi_hw_segments(struct bio *bio, unsigned int cnt)
+static inline void raid5_set_bi_stripes(struct bio *bio, unsigned int cnt)
 {
-       bio->bi_phys_segments = raid5_bi_phys_segments(bio) | (cnt << 16);
+       atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+       atomic_set(segments, cnt);
 }
 
 /* Find first data disk in a raid6 stripe */
@@ -190,49 +196,56 @@ static int stripe_operations_active(struct stripe_head *sh)
               test_bit(STRIPE_COMPUTE_RUN, &sh->state);
 }
 
-static void __release_stripe(struct r5conf *conf, struct stripe_head *sh)
+static void do_release_stripe(struct r5conf *conf, struct stripe_head *sh)
 {
-       if (atomic_dec_and_test(&sh->count)) {
-               BUG_ON(!list_empty(&sh->lru));
-               BUG_ON(atomic_read(&conf->active_stripes)==0);
-               if (test_bit(STRIPE_HANDLE, &sh->state)) {
-                       if (test_bit(STRIPE_DELAYED, &sh->state) &&
-                           !test_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
-                               list_add_tail(&sh->lru, &conf->delayed_list);
-                       else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
-                                  sh->bm_seq - conf->seq_write > 0)
-                               list_add_tail(&sh->lru, &conf->bitmap_list);
-                       else {
-                               clear_bit(STRIPE_DELAYED, &sh->state);
-                               clear_bit(STRIPE_BIT_DELAY, &sh->state);
-                               list_add_tail(&sh->lru, &conf->handle_list);
-                       }
-                       md_wakeup_thread(conf->mddev->thread);
-               } else {
-                       BUG_ON(stripe_operations_active(sh));
-                       if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
-                               if (atomic_dec_return(&conf->preread_active_stripes)
-                                   < IO_THRESHOLD)
-                                       md_wakeup_thread(conf->mddev->thread);
-                       atomic_dec(&conf->active_stripes);
-                       if (!test_bit(STRIPE_EXPANDING, &sh->state)) {
-                               list_add_tail(&sh->lru, &conf->inactive_list);
-                               wake_up(&conf->wait_for_stripe);
-                               if (conf->retry_read_aligned)
-                                       md_wakeup_thread(conf->mddev->thread);
-                       }
+       BUG_ON(!list_empty(&sh->lru));
+       BUG_ON(atomic_read(&conf->active_stripes)==0);
+       if (test_bit(STRIPE_HANDLE, &sh->state)) {
+               if (test_bit(STRIPE_DELAYED, &sh->state) &&
+                   !test_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
+                       list_add_tail(&sh->lru, &conf->delayed_list);
+               else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
+                          sh->bm_seq - conf->seq_write > 0)
+                       list_add_tail(&sh->lru, &conf->bitmap_list);
+               else {
+                       clear_bit(STRIPE_DELAYED, &sh->state);
+                       clear_bit(STRIPE_BIT_DELAY, &sh->state);
+                       list_add_tail(&sh->lru, &conf->handle_list);
+               }
+               md_wakeup_thread(conf->mddev->thread);
+       } else {
+               BUG_ON(stripe_operations_active(sh));
+               if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
+                       if (atomic_dec_return(&conf->preread_active_stripes)
+                           < IO_THRESHOLD)
+                               md_wakeup_thread(conf->mddev->thread);
+               atomic_dec(&conf->active_stripes);
+               if (!test_bit(STRIPE_EXPANDING, &sh->state)) {
+                       list_add_tail(&sh->lru, &conf->inactive_list);
+                       wake_up(&conf->wait_for_stripe);
+                       if (conf->retry_read_aligned)
+                               md_wakeup_thread(conf->mddev->thread);
                }
        }
 }
 
+static void __release_stripe(struct r5conf *conf, struct stripe_head *sh)
+{
+       if (atomic_dec_and_test(&sh->count))
+               do_release_stripe(conf, sh);
+}
+
 static void release_stripe(struct stripe_head *sh)
 {
        struct r5conf *conf = sh->raid_conf;
        unsigned long flags;
 
-       spin_lock_irqsave(&conf->device_lock, flags);
-       __release_stripe(conf, sh);
-       spin_unlock_irqrestore(&conf->device_lock, flags);
+       local_irq_save(flags);
+       if (atomic_dec_and_lock(&sh->count, &conf->device_lock)) {
+               do_release_stripe(conf, sh);
+               spin_unlock(&conf->device_lock);
+       }
+       local_irq_restore(flags);
 }
 
 static inline void remove_hash(struct stripe_head *sh)
@@ -640,6 +653,9 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
                        else
                                bi->bi_sector = (sh->sector
                                                 + rdev->data_offset);
+                       if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags))
+                               bi->bi_rw |= REQ_FLUSH;
+
                        bi->bi_flags = 1 << BIO_UPTODATE;
                        bi->bi_idx = 0;
                        bi->bi_io_vec[0].bv_len = STRIPE_SIZE;
@@ -749,14 +765,12 @@ static void ops_complete_biofill(void *stripe_head_ref)
 {
        struct stripe_head *sh = stripe_head_ref;
        struct bio *return_bi = NULL;
-       struct r5conf *conf = sh->raid_conf;
        int i;
 
        pr_debug("%s: stripe %llu\n", __func__,
                (unsigned long long)sh->sector);
 
        /* clear completed biofills */
-       spin_lock_irq(&conf->device_lock);
        for (i = sh->disks; i--; ) {
                struct r5dev *dev = &sh->dev[i];
 
@@ -774,7 +788,7 @@ static void ops_complete_biofill(void *stripe_head_ref)
                        while (rbi && rbi->bi_sector <
                                dev->sector + STRIPE_SECTORS) {
                                rbi2 = r5_next_bio(rbi, dev->sector);
-                               if (!raid5_dec_bi_phys_segments(rbi)) {
+                               if (!raid5_dec_bi_active_stripes(rbi)) {
                                        rbi->bi_next = return_bi;
                                        return_bi = rbi;
                                }
@@ -782,7 +796,6 @@ static void ops_complete_biofill(void *stripe_head_ref)
                        }
                }
        }
-       spin_unlock_irq(&conf->device_lock);
        clear_bit(STRIPE_BIOFILL_RUN, &sh->state);
 
        return_io(return_bi);
@@ -794,7 +807,6 @@ static void ops_complete_biofill(void *stripe_head_ref)
 static void ops_run_biofill(struct stripe_head *sh)
 {
        struct dma_async_tx_descriptor *tx = NULL;
-       struct r5conf *conf = sh->raid_conf;
        struct async_submit_ctl submit;
        int i;
 
@@ -805,10 +817,10 @@ static void ops_run_biofill(struct stripe_head *sh)
                struct r5dev *dev = &sh->dev[i];
                if (test_bit(R5_Wantfill, &dev->flags)) {
                        struct bio *rbi;
-                       spin_lock_irq(&conf->device_lock);
+                       spin_lock_irq(&sh->stripe_lock);
                        dev->read = rbi = dev->toread;
                        dev->toread = NULL;
-                       spin_unlock_irq(&conf->device_lock);
+                       spin_unlock_irq(&sh->stripe_lock);
                        while (rbi && rbi->bi_sector <
                                dev->sector + STRIPE_SECTORS) {
                                tx = async_copy_data(0, rbi, dev->page,
@@ -1144,12 +1156,12 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx)
                if (test_and_clear_bit(R5_Wantdrain, &dev->flags)) {
                        struct bio *wbi;
 
-                       spin_lock_irq(&sh->raid_conf->device_lock);
+                       spin_lock_irq(&sh->stripe_lock);
                        chosen = dev->towrite;
                        dev->towrite = NULL;
                        BUG_ON(dev->written);
                        wbi = dev->written = chosen;
-                       spin_unlock_irq(&sh->raid_conf->device_lock);
+                       spin_unlock_irq(&sh->stripe_lock);
 
                        while (wbi && wbi->bi_sector <
                                dev->sector + STRIPE_SECTORS) {
@@ -1454,6 +1466,8 @@ static int grow_one_stripe(struct r5conf *conf)
        init_waitqueue_head(&sh->ops.wait_for_ops);
        #endif
 
+       spin_lock_init(&sh->stripe_lock);
+
        if (grow_buffers(sh)) {
                shrink_buffers(sh);
                kmem_cache_free(conf->slab_cache, sh);
@@ -1739,7 +1753,9 @@ static void raid5_end_read_request(struct bio * bi, int error)
                        atomic_add(STRIPE_SECTORS, &rdev->corrected_errors);
                        clear_bit(R5_ReadError, &sh->dev[i].flags);
                        clear_bit(R5_ReWrite, &sh->dev[i].flags);
-               }
+               } else if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags))
+                       clear_bit(R5_ReadNoMerge, &sh->dev[i].flags);
+
                if (atomic_read(&rdev->read_errors))
                        atomic_set(&rdev->read_errors, 0);
        } else {
@@ -1784,7 +1800,11 @@ static void raid5_end_read_request(struct bio * bi, int error)
                else
                        retry = 1;
                if (retry)
-                       set_bit(R5_ReadError, &sh->dev[i].flags);
+                       if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags)) {
+                               set_bit(R5_ReadError, &sh->dev[i].flags);
+                               clear_bit(R5_ReadNoMerge, &sh->dev[i].flags);
+                       } else
+                               set_bit(R5_ReadNoMerge, &sh->dev[i].flags);
                else {
                        clear_bit(R5_ReadError, &sh->dev[i].flags);
                        clear_bit(R5_ReWrite, &sh->dev[i].flags);
@@ -2340,11 +2360,18 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
                (unsigned long long)bi->bi_sector,
                (unsigned long long)sh->sector);
 
-
-       spin_lock_irq(&conf->device_lock);
+       /*
+        * If several bio share a stripe. The bio bi_phys_segments acts as a
+        * reference count to avoid race. The reference count should already be
+        * increased before this function is called (for example, in
+        * make_request()), so other bio sharing this stripe will not free the
+        * stripe. If a stripe is owned by one stripe, the stripe lock will
+        * protect it.
+        */
+       spin_lock_irq(&sh->stripe_lock);
        if (forwrite) {
                bip = &sh->dev[dd_idx].towrite;
-               if (*bip == NULL && sh->dev[dd_idx].written == NULL)
+               if (*bip == NULL)
                        firstwrite = 1;
        } else
                bip = &sh->dev[dd_idx].toread;
@@ -2360,7 +2387,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
        if (*bip)
                bi->bi_next = *bip;
        *bip = bi;
-       bi->bi_phys_segments++;
+       raid5_inc_bi_active_stripes(bi);
 
        if (forwrite) {
                /* check if page is covered */
@@ -2375,7 +2402,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
                if (sector >= sh->dev[dd_idx].sector + STRIPE_SECTORS)
                        set_bit(R5_OVERWRITE, &sh->dev[dd_idx].flags);
        }
-       spin_unlock_irq(&conf->device_lock);
+       spin_unlock_irq(&sh->stripe_lock);
 
        pr_debug("added bi b#%llu to stripe s#%llu, disk %d.\n",
                (unsigned long long)(*bip)->bi_sector,
@@ -2391,7 +2418,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
 
  overlap:
        set_bit(R5_Overlap, &sh->dev[dd_idx].flags);
-       spin_unlock_irq(&conf->device_lock);
+       spin_unlock_irq(&sh->stripe_lock);
        return 0;
 }
 
@@ -2441,10 +2468,11 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,
                                rdev_dec_pending(rdev, conf->mddev);
                        }
                }
-               spin_lock_irq(&conf->device_lock);
+               spin_lock_irq(&sh->stripe_lock);
                /* fail all writes first */
                bi = sh->dev[i].towrite;
                sh->dev[i].towrite = NULL;
+               spin_unlock_irq(&sh->stripe_lock);
                if (bi) {
                        s->to_write--;
                        bitmap_end = 1;
@@ -2457,13 +2485,17 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,
                        sh->dev[i].sector + STRIPE_SECTORS) {
                        struct bio *nextbi = r5_next_bio(bi, sh->dev[i].sector);
                        clear_bit(BIO_UPTODATE, &bi->bi_flags);
-                       if (!raid5_dec_bi_phys_segments(bi)) {
+                       if (!raid5_dec_bi_active_stripes(bi)) {
                                md_write_end(conf->mddev);
                                bi->bi_next = *return_bi;
                                *return_bi = bi;
                        }
                        bi = nextbi;
                }
+               if (bitmap_end)
+                       bitmap_endwrite(conf->mddev->bitmap, sh->sector,
+                               STRIPE_SECTORS, 0, 0);
+               bitmap_end = 0;
                /* and fail all 'written' */
                bi = sh->dev[i].written;
                sh->dev[i].written = NULL;
@@ -2472,7 +2504,7 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,
                       sh->dev[i].sector + STRIPE_SECTORS) {
                        struct bio *bi2 = r5_next_bio(bi, sh->dev[i].sector);
                        clear_bit(BIO_UPTODATE, &bi->bi_flags);
-                       if (!raid5_dec_bi_phys_segments(bi)) {
+                       if (!raid5_dec_bi_active_stripes(bi)) {
                                md_write_end(conf->mddev);
                                bi->bi_next = *return_bi;
                                *return_bi = bi;
@@ -2496,14 +2528,13 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,
                                struct bio *nextbi =
                                        r5_next_bio(bi, sh->dev[i].sector);
                                clear_bit(BIO_UPTODATE, &bi->bi_flags);
-                               if (!raid5_dec_bi_phys_segments(bi)) {
+                               if (!raid5_dec_bi_active_stripes(bi)) {
                                        bi->bi_next = *return_bi;
                                        *return_bi = bi;
                                }
                                bi = nextbi;
                        }
                }
-               spin_unlock_irq(&conf->device_lock);
                if (bitmap_end)
                        bitmap_endwrite(conf->mddev->bitmap, sh->sector,
                                        STRIPE_SECTORS, 0, 0);
@@ -2707,30 +2738,23 @@ static void handle_stripe_clean_event(struct r5conf *conf,
                                test_bit(R5_UPTODATE, &dev->flags)) {
                                /* We can return any write requests */
                                struct bio *wbi, *wbi2;
-                               int bitmap_end = 0;
                                pr_debug("Return write for disc %d\n", i);
-                               spin_lock_irq(&conf->device_lock);
                                wbi = dev->written;
                                dev->written = NULL;
                                while (wbi && wbi->bi_sector <
                                        dev->sector + STRIPE_SECTORS) {
                                        wbi2 = r5_next_bio(wbi, dev->sector);
-                                       if (!raid5_dec_bi_phys_segments(wbi)) {
+                                       if (!raid5_dec_bi_active_stripes(wbi)) {
                                                md_write_end(conf->mddev);
                                                wbi->bi_next = *return_bi;
                                                *return_bi = wbi;
                                        }
                                        wbi = wbi2;
                                }
-                               if (dev->towrite == NULL)
-                                       bitmap_end = 1;
-                               spin_unlock_irq(&conf->device_lock);
-                               if (bitmap_end)
-                                       bitmap_endwrite(conf->mddev->bitmap,
-                                                       sh->sector,
-                                                       STRIPE_SECTORS,
+                               bitmap_endwrite(conf->mddev->bitmap, sh->sector,
+                                               STRIPE_SECTORS,
                                         !test_bit(STRIPE_DEGRADED, &sh->state),
-                                                       0);
+                                               0);
                        }
                }
 
@@ -3182,7 +3206,6 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
 
        /* Now to look around and see what can be done */
        rcu_read_lock();
-       spin_lock_irq(&conf->device_lock);
        for (i=disks; i--; ) {
                struct md_rdev *rdev;
                sector_t first_bad;
@@ -3328,7 +3351,6 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
                                do_recovery = 1;
                }
        }
-       spin_unlock_irq(&conf->device_lock);
        if (test_bit(STRIPE_SYNCING, &sh->state)) {
                /* If there is a failed device being replaced,
                 *     we must be recovering.
@@ -3791,7 +3813,7 @@ static struct bio *remove_bio_from_retry(struct r5conf *conf)
                 * this sets the active strip count to 1 and the processed
                 * strip count to zero (upper 8 bits)
                 */
-               bi->bi_phys_segments = 1; /* biased count of active stripes */
+               raid5_set_bi_stripes(bi, 1); /* biased count of active stripes */
        }
 
        return bi;
@@ -4113,7 +4135,7 @@ static void make_request(struct mddev *mddev, struct bio * bi)
                        finish_wait(&conf->wait_for_overlap, &w);
                        set_bit(STRIPE_HANDLE, &sh->state);
                        clear_bit(STRIPE_DELAYED, &sh->state);
-                       if ((bi->bi_rw & REQ_SYNC) &&
+                       if ((bi->bi_rw & REQ_NOIDLE) &&
                            !test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
                                atomic_inc(&conf->preread_active_stripes);
                        mddev_check_plugged(mddev);
@@ -4126,9 +4148,7 @@ static void make_request(struct mddev *mddev, struct bio * bi)
                }
        }
 
-       spin_lock_irq(&conf->device_lock);
-       remaining = raid5_dec_bi_phys_segments(bi);
-       spin_unlock_irq(&conf->device_lock);
+       remaining = raid5_dec_bi_active_stripes(bi);
        if (remaining == 0) {
 
                if ( rw == WRITE )
@@ -4484,7 +4504,7 @@ static int  retry_aligned_read(struct r5conf *conf, struct bio *raid_bio)
                     sector += STRIPE_SECTORS,
                     scnt++) {
 
-               if (scnt < raid5_bi_hw_segments(raid_bio))
+               if (scnt < raid5_bi_processed_stripes(raid_bio))
                        /* already done this stripe */
                        continue;
 
@@ -4492,25 +4512,24 @@ static int  retry_aligned_read(struct r5conf *conf, struct bio *raid_bio)
 
                if (!sh) {
                        /* failed to get a stripe - must wait */
-                       raid5_set_bi_hw_segments(raid_bio, scnt);
+                       raid5_set_bi_processed_stripes(raid_bio, scnt);
                        conf->retry_read_aligned = raid_bio;
                        return handled;
                }
 
                if (!add_stripe_bio(sh, raid_bio, dd_idx, 0)) {
                        release_stripe(sh);
-                       raid5_set_bi_hw_segments(raid_bio, scnt);
+                       raid5_set_bi_processed_stripes(raid_bio, scnt);
                        conf->retry_read_aligned = raid_bio;
                        return handled;
                }
 
+               set_bit(R5_ReadNoMerge, &sh->dev[dd_idx].flags);
                handle_stripe(sh);
                release_stripe(sh);
                handled++;
        }
-       spin_lock_irq(&conf->device_lock);
-       remaining = raid5_dec_bi_phys_segments(raid_bio);
-       spin_unlock_irq(&conf->device_lock);
+       remaining = raid5_dec_bi_active_stripes(raid_bio);
        if (remaining == 0)
                bio_endio(raid_bio, 0);
        if (atomic_dec_and_test(&conf->active_aligned_reads))
@@ -4543,7 +4562,7 @@ static void raid5d(struct mddev *mddev)
        while (1) {
                struct bio *bio;
 
-               if (atomic_read(&mddev->plug_cnt) == 0 &&
+               if (
                    !list_empty(&conf->bitmap_list)) {
                        /* Now is a good time to flush some bitmap updates */
                        conf->seq_flush++;
@@ -4553,8 +4572,7 @@ static void raid5d(struct mddev *mddev)
                        conf->seq_write = conf->seq_flush;
                        activate_bit_delay(conf);
                }
-               if (atomic_read(&mddev->plug_cnt) == 0)
-                       raid5_activate_delayed(conf);
+               raid5_activate_delayed(conf);
 
                while ((bio = remove_bio_from_retry(conf))) {
                        int ok;
index 2164021f3b5f6548b96ac067aac67bd7874dcd10..61dbb615c30b0f7174d834c01c316a910dda212a 100644 (file)
@@ -210,6 +210,7 @@ struct stripe_head {
        int                     disks;          /* disks in stripe */
        enum check_states       check_state;
        enum reconstruct_states reconstruct_state;
+       spinlock_t              stripe_lock;
        /**
         * struct stripe_operations
         * @target - STRIPE_OP_COMPUTE_BLK target
@@ -273,6 +274,7 @@ enum r5dev_flags {
        R5_Wantwrite,
        R5_Overlap,     /* There is a pending overlapping request
                         * on this block */
+       R5_ReadNoMerge, /* prevent bio from merging in block-layer */
        R5_ReadError,   /* seen a read error here recently */
        R5_ReWrite,     /* have tried to over-write the readerror */
 
index 9575db429df46648c25007a6de5395ef22e080f8..d941581ab92169c935a85754c6fc28d2bc2dd79a 100644 (file)
@@ -6,20 +6,82 @@ menuconfig MEDIA_SUPPORT
        tristate "Multimedia support"
        depends on HAS_IOMEM
        help
-         If you want to use Video for Linux, DVB for Linux, or DAB adapters,
+         If you want to use Webcams, Video grabber devices and/or TV devices
          enable this option and other options below.
+         Additional info and docs are available on the web at
+         <http://linuxtv.org>
 
 if MEDIA_SUPPORT
 
 comment "Multimedia core support"
 
+#
+# Multimedia support - automatically enable V4L2 and DVB core
+#
+config MEDIA_CAMERA_SUPPORT
+       bool "Cameras/video grabbers support"
+       ---help---
+         Enable support for webcams and video grabbers.
+
+         Say Y when you have a webcam or a video capture grabber board.
+
+config MEDIA_ANALOG_TV_SUPPORT
+       bool "Analog TV support"
+       ---help---
+         Enable analog TV support.
+
+         Say Y when you have a TV board with analog support or with a
+         hybrid analog/digital TV chipset.
+
+         Note: There are several DVB cards that are based on chips that
+               support both analog and digital TV. Disabling this option
+               will disable support for them.
+
+config MEDIA_DIGITAL_TV_SUPPORT
+       bool "Digital TV support"
+       ---help---
+         Enable digital TV support.
+
+         Say Y when you have a board with digital support or a board with
+         hybrid digital TV and analog TV.
+
+config MEDIA_RADIO_SUPPORT
+       bool "AM/FM radio receivers/transmitters support"
+       ---help---
+         Enable AM/FM radio support.
+
+         Additional info and docs are available on the web at
+         <http://linuxtv.org>
+
+         Say Y when you have a board with radio support.
+
+         Note: There are several TV cards that are based on chips that
+               support radio reception. Disabling this option will
+               disable support for them.
+
+config MEDIA_RC_SUPPORT
+       bool "Remote Controller support"
+       depends on INPUT
+       ---help---
+         Enable support for Remote Controllers on Linux. This is
+         needed in order to support several video capture adapters,
+         standalone IR receivers/transmitters, and RF receivers.
+
+         Enable this option if you have a video capture board even
+         if you don't need IR, as otherwise, you may not be able to
+         compile the driver for your adapter.
+
+         Say Y when you have a TV or an IR device.
+
 #
 # Media controller
+#      Selectable only for webcam/grabbers, as other drivers don't use it
 #
 
 config MEDIA_CONTROLLER
        bool "Media Controller API (EXPERIMENTAL)"
        depends on EXPERIMENTAL
+       depends on MEDIA_CAMERA_SUPPORT
        ---help---
          Enable the media controller API used to query media devices internal
          topology and configure it dynamically.
@@ -27,26 +89,15 @@ config MEDIA_CONTROLLER
          This API is mostly used by camera interfaces in embedded platforms.
 
 #
-# V4L core and enabled API's
+# Video4Linux support
+#      Only enables if one of the V4L2 types (ATV, webcam, radio) is selected
 #
 
 config VIDEO_DEV
-       tristate "Video For Linux"
-       ---help---
-         V4L core support for video capture and overlay devices, webcams and
-         AM/FM radio cards.
-
-         This kernel includes support for the new Video for Linux Two API,
-         (V4L2).
-
-         Additional info and docs are available on the web at
-         <http://linuxtv.org>
-
-         Documentation for V4L2 is also available on the web at
-         <http://bytesex.org/v4l/>.
-
-         To compile this driver as a module, choose M here: the
-         module will be called videodev.
+       tristate
+       depends on MEDIA_SUPPORT
+       depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT
+       default y
 
 config VIDEO_V4L2_COMMON
        tristate
@@ -64,25 +115,15 @@ config VIDEO_V4L2_SUBDEV_API
 
 #
 # DVB Core
+#      Only enables if one of DTV is selected
 #
 
 config DVB_CORE
-       tristate "DVB for Linux"
+       tristate
+       depends on MEDIA_SUPPORT
+       depends on MEDIA_DIGITAL_TV_SUPPORT
+       default y
        select CRC32
-       help
-         DVB core utility functions for device handling, software fallbacks etc.
-
-         Enable this if you own a DVB/ATSC adapter and want to use it or if
-         you compile Linux for a digital SetTopBox.
-
-         Say Y when you have a DVB or an ATSC card and want to use it.
-
-         API specs and user tools are available from <http://www.linuxtv.org/>.
-
-         Please report problems regarding this support to the LinuxDVB
-         mailing list.
-
-         If unsure say N.
 
 config DVB_NET
        bool "DVB Network Support"
@@ -97,12 +138,7 @@ config DVB_NET
          You may want to disable the network support on embedded devices. If
          unsure say Y.
 
-config VIDEO_MEDIA
-       tristate
-       default (DVB_CORE && (VIDEO_DEV = n)) || (VIDEO_DEV && (DVB_CORE = n)) || (DVB_CORE && VIDEO_DEV)
-
-comment "Multimedia drivers"
-
+comment "Media drivers"
 source "drivers/media/common/Kconfig"
 source "drivers/media/rc/Kconfig"
 
index bbf4945149a9e043c6b995810e7221ebb2828ea0..94c6ff7a5da3ad20bf8f10c5bc08b919153e45e5 100644 (file)
@@ -1,7 +1,8 @@
 config MEDIA_ATTACH
        bool "Load and attach frontend and tuner driver modules as needed"
-       depends on VIDEO_MEDIA
+       depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT
        depends on MODULES
+       default y if !EXPERT
        help
          Remove the static dependency of DVB card drivers on all
          frontend modules for all possible card variants. Instead,
@@ -19,15 +20,15 @@ config MEDIA_ATTACH
 
 config MEDIA_TUNER
        tristate
-       default VIDEO_MEDIA && I2C
-       depends on VIDEO_MEDIA && I2C
+       depends on (MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT) && I2C
+       default y
        select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
        select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
        select MEDIA_TUNER_XC4000 if !MEDIA_TUNER_CUSTOMISE
        select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMISE
        select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
-       select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMISE && EXPERIMENTAL
-       select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMISE
+       select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMISE && MEDIA_RADIO_SUPPORT && EXPERIMENTAL
+       select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMISE && MEDIA_RADIO_SUPPORT
        select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
        select MEDIA_TUNER_TDA9887 if !MEDIA_TUNER_CUSTOMISE
        select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE
@@ -47,10 +48,11 @@ config MEDIA_TUNER_CUSTOMISE
 
 menu "Customize TV tuners"
        visible if MEDIA_TUNER_CUSTOMISE
+       depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT
 
 config MEDIA_TUNER_SIMPLE
        tristate "Simple tuner support"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        select MEDIA_TUNER_TDA9887
        default m if MEDIA_TUNER_CUSTOMISE
        help
@@ -58,7 +60,7 @@ config MEDIA_TUNER_SIMPLE
 
 config MEDIA_TUNER_TDA8290
        tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        select MEDIA_TUNER_TDA827X
        select MEDIA_TUNER_TDA18271
        default m if MEDIA_TUNER_CUSTOMISE
@@ -67,21 +69,21 @@ config MEDIA_TUNER_TDA8290
 
 config MEDIA_TUNER_TDA827X
        tristate "Philips TDA827X silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          A DVB-T silicon tuner module. Say Y when you want to support this tuner.
 
 config MEDIA_TUNER_TDA18271
        tristate "NXP TDA18271 silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          A silicon tuner module. Say Y when you want to support this tuner.
 
 config MEDIA_TUNER_TDA9887
        tristate "TDA 9885/6/7 analog IF demodulator"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          Say Y here to include support for Philips TDA9885/6/7
@@ -89,7 +91,7 @@ config MEDIA_TUNER_TDA9887
 
 config MEDIA_TUNER_TEA5761
        tristate "TEA 5761 radio tuner (EXPERIMENTAL)"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        depends on EXPERIMENTAL
        default m if MEDIA_TUNER_CUSTOMISE
        help
@@ -97,63 +99,63 @@ config MEDIA_TUNER_TEA5761
 
 config MEDIA_TUNER_TEA5767
        tristate "TEA 5767 radio tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          Say Y here to include support for the Philips TEA5767 radio tuner.
 
 config MEDIA_TUNER_MT20XX
        tristate "Microtune 2032 / 2050 tuners"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          Say Y here to include support for the MT2032 / MT2050 tuner.
 
 config MEDIA_TUNER_MT2060
        tristate "Microtune MT2060 silicon IF tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          A driver for the silicon IF tuner MT2060 from Microtune.
 
 config MEDIA_TUNER_MT2063
        tristate "Microtune MT2063 silicon IF tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          A driver for the silicon IF tuner MT2063 from Microtune.
 
 config MEDIA_TUNER_MT2266
        tristate "Microtune MT2266 silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          A driver for the silicon baseband tuner MT2266 from Microtune.
 
 config MEDIA_TUNER_MT2131
        tristate "Microtune MT2131 silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          A driver for the silicon baseband tuner MT2131 from Microtune.
 
 config MEDIA_TUNER_QT1010
        tristate "Quantek QT1010 silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          A driver for the silicon tuner QT1010 from Quantek.
 
 config MEDIA_TUNER_XC2028
        tristate "XCeive xc2028/xc3028 tuners"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          Say Y here to include support for the xc2028/xc3028 tuners.
 
 config MEDIA_TUNER_XC5000
        tristate "Xceive XC5000 silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          A driver for the silicon tuner XC5000 from Xceive.
@@ -162,7 +164,7 @@ config MEDIA_TUNER_XC5000
 
 config MEDIA_TUNER_XC4000
        tristate "Xceive XC4000 silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          A driver for the silicon tuner XC4000 from Xceive.
@@ -171,70 +173,70 @@ config MEDIA_TUNER_XC4000
 
 config MEDIA_TUNER_MXL5005S
        tristate "MaxLinear MSL5005S silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          A driver for the silicon tuner MXL5005S from MaxLinear.
 
 config MEDIA_TUNER_MXL5007T
        tristate "MaxLinear MxL5007T silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          A driver for the silicon tuner MxL5007T from MaxLinear.
 
 config MEDIA_TUNER_MC44S803
        tristate "Freescale MC44S803 Low Power CMOS Broadband tuners"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        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
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          A driver for the silicon tuner MAX2165 from Maxim.
 
 config MEDIA_TUNER_TDA18218
        tristate "NXP TDA18218 silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          NXP TDA18218 silicon tuner driver.
 
 config MEDIA_TUNER_FC0011
        tristate "Fitipower FC0011 silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          Fitipower FC0011 silicon tuner driver.
 
 config MEDIA_TUNER_FC0012
        tristate "Fitipower FC0012 silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          Fitipower FC0012 silicon tuner driver.
 
 config MEDIA_TUNER_FC0013
        tristate "Fitipower FC0013 silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          Fitipower FC0013 silicon tuner driver.
 
 config MEDIA_TUNER_TDA18212
        tristate "NXP TDA18212 silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          NXP TDA18212 silicon tuner driver.
 
 config MEDIA_TUNER_TUA9001
        tristate "Infineon TUA 9001 silicon tuner"
-       depends on VIDEO_MEDIA && I2C
+       depends on MEDIA_SUPPORT && I2C
        default m if MEDIA_TUNER_CUSTOMISE
        help
          Infineon TUA 9001 silicon tuner driver.
index b5ee3ebfcfca03e2f9fd5973cd5d72b52d1c5f53..ea0550eafe7d185f9d547a80af33cead4d007050 100644 (file)
@@ -90,11 +90,22 @@ struct firmware_properties {
        int             scode_nr;
 };
 
+enum xc2028_state {
+       XC2028_NO_FIRMWARE = 0,
+       XC2028_WAITING_FIRMWARE,
+       XC2028_ACTIVE,
+       XC2028_SLEEP,
+       XC2028_NODEV,
+};
+
 struct xc2028_data {
        struct list_head        hybrid_tuner_instance_list;
        struct tuner_i2c_props  i2c_props;
        __u32                   frequency;
 
+       enum xc2028_state       state;
+       const char              *fname;
+
        struct firmware_description *firm;
        int                     firm_size;
        __u16                   firm_version;
@@ -255,6 +266,21 @@ static  v4l2_std_id parse_audio_std_option(void)
        return 0;
 }
 
+static int check_device_status(struct xc2028_data *priv)
+{
+       switch (priv->state) {
+       case XC2028_NO_FIRMWARE:
+       case XC2028_WAITING_FIRMWARE:
+               return -EAGAIN;
+       case XC2028_ACTIVE:
+       case XC2028_SLEEP:
+               return 0;
+       case XC2028_NODEV:
+               return -ENODEV;
+       }
+       return 0;
+}
+
 static void free_firmware(struct xc2028_data *priv)
 {
        int i;
@@ -270,45 +296,28 @@ static void free_firmware(struct xc2028_data *priv)
 
        priv->firm = NULL;
        priv->firm_size = 0;
+       priv->state = XC2028_NO_FIRMWARE;
 
        memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
 }
 
-static int load_all_firmwares(struct dvb_frontend *fe)
+static int load_all_firmwares(struct dvb_frontend *fe,
+                             const struct firmware *fw)
 {
        struct xc2028_data    *priv = fe->tuner_priv;
-       const struct firmware *fw   = NULL;
        const unsigned char   *p, *endp;
        int                   rc = 0;
        int                   n, n_array;
        char                  name[33];
-       char                  *fname;
 
        tuner_dbg("%s called\n", __func__);
 
-       if (!firmware_name[0])
-               fname = priv->ctrl.fname;
-       else
-               fname = firmware_name;
-
-       tuner_dbg("Reading firmware %s\n", fname);
-       rc = request_firmware(&fw, fname, priv->i2c_props.adap->dev.parent);
-       if (rc < 0) {
-               if (rc == -ENOENT)
-                       tuner_err("Error: firmware %s not found.\n",
-                                  fname);
-               else
-                       tuner_err("Error %d while requesting firmware %s \n",
-                                  rc, fname);
-
-               return rc;
-       }
        p = fw->data;
        endp = p + fw->size;
 
        if (fw->size < sizeof(name) - 1 + 2 + 2) {
                tuner_err("Error: firmware file %s has invalid size!\n",
-                         fname);
+                         priv->fname);
                goto corrupt;
        }
 
@@ -323,7 +332,7 @@ static int load_all_firmwares(struct dvb_frontend *fe)
        p += 2;
 
        tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n",
-                  n_array, fname, name,
+                  n_array, priv->fname, name,
                   priv->firm_version >> 8, priv->firm_version & 0xff);
 
        priv->firm = kcalloc(n_array, sizeof(*priv->firm), GFP_KERNEL);
@@ -417,9 +426,10 @@ err:
        free_firmware(priv);
 
 done:
-       release_firmware(fw);
        if (rc == 0)
                tuner_dbg("Firmware files loaded.\n");
+       else
+               priv->state = XC2028_NODEV;
 
        return rc;
 }
@@ -707,22 +717,15 @@ static int check_firmware(struct dvb_frontend *fe, unsigned int type,
 {
        struct xc2028_data         *priv = fe->tuner_priv;
        struct firmware_properties new_fw;
-       int                        rc = 0, retry_count = 0;
+       int                        rc, retry_count = 0;
        u16                        version, hwmodel;
        v4l2_std_id                std0;
 
        tuner_dbg("%s called\n", __func__);
 
-       if (!priv->firm) {
-               if (!priv->ctrl.fname) {
-                       tuner_info("xc2028/3028 firmware name not set!\n");
-                       return -EINVAL;
-               }
-
-               rc = load_all_firmwares(fe);
-               if (rc < 0)
-                       return rc;
-       }
+       rc = check_device_status(priv);
+       if (rc < 0)
+               return rc;
 
        if (priv->ctrl.mts && !(type & FM))
                type |= MTS;
@@ -749,9 +752,13 @@ retry:
                printk("scode_nr %d\n", new_fw.scode_nr);
        }
 
-       /* No need to reload base firmware if it matches */
-       if (((BASE | new_fw.type) & BASE_TYPES) ==
-           (priv->cur_fw.type & BASE_TYPES)) {
+       /*
+        * No need to reload base firmware if it matches and if the tuner
+        * is not at sleep mode
+        */
+       if ((priv->state == XC2028_ACTIVE) &&
+           (((BASE | new_fw.type) & BASE_TYPES) ==
+           (priv->cur_fw.type & BASE_TYPES))) {
                tuner_dbg("BASE firmware not changed.\n");
                goto skip_base;
        }
@@ -872,10 +879,13 @@ read_not_reliable:
         * 2. Tell whether BASE firmware was just changed the next time through.
         */
        priv->cur_fw.type |= BASE;
+       priv->state = XC2028_ACTIVE;
 
        return 0;
 
 fail:
+       priv->state = XC2028_SLEEP;
+
        memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
        if (retry_count < 8) {
                msleep(50);
@@ -893,28 +903,39 @@ static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
 {
        struct xc2028_data *priv = fe->tuner_priv;
        u16                 frq_lock, signal = 0;
-       int                 rc;
+       int                 rc, i;
 
        tuner_dbg("%s called\n", __func__);
 
+       rc = check_device_status(priv);
+       if (rc < 0)
+               return rc;
+
        mutex_lock(&priv->lock);
 
        /* Sync Lock Indicator */
-       rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
-       if (rc < 0)
-               goto ret;
+       for (i = 0; i < 3; i++) {
+               rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
+               if (rc < 0)
+                       goto ret;
 
-       /* Frequency is locked */
-       if (frq_lock == 1)
-               signal = 1 << 11;
+               if (frq_lock)
+                       break;
+               msleep(6);
+       }
+
+       /* Frequency didn't lock */
+       if (frq_lock == 2)
+               goto ret;
 
        /* Get SNR of the video signal */
        rc = xc2028_get_reg(priv, XREG_SNR, &signal);
        if (rc < 0)
                goto ret;
 
-       /* Use both frq_lock and signal to generate the result */
-       signal = signal || ((signal & 0x07) << 12);
+       /* Signal level is 3 bits only */
+
+       signal = ((1 << 12) - 1) | ((signal & 0x07) << 12);
 
 ret:
        mutex_unlock(&priv->lock);
@@ -926,6 +947,49 @@ ret:
        return rc;
 }
 
+static int xc2028_get_afc(struct dvb_frontend *fe, s32 *afc)
+{
+       struct xc2028_data *priv = fe->tuner_priv;
+       int i, rc;
+       u16 frq_lock = 0;
+       s16 afc_reg = 0;
+
+       rc = check_device_status(priv);
+       if (rc < 0)
+               return rc;
+
+       mutex_lock(&priv->lock);
+
+       /* Sync Lock Indicator */
+       for (i = 0; i < 3; i++) {
+               rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
+               if (rc < 0)
+                       goto ret;
+
+               if (frq_lock)
+                       break;
+               msleep(6);
+       }
+
+       /* Frequency didn't lock */
+       if (frq_lock == 2)
+               goto ret;
+
+       /* Get AFC */
+       rc = xc2028_get_reg(priv, XREG_FREQ_ERROR, &afc_reg);
+       if (rc < 0)
+               goto ret;
+
+       *afc = afc_reg * 15625; /* Hz */
+
+       tuner_dbg("AFC is %d Hz\n", *afc);
+
+ret:
+       mutex_unlock(&priv->lock);
+
+       return rc;
+}
+
 #define DIV 15625
 
 static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
@@ -1111,11 +1175,16 @@ static int xc2028_set_params(struct dvb_frontend *fe)
        u32 delsys = c->delivery_system;
        u32 bw = c->bandwidth_hz;
        struct xc2028_data *priv = fe->tuner_priv;
-       unsigned int       type=0;
+       int rc;
+       unsigned int       type = 0;
        u16                demod = 0;
 
        tuner_dbg("%s called\n", __func__);
 
+       rc = check_device_status(priv);
+       if (rc < 0)
+               return rc;
+
        switch (delsys) {
        case SYS_DVBT:
        case SYS_DVBT2:
@@ -1201,7 +1270,11 @@ static int xc2028_set_params(struct dvb_frontend *fe)
 static int xc2028_sleep(struct dvb_frontend *fe)
 {
        struct xc2028_data *priv = fe->tuner_priv;
-       int rc = 0;
+       int rc;
+
+       rc = check_device_status(priv);
+       if (rc < 0)
+               return rc;
 
        /* Avoid firmware reload on slow devices or if PM disabled */
        if (no_poweroff || priv->ctrl.disable_power_mgmt)
@@ -1220,7 +1293,7 @@ static int xc2028_sleep(struct dvb_frontend *fe)
        else
                rc = send_seq(priv, {0x80, XREG_POWER_DOWN, 0x00, 0x00});
 
-       priv->cur_fw.type = 0;  /* need firmware reload */
+       priv->state = XC2028_SLEEP;
 
        mutex_unlock(&priv->lock);
 
@@ -1237,8 +1310,9 @@ static int xc2028_dvb_release(struct dvb_frontend *fe)
 
        /* only perform final cleanup if this is the last instance */
        if (hybrid_tuner_report_instance_count(priv) == 1) {
-               kfree(priv->ctrl.fname);
                free_firmware(priv);
+               kfree(priv->ctrl.fname);
+               priv->ctrl.fname = NULL;
        }
 
        if (priv)
@@ -1254,14 +1328,42 @@ static int xc2028_dvb_release(struct dvb_frontend *fe)
 static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
 {
        struct xc2028_data *priv = fe->tuner_priv;
+       int rc;
 
        tuner_dbg("%s called\n", __func__);
 
+       rc = check_device_status(priv);
+       if (rc < 0)
+               return rc;
+
        *frequency = priv->frequency;
 
        return 0;
 }
 
+static void load_firmware_cb(const struct firmware *fw,
+                            void *context)
+{
+       struct dvb_frontend *fe = context;
+       struct xc2028_data *priv = fe->tuner_priv;
+       int rc;
+
+       tuner_dbg("request_firmware_nowait(): %s\n", fw ? "OK" : "error");
+       if (!fw) {
+               tuner_err("Could not load firmware %s.\n", priv->fname);
+               priv->state = XC2028_NODEV;
+               return;
+       }
+
+       rc = load_all_firmwares(fe, fw);
+
+       release_firmware(fw);
+
+       if (rc < 0)
+               return;
+       priv->state = XC2028_SLEEP;
+}
+
 static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
 {
        struct xc2028_data *priv = fe->tuner_priv;
@@ -1272,21 +1374,49 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
 
        mutex_lock(&priv->lock);
 
+       /*
+        * Copy the config data.
+        * For the firmware name, keep a local copy of the string,
+        * in order to avoid troubles during device release.
+        */
+       if (priv->ctrl.fname)
+               kfree(priv->ctrl.fname);
        memcpy(&priv->ctrl, p, sizeof(priv->ctrl));
-       if (priv->ctrl.max_len < 9)
-               priv->ctrl.max_len = 13;
-
        if (p->fname) {
-               if (priv->ctrl.fname && strcmp(p->fname, priv->ctrl.fname)) {
-                       kfree(priv->ctrl.fname);
-                       free_firmware(priv);
-               }
-
                priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL);
                if (priv->ctrl.fname == NULL)
                        rc = -ENOMEM;
        }
 
+       /*
+        * If firmware name changed, frees firmware. As free_firmware will
+        * reset the status to NO_FIRMWARE, this forces a new request_firmware
+        */
+       if (!firmware_name[0] && p->fname &&
+           priv->fname && strcmp(p->fname, priv->fname))
+               free_firmware(priv);
+
+       if (priv->ctrl.max_len < 9)
+               priv->ctrl.max_len = 13;
+
+       if (priv->state == XC2028_NO_FIRMWARE) {
+               if (!firmware_name[0])
+                       priv->fname = priv->ctrl.fname;
+               else
+                       priv->fname = firmware_name;
+
+               rc = request_firmware_nowait(THIS_MODULE, 1,
+                                            priv->fname,
+                                            priv->i2c_props.adap->dev.parent,
+                                            GFP_KERNEL,
+                                            fe, load_firmware_cb);
+               if (rc < 0) {
+                       tuner_err("Failed to request firmware %s\n",
+                                 priv->fname);
+                       priv->state = XC2028_NODEV;
+               }
+               priv->state = XC2028_WAITING_FIRMWARE;
+       }
        mutex_unlock(&priv->lock);
 
        return rc;
@@ -1305,6 +1435,7 @@ static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
        .release           = xc2028_dvb_release,
        .get_frequency     = xc2028_get_frequency,
        .get_rf_strength   = xc2028_signal,
+       .get_afc           = xc2028_get_afc,
        .set_params        = xc2028_set_params,
        .sleep             = xc2028_sleep,
 };
@@ -1375,3 +1506,5 @@ MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
 MODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");
 MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
 MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE);
+MODULE_FIRMWARE(XC3028L_DEFAULT_FIRMWARE);
index dcca42ca57bef312cb84fb059f88a315da385092..362a8d7c9738e018df797ed7504c215b4595d0de 100644 (file)
@@ -210,13 +210,15 @@ struct xc5000_fw_cfg {
        u16 size;
 };
 
+#define XC5000A_FIRMWARE "dvb-fe-xc5000-1.6.114.fw"
 static const struct xc5000_fw_cfg xc5000a_1_6_114 = {
-       .name = "dvb-fe-xc5000-1.6.114.fw",
+       .name = XC5000A_FIRMWARE,
        .size = 12401,
 };
 
+#define XC5000C_FIRMWARE "dvb-fe-xc5000c-41.024.5.fw"
 static const struct xc5000_fw_cfg xc5000c_41_024_5 = {
-       .name = "dvb-fe-xc5000c-41.024.5.fw",
+       .name = XC5000C_FIRMWARE,
        .size = 16497,
 };
 
@@ -717,6 +719,12 @@ static int xc5000_set_params(struct dvb_frontend *fe)
                priv->freq_hz = freq - 1750000;
                priv->video_standard = DTV6;
                break;
+       case SYS_ISDBT:
+               /* All ISDB-T are currently for 6 MHz bw */
+               if (!bw)
+                       bw = 6000000;
+               /* fall to OFDM handling */
+       case SYS_DMBTH:
        case SYS_DVBT:
        case SYS_DVBT2:
                dprintk(1, "%s() OFDM\n", __func__);
@@ -1253,3 +1261,5 @@ EXPORT_SYMBOL(xc5000_attach);
 MODULE_AUTHOR("Steven Toth");
 MODULE_DESCRIPTION("Xceive xc5000 silicon tuner driver");
 MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(XC5000A_FIRMWARE);
+MODULE_FIRMWARE(XC5000C_FIRMWARE);
index 131b938e9e8178384ab90fa206165c2d20861582..ebf3f05839d2c5416488b77df70c825dc01ae467 100644 (file)
@@ -578,6 +578,7 @@ static int demod_attach_drxk(struct ddb_input *input)
 
        memset(&config, 0, sizeof(config));
        config.microcode_name = "drxk_a3.mc";
+       config.qam_demod_parameter_count = 4;
        config.adr = 0x29 + (input->nr & 1);
 
        fe = input->fe = dvb_attach(drxk_attach, &config, i2c);
index e929d5697b8799bb8597d09dd620dc510dd282dc..7c64c09103a94d1e15f7b57c32ae21da4720e716 100644 (file)
@@ -220,6 +220,7 @@ struct dvb_tuner_ops {
 #define TUNER_STATUS_STEREO 2
        int (*get_status)(struct dvb_frontend *fe, u32 *status);
        int (*get_rf_strength)(struct dvb_frontend *fe, u16 *strength);
+       int (*get_afc)(struct dvb_frontend *fe, s32 *afc);
 
        /** These are provided separately from set_params in order to facilitate silicon
         * tuners which require sophisticated tuning loops, controlling each parameter separately. */
index a26949336b3d310e1b78b48ada7e902f2f84e7fb..c2161565023a4682d0ba77a5e5b681491b8a31c4 100644 (file)
@@ -418,9 +418,12 @@ config DVB_USB_RTL28XXU
        tristate "Realtek RTL28xxU DVB USB support"
        depends on DVB_USB && EXPERIMENTAL
        select DVB_RTL2830
+       select DVB_RTL2832
        select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
        select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
        select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
+       select MEDIA_TUNER_FC0012 if !MEDIA_TUNER_CUSTOMISE
+       select MEDIA_TUNER_FC0013 if !MEDIA_TUNER_CUSTOMISE
        help
          Say Y here to support the Realtek RTL28xxU DVB USB receiver.
 
index 4008b9c50fbdfa3a9b61853d2a1a7bf86be90ae6..86861e6f86d23053ebe990aa58b24e7d377d7d45 100644 (file)
@@ -590,12 +590,10 @@ static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6])
        int ret;
 
        ret = az6007_read(d, AZ6007_READ_DATA, 6, 0, st->data, 6);
-       memcpy(mac, st->data, sizeof(mac));
+       memcpy(mac, st->data, 6);
 
        if (ret > 0)
-               deb_info("%s: mac is %02x:%02x:%02x:%02x:%02x:%02x\n",
-                        __func__, mac[0], mac[1], mac[2],
-                        mac[3], mac[4], mac[5]);
+               deb_info("%s: mac is %pM\n", __func__, mac);
 
        return ret;
 }
index 7a6160bf54baeaf652aa86b8156d48fc326279ee..26c44818a5abf23d96f344fd0e83de75b20a47b4 100644 (file)
 #define USB_PID_CONCEPTRONIC_CTVDIGRCU                 0xe397
 #define USB_PID_CONEXANT_D680_DMB                      0x86d6
 #define USB_PID_CREATIX_CTX1921                                0x1921
+#define USB_PID_DELOCK_USB2_DVBT                       0xb803
 #define USB_PID_DIBCOM_HOOK_DEFAULT                    0x0064
 #define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM             0x0065
 #define USB_PID_DIBCOM_MOD3000_COLD                    0x0bb8
 #define USB_PID_TERRATEC_CINERGY_T_STICK               0x0093
 #define USB_PID_TERRATEC_CINERGY_T_STICK_RC            0x0097
 #define USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC       0x0099
+#define USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1    0x00a9
 #define USB_PID_TWINHAN_VP7041_COLD                    0x3201
 #define USB_PID_TWINHAN_VP7041_WARM                    0x3202
 #define USB_PID_TWINHAN_VP7020_COLD                    0x3203
 #define USB_PID_TERRATEC_H7_2                          0x10a3
 #define USB_PID_TERRATEC_T3                            0x10a0
 #define USB_PID_TERRATEC_T5                            0x10a1
+#define USB_PID_NOXON_DAB_STICK                                0x00b3
 #define USB_PID_PINNACLE_EXPRESSCARD_320CX             0x022e
 #define USB_PID_PINNACLE_PCTV2000E                     0x022c
 #define USB_PID_PINNACLE_PCTV_DVB_T_FLASH              0x0228
index 41e1f5537f44b5b67d31140f52fec2f9cc25224b..6bd0bd792437d38c2fd769470ddaa3a943f49016 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
  * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@googlemail.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
 #include "rtl28xxu.h"
 
 #include "rtl2830.h"
+#include "rtl2832.h"
 
 #include "qt1010.h"
 #include "mt2060.h"
 #include "mxl5005s.h"
+#include "fc0012.h"
+#include "fc0013.h"
 
 /* debug */
 static int dvb_usb_rtl28xxu_debug;
@@ -80,7 +84,7 @@ err:
        return ret;
 }
 
-static int rtl2831_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
+static int rtl28xx_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
 {
        struct rtl28xxu_req req;
 
@@ -116,12 +120,12 @@ static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
        return rtl28xxu_ctrl_msg(d, &req);
 }
 
-static int rtl2831_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val)
+static int rtl28xx_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val)
 {
-       return rtl2831_wr_regs(d, reg, &val, 1);
+       return rtl28xx_wr_regs(d, reg, &val, 1);
 }
 
-static int rtl2831_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
+static int rtl28xx_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
 {
        return rtl2831_rd_regs(d, reg, val, 1);
 }
@@ -308,12 +312,12 @@ static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap)
         */
 
        /* GPIO direction */
-       ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
+       ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
        if (ret)
                goto err;
 
        /* enable as output GPIO0, GPIO2, GPIO4 */
-       ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
+       ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
        if (ret)
                goto err;
 
@@ -381,34 +385,159 @@ err:
        return ret;
 }
 
+static struct rtl2832_config rtl28xxu_rtl2832_fc0012_config = {
+       .i2c_addr = 0x10, /* 0x20 */
+       .xtal = 28800000,
+       .if_dvbt = 0,
+       .tuner = TUNER_RTL2832_FC0012
+};
+
+static struct rtl2832_config rtl28xxu_rtl2832_fc0013_config = {
+       .i2c_addr = 0x10, /* 0x20 */
+       .xtal = 28800000,
+       .if_dvbt = 0,
+       .tuner = TUNER_RTL2832_FC0013
+};
+
+static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d,
+               int cmd, int arg)
+{
+       int ret;
+       u8 val;
+
+       deb_info("%s cmd=%d arg=%d\n", __func__, cmd, arg);
+       switch (cmd) {
+       case FC_FE_CALLBACK_VHF_ENABLE:
+               /* set output values */
+               ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
+               if (ret)
+                       goto err;
+
+               if (arg)
+                       val &= 0xbf; /* set GPIO6 low */
+               else
+                       val |= 0x40; /* set GPIO6 high */
+
+
+               ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
+               if (ret)
+                       goto err;
+               break;
+       default:
+               ret = -EINVAL;
+               goto err;
+       }
+       return 0;
+
+err:
+       err("%s: failed=%d\n", __func__, ret);
+
+       return ret;
+}
+
+
+static int rtl2832u_fc0013_tuner_callback(struct dvb_usb_device *d,
+               int cmd, int arg)
+{
+       /* TODO implement*/
+       return 0;
+}
+
+static int rtl2832u_tuner_callback(struct dvb_usb_device *d, int cmd, int arg)
+{
+       struct rtl28xxu_priv *priv = d->priv;
+
+       switch (priv->tuner) {
+       case TUNER_RTL2832_FC0012:
+               return rtl2832u_fc0012_tuner_callback(d, cmd, arg);
+
+       case TUNER_RTL2832_FC0013:
+               return rtl2832u_fc0013_tuner_callback(d, cmd, arg);
+       default:
+               break;
+       }
+
+       return -ENODEV;
+}
+
+static int rtl2832u_frontend_callback(void *adapter_priv, int component,
+                                   int cmd, int arg)
+{
+       struct i2c_adapter *adap = adapter_priv;
+       struct dvb_usb_device *d = i2c_get_adapdata(adap);
+
+       switch (component) {
+       case DVB_FRONTEND_COMPONENT_TUNER:
+               return rtl2832u_tuner_callback(d, cmd, arg);
+       default:
+               break;
+       }
+
+       return -EINVAL;
+}
+
+
+
+
 static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
 {
        int ret;
        struct rtl28xxu_priv *priv = adap->dev->priv;
-       u8 buf[1];
+       struct rtl2832_config *rtl2832_config;
+
+       u8 buf[2], val;
        /* open RTL2832U/RTL2832 I2C gate */
        struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x18"};
        /* close RTL2832U/RTL2832 I2C gate */
        struct rtl28xxu_req req_gate_close = {0x0120, 0x0011, 0x0001, "\x10"};
+       /* for FC0012 tuner probe */
+       struct rtl28xxu_req req_fc0012 = {0x00c6, CMD_I2C_RD, 1, buf};
+       /* for FC0013 tuner probe */
+       struct rtl28xxu_req req_fc0013 = {0x00c6, CMD_I2C_RD, 1, buf};
+       /* for MT2266 tuner probe */
+       struct rtl28xxu_req req_mt2266 = {0x00c0, CMD_I2C_RD, 1, buf};
        /* for FC2580 tuner probe */
        struct rtl28xxu_req req_fc2580 = {0x01ac, CMD_I2C_RD, 1, buf};
+       /* for MT2063 tuner probe */
+       struct rtl28xxu_req req_mt2063 = {0x00c0, CMD_I2C_RD, 1, buf};
+       /* for MAX3543 tuner probe */
+       struct rtl28xxu_req req_max3543 = {0x00c0, CMD_I2C_RD, 1, buf};
+       /* for TUA9001 tuner probe */
+       struct rtl28xxu_req req_tua9001 = {0x7ec0, CMD_I2C_RD, 2, buf};
+       /* for MXL5007T tuner probe */
+       struct rtl28xxu_req req_mxl5007t = {0xd9c0, CMD_I2C_RD, 1, buf};
+       /* for E4000 tuner probe */
+       struct rtl28xxu_req req_e4000 = {0x02c8, CMD_I2C_RD, 1, buf};
+       /* for TDA18272 tuner probe */
+       struct rtl28xxu_req req_tda18272 = {0x00c0, CMD_I2C_RD, 2, buf};
 
        deb_info("%s:\n", __func__);
 
-       /* GPIO direction */
-       ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
+
+       ret = rtl28xx_rd_reg(adap->dev, SYS_GPIO_DIR, &val);
        if (ret)
                goto err;
 
-       /* enable as output GPIO0, GPIO2, GPIO4 */
-       ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
+       val &= 0xbf;
+
+       ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_DIR, val);
        if (ret)
                goto err;
 
-       ret = rtl2831_wr_reg(adap->dev, SYS_DEMOD_CTL, 0xe8);
+
+       /* enable as output GPIO3 and GPIO6*/
+       ret = rtl28xx_rd_reg(adap->dev, SYS_GPIO_OUT_EN, &val);
        if (ret)
                goto err;
 
+       val |= 0x48;
+
+       ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_OUT_EN, val);
+       if (ret)
+               goto err;
+
+
+
        /*
         * Probe used tuner. We need to know used tuner before demod attach
         * since there is some demod params needed to set according to tuner.
@@ -419,25 +548,108 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
        if (ret)
                goto err;
 
+       priv->tuner = TUNER_NONE;
+
+       /* check FC0012 ID register; reg=00 val=a1 */
+       ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc0012);
+       if (ret == 0 && buf[0] == 0xa1) {
+               priv->tuner = TUNER_RTL2832_FC0012;
+               rtl2832_config = &rtl28xxu_rtl2832_fc0012_config;
+               info("%s: FC0012 tuner found", __func__);
+               goto found;
+       }
+
+       /* check FC0013 ID register; reg=00 val=a3 */
+       ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc0013);
+       if (ret == 0 && buf[0] == 0xa3) {
+               priv->tuner = TUNER_RTL2832_FC0013;
+               rtl2832_config = &rtl28xxu_rtl2832_fc0013_config;
+               info("%s: FC0013 tuner found", __func__);
+               goto found;
+       }
+
+       /* check MT2266 ID register; reg=00 val=85 */
+       ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2266);
+       if (ret == 0 && buf[0] == 0x85) {
+               priv->tuner = TUNER_RTL2832_MT2266;
+               /* TODO implement tuner */
+               info("%s: MT2266 tuner found", __func__);
+               goto unsupported;
+       }
+
        /* check FC2580 ID register; reg=01 val=56 */
        ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc2580);
        if (ret == 0 && buf[0] == 0x56) {
                priv->tuner = TUNER_RTL2832_FC2580;
-               deb_info("%s: FC2580\n", __func__);
-               goto found;
-       } else {
-               deb_info("%s: FC2580 probe failed=%d - %02x\n",
-                       __func__, ret, buf[0]);
+               /* TODO implement tuner */
+               info("%s: FC2580 tuner found", __func__);
+               goto unsupported;
        }
 
+       /* check MT2063 ID register; reg=00 val=9e || 9c */
+       ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2063);
+       if (ret == 0 && (buf[0] == 0x9e || buf[0] == 0x9c)) {
+               priv->tuner = TUNER_RTL2832_MT2063;
+               /* TODO implement tuner */
+               info("%s: MT2063 tuner found", __func__);
+               goto unsupported;
+       }
+
+       /* check MAX3543 ID register; reg=00 val=38 */
+       ret = rtl28xxu_ctrl_msg(adap->dev, &req_max3543);
+       if (ret == 0 && buf[0] == 0x38) {
+               priv->tuner = TUNER_RTL2832_MAX3543;
+               /* TODO implement tuner */
+               info("%s: MAX3534 tuner found", __func__);
+               goto unsupported;
+       }
+
+       /* check TUA9001 ID register; reg=7e val=2328 */
+       ret = rtl28xxu_ctrl_msg(adap->dev, &req_tua9001);
+       if (ret == 0 && buf[0] == 0x23 && buf[1] == 0x28) {
+               priv->tuner = TUNER_RTL2832_TUA9001;
+               /* TODO implement tuner */
+               info("%s: TUA9001 tuner found", __func__);
+               goto unsupported;
+       }
+
+       /* check MXL5007R ID register; reg=d9 val=14 */
+       ret = rtl28xxu_ctrl_msg(adap->dev, &req_mxl5007t);
+       if (ret == 0 && buf[0] == 0x14) {
+               priv->tuner = TUNER_RTL2832_MXL5007T;
+               /* TODO implement tuner */
+               info("%s: MXL5007T tuner found", __func__);
+               goto unsupported;
+       }
+
+       /* check E4000 ID register; reg=02 val=40 */
+       ret = rtl28xxu_ctrl_msg(adap->dev, &req_e4000);
+       if (ret == 0 && buf[0] == 0x40) {
+               priv->tuner = TUNER_RTL2832_E4000;
+               /* TODO implement tuner */
+               info("%s: E4000 tuner found", __func__);
+               goto unsupported;
+       }
+
+       /* check TDA18272 ID register; reg=00 val=c760  */
+       ret = rtl28xxu_ctrl_msg(adap->dev, &req_tda18272);
+       if (ret == 0 && (buf[0] == 0xc7 || buf[1] == 0x60)) {
+               priv->tuner = TUNER_RTL2832_TDA18272;
+               /* TODO implement tuner */
+               info("%s: TDA18272 tuner found", __func__);
+               goto unsupported;
+       }
+
+unsupported:
        /* close demod I2C gate */
        ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close);
        if (ret)
                goto err;
 
        /* tuner not found */
+       deb_info("No compatible tuner found");
        ret = -ENODEV;
-       goto err;
+       return ret;
 
 found:
        /* close demod I2C gate */
@@ -446,9 +658,18 @@ found:
                goto err;
 
        /* attach demodulator */
-       /* TODO: */
+       adap->fe_adap[0].fe = dvb_attach(rtl2832_attach, rtl2832_config,
+               &adap->dev->i2c_adap);
+               if (adap->fe_adap[0].fe == NULL) {
+                       ret = -ENODEV;
+                       goto err;
+               }
+
+       /* set fe callbacks */
+       adap->fe_adap[0].fe->callback = rtl2832u_frontend_callback;
 
        return ret;
+
 err:
        deb_info("%s: failed=%d\n", __func__, ret);
        return ret;
@@ -531,10 +752,24 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
        deb_info("%s:\n", __func__);
 
        switch (priv->tuner) {
-       case TUNER_RTL2832_FC2580:
-               /* TODO: */
-               fe = NULL;
+       case TUNER_RTL2832_FC0012:
+               fe = dvb_attach(fc0012_attach, adap->fe_adap[0].fe,
+                       &adap->dev->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ);
+
+               /* since fc0012 includs reading the signal strength delegate
+                * that to the tuner driver */
+               adap->fe_adap[0].fe->ops.read_signal_strength = adap->fe_adap[0].
+                               fe->ops.tuner_ops.get_rf_strength;
+               return 0;
                break;
+       case TUNER_RTL2832_FC0013:
+               fe = dvb_attach(fc0013_attach, adap->fe_adap[0].fe,
+                       &adap->dev->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ);
+
+               /* fc0013 also supports signal strength reading */
+               adap->fe_adap[0].fe->ops.read_signal_strength = adap->fe_adap[0]
+                       .fe->ops.tuner_ops.get_rf_strength;
+               return 0;
        default:
                fe = NULL;
                err("unknown tuner=%d", priv->tuner);
@@ -551,14 +786,14 @@ err:
        return ret;
 }
 
-static int rtl28xxu_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
+static int rtl2831u_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
 {
        int ret;
        u8 buf[2], gpio;
 
        deb_info("%s: onoff=%d\n", __func__, onoff);
 
-       ret = rtl2831_rd_reg(adap->dev, SYS_GPIO_OUT_VAL, &gpio);
+       ret = rtl28xx_rd_reg(adap->dev, SYS_GPIO_OUT_VAL, &gpio);
        if (ret)
                goto err;
 
@@ -572,11 +807,37 @@ static int rtl28xxu_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
                gpio &= (~0x04); /* LED off */
        }
 
-       ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_VAL, gpio);
+       ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_OUT_VAL, gpio);
        if (ret)
                goto err;
 
-       ret = rtl2831_wr_regs(adap->dev, USB_EPA_CTL, buf, 2);
+       ret = rtl28xx_wr_regs(adap->dev, USB_EPA_CTL, buf, 2);
+       if (ret)
+               goto err;
+
+       return ret;
+err:
+       deb_info("%s: failed=%d\n", __func__, ret);
+       return ret;
+}
+
+static int rtl2832u_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
+{
+       int ret;
+       u8 buf[2];
+
+       deb_info("%s: onoff=%d\n", __func__, onoff);
+
+
+       if (onoff) {
+               buf[0] = 0x00;
+               buf[1] = 0x00;
+       } else {
+               buf[0] = 0x10; /* stall EPA */
+               buf[1] = 0x02; /* reset EPA */
+       }
+
+       ret = rtl28xx_wr_regs(adap->dev, USB_EPA_CTL, buf, 2);
        if (ret)
                goto err;
 
@@ -586,7 +847,7 @@ err:
        return ret;
 }
 
-static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff)
+static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff)
 {
        int ret;
        u8 gpio, sys0;
@@ -594,12 +855,12 @@ static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff)
        deb_info("%s: onoff=%d\n", __func__, onoff);
 
        /* demod adc */
-       ret = rtl2831_rd_reg(d, SYS_SYS0, &sys0);
+       ret = rtl28xx_rd_reg(d, SYS_SYS0, &sys0);
        if (ret)
                goto err;
 
        /* tuner power, read GPIOs */
-       ret = rtl2831_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio);
+       ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio);
        if (ret)
                goto err;
 
@@ -619,12 +880,12 @@ static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff)
        deb_info("%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio);
 
        /* demod adc */
-       ret = rtl2831_wr_reg(d, SYS_SYS0, sys0);
+       ret = rtl28xx_wr_reg(d, SYS_SYS0, sys0);
        if (ret)
                goto err;
 
        /* tuner power, write GPIOs */
-       ret = rtl2831_wr_reg(d, SYS_GPIO_OUT_VAL, gpio);
+       ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, gpio);
        if (ret)
                goto err;
 
@@ -634,6 +895,128 @@ err:
        return ret;
 }
 
+static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+       int ret;
+       u8 val;
+
+       deb_info("%s: onoff=%d\n", __func__, onoff);
+
+       if (onoff) {
+               /* set output values */
+               ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
+               if (ret)
+                       goto err;
+
+               val |= 0x08;
+               val &= 0xef;
+
+               ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
+               if (ret)
+                       goto err;
+
+               /* demod_ctl_1 */
+               ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val);
+               if (ret)
+                       goto err;
+
+               val &= 0xef;
+
+               ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL1, val);
+               if (ret)
+                       goto err;
+
+               /* demod control */
+               /* PLL enable */
+               ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
+               if (ret)
+                       goto err;
+
+               /* bit 7 to 1 */
+               val |= 0x80;
+
+               ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+               if (ret)
+                       goto err;
+
+               /* demod HW reset */
+               ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
+               if (ret)
+                       goto err;
+               /* bit 5 to 0 */
+               val &= 0xdf;
+
+               ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+               if (ret)
+                       goto err;
+
+               ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
+               if (ret)
+                       goto err;
+
+               val |= 0x20;
+
+               ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+               if (ret)
+                       goto err;
+
+               mdelay(5);
+
+               /*enable ADC_Q and ADC_I */
+               ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
+               if (ret)
+                       goto err;
+
+               val |= 0x48;
+
+               ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+               if (ret)
+                       goto err;
+
+
+       } else {
+               /* demod_ctl_1 */
+               ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val);
+               if (ret)
+                       goto err;
+
+               val |= 0x0c;
+
+               ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL1, val);
+               if (ret)
+                       goto err;
+
+               /* set output values */
+               ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
+               if (ret)
+                               goto err;
+
+               val |= 0x10;
+
+               ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
+               if (ret)
+                       goto err;
+
+               /* demod control */
+               ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
+               if (ret)
+                       goto err;
+
+               val &= 0x37;
+
+               ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+               if (ret)
+                       goto err;
+
+       }
+
+       return ret;
+err:
+       deb_info("%s: failed=%d\n", __func__, ret);
+       return ret;
+}
+
+
 static int rtl2831u_rc_query(struct dvb_usb_device *d)
 {
        int ret, i;
@@ -660,7 +1043,7 @@ static int rtl2831u_rc_query(struct dvb_usb_device *d)
        /* init remote controller */
        if (!priv->rc_active) {
                for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
-                       ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg,
+                       ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg,
                                        rc_nec_tab[i].val);
                        if (ret)
                                goto err;
@@ -690,12 +1073,12 @@ static int rtl2831u_rc_query(struct dvb_usb_device *d)
 
                rc_keydown(d->rc_dev, rc_code, 0);
 
-               ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1);
+               ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1);
                if (ret)
                        goto err;
 
                /* repeated intentionally to avoid extra keypress */
-               ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1);
+               ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1);
                if (ret)
                        goto err;
        }
@@ -732,7 +1115,7 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d)
        /* init remote controller */
        if (!priv->rc_active) {
                for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
-                       ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg,
+                       ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg,
                                        rc_nec_tab[i].val);
                        if (ret)
                                goto err;
@@ -740,14 +1123,14 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d)
                priv->rc_active = true;
        }
 
-       ret = rtl2831_rd_reg(d, IR_RX_IF, &buf[0]);
+       ret = rtl28xx_rd_reg(d, IR_RX_IF, &buf[0]);
        if (ret)
                goto err;
 
        if (buf[0] != 0x83)
                goto exit;
 
-       ret = rtl2831_rd_reg(d, IR_RX_BC, &buf[0]);
+       ret = rtl28xx_rd_reg(d, IR_RX_BC, &buf[0]);
        if (ret)
                goto err;
 
@@ -756,9 +1139,9 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d)
 
        /* TODO: pass raw IR to Kernel IR decoder */
 
-       ret = rtl2831_wr_reg(d, IR_RX_IF, 0x03);
-       ret = rtl2831_wr_reg(d, IR_RX_BUF_CTRL, 0x80);
-       ret = rtl2831_wr_reg(d, IR_RX_CTRL, 0x80);
+       ret = rtl28xx_wr_reg(d, IR_RX_IF, 0x03);
+       ret = rtl28xx_wr_reg(d, IR_RX_BUF_CTRL, 0x80);
+       ret = rtl28xx_wr_reg(d, IR_RX_CTRL, 0x80);
 
 exit:
        return ret;
@@ -771,6 +1154,9 @@ enum rtl28xxu_usb_table_entry {
        RTL2831U_0BDA_2831,
        RTL2831U_14AA_0160,
        RTL2831U_14AA_0161,
+       RTL2832U_0CCD_00A9,
+       RTL2832U_1F4D_B803,
+       RTL2832U_0CCD_00B3,
 };
 
 static struct usb_device_id rtl28xxu_table[] = {
@@ -783,6 +1169,12 @@ static struct usb_device_id rtl28xxu_table[] = {
                USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT_2)},
 
        /* RTL2832U */
+       [RTL2832U_0CCD_00A9] = {
+               USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1)},
+       [RTL2832U_1F4D_B803] = {
+               USB_DEVICE(USB_VID_GTEK, USB_PID_DELOCK_USB2_DVBT)},
+       [RTL2832U_0CCD_00B3] = {
+               USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK)},
        {} /* terminating entry */
 };
 
@@ -805,7 +1197,7 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = {
                                        {
                                                .frontend_attach = rtl2831u_frontend_attach,
                                                .tuner_attach    = rtl2831u_tuner_attach,
-                                               .streaming_ctrl  = rtl28xxu_streaming_ctrl,
+                                               .streaming_ctrl  = rtl2831u_streaming_ctrl,
                                                .stream = {
                                                        .type = USB_BULK,
                                                        .count = 6,
@@ -821,7 +1213,7 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = {
                        }
                },
 
-               .power_ctrl = rtl28xxu_power_ctrl,
+               .power_ctrl = rtl2831u_power_ctrl,
 
                .rc.core = {
                        .protocol       = RC_TYPE_NEC,
@@ -867,7 +1259,7 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = {
                                        {
                                                .frontend_attach = rtl2832u_frontend_attach,
                                                .tuner_attach    = rtl2832u_tuner_attach,
-                                               .streaming_ctrl  = rtl28xxu_streaming_ctrl,
+                                               .streaming_ctrl  = rtl2832u_streaming_ctrl,
                                                .stream = {
                                                        .type = USB_BULK,
                                                        .count = 6,
@@ -883,7 +1275,7 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = {
                        }
                },
 
-               .power_ctrl = rtl28xxu_power_ctrl,
+               .power_ctrl = rtl2832u_power_ctrl,
 
                .rc.core = {
                        .protocol       = RC_TYPE_NEC,
@@ -896,10 +1288,25 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = {
 
                .i2c_algo = &rtl28xxu_i2c_algo,
 
-               .num_device_descs = 0, /* disabled as no support for RTL2832 */
+               .num_device_descs = 3,
                .devices = {
                        {
-                               .name = "Realtek RTL2832U reference design",
+                               .name = "Terratec Cinergy T Stick Black",
+                               .warm_ids = {
+                                       &rtl28xxu_table[RTL2832U_0CCD_00A9],
+                               },
+                       },
+                       {
+                               .name = "G-Tek Electronics Group Lifeview LV5TDLX DVB-T",
+                               .warm_ids = {
+                                       &rtl28xxu_table[RTL2832U_1F4D_B803],
+                               },
+                       },
+                       {
+                               .name = "NOXON DAB/DAB+ USB dongle",
+                               .warm_ids = {
+                                       &rtl28xxu_table[RTL2832U_0CCD_00B3],
+                               },
                        },
                }
        },
@@ -910,6 +1317,7 @@ static int rtl28xxu_probe(struct usb_interface *intf,
                const struct usb_device_id *id)
 {
        int ret, i;
+       u8 val;
        int properties_count = ARRAY_SIZE(rtl28xxu_properties);
        struct dvb_usb_device *d;
        struct usb_device *udev;
@@ -954,16 +1362,25 @@ static int rtl28xxu_probe(struct usb_interface *intf,
        if (ret)
                goto err;
 
+
        /* init USB endpoints */
-       ret = rtl2831_wr_reg(d, USB_SYSCTL_0, 0x09);
+       ret = rtl28xx_rd_reg(d, USB_SYSCTL_0, &val);
+       if (ret)
+                       goto err;
+
+       /* enable DMA and Full Packet Mode*/
+       val |= 0x09;
+       ret = rtl28xx_wr_reg(d, USB_SYSCTL_0, val);
        if (ret)
                goto err;
 
-       ret = rtl2831_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4);
+       /* set EPA maximum packet size to 0x0200 */
+       ret = rtl28xx_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4);
        if (ret)
                goto err;
 
-       ret = rtl2831_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4);
+       /* change EPA FIFO length */
+       ret = rtl28xx_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4);
        if (ret)
                goto err;
 
@@ -1007,4 +1424,5 @@ module_exit(rtl28xxu_module_exit);
 
 MODULE_DESCRIPTION("Realtek RTL28xxU DVB USB driver");
 MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_AUTHOR("Thomas Mair <thomas.mair86@googlemail.com>");
 MODULE_LICENSE("GPL");
index b98ebb264e2967feef670656392fd0b3e1afb996..a08c2152d0eeb98c591278731f140a02cc244b03 100644 (file)
@@ -1,6 +1,7 @@
 config DVB_FE_CUSTOMISE
        bool "Customise the frontend modules to build"
        depends on DVB_CORE
+       depends on EXPERT
        default y if EXPERT
        help
          This allows the user to select/deselect frontend drivers for their
@@ -432,6 +433,13 @@ config DVB_RTL2830
        help
          Say Y when you want to support this frontend.
 
+config DVB_RTL2832
+       tristate "Realtek RTL2832 DVB-T"
+       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
 
index cd1ac2fd577447b46a2d6b35440014175af70cb7..185bb8b51952ec0190aa4a35c2c0bd0cbfbef42e 100644 (file)
@@ -99,6 +99,7 @@ obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o
 obj-$(CONFIG_DVB_A8293) += a8293.o
 obj-$(CONFIG_DVB_TDA10071) += tda10071.o
 obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
+obj-$(CONFIG_DVB_RTL2832) += rtl2832.o
 obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
 obj-$(CONFIG_DVB_AF9033) += af9033.o
 
index bb56497e940a543ff0c710e65e1b6bee496b29d6..cff44a389b404c1c492fe7e25d7925a29b076d05 100644 (file)
 #include "dvb_frontend.h"
 #include "a8293.h"
 
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-
-#define LOG_PREFIX "a8293"
-
-#undef dbg
-#define dbg(f, arg...) \
-       if (debug) \
-               printk(KERN_INFO   LOG_PREFIX": " f "\n" , ## arg)
-#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)
-
-
 struct a8293_priv {
        struct i2c_adapter *i2c;
        const struct a8293_config *cfg;
@@ -65,7 +47,8 @@ static int a8293_i2c(struct a8293_priv *priv, u8 *val, int len, bool rd)
        if (ret == 1) {
                ret = 0;
        } else {
-               warn("i2c failed=%d rd=%d", ret, rd);
+               dev_warn(&priv->i2c->dev, "%s: i2c failed=%d rd=%d\n",
+                               KBUILD_MODNAME, ret, rd);
                ret = -EREMOTEIO;
        }
 
@@ -88,7 +71,8 @@ static int a8293_set_voltage(struct dvb_frontend *fe,
        struct a8293_priv *priv = fe->sec_priv;
        int ret;
 
-       dbg("%s: fe_sec_voltage=%d", __func__, fe_sec_voltage);
+       dev_dbg(&priv->i2c->dev, "%s: fe_sec_voltage=%d\n", __func__,
+                       fe_sec_voltage);
 
        switch (fe_sec_voltage) {
        case SEC_VOLTAGE_OFF:
@@ -114,14 +98,12 @@ static int a8293_set_voltage(struct dvb_frontend *fe,
 
        return ret;
 err:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
 static void a8293_release_sec(struct dvb_frontend *fe)
 {
-       dbg("%s:", __func__);
-
        a8293_set_voltage(fe, SEC_VOLTAGE_OFF);
 
        kfree(fe->sec_priv);
@@ -154,7 +136,7 @@ struct dvb_frontend *a8293_attach(struct dvb_frontend *fe,
 
        /* ENB=0 */
        priv->reg[0] = 0x10;
-       ret = a8293_wr(priv, &priv->reg[1], 1);
+       ret = a8293_wr(priv, &priv->reg[0], 1);
        if (ret)
                goto err;
 
@@ -164,16 +146,17 @@ struct dvb_frontend *a8293_attach(struct dvb_frontend *fe,
        if (ret)
                goto err;
 
-       info("Allegro A8293 SEC attached.");
-
        fe->ops.release_sec = a8293_release_sec;
 
        /* override frontend ops */
        fe->ops.set_voltage = a8293_set_voltage;
 
+       dev_info(&priv->i2c->dev, "%s: Allegro A8293 SEC attached\n",
+                       KBUILD_MODNAME);
+
        return fe;
 err:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
        kfree(priv);
        return NULL;
 }
index 9ca34f495009dc29f15509f9075986af3247fe48..1f3bcb5a1de8a592e25aff1bcd100069fcded1ec 100644 (file)
@@ -2680,12 +2680,14 @@ static int dib8000_tune(struct dvb_frontend *fe)
 {
        struct dib8000_state *state = fe->demodulator_priv;
        int ret = 0;
-       u16 lock, value, mode = fft_to_mode(state);
+       u16 lock, value, mode;
 
        // we are already tuned - just resuming from suspend
        if (state == NULL)
                return -EINVAL;
 
+       mode = fft_to_mode(state);
+
        dib8000_set_bandwidth(fe, state->fe[0]->dtv_property_cache.bandwidth_hz / 1000);
        dib8000_set_channel(state, 0, 0);
 
index 9d64e4fea066818969ad54a39a7eff03a6e368c2..d615d7d055a29dfba39b3fd6e9879830cd96a080 100644 (file)
  *                     means that 1=DVBC, 0 = DVBT. Zero means the opposite.
  * @mpeg_out_clk_strength: DRXK Mpeg output clock drive strength.
  * @microcode_name:    Name of the firmware file with the microcode
+ * @qam_demod_parameter_count: The number of parameters used for the command
+ *                             to set the demodulator parameters. All
+ *                             firmwares are using the 2-parameter commmand.
+ *                             An exception is the "drxk_a3.mc" firmware,
+ *                             which uses the 4-parameter command.
+ *                             A value of 0 (default) or lower indicates that
+ *                             the correct number of parameters will be
+ *                             automatically detected.
  *
  * On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is
  * UIO-3.
@@ -38,7 +46,8 @@ struct drxk_config {
        u8      mpeg_out_clk_strength;
        int     chunk_size;
 
-       const char *microcode_name;
+       const char      *microcode_name;
+       int              qam_demod_parameter_count;
 };
 
 #if defined(CONFIG_DVB_DRXK) || (defined(CONFIG_DVB_DRXK_MODULE) \
index 60b868faeacfaf372f617ae31885727f701faa32..1ab8154542daebc056912efbdb7866bedae7304d 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/delay.h>
 #include <linux/firmware.h>
 #include <linux/i2c.h>
+#include <linux/hardirq.h>
 #include <asm/div64.h>
 
 #include "dvb_frontend.h"
@@ -308,16 +309,42 @@ static u32 Log10Times100(u32 x)
 /* I2C **********************************************************************/
 /****************************************************************************/
 
-static int i2c_read1(struct i2c_adapter *adapter, u8 adr, u8 *val)
+static int drxk_i2c_lock(struct drxk_state *state)
+{
+       i2c_lock_adapter(state->i2c);
+       state->drxk_i2c_exclusive_lock = true;
+
+       return 0;
+}
+
+static void drxk_i2c_unlock(struct drxk_state *state)
+{
+       if (!state->drxk_i2c_exclusive_lock)
+               return;
+
+       i2c_unlock_adapter(state->i2c);
+       state->drxk_i2c_exclusive_lock = false;
+}
+
+static int drxk_i2c_transfer(struct drxk_state *state, struct i2c_msg *msgs,
+                            unsigned len)
+{
+       if (state->drxk_i2c_exclusive_lock)
+               return __i2c_transfer(state->i2c, msgs, len);
+       else
+               return i2c_transfer(state->i2c, msgs, len);
+}
+
+static int i2c_read1(struct drxk_state *state, u8 adr, u8 *val)
 {
        struct i2c_msg msgs[1] = { {.addr = adr, .flags = I2C_M_RD,
                                    .buf = val, .len = 1}
        };
 
-       return i2c_transfer(adapter, msgs, 1);
+       return drxk_i2c_transfer(state, msgs, 1);
 }
 
-static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
+static int i2c_write(struct drxk_state *state, u8 adr, u8 *data, int len)
 {
        int status;
        struct i2c_msg msg = {
@@ -330,7 +357,7 @@ static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
                        printk(KERN_CONT " %02x", data[i]);
                printk(KERN_CONT "\n");
        }
-       status = i2c_transfer(adap, &msg, 1);
+       status = drxk_i2c_transfer(state, &msg, 1);
        if (status >= 0 && status != 1)
                status = -EIO;
 
@@ -340,7 +367,7 @@ static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
        return status;
 }
 
-static int i2c_read(struct i2c_adapter *adap,
+static int i2c_read(struct drxk_state *state,
                    u8 adr, u8 *msg, int len, u8 *answ, int alen)
 {
        int status;
@@ -351,7 +378,7 @@ static int i2c_read(struct i2c_adapter *adap,
                 .buf = answ, .len = alen}
        };
 
-       status = i2c_transfer(adap, msgs, 2);
+       status = drxk_i2c_transfer(state, msgs, 2);
        if (status != 2) {
                if (debug > 2)
                        printk(KERN_CONT ": ERROR!\n");
@@ -394,7 +421,7 @@ static int read16_flags(struct drxk_state *state, u32 reg, u16 *data, u8 flags)
                len = 2;
        }
        dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags);
-       status = i2c_read(state->i2c, adr, mm1, len, mm2, 2);
+       status = i2c_read(state, adr, mm1, len, mm2, 2);
        if (status < 0)
                return status;
        if (data)
@@ -428,7 +455,7 @@ static int read32_flags(struct drxk_state *state, u32 reg, u32 *data, u8 flags)
                len = 2;
        }
        dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags);
-       status = i2c_read(state->i2c, adr, mm1, len, mm2, 4);
+       status = i2c_read(state, adr, mm1, len, mm2, 4);
        if (status < 0)
                return status;
        if (data)
@@ -464,7 +491,7 @@ static int write16_flags(struct drxk_state *state, u32 reg, u16 data, u8 flags)
        mm[len + 1] = (data >> 8) & 0xff;
 
        dprintk(2, "(0x%08x, 0x%04x, 0x%02x)\n", reg, data, flags);
-       return i2c_write(state->i2c, adr, mm, len + 2);
+       return i2c_write(state, adr, mm, len + 2);
 }
 
 static int write16(struct drxk_state *state, u32 reg, u16 data)
@@ -495,7 +522,7 @@ static int write32_flags(struct drxk_state *state, u32 reg, u32 data, u8 flags)
        mm[len + 3] = (data >> 24) & 0xff;
        dprintk(2, "(0x%08x, 0x%08x, 0x%02x)\n", reg, data, flags);
 
-       return i2c_write(state->i2c, adr, mm, len + 4);
+       return i2c_write(state, adr, mm, len + 4);
 }
 
 static int write32(struct drxk_state *state, u32 reg, u32 data)
@@ -542,7 +569,7 @@ static int write_block(struct drxk_state *state, u32 Address,
                                        printk(KERN_CONT " %02x", pBlock[i]);
                        printk(KERN_CONT "\n");
                }
-               status = i2c_write(state->i2c, state->demod_address,
+               status = i2c_write(state, state->demod_address,
                                   &state->Chunk[0], Chunk + AdrLength);
                if (status < 0) {
                        printk(KERN_ERR "drxk: %s: i2c write error at addr 0x%02x\n",
@@ -568,17 +595,17 @@ int PowerUpDevice(struct drxk_state *state)
 
        dprintk(1, "\n");
 
-       status = i2c_read1(state->i2c, state->demod_address, &data);
+       status = i2c_read1(state, state->demod_address, &data);
        if (status < 0) {
                do {
                        data = 0;
-                       status = i2c_write(state->i2c, state->demod_address,
+                       status = i2c_write(state, state->demod_address,
                                           &data, 1);
                        msleep(10);
                        retryCount++;
                        if (status < 0)
                                continue;
-                       status = i2c_read1(state->i2c, state->demod_address,
+                       status = i2c_read1(state, state->demod_address,
                                           &data);
                } while (status < 0 &&
                         (retryCount < DRXK_MAX_RETRIES_POWERUP));
@@ -932,7 +959,7 @@ static int GetDeviceCapabilities(struct drxk_state *state)
        if (status < 0)
                goto error;
 
-printk(KERN_ERR "drxk: status = 0x%08x\n", sioTopJtagidLo);
+       printk(KERN_INFO "drxk: status = 0x%08x\n", sioTopJtagidLo);
 
        /* driver 0.9.0 */
        switch ((sioTopJtagidLo >> 29) & 0xF) {
@@ -2824,7 +2851,7 @@ static int ConfigureI2CBridge(struct drxk_state *state, bool bEnableBridge)
        dprintk(1, "\n");
 
        if (state->m_DrxkState == DRXK_UNINITIALIZED)
-               goto error;
+               return 0;
        if (state->m_DrxkState == DRXK_POWERED_DOWN)
                goto error;
 
@@ -2977,7 +3004,7 @@ static int ADCSynchronization(struct drxk_state *state)
                status = read16(state, IQM_AF_CLKNEG__A, &clkNeg);
                if (status < 0)
                        goto error;
-               if ((clkNeg | IQM_AF_CLKNEG_CLKNEGDATA__M) ==
+               if ((clkNeg & IQM_AF_CLKNEG_CLKNEGDATA__M) ==
                        IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS) {
                        clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
                        clkNeg |=
@@ -5361,7 +5388,7 @@ static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus)
                        SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK, 0, NULL, 2,
                        Result);
        if (status < 0)
-               printk(KERN_ERR "drxk: %s status = %08x\n", __func__, status);
+               printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
 
        if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED) {
                /* 0x0000 NOT LOCKED */
@@ -5388,12 +5415,67 @@ static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus)
 #define QAM_LOCKRANGE__M      0x10
 #define QAM_LOCKRANGE_NORMAL  0x10
 
+static int QAMDemodulatorCommand(struct drxk_state *state,
+                                int numberOfParameters)
+{
+       int status;
+       u16 cmdResult;
+       u16 setParamParameters[4] = { 0, 0, 0, 0 };
+
+       setParamParameters[0] = state->m_Constellation; /* modulation     */
+       setParamParameters[1] = DRXK_QAM_I12_J17;       /* interleave mode   */
+
+       if (numberOfParameters == 2) {
+               u16 setEnvParameters[1] = { 0 };
+
+               if (state->m_OperationMode == OM_QAM_ITU_C)
+                       setEnvParameters[0] = QAM_TOP_ANNEX_C;
+               else
+                       setEnvParameters[0] = QAM_TOP_ANNEX_A;
+
+               status = scu_command(state,
+                                    SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV,
+                                    1, setEnvParameters, 1, &cmdResult);
+               if (status < 0)
+                       goto error;
+
+               status = scu_command(state,
+                                    SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM,
+                                    numberOfParameters, setParamParameters,
+                                    1, &cmdResult);
+       } else if (numberOfParameters == 4) {
+               if (state->m_OperationMode == OM_QAM_ITU_C)
+                       setParamParameters[2] = QAM_TOP_ANNEX_C;
+               else
+                       setParamParameters[2] = QAM_TOP_ANNEX_A;
+
+               setParamParameters[3] |= (QAM_MIRROR_AUTO_ON);
+               /* Env parameters */
+               /* check for LOCKRANGE Extented */
+               /* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */
+
+               status = scu_command(state,
+                                    SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM,
+                                    numberOfParameters, setParamParameters,
+                                    1, &cmdResult);
+       } else {
+               printk(KERN_WARNING "drxk: Unknown QAM demodulator parameter "
+                       "count %d\n", numberOfParameters);
+       }
+
+error:
+       if (status < 0)
+               printk(KERN_WARNING "drxk: Warning %d on %s\n",
+                      status, __func__);
+       return status;
+}
+
 static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz,
                  s32 tunerFreqOffset)
 {
        int status;
-       u16 setParamParameters[4] = { 0, 0, 0, 0 };
        u16 cmdResult;
+       int qamDemodParamCount = state->qam_demod_parameter_count;
 
        dprintk(1, "\n");
        /*
@@ -5445,34 +5527,42 @@ static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz,
        }
        if (status < 0)
                goto error;
-       setParamParameters[0] = state->m_Constellation; /* modulation     */
-       setParamParameters[1] = DRXK_QAM_I12_J17;       /* interleave mode   */
-       if (state->m_OperationMode == OM_QAM_ITU_C)
-               setParamParameters[2] = QAM_TOP_ANNEX_C;
-       else
-               setParamParameters[2] = QAM_TOP_ANNEX_A;
-       setParamParameters[3] |= (QAM_MIRROR_AUTO_ON);
-       /* Env parameters */
-       /* check for LOCKRANGE Extented */
-       /* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */
 
-       status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, 4, setParamParameters, 1, &cmdResult);
-       if (status < 0) {
-               /* Fall-back to the simpler call */
-               if (state->m_OperationMode == OM_QAM_ITU_C)
-                       setParamParameters[0] = QAM_TOP_ANNEX_C;
-               else
-                       setParamParameters[0] = QAM_TOP_ANNEX_A;
-               status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, 1, setParamParameters, 1, &cmdResult);
-               if (status < 0)
-                       goto error;
+       /* Use the 4-parameter if it's requested or we're probing for
+        * the correct command. */
+       if (state->qam_demod_parameter_count == 4
+               || !state->qam_demod_parameter_count) {
+               qamDemodParamCount = 4;
+               status = QAMDemodulatorCommand(state, qamDemodParamCount);
+       }
 
-               setParamParameters[0] = state->m_Constellation; /* modulation     */
-               setParamParameters[1] = DRXK_QAM_I12_J17;       /* interleave mode   */
-               status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, 2, setParamParameters, 1, &cmdResult);
+       /* Use the 2-parameter command if it was requested or if we're
+        * probing for the correct command and the 4-parameter command
+        * failed. */
+       if (state->qam_demod_parameter_count == 2
+               || (!state->qam_demod_parameter_count && status < 0)) {
+               qamDemodParamCount = 2;
+               status = QAMDemodulatorCommand(state, qamDemodParamCount);
        }
-       if (status < 0)
+
+       if (status < 0) {
+               dprintk(1, "Could not set demodulator parameters. Make "
+                       "sure qam_demod_parameter_count (%d) is correct for "
+                       "your firmware (%s).\n",
+                       state->qam_demod_parameter_count,
+                       state->microcode_name);
                goto error;
+       } else if (!state->qam_demod_parameter_count) {
+               dprintk(1, "Auto-probing the correct QAM demodulator command "
+                       "parameters was successful - using %d parameters.\n",
+                       qamDemodParamCount);
+
+               /*
+                * One of our commands was successful. We don't need to
+                * auto-probe anymore, now that we got the correct command.
+                */
+               state->qam_demod_parameter_count = qamDemodParamCount;
+       }
 
        /*
         * STEP 3: enable the system in a mode where the ADC provides valid
@@ -5968,34 +6058,15 @@ error:
        return status;
 }
 
-static int load_microcode(struct drxk_state *state, const char *mc_name)
-{
-       const struct firmware *fw = NULL;
-       int err = 0;
-
-       dprintk(1, "\n");
-
-       err = request_firmware(&fw, mc_name, state->i2c->dev.parent);
-       if (err < 0) {
-               printk(KERN_ERR
-                      "drxk: Could not load firmware file %s.\n", mc_name);
-               printk(KERN_INFO
-                      "drxk: Copy %s to your hotplug directory!\n", mc_name);
-               return err;
-       }
-       err = DownloadMicrocode(state, fw->data, fw->size);
-       release_firmware(fw);
-       return err;
-}
-
 static int init_drxk(struct drxk_state *state)
 {
-       int status = 0;
+       int status = 0, n = 0;
        enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
        u16 driverVersion;
 
        dprintk(1, "\n");
        if ((state->m_DrxkState == DRXK_UNINITIALIZED)) {
+               drxk_i2c_lock(state);
                status = PowerUpDevice(state);
                if (status < 0)
                        goto error;
@@ -6073,8 +6144,12 @@ static int init_drxk(struct drxk_state *state)
                if (status < 0)
                        goto error;
 
-               if (state->microcode_name)
-                       load_microcode(state, state->microcode_name);
+               if (state->fw) {
+                       status = DownloadMicrocode(state, state->fw->data,
+                                                  state->fw->size);
+                       if (status < 0)
+                               goto error;
+               }
 
                /* disable token-ring bus through OFDM block for possible ucode upload */
                status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_OFF);
@@ -6167,19 +6242,71 @@ static int init_drxk(struct drxk_state *state)
                        state->m_DrxkState = DRXK_POWERED_DOWN;
                } else
                        state->m_DrxkState = DRXK_STOPPED;
+
+               /* Initialize the supported delivery systems */
+               n = 0;
+               if (state->m_hasDVBC) {
+                       state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A;
+                       state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C;
+                       strlcat(state->frontend.ops.info.name, " DVB-C",
+                               sizeof(state->frontend.ops.info.name));
+               }
+               if (state->m_hasDVBT) {
+                       state->frontend.ops.delsys[n++] = SYS_DVBT;
+                       strlcat(state->frontend.ops.info.name, " DVB-T",
+                               sizeof(state->frontend.ops.info.name));
+               }
+               drxk_i2c_unlock(state);
        }
 error:
-       if (status < 0)
+       if (status < 0) {
+               state->m_DrxkState = DRXK_NO_DEV;
+               drxk_i2c_unlock(state);
                printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+       }
 
        return status;
 }
 
+static void load_firmware_cb(const struct firmware *fw,
+                            void *context)
+{
+       struct drxk_state *state = context;
+
+       dprintk(1, ": %s\n", fw ? "firmware loaded" : "firmware not loaded");
+       if (!fw) {
+               printk(KERN_ERR
+                      "drxk: Could not load firmware file %s.\n",
+                       state->microcode_name);
+               printk(KERN_INFO
+                      "drxk: Copy %s to your hotplug directory!\n",
+                       state->microcode_name);
+               state->microcode_name = NULL;
+
+               /*
+                * As firmware is now load asynchronous, it is not possible
+                * anymore to fail at frontend attach. We might silently
+                * return here, and hope that the driver won't crash.
+                * We might also change all DVB callbacks to return -ENODEV
+                * if the device is not initialized.
+                * As the DRX-K devices have their own internal firmware,
+                * let's just hope that it will match a firmware revision
+                * compatible with this driver and proceed.
+                */
+       }
+       state->fw = fw;
+
+       init_drxk(state);
+}
+
 static void drxk_release(struct dvb_frontend *fe)
 {
        struct drxk_state *state = fe->demodulator_priv;
 
        dprintk(1, "\n");
+       if (state->fw)
+               release_firmware(state->fw);
+
        kfree(state);
 }
 
@@ -6188,6 +6315,12 @@ static int drxk_sleep(struct dvb_frontend *fe)
        struct drxk_state *state = fe->demodulator_priv;
 
        dprintk(1, "\n");
+
+       if (state->m_DrxkState == DRXK_NO_DEV)
+               return -ENODEV;
+       if (state->m_DrxkState == DRXK_UNINITIALIZED)
+               return 0;
+
        ShutDown(state);
        return 0;
 }
@@ -6196,7 +6329,11 @@ static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
 {
        struct drxk_state *state = fe->demodulator_priv;
 
-       dprintk(1, "%s\n", enable ? "enable" : "disable");
+       dprintk(1, ": %s\n", enable ? "enable" : "disable");
+
+       if (state->m_DrxkState == DRXK_NO_DEV)
+               return -ENODEV;
+
        return ConfigureI2CBridge(state, enable ? true : false);
 }
 
@@ -6209,6 +6346,12 @@ static int drxk_set_parameters(struct dvb_frontend *fe)
 
        dprintk(1, "\n");
 
+       if (state->m_DrxkState == DRXK_NO_DEV)
+               return -ENODEV;
+
+       if (state->m_DrxkState == DRXK_UNINITIALIZED)
+               return -EAGAIN;
+
        if (!fe->ops.tuner_ops.get_if_frequency) {
                printk(KERN_ERR
                       "drxk: Error: get_if_frequency() not defined at tuner. Can't work without it!\n");
@@ -6262,6 +6405,12 @@ static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status)
        u32 stat;
 
        dprintk(1, "\n");
+
+       if (state->m_DrxkState == DRXK_NO_DEV)
+               return -ENODEV;
+       if (state->m_DrxkState == DRXK_UNINITIALIZED)
+               return -EAGAIN;
+
        *status = 0;
        GetLockStatus(state, &stat, 0);
        if (stat == MPEG_LOCK)
@@ -6275,8 +6424,15 @@ static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status)
 
 static int drxk_read_ber(struct dvb_frontend *fe, u32 *ber)
 {
+       struct drxk_state *state = fe->demodulator_priv;
+
        dprintk(1, "\n");
 
+       if (state->m_DrxkState == DRXK_NO_DEV)
+               return -ENODEV;
+       if (state->m_DrxkState == DRXK_UNINITIALIZED)
+               return -EAGAIN;
+
        *ber = 0;
        return 0;
 }
@@ -6288,6 +6444,12 @@ static int drxk_read_signal_strength(struct dvb_frontend *fe,
        u32 val = 0;
 
        dprintk(1, "\n");
+
+       if (state->m_DrxkState == DRXK_NO_DEV)
+               return -ENODEV;
+       if (state->m_DrxkState == DRXK_UNINITIALIZED)
+               return -EAGAIN;
+
        ReadIFAgc(state, &val);
        *strength = val & 0xffff;
        return 0;
@@ -6299,6 +6461,12 @@ static int drxk_read_snr(struct dvb_frontend *fe, u16 *snr)
        s32 snr2;
 
        dprintk(1, "\n");
+
+       if (state->m_DrxkState == DRXK_NO_DEV)
+               return -ENODEV;
+       if (state->m_DrxkState == DRXK_UNINITIALIZED)
+               return -EAGAIN;
+
        GetSignalToNoise(state, &snr2);
        *snr = snr2 & 0xffff;
        return 0;
@@ -6310,6 +6478,12 @@ static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
        u16 err;
 
        dprintk(1, "\n");
+
+       if (state->m_DrxkState == DRXK_NO_DEV)
+               return -ENODEV;
+       if (state->m_DrxkState == DRXK_UNINITIALIZED)
+               return -EAGAIN;
+
        DVBTQAMGetAccPktErr(state, &err);
        *ucblocks = (u32) err;
        return 0;
@@ -6318,9 +6492,16 @@ static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
 static int drxk_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings
                                    *sets)
 {
+       struct drxk_state *state = fe->demodulator_priv;
        struct dtv_frontend_properties *p = &fe->dtv_property_cache;
 
        dprintk(1, "\n");
+
+       if (state->m_DrxkState == DRXK_NO_DEV)
+               return -ENODEV;
+       if (state->m_DrxkState == DRXK_UNINITIALIZED)
+               return -EAGAIN;
+
        switch (p->delivery_system) {
        case SYS_DVBC_ANNEX_A:
        case SYS_DVBC_ANNEX_C:
@@ -6371,10 +6552,9 @@ static struct dvb_frontend_ops drxk_ops = {
 struct dvb_frontend *drxk_attach(const struct drxk_config *config,
                                 struct i2c_adapter *i2c)
 {
-       int n;
-
        struct drxk_state *state = NULL;
        u8 adr = config->adr;
+       int status;
 
        dprintk(1, "\n");
        state = kzalloc(sizeof(struct drxk_state), GFP_KERNEL);
@@ -6385,6 +6565,7 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config,
        state->demod_address = adr;
        state->single_master = config->single_master;
        state->microcode_name = config->microcode_name;
+       state->qam_demod_parameter_count = config->qam_demod_parameter_count;
        state->no_i2c_bridge = config->no_i2c_bridge;
        state->antenna_gpio = config->antenna_gpio;
        state->antenna_dvbt = config->antenna_dvbt;
@@ -6425,22 +6606,21 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config,
        state->frontend.demodulator_priv = state;
 
        init_state(state);
-       if (init_drxk(state) < 0)
-               goto error;
 
-       /* Initialize the supported delivery systems */
-       n = 0;
-       if (state->m_hasDVBC) {
-               state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A;
-               state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C;
-               strlcat(state->frontend.ops.info.name, " DVB-C",
-                       sizeof(state->frontend.ops.info.name));
-       }
-       if (state->m_hasDVBT) {
-               state->frontend.ops.delsys[n++] = SYS_DVBT;
-               strlcat(state->frontend.ops.info.name, " DVB-T",
-                       sizeof(state->frontend.ops.info.name));
-       }
+       /* Load firmware and initialize DRX-K */
+       if (state->microcode_name) {
+               status = request_firmware_nowait(THIS_MODULE, 1,
+                                             state->microcode_name,
+                                             state->i2c->dev.parent,
+                                             GFP_KERNEL,
+                                             state, load_firmware_cb);
+               if (status < 0) {
+                       printk(KERN_ERR
+                       "drxk: failed to request a firmware\n");
+                       return NULL;
+               }
+       } else if (init_drxk(state) < 0)
+               goto error;
 
        printk(KERN_INFO "drxk: frontend initialized.\n");
        return &state->frontend;
index 4bbf841de83a77b90d70e092922ff8d88aa36682..6bb9fc4a7b96cbeeb6a082503499fbff18feb4d2 100644 (file)
@@ -94,7 +94,15 @@ enum DRXPowerMode {
 
 
 enum AGC_CTRL_MODE { DRXK_AGC_CTRL_AUTO = 0, DRXK_AGC_CTRL_USER, DRXK_AGC_CTRL_OFF };
-enum EDrxkState { DRXK_UNINITIALIZED = 0, DRXK_STOPPED, DRXK_DTV_STARTED, DRXK_ATV_STARTED, DRXK_POWERED_DOWN };
+enum EDrxkState {
+       DRXK_UNINITIALIZED = 0,
+       DRXK_STOPPED,
+       DRXK_DTV_STARTED,
+       DRXK_ATV_STARTED,
+       DRXK_POWERED_DOWN,
+       DRXK_NO_DEV                     /* If drxk init failed */
+};
+
 enum EDrxkCoefArrayIndex {
        DRXK_COEF_IDX_MN = 0,
        DRXK_COEF_IDX_FM    ,
@@ -325,6 +333,9 @@ struct drxk_state {
 
        enum DRXPowerMode m_currentPowerMode;
 
+       /* when true, avoids other devices to use the I2C bus */
+       bool              drxk_i2c_exclusive_lock;
+
        /*
         * Configurable parameters at the driver. They stores the values found
         * at struct drxk_config.
@@ -338,7 +349,11 @@ struct drxk_state {
        bool    antenna_dvbt;
        u16     antenna_gpio;
 
+       /* Firmware */
        const char *microcode_name;
+       struct completion fw_wait_load;
+       const struct firmware *fw;
+       int qam_demod_parameter_count;
 };
 
 #define NEVER_LOCK 0
index 568363a10a31ca2241be7a3772273ba6d45b046f..c2ea2749ebedd40c0466a96ec33f90bfebcbee13 100644 (file)
@@ -40,6 +40,8 @@
 static int debug;
 static int fake_signal_str = 1;
 
+#define LGS8GXX_FIRMWARE "lgs8g75.fw"
+
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
@@ -592,7 +594,7 @@ static int lgs8g75_init_data(struct lgs8gxx_state *priv)
        int rc;
        int i;
 
-       rc = request_firmware(&fw, "lgs8g75.fw", &priv->i2c->dev);
+       rc = request_firmware(&fw, LGS8GXX_FIRMWARE, &priv->i2c->dev);
        if (rc)
                return rc;
 
@@ -1070,3 +1072,4 @@ EXPORT_SYMBOL(lgs8gxx_attach);
 MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver");
 MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
 MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(LGS8GXX_FIRMWARE);
diff --git a/drivers/media/dvb/frontends/rtl2832.c b/drivers/media/dvb/frontends/rtl2832.c
new file mode 100644 (file)
index 0000000..28269cc
--- /dev/null
@@ -0,0 +1,789 @@
+/*
+ * Realtek RTL2832 DVB-T demodulator driver
+ *
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@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.,
+ *     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "rtl2832_priv.h"
+#include <linux/bitops.h>
+
+int rtl2832_debug;
+module_param_named(debug, rtl2832_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+#define REG_MASK(b) (BIT(b + 1) - 1)
+
+static const struct rtl2832_reg_entry registers[] = {
+       [DVBT_SOFT_RST]         = {0x1, 0x1,   2, 2},
+       [DVBT_IIC_REPEAT]       = {0x1, 0x1,   3, 3},
+       [DVBT_TR_WAIT_MIN_8K]   = {0x1, 0x88, 11, 2},
+       [DVBT_RSD_BER_FAIL_VAL] = {0x1, 0x8f, 15, 0},
+       [DVBT_EN_BK_TRK]        = {0x1, 0xa6,  7, 7},
+       [DVBT_AD_EN_REG]        = {0x0, 0x8,   7, 7},
+       [DVBT_AD_EN_REG1]       = {0x0, 0x8,   6, 6},
+       [DVBT_EN_BBIN]          = {0x1, 0xb1,  0, 0},
+       [DVBT_MGD_THD0]         = {0x1, 0x95,  7, 0},
+       [DVBT_MGD_THD1]         = {0x1, 0x96,  7, 0},
+       [DVBT_MGD_THD2]         = {0x1, 0x97,  7, 0},
+       [DVBT_MGD_THD3]         = {0x1, 0x98,  7, 0},
+       [DVBT_MGD_THD4]         = {0x1, 0x99,  7, 0},
+       [DVBT_MGD_THD5]         = {0x1, 0x9a,  7, 0},
+       [DVBT_MGD_THD6]         = {0x1, 0x9b,  7, 0},
+       [DVBT_MGD_THD7]         = {0x1, 0x9c,  7, 0},
+       [DVBT_EN_CACQ_NOTCH]    = {0x1, 0x61,  4, 4},
+       [DVBT_AD_AV_REF]        = {0x0, 0x9,   6, 0},
+       [DVBT_REG_PI]           = {0x0, 0xa,   2, 0},
+       [DVBT_PIP_ON]           = {0x0, 0x21,  3, 3},
+       [DVBT_SCALE1_B92]       = {0x2, 0x92,  7, 0},
+       [DVBT_SCALE1_B93]       = {0x2, 0x93,  7, 0},
+       [DVBT_SCALE1_BA7]       = {0x2, 0xa7,  7, 0},
+       [DVBT_SCALE1_BA9]       = {0x2, 0xa9,  7, 0},
+       [DVBT_SCALE1_BAA]       = {0x2, 0xaa,  7, 0},
+       [DVBT_SCALE1_BAB]       = {0x2, 0xab,  7, 0},
+       [DVBT_SCALE1_BAC]       = {0x2, 0xac,  7, 0},
+       [DVBT_SCALE1_BB0]       = {0x2, 0xb0,  7, 0},
+       [DVBT_SCALE1_BB1]       = {0x2, 0xb1,  7, 0},
+       [DVBT_KB_P1]            = {0x1, 0x64,  3, 1},
+       [DVBT_KB_P2]            = {0x1, 0x64,  6, 4},
+       [DVBT_KB_P3]            = {0x1, 0x65,  2, 0},
+       [DVBT_OPT_ADC_IQ]       = {0x0, 0x6,   5, 4},
+       [DVBT_AD_AVI]           = {0x0, 0x9,   1, 0},
+       [DVBT_AD_AVQ]           = {0x0, 0x9,   3, 2},
+       [DVBT_K1_CR_STEP12]     = {0x2, 0xad,  9, 4},
+       [DVBT_TRK_KS_P2]        = {0x1, 0x6f,  2, 0},
+       [DVBT_TRK_KS_I2]        = {0x1, 0x70,  5, 3},
+       [DVBT_TR_THD_SET2]      = {0x1, 0x72,  3, 0},
+       [DVBT_TRK_KC_P2]        = {0x1, 0x73,  5, 3},
+       [DVBT_TRK_KC_I2]        = {0x1, 0x75,  2, 0},
+       [DVBT_CR_THD_SET2]      = {0x1, 0x76,  7, 6},
+       [DVBT_PSET_IFFREQ]      = {0x1, 0x19, 21, 0},
+       [DVBT_SPEC_INV]         = {0x1, 0x15,  0, 0},
+       [DVBT_RSAMP_RATIO]      = {0x1, 0x9f, 27, 2},
+       [DVBT_CFREQ_OFF_RATIO]  = {0x1, 0x9d, 23, 4},
+       [DVBT_FSM_STAGE]        = {0x3, 0x51,  6, 3},
+       [DVBT_RX_CONSTEL]       = {0x3, 0x3c,  3, 2},
+       [DVBT_RX_HIER]          = {0x3, 0x3c,  6, 4},
+       [DVBT_RX_C_RATE_LP]     = {0x3, 0x3d,  2, 0},
+       [DVBT_RX_C_RATE_HP]     = {0x3, 0x3d,  5, 3},
+       [DVBT_GI_IDX]           = {0x3, 0x51,  1, 0},
+       [DVBT_FFT_MODE_IDX]     = {0x3, 0x51,  2, 2},
+       [DVBT_RSD_BER_EST]      = {0x3, 0x4e, 15, 0},
+       [DVBT_CE_EST_EVM]       = {0x4, 0xc,  15, 0},
+       [DVBT_RF_AGC_VAL]       = {0x3, 0x5b, 13, 0},
+       [DVBT_IF_AGC_VAL]       = {0x3, 0x59, 13, 0},
+       [DVBT_DAGC_VAL]         = {0x3, 0x5,   7, 0},
+       [DVBT_SFREQ_OFF]        = {0x3, 0x18, 13, 0},
+       [DVBT_CFREQ_OFF]        = {0x3, 0x5f, 17, 0},
+       [DVBT_POLAR_RF_AGC]     = {0x0, 0xe,   1, 1},
+       [DVBT_POLAR_IF_AGC]     = {0x0, 0xe,   0, 0},
+       [DVBT_AAGC_HOLD]        = {0x1, 0x4,   5, 5},
+       [DVBT_EN_RF_AGC]        = {0x1, 0x4,   6, 6},
+       [DVBT_EN_IF_AGC]        = {0x1, 0x4,   7, 7},
+       [DVBT_IF_AGC_MIN]       = {0x1, 0x8,   7, 0},
+       [DVBT_IF_AGC_MAX]       = {0x1, 0x9,   7, 0},
+       [DVBT_RF_AGC_MIN]       = {0x1, 0xa,   7, 0},
+       [DVBT_RF_AGC_MAX]       = {0x1, 0xb,   7, 0},
+       [DVBT_IF_AGC_MAN]       = {0x1, 0xc,   6, 6},
+       [DVBT_IF_AGC_MAN_VAL]   = {0x1, 0xc,  13, 0},
+       [DVBT_RF_AGC_MAN]       = {0x1, 0xe,   6, 6},
+       [DVBT_RF_AGC_MAN_VAL]   = {0x1, 0xe,  13, 0},
+       [DVBT_DAGC_TRG_VAL]     = {0x1, 0x12,  7, 0},
+       [DVBT_AGC_TARG_VAL_0]   = {0x1, 0x2,   0, 0},
+       [DVBT_AGC_TARG_VAL_8_1] = {0x1, 0x3,   7, 0},
+       [DVBT_AAGC_LOOP_GAIN]   = {0x1, 0xc7,  5, 1},
+       [DVBT_LOOP_GAIN2_3_0]   = {0x1, 0x4,   4, 1},
+       [DVBT_LOOP_GAIN2_4]     = {0x1, 0x5,   7, 7},
+       [DVBT_LOOP_GAIN3]       = {0x1, 0xc8,  4, 0},
+       [DVBT_VTOP1]            = {0x1, 0x6,   5, 0},
+       [DVBT_VTOP2]            = {0x1, 0xc9,  5, 0},
+       [DVBT_VTOP3]            = {0x1, 0xca,  5, 0},
+       [DVBT_KRF1]             = {0x1, 0xcb,  7, 0},
+       [DVBT_KRF2]             = {0x1, 0x7,   7, 0},
+       [DVBT_KRF3]             = {0x1, 0xcd,  7, 0},
+       [DVBT_KRF4]             = {0x1, 0xce,  7, 0},
+       [DVBT_EN_GI_PGA]        = {0x1, 0xe5,  0, 0},
+       [DVBT_THD_LOCK_UP]      = {0x1, 0xd9,  8, 0},
+       [DVBT_THD_LOCK_DW]      = {0x1, 0xdb,  8, 0},
+       [DVBT_THD_UP1]          = {0x1, 0xdd,  7, 0},
+       [DVBT_THD_DW1]          = {0x1, 0xde,  7, 0},
+       [DVBT_INTER_CNT_LEN]    = {0x1, 0xd8,  3, 0},
+       [DVBT_GI_PGA_STATE]     = {0x1, 0xe6,  3, 3},
+       [DVBT_EN_AGC_PGA]       = {0x1, 0xd7,  0, 0},
+       [DVBT_CKOUTPAR]         = {0x1, 0x7b,  5, 5},
+       [DVBT_CKOUT_PWR]        = {0x1, 0x7b,  6, 6},
+       [DVBT_SYNC_DUR]         = {0x1, 0x7b,  7, 7},
+       [DVBT_ERR_DUR]          = {0x1, 0x7c,  0, 0},
+       [DVBT_SYNC_LVL]         = {0x1, 0x7c,  1, 1},
+       [DVBT_ERR_LVL]          = {0x1, 0x7c,  2, 2},
+       [DVBT_VAL_LVL]          = {0x1, 0x7c,  3, 3},
+       [DVBT_SERIAL]           = {0x1, 0x7c,  4, 4},
+       [DVBT_SER_LSB]          = {0x1, 0x7c,  5, 5},
+       [DVBT_CDIV_PH0]         = {0x1, 0x7d,  3, 0},
+       [DVBT_CDIV_PH1]         = {0x1, 0x7d,  7, 4},
+       [DVBT_MPEG_IO_OPT_2_2]  = {0x0, 0x6,   7, 7},
+       [DVBT_MPEG_IO_OPT_1_0]  = {0x0, 0x7,   7, 6},
+       [DVBT_CKOUTPAR_PIP]     = {0x0, 0xb7,  4, 4},
+       [DVBT_CKOUT_PWR_PIP]    = {0x0, 0xb7,  3, 3},
+       [DVBT_SYNC_LVL_PIP]     = {0x0, 0xb7,  2, 2},
+       [DVBT_ERR_LVL_PIP]      = {0x0, 0xb7,  1, 1},
+       [DVBT_VAL_LVL_PIP]      = {0x0, 0xb7,  0, 0},
+       [DVBT_CKOUTPAR_PID]     = {0x0, 0xb9,  4, 4},
+       [DVBT_CKOUT_PWR_PID]    = {0x0, 0xb9,  3, 3},
+       [DVBT_SYNC_LVL_PID]     = {0x0, 0xb9,  2, 2},
+       [DVBT_ERR_LVL_PID]      = {0x0, 0xb9,  1, 1},
+       [DVBT_VAL_LVL_PID]      = {0x0, 0xb9,  0, 0},
+       [DVBT_SM_PASS]          = {0x1, 0x93, 11, 0},
+       [DVBT_AD7_SETTING]      = {0x0, 0x11, 15, 0},
+       [DVBT_RSSI_R]           = {0x3, 0x1,   6, 0},
+       [DVBT_ACI_DET_IND]      = {0x3, 0x12,  0, 0},
+       [DVBT_REG_MON]          = {0x0, 0xd,   1, 0},
+       [DVBT_REG_MONSEL]       = {0x0, 0xd,   2, 2},
+       [DVBT_REG_GPE]          = {0x0, 0xd,   7, 7},
+       [DVBT_REG_GPO]          = {0x0, 0x10,  0, 0},
+       [DVBT_REG_4MSEL]        = {0x0, 0x13,  0, 0},
+};
+
+/* write multiple hardware registers */
+static int rtl2832_wr(struct rtl2832_priv *priv, u8 reg, u8 *val, int len)
+{
+       int ret;
+       u8 buf[1+len];
+       struct i2c_msg msg[1] = {
+               {
+                       .addr = priv->cfg.i2c_addr,
+                       .flags = 0,
+                       .len = 1+len,
+                       .buf = buf,
+               }
+       };
+
+       buf[0] = reg;
+       memcpy(&buf[1], val, len);
+
+       ret = i2c_transfer(priv->i2c, msg, 1);
+       if (ret == 1) {
+               ret = 0;
+       } else {
+               warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len);
+               ret = -EREMOTEIO;
+       }
+       return ret;
+}
+
+/* read multiple hardware registers */
+static int rtl2832_rd(struct rtl2832_priv *priv, u8 reg, u8 *val, int len)
+{
+       int ret;
+       struct i2c_msg msg[2] = {
+               {
+                       .addr = priv->cfg.i2c_addr,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = &reg,
+               }, {
+                       .addr = priv->cfg.i2c_addr,
+                       .flags = I2C_M_RD,
+                       .len = len,
+                       .buf = val,
+               }
+       };
+
+       ret = i2c_transfer(priv->i2c, msg, 2);
+       if (ret == 2) {
+               ret = 0;
+       } else {
+               warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
+               ret = -EREMOTEIO;
+}
+return ret;
+}
+
+/* write multiple registers */
+static int rtl2832_wr_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val,
+       int len)
+{
+       int ret;
+
+
+       /* switch bank if needed */
+       if (page != priv->page) {
+               ret = rtl2832_wr(priv, 0x00, &page, 1);
+               if (ret)
+                       return ret;
+
+               priv->page = page;
+}
+
+return rtl2832_wr(priv, reg, val, len);
+}
+
+/* read multiple registers */
+static int rtl2832_rd_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val,
+       int len)
+{
+       int ret;
+
+       /* switch bank if needed */
+       if (page != priv->page) {
+               ret = rtl2832_wr(priv, 0x00, &page, 1);
+               if (ret)
+                       return ret;
+
+               priv->page = page;
+       }
+
+       return rtl2832_rd(priv, reg, val, len);
+}
+
+#if 0 /* currently not used */
+/* write single register */
+static int rtl2832_wr_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 val)
+{
+       return rtl2832_wr_regs(priv, reg, page, &val, 1);
+}
+#endif
+
+/* read single register */
+static int rtl2832_rd_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val)
+{
+       return rtl2832_rd_regs(priv, reg, page, val, 1);
+}
+
+int rtl2832_rd_demod_reg(struct rtl2832_priv *priv, int reg, u32 *val)
+{
+       int ret;
+
+       u8 reg_start_addr;
+       u8 msb, lsb;
+       u8 page;
+       u8 reading[4];
+       u32 reading_tmp;
+       int i;
+
+       u8 len;
+       u32 mask;
+
+       reg_start_addr = registers[reg].start_address;
+       msb = registers[reg].msb;
+       lsb = registers[reg].lsb;
+       page = registers[reg].page;
+
+       len = (msb >> 3) + 1;
+       mask = REG_MASK(msb - lsb);
+
+       ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len);
+       if (ret)
+               goto err;
+
+       reading_tmp = 0;
+       for (i = 0; i < len; i++)
+               reading_tmp |= reading[i] << ((len - 1 - i) * 8);
+
+       *val = (reading_tmp >> lsb) & mask;
+
+       return ret;
+
+err:
+       dbg("%s: failed=%d", __func__, ret);
+       return ret;
+
+}
+
+int rtl2832_wr_demod_reg(struct rtl2832_priv *priv, int reg, u32 val)
+{
+       int ret, i;
+       u8 len;
+       u8 reg_start_addr;
+       u8 msb, lsb;
+       u8 page;
+       u32 mask;
+
+
+       u8 reading[4];
+       u8 writing[4];
+       u32 reading_tmp;
+       u32 writing_tmp;
+
+
+       reg_start_addr = registers[reg].start_address;
+       msb = registers[reg].msb;
+       lsb = registers[reg].lsb;
+       page = registers[reg].page;
+
+       len = (msb >> 3) + 1;
+       mask = REG_MASK(msb - lsb);
+
+
+       ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len);
+       if (ret)
+               goto err;
+
+       reading_tmp = 0;
+       for (i = 0; i < len; i++)
+               reading_tmp |= reading[i] << ((len - 1 - i) * 8);
+
+       writing_tmp = reading_tmp & ~(mask << lsb);
+       writing_tmp |= ((val & mask) << lsb);
+
+
+       for (i = 0; i < len; i++)
+               writing[i] = (writing_tmp >> ((len - 1 - i) * 8)) & 0xff;
+
+       ret = rtl2832_wr_regs(priv, reg_start_addr, page, &writing[0], len);
+       if (ret)
+               goto err;
+
+       return ret;
+
+err:
+       dbg("%s: failed=%d", __func__, ret);
+       return ret;
+
+}
+
+
+static int rtl2832_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+       int ret;
+       struct rtl2832_priv *priv = fe->demodulator_priv;
+
+       dbg("%s: enable=%d", __func__, enable);
+
+       /* gate already open or close */
+       if (priv->i2c_gate_state == enable)
+               return 0;
+
+       ret = rtl2832_wr_demod_reg(priv, DVBT_IIC_REPEAT, (enable ? 0x1 : 0x0));
+       if (ret)
+               goto err;
+
+       priv->i2c_gate_state = enable;
+
+       return ret;
+err:
+       dbg("%s: failed=%d", __func__, ret);
+       return ret;
+}
+
+
+
+static int rtl2832_init(struct dvb_frontend *fe)
+{
+       struct rtl2832_priv *priv = fe->demodulator_priv;
+       int i, ret;
+
+       u8 en_bbin;
+       u64 pset_iffreq;
+
+       /* initialization values for the demodulator registers */
+       struct rtl2832_reg_value rtl2832_initial_regs[] = {
+               {DVBT_AD_EN_REG,                0x1},
+               {DVBT_AD_EN_REG1,               0x1},
+               {DVBT_RSD_BER_FAIL_VAL,         0x2800},
+               {DVBT_MGD_THD0,                 0x10},
+               {DVBT_MGD_THD1,                 0x20},
+               {DVBT_MGD_THD2,                 0x20},
+               {DVBT_MGD_THD3,                 0x40},
+               {DVBT_MGD_THD4,                 0x22},
+               {DVBT_MGD_THD5,                 0x32},
+               {DVBT_MGD_THD6,                 0x37},
+               {DVBT_MGD_THD7,                 0x39},
+               {DVBT_EN_BK_TRK,                0x0},
+               {DVBT_EN_CACQ_NOTCH,            0x0},
+               {DVBT_AD_AV_REF,                0x2a},
+               {DVBT_REG_PI,                   0x6},
+               {DVBT_PIP_ON,                   0x0},
+               {DVBT_CDIV_PH0,                 0x8},
+               {DVBT_CDIV_PH1,                 0x8},
+               {DVBT_SCALE1_B92,               0x4},
+               {DVBT_SCALE1_B93,               0xb0},
+               {DVBT_SCALE1_BA7,               0x78},
+               {DVBT_SCALE1_BA9,               0x28},
+               {DVBT_SCALE1_BAA,               0x59},
+               {DVBT_SCALE1_BAB,               0x83},
+               {DVBT_SCALE1_BAC,               0xd4},
+               {DVBT_SCALE1_BB0,               0x65},
+               {DVBT_SCALE1_BB1,               0x43},
+               {DVBT_KB_P1,                    0x1},
+               {DVBT_KB_P2,                    0x4},
+               {DVBT_KB_P3,                    0x7},
+               {DVBT_K1_CR_STEP12,             0xa},
+               {DVBT_REG_GPE,                  0x1},
+               {DVBT_SERIAL,                   0x0},
+               {DVBT_CDIV_PH0,                 0x9},
+               {DVBT_CDIV_PH1,                 0x9},
+               {DVBT_MPEG_IO_OPT_2_2,          0x0},
+               {DVBT_MPEG_IO_OPT_1_0,          0x0},
+               {DVBT_TRK_KS_P2,                0x4},
+               {DVBT_TRK_KS_I2,                0x7},
+               {DVBT_TR_THD_SET2,              0x6},
+               {DVBT_TRK_KC_I2,                0x5},
+               {DVBT_CR_THD_SET2,              0x1},
+               {DVBT_SPEC_INV,                 0x0},
+               {DVBT_DAGC_TRG_VAL,             0x5a},
+               {DVBT_AGC_TARG_VAL_0,           0x0},
+               {DVBT_AGC_TARG_VAL_8_1,         0x5a},
+               {DVBT_AAGC_LOOP_GAIN,           0x16},
+               {DVBT_LOOP_GAIN2_3_0,           0x6},
+               {DVBT_LOOP_GAIN2_4,             0x1},
+               {DVBT_LOOP_GAIN3,               0x16},
+               {DVBT_VTOP1,                    0x35},
+               {DVBT_VTOP2,                    0x21},
+               {DVBT_VTOP3,                    0x21},
+               {DVBT_KRF1,                     0x0},
+               {DVBT_KRF2,                     0x40},
+               {DVBT_KRF3,                     0x10},
+               {DVBT_KRF4,                     0x10},
+               {DVBT_IF_AGC_MIN,               0x80},
+               {DVBT_IF_AGC_MAX,               0x7f},
+               {DVBT_RF_AGC_MIN,               0x80},
+               {DVBT_RF_AGC_MAX,               0x7f},
+               {DVBT_POLAR_RF_AGC,             0x0},
+               {DVBT_POLAR_IF_AGC,             0x0},
+               {DVBT_AD7_SETTING,              0xe9bf},
+               {DVBT_EN_GI_PGA,                0x0},
+               {DVBT_THD_LOCK_UP,              0x0},
+               {DVBT_THD_LOCK_DW,              0x0},
+               {DVBT_THD_UP1,                  0x11},
+               {DVBT_THD_DW1,                  0xef},
+               {DVBT_INTER_CNT_LEN,            0xc},
+               {DVBT_GI_PGA_STATE,             0x0},
+               {DVBT_EN_AGC_PGA,               0x1},
+               {DVBT_IF_AGC_MAN,               0x0},
+       };
+
+
+       dbg("%s", __func__);
+
+       en_bbin = (priv->cfg.if_dvbt == 0 ? 0x1 : 0x0);
+
+       /*
+       * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22)
+       *               / CrystalFreqHz)
+       */
+       pset_iffreq = priv->cfg.if_dvbt % priv->cfg.xtal;
+       pset_iffreq *= 0x400000;
+       pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal);
+       pset_iffreq = pset_iffreq & 0x3fffff;
+
+
+
+       for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) {
+               ret = rtl2832_wr_demod_reg(priv, rtl2832_initial_regs[i].reg,
+                       rtl2832_initial_regs[i].value);
+               if (ret)
+                       goto err;
+       }
+
+       /* if frequency settings */
+       ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin);
+               if (ret)
+                       goto err;
+
+       ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq);
+               if (ret)
+                       goto err;
+
+       priv->sleeping = false;
+
+       return ret;
+
+err:
+       dbg("%s: failed=%d", __func__, ret);
+       return ret;
+}
+
+static int rtl2832_sleep(struct dvb_frontend *fe)
+{
+       struct rtl2832_priv *priv = fe->demodulator_priv;
+
+       dbg("%s", __func__);
+       priv->sleeping = true;
+       return 0;
+}
+
+int rtl2832_get_tune_settings(struct dvb_frontend *fe,
+       struct dvb_frontend_tune_settings *s)
+{
+       dbg("%s", __func__);
+       s->min_delay_ms = 1000;
+       s->step_size = fe->ops.info.frequency_stepsize * 2;
+       s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
+       return 0;
+}
+
+static int rtl2832_set_frontend(struct dvb_frontend *fe)
+{
+       struct rtl2832_priv *priv = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       int ret, i, j;
+       u64 bw_mode, num, num2;
+       u32 resamp_ratio, cfreq_off_ratio;
+
+
+       static u8 bw_params[3][32] = {
+       /* 6 MHz bandwidth */
+               {
+               0xf5, 0xff, 0x15, 0x38, 0x5d, 0x6d, 0x52, 0x07, 0xfa, 0x2f,
+               0x53, 0xf5, 0x3f, 0xca, 0x0b, 0x91, 0xea, 0x30, 0x63, 0xb2,
+               0x13, 0xda, 0x0b, 0xc4, 0x18, 0x7e, 0x16, 0x66, 0x08, 0x67,
+               0x19, 0xe0,
+               },
+
+       /*  7 MHz bandwidth */
+               {
+               0xe7, 0xcc, 0xb5, 0xba, 0xe8, 0x2f, 0x67, 0x61, 0x00, 0xaf,
+               0x86, 0xf2, 0xbf, 0x59, 0x04, 0x11, 0xb6, 0x33, 0xa4, 0x30,
+               0x15, 0x10, 0x0a, 0x42, 0x18, 0xf8, 0x17, 0xd9, 0x07, 0x22,
+               0x19, 0x10,
+               },
+
+       /*  8 MHz bandwidth */
+               {
+               0x09, 0xf6, 0xd2, 0xa7, 0x9a, 0xc9, 0x27, 0x77, 0x06, 0xbf,
+               0xec, 0xf4, 0x4f, 0x0b, 0xfc, 0x01, 0x63, 0x35, 0x54, 0xa7,
+               0x16, 0x66, 0x08, 0xb4, 0x19, 0x6e, 0x19, 0x65, 0x05, 0xc8,
+               0x19, 0xe0,
+               },
+       };
+
+
+       dbg("%s: frequency=%d bandwidth_hz=%d inversion=%d", __func__,
+               c->frequency, c->bandwidth_hz, c->inversion);
+
+
+       /* program tuner */
+       if (fe->ops.tuner_ops.set_params)
+               fe->ops.tuner_ops.set_params(fe);
+
+
+       switch (c->bandwidth_hz) {
+       case 6000000:
+               i = 0;
+               bw_mode = 48000000;
+               break;
+       case 7000000:
+               i = 1;
+               bw_mode = 56000000;
+               break;
+       case 8000000:
+               i = 2;
+               bw_mode = 64000000;
+               break;
+       default:
+               dbg("invalid bandwidth");
+               return -EINVAL;
+       }
+
+       for (j = 0; j < sizeof(bw_params[0]); j++) {
+               ret = rtl2832_wr_regs(priv, 0x1c+j, 1, &bw_params[i][j], 1);
+               if (ret)
+                       goto err;
+       }
+
+       /* calculate and set resample ratio
+       * RSAMP_RATIO = floor(CrystalFreqHz * 7 * pow(2, 22)
+       *       / ConstWithBandwidthMode)
+       */
+       num = priv->cfg.xtal * 7;
+       num *= 0x400000;
+       num = div_u64(num, bw_mode);
+       resamp_ratio =  num & 0x3ffffff;
+       ret = rtl2832_wr_demod_reg(priv, DVBT_RSAMP_RATIO, resamp_ratio);
+       if (ret)
+               goto err;
+
+       /* calculate and set cfreq off ratio
+       * CFREQ_OFF_RATIO = - floor(ConstWithBandwidthMode * pow(2, 20)
+       *       / (CrystalFreqHz * 7))
+       */
+       num = bw_mode << 20;
+       num2 = priv->cfg.xtal * 7;
+       num = div_u64(num, num2);
+       num = -num;
+       cfreq_off_ratio = num & 0xfffff;
+       ret = rtl2832_wr_demod_reg(priv, DVBT_CFREQ_OFF_RATIO, cfreq_off_ratio);
+       if (ret)
+               goto err;
+
+
+       /* soft reset */
+       ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1);
+       if (ret)
+               goto err;
+
+       ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0);
+       if (ret)
+               goto err;
+
+       return ret;
+err:
+       info("%s: failed=%d", __func__, ret);
+       return ret;
+}
+
+static int rtl2832_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+       struct rtl2832_priv *priv = fe->demodulator_priv;
+       int ret;
+       u32 tmp;
+       *status = 0;
+
+
+       dbg("%s", __func__);
+       if (priv->sleeping)
+               return 0;
+
+       ret = rtl2832_rd_demod_reg(priv, DVBT_FSM_STAGE, &tmp);
+       if (ret)
+               goto err;
+
+       if (tmp == 11) {
+               *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+                               FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+       }
+       /* TODO find out if this is also true for rtl2832? */
+       /*else if (tmp == 10) {
+               *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+                               FE_HAS_VITERBI;
+       }*/
+
+       return ret;
+err:
+       info("%s: failed=%d", __func__, ret);
+       return ret;
+}
+
+static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+       *snr = 0;
+       return 0;
+}
+
+static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+       *ber = 0;
+       return 0;
+}
+
+static int rtl2832_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+       *ucblocks = 0;
+       return 0;
+}
+
+
+static int rtl2832_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+       *strength = 0;
+       return 0;
+}
+
+static struct dvb_frontend_ops rtl2832_ops;
+
+static void rtl2832_release(struct dvb_frontend *fe)
+{
+       struct rtl2832_priv *priv = fe->demodulator_priv;
+
+       dbg("%s", __func__);
+       kfree(priv);
+}
+
+struct dvb_frontend *rtl2832_attach(const struct rtl2832_config *cfg,
+       struct i2c_adapter *i2c)
+{
+       struct rtl2832_priv *priv = NULL;
+       int ret = 0;
+       u8 tmp;
+
+       dbg("%s", __func__);
+
+       /* allocate memory for the internal state */
+       priv = kzalloc(sizeof(struct rtl2832_priv), GFP_KERNEL);
+       if (priv == NULL)
+               goto err;
+
+       /* setup the priv */
+       priv->i2c = i2c;
+       priv->tuner = cfg->tuner;
+       memcpy(&priv->cfg, cfg, sizeof(struct rtl2832_config));
+
+       /* check if the demod is there */
+       ret = rtl2832_rd_reg(priv, 0x00, 0x0, &tmp);
+       if (ret)
+               goto err;
+
+       /* create dvb_frontend */
+       memcpy(&priv->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops));
+       priv->fe.demodulator_priv = priv;
+
+       /* TODO implement sleep mode */
+       priv->sleeping = true;
+
+       return &priv->fe;
+err:
+       dbg("%s: failed=%d", __func__, ret);
+       kfree(priv);
+       return NULL;
+}
+EXPORT_SYMBOL(rtl2832_attach);
+
+static struct dvb_frontend_ops rtl2832_ops = {
+       .delsys = { SYS_DVBT },
+       .info = {
+               .name = "Realtek RTL2832 (DVB-T)",
+               .frequency_min    = 174000000,
+               .frequency_max    = 862000000,
+               .frequency_stepsize = 166667,
+               .caps = FE_CAN_FEC_1_2 |
+                       FE_CAN_FEC_2_3 |
+                       FE_CAN_FEC_3_4 |
+                       FE_CAN_FEC_5_6 |
+                       FE_CAN_FEC_7_8 |
+                       FE_CAN_FEC_AUTO |
+                       FE_CAN_QPSK |
+                       FE_CAN_QAM_16 |
+                       FE_CAN_QAM_64 |
+                       FE_CAN_QAM_AUTO |
+                       FE_CAN_TRANSMISSION_MODE_AUTO |
+                       FE_CAN_GUARD_INTERVAL_AUTO |
+                       FE_CAN_HIERARCHY_AUTO |
+                       FE_CAN_RECOVER |
+                       FE_CAN_MUTE_TS
+        },
+
+       .release = rtl2832_release,
+
+       .init = rtl2832_init,
+       .sleep = rtl2832_sleep,
+
+       .get_tune_settings = rtl2832_get_tune_settings,
+
+       .set_frontend = rtl2832_set_frontend,
+
+       .read_status = rtl2832_read_status,
+       .read_snr = rtl2832_read_snr,
+       .read_ber = rtl2832_read_ber,
+       .read_ucblocks = rtl2832_read_ucblocks,
+       .read_signal_strength = rtl2832_read_signal_strength,
+       .i2c_gate_ctrl = rtl2832_i2c_gate_ctrl,
+};
+
+MODULE_AUTHOR("Thomas Mair <mair.thomas86@gmail.com>");
+MODULE_DESCRIPTION("Realtek RTL2832 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.5");
diff --git a/drivers/media/dvb/frontends/rtl2832.h b/drivers/media/dvb/frontends/rtl2832.h
new file mode 100644 (file)
index 0000000..d94dc9a
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Realtek RTL2832 DVB-T demodulator driver
+ *
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@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.,
+ *     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL2832_H
+#define RTL2832_H
+
+#include <linux/dvb/frontend.h>
+
+struct rtl2832_config {
+       /*
+        * Demodulator I2C address.
+        */
+       u8 i2c_addr;
+
+       /*
+        * Xtal frequency.
+        * Hz
+        * 4000000, 16000000, 25000000, 28800000
+        */
+       u32 xtal;
+
+       /*
+        * IFs for all used modes.
+        * Hz
+        * 4570000, 4571429, 36000000, 36125000, 36166667, 44000000
+        */
+       u32 if_dvbt;
+
+       /*
+        */
+       u8 tuner;
+};
+
+
+#if defined(CONFIG_DVB_RTL2832) || \
+       (defined(CONFIG_DVB_RTL2832_MODULE) && defined(MODULE))
+extern struct dvb_frontend *rtl2832_attach(
+       const struct rtl2832_config *cfg,
+       struct i2c_adapter *i2c
+);
+
+extern struct i2c_adapter *rtl2832_get_tuner_i2c_adapter(
+       struct dvb_frontend *fe
+);
+#else
+static inline struct dvb_frontend *rtl2832_attach(
+       const struct rtl2832_config *config,
+       struct i2c_adapter *i2c
+)
+{
+       printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+       return NULL;
+}
+#endif
+
+
+#endif /* RTL2832_H */
diff --git a/drivers/media/dvb/frontends/rtl2832_priv.h b/drivers/media/dvb/frontends/rtl2832_priv.h
new file mode 100644 (file)
index 0000000..0ce9502
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Realtek RTL2832 DVB-T demodulator driver
+ *
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@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.,
+ *     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL2832_PRIV_H
+#define RTL2832_PRIV_H
+
+#include "dvb_frontend.h"
+#include "rtl2832.h"
+
+#define LOG_PREFIX "rtl2832"
+
+#undef dbg
+#define dbg(f, arg...) \
+do { \
+       if (rtl2832_debug)  \
+               printk(KERN_INFO     LOG_PREFIX": " f "\n" , ## arg); \
+} while (0)
+#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)
+
+struct rtl2832_priv {
+       struct i2c_adapter *i2c;
+       struct dvb_frontend fe;
+       struct rtl2832_config cfg;
+
+       bool i2c_gate_state;
+       bool sleeping;
+
+       u8 tuner;
+       u8 page; /* active register page */
+};
+
+struct rtl2832_reg_entry {
+       u8 page;
+       u8 start_address;
+       u8 msb;
+       u8 lsb;
+};
+
+struct rtl2832_reg_value {
+       int reg;
+       u32 value;
+};
+
+
+/* Demod register bit names */
+enum DVBT_REG_BIT_NAME {
+       DVBT_SOFT_RST,
+       DVBT_IIC_REPEAT,
+       DVBT_TR_WAIT_MIN_8K,
+       DVBT_RSD_BER_FAIL_VAL,
+       DVBT_EN_BK_TRK,
+       DVBT_REG_PI,
+       DVBT_REG_PFREQ_1_0,
+       DVBT_PD_DA8,
+       DVBT_LOCK_TH,
+       DVBT_BER_PASS_SCAL,
+       DVBT_CE_FFSM_BYPASS,
+       DVBT_ALPHAIIR_N,
+       DVBT_ALPHAIIR_DIF,
+       DVBT_EN_TRK_SPAN,
+       DVBT_LOCK_TH_LEN,
+       DVBT_CCI_THRE,
+       DVBT_CCI_MON_SCAL,
+       DVBT_CCI_M0,
+       DVBT_CCI_M1,
+       DVBT_CCI_M2,
+       DVBT_CCI_M3,
+       DVBT_SPEC_INIT_0,
+       DVBT_SPEC_INIT_1,
+       DVBT_SPEC_INIT_2,
+       DVBT_AD_EN_REG,
+       DVBT_AD_EN_REG1,
+       DVBT_EN_BBIN,
+       DVBT_MGD_THD0,
+       DVBT_MGD_THD1,
+       DVBT_MGD_THD2,
+       DVBT_MGD_THD3,
+       DVBT_MGD_THD4,
+       DVBT_MGD_THD5,
+       DVBT_MGD_THD6,
+       DVBT_MGD_THD7,
+       DVBT_EN_CACQ_NOTCH,
+       DVBT_AD_AV_REF,
+       DVBT_PIP_ON,
+       DVBT_SCALE1_B92,
+       DVBT_SCALE1_B93,
+       DVBT_SCALE1_BA7,
+       DVBT_SCALE1_BA9,
+       DVBT_SCALE1_BAA,
+       DVBT_SCALE1_BAB,
+       DVBT_SCALE1_BAC,
+       DVBT_SCALE1_BB0,
+       DVBT_SCALE1_BB1,
+       DVBT_KB_P1,
+       DVBT_KB_P2,
+       DVBT_KB_P3,
+       DVBT_OPT_ADC_IQ,
+       DVBT_AD_AVI,
+       DVBT_AD_AVQ,
+       DVBT_K1_CR_STEP12,
+       DVBT_TRK_KS_P2,
+       DVBT_TRK_KS_I2,
+       DVBT_TR_THD_SET2,
+       DVBT_TRK_KC_P2,
+       DVBT_TRK_KC_I2,
+       DVBT_CR_THD_SET2,
+       DVBT_PSET_IFFREQ,
+       DVBT_SPEC_INV,
+       DVBT_BW_INDEX,
+       DVBT_RSAMP_RATIO,
+       DVBT_CFREQ_OFF_RATIO,
+       DVBT_FSM_STAGE,
+       DVBT_RX_CONSTEL,
+       DVBT_RX_HIER,
+       DVBT_RX_C_RATE_LP,
+       DVBT_RX_C_RATE_HP,
+       DVBT_GI_IDX,
+       DVBT_FFT_MODE_IDX,
+       DVBT_RSD_BER_EST,
+       DVBT_CE_EST_EVM,
+       DVBT_RF_AGC_VAL,
+       DVBT_IF_AGC_VAL,
+       DVBT_DAGC_VAL,
+       DVBT_SFREQ_OFF,
+       DVBT_CFREQ_OFF,
+       DVBT_POLAR_RF_AGC,
+       DVBT_POLAR_IF_AGC,
+       DVBT_AAGC_HOLD,
+       DVBT_EN_RF_AGC,
+       DVBT_EN_IF_AGC,
+       DVBT_IF_AGC_MIN,
+       DVBT_IF_AGC_MAX,
+       DVBT_RF_AGC_MIN,
+       DVBT_RF_AGC_MAX,
+       DVBT_IF_AGC_MAN,
+       DVBT_IF_AGC_MAN_VAL,
+       DVBT_RF_AGC_MAN,
+       DVBT_RF_AGC_MAN_VAL,
+       DVBT_DAGC_TRG_VAL,
+       DVBT_AGC_TARG_VAL,
+       DVBT_LOOP_GAIN_3_0,
+       DVBT_LOOP_GAIN_4,
+       DVBT_VTOP,
+       DVBT_KRF,
+       DVBT_AGC_TARG_VAL_0,
+       DVBT_AGC_TARG_VAL_8_1,
+       DVBT_AAGC_LOOP_GAIN,
+       DVBT_LOOP_GAIN2_3_0,
+       DVBT_LOOP_GAIN2_4,
+       DVBT_LOOP_GAIN3,
+       DVBT_VTOP1,
+       DVBT_VTOP2,
+       DVBT_VTOP3,
+       DVBT_KRF1,
+       DVBT_KRF2,
+       DVBT_KRF3,
+       DVBT_KRF4,
+       DVBT_EN_GI_PGA,
+       DVBT_THD_LOCK_UP,
+       DVBT_THD_LOCK_DW,
+       DVBT_THD_UP1,
+       DVBT_THD_DW1,
+       DVBT_INTER_CNT_LEN,
+       DVBT_GI_PGA_STATE,
+       DVBT_EN_AGC_PGA,
+       DVBT_CKOUTPAR,
+       DVBT_CKOUT_PWR,
+       DVBT_SYNC_DUR,
+       DVBT_ERR_DUR,
+       DVBT_SYNC_LVL,
+       DVBT_ERR_LVL,
+       DVBT_VAL_LVL,
+       DVBT_SERIAL,
+       DVBT_SER_LSB,
+       DVBT_CDIV_PH0,
+       DVBT_CDIV_PH1,
+       DVBT_MPEG_IO_OPT_2_2,
+       DVBT_MPEG_IO_OPT_1_0,
+       DVBT_CKOUTPAR_PIP,
+       DVBT_CKOUT_PWR_PIP,
+       DVBT_SYNC_LVL_PIP,
+       DVBT_ERR_LVL_PIP,
+       DVBT_VAL_LVL_PIP,
+       DVBT_CKOUTPAR_PID,
+       DVBT_CKOUT_PWR_PID,
+       DVBT_SYNC_LVL_PID,
+       DVBT_ERR_LVL_PID,
+       DVBT_VAL_LVL_PID,
+       DVBT_SM_PASS,
+       DVBT_UPDATE_REG_2,
+       DVBT_BTHD_P3,
+       DVBT_BTHD_D3,
+       DVBT_FUNC4_REG0,
+       DVBT_FUNC4_REG1,
+       DVBT_FUNC4_REG2,
+       DVBT_FUNC4_REG3,
+       DVBT_FUNC4_REG4,
+       DVBT_FUNC4_REG5,
+       DVBT_FUNC4_REG6,
+       DVBT_FUNC4_REG7,
+       DVBT_FUNC4_REG8,
+       DVBT_FUNC4_REG9,
+       DVBT_FUNC4_REG10,
+       DVBT_FUNC5_REG0,
+       DVBT_FUNC5_REG1,
+       DVBT_FUNC5_REG2,
+       DVBT_FUNC5_REG3,
+       DVBT_FUNC5_REG4,
+       DVBT_FUNC5_REG5,
+       DVBT_FUNC5_REG6,
+       DVBT_FUNC5_REG7,
+       DVBT_FUNC5_REG8,
+       DVBT_FUNC5_REG9,
+       DVBT_FUNC5_REG10,
+       DVBT_FUNC5_REG11,
+       DVBT_FUNC5_REG12,
+       DVBT_FUNC5_REG13,
+       DVBT_FUNC5_REG14,
+       DVBT_FUNC5_REG15,
+       DVBT_FUNC5_REG16,
+       DVBT_FUNC5_REG17,
+       DVBT_FUNC5_REG18,
+       DVBT_AD7_SETTING,
+       DVBT_RSSI_R,
+       DVBT_ACI_DET_IND,
+       DVBT_REG_MON,
+       DVBT_REG_MONSEL,
+       DVBT_REG_GPE,
+       DVBT_REG_GPO,
+       DVBT_REG_4MSEL,
+       DVBT_TEST_REG_1,
+       DVBT_TEST_REG_2,
+       DVBT_TEST_REG_3,
+       DVBT_TEST_REG_4,
+       DVBT_REG_BIT_NAME_ITEM_TERMINATOR,
+};
+
+#endif /* RTL2832_PRIV_H */
index 2322257c69ae33bb5af5ed2ea200b82a671ff18a..e2fec9ebf947d6b5c0ca03eeb50befd233f6f19a 100644 (file)
@@ -634,7 +634,6 @@ static int s5h1420_set_frontend(struct dvb_frontend *fe)
        struct s5h1420_state* state = fe->demodulator_priv;
        int frequency_delta;
        struct dvb_frontend_tune_settings fesettings;
-       uint8_t clock_setting;
 
        dprintk("enter %s\n", __func__);
 
@@ -679,25 +678,6 @@ static int s5h1420_set_frontend(struct dvb_frontend *fe)
        else
                state->fclk = 44000000;
 
-       /* Clock */
-       switch (state->fclk) {
-       default:
-       case 88000000:
-               clock_setting = 80;
-               break;
-       case 86000000:
-               clock_setting = 78;
-               break;
-       case 80000000:
-               clock_setting = 72;
-               break;
-       case 59000000:
-               clock_setting = 51;
-               break;
-       case 44000000:
-               clock_setting = 36;
-               break;
-       }
        dprintk("pll01: %d, ToneFreq: %d\n", state->fclk/1000000 - 8, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32));
        s5h1420_writereg(state, PLL01, state->fclk/1000000 - 8);
        s5h1420_writereg(state, PLL02, 0x40);
index 8b0dc74a3298c8ec03d99cf40b1faa2be0e8a2ba..5d7f8a9b451b8ec93fc596a6d813004b8866cd87 100644 (file)
@@ -1129,7 +1129,6 @@ static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber)
        struct stb0899_internal *internal       = &state->internal;
 
        u8  lsb, msb;
-       u32 i;
 
        *ber = 0;
 
@@ -1137,14 +1136,9 @@ static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber)
        case SYS_DVBS:
        case SYS_DSS:
                if (internal->lock) {
-                       /* average 5 BER values */
-                       for (i = 0; i < 5; i++) {
-                               msleep(100);
-                               lsb = stb0899_read_reg(state, STB0899_ECNT1L);
-                               msb = stb0899_read_reg(state, STB0899_ECNT1M);
-                               *ber += MAKEWORD16(msb, lsb);
-                       }
-                       *ber /= 5;
+                       lsb = stb0899_read_reg(state, STB0899_ECNT1L);
+                       msb = stb0899_read_reg(state, STB0899_ECNT1M);
+                       *ber = MAKEWORD16(msb, lsb);
                        /* Viterbi Check        */
                        if (STB0899_GETFIELD(VSTATUS_PRFVIT, internal->v_status)) {
                                /* Error Rate           */
@@ -1157,13 +1151,9 @@ static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber)
                break;
        case SYS_DVBS2:
                if (internal->lock) {
-                       /* Average 5 PER values */
-                       for (i = 0; i < 5; i++) {
-                               msleep(100);
-                               lsb = stb0899_read_reg(state, STB0899_ECNT1L);
-                               msb = stb0899_read_reg(state, STB0899_ECNT1M);
-                               *ber += MAKEWORD16(msb, lsb);
-                       }
+                       lsb = stb0899_read_reg(state, STB0899_ECNT1L);
+                       msb = stb0899_read_reg(state, STB0899_ECNT1M);
+                       *ber = MAKEWORD16(msb, lsb);
                        /* ber = ber * 10 ^ 7   */
                        *ber *= 10000000;
                        *ber /= (-1 + (1 << (4 + 2 * STB0899_GETFIELD(NOE, internal->err_ctrl))));
index fdd20c7737b57a968d4e5e8edf4d9900ef859d2b..2a8aaeb1112dc10ef1267531c36eb14bbd1c8989 100644 (file)
@@ -1584,7 +1584,7 @@ static int stv0367ter_algo(struct dvb_frontend *fe)
        struct stv0367ter_state *ter_state = state->ter_state;
        int offset = 0, tempo = 0;
        u8 u_var;
-       u8 /*constell,*/ counter, tps_rcvd[2];
+       u8 /*constell,*/ counter;
        s8 step;
        s32 timing_offset = 0;
        u32 trl_nomrate = 0, InternalFreq = 0, temp = 0;
@@ -1709,9 +1709,6 @@ static int stv0367ter_algo(struct dvb_frontend *fe)
                return 0;
 
        ter_state->state = FE_TER_LOCKOK;
-       /* update results */
-       tps_rcvd[0] = stv0367_readreg(state, R367TER_TPS_RCVD2);
-       tps_rcvd[1] = stv0367_readreg(state, R367TER_TPS_RCVD3);
 
        ter_state->mode = stv0367_readbits(state, F367TER_SYR_MODE);
        ter_state->guard = stv0367_readbits(state, F367TER_SYR_GUARD);
index d79e69f65cbb6ee39d9c1b28b6917b37e5182a1b..ea86a5603e5756106a342a18668d3b8889ebdd1e 100644 (file)
@@ -3172,7 +3172,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
        enum stv090x_signal_state signal_state = STV090x_NOCARRIER;
        u32 reg;
        s32 agc1_power, power_iq = 0, i;
-       int lock = 0, low_sr = 0, no_signal = 0;
+       int lock = 0, low_sr = 0;
 
        reg = STV090x_READ_DEMOD(state, TSCFGH);
        STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 1); /* Stop path 1 stream merger */
@@ -3413,7 +3413,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
                                goto err;
                } else {
                        signal_state = STV090x_NODATA;
-                       no_signal = stv090x_chk_signal(state);
+                       stv090x_chk_signal(state);
                }
        }
        return signal_state;
index c21bc92d2811219d11c468ece8fc9a171498a131..703c3d05f9f453f67f054234f4e86818743ff5f9 100644 (file)
 
 #include "tda10071_priv.h"
 
-int tda10071_debug;
-module_param_named(debug, tda10071_debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
 static struct dvb_frontend_ops tda10071_ops;
 
 /* write multiple registers */
@@ -48,7 +44,8 @@ static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
        if (ret == 1) {
                ret = 0;
        } else {
-               warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len);
+               dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
+                               "len=%d\n", KBUILD_MODNAME, ret, reg, len);
                ret = -EREMOTEIO;
        }
        return ret;
@@ -79,7 +76,8 @@ static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
                memcpy(val, buf, len);
                ret = 0;
        } else {
-               warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
+               dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
+                               "len=%d\n", KBUILD_MODNAME, ret, reg, len);
                ret = -EREMOTEIO;
        }
        return ret;
@@ -170,7 +168,7 @@ static int tda10071_cmd_execute(struct tda10071_priv *priv,
                usleep_range(200, 5000);
        }
 
-       dbg("%s: loop=%d", __func__, i);
+       dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
 
        if (i == 0) {
                ret = -ETIMEDOUT;
@@ -179,7 +177,7 @@ static int tda10071_cmd_execute(struct tda10071_priv *priv,
 
        return ret;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -196,7 +194,8 @@ static int tda10071_set_tone(struct dvb_frontend *fe,
                goto error;
        }
 
-       dbg("%s: tone_mode=%d", __func__, fe_sec_tone_mode);
+       dev_dbg(&priv->i2c->dev, "%s: tone_mode=%d\n", __func__,
+                       fe_sec_tone_mode);
 
        switch (fe_sec_tone_mode) {
        case SEC_TONE_ON:
@@ -206,24 +205,25 @@ static int tda10071_set_tone(struct dvb_frontend *fe,
                tone = 0;
                break;
        default:
-               dbg("%s: invalid fe_sec_tone_mode", __func__);
+               dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_tone_mode\n",
+                               __func__);
                ret = -EINVAL;
                goto error;
        }
 
-       cmd.args[0x00] = CMD_LNB_PCB_CONFIG;
-       cmd.args[0x01] = 0;
-       cmd.args[0x02] = 0x00;
-       cmd.args[0x03] = 0x00;
-       cmd.args[0x04] = tone;
-       cmd.len = 0x05;
+       cmd.args[0] = CMD_LNB_PCB_CONFIG;
+       cmd.args[1] = 0;
+       cmd.args[2] = 0x00;
+       cmd.args[3] = 0x00;
+       cmd.args[4] = tone;
+       cmd.len = 5;
        ret = tda10071_cmd_execute(priv, &cmd);
        if (ret)
                goto error;
 
        return ret;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -240,7 +240,7 @@ static int tda10071_set_voltage(struct dvb_frontend *fe,
                goto error;
        }
 
-       dbg("%s: voltage=%d", __func__, fe_sec_voltage);
+       dev_dbg(&priv->i2c->dev, "%s: voltage=%d\n", __func__, fe_sec_voltage);
 
        switch (fe_sec_voltage) {
        case SEC_VOLTAGE_13:
@@ -253,22 +253,23 @@ static int tda10071_set_voltage(struct dvb_frontend *fe,
                voltage = 0;
                break;
        default:
-               dbg("%s: invalid fe_sec_voltage", __func__);
+               dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_voltage\n",
+                               __func__);
                ret = -EINVAL;
                goto error;
        };
 
-       cmd.args[0x00] = CMD_LNB_SET_DC_LEVEL;
-       cmd.args[0x01] = 0;
-       cmd.args[0x02] = voltage;
-       cmd.len = 0x03;
+       cmd.args[0] = CMD_LNB_SET_DC_LEVEL;
+       cmd.args[1] = 0;
+       cmd.args[2] = voltage;
+       cmd.len = 3;
        ret = tda10071_cmd_execute(priv, &cmd);
        if (ret)
                goto error;
 
        return ret;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -285,9 +286,10 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe,
                goto error;
        }
 
-       dbg("%s: msg_len=%d", __func__, diseqc_cmd->msg_len);
+       dev_dbg(&priv->i2c->dev, "%s: msg_len=%d\n", __func__,
+                       diseqc_cmd->msg_len);
 
-       if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 16) {
+       if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) {
                ret = -EINVAL;
                goto error;
        }
@@ -301,7 +303,7 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe,
                usleep_range(10000, 20000);
        }
 
-       dbg("%s: loop=%d", __func__, i);
+       dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
 
        if (i == 0) {
                ret = -ETIMEDOUT;
@@ -312,22 +314,22 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe,
        if (ret)
                goto error;
 
-       cmd.args[0x00] = CMD_LNB_SEND_DISEQC;
-       cmd.args[0x01] = 0;
-       cmd.args[0x02] = 0;
-       cmd.args[0x03] = 0;
-       cmd.args[0x04] = 2;
-       cmd.args[0x05] = 0;
-       cmd.args[0x06] = diseqc_cmd->msg_len;
-       memcpy(&cmd.args[0x07], diseqc_cmd->msg, diseqc_cmd->msg_len);
-       cmd.len = 0x07 + diseqc_cmd->msg_len;
+       cmd.args[0] = CMD_LNB_SEND_DISEQC;
+       cmd.args[1] = 0;
+       cmd.args[2] = 0;
+       cmd.args[3] = 0;
+       cmd.args[4] = 2;
+       cmd.args[5] = 0;
+       cmd.args[6] = diseqc_cmd->msg_len;
+       memcpy(&cmd.args[7], diseqc_cmd->msg, diseqc_cmd->msg_len);
+       cmd.len = 7 + diseqc_cmd->msg_len;
        ret = tda10071_cmd_execute(priv, &cmd);
        if (ret)
                goto error;
 
        return ret;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -344,7 +346,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe,
                goto error;
        }
 
-       dbg("%s:", __func__);
+       dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
 
        /* wait LNB RX */
        for (i = 500, tmp = 0; i && !tmp; i--) {
@@ -355,7 +357,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe,
                usleep_range(10000, 20000);
        }
 
-       dbg("%s: loop=%d", __func__, i);
+       dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
 
        if (i == 0) {
                ret = -ETIMEDOUT;
@@ -372,9 +374,9 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe,
                reply->msg_len = sizeof(reply->msg); /* truncate API max */
 
        /* read reply */
-       cmd.args[0x00] = CMD_LNB_UPDATE_REPLY;
-       cmd.args[0x01] = 0;
-       cmd.len = 0x02;
+       cmd.args[0] = CMD_LNB_UPDATE_REPLY;
+       cmd.args[1] = 0;
+       cmd.len = 2;
        ret = tda10071_cmd_execute(priv, &cmd);
        if (ret)
                goto error;
@@ -385,7 +387,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe,
 
        return ret;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -402,7 +404,8 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe,
                goto error;
        }
 
-       dbg("%s: fe_sec_mini_cmd=%d", __func__, fe_sec_mini_cmd);
+       dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__,
+                       fe_sec_mini_cmd);
 
        switch (fe_sec_mini_cmd) {
        case SEC_MINI_A:
@@ -412,7 +415,8 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe,
                burst = 1;
                break;
        default:
-               dbg("%s: invalid fe_sec_mini_cmd", __func__);
+               dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_mini_cmd\n",
+                               __func__);
                ret = -EINVAL;
                goto error;
        }
@@ -426,7 +430,7 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe,
                usleep_range(10000, 20000);
        }
 
-       dbg("%s: loop=%d", __func__, i);
+       dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
 
        if (i == 0) {
                ret = -ETIMEDOUT;
@@ -437,17 +441,17 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe,
        if (ret)
                goto error;
 
-       cmd.args[0x00] = CMD_LNB_SEND_TONEBURST;
-       cmd.args[0x01] = 0;
-       cmd.args[0x02] = burst;
-       cmd.len = 0x03;
+       cmd.args[0] = CMD_LNB_SEND_TONEBURST;
+       cmd.args[1] = 0;
+       cmd.args[2] = burst;
+       cmd.len = 3;
        ret = tda10071_cmd_execute(priv, &cmd);
        if (ret)
                goto error;
 
        return ret;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -481,7 +485,7 @@ static int tda10071_read_status(struct dvb_frontend *fe, fe_status_t *status)
 
        return ret;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -506,7 +510,7 @@ static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr)
 
        return ret;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -523,9 +527,9 @@ static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
                goto error;
        }
 
-       cmd.args[0x00] = CMD_GET_AGCACC;
-       cmd.args[0x01] = 0;
-       cmd.len = 0x02;
+       cmd.args[0] = CMD_GET_AGCACC;
+       cmd.args[1] = 0;
+       cmd.len = 2;
        ret = tda10071_cmd_execute(priv, &cmd);
        if (ret)
                goto error;
@@ -545,7 +549,7 @@ static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
 
        return ret;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -583,17 +587,18 @@ static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber)
                goto error;
 
        if (priv->meas_count[i] == tmp) {
-               dbg("%s: meas not ready=%02x", __func__, tmp);
+               dev_dbg(&priv->i2c->dev, "%s: meas not ready=%02x\n", __func__,
+                               tmp);
                *ber = priv->ber;
                return 0;
        } else {
                priv->meas_count[i] = tmp;
        }
 
-       cmd.args[0x00] = CMD_BER_UPDATE_COUNTERS;
-       cmd.args[0x01] = 0;
-       cmd.args[0x02] = i;
-       cmd.len = 0x03;
+       cmd.args[0] = CMD_BER_UPDATE_COUNTERS;
+       cmd.args[1] = 0;
+       cmd.args[2] = i;
+       cmd.len = 3;
        ret = tda10071_cmd_execute(priv, &cmd);
        if (ret)
                goto error;
@@ -612,7 +617,7 @@ static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber)
 
        return ret;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -632,7 +637,7 @@ static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
 
        return ret;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -644,10 +649,11 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
        int ret, i;
        u8 mode, rolloff, pilot, inversion, div;
 
-       dbg("%s: delivery_system=%d modulation=%d frequency=%d " \
-               "symbol_rate=%d inversion=%d pilot=%d rolloff=%d", __func__,
-               c->delivery_system, c->modulation, c->frequency,
-               c->symbol_rate, c->inversion, c->pilot, c->rolloff);
+       dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d modulation=%d " \
+               "frequency=%d symbol_rate=%d inversion=%d pilot=%d " \
+               "rolloff=%d\n", __func__, c->delivery_system, c->modulation,
+               c->frequency, c->symbol_rate, c->inversion, c->pilot,
+               c->rolloff);
 
        priv->delivery_system = SYS_UNDEFINED;
 
@@ -669,7 +675,7 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
                inversion = 3;
                break;
        default:
-               dbg("%s: invalid inversion", __func__);
+               dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n", __func__);
                ret = -EINVAL;
                goto error;
        }
@@ -692,7 +698,8 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
                        break;
                case ROLLOFF_AUTO:
                default:
-                       dbg("%s: invalid rolloff", __func__);
+                       dev_dbg(&priv->i2c->dev, "%s: invalid rolloff\n",
+                                       __func__);
                        ret = -EINVAL;
                        goto error;
                }
@@ -708,13 +715,15 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
                        pilot = 2;
                        break;
                default:
-                       dbg("%s: invalid pilot", __func__);
+                       dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n",
+                                       __func__);
                        ret = -EINVAL;
                        goto error;
                }
                break;
        default:
-               dbg("%s: invalid delivery_system", __func__);
+               dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
+                               __func__);
                ret = -EINVAL;
                goto error;
        }
@@ -724,13 +733,15 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
                        c->modulation == TDA10071_MODCOD[i].modulation &&
                        c->fec_inner == TDA10071_MODCOD[i].fec) {
                        mode = TDA10071_MODCOD[i].val;
-                       dbg("%s: mode found=%02x", __func__, mode);
+                       dev_dbg(&priv->i2c->dev, "%s: mode found=%02x\n",
+                                       __func__, mode);
                        break;
                }
        }
 
        if (mode == 0xff) {
-               dbg("%s: invalid parameter combination", __func__);
+               dev_dbg(&priv->i2c->dev, "%s: invalid parameter combination\n",
+                               __func__);
                ret = -EINVAL;
                goto error;
        }
@@ -748,22 +759,22 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
        if (ret)
                goto error;
 
-       cmd.args[0x00] = CMD_CHANGE_CHANNEL;
-       cmd.args[0x01] = 0;
-       cmd.args[0x02] = mode;
-       cmd.args[0x03] = (c->frequency >> 16) & 0xff;
-       cmd.args[0x04] = (c->frequency >>  8) & 0xff;
-       cmd.args[0x05] = (c->frequency >>  0) & 0xff;
-       cmd.args[0x06] = ((c->symbol_rate / 1000) >> 8) & 0xff;
-       cmd.args[0x07] = ((c->symbol_rate / 1000) >> 0) & 0xff;
-       cmd.args[0x08] = (tda10071_ops.info.frequency_tolerance >> 8) & 0xff;
-       cmd.args[0x09] = (tda10071_ops.info.frequency_tolerance >> 0) & 0xff;
-       cmd.args[0x0a] = rolloff;
-       cmd.args[0x0b] = inversion;
-       cmd.args[0x0c] = pilot;
-       cmd.args[0x0d] = 0x00;
-       cmd.args[0x0e] = 0x00;
-       cmd.len = 0x0f;
+       cmd.args[0] = CMD_CHANGE_CHANNEL;
+       cmd.args[1] = 0;
+       cmd.args[2] = mode;
+       cmd.args[3] = (c->frequency >> 16) & 0xff;
+       cmd.args[4] = (c->frequency >>  8) & 0xff;
+       cmd.args[5] = (c->frequency >>  0) & 0xff;
+       cmd.args[6] = ((c->symbol_rate / 1000) >> 8) & 0xff;
+       cmd.args[7] = ((c->symbol_rate / 1000) >> 0) & 0xff;
+       cmd.args[8] = (tda10071_ops.info.frequency_tolerance >> 8) & 0xff;
+       cmd.args[9] = (tda10071_ops.info.frequency_tolerance >> 0) & 0xff;
+       cmd.args[10] = rolloff;
+       cmd.args[11] = inversion;
+       cmd.args[12] = pilot;
+       cmd.args[13] = 0x00;
+       cmd.args[14] = 0x00;
+       cmd.len = 15;
        ret = tda10071_cmd_execute(priv, &cmd);
        if (ret)
                goto error;
@@ -772,7 +783,7 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
 
        return ret;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -829,7 +840,7 @@ static int tda10071_get_frontend(struct dvb_frontend *fe)
 
        return ret;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -915,10 +926,10 @@ static int tda10071_init(struct dvb_frontend *fe)
                                goto error;
                }
 
-               cmd.args[0x00] = CMD_SET_SLEEP_MODE;
-               cmd.args[0x01] = 0;
-               cmd.args[0x02] = 0;
-               cmd.len = 0x03;
+               cmd.args[0] = CMD_SET_SLEEP_MODE;
+               cmd.args[1] = 0;
+               cmd.args[2] = 0;
+               cmd.len = 3;
                ret = tda10071_cmd_execute(priv, &cmd);
                if (ret)
                        goto error;
@@ -929,10 +940,11 @@ static int tda10071_init(struct dvb_frontend *fe)
                /* request the firmware, this will block and timeout */
                ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
                if (ret) {
-                       err("did not find the firmware file. (%s) "
-                               "Please see linux/Documentation/dvb/ for more" \
-                               " details on firmware-problems. (%d)",
-                               fw_file, ret);
+                       dev_err(&priv->i2c->dev, "%s: did not find the " \
+                                       "firmware file. (%s) Please see " \
+                                       "linux/Documentation/dvb/ for more " \
+                                       "details on firmware-problems. (%d)\n",
+                                       KBUILD_MODNAME, fw_file, ret);
                        goto error;
                }
 
@@ -961,10 +973,11 @@ static int tda10071_init(struct dvb_frontend *fe)
                if (ret)
                        goto error_release_firmware;
 
-               info("found a '%s' in cold state, will try to load a firmware",
-                       tda10071_ops.info.name);
-
-               info("downloading firmware from file '%s'", fw_file);
+               dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state, " \
+                               "will try to load a firmware\n", KBUILD_MODNAME,
+                               tda10071_ops.info.name);
+               dev_info(&priv->i2c->dev, "%s: downloading firmware from " \
+                               "file '%s'\n", KBUILD_MODNAME, fw_file);
 
                /* do not download last byte */
                fw_size = fw->size - 1;
@@ -978,7 +991,9 @@ static int tda10071_init(struct dvb_frontend *fe)
                        ret = tda10071_wr_regs(priv, 0xfa,
                                (u8 *) &fw->data[fw_size - remaining], len);
                        if (ret) {
-                               err("firmware download failed=%d", ret);
+                               dev_err(&priv->i2c->dev, "%s: firmware " \
+                                               "download failed=%d\n",
+                                               KBUILD_MODNAME, ret);
                                if (ret)
                                        goto error_release_firmware;
                        }
@@ -1002,15 +1017,16 @@ static int tda10071_init(struct dvb_frontend *fe)
                        goto error;
 
                if (tmp) {
-                       info("firmware did not run");
+                       dev_info(&priv->i2c->dev, "%s: firmware did not run\n",
+                                       KBUILD_MODNAME);
                        ret = -EFAULT;
                        goto error;
                } else {
                        priv->warm = 1;
                }
 
-               cmd.args[0x00] = CMD_GET_FW_VERSION;
-               cmd.len = 0x01;
+               cmd.args[0] = CMD_GET_FW_VERSION;
+               cmd.len = 1;
                ret = tda10071_cmd_execute(priv, &cmd);
                if (ret)
                        goto error;
@@ -1019,54 +1035,55 @@ static int tda10071_init(struct dvb_frontend *fe)
                if (ret)
                        goto error;
 
-               info("firmware version %d.%d.%d.%d",
-                       buf[0], buf[1], buf[2], buf[3]);
-               info("found a '%s' in warm state.", tda10071_ops.info.name);
+               dev_info(&priv->i2c->dev, "%s: firmware version %d.%d.%d.%d\n",
+                               KBUILD_MODNAME, buf[0], buf[1], buf[2], buf[3]);
+               dev_info(&priv->i2c->dev, "%s: found a '%s' in warm state\n",
+                               KBUILD_MODNAME, tda10071_ops.info.name);
 
                ret = tda10071_rd_regs(priv, 0x81, buf, 2);
                if (ret)
                        goto error;
 
-               cmd.args[0x00] = CMD_DEMOD_INIT;
-               cmd.args[0x01] = ((priv->cfg.xtal / 1000) >> 8) & 0xff;
-               cmd.args[0x02] = ((priv->cfg.xtal / 1000) >> 0) & 0xff;
-               cmd.args[0x03] = buf[0];
-               cmd.args[0x04] = buf[1];
-               cmd.args[0x05] = priv->cfg.pll_multiplier;
-               cmd.args[0x06] = priv->cfg.spec_inv;
-               cmd.args[0x07] = 0x00;
-               cmd.len = 0x08;
+               cmd.args[0] = CMD_DEMOD_INIT;
+               cmd.args[1] = ((priv->cfg.xtal / 1000) >> 8) & 0xff;
+               cmd.args[2] = ((priv->cfg.xtal / 1000) >> 0) & 0xff;
+               cmd.args[3] = buf[0];
+               cmd.args[4] = buf[1];
+               cmd.args[5] = priv->cfg.pll_multiplier;
+               cmd.args[6] = priv->cfg.spec_inv;
+               cmd.args[7] = 0x00;
+               cmd.len = 8;
                ret = tda10071_cmd_execute(priv, &cmd);
                if (ret)
                        goto error;
 
-               cmd.args[0x00] = CMD_TUNER_INIT;
-               cmd.args[0x01] = 0x00;
-               cmd.args[0x02] = 0x00;
-               cmd.args[0x03] = 0x00;
-               cmd.args[0x04] = 0x00;
-               cmd.args[0x05] = 0x14;
-               cmd.args[0x06] = 0x00;
-               cmd.args[0x07] = 0x03;
-               cmd.args[0x08] = 0x02;
-               cmd.args[0x09] = 0x02;
-               cmd.args[0x0a] = 0x00;
-               cmd.args[0x0b] = 0x00;
-               cmd.args[0x0c] = 0x00;
-               cmd.args[0x0d] = 0x00;
-               cmd.args[0x0e] = 0x00;
-               cmd.len = 0x0f;
+               cmd.args[0] = CMD_TUNER_INIT;
+               cmd.args[1] = 0x00;
+               cmd.args[2] = 0x00;
+               cmd.args[3] = 0x00;
+               cmd.args[4] = 0x00;
+               cmd.args[5] = 0x14;
+               cmd.args[6] = 0x00;
+               cmd.args[7] = 0x03;
+               cmd.args[8] = 0x02;
+               cmd.args[9] = 0x02;
+               cmd.args[10] = 0x00;
+               cmd.args[11] = 0x00;
+               cmd.args[12] = 0x00;
+               cmd.args[13] = 0x00;
+               cmd.args[14] = 0x00;
+               cmd.len = 15;
                ret = tda10071_cmd_execute(priv, &cmd);
                if (ret)
                        goto error;
 
-               cmd.args[0x00] = CMD_MPEG_CONFIG;
-               cmd.args[0x01] = 0;
-               cmd.args[0x02] = priv->cfg.ts_mode;
-               cmd.args[0x03] = 0x00;
-               cmd.args[0x04] = 0x04;
-               cmd.args[0x05] = 0x00;
-               cmd.len = 0x06;
+               cmd.args[0] = CMD_MPEG_CONFIG;
+               cmd.args[1] = 0;
+               cmd.args[2] = priv->cfg.ts_mode;
+               cmd.args[3] = 0x00;
+               cmd.args[4] = 0x04;
+               cmd.args[5] = 0x00;
+               cmd.len = 6;
                ret = tda10071_cmd_execute(priv, &cmd);
                if (ret)
                        goto error;
@@ -1075,27 +1092,27 @@ static int tda10071_init(struct dvb_frontend *fe)
                if (ret)
                        goto error;
 
-               cmd.args[0x00] = CMD_LNB_CONFIG;
-               cmd.args[0x01] = 0;
-               cmd.args[0x02] = 150;
-               cmd.args[0x03] = 3;
-               cmd.args[0x04] = 22;
-               cmd.args[0x05] = 1;
-               cmd.args[0x06] = 1;
-               cmd.args[0x07] = 30;
-               cmd.args[0x08] = 30;
-               cmd.args[0x09] = 30;
-               cmd.args[0x0a] = 30;
-               cmd.len = 0x0b;
+               cmd.args[0] = CMD_LNB_CONFIG;
+               cmd.args[1] = 0;
+               cmd.args[2] = 150;
+               cmd.args[3] = 3;
+               cmd.args[4] = 22;
+               cmd.args[5] = 1;
+               cmd.args[6] = 1;
+               cmd.args[7] = 30;
+               cmd.args[8] = 30;
+               cmd.args[9] = 30;
+               cmd.args[10] = 30;
+               cmd.len = 11;
                ret = tda10071_cmd_execute(priv, &cmd);
                if (ret)
                        goto error;
 
-               cmd.args[0x00] = CMD_BER_CONTROL;
-               cmd.args[0x01] = 0;
-               cmd.args[0x02] = 14;
-               cmd.args[0x03] = 14;
-               cmd.len = 0x04;
+               cmd.args[0] = CMD_BER_CONTROL;
+               cmd.args[1] = 0;
+               cmd.args[2] = 14;
+               cmd.args[3] = 14;
+               cmd.len = 4;
                ret = tda10071_cmd_execute(priv, &cmd);
                if (ret)
                        goto error;
@@ -1105,7 +1122,7 @@ static int tda10071_init(struct dvb_frontend *fe)
 error_release_firmware:
        release_firmware(fw);
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -1132,10 +1149,10 @@ static int tda10071_sleep(struct dvb_frontend *fe)
                goto error;
        }
 
-       cmd.args[0x00] = CMD_SET_SLEEP_MODE;
-       cmd.args[0x01] = 0;
-       cmd.args[0x02] = 1;
-       cmd.len = 0x03;
+       cmd.args[0] = CMD_SET_SLEEP_MODE;
+       cmd.args[1] = 0;
+       cmd.args[2] = 1;
+       cmd.len = 3;
        ret = tda10071_cmd_execute(priv, &cmd);
        if (ret)
                goto error;
@@ -1149,7 +1166,7 @@ static int tda10071_sleep(struct dvb_frontend *fe)
 
        return ret;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
        return ret;
 }
 
@@ -1208,7 +1225,7 @@ struct dvb_frontend *tda10071_attach(const struct tda10071_config *config,
 
        return &priv->fe;
 error:
-       dbg("%s: failed=%d", __func__, ret);
+       dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
        kfree(priv);
        return NULL;
 }
index 93c5e6317f076f2b309db44d2f0786579dc8c426..0fa85cfa70c21d85b0f06952b0e8b035c6cd4c92 100644 (file)
 #include "tda10071.h"
 #include <linux/firmware.h>
 
-#define LOG_PREFIX "tda10071"
-
-#undef dbg
-#define dbg(f, arg...) \
-       if (tda10071_debug) \
-               printk(KERN_INFO   LOG_PREFIX": " f "\n" , ## arg)
-#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)
-
 struct tda10071_priv {
        struct i2c_adapter *i2c;
        struct dvb_frontend fe;
@@ -112,7 +99,7 @@ struct tda10071_reg_val_mask {
 #define CMD_BER_UPDATE_COUNTERS 0x3f
 
 /* firmare command struct */
-#define TDA10071_ARGLEN      0x1e
+#define TDA10071_ARGLEN      30
 struct tda10071_cmd {
        u8 args[TDA10071_ARGLEN];
        u8 len;
index 7539a5d7102964aaa094be28cd0ae7b9de245687..72ee8de0226025618ecd733716fa30f494eb646b 100644 (file)
@@ -217,6 +217,7 @@ static int demod_attach_drxk(struct ngene_channel *chan,
 
        memset(&config, 0, sizeof(config));
        config.microcode_name = "drxk_a3.mc";
+       config.qam_demod_parameter_count = 4;
        config.adr = 0x29 + (chan->number ^ 2);
 
        chan->fe = dvb_attach(drxk_attach, &config, i2c);
index 7331e8450d1a7d96caa64b2ba88e3d13be622b6f..9cc55546cc30b138660ddc5cdcb4f857003caf5d 100644 (file)
@@ -276,16 +276,13 @@ static void smscore_notify_clients(struct smscore_device_t *coredev)
 static int smscore_notify_callbacks(struct smscore_device_t *coredev,
                                    struct device *device, int arrival)
 {
-       struct list_head *next, *first;
+       struct smscore_device_notifyee_t *elem;
        int rc = 0;
 
        /* note: must be called under g_deviceslock */
 
-       first = &g_smscore_notifyees;
-
-       for (next = first->next; next != first; next = next->next) {
-               rc = ((struct smscore_device_notifyee_t *) next)->
-                               hotplug(coredev, device, arrival);
+       list_for_each_entry(elem, &g_smscore_notifyees, entry) {
+               rc = elem->hotplug(coredev, device, arrival);
                if (rc < 0)
                        break;
        }
@@ -940,29 +937,25 @@ static struct
 smscore_client_t *smscore_find_client(struct smscore_device_t *coredev,
                                      int data_type, int id)
 {
-       struct smscore_client_t *client = NULL;
-       struct list_head *next, *first;
+       struct list_head *first;
+       struct smscore_client_t *client;
        unsigned long flags;
-       struct list_head *firstid, *nextid;
-
+       struct list_head *firstid;
+       struct smscore_idlist_t *client_id;
 
        spin_lock_irqsave(&coredev->clientslock, flags);
        first = &coredev->clients;
-       for (next = first->next;
-            (next != first) && !client;
-            next = next->next) {
-               firstid = &((struct smscore_client_t *)next)->idlist;
-               for (nextid = firstid->next;
-                    nextid != firstid;
-                    nextid = nextid->next) {
-                       if ((((struct smscore_idlist_t *)nextid)->id == id) &&
-                           (((struct smscore_idlist_t *)nextid)->data_type == data_type ||
-                           (((struct smscore_idlist_t *)nextid)->data_type == 0))) {
-                               client = (struct smscore_client_t *) next;
-                               break;
-                       }
+       list_for_each_entry(client, first, entry) {
+               firstid = &client->idlist;
+               list_for_each_entry(client_id, firstid, entry) {
+                       if ((client_id->id == id) &&
+                           (client_id->data_type == data_type ||
+                           (client_id->data_type == 0)))
+                               goto found;
                }
        }
+       client = NULL;
+found:
        spin_unlock_irqrestore(&coredev->clientslock, flags);
        return client;
 }
index c257da13d7663c57cc048d461590b3ce87014bc8..8090b87b306664b6401f05d6ba71cea0f0047b2c 100644 (file)
@@ -5,6 +5,7 @@
 menuconfig RADIO_ADAPTERS
        bool "Radio Adapters"
        depends on VIDEO_V4L2
+       depends on MEDIA_RADIO_SUPPORT
        default y
        ---help---
          Say Y here to enable selecting AM/FM radio adapters.
@@ -56,6 +57,39 @@ config RADIO_MAXIRADIO
          To compile this driver as a module, choose M here: the
          module will be called radio-maxiradio.
 
+config RADIO_SHARK
+       tristate "Griffin radioSHARK USB radio receiver"
+       depends on USB && SND
+       ---help---
+         Choose Y here if you have this radio receiver.
+
+         There are 2 versions of this device, this driver is for version 1,
+         which is white.
+
+         In order to control your radio card, you will need to use programs
+         that are compatible with the Video For Linux API.  Information on
+         this API and pointers to "v4l" programs may be found at
+         <file:Documentation/video4linux/API.html>.
+
+         To compile this driver as a module, choose M here: the
+         module will be called radio-shark.
+
+config RADIO_SHARK2
+       tristate "Griffin radioSHARK2 USB radio receiver"
+       depends on USB
+       ---help---
+         Choose Y here if you have this radio receiver.
+
+         There are 2 versions of this device, this driver is for version 2,
+         which is black.
+
+         In order to control your radio card, you will need to use programs
+         that are compatible with the Video For Linux API.  Information on
+         this API and pointers to "v4l" programs may be found at
+         <file:Documentation/video4linux/API.html>.
+
+         To compile this driver as a module, choose M here: the
+         module will be called radio-shark2.
 
 config I2C_SI4713
        tristate "I2C driver for Silicon Labs Si4713 device"
index ca8c7d134b95137f6bacf8e0870d95dff828aeaf..c03ce4fe74e97fb5e0bb92f8da5bbb8065599e82 100644 (file)
@@ -11,6 +11,8 @@ obj-$(CONFIG_RADIO_CADET) += radio-cadet.o
 obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o
 obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o
 obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o
+obj-$(CONFIG_RADIO_SHARK) += radio-shark.o
+obj-$(CONFIG_RADIO_SHARK2) += shark2.o
 obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o
 obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o
 obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o
@@ -29,4 +31,6 @@ obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
 obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o
 obj-$(CONFIG_RADIO_WL128X) += wl128x/
 
+shark2-objs := radio-shark2.o radio-tea5777.o
+
 ccflags-y += -Isound
diff --git a/drivers/media/radio/lm7000.h b/drivers/media/radio/lm7000.h
new file mode 100644 (file)
index 0000000..139cd6b
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef __LM7000_H
+#define __LM7000_H
+
+/* Sanyo LM7000 tuner chip control
+ *
+ * Copyright 2012 Ondrej Zary <linux@rainbow-software.org>
+ * based on radio-aimslab.c by M. Kirkwood
+ * and radio-sf16fmi.c by M. Kirkwood and Petr Vandrovec
+ */
+
+#define LM7000_DATA    (1 << 0)
+#define LM7000_CLK     (1 << 1)
+#define LM7000_CE      (1 << 2)
+
+#define LM7000_FM_100  (0 << 20)
+#define LM7000_FM_50   (1 << 20)
+#define LM7000_FM_25   (2 << 20)
+#define LM7000_BIT_FM  (1 << 23)
+
+static inline void lm7000_set_freq(u32 freq, void *handle,
+                               void (*set_pins)(void *handle, u8 pins))
+{
+       int i;
+       u8 data;
+       u32 val;
+
+       freq += 171200;         /* Add 10.7 MHz IF */
+       freq /= 400;            /* Convert to 25 kHz units */
+       val = freq | LM7000_FM_25 | LM7000_BIT_FM;
+       /* write the 24-bit register, starting with LSB */
+       for (i = 0; i < 24; i++) {
+               data = val & (1 << i) ? LM7000_DATA : 0;
+               set_pins(handle, data | LM7000_CE);
+               udelay(2);
+               set_pins(handle, data | LM7000_CE | LM7000_CLK);
+               udelay(2);
+               set_pins(handle, data | LM7000_CE);
+               udelay(2);
+       }
+       set_pins(handle, 0);
+}
+
+#endif /* __LM7000_H */
index 98e0c8c20312a5f547f8b175a30182b2e799590a..12c70e876f5863587359e5da742139e8a78a6ebd 100644 (file)
@@ -37,6 +37,7 @@
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-ctrls.h>
 #include "radio-isa.h"
+#include "lm7000.h"
 
 MODULE_AUTHOR("M. Kirkwood");
 MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
@@ -72,55 +73,38 @@ static struct radio_isa_card *rtrack_alloc(void)
        return rt ? &rt->isa : NULL;
 }
 
-/* The 128+64 on these outb's is to keep the volume stable while tuning.
- * Without them, the volume _will_ creep up with each frequency change
- * and bit 4 (+16) is to keep the signal strength meter enabled.
- */
+#define AIMS_BIT_TUN_CE                (1 << 0)
+#define AIMS_BIT_TUN_CLK       (1 << 1)
+#define AIMS_BIT_TUN_DATA      (1 << 2)
+#define AIMS_BIT_VOL_CE                (1 << 3)
+#define AIMS_BIT_TUN_STRQ      (1 << 4)
+/* bit 5 is not connected */
+#define AIMS_BIT_VOL_UP                (1 << 6)        /* active low */
+#define AIMS_BIT_VOL_DN                (1 << 7)        /* active low */
 
-static void send_0_byte(struct radio_isa_card *isa, int on)
+void rtrack_set_pins(void *handle, u8 pins)
 {
-       outb_p(128+64+16+on+1, isa->io);        /* wr-enable + data low */
-       outb_p(128+64+16+on+2+1, isa->io);      /* clock */
-       msleep(1);
-}
+       struct radio_isa_card *isa = handle;
+       struct rtrack *rt = container_of(isa, struct rtrack, isa);
+       u8 bits = AIMS_BIT_VOL_DN | AIMS_BIT_VOL_UP | AIMS_BIT_TUN_STRQ;
 
-static void send_1_byte(struct radio_isa_card *isa, int on)
-{
-       outb_p(128+64+16+on+4+1, isa->io);      /* wr-enable+data high */
-       outb_p(128+64+16+on+4+2+1, isa->io);    /* clock */
-       msleep(1);
+       if (!v4l2_ctrl_g_ctrl(rt->isa.mute))
+               bits |= AIMS_BIT_VOL_CE;
+
+       if (pins & LM7000_DATA)
+               bits |= AIMS_BIT_TUN_DATA;
+       if (pins & LM7000_CLK)
+               bits |= AIMS_BIT_TUN_CLK;
+       if (pins & LM7000_CE)
+               bits |= AIMS_BIT_TUN_CE;
+
+       outb_p(bits, rt->isa.io);
 }
 
 static int rtrack_s_frequency(struct radio_isa_card *isa, u32 freq)
 {
-       int on = v4l2_ctrl_g_ctrl(isa->mute) ? 0 : 8;
-       int i;
-
-       freq += 171200;                 /* Add 10.7 MHz IF              */
-       freq /= 800;                    /* Convert to 50 kHz units      */
-
-       send_0_byte(isa, on);           /*  0: LSB of frequency         */
-
-       for (i = 0; i < 13; i++)        /*   : frequency bits (1-13)    */
-               if (freq & (1 << i))
-                       send_1_byte(isa, on);
-               else
-                       send_0_byte(isa, on);
-
-       send_0_byte(isa, on);           /* 14: test bit - always 0    */
-       send_0_byte(isa, on);           /* 15: test bit - always 0    */
-
-       send_0_byte(isa, on);           /* 16: band data 0 - always 0 */
-       send_0_byte(isa, on);           /* 17: band data 1 - always 0 */
-       send_0_byte(isa, on);           /* 18: band data 2 - always 0 */
-       send_0_byte(isa, on);           /* 19: time base - always 0   */
-
-       send_0_byte(isa, on);           /* 20: spacing (0 = 25 kHz)   */
-       send_1_byte(isa, on);           /* 21: spacing (1 = 25 kHz)   */
-       send_0_byte(isa, on);           /* 22: spacing (0 = 25 kHz)   */
-       send_1_byte(isa, on);           /* 23: AM/FM (FM = 1, always) */
+       lm7000_set_freq(freq, isa, rtrack_set_pins);
 
-       outb(0xd0 + on, isa->io);       /* volume steady + sigstr */
        return 0;
 }
 
index 16a089fad909f241f76d6f5a8a1eb8b22f9358bb..697a421c99406faa84df2906d41e3d4492c8bd48 100644 (file)
@@ -41,6 +41,9 @@
 #include <linux/io.h>          /* outb, outb_p                 */
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
 
 MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
 MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
@@ -61,14 +64,15 @@ module_param(radio_nr, int, 0);
 struct cadet {
        struct v4l2_device v4l2_dev;
        struct video_device vdev;
+       struct v4l2_ctrl_handler ctrl_handler;
        int io;
-       int users;
-       int curtuner;
+       bool is_fm_band;
+       u32 curfreq;
        int tunestat;
        int sigstrength;
        wait_queue_head_t read_queue;
        struct timer_list readtimer;
-       __u8 rdsin, rdsout, rdsstat;
+       u8 rdsin, rdsout, rdsstat;
        unsigned char rdsbuf[RDS_BUFFER];
        struct mutex lock;
        int reading;
@@ -81,9 +85,9 @@ static struct cadet cadet_card;
  * The V4L API spec does not define any particular unit for the signal
  * strength value.  These values are in microvolts of RF at the tuner's input.
  */
-static __u16 sigtable[2][4] = {
-       {  5, 10, 30,  150 },
-       { 28, 40, 63, 1000 }
+static u16 sigtable[2][4] = {
+       { 1835, 2621,  4128, 65535 },
+       { 2185, 4369, 13107, 65535 },
 };
 
 
@@ -91,14 +95,12 @@ static int cadet_getstereo(struct cadet *dev)
 {
        int ret = V4L2_TUNER_SUB_MONO;
 
-       if (dev->curtuner != 0) /* Only FM has stereo capability! */
+       if (!dev->is_fm_band)   /* Only FM has stereo capability! */
                return V4L2_TUNER_SUB_MONO;
 
-       mutex_lock(&dev->lock);
        outb(7, dev->io);          /* Select tuner control */
        if ((inb(dev->io + 1) & 0x40) == 0)
                ret = V4L2_TUNER_SUB_STEREO;
-       mutex_unlock(&dev->lock);
        return ret;
 }
 
@@ -111,8 +113,6 @@ static unsigned cadet_gettune(struct cadet *dev)
         * Prepare for read
         */
 
-       mutex_lock(&dev->lock);
-
        outb(7, dev->io);       /* Select tuner control */
        curvol = inb(dev->io + 1); /* Save current volume/mute setting */
        outb(0x00, dev->io + 1);  /* Ensure WRITE-ENABLE is LOW */
@@ -134,8 +134,6 @@ static unsigned cadet_gettune(struct cadet *dev)
         * Restore volume/mute setting
         */
        outb(curvol, dev->io + 1);
-       mutex_unlock(&dev->lock);
-
        return fifo;
 }
 
@@ -152,20 +150,18 @@ static unsigned cadet_getfreq(struct cadet *dev)
        /*
         * Convert to actual frequency
         */
-       if (dev->curtuner == 0) {    /* FM */
-               test = 12500;
-               for (i = 0; i < 14; i++) {
-                       if ((fifo & 0x01) != 0)
-                               freq += test;
-                       test = test << 1;
-                       fifo = fifo >> 1;
-               }
-               freq -= 10700000;           /* IF frequency is 10.7 MHz */
-               freq = (freq * 16) / 1000000;   /* Make it 1/16 MHz */
+       if (!dev->is_fm_band)    /* AM */
+               return ((fifo & 0x7fff) - 450) * 16;
+
+       test = 12500;
+       for (i = 0; i < 14; i++) {
+               if ((fifo & 0x01) != 0)
+                       freq += test;
+               test = test << 1;
+               fifo = fifo >> 1;
        }
-       if (dev->curtuner == 1)    /* AM */
-               freq = ((fifo & 0x7fff) - 2010) * 16;
-
+       freq -= 10700000;           /* IF frequency is 10.7 MHz */
+       freq = (freq * 16) / 1000;   /* Make it 1/16 kHz */
        return freq;
 }
 
@@ -174,8 +170,6 @@ static void cadet_settune(struct cadet *dev, unsigned fifo)
        int i;
        unsigned test;
 
-       mutex_lock(&dev->lock);
-
        outb(7, dev->io);                /* Select tuner control */
        /*
         * Write the shift register
@@ -194,7 +188,6 @@ static void cadet_settune(struct cadet *dev, unsigned fifo)
                test = 0x1c | ((fifo >> 23) & 0x02);
                outb(test, dev->io + 1);
        }
-       mutex_unlock(&dev->lock);
 }
 
 static void cadet_setfreq(struct cadet *dev, unsigned freq)
@@ -203,13 +196,14 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
        int i, j, test;
        int curvol;
 
+       dev->curfreq = freq;
        /*
         * Formulate a fifo command
         */
        fifo = 0;
-       if (dev->curtuner == 0) {    /* FM */
+       if (dev->is_fm_band) {    /* FM */
                test = 102400;
-               freq = (freq * 1000) / 16;       /* Make it kHz */
+               freq = freq / 16;       /* Make it kHz */
                freq += 10700;               /* IF is 10700 kHz */
                for (i = 0; i < 14; i++) {
                        fifo = fifo << 1;
@@ -219,20 +213,17 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
                        }
                        test = test >> 1;
                }
-       }
-       if (dev->curtuner == 1) {    /* AM */
-               fifo = (freq / 16) + 2010;            /* Make it kHz */
-               fifo |= 0x100000;            /* Select AM Band */
+       } else {        /* AM */
+               fifo = (freq / 16) + 450;       /* Make it kHz */
+               fifo |= 0x100000;               /* Select AM Band */
        }
 
        /*
         * Save current volume/mute setting
         */
 
-       mutex_lock(&dev->lock);
        outb(7, dev->io);                /* Select tuner control */
        curvol = inb(dev->io + 1);
-       mutex_unlock(&dev->lock);
 
        /*
         * Tune the card
@@ -240,49 +231,24 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
        for (j = 3; j > -1; j--) {
                cadet_settune(dev, fifo | (j << 16));
 
-               mutex_lock(&dev->lock);
                outb(7, dev->io);         /* Select tuner control */
                outb(curvol, dev->io + 1);
-               mutex_unlock(&dev->lock);
 
                msleep(100);
 
                cadet_gettune(dev);
                if ((dev->tunestat & 0x40) == 0) {   /* Tuned */
-                       dev->sigstrength = sigtable[dev->curtuner][j];
-                       return;
+                       dev->sigstrength = sigtable[dev->is_fm_band][j];
+                       goto reset_rds;
                }
        }
        dev->sigstrength = 0;
+reset_rds:
+       outb(3, dev->io);
+       outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
 }
 
 
-static int cadet_getvol(struct cadet *dev)
-{
-       int ret = 0;
-
-       mutex_lock(&dev->lock);
-
-       outb(7, dev->io);                /* Select tuner control */
-       if ((inb(dev->io + 1) & 0x20) != 0)
-               ret = 0xffff;
-
-       mutex_unlock(&dev->lock);
-       return ret;
-}
-
-
-static void cadet_setvol(struct cadet *dev, int vol)
-{
-       mutex_lock(&dev->lock);
-       outb(7, dev->io);                /* Select tuner control */
-       if (vol > 0)
-               outb(0x20, dev->io + 1);
-       else
-               outb(0x00, dev->io + 1);
-       mutex_unlock(&dev->lock);
-}
-
 static void cadet_handler(unsigned long data)
 {
        struct cadet *dev = (void *)data;
@@ -295,7 +261,7 @@ static void cadet_handler(unsigned long data)
                outb(0x80, dev->io);      /* Select RDS fifo */
                while ((inb(dev->io) & 0x80) != 0) {
                        dev->rdsbuf[dev->rdsin] = inb(dev->io + 1);
-                       if (dev->rdsin == dev->rdsout)
+                       if (dev->rdsin + 1 == dev->rdsout)
                                printk(KERN_WARNING "cadet: RDS buffer overflow\n");
                        else
                                dev->rdsin++;
@@ -314,11 +280,21 @@ static void cadet_handler(unsigned long data)
         */
        init_timer(&dev->readtimer);
        dev->readtimer.function = cadet_handler;
-       dev->readtimer.data = (unsigned long)0;
+       dev->readtimer.data = data;
        dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
        add_timer(&dev->readtimer);
 }
 
+static void cadet_start_rds(struct cadet *dev)
+{
+       dev->rdsstat = 1;
+       outb(0x80, dev->io);        /* Select RDS fifo */
+       init_timer(&dev->readtimer);
+       dev->readtimer.function = cadet_handler;
+       dev->readtimer.data = (unsigned long)dev;
+       dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
+       add_timer(&dev->readtimer);
+}
 
 static ssize_t cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
 {
@@ -327,28 +303,24 @@ static ssize_t cadet_read(struct file *file, char __user *data, size_t count, lo
        int i = 0;
 
        mutex_lock(&dev->lock);
-       if (dev->rdsstat == 0) {
-               dev->rdsstat = 1;
-               outb(0x80, dev->io);        /* Select RDS fifo */
-               init_timer(&dev->readtimer);
-               dev->readtimer.function = cadet_handler;
-               dev->readtimer.data = (unsigned long)dev;
-               dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
-               add_timer(&dev->readtimer);
-       }
+       if (dev->rdsstat == 0)
+               cadet_start_rds(dev);
        if (dev->rdsin == dev->rdsout) {
+               if (file->f_flags & O_NONBLOCK) {
+                       i = -EWOULDBLOCK;
+                       goto unlock;
+               }
                mutex_unlock(&dev->lock);
-               if (file->f_flags & O_NONBLOCK)
-                       return -EWOULDBLOCK;
                interruptible_sleep_on(&dev->read_queue);
                mutex_lock(&dev->lock);
        }
        while (i < count && dev->rdsin != dev->rdsout)
                readbuf[i++] = dev->rdsbuf[dev->rdsout++];
-       mutex_unlock(&dev->lock);
 
-       if (copy_to_user(data, readbuf, i))
-               return -EFAULT;
+       if (i && copy_to_user(data, readbuf, i))
+               i = -EFAULT;
+unlock:
+       mutex_unlock(&dev->lock);
        return i;
 }
 
@@ -359,48 +331,58 @@ static int vidioc_querycap(struct file *file, void *priv,
        strlcpy(v->driver, "ADS Cadet", sizeof(v->driver));
        strlcpy(v->card, "ADS Cadet", sizeof(v->card));
        strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-       v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
+       v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
                          V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE;
+       v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
        return 0;
 }
 
+static const struct v4l2_frequency_band bands[] = {
+       {
+               .index = 0,
+               .type = V4L2_TUNER_RADIO,
+               .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
+               .rangelow = 8320,      /* 520 kHz */
+               .rangehigh = 26400,    /* 1650 kHz */
+               .modulation = V4L2_BAND_MODULATION_AM,
+       }, {
+               .index = 1,
+               .type = V4L2_TUNER_RADIO,
+               .capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
+                       V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_LOW |
+                       V4L2_TUNER_CAP_FREQ_BANDS,
+               .rangelow = 1400000,   /* 87.5 MHz */
+               .rangehigh = 1728000,  /* 108.0 MHz */
+               .modulation = V4L2_BAND_MODULATION_FM,
+       },
+};
+
 static int vidioc_g_tuner(struct file *file, void *priv,
                                struct v4l2_tuner *v)
 {
        struct cadet *dev = video_drvdata(file);
 
+       if (v->index)
+               return -EINVAL;
        v->type = V4L2_TUNER_RADIO;
-       switch (v->index) {
-       case 0:
-               strlcpy(v->name, "FM", sizeof(v->name));
-               v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
-                       V4L2_TUNER_CAP_RDS_BLOCK_IO;
-               v->rangelow = 1400;     /* 87.5 MHz */
-               v->rangehigh = 1728;    /* 108.0 MHz */
+       strlcpy(v->name, "Radio", sizeof(v->name));
+       v->capability = bands[0].capability | bands[1].capability;
+       v->rangelow = bands[0].rangelow;           /* 520 kHz (start of AM band) */
+       v->rangehigh = bands[1].rangehigh;    /* 108.0 MHz (end of FM band) */
+       if (dev->is_fm_band) {
                v->rxsubchans = cadet_getstereo(dev);
-               switch (v->rxsubchans) {
-               case V4L2_TUNER_SUB_MONO:
-                       v->audmode = V4L2_TUNER_MODE_MONO;
-                       break;
-               case V4L2_TUNER_SUB_STEREO:
-                       v->audmode = V4L2_TUNER_MODE_STEREO;
-                       break;
-               default:
-                       break;
-               }
-               v->rxsubchans |= V4L2_TUNER_SUB_RDS;
-               break;
-       case 1:
-               strlcpy(v->name, "AM", sizeof(v->name));
-               v->capability = V4L2_TUNER_CAP_LOW;
+               outb(3, dev->io);
+               outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
+               mdelay(100);
+               outb(3, dev->io);
+               if (inb(dev->io + 1) & 0x80)
+                       v->rxsubchans |= V4L2_TUNER_SUB_RDS;
+       } else {
                v->rangelow = 8320;      /* 520 kHz */
                v->rangehigh = 26400;    /* 1650 kHz */
                v->rxsubchans = V4L2_TUNER_SUB_MONO;
-               v->audmode = V4L2_TUNER_MODE_MONO;
-               break;
-       default:
-               return -EINVAL;
        }
+       v->audmode = V4L2_TUNER_MODE_STEREO;
        v->signal = dev->sigstrength; /* We might need to modify scaling of this */
        return 0;
 }
@@ -408,11 +390,17 @@ static int vidioc_g_tuner(struct file *file, void *priv,
 static int vidioc_s_tuner(struct file *file, void *priv,
                                struct v4l2_tuner *v)
 {
-       struct cadet *dev = video_drvdata(file);
+       return v->index ? -EINVAL : 0;
+}
 
-       if (v->index != 0 && v->index != 1)
+static int vidioc_enum_freq_bands(struct file *file, void *priv,
+                               struct v4l2_frequency_band *band)
+{
+       if (band->tuner)
+               return -EINVAL;
+       if (band->index >= ARRAY_SIZE(bands))
                return -EINVAL;
-       dev->curtuner = v->index;
+       *band = bands[band->index];
        return 0;
 }
 
@@ -421,9 +409,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
 {
        struct cadet *dev = video_drvdata(file);
 
-       f->tuner = dev->curtuner;
+       if (f->tuner)
+               return -EINVAL;
        f->type = V4L2_TUNER_RADIO;
-       f->frequency = cadet_getfreq(dev);
+       f->frequency = dev->curfreq;
        return 0;
 }
 
@@ -433,103 +422,46 @@ static int vidioc_s_frequency(struct file *file, void *priv,
 {
        struct cadet *dev = video_drvdata(file);
 
-       if (f->type != V4L2_TUNER_RADIO)
-               return -EINVAL;
-       if (dev->curtuner == 0 && (f->frequency < 1400 || f->frequency > 1728))
-               return -EINVAL;
-       if (dev->curtuner == 1 && (f->frequency < 8320 || f->frequency > 26400))
+       if (f->tuner)
                return -EINVAL;
+       dev->is_fm_band =
+               f->frequency >= (bands[0].rangehigh + bands[1].rangelow) / 2;
+       clamp(f->frequency, bands[dev->is_fm_band].rangelow,
+                           bands[dev->is_fm_band].rangehigh);
        cadet_setfreq(dev, f->frequency);
        return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-                               struct v4l2_queryctrl *qc)
+static int cadet_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       switch (qc->id) {
-       case V4L2_CID_AUDIO_MUTE:
-               return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
-       case V4L2_CID_AUDIO_VOLUME:
-               return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
-       }
-       return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-                               struct v4l2_control *ctrl)
-{
-       struct cadet *dev = video_drvdata(file);
+       struct cadet *dev = container_of(ctrl->handler, struct cadet, ctrl_handler);
 
        switch (ctrl->id) {
-       case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
-               ctrl->value = (cadet_getvol(dev) == 0);
-               break;
-       case V4L2_CID_AUDIO_VOLUME:
-               ctrl->value = cadet_getvol(dev);
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-                               struct v4l2_control *ctrl)
-{
-       struct cadet *dev = video_drvdata(file);
-
-       switch (ctrl->id){
-       case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
-               if (ctrl->value)
-                       cadet_setvol(dev, 0);
+       case V4L2_CID_AUDIO_MUTE:
+               outb(7, dev->io);                /* Select tuner control */
+               if (ctrl->val)
+                       outb(0x00, dev->io + 1);
                else
-                       cadet_setvol(dev, 0xffff);
-               break;
-       case V4L2_CID_AUDIO_VOLUME:
-               cadet_setvol(dev, ctrl->value);
-               break;
-       default:
-               return -EINVAL;
+                       outb(0x20, dev->io + 1);
+               return 0;
        }
-       return 0;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
-       *i = 0;
-       return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
-       return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
-                               struct v4l2_audio *a)
-{
-       a->index = 0;
-       strlcpy(a->name, "Radio", sizeof(a->name));
-       a->capability = V4L2_AUDCAP_STEREO;
-       return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
-                               struct v4l2_audio *a)
-{
-       return a->index ? -EINVAL : 0;
+       return -EINVAL;
 }
 
 static int cadet_open(struct file *file)
 {
        struct cadet *dev = video_drvdata(file);
+       int err;
 
        mutex_lock(&dev->lock);
-       dev->users++;
-       if (1 == dev->users)
+       err = v4l2_fh_open(file);
+       if (err)
+               goto fail;
+       if (v4l2_fh_is_singular_file(file))
                init_waitqueue_head(&dev->read_queue);
+fail:
        mutex_unlock(&dev->lock);
-       return 0;
+       return err;
 }
 
 static int cadet_release(struct file *file)
@@ -537,11 +469,11 @@ static int cadet_release(struct file *file)
        struct cadet *dev = video_drvdata(file);
 
        mutex_lock(&dev->lock);
-       dev->users--;
-       if (0 == dev->users) {
+       if (v4l2_fh_is_singular_file(file) && dev->rdsstat) {
                del_timer_sync(&dev->readtimer);
                dev->rdsstat = 0;
        }
+       v4l2_fh_release(file);
        mutex_unlock(&dev->lock);
        return 0;
 }
@@ -549,11 +481,19 @@ static int cadet_release(struct file *file)
 static unsigned int cadet_poll(struct file *file, struct poll_table_struct *wait)
 {
        struct cadet *dev = video_drvdata(file);
+       unsigned long req_events = poll_requested_events(wait);
+       unsigned int res = v4l2_ctrl_poll(file, wait);
 
        poll_wait(file, &dev->read_queue, wait);
+       if (dev->rdsstat == 0 && (req_events & (POLLIN | POLLRDNORM))) {
+               mutex_lock(&dev->lock);
+               if (dev->rdsstat == 0)
+                       cadet_start_rds(dev);
+               mutex_unlock(&dev->lock);
+       }
        if (dev->rdsin != dev->rdsout)
-               return POLLIN | POLLRDNORM;
-       return 0;
+               res |= POLLIN | POLLRDNORM;
+       return res;
 }
 
 
@@ -572,13 +512,14 @@ static const struct v4l2_ioctl_ops cadet_ioctl_ops = {
        .vidioc_s_tuner     = vidioc_s_tuner,
        .vidioc_g_frequency = vidioc_g_frequency,
        .vidioc_s_frequency = vidioc_s_frequency,
-       .vidioc_queryctrl   = vidioc_queryctrl,
-       .vidioc_g_ctrl      = vidioc_g_ctrl,
-       .vidioc_s_ctrl      = vidioc_s_ctrl,
-       .vidioc_g_audio     = vidioc_g_audio,
-       .vidioc_s_audio     = vidioc_s_audio,
-       .vidioc_g_input     = vidioc_g_input,
-       .vidioc_s_input     = vidioc_s_input,
+       .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
+       .vidioc_log_status  = v4l2_ctrl_log_status,
+       .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_ctrl_ops cadet_ctrl_ops = {
+       .s_ctrl = cadet_s_ctrl,
 };
 
 #ifdef CONFIG_PNP
@@ -628,8 +569,8 @@ static void cadet_probe(struct cadet *dev)
        for (i = 0; i < 8; i++) {
                dev->io = iovals[i];
                if (request_region(dev->io, 2, "cadet-probe")) {
-                       cadet_setfreq(dev, 1410);
-                       if (cadet_getfreq(dev) == 1410) {
+                       cadet_setfreq(dev, bands[1].rangelow);
+                       if (cadet_getfreq(dev) == bands[1].rangelow) {
                                release_region(dev->io, 2);
                                return;
                        }
@@ -648,7 +589,8 @@ static int __init cadet_init(void)
 {
        struct cadet *dev = &cadet_card;
        struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
-       int res;
+       struct v4l2_ctrl_handler *hdl;
+       int res = -ENODEV;
 
        strlcpy(v4l2_dev->name, "cadet", sizeof(v4l2_dev->name));
        mutex_init(&dev->lock);
@@ -680,23 +622,40 @@ static int __init cadet_init(void)
                goto fail;
        }
 
+       hdl = &dev->ctrl_handler;
+       v4l2_ctrl_handler_init(hdl, 2);
+       v4l2_ctrl_new_std(hdl, &cadet_ctrl_ops,
+                       V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+       v4l2_dev->ctrl_handler = hdl;
+       if (hdl->error) {
+               res = hdl->error;
+               v4l2_err(v4l2_dev, "Could not register controls\n");
+               goto err_hdl;
+       }
+
+       dev->is_fm_band = true;
+       dev->curfreq = bands[dev->is_fm_band].rangelow;
+       cadet_setfreq(dev, dev->curfreq);
        strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
        dev->vdev.v4l2_dev = v4l2_dev;
        dev->vdev.fops = &cadet_fops;
        dev->vdev.ioctl_ops = &cadet_ioctl_ops;
        dev->vdev.release = video_device_release_empty;
+       dev->vdev.lock = &dev->lock;
+       set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);
        video_set_drvdata(&dev->vdev, dev);
 
-       if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
-               v4l2_device_unregister(v4l2_dev);
-               release_region(dev->io, 2);
-               goto fail;
-       }
+       if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0)
+               goto err_hdl;
        v4l2_info(v4l2_dev, "ADS Cadet Radio Card at 0x%x\n", dev->io);
        return 0;
+err_hdl:
+       v4l2_ctrl_handler_free(hdl);
+       v4l2_device_unregister(v4l2_dev);
+       release_region(dev->io, 2);
 fail:
        pnp_unregister_driver(&cadet_pnp_driver);
-       return -ENODEV;
+       return res;
 }
 
 static void __exit cadet_exit(void)
@@ -704,7 +663,10 @@ static void __exit cadet_exit(void)
        struct cadet *dev = &cadet_card;
 
        video_unregister_device(&dev->vdev);
+       v4l2_ctrl_handler_free(&dev->ctrl_handler);
        v4l2_device_unregister(&dev->v4l2_dev);
+       outb(7, dev->io);       /* Mute */
+       outb(0x00, dev->io + 1);
        release_region(dev->io, 2);
        pnp_unregister_driver(&cadet_pnp_driver);
 }
index 94cb6bc690f5631e7cd6a6814cc7148d598c9ec7..3182b26d6efa8f99cb50fdc7d81d5b9681782ab9 100644 (file)
@@ -295,7 +295,8 @@ static int vidioc_g_tuner(struct file *file, void *priv,
        v->type = V4L2_TUNER_RADIO;
        v->rangelow = FREQ_MIN * FREQ_MUL;
        v->rangehigh = FREQ_MAX * FREQ_MUL;
-       v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+       v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+               V4L2_TUNER_CAP_HWSEEK_WRAP;
        v->rxsubchans = is_stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
        v->audmode = radio->stereo ?
                V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
@@ -372,7 +373,7 @@ static int vidioc_s_hw_freq_seek(struct file *file, void *priv,
        timeout = jiffies + msecs_to_jiffies(30000);
        for (;;) {
                if (time_after(jiffies, timeout)) {
-                       retval = -EAGAIN;
+                       retval = -ENODATA;
                        break;
                }
                if (schedule_timeout_interruptible(msecs_to_jiffies(10))) {
index a81d723b8c779a883381c2c00f188fb66380bdf4..8185d5fbfa89af74cf0153a822659a69e4846f8b 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/io.h>          /* outb, outb_p                 */
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
+#include "lm7000.h"
 
 MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
 MODULE_DESCRIPTION("A driver for the SF16-FMI, SF16-FMP and SF16-FMD radio.");
@@ -54,31 +55,33 @@ static struct fmi fmi_card;
 static struct pnp_dev *dev;
 bool pnp_attached;
 
-/* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
-/* It is only useful to give freq in interval of 800 (=0.05Mhz),
- * other bits will be truncated, e.g 92.7400016 -> 92.7, but
- * 92.7400017 -> 92.75
- */
-#define RSF16_ENCODE(x)        ((x) / 800 + 214)
 #define RSF16_MINFREQ (87 * 16000)
 #define RSF16_MAXFREQ (108 * 16000)
 
-static void outbits(int bits, unsigned int data, int io)
+#define FMI_BIT_TUN_CE         (1 << 0)
+#define FMI_BIT_TUN_CLK                (1 << 1)
+#define FMI_BIT_TUN_DATA       (1 << 2)
+#define FMI_BIT_VOL_SW         (1 << 3)
+#define FMI_BIT_TUN_STRQ       (1 << 4)
+
+void fmi_set_pins(void *handle, u8 pins)
 {
-       while (bits--) {
-               if (data & 1) {
-                       outb(5, io);
-                       udelay(6);
-                       outb(7, io);
-                       udelay(6);
-               } else {
-                       outb(1, io);
-                       udelay(6);
-                       outb(3, io);
-                       udelay(6);
-               }
-               data >>= 1;
-       }
+       struct fmi *fmi = handle;
+       u8 bits = FMI_BIT_TUN_STRQ;
+
+       if (!fmi->mute)
+               bits |= FMI_BIT_VOL_SW;
+
+       if (pins & LM7000_DATA)
+               bits |= FMI_BIT_TUN_DATA;
+       if (pins & LM7000_CLK)
+               bits |= FMI_BIT_TUN_CLK;
+       if (pins & LM7000_CE)
+               bits |= FMI_BIT_TUN_CE;
+
+       mutex_lock(&fmi->lock);
+       outb_p(bits, fmi->io);
+       mutex_unlock(&fmi->lock);
 }
 
 static inline void fmi_mute(struct fmi *fmi)
@@ -95,20 +98,6 @@ static inline void fmi_unmute(struct fmi *fmi)
        mutex_unlock(&fmi->lock);
 }
 
-static inline int fmi_setfreq(struct fmi *fmi, unsigned long freq)
-{
-       mutex_lock(&fmi->lock);
-       fmi->curfreq = freq;
-
-       outbits(16, RSF16_ENCODE(freq), fmi->io);
-       outbits(8, 0xC0, fmi->io);
-       msleep(143);            /* was schedule_timeout(HZ/7) */
-       mutex_unlock(&fmi->lock);
-       if (!fmi->mute)
-               fmi_unmute(fmi);
-       return 0;
-}
-
 static inline int fmi_getsigstr(struct fmi *fmi)
 {
        int val;
@@ -173,7 +162,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
                return -EINVAL;
        /* rounding in steps of 800 to match the freq
           that will be used */
-       fmi_setfreq(fmi, (f->frequency / 800) * 800);
+       lm7000_set_freq((f->frequency / 800) * 800, fmi, fmi_set_pins);
        return 0;
 }
 
diff --git a/drivers/media/radio/radio-shark.c b/drivers/media/radio/radio-shark.c
new file mode 100644 (file)
index 0000000..d0b6bb5
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * Linux V4L2 radio driver for the Griffin radioSHARK USB radio receiver
+ *
+ * Note the radioSHARK offers the audio through a regular USB audio device,
+ * this driver only handles the tuning.
+ *
+ * The info necessary to drive the shark was taken from the small userspace
+ * shark.c program by Michael Rolig, which he kindly placed in the Public
+ * Domain.
+ *
+ * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-device.h>
+#include <sound/tea575x-tuner.h>
+
+/*
+ * Version Information
+ */
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Griffin radioSHARK, USB radio receiver driver");
+MODULE_LICENSE("GPL");
+
+#define SHARK_IN_EP            0x83
+#define SHARK_OUT_EP           0x05
+
+#define TEA575X_BIT_MONO       (1<<22)         /* 0 = stereo, 1 = mono */
+#define TEA575X_BIT_BAND_MASK  (3<<20)
+#define TEA575X_BIT_BAND_FM    (0<<20)
+
+#define TB_LEN 6
+#define DRV_NAME "radioshark"
+
+#define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev)
+
+enum { BLUE_LED, BLUE_PULSE_LED, RED_LED, NO_LEDS };
+
+static void shark_led_set_blue(struct led_classdev *led_cdev,
+                              enum led_brightness value);
+static void shark_led_set_blue_pulse(struct led_classdev *led_cdev,
+                                    enum led_brightness value);
+static void shark_led_set_red(struct led_classdev *led_cdev,
+                             enum led_brightness value);
+
+static const struct led_classdev shark_led_templates[NO_LEDS] = {
+       [BLUE_LED] = {
+               .name           = "%s:blue:",
+               .brightness     = LED_OFF,
+               .max_brightness = 127,
+               .brightness_set = shark_led_set_blue,
+       },
+       [BLUE_PULSE_LED] = {
+               .name           = "%s:blue-pulse:",
+               .brightness     = LED_OFF,
+               .max_brightness = 255,
+               .brightness_set = shark_led_set_blue_pulse,
+       },
+       [RED_LED] = {
+               .name           = "%s:red:",
+               .brightness     = LED_OFF,
+               .max_brightness = 1,
+               .brightness_set = shark_led_set_red,
+       },
+};
+
+struct shark_device {
+       struct usb_device *usbdev;
+       struct v4l2_device v4l2_dev;
+       struct snd_tea575x tea;
+
+       struct work_struct led_work;
+       struct led_classdev leds[NO_LEDS];
+       char led_names[NO_LEDS][32];
+       atomic_t brightness[NO_LEDS];
+       unsigned long brightness_new;
+
+       u8 *transfer_buffer;
+       u32 last_val;
+};
+
+static atomic_t shark_instance = ATOMIC_INIT(0);
+
+static void shark_write_val(struct snd_tea575x *tea, u32 val)
+{
+       struct shark_device *shark = tea->private_data;
+       int i, res, actual_len;
+
+       /* Avoid unnecessary (slow) USB transfers */
+       if (shark->last_val == val)
+               return;
+
+       memset(shark->transfer_buffer, 0, TB_LEN);
+       shark->transfer_buffer[0] = 0xc0; /* Write shift register command */
+       for (i = 0; i < 4; i++)
+               shark->transfer_buffer[i] |= (val >> (24 - i * 8)) & 0xff;
+
+       res = usb_interrupt_msg(shark->usbdev,
+                               usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
+                               shark->transfer_buffer, TB_LEN,
+                               &actual_len, 1000);
+       if (res >= 0)
+               shark->last_val = val;
+       else
+               v4l2_err(&shark->v4l2_dev, "set-freq error: %d\n", res);
+}
+
+static u32 shark_read_val(struct snd_tea575x *tea)
+{
+       struct shark_device *shark = tea->private_data;
+       int i, res, actual_len;
+       u32 val = 0;
+
+       memset(shark->transfer_buffer, 0, TB_LEN);
+       shark->transfer_buffer[0] = 0x80;
+       res = usb_interrupt_msg(shark->usbdev,
+                               usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
+                               shark->transfer_buffer, TB_LEN,
+                               &actual_len, 1000);
+       if (res < 0) {
+               v4l2_err(&shark->v4l2_dev, "request-status error: %d\n", res);
+               return shark->last_val;
+       }
+
+       res = usb_interrupt_msg(shark->usbdev,
+                               usb_rcvintpipe(shark->usbdev, SHARK_IN_EP),
+                               shark->transfer_buffer, TB_LEN,
+                               &actual_len, 1000);
+       if (res < 0) {
+               v4l2_err(&shark->v4l2_dev, "get-status error: %d\n", res);
+               return shark->last_val;
+       }
+
+       for (i = 0; i < 4; i++)
+               val |= shark->transfer_buffer[i] << (24 - i * 8);
+
+       shark->last_val = val;
+
+       /*
+        * The shark does not allow actually reading the stereo / mono pin :(
+        * So assume that when we're tuned to an FM station and mono has not
+        * been requested, that we're receiving stereo.
+        */
+       if (((val & TEA575X_BIT_BAND_MASK) == TEA575X_BIT_BAND_FM) &&
+           !(val & TEA575X_BIT_MONO))
+               shark->tea.stereo = true;
+       else
+               shark->tea.stereo = false;
+
+       return val;
+}
+
+static struct snd_tea575x_ops shark_tea_ops = {
+       .write_val = shark_write_val,
+       .read_val  = shark_read_val,
+};
+
+static void shark_led_work(struct work_struct *work)
+{
+       struct shark_device *shark =
+               container_of(work, struct shark_device, led_work);
+       int i, res, brightness, actual_len;
+
+       /*
+        * We use the v4l2_dev lock and registered bit to ensure the device
+        * does not get unplugged and unreffed while we're running.
+        */
+       mutex_lock(&shark->tea.mutex);
+       if (!video_is_registered(&shark->tea.vd))
+               goto leave;
+
+       for (i = 0; i < 3; i++) {
+               if (!test_and_clear_bit(i, &shark->brightness_new))
+                       continue;
+
+               brightness = atomic_read(&shark->brightness[i]);
+               memset(shark->transfer_buffer, 0, TB_LEN);
+               if (i != RED_LED) {
+                       shark->transfer_buffer[0] = 0xA0 + i;
+                       shark->transfer_buffer[1] = brightness;
+               } else
+                       shark->transfer_buffer[0] = brightness ? 0xA9 : 0xA8;
+               res = usb_interrupt_msg(shark->usbdev,
+                                       usb_sndintpipe(shark->usbdev, 0x05),
+                                       shark->transfer_buffer, TB_LEN,
+                                       &actual_len, 1000);
+               if (res < 0)
+                       v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n",
+                                shark->led_names[i], res);
+       }
+leave:
+       mutex_unlock(&shark->tea.mutex);
+}
+
+static void shark_led_set_blue(struct led_classdev *led_cdev,
+                              enum led_brightness value)
+{
+       struct shark_device *shark =
+               container_of(led_cdev, struct shark_device, leds[BLUE_LED]);
+
+       atomic_set(&shark->brightness[BLUE_LED], value);
+       set_bit(BLUE_LED, &shark->brightness_new);
+       schedule_work(&shark->led_work);
+}
+
+static void shark_led_set_blue_pulse(struct led_classdev *led_cdev,
+                                    enum led_brightness value)
+{
+       struct shark_device *shark = container_of(led_cdev,
+                               struct shark_device, leds[BLUE_PULSE_LED]);
+
+       atomic_set(&shark->brightness[BLUE_PULSE_LED], 256 - value);
+       set_bit(BLUE_PULSE_LED, &shark->brightness_new);
+       schedule_work(&shark->led_work);
+}
+
+static void shark_led_set_red(struct led_classdev *led_cdev,
+                             enum led_brightness value)
+{
+       struct shark_device *shark =
+               container_of(led_cdev, struct shark_device, leds[RED_LED]);
+
+       atomic_set(&shark->brightness[RED_LED], value);
+       set_bit(RED_LED, &shark->brightness_new);
+       schedule_work(&shark->led_work);
+}
+
+static void usb_shark_disconnect(struct usb_interface *intf)
+{
+       struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
+       struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
+       int i;
+
+       mutex_lock(&shark->tea.mutex);
+       v4l2_device_disconnect(&shark->v4l2_dev);
+       snd_tea575x_exit(&shark->tea);
+       mutex_unlock(&shark->tea.mutex);
+
+       for (i = 0; i < NO_LEDS; i++)
+               led_classdev_unregister(&shark->leds[i]);
+
+       v4l2_device_put(&shark->v4l2_dev);
+}
+
+static void usb_shark_release(struct v4l2_device *v4l2_dev)
+{
+       struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
+
+       cancel_work_sync(&shark->led_work);
+       v4l2_device_unregister(&shark->v4l2_dev);
+       kfree(shark->transfer_buffer);
+       kfree(shark);
+}
+
+static int usb_shark_probe(struct usb_interface *intf,
+                          const struct usb_device_id *id)
+{
+       struct shark_device *shark;
+       int i, retval = -ENOMEM;
+
+       shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL);
+       if (!shark)
+               return retval;
+
+       shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);
+       if (!shark->transfer_buffer)
+               goto err_alloc_buffer;
+
+       /*
+        * Work around a bug in usbhid/hid-core.c, where it leaves a dangling
+        * pointer in intfdata causing v4l2-device.c to not set it. Which
+        * results in usb_shark_disconnect() referencing the dangling pointer
+        *
+        * REMOVE (as soon as the above bug is fixed, patch submitted)
+        */
+       usb_set_intfdata(intf, NULL);
+
+       shark->v4l2_dev.release = usb_shark_release;
+       v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
+       retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev);
+       if (retval) {
+               v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n");
+               goto err_reg_dev;
+       }
+
+       shark->usbdev = interface_to_usbdev(intf);
+       shark->tea.v4l2_dev = &shark->v4l2_dev;
+       shark->tea.private_data = shark;
+       shark->tea.radio_nr = -1;
+       shark->tea.ops = &shark_tea_ops;
+       shark->tea.cannot_mute = true;
+       strlcpy(shark->tea.card, "Griffin radioSHARK",
+               sizeof(shark->tea.card));
+       usb_make_path(shark->usbdev, shark->tea.bus_info,
+               sizeof(shark->tea.bus_info));
+
+       retval = snd_tea575x_init(&shark->tea, THIS_MODULE);
+       if (retval) {
+               v4l2_err(&shark->v4l2_dev, "couldn't init tea5757\n");
+               goto err_init_tea;
+       }
+
+       INIT_WORK(&shark->led_work, shark_led_work);
+       for (i = 0; i < NO_LEDS; i++) {
+               shark->leds[i] = shark_led_templates[i];
+               snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
+                        shark->leds[i].name, shark->v4l2_dev.name);
+               shark->leds[i].name = shark->led_names[i];
+               /*
+                * We don't fail the probe if we fail to register the leds,
+                * because once we've called snd_tea575x_init, the /dev/radio0
+                * node may be opened from userspace holding a reference to us!
+                *
+                * Note we cannot register the leds first instead as
+                * shark_led_work depends on the v4l2 mutex and registered bit.
+                */
+               retval = led_classdev_register(&intf->dev, &shark->leds[i]);
+               if (retval)
+                       v4l2_err(&shark->v4l2_dev,
+                                "couldn't register led: %s\n",
+                                shark->led_names[i]);
+       }
+
+       return 0;
+
+err_init_tea:
+       v4l2_device_unregister(&shark->v4l2_dev);
+err_reg_dev:
+       kfree(shark->transfer_buffer);
+err_alloc_buffer:
+       kfree(shark);
+
+       return retval;
+}
+
+/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */
+static struct usb_device_id usb_shark_device_table[] = {
+       { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
+                        USB_DEVICE_ID_MATCH_INT_CLASS,
+         .idVendor     = 0x077d,
+         .idProduct    = 0x627a,
+         .bcdDevice_lo = 0x0001,
+         .bcdDevice_hi = 0x0001,
+         .bInterfaceClass = 3,
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(usb, usb_shark_device_table);
+
+static struct usb_driver usb_shark_driver = {
+       .name                   = DRV_NAME,
+       .probe                  = usb_shark_probe,
+       .disconnect             = usb_shark_disconnect,
+       .id_table               = usb_shark_device_table,
+};
+module_usb_driver(usb_shark_driver);
diff --git a/drivers/media/radio/radio-shark2.c b/drivers/media/radio/radio-shark2.c
new file mode 100644 (file)
index 0000000..b9575de
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * Linux V4L2 radio driver for the Griffin radioSHARK2 USB radio receiver
+ *
+ * Note the radioSHARK2 offers the audio through a regular USB audio device,
+ * this driver only handles the tuning.
+ *
+ * The info necessary to drive the shark2 was taken from the small userspace
+ * shark2.c program by Hisaaki Shibata, which he kindly placed in the Public
+ * Domain.
+ *
+ * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-device.h>
+#include "radio-tea5777.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Griffin radioSHARK2, USB radio receiver driver");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+#define SHARK_IN_EP            0x83
+#define SHARK_OUT_EP           0x05
+
+#define TB_LEN 7
+#define DRV_NAME "radioshark2"
+
+#define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev)
+
+enum { BLUE_LED, RED_LED, NO_LEDS };
+
+static void shark_led_set_blue(struct led_classdev *led_cdev,
+                              enum led_brightness value);
+static void shark_led_set_red(struct led_classdev *led_cdev,
+                             enum led_brightness value);
+
+static const struct led_classdev shark_led_templates[NO_LEDS] = {
+       [BLUE_LED] = {
+               .name           = "%s:blue:",
+               .brightness     = LED_OFF,
+               .max_brightness = 127,
+               .brightness_set = shark_led_set_blue,
+       },
+       [RED_LED] = {
+               .name           = "%s:red:",
+               .brightness     = LED_OFF,
+               .max_brightness = 1,
+               .brightness_set = shark_led_set_red,
+       },
+};
+
+struct shark_device {
+       struct usb_device *usbdev;
+       struct v4l2_device v4l2_dev;
+       struct radio_tea5777 tea;
+
+       struct work_struct led_work;
+       struct led_classdev leds[NO_LEDS];
+       char led_names[NO_LEDS][32];
+       atomic_t brightness[NO_LEDS];
+       unsigned long brightness_new;
+
+       u8 *transfer_buffer;
+};
+
+static atomic_t shark_instance = ATOMIC_INIT(0);
+
+static int shark_write_reg(struct radio_tea5777 *tea, u64 reg)
+{
+       struct shark_device *shark = tea->private_data;
+       int i, res, actual_len;
+
+       memset(shark->transfer_buffer, 0, TB_LEN);
+       shark->transfer_buffer[0] = 0x81; /* Write register command */
+       for (i = 0; i < 6; i++)
+               shark->transfer_buffer[i + 1] = (reg >> (40 - i * 8)) & 0xff;
+
+       v4l2_dbg(1, debug, tea->v4l2_dev,
+                "shark2-write: %02x %02x %02x %02x %02x %02x %02x\n",
+                shark->transfer_buffer[0], shark->transfer_buffer[1],
+                shark->transfer_buffer[2], shark->transfer_buffer[3],
+                shark->transfer_buffer[4], shark->transfer_buffer[5],
+                shark->transfer_buffer[6]);
+
+       res = usb_interrupt_msg(shark->usbdev,
+                               usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
+                               shark->transfer_buffer, TB_LEN,
+                               &actual_len, 1000);
+       if (res < 0) {
+               v4l2_err(tea->v4l2_dev, "write error: %d\n", res);
+               return res;
+       }
+
+       return 0;
+}
+
+static int shark_read_reg(struct radio_tea5777 *tea, u32 *reg_ret)
+{
+       struct shark_device *shark = tea->private_data;
+       int i, res, actual_len;
+       u32 reg = 0;
+
+       memset(shark->transfer_buffer, 0, TB_LEN);
+       shark->transfer_buffer[0] = 0x82;
+       res = usb_interrupt_msg(shark->usbdev,
+                               usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
+                               shark->transfer_buffer, TB_LEN,
+                               &actual_len, 1000);
+       if (res < 0) {
+               v4l2_err(tea->v4l2_dev, "request-read error: %d\n", res);
+               return res;
+       }
+
+       res = usb_interrupt_msg(shark->usbdev,
+                               usb_rcvintpipe(shark->usbdev, SHARK_IN_EP),
+                               shark->transfer_buffer, TB_LEN,
+                               &actual_len, 1000);
+       if (res < 0) {
+               v4l2_err(tea->v4l2_dev, "read error: %d\n", res);
+               return res;
+       }
+
+       for (i = 0; i < 3; i++)
+               reg |= shark->transfer_buffer[i] << (16 - i * 8);
+
+       v4l2_dbg(1, debug, tea->v4l2_dev, "shark2-read: %02x %02x %02x\n",
+                shark->transfer_buffer[0], shark->transfer_buffer[1],
+                shark->transfer_buffer[2]);
+
+       *reg_ret = reg;
+       return 0;
+}
+
+static struct radio_tea5777_ops shark_tea_ops = {
+       .write_reg = shark_write_reg,
+       .read_reg  = shark_read_reg,
+};
+
+static void shark_led_work(struct work_struct *work)
+{
+       struct shark_device *shark =
+               container_of(work, struct shark_device, led_work);
+       int i, res, brightness, actual_len;
+       /*
+        * We use the v4l2_dev lock and registered bit to ensure the device
+        * does not get unplugged and unreffed while we're running.
+        */
+       mutex_lock(&shark->tea.mutex);
+       if (!video_is_registered(&shark->tea.vd))
+               goto leave;
+
+       for (i = 0; i < 2; i++) {
+               if (!test_and_clear_bit(i, &shark->brightness_new))
+                       continue;
+
+               brightness = atomic_read(&shark->brightness[i]);
+               memset(shark->transfer_buffer, 0, TB_LEN);
+               shark->transfer_buffer[0] = 0x83 + i;
+               shark->transfer_buffer[1] = brightness;
+               res = usb_interrupt_msg(shark->usbdev,
+                                       usb_sndintpipe(shark->usbdev,
+                                                      SHARK_OUT_EP),
+                                       shark->transfer_buffer, TB_LEN,
+                                       &actual_len, 1000);
+               if (res < 0)
+                       v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n",
+                                shark->led_names[i], res);
+       }
+leave:
+       mutex_unlock(&shark->tea.mutex);
+}
+
+static void shark_led_set_blue(struct led_classdev *led_cdev,
+                              enum led_brightness value)
+{
+       struct shark_device *shark =
+               container_of(led_cdev, struct shark_device, leds[BLUE_LED]);
+
+       atomic_set(&shark->brightness[BLUE_LED], value);
+       set_bit(BLUE_LED, &shark->brightness_new);
+       schedule_work(&shark->led_work);
+}
+
+static void shark_led_set_red(struct led_classdev *led_cdev,
+                             enum led_brightness value)
+{
+       struct shark_device *shark =
+               container_of(led_cdev, struct shark_device, leds[RED_LED]);
+
+       atomic_set(&shark->brightness[RED_LED], value);
+       set_bit(RED_LED, &shark->brightness_new);
+       schedule_work(&shark->led_work);
+}
+
+static void usb_shark_disconnect(struct usb_interface *intf)
+{
+       struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
+       struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
+       int i;
+
+       mutex_lock(&shark->tea.mutex);
+       v4l2_device_disconnect(&shark->v4l2_dev);
+       radio_tea5777_exit(&shark->tea);
+       mutex_unlock(&shark->tea.mutex);
+
+       for (i = 0; i < NO_LEDS; i++)
+               led_classdev_unregister(&shark->leds[i]);
+
+       v4l2_device_put(&shark->v4l2_dev);
+}
+
+static void usb_shark_release(struct v4l2_device *v4l2_dev)
+{
+       struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
+
+       cancel_work_sync(&shark->led_work);
+       v4l2_device_unregister(&shark->v4l2_dev);
+       kfree(shark->transfer_buffer);
+       kfree(shark);
+}
+
+static int usb_shark_probe(struct usb_interface *intf,
+                          const struct usb_device_id *id)
+{
+       struct shark_device *shark;
+       int i, retval = -ENOMEM;
+
+       shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL);
+       if (!shark)
+               return retval;
+
+       shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);
+       if (!shark->transfer_buffer)
+               goto err_alloc_buffer;
+
+       /*
+        * Work around a bug in usbhid/hid-core.c, where it leaves a dangling
+        * pointer in intfdata causing v4l2-device.c to not set it. Which
+        * results in usb_shark_disconnect() referencing the dangling pointer
+        *
+        * REMOVE (as soon as the above bug is fixed, patch submitted)
+        */
+       usb_set_intfdata(intf, NULL);
+
+       shark->v4l2_dev.release = usb_shark_release;
+       v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
+       retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev);
+       if (retval) {
+               v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n");
+               goto err_reg_dev;
+       }
+
+       shark->usbdev = interface_to_usbdev(intf);
+       shark->tea.v4l2_dev = &shark->v4l2_dev;
+       shark->tea.private_data = shark;
+       shark->tea.ops = &shark_tea_ops;
+       shark->tea.has_am = true;
+       shark->tea.write_before_read = true;
+       strlcpy(shark->tea.card, "Griffin radioSHARK2",
+               sizeof(shark->tea.card));
+       usb_make_path(shark->usbdev, shark->tea.bus_info,
+               sizeof(shark->tea.bus_info));
+
+       retval = radio_tea5777_init(&shark->tea, THIS_MODULE);
+       if (retval) {
+               v4l2_err(&shark->v4l2_dev, "couldn't init tea5777\n");
+               goto err_init_tea;
+       }
+
+       INIT_WORK(&shark->led_work, shark_led_work);
+       for (i = 0; i < NO_LEDS; i++) {
+               shark->leds[i] = shark_led_templates[i];
+               snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
+                        shark->leds[i].name, shark->v4l2_dev.name);
+               shark->leds[i].name = shark->led_names[i];
+               /*
+                * We don't fail the probe if we fail to register the leds,
+                * because once we've called radio_tea5777_init, the /dev/radio0
+                * node may be opened from userspace holding a reference to us!
+                *
+                * Note we cannot register the leds first instead as
+                * shark_led_work depends on the v4l2 mutex and registered bit.
+                */
+               retval = led_classdev_register(&intf->dev, &shark->leds[i]);
+               if (retval)
+                       v4l2_err(&shark->v4l2_dev,
+                                "couldn't register led: %s\n",
+                                shark->led_names[i]);
+       }
+
+       return 0;
+
+err_init_tea:
+       v4l2_device_unregister(&shark->v4l2_dev);
+err_reg_dev:
+       kfree(shark->transfer_buffer);
+err_alloc_buffer:
+       kfree(shark);
+
+       return retval;
+}
+
+/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */
+static struct usb_device_id usb_shark_device_table[] = {
+       { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
+                        USB_DEVICE_ID_MATCH_INT_CLASS,
+         .idVendor     = 0x077d,
+         .idProduct    = 0x627a,
+         .bcdDevice_lo = 0x0010,
+         .bcdDevice_hi = 0x0010,
+         .bInterfaceClass = 3,
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(usb, usb_shark_device_table);
+
+static struct usb_driver usb_shark_driver = {
+       .name                   = DRV_NAME,
+       .probe                  = usb_shark_probe,
+       .disconnect             = usb_shark_disconnect,
+       .id_table               = usb_shark_device_table,
+};
+module_usb_driver(usb_shark_driver);
diff --git a/drivers/media/radio/radio-tea5777.c b/drivers/media/radio/radio-tea5777.c
new file mode 100644 (file)
index 0000000..5bc9fa6
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ *   v4l2 driver for TEA5777 Philips AM/FM radio tuner chips
+ *
+ *     Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
+ *
+ *   Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips:
+ *
+ *     Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <asm/div64.h>
+#include "radio-tea5777.h"
+
+MODULE_AUTHOR("Hans de Goede <perex@perex.cz>");
+MODULE_DESCRIPTION("Routines for control of TEA5777 Philips AM/FM radio tuner chips");
+MODULE_LICENSE("GPL");
+
+/* Fixed FM only band for now, will implement multi-band support when the
+   VIDIOC_ENUM_FREQ_BANDS API is upstream */
+#define TEA5777_FM_RANGELOW            (76000 * 16)
+#define TEA5777_FM_RANGEHIGH           (108000 * 16)
+
+#define TEA5777_FM_IF                  150 /* kHz */
+#define TEA5777_FM_FREQ_STEP           50 /* kHz */
+
+/* Write reg, common bits */
+#define TEA5777_W_MUTE_MASK            (1LL << 47)
+#define TEA5777_W_MUTE_SHIFT           47
+#define TEA5777_W_AM_FM_MASK           (1LL << 46)
+#define TEA5777_W_AM_FM_SHIFT          46
+#define TEA5777_W_STB_MASK             (1LL << 45)
+#define TEA5777_W_STB_SHIFT            45
+
+#define TEA5777_W_IFCE_MASK            (1LL << 29)
+#define TEA5777_W_IFCE_SHIFT           29
+#define TEA5777_W_IFW_MASK             (1LL << 28)
+#define TEA5777_W_IFW_SHIFT            28
+#define TEA5777_W_HILO_MASK            (1LL << 27)
+#define TEA5777_W_HILO_SHIFT           27
+#define TEA5777_W_DBUS_MASK            (1LL << 26)
+#define TEA5777_W_DBUS_SHIFT           26
+
+#define TEA5777_W_INTEXT_MASK          (1LL << 24)
+#define TEA5777_W_INTEXT_SHIFT         24
+#define TEA5777_W_P1_MASK              (1LL << 23)
+#define TEA5777_W_P1_SHIFT             23
+#define TEA5777_W_P0_MASK              (1LL << 22)
+#define TEA5777_W_P0_SHIFT             22
+#define TEA5777_W_PEN1_MASK            (1LL << 21)
+#define TEA5777_W_PEN1_SHIFT           21
+#define TEA5777_W_PEN0_MASK            (1LL << 20)
+#define TEA5777_W_PEN0_SHIFT           20
+
+#define TEA5777_W_CHP0_MASK            (1LL << 18)
+#define TEA5777_W_CHP0_SHIFT           18
+#define TEA5777_W_DEEM_MASK            (1LL << 17)
+#define TEA5777_W_DEEM_SHIFT           17
+
+#define TEA5777_W_SEARCH_MASK          (1LL << 7)
+#define TEA5777_W_SEARCH_SHIFT         7
+#define TEA5777_W_PROGBLIM_MASK                (1LL << 6)
+#define TEA5777_W_PROGBLIM_SHIFT       6
+#define TEA5777_W_UPDWN_MASK           (1LL << 5)
+#define TEA5777_W_UPDWN_SHIFT          5
+#define TEA5777_W_SLEV_MASK            (3LL << 3)
+#define TEA5777_W_SLEV_SHIFT           3
+
+/* Write reg, FM specific bits */
+#define TEA5777_W_FM_PLL_MASK          (0x1fffLL << 32)
+#define TEA5777_W_FM_PLL_SHIFT         32
+#define TEA5777_W_FM_FREF_MASK         (0x03LL << 30)
+#define TEA5777_W_FM_FREF_SHIFT                30
+#define TEA5777_W_FM_FREF_VALUE                0 /* 50 kHz tune steps, 150 kHz IF */
+
+#define TEA5777_W_FM_FORCEMONO_MASK    (1LL << 15)
+#define TEA5777_W_FM_FORCEMONO_SHIFT   15
+#define TEA5777_W_FM_SDSOFF_MASK       (1LL << 14)
+#define TEA5777_W_FM_SDSOFF_SHIFT      14
+#define TEA5777_W_FM_DOFF_MASK         (1LL << 13)
+#define TEA5777_W_FM_DOFF_SHIFT                13
+
+#define TEA5777_W_FM_STEP_MASK         (3LL << 1)
+#define TEA5777_W_FM_STEP_SHIFT                1
+
+/* Write reg, AM specific bits */
+#define TEA5777_W_AM_PLL_MASK          (0x7ffLL << 34)
+#define TEA5777_W_AM_PLL_SHIFT         34
+#define TEA5777_W_AM_AGCRF_MASK                (1LL << 33)
+#define TEA5777_W_AM_AGCRF_SHIFT       33
+#define TEA5777_W_AM_AGCIF_MASK                (1LL << 32)
+#define TEA5777_W_AM_AGCIF_SHIFT       32
+#define TEA5777_W_AM_MWLW_MASK         (1LL << 31)
+#define TEA5777_W_AM_MWLW_SHIFT                31
+#define TEA5777_W_AM_LNA_MASK          (1LL << 30)
+#define TEA5777_W_AM_LNA_SHIFT         30
+
+#define TEA5777_W_AM_PEAK_MASK         (1LL << 25)
+#define TEA5777_W_AM_PEAK_SHIFT                25
+
+#define TEA5777_W_AM_RFB_MASK          (1LL << 16)
+#define TEA5777_W_AM_RFB_SHIFT         16
+#define TEA5777_W_AM_CALLIGN_MASK      (1LL << 15)
+#define TEA5777_W_AM_CALLIGN_SHIFT     15
+#define TEA5777_W_AM_CBANK_MASK                (0x7fLL << 8)
+#define TEA5777_W_AM_CBANK_SHIFT       8
+
+#define TEA5777_W_AM_DELAY_MASK                (1LL << 2)
+#define TEA5777_W_AM_DELAY_SHIFT       2
+#define TEA5777_W_AM_STEP_MASK         (1LL << 1)
+#define TEA5777_W_AM_STEP_SHIFT                1
+
+/* Read reg, common bits */
+#define TEA5777_R_LEVEL_MASK           (0x0f << 17)
+#define TEA5777_R_LEVEL_SHIFT          17
+#define TEA5777_R_SFOUND_MASK          (0x01 << 16)
+#define TEA5777_R_SFOUND_SHIFT         16
+#define TEA5777_R_BLIM_MASK            (0x01 << 15)
+#define TEA5777_R_BLIM_SHIFT           15
+
+/* Read reg, FM specific bits */
+#define TEA5777_R_FM_STEREO_MASK       (0x01 << 21)
+#define TEA5777_R_FM_STEREO_SHIFT      21
+#define TEA5777_R_FM_PLL_MASK          0x1fff
+#define TEA5777_R_FM_PLL_SHIFT         0
+
+static u32 tea5777_freq_to_v4l2_freq(struct radio_tea5777 *tea, u32 freq)
+{
+       return (freq * TEA5777_FM_FREQ_STEP + TEA5777_FM_IF) * 16;
+}
+
+static int radio_tea5777_set_freq(struct radio_tea5777 *tea)
+{
+       u64 freq;
+       int res;
+
+       freq = clamp_t(u32, tea->freq,
+                      TEA5777_FM_RANGELOW, TEA5777_FM_RANGEHIGH) + 8;
+       do_div(freq, 16); /* to kHz */
+
+       freq -= TEA5777_FM_IF;
+       do_div(freq, TEA5777_FM_FREQ_STEP);
+
+       tea->write_reg &= ~(TEA5777_W_FM_PLL_MASK | TEA5777_W_FM_FREF_MASK);
+       tea->write_reg |= freq << TEA5777_W_FM_PLL_SHIFT;
+       tea->write_reg |= TEA5777_W_FM_FREF_VALUE << TEA5777_W_FM_FREF_SHIFT;
+
+       res = tea->ops->write_reg(tea, tea->write_reg);
+       if (res)
+               return res;
+
+       tea->needs_write = false;
+       tea->read_reg = -1;
+       tea->freq = tea5777_freq_to_v4l2_freq(tea, freq);
+
+       return 0;
+}
+
+static int radio_tea5777_update_read_reg(struct radio_tea5777 *tea, int wait)
+{
+       int res;
+
+       if (tea->read_reg != -1)
+               return 0;
+
+       if (tea->write_before_read && tea->needs_write) {
+               res = radio_tea5777_set_freq(tea);
+               if (res)
+                       return res;
+       }
+
+       if (wait) {
+               if (schedule_timeout_interruptible(msecs_to_jiffies(wait)))
+                       return -ERESTARTSYS;
+       }
+
+       res = tea->ops->read_reg(tea, &tea->read_reg);
+       if (res)
+               return res;
+
+       tea->needs_write = true;
+       return 0;
+}
+
+/*
+ * Linux Video interface
+ */
+
+static int vidioc_querycap(struct file *file, void  *priv,
+                                       struct v4l2_capability *v)
+{
+       struct radio_tea5777 *tea = video_drvdata(file);
+
+       strlcpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
+       strlcpy(v->card, tea->card, sizeof(v->card));
+       strlcat(v->card, " TEA5777", sizeof(v->card));
+       strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
+       v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+       v->device_caps |= V4L2_CAP_HW_FREQ_SEEK;
+       v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
+       return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+                                       struct v4l2_tuner *v)
+{
+       struct radio_tea5777 *tea = video_drvdata(file);
+       int res;
+
+       if (v->index > 0)
+               return -EINVAL;
+
+       res = radio_tea5777_update_read_reg(tea, 0);
+       if (res)
+               return res;
+
+       memset(v, 0, sizeof(*v));
+       if (tea->has_am)
+               strlcpy(v->name, "AM/FM", sizeof(v->name));
+       else
+               strlcpy(v->name, "FM", sizeof(v->name));
+       v->type = V4L2_TUNER_RADIO;
+       v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+                       V4L2_TUNER_CAP_HWSEEK_BOUNDED;
+       v->rangelow   = TEA5777_FM_RANGELOW;
+       v->rangehigh  = TEA5777_FM_RANGEHIGH;
+       v->rxsubchans = (tea->read_reg & TEA5777_R_FM_STEREO_MASK) ?
+                       V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
+       v->audmode = (tea->write_reg & TEA5777_W_FM_FORCEMONO_MASK) ?
+               V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
+       /* shift - 12 to convert 4-bits (0-15) scale to 16-bits (0-65535) */
+       v->signal = (tea->read_reg & TEA5777_R_LEVEL_MASK) >>
+                   (TEA5777_R_LEVEL_SHIFT - 12);
+
+       /* Invalidate read_reg, so that next call we return up2date signal */
+       tea->read_reg = -1;
+
+       return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+                                       struct v4l2_tuner *v)
+{
+       struct radio_tea5777 *tea = video_drvdata(file);
+
+       if (v->index)
+               return -EINVAL;
+
+       if (v->audmode == V4L2_TUNER_MODE_MONO)
+               tea->write_reg |= TEA5777_W_FM_FORCEMONO_MASK;
+       else
+               tea->write_reg &= ~TEA5777_W_FM_FORCEMONO_MASK;
+
+       return radio_tea5777_set_freq(tea);
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+                                       struct v4l2_frequency *f)
+{
+       struct radio_tea5777 *tea = video_drvdata(file);
+
+       if (f->tuner != 0)
+               return -EINVAL;
+       f->type = V4L2_TUNER_RADIO;
+       f->frequency = tea->freq;
+       return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+                                       struct v4l2_frequency *f)
+{
+       struct radio_tea5777 *tea = video_drvdata(file);
+
+       if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+               return -EINVAL;
+
+       tea->freq = f->frequency;
+       return radio_tea5777_set_freq(tea);
+}
+
+static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
+                                       struct v4l2_hw_freq_seek *a)
+{
+       struct radio_tea5777 *tea = video_drvdata(file);
+       u32 orig_freq = tea->freq;
+       unsigned long timeout;
+       int res, spacing = 200 * 16; /* 200 kHz */
+       /* These are fixed *for now* */
+       const u32 seek_rangelow  = TEA5777_FM_RANGELOW;
+       const u32 seek_rangehigh = TEA5777_FM_RANGEHIGH;
+
+       if (a->tuner || a->wrap_around)
+               return -EINVAL;
+
+       tea->write_reg |= TEA5777_W_PROGBLIM_MASK;
+       if (seek_rangelow != tea->seek_rangelow) {
+               tea->write_reg &= ~TEA5777_W_UPDWN_MASK;
+               tea->freq = seek_rangelow;
+               res = radio_tea5777_set_freq(tea);
+               if (res)
+                       goto leave;
+               tea->seek_rangelow = tea->freq;
+       }
+       if (seek_rangehigh != tea->seek_rangehigh) {
+               tea->write_reg |= TEA5777_W_UPDWN_MASK;
+               tea->freq = seek_rangehigh;
+               res = radio_tea5777_set_freq(tea);
+               if (res)
+                       goto leave;
+               tea->seek_rangehigh = tea->freq;
+       }
+       tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK;
+
+       tea->write_reg |= TEA5777_W_SEARCH_MASK;
+       if (a->seek_upward) {
+               tea->write_reg |= TEA5777_W_UPDWN_MASK;
+               tea->freq = orig_freq + spacing;
+       } else {
+               tea->write_reg &= ~TEA5777_W_UPDWN_MASK;
+               tea->freq = orig_freq - spacing;
+       }
+       res = radio_tea5777_set_freq(tea);
+       if (res)
+               goto leave;
+
+       timeout = jiffies + msecs_to_jiffies(5000);
+       for (;;) {
+               if (time_after(jiffies, timeout)) {
+                       res = -ENODATA;
+                       break;
+               }
+
+               res = radio_tea5777_update_read_reg(tea, 100);
+               if (res)
+                       break;
+
+               /*
+                * Note we use tea->freq to track how far we've searched sofar
+                * this is necessary to ensure we continue seeking at the right
+                * point, in the write_before_read case.
+                */
+               tea->freq = (tea->read_reg & TEA5777_R_FM_PLL_MASK);
+               tea->freq = tea5777_freq_to_v4l2_freq(tea, tea->freq);
+
+               if ((tea->read_reg & TEA5777_R_SFOUND_MASK)) {
+                       tea->write_reg &= ~TEA5777_W_SEARCH_MASK;
+                       return 0;
+               }
+
+               if (tea->read_reg & TEA5777_R_BLIM_MASK) {
+                       res = -ENODATA;
+                       break;
+               }
+
+               /* Force read_reg update */
+               tea->read_reg = -1;
+       }
+leave:
+       tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK;
+       tea->write_reg &= ~TEA5777_W_SEARCH_MASK;
+       tea->freq = orig_freq;
+       radio_tea5777_set_freq(tea);
+       return res;
+}
+
+static int tea575x_s_ctrl(struct v4l2_ctrl *c)
+{
+       struct radio_tea5777 *tea =
+               container_of(c->handler, struct radio_tea5777, ctrl_handler);
+
+       switch (c->id) {
+       case V4L2_CID_AUDIO_MUTE:
+               if (c->val)
+                       tea->write_reg |= TEA5777_W_MUTE_MASK;
+               else
+                       tea->write_reg &= ~TEA5777_W_MUTE_MASK;
+
+               return radio_tea5777_set_freq(tea);
+       }
+
+       return -EINVAL;
+}
+
+static const struct v4l2_file_operations tea575x_fops = {
+       .unlocked_ioctl = video_ioctl2,
+       .open           = v4l2_fh_open,
+       .release        = v4l2_fh_release,
+       .poll           = v4l2_ctrl_poll,
+};
+
+static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
+       .vidioc_querycap    = vidioc_querycap,
+       .vidioc_g_tuner     = vidioc_g_tuner,
+       .vidioc_s_tuner     = vidioc_s_tuner,
+       .vidioc_g_frequency = vidioc_g_frequency,
+       .vidioc_s_frequency = vidioc_s_frequency,
+       .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
+       .vidioc_log_status  = v4l2_ctrl_log_status,
+       .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct video_device tea575x_radio = {
+       .ioctl_ops      = &tea575x_ioctl_ops,
+       .release        = video_device_release_empty,
+};
+
+static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
+       .s_ctrl = tea575x_s_ctrl,
+};
+
+int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner)
+{
+       int res;
+
+       tea->write_reg = (1LL << TEA5777_W_IFCE_SHIFT) |
+                        (1LL << TEA5777_W_IFW_SHIFT) |
+                        (1LL << TEA5777_W_INTEXT_SHIFT) |
+                        (1LL << TEA5777_W_CHP0_SHIFT) |
+                        (2LL << TEA5777_W_SLEV_SHIFT);
+       tea->freq = 90500 * 16; /* 90.5Mhz default */
+       res = radio_tea5777_set_freq(tea);
+       if (res) {
+               v4l2_err(tea->v4l2_dev, "can't set initial freq (%d)\n", res);
+               return res;
+       }
+
+       tea->vd = tea575x_radio;
+       video_set_drvdata(&tea->vd, tea);
+       mutex_init(&tea->mutex);
+       strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name));
+       tea->vd.lock = &tea->mutex;
+       tea->vd.v4l2_dev = tea->v4l2_dev;
+       tea->fops = tea575x_fops;
+       tea->fops.owner = owner;
+       tea->vd.fops = &tea->fops;
+       set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags);
+
+       tea->vd.ctrl_handler = &tea->ctrl_handler;
+       v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+       v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops,
+                         V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+       res = tea->ctrl_handler.error;
+       if (res) {
+               v4l2_err(tea->v4l2_dev, "can't initialize controls\n");
+               v4l2_ctrl_handler_free(&tea->ctrl_handler);
+               return res;
+       }
+       v4l2_ctrl_handler_setup(&tea->ctrl_handler);
+
+       res = video_register_device(&tea->vd, VFL_TYPE_RADIO, -1);
+       if (res) {
+               v4l2_err(tea->v4l2_dev, "can't register video device!\n");
+               v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
+               return res;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(radio_tea5777_init);
+
+void radio_tea5777_exit(struct radio_tea5777 *tea)
+{
+       video_unregister_device(&tea->vd);
+       v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
+}
+EXPORT_SYMBOL_GPL(radio_tea5777_exit);
diff --git a/drivers/media/radio/radio-tea5777.h b/drivers/media/radio/radio-tea5777.h
new file mode 100644 (file)
index 0000000..55cbd78
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef __RADIO_TEA5777_H
+#define __RADIO_TEA5777_H
+
+/*
+ *   v4l2 driver for TEA5777 Philips AM/FM radio tuner chips
+ *
+ *     Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
+ *
+ *   Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips:
+ *
+ *     Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
+ *     Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+
+#define TEA575X_FMIF   10700
+#define TEA575X_AMIF     450
+
+struct radio_tea5777;
+
+struct radio_tea5777_ops {
+       /*
+        * Write the 6 bytes large write register of the tea5777
+        *
+        * val represents the 6 write registers, with byte 1 from the
+        * datasheet being the most significant byte (so byte 5 of the u64),
+        * and byte 6 from the datasheet being the least significant byte.
+        *
+        * returns 0 on success.
+        */
+       int (*write_reg)(struct radio_tea5777 *tea, u64 val);
+       /*
+        * Read the 3 bytes large read register of the tea5777
+        *
+        * The read value gets returned in val, akin to write_reg, byte 1 from
+        * the datasheet is stored as the most significant byte (so byte 2 of
+        * the u32), and byte 3 from the datasheet gets stored as the least
+        * significant byte (iow byte 0 of the u32).
+        *
+        * returns 0 on success.
+        */
+       int (*read_reg)(struct radio_tea5777 *tea, u32 *val);
+};
+
+struct radio_tea5777 {
+       struct v4l2_device *v4l2_dev;
+       struct v4l2_file_operations fops;
+       struct video_device vd;         /* video device */
+       bool has_am;                    /* Device can tune to AM freqs */
+       bool write_before_read;         /* must write before read quirk */
+       bool needs_write;               /* for write before read quirk */
+       u32 freq;                       /* current frequency */
+       u32 seek_rangelow;              /* current hwseek limits */
+       u32 seek_rangehigh;
+       u32 read_reg;
+       u64 write_reg;
+       struct mutex mutex;
+       struct radio_tea5777_ops *ops;
+       void *private_data;
+       u8 card[32];
+       u8 bus_info[32];
+       struct v4l2_ctrl_handler ctrl_handler;
+};
+
+int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner);
+void radio_tea5777_exit(struct radio_tea5777 *tea);
+
+#endif /* __RADIO_TEA5777_H */
index f1b607099b6c10361a1305c521b612d7c10ca8ff..e8428f573ccdf0a5649a9b04e187187117f33448 100644 (file)
@@ -1514,7 +1514,8 @@ static int wl1273_fm_vidioc_g_tuner(struct file *file, void *priv,
        tuner->rangehigh = WL1273_FREQ(WL1273_BAND_OTHER_HIGH);
 
        tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_RDS |
-               V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS_BLOCK_IO;
+               V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+               V4L2_TUNER_CAP_HWSEEK_BOUNDED | V4L2_TUNER_CAP_HWSEEK_WRAP;
 
        if (radio->stereo)
                tuner->audmode = V4L2_TUNER_MODE_STEREO;
index 969cf494d85bddb50d86bb2cd589c34d970ad5f4..9e38132afec66488299ebd4227a94e9a2e5d3a11 100644 (file)
@@ -4,6 +4,7 @@
  *  Driver for radios with Silicon Labs Si470x FM Radio Receivers
  *
  *  Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
+ *  Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -127,14 +128,6 @@ static unsigned short space = 2;
 module_param(space, ushort, 0444);
 MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
 
-/* Bottom of Band (MHz) */
-/* 0: 87.5 - 108 MHz (USA, Europe)*/
-/* 1: 76   - 108 MHz (Japan wide band) */
-/* 2: 76   -  90 MHz (Japan) */
-static unsigned short band = 1;
-module_param(band, ushort, 0444);
-MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
-
 /* De-emphasis */
 /* 0: 75 us (USA) */
 /* 1: 50 us (Europe, Australia, Japan) */
@@ -152,19 +145,66 @@ static unsigned int seek_timeout = 5000;
 module_param(seek_timeout, uint, 0644);
 MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
 
-
+static const struct v4l2_frequency_band bands[] = {
+       {
+               .type = V4L2_TUNER_RADIO,
+               .index = 0,
+               .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+                           V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+                           V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+                           V4L2_TUNER_CAP_HWSEEK_WRAP,
+               .rangelow   =  87500 * 16,
+               .rangehigh  = 108000 * 16,
+               .modulation = V4L2_BAND_MODULATION_FM,
+       },
+       {
+               .type = V4L2_TUNER_RADIO,
+               .index = 1,
+               .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+                           V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+                           V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+                           V4L2_TUNER_CAP_HWSEEK_WRAP,
+               .rangelow   =  76000 * 16,
+               .rangehigh  = 108000 * 16,
+               .modulation = V4L2_BAND_MODULATION_FM,
+       },
+       {
+               .type = V4L2_TUNER_RADIO,
+               .index = 2,
+               .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+                           V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+                           V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+                           V4L2_TUNER_CAP_HWSEEK_WRAP,
+               .rangelow   =  76000 * 16,
+               .rangehigh  =  90000 * 16,
+               .modulation = V4L2_BAND_MODULATION_FM,
+       },
+};
 
 /**************************************************************************
  * Generic Functions
  **************************************************************************/
 
+/*
+ * si470x_set_band - set the band
+ */
+static int si470x_set_band(struct si470x_device *radio, int band)
+{
+       if (radio->band == band)
+               return 0;
+
+       radio->band = band;
+       radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_BAND;
+       radio->registers[SYSCONFIG2] |= radio->band << 6;
+       return si470x_set_register(radio, SYSCONFIG2);
+}
+
 /*
  * si470x_set_chan - set the channel
  */
 static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
 {
        int retval;
-       unsigned long timeout;
        bool timed_out = 0;
 
        /* start tuning */
@@ -174,26 +214,12 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
        if (retval < 0)
                goto done;
 
-       /* currently I2C driver only uses interrupt way to tune */
-       if (radio->stci_enabled) {
-               INIT_COMPLETION(radio->completion);
-
-               /* wait till tune operation has completed */
-               retval = wait_for_completion_timeout(&radio->completion,
-                               msecs_to_jiffies(tune_timeout));
-               if (!retval)
-                       timed_out = true;
-       } else {
-               /* wait till tune operation has completed */
-               timeout = jiffies + msecs_to_jiffies(tune_timeout);
-               do {
-                       retval = si470x_get_register(radio, STATUSRSSI);
-                       if (retval < 0)
-                               goto stop;
-                       timed_out = time_after(jiffies, timeout);
-               } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
-                               && (!timed_out));
-       }
+       /* wait till tune operation has completed */
+       INIT_COMPLETION(radio->completion);
+       retval = wait_for_completion_timeout(&radio->completion,
+                       msecs_to_jiffies(tune_timeout));
+       if (!retval)
+               timed_out = true;
 
        if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
                dev_warn(&radio->videodev.dev, "tune does not complete\n");
@@ -201,7 +227,6 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
                dev_warn(&radio->videodev.dev,
                        "tune timed out after %u ms\n", tune_timeout);
 
-stop:
        /* stop tuning */
        radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
        retval = si470x_set_register(radio, CHANNEL);
@@ -210,48 +235,39 @@ done:
        return retval;
 }
 
-
 /*
- * si470x_get_freq - get the frequency
+ * si470x_get_step - get channel spacing
  */
-static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
+static unsigned int si470x_get_step(struct si470x_device *radio)
 {
-       unsigned int spacing, band_bottom;
-       unsigned short chan;
-       int retval;
-
        /* Spacing (kHz) */
        switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
        /* 0: 200 kHz (USA, Australia) */
        case 0:
-               spacing = 0.200 * FREQ_MUL; break;
+               return 200 * 16;
        /* 1: 100 kHz (Europe, Japan) */
        case 1:
-               spacing = 0.100 * FREQ_MUL; break;
+               return 100 * 16;
        /* 2:  50 kHz */
        default:
-               spacing = 0.050 * FREQ_MUL; break;
+               return 50 * 16;
        };
+}
 
-       /* Bottom of Band (MHz) */
-       switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
-       /* 0: 87.5 - 108 MHz (USA, Europe) */
-       case 0:
-               band_bottom = 87.5 * FREQ_MUL; break;
-       /* 1: 76   - 108 MHz (Japan wide band) */
-       default:
-               band_bottom = 76   * FREQ_MUL; break;
-       /* 2: 76   -  90 MHz (Japan) */
-       case 2:
-               band_bottom = 76   * FREQ_MUL; break;
-       };
+
+/*
+ * si470x_get_freq - get the frequency
+ */
+static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
+{
+       int chan, retval;
 
        /* read channel */
        retval = si470x_get_register(radio, READCHAN);
        chan = radio->registers[READCHAN] & READCHAN_READCHAN;
 
        /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
-       *freq = chan * spacing + band_bottom;
+       *freq = chan * si470x_get_step(radio) + bands[radio->band].rangelow;
 
        return retval;
 }
@@ -262,44 +278,12 @@ static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
  */
 int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
 {
-       unsigned int spacing, band_bottom, band_top;
        unsigned short chan;
 
-       /* Spacing (kHz) */
-       switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
-       /* 0: 200 kHz (USA, Australia) */
-       case 0:
-               spacing = 0.200 * FREQ_MUL; break;
-       /* 1: 100 kHz (Europe, Japan) */
-       case 1:
-               spacing = 0.100 * FREQ_MUL; break;
-       /* 2:  50 kHz */
-       default:
-               spacing = 0.050 * FREQ_MUL; break;
-       };
-
-       /* Bottom/Top of Band (MHz) */
-       switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
-       /* 0: 87.5 - 108 MHz (USA, Europe) */
-       case 0:
-               band_bottom = 87.5 * FREQ_MUL;
-               band_top = 108 * FREQ_MUL;
-               break;
-       /* 1: 76   - 108 MHz (Japan wide band) */
-       default:
-               band_bottom = 76 * FREQ_MUL;
-               band_top = 108 * FREQ_MUL;
-               break;
-       /* 2: 76   -  90 MHz (Japan) */
-       case 2:
-               band_bottom = 76 * FREQ_MUL;
-               band_top = 90 * FREQ_MUL;
-               break;
-       };
-
-       freq = clamp(freq, band_bottom, band_top);
+       freq = clamp(freq, bands[radio->band].rangelow,
+                          bands[radio->band].rangehigh);
        /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
-       chan = (freq - band_bottom) / spacing;
+       chan = (freq - bands[radio->band].rangelow) / si470x_get_step(radio);
 
        return si470x_set_chan(radio, chan);
 }
@@ -309,19 +293,43 @@ int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
  * si470x_set_seek - set seek
  */
 static int si470x_set_seek(struct si470x_device *radio,
-               unsigned int wrap_around, unsigned int seek_upward)
+                          struct v4l2_hw_freq_seek *seek)
 {
-       int retval = 0;
-       unsigned long timeout;
+       int band, retval;
+       unsigned int freq;
        bool timed_out = 0;
 
+       /* set band */
+       if (seek->rangelow || seek->rangehigh) {
+               for (band = 0; band < ARRAY_SIZE(bands); band++) {
+                       if (bands[band].rangelow  == seek->rangelow &&
+                           bands[band].rangehigh == seek->rangehigh)
+                               break;
+               }
+               if (band == ARRAY_SIZE(bands))
+                       return -EINVAL; /* No matching band found */
+       } else
+               band = 1; /* If nothing is specified seek 76 - 108 Mhz */
+
+       if (radio->band != band) {
+               retval = si470x_get_freq(radio, &freq);
+               if (retval)
+                       return retval;
+               retval = si470x_set_band(radio, band);
+               if (retval)
+                       return retval;
+               retval = si470x_set_freq(radio, freq);
+               if (retval)
+                       return retval;
+       }
+
        /* start seeking */
        radio->registers[POWERCFG] |= POWERCFG_SEEK;
-       if (wrap_around == 1)
+       if (seek->wrap_around)
                radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
        else
                radio->registers[POWERCFG] |= POWERCFG_SKMODE;
-       if (seek_upward == 1)
+       if (seek->seek_upward)
                radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
        else
                radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
@@ -329,26 +337,12 @@ static int si470x_set_seek(struct si470x_device *radio,
        if (retval < 0)
                return retval;
 
-       /* currently I2C driver only uses interrupt way to seek */
-       if (radio->stci_enabled) {
-               INIT_COMPLETION(radio->completion);
-
-               /* wait till seek operation has completed */
-               retval = wait_for_completion_timeout(&radio->completion,
-                               msecs_to_jiffies(seek_timeout));
-               if (!retval)
-                       timed_out = true;
-       } else {
-               /* wait till seek operation has completed */
-               timeout = jiffies + msecs_to_jiffies(seek_timeout);
-               do {
-                       retval = si470x_get_register(radio, STATUSRSSI);
-                       if (retval < 0)
-                               goto stop;
-                       timed_out = time_after(jiffies, timeout);
-               } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
-                               && (!timed_out));
-       }
+       /* wait till tune operation has completed */
+       INIT_COMPLETION(radio->completion);
+       retval = wait_for_completion_timeout(&radio->completion,
+                       msecs_to_jiffies(seek_timeout));
+       if (!retval)
+               timed_out = true;
 
        if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
                dev_warn(&radio->videodev.dev, "seek does not complete\n");
@@ -356,14 +350,13 @@ static int si470x_set_seek(struct si470x_device *radio,
                dev_warn(&radio->videodev.dev,
                        "seek failed / band limit reached\n");
 
-stop:
        /* stop seeking */
        radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
        retval = si470x_set_register(radio, POWERCFG);
 
        /* try again, if timed out */
        if (retval == 0 && timed_out)
-               return -EAGAIN;
+               return -ENODATA;
        return retval;
 }
 
@@ -391,8 +384,8 @@ int si470x_start(struct si470x_device *radio)
 
        /* sysconfig 2 */
        radio->registers[SYSCONFIG2] =
-               (0x3f  << 8) |                          /* SEEKTH */
-               ((band  << 6) & SYSCONFIG2_BAND)  |     /* BAND */
+               (0x1f  << 8) |                          /* SEEKTH */
+               ((radio->band << 6) & SYSCONFIG2_BAND) |/* BAND */
                ((space << 4) & SYSCONFIG2_SPACE) |     /* SPACE */
                15;                                     /* VOLUME (max) */
        retval = si470x_set_register(radio, SYSCONFIG2);
@@ -583,39 +576,26 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
                struct v4l2_tuner *tuner)
 {
        struct si470x_device *radio = video_drvdata(file);
-       int retval;
+       int retval = 0;
 
        if (tuner->index != 0)
                return -EINVAL;
 
-       retval = si470x_get_register(radio, STATUSRSSI);
-       if (retval < 0)
-               return retval;
+       if (!radio->status_rssi_auto_update) {
+               retval = si470x_get_register(radio, STATUSRSSI);
+               if (retval < 0)
+                       return retval;
+       }
 
        /* driver constants */
        strcpy(tuner->name, "FM");
        tuner->type = V4L2_TUNER_RADIO;
        tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
-                           V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO;
-
-       /* range limits */
-       switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
-       /* 0: 87.5 - 108 MHz (USA, Europe, default) */
-       default:
-               tuner->rangelow  =  87.5 * FREQ_MUL;
-               tuner->rangehigh = 108   * FREQ_MUL;
-               break;
-       /* 1: 76   - 108 MHz (Japan wide band) */
-       case 1:
-               tuner->rangelow  =  76   * FREQ_MUL;
-               tuner->rangehigh = 108   * FREQ_MUL;
-               break;
-       /* 2: 76   -  90 MHz (Japan) */
-       case 2:
-               tuner->rangelow  =  76   * FREQ_MUL;
-               tuner->rangehigh =  90   * FREQ_MUL;
-               break;
-       };
+                           V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+                           V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+                           V4L2_TUNER_CAP_HWSEEK_WRAP;
+       tuner->rangelow  =  76 * FREQ_MUL;
+       tuner->rangehigh = 108 * FREQ_MUL;
 
        /* stereo indicator == stereo (instead of mono) */
        if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
@@ -698,10 +678,18 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv,
                struct v4l2_frequency *freq)
 {
        struct si470x_device *radio = video_drvdata(file);
+       int retval;
 
        if (freq->tuner != 0)
                return -EINVAL;
 
+       if (freq->frequency < bands[radio->band].rangelow ||
+           freq->frequency > bands[radio->band].rangehigh) {
+               /* Switch to band 1 which covers everything we support */
+               retval = si470x_set_band(radio, 1);
+               if (retval)
+                       return retval;
+       }
        return si470x_set_freq(radio, freq->frequency);
 }
 
@@ -717,7 +705,21 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
        if (seek->tuner != 0)
                return -EINVAL;
 
-       return si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
+       return si470x_set_seek(radio, seek);
+}
+
+/*
+ * si470x_vidioc_enum_freq_bands - enumerate supported bands
+ */
+static int si470x_vidioc_enum_freq_bands(struct file *file, void *priv,
+                                        struct v4l2_frequency_band *band)
+{
+       if (band->tuner != 0)
+               return -EINVAL;
+       if (band->index >= ARRAY_SIZE(bands))
+               return -EINVAL;
+       *band = bands[band->index];
+       return 0;
 }
 
 const struct v4l2_ctrl_ops si470x_ctrl_ops = {
@@ -734,6 +736,7 @@ static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
        .vidioc_g_frequency     = si470x_vidioc_g_frequency,
        .vidioc_s_frequency     = si470x_vidioc_s_frequency,
        .vidioc_s_hw_freq_seek  = si470x_vidioc_s_hw_freq_seek,
+       .vidioc_enum_freq_bands = si470x_vidioc_enum_freq_bands,
        .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
        .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
 };
index a80044c5874e4c3fafef10fafd7a6ae41d484eb9..643a6ff7c5d0e629e70112c0c4e67fe0459c580b 100644 (file)
@@ -350,7 +350,9 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
        }
 
        radio->client = client;
+       radio->band = 1; /* Default to 76 - 108 MHz */
        mutex_init(&radio->lock);
+       init_completion(&radio->completion);
 
        /* video device initialization */
        radio->videodev = si470x_viddev_template;
@@ -406,10 +408,6 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
        radio->rd_index = 0;
        init_waitqueue_head(&radio->read_queue);
 
-       /* mark Seek/Tune Complete Interrupt enabled */
-       radio->stci_enabled = true;
-       init_completion(&radio->completion);
-
        retval = request_threaded_irq(client->irq, NULL, si470x_i2c_interrupt,
                        IRQF_TRIGGER_FALLING, DRIVER_NAME, radio);
        if (retval) {
index f412f7ab270b63e0f39bc47aefdf58b3d3e5f82c..146be4263ea17d92ab072d13ae4d12fa610b9276 100644 (file)
@@ -143,7 +143,7 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
  * Software/Hardware Versions from Scratch Page
  **************************************************************************/
 #define RADIO_SW_VERSION_NOT_BOOTLOADABLE      6
-#define RADIO_SW_VERSION                       7
+#define RADIO_SW_VERSION                       1
 #define RADIO_HW_VERSION                       1
 
 
@@ -399,12 +399,19 @@ static void si470x_int_in_callback(struct urb *urb)
                }
        }
 
-       if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+       /* Sometimes the device returns len 0 packets */
+       if (urb->actual_length != RDS_REPORT_SIZE)
                goto resubmit;
 
-       if (urb->actual_length > 0) {
+       radio->registers[STATUSRSSI] =
+               get_unaligned_be16(&radio->int_in_buffer[1]);
+
+       if (radio->registers[STATUSRSSI] & STATUSRSSI_STC)
+               complete(&radio->completion);
+
+       if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS)) {
                /* Update RDS registers with URB data */
-               for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
+               for (regnr = 1; regnr < RDS_REGISTER_NUM; regnr++)
                        radio->registers[STATUSRSSI + regnr] =
                            get_unaligned_be16(&radio->int_in_buffer[
                                regnr * RADIO_REGISTER_SIZE + 1]);
@@ -480,6 +487,7 @@ resubmit:
                        radio->int_in_running = 0;
                }
        }
+       radio->status_rssi_auto_update = radio->int_in_running;
 }
 
 
@@ -534,13 +542,6 @@ static int si470x_start_usb(struct si470x_device *radio)
 {
        int retval;
 
-       /* start radio */
-       retval = si470x_start(radio);
-       if (retval < 0)
-               return retval;
-
-       v4l2_ctrl_handler_setup(&radio->hdl);
-
        /* initialize interrupt urb */
        usb_fill_int_urb(radio->int_in_urb, radio->usbdev,
                        usb_rcvintpipe(radio->usbdev,
@@ -560,6 +561,15 @@ static int si470x_start_usb(struct si470x_device *radio)
                                "submitting int urb failed (%d)\n", retval);
                radio->int_in_running = 0;
        }
+       radio->status_rssi_auto_update = radio->int_in_running;
+
+       /* start radio */
+       retval = si470x_start(radio);
+       if (retval < 0)
+               return retval;
+
+       v4l2_ctrl_handler_setup(&radio->hdl);
+
        return retval;
 }
 
@@ -587,7 +597,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
        }
        radio->usbdev = interface_to_usbdev(intf);
        radio->intf = intf;
+       radio->band = 1; /* Default to 76 - 108 MHz */
        mutex_init(&radio->lock);
+       init_completion(&radio->completion);
 
        iface_desc = intf->cur_altsetting;
 
@@ -698,9 +710,6 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
                        "linux-media@vger.kernel.org\n");
        }
 
-       /* set initial frequency */
-       si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
-
        /* set led to connect state */
        si470x_set_led_state(radio, BLINK_GREEN_LED);
 
@@ -723,6 +732,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
        if (retval < 0)
                goto err_all;
 
+       /* set initial frequency */
+       si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
+
        /* register video device */
        retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO,
                        radio_nr);
@@ -781,11 +793,16 @@ static int si470x_usb_driver_suspend(struct usb_interface *intf,
 static int si470x_usb_driver_resume(struct usb_interface *intf)
 {
        struct si470x_device *radio = usb_get_intfdata(intf);
+       int ret;
 
        dev_info(&intf->dev, "resuming now...\n");
 
        /* start radio */
-       return si470x_start_usb(radio);
+       ret = si470x_start_usb(radio);
+       if (ret == 0)
+               v4l2_ctrl_handler_setup(&radio->hdl);
+
+       return ret;
 }
 
 
index 4921cab8e0fa084b4add96a348515749cde18de8..2f089b4252dfe2f9f10956acb267392a38373caa 100644 (file)
@@ -87,7 +87,7 @@
 
 #define SYSCONFIG2             5       /* System Configuration 2 */
 #define SYSCONFIG2_SEEKTH      0xff00  /* bits 15..08: RSSI Seek Threshold */
-#define SYSCONFIG2_BAND                0x0080  /* bits 07..06: Band Select */
+#define SYSCONFIG2_BAND                0x00c0  /* bits 07..06: Band Select */
 #define SYSCONFIG2_SPACE       0x0030  /* bits 05..04: Channel Spacing */
 #define SYSCONFIG2_VOLUME      0x000f  /* bits 03..00: Volume */
 
@@ -147,6 +147,7 @@ struct si470x_device {
        struct v4l2_device v4l2_dev;
        struct video_device videodev;
        struct v4l2_ctrl_handler hdl;
+       int band;
 
        /* Silabs internal registers (0..15) */
        unsigned short registers[RADIO_REGISTER_NUM];
@@ -160,7 +161,7 @@ struct si470x_device {
        unsigned int wr_index;
 
        struct completion completion;
-       bool stci_enabled;              /* Seek/Tune Complete Interrupt */
+       bool status_rssi_auto_update;   /* Does RSSI get updated automatic? */
 
 #if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
        /* reference to USB and video device */
@@ -189,7 +190,7 @@ struct si470x_device {
  * Firmware Versions
  **************************************************************************/
 
-#define RADIO_FW_VERSION       15
+#define RADIO_FW_VERSION       12
 
 
 
index 43fb72291bea15551f7bf5ca01577d23db879ef9..3dd9fc097c473f5974034fdce141079e242e915e 100644 (file)
@@ -251,7 +251,7 @@ again:
        if (!timeleft) {
                fmerr("Timeout(%d sec),didn't get tune ended int\n",
                           jiffies_to_msecs(FM_DRV_RX_SEEK_TIMEOUT) / 1000);
-               return -ETIMEDOUT;
+               return -ENODATA;
        }
 
        int_reason = fmdev->irq_info.flag & (FM_TUNE_COMPLETE | FM_BAND_LIMIT);
index 080b96a61f1a41783e8d7025e63722fb94ceeb0e..49a11ec1f44967fe99bce27b3a602ed320e41170 100644 (file)
@@ -285,7 +285,9 @@ static int fm_v4l2_vidioc_g_tuner(struct file *file, void *priv,
        tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO |
        ((fmdev->rx.rds.flag == FM_RDS_ENABLE) ? V4L2_TUNER_SUB_RDS : 0);
        tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
-                           V4L2_TUNER_CAP_LOW;
+                           V4L2_TUNER_CAP_LOW |
+                           V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+                           V4L2_TUNER_CAP_HWSEEK_WRAP;
        tuner->audmode = (stereo_mono_mode ?
                          V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO);
 
index f97eeb870455a975fc5ee5c0b7135e4eaa084fc7..5180390be7ab0915ffa24189269ce4707e1e3baf 100644 (file)
@@ -1,21 +1,20 @@
-menuconfig RC_CORE
-       tristate "Remote Controller adapters"
+config RC_CORE
+       tristate
+       depends on MEDIA_RC_SUPPORT
        depends on INPUT
-       default INPUT
-       ---help---
-         Enable support for Remote Controllers on Linux. This is
-         needed in order to support several video capture adapters,
-         standalone IR receivers/transmitters, and RF receivers.
+       default y
 
-         Enable this option if you have a video capture board even
-         if you don't need IR, as otherwise, you may not be able to
-         compile the driver for your adapter.
+source "drivers/media/rc/keymaps/Kconfig"
 
-if RC_CORE
+menuconfig RC_DECODERS
+        bool "Remote controller decoders"
+       depends on RC_CORE
+       default y
 
+if RC_DECODERS
 config LIRC
-       tristate
-       default y
+       tristate "LIRC interface driver"
+       depends on RC_CORE
 
        ---help---
           Enable this option to build the Linux Infrared Remote
@@ -24,7 +23,16 @@ config LIRC
           LIRC daemon handles protocol decoding for IR reception and
           encoding for IR transmitting (aka "blasting").
 
-source "drivers/media/rc/keymaps/Kconfig"
+config IR_LIRC_CODEC
+       tristate "Enable IR to LIRC bridge"
+       depends on RC_CORE
+       depends on LIRC
+       default y
+
+       ---help---
+          Enable this option to pass raw IR to and from userspace via
+          the LIRC interface.
+
 
 config IR_NEC_DECODER
        tristate "Enable IR raw decoder for the NEC protocol"
@@ -108,16 +116,13 @@ config IR_MCE_KBD_DECODER
           Enable this option if you have a Microsoft Remote Keyboard for
           Windows Media Center Edition, which you would like to use with
           a raw IR receiver in your system.
+endif #RC_DECODERS
 
-config IR_LIRC_CODEC
-       tristate "Enable IR to LIRC bridge"
+menuconfig RC_DEVICES
+       bool "Remote Controller devices"
        depends on RC_CORE
-       depends on LIRC
-       default y
 
-       ---help---
-          Enable this option to pass raw IR to and from userspace via
-          the LIRC interface.
+if RC_DEVICES
 
 config RC_ATI_REMOTE
        tristate "ATI / X10 based USB RF remote controls"
@@ -254,6 +259,17 @@ config IR_WINBOND_CIR
           To compile this driver as a module, choose M here: the module will
           be called winbond_cir.
 
+config IR_IGUANA
+       tristate "IguanaWorks USB IR Transceiver"
+       depends on RC_CORE
+       select USB
+       ---help---
+          Say Y here if you want to use the IgaunaWorks USB IR Transceiver.
+          Both infrared receive and send are supported.
+
+          To compile this driver as a module, choose M here: the module will
+          be called iguanair.
+
 config RC_LOOPBACK
        tristate "Remote Control Loopback Driver"
        depends on RC_CORE
@@ -276,4 +292,4 @@ config IR_GPIO_CIR
           To compile this driver as a module, choose M here: the module will
           be called gpio-ir-recv.
 
-endif #RC_CORE
+endif #RC_DEVICES
index 29f364f88a94d0e8c492829c2a1fd280d1765704..f871d1986c21901011477f61f60374d032c28c14 100644 (file)
@@ -27,3 +27,4 @@ obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
 obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
 obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
 obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
+obj-$(CONFIG_IR_IGUANA) += iguanair.o
index 7be377fc1be8a02fd9ac07140de7252ad299fbe2..8fa72e2dacb1cf709abded7d041fd35d0e2f456e 100644 (file)
@@ -147,7 +147,8 @@ static bool mouse = true;
 module_param(mouse, bool, 0444);
 MODULE_PARM_DESC(mouse, "Enable mouse device, default = yes");
 
-#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+#define dbginfo(dev, format, arg...) \
+       do { if (debug) dev_info(dev , format , ## arg); } while (0)
 #undef err
 #define err(format, arg...) printk(KERN_ERR format , ## arg)
 
@@ -191,17 +192,41 @@ static const char *get_medion_keymap(struct usb_interface *interface)
        return RC_MAP_MEDION_X10;
 }
 
-static const struct ati_receiver_type type_ati         = { .default_keymap = RC_MAP_ATI_X10 };
-static const struct ati_receiver_type type_medion      = { .get_default_keymap = get_medion_keymap };
-static const struct ati_receiver_type type_firefly     = { .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY };
+static const struct ati_receiver_type type_ati         = {
+       .default_keymap = RC_MAP_ATI_X10
+};
+static const struct ati_receiver_type type_medion      = {
+       .get_default_keymap = get_medion_keymap
+};
+static const struct ati_receiver_type type_firefly     = {
+       .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY
+};
 
 static struct usb_device_id ati_remote_table[] = {
-       { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID),     .driver_info = (unsigned long)&type_ati },
-       { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID),    .driver_info = (unsigned long)&type_ati },
-       { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID),      .driver_info = (unsigned long)&type_ati },
-       { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID),   .driver_info = (unsigned long)&type_ati },
-       { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID),   .driver_info = (unsigned long)&type_medion },
-       { USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID),  .driver_info = (unsigned long)&type_firefly },
+       {
+               USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID),
+               .driver_info = (unsigned long)&type_ati
+       },
+       {
+               USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID),
+               .driver_info = (unsigned long)&type_ati
+       },
+       {
+               USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID),
+               .driver_info = (unsigned long)&type_ati
+       },
+       {
+               USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID),
+               .driver_info = (unsigned long)&type_ati
+       },
+       {
+               USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID),
+               .driver_info = (unsigned long)&type_medion
+       },
+       {
+               USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID),
+               .driver_info = (unsigned long)&type_firefly
+       },
        {}      /* Terminating entry */
 };
 
@@ -296,25 +321,8 @@ static const struct {
        {KIND_END, 0x00, EV_MAX + 1, 0, 0}
 };
 
-/* Local function prototypes */
-static int ati_remote_sendpacket       (struct ati_remote *ati_remote, u16 cmd, unsigned char *data);
-static void ati_remote_irq_out         (struct urb *urb);
-static void ati_remote_irq_in          (struct urb *urb);
-static void ati_remote_input_report    (struct urb *urb);
-static int ati_remote_initialize       (struct ati_remote *ati_remote);
-static int ati_remote_probe            (struct usb_interface *interface, const struct usb_device_id *id);
-static void ati_remote_disconnect      (struct usb_interface *interface);
-
-/* usb specific object to register with the usb subsystem */
-static struct usb_driver ati_remote_driver = {
-       .name         = "ati_remote",
-       .probe        = ati_remote_probe,
-       .disconnect   = ati_remote_disconnect,
-       .id_table     = ati_remote_table,
-};
-
 /*
- *     ati_remote_dump_input
+ * ati_remote_dump_input
  */
 static void ati_remote_dump(struct device *dev, unsigned char *data,
                            unsigned int len)
@@ -326,12 +334,14 @@ static void ati_remote_dump(struct device *dev, unsigned char *data,
                dev_warn(dev, "Weird key %02x %02x %02x %02x\n",
                     data[0], data[1], data[2], data[3]);
        else
-               dev_warn(dev, "Weird data, len=%d %02x %02x %02x %02x %02x %02x ...\n",
-                    len, data[0], data[1], data[2], data[3], data[4], data[5]);
+               dev_warn(dev,
+                       "Weird data, len=%d %02x %02x %02x %02x %02x %02x ...\n",
+                       len, data[0], data[1], data[2], data[3], data[4],
+                       data[5]);
 }
 
 /*
- *     ati_remote_open
+ * ati_remote_open
  */
 static int ati_remote_open(struct ati_remote *ati_remote)
 {
@@ -355,7 +365,7 @@ out:        mutex_unlock(&ati_remote->open_mutex);
 }
 
 /*
- *     ati_remote_close
+ * ati_remote_close
  */
 static void ati_remote_close(struct ati_remote *ati_remote)
 {
@@ -390,7 +400,7 @@ static void ati_remote_rc_close(struct rc_dev *rdev)
 }
 
 /*
- *             ati_remote_irq_out
+ * ati_remote_irq_out
  */
 static void ati_remote_irq_out(struct urb *urb)
 {
@@ -408,11 +418,12 @@ static void ati_remote_irq_out(struct urb *urb)
 }
 
 /*
- *     ati_remote_sendpacket
+ * ati_remote_sendpacket
  *
- *     Used to send device initialization strings
+ * Used to send device initialization strings
  */
-static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data)
+static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd,
+       unsigned char *data)
 {
        int retval = 0;
 
@@ -441,7 +452,7 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigne
 }
 
 /*
- *     ati_remote_compute_accel
+ * ati_remote_compute_accel
  *
  * Implements acceleration curve for directional control pad
  * If elapsed time since last event is > 1/4 second, user "stopped",
@@ -478,7 +489,7 @@ static int ati_remote_compute_accel(struct ati_remote *ati_remote)
 }
 
 /*
- *     ati_remote_report_input
+ * ati_remote_report_input
  */
 static void ati_remote_input_report(struct urb *urb)
 {
@@ -518,7 +529,8 @@ static void ati_remote_input_report(struct urb *urb)
        remote_num = (data[3] >> 4) & 0x0f;
        if (channel_mask & (1 << (remote_num + 1))) {
                dbginfo(&ati_remote->interface->dev,
-                       "Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n",
+                       "Masked input from channel 0x%02x: data %02x,%02x, "
+                       "mask= 0x%02lx\n",
                        remote_num, data[1], data[2], channel_mask);
                return;
        }
@@ -546,7 +558,9 @@ static void ati_remote_input_report(struct urb *urb)
                if (wheel_keycode == KEY_RESERVED) {
                        /* scrollwheel was not mapped, assume mouse */
 
-                       /* Look up event code index in the mouse translation table. */
+                       /* Look up event code index in the mouse translation
+                        * table.
+                        */
                        for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
                                if (scancode == ati_remote_tbl[i].data) {
                                        index = i;
@@ -630,9 +644,9 @@ static void ati_remote_input_report(struct urb *urb)
        } else {
 
                /*
-                * Other event kinds are from the directional control pad, and have an
-                * acceleration factor applied to them.  Without this acceleration, the
-                * control pad is mostly unusable.
+                * Other event kinds are from the directional control pad, and
+                * have an acceleration factor applied to them.  Without this
+                * acceleration, the control pad is mostly unusable.
                 */
                acc = ati_remote_compute_accel(ati_remote);
 
@@ -659,7 +673,8 @@ static void ati_remote_input_report(struct urb *urb)
                        input_report_rel(dev, REL_Y, acc);
                        break;
                default:
-                       dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n",
+                       dev_dbg(&ati_remote->interface->dev,
+                               "ati_remote kind=%d\n",
                                ati_remote_tbl[index].kind);
                }
                input_sync(dev);
@@ -670,7 +685,7 @@ static void ati_remote_input_report(struct urb *urb)
 }
 
 /*
- *     ati_remote_irq_in
+ * ati_remote_irq_in
  */
 static void ati_remote_irq_in(struct urb *urb)
 {
@@ -684,22 +699,25 @@ static void ati_remote_irq_in(struct urb *urb)
        case -ECONNRESET:       /* unlink */
        case -ENOENT:
        case -ESHUTDOWN:
-               dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n",
+               dev_dbg(&ati_remote->interface->dev,
+                       "%s: urb error status, unlink?\n",
                        __func__);
                return;
        default:                /* error */
-               dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n",
+               dev_dbg(&ati_remote->interface->dev,
+                       "%s: Nonzero urb status %d\n",
                        __func__, urb->status);
        }
 
        retval = usb_submit_urb(urb, GFP_ATOMIC);
        if (retval)
-               dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n",
+               dev_err(&ati_remote->interface->dev,
+                       "%s: usb_submit_urb()=%d\n",
                        __func__, retval);
 }
 
 /*
- *     ati_remote_alloc_buffers
+ * ati_remote_alloc_buffers
  */
 static int ati_remote_alloc_buffers(struct usb_device *udev,
                                    struct ati_remote *ati_remote)
@@ -726,7 +744,7 @@ static int ati_remote_alloc_buffers(struct usb_device *udev,
 }
 
 /*
- *     ati_remote_free_buffers
+ * ati_remote_free_buffers
  */
 static void ati_remote_free_buffers(struct ati_remote *ati_remote)
 {
@@ -825,9 +843,10 @@ static int ati_remote_initialize(struct ati_remote *ati_remote)
 }
 
 /*
- *     ati_remote_probe
+ * ati_remote_probe
  */
-static int ati_remote_probe(struct usb_interface *interface, const struct usb_device_id *id)
+static int ati_remote_probe(struct usb_interface *interface,
+       const struct usb_device_id *id)
 {
        struct usb_device *udev = interface_to_usbdev(interface);
        struct usb_host_interface *iface_host = interface->cur_altsetting;
@@ -949,7 +968,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
 }
 
 /*
- *     ati_remote_disconnect
+ * ati_remote_disconnect
  */
 static void ati_remote_disconnect(struct usb_interface *interface)
 {
@@ -971,6 +990,14 @@ static void ati_remote_disconnect(struct usb_interface *interface)
        kfree(ati_remote);
 }
 
+/* usb specific object to register with the usb subsystem */
+static struct usb_driver ati_remote_driver = {
+       .name         = "ati_remote",
+       .probe        = ati_remote_probe,
+       .disconnect   = ati_remote_disconnect,
+       .id_table     = ati_remote_table,
+};
+
 module_usb_driver(ati_remote_driver);
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
index bef5296173c904baffc14b8856627839de57f51d..647dd951b0e8c31772e66bcdc333a54c3f839574 100644 (file)
@@ -1018,6 +1018,8 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 
        spin_lock_init(&dev->hw_lock);
 
+       dev->hw_io = pnp_port_start(pnp_dev, 0);
+
        pnp_set_drvdata(pnp_dev, dev);
        dev->pnp_dev = pnp_dev;
 
@@ -1072,7 +1074,6 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 
        /* claim the resources */
        error = -EBUSY;
-       dev->hw_io = pnp_port_start(pnp_dev, 0);
        if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) {
                dev->hw_io = -1;
                dev->irq = -1;
index 6aabf7ae3a31b79948f6afa5341284ce185f5ca9..ab30c64f812491d186e3a088bea23cc6849651a2 100644 (file)
@@ -23,6 +23,8 @@
  * USA
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/pnp.h>
@@ -110,30 +112,32 @@ static u8 fintek_cir_reg_read(struct fintek_dev *fintek, u8 offset)
        return val;
 }
 
-#define pr_reg(text, ...) \
-       printk(KERN_INFO KBUILD_MODNAME ": " text, ## __VA_ARGS__)
-
 /* dump current cir register contents */
 static void cir_dump_regs(struct fintek_dev *fintek)
 {
        fintek_config_mode_enable(fintek);
        fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
 
-       pr_reg("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME);
-       pr_reg(" * CR CIR BASE ADDR: 0x%x\n",
-              (fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) |
+       pr_info("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME);
+       pr_info(" * CR CIR BASE ADDR: 0x%x\n",
+               (fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) |
                fintek_cr_read(fintek, CIR_CR_BASE_ADDR_LO));
-       pr_reg(" * CR CIR IRQ NUM:   0x%x\n",
-              fintek_cr_read(fintek, CIR_CR_IRQ_SEL));
+       pr_info(" * CR CIR IRQ NUM:   0x%x\n",
+               fintek_cr_read(fintek, CIR_CR_IRQ_SEL));
 
        fintek_config_mode_disable(fintek);
 
-       pr_reg("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME);
-       pr_reg(" * STATUS:     0x%x\n", fintek_cir_reg_read(fintek, CIR_STATUS));
-       pr_reg(" * CONTROL:    0x%x\n", fintek_cir_reg_read(fintek, CIR_CONTROL));
-       pr_reg(" * RX_DATA:    0x%x\n", fintek_cir_reg_read(fintek, CIR_RX_DATA));
-       pr_reg(" * TX_CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_CONTROL));
-       pr_reg(" * TX_DATA:    0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_DATA));
+       pr_info("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME);
+       pr_info(" * STATUS:     0x%x\n",
+               fintek_cir_reg_read(fintek, CIR_STATUS));
+       pr_info(" * CONTROL:    0x%x\n",
+               fintek_cir_reg_read(fintek, CIR_CONTROL));
+       pr_info(" * RX_DATA:    0x%x\n",
+               fintek_cir_reg_read(fintek, CIR_RX_DATA));
+       pr_info(" * TX_CONTROL: 0x%x\n",
+               fintek_cir_reg_read(fintek, CIR_TX_CONTROL));
+       pr_info(" * TX_DATA:    0x%x\n",
+               fintek_cir_reg_read(fintek, CIR_TX_DATA));
 }
 
 /* detect hardware features */
index 0d875450c5ce3d92f1295445b852f77617ed5364..04cb272db16a37fb11e2cd2cdeadb75b3d7b3760 100644 (file)
@@ -82,12 +82,21 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
                goto err_allocate_device;
        }
 
+       rcdev->priv = gpio_dev;
        rcdev->driver_type = RC_DRIVER_IR_RAW;
-       rcdev->allowed_protos = RC_TYPE_ALL;
        rcdev->input_name = GPIO_IR_DEVICE_NAME;
+       rcdev->input_phys = GPIO_IR_DEVICE_NAME "/input0";
        rcdev->input_id.bustype = BUS_HOST;
+       rcdev->input_id.vendor = 0x0001;
+       rcdev->input_id.product = 0x0001;
+       rcdev->input_id.version = 0x0100;
+       rcdev->dev.parent = &pdev->dev;
        rcdev->driver_name = GPIO_IR_DRIVER_NAME;
-       rcdev->map_name = RC_MAP_EMPTY;
+       if (pdata->allowed_protos)
+               rcdev->allowed_protos = pdata->allowed_protos;
+       else
+               rcdev->allowed_protos = RC_TYPE_ALL;
+       rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY;
 
        gpio_dev->rcdev = rcdev;
        gpio_dev->gpio_nr = pdata->gpio_nr;
@@ -188,18 +197,7 @@ static struct platform_driver gpio_ir_recv_driver = {
 #endif
        },
 };
-
-static int __init gpio_ir_recv_init(void)
-{
-       return platform_driver_register(&gpio_ir_recv_driver);
-}
-module_init(gpio_ir_recv_init);
-
-static void __exit gpio_ir_recv_exit(void)
-{
-       platform_driver_unregister(&gpio_ir_recv_driver);
-}
-module_exit(gpio_ir_recv_exit);
+module_platform_driver(gpio_ir_recv_driver);
 
 MODULE_DESCRIPTION("GPIO IR Receiver driver");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
new file mode 100644 (file)
index 0000000..5e2eaf8
--- /dev/null
@@ -0,0 +1,639 @@
+/*
+ * IguanaWorks USB IR Transceiver support
+ *
+ * Copyright (C) 2012 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <media/rc-core.h>
+
+#define DRIVER_NAME "iguanair"
+
+struct iguanair {
+       struct rc_dev *rc;
+
+       struct device *dev;
+       struct usb_device *udev;
+
+       int pipe_in, pipe_out;
+       uint8_t bufsize;
+       uint8_t version[2];
+
+       struct mutex lock;
+
+       /* receiver support */
+       bool receiver_on;
+       dma_addr_t dma_in;
+       uint8_t *buf_in;
+       struct urb *urb_in;
+       struct completion completion;
+
+       /* transmit support */
+       bool tx_overflow;
+       uint32_t carrier;
+       uint8_t cycle_overhead;
+       uint8_t channels;
+       uint8_t busy4;
+       uint8_t busy7;
+
+       char name[64];
+       char phys[64];
+};
+
+#define CMD_GET_VERSION                0x01
+#define CMD_GET_BUFSIZE                0x11
+#define CMD_GET_FEATURES       0x10
+#define CMD_SEND               0x15
+#define CMD_EXECUTE            0x1f
+#define CMD_RX_OVERFLOW                0x31
+#define CMD_TX_OVERFLOW                0x32
+#define CMD_RECEIVER_ON                0x12
+#define CMD_RECEIVER_OFF       0x14
+
+#define DIR_IN                 0xdc
+#define DIR_OUT                        0xcd
+
+#define MAX_PACKET_SIZE                8u
+#define TIMEOUT                        1000
+
+struct packet {
+       uint16_t start;
+       uint8_t direction;
+       uint8_t cmd;
+};
+
+struct response_packet {
+       struct packet header;
+       uint8_t data[4];
+};
+
+struct send_packet {
+       struct packet header;
+       uint8_t length;
+       uint8_t channels;
+       uint8_t busy7;
+       uint8_t busy4;
+       uint8_t payload[0];
+};
+
+static void process_ir_data(struct iguanair *ir, unsigned len)
+{
+       if (len >= 4 && ir->buf_in[0] == 0 && ir->buf_in[1] == 0) {
+               switch (ir->buf_in[3]) {
+               case CMD_TX_OVERFLOW:
+                       ir->tx_overflow = true;
+               case CMD_RECEIVER_OFF:
+               case CMD_RECEIVER_ON:
+               case CMD_SEND:
+                       complete(&ir->completion);
+                       break;
+               case CMD_RX_OVERFLOW:
+                       dev_warn(ir->dev, "receive overflow\n");
+                       break;
+               default:
+                       dev_warn(ir->dev, "control code %02x received\n",
+                                                       ir->buf_in[3]);
+                       break;
+               }
+       } else if (len >= 7) {
+               DEFINE_IR_RAW_EVENT(rawir);
+               unsigned i;
+
+               init_ir_raw_event(&rawir);
+
+               for (i = 0; i < 7; i++) {
+                       if (ir->buf_in[i] == 0x80) {
+                               rawir.pulse = false;
+                               rawir.duration = US_TO_NS(21845);
+                       } else {
+                               rawir.pulse = (ir->buf_in[i] & 0x80) == 0;
+                               rawir.duration = ((ir->buf_in[i] & 0x7f) + 1) *
+                                                                        21330;
+                       }
+
+                       ir_raw_event_store_with_filter(ir->rc, &rawir);
+               }
+
+               ir_raw_event_handle(ir->rc);
+       }
+}
+
+static void iguanair_rx(struct urb *urb)
+{
+       struct iguanair *ir;
+
+       if (!urb)
+               return;
+
+       ir = urb->context;
+       if (!ir) {
+               usb_unlink_urb(urb);
+               return;
+       }
+
+       switch (urb->status) {
+       case 0:
+               process_ir_data(ir, urb->actual_length);
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               usb_unlink_urb(urb);
+               return;
+       case -EPIPE:
+       default:
+               dev_dbg(ir->dev, "Error: urb status = %d\n", urb->status);
+               break;
+       }
+
+       usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int iguanair_send(struct iguanair *ir, void *data, unsigned size,
+                       struct response_packet *response, unsigned *res_len)
+{
+       unsigned offset, len;
+       int rc, transferred;
+
+       for (offset = 0; offset < size; offset += MAX_PACKET_SIZE) {
+               len = min(size - offset, MAX_PACKET_SIZE);
+
+               if (ir->tx_overflow)
+                       return -EOVERFLOW;
+
+               rc = usb_interrupt_msg(ir->udev, ir->pipe_out, data + offset,
+                                               len, &transferred, TIMEOUT);
+               if (rc)
+                       return rc;
+
+               if (transferred != len)
+                       return -EIO;
+       }
+
+       if (response) {
+               rc = usb_interrupt_msg(ir->udev, ir->pipe_in, response,
+                                       sizeof(*response), res_len, TIMEOUT);
+       }
+
+       return rc;
+}
+
+static int iguanair_get_features(struct iguanair *ir)
+{
+       struct packet packet;
+       struct response_packet response;
+       int rc, len;
+
+       packet.start = 0;
+       packet.direction = DIR_OUT;
+       packet.cmd = CMD_GET_VERSION;
+
+       rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+       if (rc) {
+               dev_info(ir->dev, "failed to get version\n");
+               goto out;
+       }
+
+       if (len != 6) {
+               dev_info(ir->dev, "failed to get version\n");
+               rc = -EIO;
+               goto out;
+       }
+
+       ir->version[0] = response.data[0];
+       ir->version[1] = response.data[1];
+       ir->bufsize = 150;
+       ir->cycle_overhead = 65;
+
+       packet.cmd = CMD_GET_BUFSIZE;
+
+       rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+       if (rc) {
+               dev_info(ir->dev, "failed to get buffer size\n");
+               goto out;
+       }
+
+       if (len != 5) {
+               dev_info(ir->dev, "failed to get buffer size\n");
+               rc = -EIO;
+               goto out;
+       }
+
+       ir->bufsize = response.data[0];
+
+       if (ir->version[0] == 0 || ir->version[1] == 0)
+               goto out;
+
+       packet.cmd = CMD_GET_FEATURES;
+
+       rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+       if (rc) {
+               dev_info(ir->dev, "failed to get features\n");
+               goto out;
+       }
+
+       if (len < 5) {
+               dev_info(ir->dev, "failed to get features\n");
+               rc = -EIO;
+               goto out;
+       }
+
+       if (len > 5 && ir->version[0] >= 4)
+               ir->cycle_overhead = response.data[1];
+
+out:
+       return rc;
+}
+
+static int iguanair_receiver(struct iguanair *ir, bool enable)
+{
+       struct packet packet = { 0, DIR_OUT, enable ?
+                               CMD_RECEIVER_ON : CMD_RECEIVER_OFF };
+       int rc;
+
+       INIT_COMPLETION(ir->completion);
+
+       rc = iguanair_send(ir, &packet, sizeof(packet), NULL, NULL);
+       if (rc)
+               return rc;
+
+       wait_for_completion_timeout(&ir->completion, TIMEOUT);
+
+       return 0;
+}
+
+/*
+ * The iguana ir creates the carrier by busy spinning after each pulse or
+ * space. This is counted in CPU cycles, with the CPU running at 24MHz. It is
+ * broken down into 7-cycles and 4-cyles delays, with a preference for
+ * 4-cycle delays.
+ */
+static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
+{
+       struct iguanair *ir = dev->priv;
+
+       if (carrier < 25000 || carrier > 150000)
+               return -EINVAL;
+
+       mutex_lock(&ir->lock);
+
+       if (carrier != ir->carrier) {
+               uint32_t cycles, fours, sevens;
+
+               ir->carrier = carrier;
+
+               cycles = DIV_ROUND_CLOSEST(24000000, carrier * 2) -
+                                                       ir->cycle_overhead;
+
+               /*  make up the the remainer of 4-cycle blocks */
+               switch (cycles & 3) {
+               case 0:
+                       sevens = 0;
+                       break;
+               case 1:
+                       sevens = 3;
+                       break;
+               case 2:
+                       sevens = 2;
+                       break;
+               case 3:
+                       sevens = 1;
+                       break;
+               }
+
+               fours = (cycles - sevens * 7) / 4;
+
+               /* magic happens here */
+               ir->busy7 = (4 - sevens) * 2;
+               ir->busy4 = 110 - fours;
+       }
+
+       mutex_unlock(&ir->lock);
+
+       return carrier;
+}
+
+static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
+{
+       struct iguanair *ir = dev->priv;
+
+       if (mask > 15)
+               return 4;
+
+       mutex_lock(&ir->lock);
+       ir->channels = mask;
+       mutex_unlock(&ir->lock);
+
+       return 0;
+}
+
+static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
+{
+       struct iguanair *ir = dev->priv;
+       uint8_t space, *payload;
+       unsigned i, size, rc;
+       struct send_packet *packet;
+
+       mutex_lock(&ir->lock);
+
+       /* convert from us to carrier periods */
+       for (i = size = 0; i < count; i++) {
+               txbuf[i] = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
+               size += (txbuf[i] + 126) / 127;
+       }
+
+       packet = kmalloc(sizeof(*packet) + size, GFP_KERNEL);
+       if (!packet) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       if (size > ir->bufsize) {
+               rc = -E2BIG;
+               goto out;
+       }
+
+       packet->header.start = 0;
+       packet->header.direction = DIR_OUT;
+       packet->header.cmd = CMD_SEND;
+       packet->length = size;
+       packet->channels = ir->channels << 4;
+       packet->busy7 = ir->busy7;
+       packet->busy4 = ir->busy4;
+
+       space = 0;
+       payload = packet->payload;
+
+       for (i = 0; i < count; i++) {
+               unsigned periods = txbuf[i];
+
+               while (periods > 127) {
+                       *payload++ = 127 | space;
+                       periods -= 127;
+               }
+
+               *payload++ = periods | space;
+               space ^= 0x80;
+       }
+
+       if (ir->receiver_on) {
+               rc = iguanair_receiver(ir, false);
+               if (rc) {
+                       dev_warn(ir->dev, "disable receiver before transmit failed\n");
+                       goto out;
+               }
+       }
+
+       ir->tx_overflow = false;
+
+       INIT_COMPLETION(ir->completion);
+
+       rc = iguanair_send(ir, packet, size + 8, NULL, NULL);
+
+       if (rc == 0) {
+               wait_for_completion_timeout(&ir->completion, TIMEOUT);
+               if (ir->tx_overflow)
+                       rc = -EOVERFLOW;
+       }
+
+       ir->tx_overflow = false;
+
+       if (ir->receiver_on) {
+               if (iguanair_receiver(ir, true))
+                       dev_warn(ir->dev, "re-enable receiver after transmit failed\n");
+       }
+
+out:
+       mutex_unlock(&ir->lock);
+       kfree(packet);
+
+       return rc;
+}
+
+static int iguanair_open(struct rc_dev *rdev)
+{
+       struct iguanair *ir = rdev->priv;
+       int rc;
+
+       mutex_lock(&ir->lock);
+
+       usb_submit_urb(ir->urb_in, GFP_KERNEL);
+
+       BUG_ON(ir->receiver_on);
+
+       rc = iguanair_receiver(ir, true);
+       if (rc == 0)
+               ir->receiver_on = true;
+
+       mutex_unlock(&ir->lock);
+
+       return rc;
+}
+
+static void iguanair_close(struct rc_dev *rdev)
+{
+       struct iguanair *ir = rdev->priv;
+       int rc;
+
+       mutex_lock(&ir->lock);
+
+       rc = iguanair_receiver(ir, false);
+       ir->receiver_on = false;
+       if (rc)
+               dev_warn(ir->dev, "failed to disable receiver: %d\n", rc);
+
+       usb_kill_urb(ir->urb_in);
+
+       mutex_unlock(&ir->lock);
+}
+
+static int __devinit iguanair_probe(struct usb_interface *intf,
+                                               const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct iguanair *ir;
+       struct rc_dev *rc;
+       int ret;
+       struct usb_host_interface *idesc;
+
+       ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+       rc = rc_allocate_device();
+       if (!ir || !rc) {
+               ret = ENOMEM;
+               goto out;
+       }
+
+       ir->buf_in = usb_alloc_coherent(udev, MAX_PACKET_SIZE, GFP_ATOMIC,
+                                                               &ir->dma_in);
+       ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+
+       if (!ir->buf_in || !ir->urb_in) {
+               ret = ENOMEM;
+               goto out;
+       }
+
+       idesc = intf->altsetting;
+
+       if (idesc->desc.bNumEndpoints < 2) {
+               ret = -ENODEV;
+               goto out;
+       }
+
+       ir->rc = rc;
+       ir->dev = &intf->dev;
+       ir->udev = udev;
+       ir->pipe_in = usb_rcvintpipe(udev,
+                               idesc->endpoint[0].desc.bEndpointAddress);
+       ir->pipe_out = usb_sndintpipe(udev,
+                               idesc->endpoint[1].desc.bEndpointAddress);
+       mutex_init(&ir->lock);
+       init_completion(&ir->completion);
+
+       ret = iguanair_get_features(ir);
+       if (ret) {
+               dev_warn(&intf->dev, "failed to get device features");
+               goto out;
+       }
+
+       usb_fill_int_urb(ir->urb_in, ir->udev, ir->pipe_in, ir->buf_in,
+               MAX_PACKET_SIZE, iguanair_rx, ir,
+               idesc->endpoint[0].desc.bInterval);
+       ir->urb_in->transfer_dma = ir->dma_in;
+       ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+       snprintf(ir->name, sizeof(ir->name),
+               "IguanaWorks USB IR Transceiver version %d.%d",
+               ir->version[0], ir->version[1]);
+
+       usb_make_path(ir->udev, ir->phys, sizeof(ir->phys));
+
+       rc->input_name = ir->name;
+       rc->input_phys = ir->phys;
+       usb_to_input_id(ir->udev, &rc->input_id);
+       rc->dev.parent = &intf->dev;
+       rc->driver_type = RC_DRIVER_IR_RAW;
+       rc->allowed_protos = RC_TYPE_ALL;
+       rc->priv = ir;
+       rc->open = iguanair_open;
+       rc->close = iguanair_close;
+       rc->s_tx_mask = iguanair_set_tx_mask;
+       rc->s_tx_carrier = iguanair_set_tx_carrier;
+       rc->tx_ir = iguanair_tx;
+       rc->driver_name = DRIVER_NAME;
+       rc->map_name = RC_MAP_EMPTY;
+
+       iguanair_set_tx_carrier(rc, 38000);
+
+       ret = rc_register_device(rc);
+       if (ret < 0) {
+               dev_err(&intf->dev, "failed to register rc device %d", ret);
+               goto out;
+       }
+
+       usb_set_intfdata(intf, ir);
+
+       dev_info(&intf->dev, "Registered %s", ir->name);
+
+       return 0;
+out:
+       if (ir) {
+               usb_free_urb(ir->urb_in);
+               usb_free_coherent(udev, MAX_PACKET_SIZE, ir->buf_in,
+                                                               ir->dma_in);
+       }
+       rc_free_device(rc);
+       kfree(ir);
+       return ret;
+}
+
+static void __devexit iguanair_disconnect(struct usb_interface *intf)
+{
+       struct iguanair *ir = usb_get_intfdata(intf);
+
+       usb_set_intfdata(intf, NULL);
+
+       usb_kill_urb(ir->urb_in);
+       usb_free_urb(ir->urb_in);
+       usb_free_coherent(ir->udev, MAX_PACKET_SIZE, ir->buf_in, ir->dma_in);
+       rc_unregister_device(ir->rc);
+       kfree(ir);
+}
+
+static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct iguanair *ir = usb_get_intfdata(intf);
+       int rc = 0;
+
+       mutex_lock(&ir->lock);
+
+       if (ir->receiver_on) {
+               rc = iguanair_receiver(ir, false);
+               if (rc)
+                       dev_warn(ir->dev, "failed to disable receiver for suspend\n");
+       }
+
+       mutex_unlock(&ir->lock);
+
+       return rc;
+}
+
+static int iguanair_resume(struct usb_interface *intf)
+{
+       struct iguanair *ir = usb_get_intfdata(intf);
+       int rc = 0;
+
+       mutex_lock(&ir->lock);
+
+       if (ir->receiver_on) {
+               rc = iguanair_receiver(ir, true);
+               if (rc)
+                       dev_warn(ir->dev, "failed to enable receiver after resume\n");
+       }
+
+       mutex_unlock(&ir->lock);
+
+       return rc;
+}
+
+static const struct usb_device_id iguanair_table[] = {
+       { USB_DEVICE(0x1781, 0x0938) },
+       { }
+};
+
+static struct usb_driver iguanair_driver = {
+       .name = DRIVER_NAME,
+       .probe = iguanair_probe,
+       .disconnect = __devexit_p(iguanair_disconnect),
+       .suspend = iguanair_suspend,
+       .resume = iguanair_resume,
+       .reset_resume = iguanair_resume,
+       .id_table = iguanair_table
+};
+
+module_usb_driver(iguanair_driver);
+
+MODULE_DESCRIPTION("IguanaWorks USB IR Transceiver");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, iguanair_table);
+
index 84e06d3aa696bafe0f3360e337293a27332a6d27..f38d9a8c6880168a4c6256bdb227218c70123ed9 100644 (file)
@@ -199,6 +199,7 @@ static bool debug;
 #define VENDOR_REALTEK         0x0bda
 #define VENDOR_TIVO            0x105a
 #define VENDOR_CONEXANT                0x0572
+#define VENDOR_TWISTEDMELON    0x2596
 
 enum mceusb_model_type {
        MCE_GEN2 = 0,           /* Most boards */
@@ -391,6 +392,12 @@ static struct usb_device_id mceusb_dev_table[] = {
        /* Conexant Hybrid TV RDU253S Polaris */
        { USB_DEVICE(VENDOR_CONEXANT, 0x58a5),
          .driver_info = CX_HYBRID_TV },
+       /* Twisted Melon Inc. - Manta Mini Receiver */
+       { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8008) },
+       /* Twisted Melon Inc. - Manta Pico Receiver */
+       { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8016) },
+       /* Twisted Melon Inc. - Manta Transceiver */
+       { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8042) },
        /* Terminating entry */
        { }
 };
@@ -410,14 +417,12 @@ struct mceusb_dev {
        /* usb */
        struct usb_device *usbdev;
        struct urb *urb_in;
-       struct usb_endpoint_descriptor *usb_ep_in;
        struct usb_endpoint_descriptor *usb_ep_out;
 
        /* buffers and dma */
        unsigned char *buf_in;
        unsigned int len_in;
        dma_addr_t dma_in;
-       dma_addr_t dma_out;
 
        enum {
                CMD_HEADER = 0,
@@ -686,7 +691,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
                dev_info(dev, "Raw IR data, %d pulse/space samples\n", ir->rem);
 }
 
-static void mce_async_callback(struct urb *urb, struct pt_regs *regs)
+static void mce_async_callback(struct urb *urb)
 {
        struct mceusb_dev *ir;
        int len;
@@ -733,7 +738,7 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data,
                pipe = usb_sndintpipe(ir->usbdev,
                                      ir->usb_ep_out->bEndpointAddress);
                usb_fill_int_urb(async_urb, ir->usbdev, pipe,
-                       async_buf, size, (usb_complete_t)mce_async_callback,
+                       async_buf, size, mce_async_callback,
                        ir, ir->usb_ep_out->bInterval);
                memcpy(async_buf, data, size);
 
@@ -1031,7 +1036,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
        ir_raw_event_handle(ir->rc);
 }
 
-static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
+static void mceusb_dev_recv(struct urb *urb)
 {
        struct mceusb_dev *ir;
        int buf_len;
@@ -1331,7 +1336,6 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
        ir->model = model;
 
        /* Saving usb interface data for use by the transmitter routine */
-       ir->usb_ep_in = ep_in;
        ir->usb_ep_out = ep_out;
 
        if (dev->descriptor.iManufacturer
@@ -1349,8 +1353,8 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
                goto rc_dev_fail;
 
        /* wire up inbound data handler */
-       usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
-               maxp, (usb_complete_t) mceusb_dev_recv, ir, ep_in->bInterval);
+       usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp,
+                               mceusb_dev_recv, ir, ep_in->bInterval);
        ir->urb_in->transfer_dma = ir->dma_in;
        ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 
index dc8a7dddccd458edb615e60b0c8eb0a242167779..699eef39128bf3eacf91a08d1a1026a1dfac1653 100644 (file)
@@ -25,6 +25,8 @@
  * USA
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/pnp.h>
@@ -123,43 +125,40 @@ static u8 nvt_cir_wake_reg_read(struct nvt_dev *nvt, u8 offset)
        return val;
 }
 
-#define pr_reg(text, ...) \
-       printk(KERN_INFO KBUILD_MODNAME ": " text, ## __VA_ARGS__)
-
 /* dump current cir register contents */
 static void cir_dump_regs(struct nvt_dev *nvt)
 {
        nvt_efm_enable(nvt);
        nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
 
-       pr_reg("%s: Dump CIR logical device registers:\n", NVT_DRIVER_NAME);
-       pr_reg(" * CR CIR ACTIVE :   0x%x\n",
-              nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
-       pr_reg(" * CR CIR BASE ADDR: 0x%x\n",
-              (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
+       pr_info("%s: Dump CIR logical device registers:\n", NVT_DRIVER_NAME);
+       pr_info(" * CR CIR ACTIVE :   0x%x\n",
+               nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
+       pr_info(" * CR CIR BASE ADDR: 0x%x\n",
+               (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
                nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO));
-       pr_reg(" * CR CIR IRQ NUM:   0x%x\n",
-              nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
+       pr_info(" * CR CIR IRQ NUM:   0x%x\n",
+               nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
 
        nvt_efm_disable(nvt);
 
-       pr_reg("%s: Dump CIR registers:\n", NVT_DRIVER_NAME);
-       pr_reg(" * IRCON:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRCON));
-       pr_reg(" * IRSTS:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRSTS));
-       pr_reg(" * IREN:      0x%x\n", nvt_cir_reg_read(nvt, CIR_IREN));
-       pr_reg(" * RXFCONT:   0x%x\n", nvt_cir_reg_read(nvt, CIR_RXFCONT));
-       pr_reg(" * CP:        0x%x\n", nvt_cir_reg_read(nvt, CIR_CP));
-       pr_reg(" * CC:        0x%x\n", nvt_cir_reg_read(nvt, CIR_CC));
-       pr_reg(" * SLCH:      0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCH));
-       pr_reg(" * SLCL:      0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCL));
-       pr_reg(" * FIFOCON:   0x%x\n", nvt_cir_reg_read(nvt, CIR_FIFOCON));
-       pr_reg(" * IRFIFOSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFIFOSTS));
-       pr_reg(" * SRXFIFO:   0x%x\n", nvt_cir_reg_read(nvt, CIR_SRXFIFO));
-       pr_reg(" * TXFCONT:   0x%x\n", nvt_cir_reg_read(nvt, CIR_TXFCONT));
-       pr_reg(" * STXFIFO:   0x%x\n", nvt_cir_reg_read(nvt, CIR_STXFIFO));
-       pr_reg(" * FCCH:      0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCH));
-       pr_reg(" * FCCL:      0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCL));
-       pr_reg(" * IRFSM:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFSM));
+       pr_info("%s: Dump CIR registers:\n", NVT_DRIVER_NAME);
+       pr_info(" * IRCON:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRCON));
+       pr_info(" * IRSTS:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRSTS));
+       pr_info(" * IREN:      0x%x\n", nvt_cir_reg_read(nvt, CIR_IREN));
+       pr_info(" * RXFCONT:   0x%x\n", nvt_cir_reg_read(nvt, CIR_RXFCONT));
+       pr_info(" * CP:        0x%x\n", nvt_cir_reg_read(nvt, CIR_CP));
+       pr_info(" * CC:        0x%x\n", nvt_cir_reg_read(nvt, CIR_CC));
+       pr_info(" * SLCH:      0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCH));
+       pr_info(" * SLCL:      0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCL));
+       pr_info(" * FIFOCON:   0x%x\n", nvt_cir_reg_read(nvt, CIR_FIFOCON));
+       pr_info(" * IRFIFOSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFIFOSTS));
+       pr_info(" * SRXFIFO:   0x%x\n", nvt_cir_reg_read(nvt, CIR_SRXFIFO));
+       pr_info(" * TXFCONT:   0x%x\n", nvt_cir_reg_read(nvt, CIR_TXFCONT));
+       pr_info(" * STXFIFO:   0x%x\n", nvt_cir_reg_read(nvt, CIR_STXFIFO));
+       pr_info(" * FCCH:      0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCH));
+       pr_info(" * FCCL:      0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCL));
+       pr_info(" * IRFSM:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFSM));
 }
 
 /* dump current cir wake register contents */
@@ -170,59 +169,59 @@ static void cir_wake_dump_regs(struct nvt_dev *nvt)
        nvt_efm_enable(nvt);
        nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
 
-       pr_reg("%s: Dump CIR WAKE logical device registers:\n",
-              NVT_DRIVER_NAME);
-       pr_reg(" * CR CIR WAKE ACTIVE :   0x%x\n",
-              nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
-       pr_reg(" * CR CIR WAKE BASE ADDR: 0x%x\n",
-              (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
+       pr_info("%s: Dump CIR WAKE logical device registers:\n",
+               NVT_DRIVER_NAME);
+       pr_info(" * CR CIR WAKE ACTIVE :   0x%x\n",
+               nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
+       pr_info(" * CR CIR WAKE BASE ADDR: 0x%x\n",
+               (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
                nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO));
-       pr_reg(" * CR CIR WAKE IRQ NUM:   0x%x\n",
-              nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
+       pr_info(" * CR CIR WAKE IRQ NUM:   0x%x\n",
+               nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
 
        nvt_efm_disable(nvt);
 
-       pr_reg("%s: Dump CIR WAKE registers\n", NVT_DRIVER_NAME);
-       pr_reg(" * IRCON:          0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON));
-       pr_reg(" * IRSTS:          0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS));
-       pr_reg(" * IREN:           0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN));
-       pr_reg(" * FIFO CMP DEEP:  0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_DEEP));
-       pr_reg(" * FIFO CMP TOL:   0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_TOL));
-       pr_reg(" * FIFO COUNT:     0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT));
-       pr_reg(" * SLCH:           0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCH));
-       pr_reg(" * SLCL:           0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCL));
-       pr_reg(" * FIFOCON:        0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON));
-       pr_reg(" * SRXFSTS:        0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_SRXFSTS));
-       pr_reg(" * SAMPLE RX FIFO: 0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_SAMPLE_RX_FIFO));
-       pr_reg(" * WR FIFO DATA:   0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_WR_FIFO_DATA));
-       pr_reg(" * RD FIFO ONLY:   0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
-       pr_reg(" * RD FIFO ONLY IDX: 0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX));
-       pr_reg(" * FIFO IGNORE:    0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_IGNORE));
-       pr_reg(" * IRFSM:          0x%x\n",
-              nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRFSM));
+       pr_info("%s: Dump CIR WAKE registers\n", NVT_DRIVER_NAME);
+       pr_info(" * IRCON:          0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON));
+       pr_info(" * IRSTS:          0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS));
+       pr_info(" * IREN:           0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN));
+       pr_info(" * FIFO CMP DEEP:  0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_DEEP));
+       pr_info(" * FIFO CMP TOL:   0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_TOL));
+       pr_info(" * FIFO COUNT:     0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT));
+       pr_info(" * SLCH:           0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCH));
+       pr_info(" * SLCL:           0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCL));
+       pr_info(" * FIFOCON:        0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON));
+       pr_info(" * SRXFSTS:        0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_SRXFSTS));
+       pr_info(" * SAMPLE RX FIFO: 0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_SAMPLE_RX_FIFO));
+       pr_info(" * WR FIFO DATA:   0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_WR_FIFO_DATA));
+       pr_info(" * RD FIFO ONLY:   0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
+       pr_info(" * RD FIFO ONLY IDX: 0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX));
+       pr_info(" * FIFO IGNORE:    0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_IGNORE));
+       pr_info(" * IRFSM:          0x%x\n",
+               nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRFSM));
 
        fifo_len = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT);
-       pr_reg("%s: Dump CIR WAKE FIFO (len %d)\n", NVT_DRIVER_NAME, fifo_len);
-       pr_reg("* Contents = ");
+       pr_info("%s: Dump CIR WAKE FIFO (len %d)\n", NVT_DRIVER_NAME, fifo_len);
+       pr_info("* Contents =");
        for (i = 0; i < fifo_len; i++)
-               printk(KERN_CONT "%02x ",
-                      nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
-       printk(KERN_CONT "\n");
+               pr_cont(" %02x",
+                       nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
+       pr_cont("\n");
 }
 
 /* detect hardware features */
index 6e16b09c24a99e1883bf0f4ad987e552bf98aa4e..cabc19c105152937dab25fd0b04052f7d053a63f 100644 (file)
@@ -775,10 +775,11 @@ static ssize_t show_protocols(struct device *device,
        if (dev->driver_type == RC_DRIVER_SCANCODE) {
                enabled = dev->rc_map.rc_type;
                allowed = dev->allowed_protos;
-       } else {
+       } else if (dev->raw) {
                enabled = dev->raw->enabled_protocols;
                allowed = ir_raw_get_allowed_protocols();
-       }
+       } else
+               return -ENODEV;
 
        IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n",
                   (long long)allowed,
index 99937c94d7dfdd34ef30d9cb28cf23d3df17a3f9..c128fac0ce2cf4b4c0f2d7484fc21b024b3cb5ef 100644 (file)
@@ -5,7 +5,7 @@
 config VIDEO_V4L2
        tristate
        depends on VIDEO_DEV && VIDEO_V4L2_COMMON
-       default VIDEO_DEV && VIDEO_V4L2_COMMON
+       default y
 
 config VIDEOBUF_GEN
        tristate
@@ -73,6 +73,7 @@ config VIDEOBUF2_DMA_SG
 menuconfig VIDEO_CAPTURE_DRIVERS
        bool "Video capture adapters"
        depends on VIDEO_V4L2
+       depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT
        default y
        ---help---
          Say Y here to enable selecting the video adapters for
@@ -461,6 +462,15 @@ config VIDEO_ADV7343
          To compile this driver as a module, choose M here: the
          module will be called adv7343.
 
+config VIDEO_ADV7393
+       tristate "ADV7393 video encoder"
+       depends on I2C
+       help
+         Support for Analog Devices I2C bus based ADV7393 encoder.
+
+         To compile this driver as a module, choose M here: the
+         module will be called adv7393.
+
 config VIDEO_AK881X
        tristate "AK8813/AK8814 video encoders"
        depends on I2C
@@ -478,6 +488,7 @@ config VIDEO_SMIAPP_PLL
 config VIDEO_OV7670
        tristate "OmniVision OV7670 sensor support"
        depends on I2C && VIDEO_V4L2
+       depends on MEDIA_CAMERA_SUPPORT
        ---help---
          This is a Video4Linux2 sensor-level driver for the OmniVision
          OV7670 VGA camera.  It currently only works with the M88ALP01
@@ -486,6 +497,7 @@ config VIDEO_OV7670
 config VIDEO_VS6624
        tristate "ST VS6624 sensor support"
        depends on VIDEO_V4L2 && I2C
+       depends on MEDIA_CAMERA_SUPPORT
        ---help---
          This is a Video4Linux2 sensor-level driver for the ST VS6624
          camera.
@@ -496,6 +508,7 @@ config VIDEO_VS6624
 config VIDEO_MT9M032
        tristate "MT9M032 camera sensor support"
        depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+       depends on MEDIA_CAMERA_SUPPORT
        select VIDEO_APTINA_PLL
        ---help---
          This driver supports MT9M032 camera sensors from Aptina, monochrome
@@ -504,6 +517,7 @@ config VIDEO_MT9M032
 config VIDEO_MT9P031
        tristate "Aptina MT9P031 support"
        depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+       depends on MEDIA_CAMERA_SUPPORT
        select VIDEO_APTINA_PLL
        ---help---
          This is a Video4Linux2 sensor-level driver for the Aptina
@@ -512,6 +526,7 @@ config VIDEO_MT9P031
 config VIDEO_MT9T001
        tristate "Aptina MT9T001 support"
        depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+       depends on MEDIA_CAMERA_SUPPORT
        ---help---
          This is a Video4Linux2 sensor-level driver for the Aptina
          (Micron) mt0t001 3 Mpixel camera.
@@ -519,6 +534,7 @@ config VIDEO_MT9T001
 config VIDEO_MT9V011
        tristate "Micron mt9v011 sensor support"
        depends on I2C && VIDEO_V4L2
+       depends on MEDIA_CAMERA_SUPPORT
        ---help---
          This is a Video4Linux2 sensor-level driver for the Micron
          mt0v011 1.3 Mpixel camera.  It currently only works with the
@@ -527,6 +543,7 @@ config VIDEO_MT9V011
 config VIDEO_MT9V032
        tristate "Micron MT9V032 sensor support"
        depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+       depends on MEDIA_CAMERA_SUPPORT
        ---help---
          This is a Video4Linux2 sensor-level driver for the Micron
          MT9V032 752x480 CMOS sensor.
@@ -534,6 +551,7 @@ config VIDEO_MT9V032
 config VIDEO_TCM825X
        tristate "TCM825x camera sensor support"
        depends on I2C && VIDEO_V4L2
+       depends on MEDIA_CAMERA_SUPPORT
        ---help---
          This is a driver for the Toshiba TCM825x VGA camera sensor.
          It is used for example in Nokia N800.
@@ -541,12 +559,14 @@ config VIDEO_TCM825X
 config VIDEO_SR030PC30
        tristate "Siliconfile SR030PC30 sensor support"
        depends on I2C && VIDEO_V4L2
+       depends on MEDIA_CAMERA_SUPPORT
        ---help---
          This driver supports SR030PC30 VGA camera from Siliconfile
 
 config VIDEO_NOON010PC30
        tristate "Siliconfile NOON010PC30 sensor support"
        depends on I2C && VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_SUBDEV_API
+       depends on MEDIA_CAMERA_SUPPORT
        ---help---
          This driver supports NOON010PC30 CIF camera from Siliconfile
 
@@ -554,6 +574,7 @@ source "drivers/media/video/m5mols/Kconfig"
 
 config VIDEO_S5K6AA
        tristate "Samsung S5K6AAFX sensor support"
+       depends on MEDIA_CAMERA_SUPPORT
        depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
        ---help---
          This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
@@ -566,6 +587,7 @@ comment "Flash devices"
 config VIDEO_ADP1653
        tristate "ADP1653 flash support"
        depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+       depends on MEDIA_CAMERA_SUPPORT
        ---help---
          This is a driver for the ADP1653 flash controller. It is used for
          example in Nokia N900.
@@ -573,6 +595,7 @@ config VIDEO_ADP1653
 config VIDEO_AS3645A
        tristate "AS3645A flash driver support"
        depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+       depends on MEDIA_CAMERA_SUPPORT
        ---help---
          This is a driver for the AS3645A and LM3555 flash controllers. It has
          build in control for flash, torch and indicator LEDs.
@@ -647,30 +670,14 @@ menuconfig V4L_USB_DRIVERS
        depends on USB
        default y
 
-if V4L_USB_DRIVERS
+if V4L_USB_DRIVERS && MEDIA_CAMERA_SUPPORT
 
-source "drivers/media/video/au0828/Kconfig"
+       comment "Webcam devices"
 
 source "drivers/media/video/uvc/Kconfig"
 
 source "drivers/media/video/gspca/Kconfig"
 
-source "drivers/media/video/pvrusb2/Kconfig"
-
-source "drivers/media/video/hdpvr/Kconfig"
-
-source "drivers/media/video/em28xx/Kconfig"
-
-source "drivers/media/video/tlg2300/Kconfig"
-
-source "drivers/media/video/cx231xx/Kconfig"
-
-source "drivers/media/video/tm6000/Kconfig"
-
-source "drivers/media/video/usbvision/Kconfig"
-
-source "drivers/media/video/sn9c102/Kconfig"
-
 source "drivers/media/video/pwc/Kconfig"
 
 source "drivers/media/video/cpia2/Kconfig"
@@ -711,15 +718,46 @@ config USB_S2255
          Say Y here if you want support for the Sensoray 2255 USB device.
          This driver can be compiled as a module, called s2255drv.
 
+source "drivers/media/video/sn9c102/Kconfig"
+
+endif # V4L_USB_DRIVERS && MEDIA_CAMERA_SUPPORT
+
+if V4L_USB_DRIVERS
+
+       comment "Webcam and/or TV USB devices"
+
+source "drivers/media/video/em28xx/Kconfig"
+
+endif
+
+if V4L_USB_DRIVERS && MEDIA_ANALOG_TV_SUPPORT
+
+       comment "TV USB devices"
+
+source "drivers/media/video/au0828/Kconfig"
+
+source "drivers/media/video/pvrusb2/Kconfig"
+
+source "drivers/media/video/hdpvr/Kconfig"
+
+source "drivers/media/video/tlg2300/Kconfig"
+
+source "drivers/media/video/cx231xx/Kconfig"
+
+source "drivers/media/video/tm6000/Kconfig"
+
+source "drivers/media/video/usbvision/Kconfig"
+
 endif # V4L_USB_DRIVERS
 
 #
-# PCI drivers configuration
+# PCI drivers configuration - No devices here are for webcams
 #
 
 menuconfig V4L_PCI_DRIVERS
        bool "V4L PCI(e) devices"
        depends on PCI
+       depends on MEDIA_ANALOG_TV_SUPPORT
        default y
        ---help---
          Say Y here to enable support for these PCI(e) drivers.
@@ -814,11 +852,13 @@ endif # V4L_PCI_DRIVERS
 
 #
 # ISA & parallel port drivers configuration
+#      All devices here are webcam or grabber devices
 #
 
 menuconfig V4L_ISA_PARPORT_DRIVERS
        bool "V4L ISA and parallel port devices"
        depends on ISA || PARPORT
+       depends on MEDIA_CAMERA_SUPPORT
        default n
        ---help---
          Say Y here to enable support for these ISA and parallel port drivers.
@@ -871,8 +911,13 @@ config VIDEO_W9966
 
 endif # V4L_ISA_PARPORT_DRIVERS
 
+#
+# Platform drivers
+#      All drivers here are currently for webcam support
+
 menuconfig V4L_PLATFORM_DRIVERS
        bool "V4L platform devices"
+       depends on MEDIA_CAMERA_SUPPORT
        default n
        ---help---
          Say Y here to enable support for platform-specific V4L drivers.
index d209de0e0ca8821dd566844e288d6077fc75f6bf..b7da9faa3b0ac96030983dbf1c057d2c7f88ac8e 100644 (file)
@@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
 obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
 obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
 obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
+obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o
 obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
 obj-$(CONFIG_VIDEO_VS6624)  += vs6624.o
 obj-$(CONFIG_VIDEO_BT819) += bt819.o
index 174bffacf1173fd41136adc99147de807a2cb8b3..45ecf8db1eaecadbc6debf310d0d5313dd2bc185 100644 (file)
 #include <media/v4l2-ioctl.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.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_AD_PAL_BG_NTSC_J_SECAM   0x00
 #define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10
 
 #define ADV7180_AUTODETECT_ENABLE_REG                  0x07
 #define ADV7180_AUTODETECT_DEFAULT                     0x7f
-
+/* Contrast */
 #define ADV7180_CON_REG                0x08    /*Unsigned */
-#define CON_REG_MIN            0
-#define CON_REG_DEF            128
-#define CON_REG_MAX            255
-
+#define ADV7180_CON_MIN                0
+#define ADV7180_CON_DEF                128
+#define ADV7180_CON_MAX                255
+/* Brightness*/
 #define ADV7180_BRI_REG                0x0a    /*Signed */
-#define BRI_REG_MIN            -128
-#define BRI_REG_DEF            0
-#define BRI_REG_MAX            127
-
+#define ADV7180_BRI_MIN                -128
+#define ADV7180_BRI_DEF                0
+#define ADV7180_BRI_MAX                127
+/* Hue */
 #define ADV7180_HUE_REG                0x0b    /*Signed, inverted */
-#define HUE_REG_MIN            -127
-#define HUE_REG_DEF            0
-#define HUE_REG_MAX            128
+#define ADV7180_HUE_MIN                -127
+#define ADV7180_HUE_DEF                0
+#define ADV7180_HUE_MAX                128
 
 #define ADV7180_ADI_CTRL_REG                           0x0e
 #define ADV7180_ADI_CTRL_IRQ_SPACE                     0x20
 #define ADV7180_ICONF1_ACTIVE_LOW      0x01
 #define ADV7180_ICONF1_PSYNC_ONLY      0x10
 #define ADV7180_ICONF1_ACTIVE_TO_CLR   0xC0
-
+/* Saturation */
 #define ADV7180_SD_SAT_CB_REG  0xe3    /*Unsigned */
 #define ADV7180_SD_SAT_CR_REG  0xe4    /*Unsigned */
-#define SAT_REG_MIN            0
-#define SAT_REG_DEF            128
-#define SAT_REG_MAX            255
+#define ADV7180_SAT_MIN                0
+#define ADV7180_SAT_DEF                128
+#define ADV7180_SAT_MAX                255
 
 #define ADV7180_IRQ1_LOCK      0x01
 #define ADV7180_IRQ1_UNLOCK    0x02
 #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND    0x4F
 
 struct adv7180_state {
+       struct v4l2_ctrl_handler ctrl_hdl;
        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;
-       s8                      brightness;
-       s16                     hue;
-       u8                      contrast;
-       u8                      saturation;
        u8                      input;
 };
+#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler,            \
+                                           struct adv7180_state,       \
+                                           ctrl_hdl)->sd)
 
 static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
 {
@@ -237,7 +236,7 @@ static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
        if (ret)
                return ret;
 
-       /*We cannot discriminate between LQFP and 40-pin LFCSP, so accept
+       /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
         * all inputs and let the card driver take care of validation
         */
        if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input)
@@ -316,117 +315,39 @@ out:
        return ret;
 }
 
-static int adv7180_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
-       switch (qc->id) {
-       case V4L2_CID_BRIGHTNESS:
-               return v4l2_ctrl_query_fill(qc, BRI_REG_MIN, BRI_REG_MAX,
-                                           1, BRI_REG_DEF);
-       case V4L2_CID_HUE:
-               return v4l2_ctrl_query_fill(qc, HUE_REG_MIN, HUE_REG_MAX,
-                                           1, HUE_REG_DEF);
-       case V4L2_CID_CONTRAST:
-               return v4l2_ctrl_query_fill(qc, CON_REG_MIN, CON_REG_MAX,
-                                           1, CON_REG_DEF);
-       case V4L2_CID_SATURATION:
-               return v4l2_ctrl_query_fill(qc, SAT_REG_MIN, SAT_REG_MAX,
-                                           1, SAT_REG_DEF);
-       default:
-               break;
-       }
-
-       return -EINVAL;
-}
-
-static int adv7180_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       struct adv7180_state *state = to_state(sd);
-       int ret = mutex_lock_interruptible(&state->mutex);
-       if (ret)
-               return ret;
-
-       switch (ctrl->id) {
-       case V4L2_CID_BRIGHTNESS:
-               ctrl->value = state->brightness;
-               break;
-       case V4L2_CID_HUE:
-               ctrl->value = state->hue;
-               break;
-       case V4L2_CID_CONTRAST:
-               ctrl->value = state->contrast;
-               break;
-       case V4L2_CID_SATURATION:
-               ctrl->value = state->saturation;
-               break;
-       default:
-               ret = -EINVAL;
-       }
-
-       mutex_unlock(&state->mutex);
-       return ret;
-}
-
-static int adv7180_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+       struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
        struct adv7180_state *state = to_state(sd);
        struct i2c_client *client = v4l2_get_subdevdata(sd);
        int ret = mutex_lock_interruptible(&state->mutex);
+       int val;
+
        if (ret)
                return ret;
-
+       val = ctrl->val;
        switch (ctrl->id) {
        case V4L2_CID_BRIGHTNESS:
-               if ((ctrl->value > BRI_REG_MAX)
-                   || (ctrl->value < BRI_REG_MIN)) {
-                       ret = -ERANGE;
-                       break;
-               }
-               state->brightness = ctrl->value;
-               ret = i2c_smbus_write_byte_data(client,
-                                               ADV7180_BRI_REG,
-                                               state->brightness);
+               ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val);
                break;
        case V4L2_CID_HUE:
-               if ((ctrl->value > HUE_REG_MAX)
-                   || (ctrl->value < HUE_REG_MIN)) {
-                       ret = -ERANGE;
-                       break;
-               }
-               state->hue = ctrl->value;
                /*Hue is inverted according to HSL chart */
-               ret = i2c_smbus_write_byte_data(client,
-                                               ADV7180_HUE_REG, -state->hue);
+               ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val);
                break;
        case V4L2_CID_CONTRAST:
-               if ((ctrl->value > CON_REG_MAX)
-                   || (ctrl->value < CON_REG_MIN)) {
-                       ret = -ERANGE;
-                       break;
-               }
-               state->contrast = ctrl->value;
-               ret = i2c_smbus_write_byte_data(client,
-                                               ADV7180_CON_REG,
-                                               state->contrast);
+               ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val);
                break;
        case V4L2_CID_SATURATION:
-               if ((ctrl->value > SAT_REG_MAX)
-                   || (ctrl->value < SAT_REG_MIN)) {
-                       ret = -ERANGE;
-                       break;
-               }
                /*
                 *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
                 *Let's not confuse the user, everybody understands saturation
                 */
-               state->saturation = ctrl->value;
-               ret = i2c_smbus_write_byte_data(client,
-                                               ADV7180_SD_SAT_CB_REG,
-                                               state->saturation);
+               ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG,
+                                               val);
                if (ret < 0)
                        break;
-               ret = i2c_smbus_write_byte_data(client,
-                                               ADV7180_SD_SAT_CR_REG,
-                                               state->saturation);
+               ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG,
+                                               val);
                break;
        default:
                ret = -EINVAL;
@@ -436,6 +357,42 @@ static int adv7180_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
        return ret;
 }
 
+static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
+       .s_ctrl = adv7180_s_ctrl,
+};
+
+static int adv7180_init_controls(struct adv7180_state *state)
+{
+       v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
+
+       v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
+                         V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN,
+                         ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF);
+       v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
+                         V4L2_CID_CONTRAST, ADV7180_CON_MIN,
+                         ADV7180_CON_MAX, 1, ADV7180_CON_DEF);
+       v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
+                         V4L2_CID_SATURATION, ADV7180_SAT_MIN,
+                         ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF);
+       v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
+                         V4L2_CID_HUE, ADV7180_HUE_MIN,
+                         ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
+       state->sd.ctrl_handler = &state->ctrl_hdl;
+       if (state->ctrl_hdl.error) {
+               int err = state->ctrl_hdl.error;
+
+               v4l2_ctrl_handler_free(&state->ctrl_hdl);
+               return err;
+       }
+       v4l2_ctrl_handler_setup(&state->ctrl_hdl);
+
+       return 0;
+}
+static void adv7180_exit_controls(struct adv7180_state *state)
+{
+       v4l2_ctrl_handler_free(&state->ctrl_hdl);
+}
+
 static const struct v4l2_subdev_video_ops adv7180_video_ops = {
        .querystd = adv7180_querystd,
        .g_input_status = adv7180_g_input_status,
@@ -445,9 +402,9 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = {
 static const struct v4l2_subdev_core_ops adv7180_core_ops = {
        .g_chip_ident = adv7180_g_chip_ident,
        .s_std = adv7180_s_std,
-       .queryctrl = adv7180_queryctrl,
-       .g_ctrl = adv7180_g_ctrl,
-       .s_ctrl = adv7180_s_ctrl,
+       .queryctrl = v4l2_subdev_queryctrl,
+       .g_ctrl = v4l2_subdev_g_ctrl,
+       .s_ctrl = v4l2_subdev_s_ctrl,
 };
 
 static const struct v4l2_subdev_ops adv7180_ops = {
@@ -539,7 +496,7 @@ static int init_device(struct i2c_client *client, struct adv7180_state *state)
 
        /* register for interrupts */
        if (state->irq > 0) {
-               ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME,
+               ret = request_irq(state->irq, adv7180_irq, 0, KBUILD_MODNAME,
                                  state);
                if (ret)
                        return ret;
@@ -580,31 +537,6 @@ static int init_device(struct i2c_client *client, struct adv7180_state *state)
                        return ret;
        }
 
-       /*Set default value for controls */
-       ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG,
-                                       state->brightness);
-       if (ret < 0)
-               return ret;
-
-       ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, state->hue);
-       if (ret < 0)
-               return ret;
-
-       ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG,
-                                       state->contrast);
-       if (ret < 0)
-               return ret;
-
-       ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG,
-                                       state->saturation);
-       if (ret < 0)
-               return ret;
-
-       ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG,
-                                       state->saturation);
-       if (ret < 0)
-               return ret;
-
        return 0;
 }
 
@@ -632,25 +564,26 @@ static __devinit int adv7180_probe(struct i2c_client *client,
        INIT_WORK(&state->work, adv7180_work);
        mutex_init(&state->mutex);
        state->autodetect = true;
-       state->brightness = BRI_REG_DEF;
-       state->hue = HUE_REG_DEF;
-       state->contrast = CON_REG_DEF;
-       state->saturation = SAT_REG_DEF;
        state->input = 0;
        sd = &state->sd;
        v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
 
-       ret = init_device(client, state);
-       if (0 != ret)
+       ret = adv7180_init_controls(state);
+       if (ret)
                goto err_unreg_subdev;
+       ret = init_device(client, state);
+       if (ret)
+               goto err_free_ctrl;
        return 0;
 
+err_free_ctrl:
+       adv7180_exit_controls(state);
 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);
+       printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret);
        return ret;
 }
 
@@ -678,7 +611,7 @@ static __devexit int adv7180_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id adv7180_id[] = {
-       {DRIVER_NAME, 0},
+       {KBUILD_MODNAME, 0},
        {},
 };
 
@@ -716,7 +649,7 @@ MODULE_DEVICE_TABLE(i2c, adv7180_id);
 static struct i2c_driver adv7180_driver = {
        .driver = {
                   .owner = THIS_MODULE,
-                  .name = DRIVER_NAME,
+                  .name = KBUILD_MODNAME,
                   },
        .probe = adv7180_probe,
        .remove = __devexit_p(adv7180_remove),
diff --git a/drivers/media/video/adv7393.c b/drivers/media/video/adv7393.c
new file mode 100644 (file)
index 0000000..3dc6098
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ * adv7393 - ADV7393 Video Encoder Driver
+ *
+ * The encoder hardware does not support SECAM.
+ *
+ * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/
+ * Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
+ *
+ * Based on ADV7343 driver,
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/videodev2.h>
+#include <linux/uaccess.h>
+
+#include <media/adv7393.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+#include "adv7393_regs.h"
+
+MODULE_DESCRIPTION("ADV7393 video encoder driver");
+MODULE_LICENSE("GPL");
+
+static bool debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+
+struct adv7393_state {
+       struct v4l2_subdev sd;
+       struct v4l2_ctrl_handler hdl;
+       u8 reg00;
+       u8 reg01;
+       u8 reg02;
+       u8 reg35;
+       u8 reg80;
+       u8 reg82;
+       u32 output;
+       v4l2_std_id std;
+};
+
+static inline struct adv7393_state *to_state(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct adv7393_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+       return &container_of(ctrl->handler, struct adv7393_state, hdl)->sd;
+}
+
+static inline int adv7393_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+       return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static const u8 adv7393_init_reg_val[] = {
+       ADV7393_SOFT_RESET, ADV7393_SOFT_RESET_DEFAULT,
+       ADV7393_POWER_MODE_REG, ADV7393_POWER_MODE_REG_DEFAULT,
+
+       ADV7393_HD_MODE_REG1, ADV7393_HD_MODE_REG1_DEFAULT,
+       ADV7393_HD_MODE_REG2, ADV7393_HD_MODE_REG2_DEFAULT,
+       ADV7393_HD_MODE_REG3, ADV7393_HD_MODE_REG3_DEFAULT,
+       ADV7393_HD_MODE_REG4, ADV7393_HD_MODE_REG4_DEFAULT,
+       ADV7393_HD_MODE_REG5, ADV7393_HD_MODE_REG5_DEFAULT,
+       ADV7393_HD_MODE_REG6, ADV7393_HD_MODE_REG6_DEFAULT,
+       ADV7393_HD_MODE_REG7, ADV7393_HD_MODE_REG7_DEFAULT,
+
+       ADV7393_SD_MODE_REG1, ADV7393_SD_MODE_REG1_DEFAULT,
+       ADV7393_SD_MODE_REG2, ADV7393_SD_MODE_REG2_DEFAULT,
+       ADV7393_SD_MODE_REG3, ADV7393_SD_MODE_REG3_DEFAULT,
+       ADV7393_SD_MODE_REG4, ADV7393_SD_MODE_REG4_DEFAULT,
+       ADV7393_SD_MODE_REG5, ADV7393_SD_MODE_REG5_DEFAULT,
+       ADV7393_SD_MODE_REG6, ADV7393_SD_MODE_REG6_DEFAULT,
+       ADV7393_SD_MODE_REG7, ADV7393_SD_MODE_REG7_DEFAULT,
+       ADV7393_SD_MODE_REG8, ADV7393_SD_MODE_REG8_DEFAULT,
+
+       ADV7393_SD_TIMING_REG0, ADV7393_SD_TIMING_REG0_DEFAULT,
+
+       ADV7393_SD_HUE_ADJUST, ADV7393_SD_HUE_ADJUST_DEFAULT,
+       ADV7393_SD_CGMS_WSS0, ADV7393_SD_CGMS_WSS0_DEFAULT,
+       ADV7393_SD_BRIGHTNESS_WSS, ADV7393_SD_BRIGHTNESS_WSS_DEFAULT,
+};
+
+/*
+ *                         2^32
+ * FSC(reg) =  FSC (HZ) * --------
+ *                       27000000
+ */
+static const struct adv7393_std_info stdinfo[] = {
+       {
+               /* FSC(Hz) = 4,433,618.75 Hz */
+               SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443,
+       }, {
+               /* FSC(Hz) = 3,579,545.45 Hz */
+               SD_STD_NTSC, 569408542, V4L2_STD_NTSC,
+       }, {
+               /* FSC(Hz) = 3,575,611.00 Hz */
+               SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M,
+       }, {
+               /* FSC(Hz) = 3,582,056.00 Hz */
+               SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc,
+       }, {
+               /* FSC(Hz) = 4,433,618.75 Hz */
+               SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N,
+       }, {
+               /* FSC(Hz) = 4,433,618.75 Hz */
+               SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60,
+       }, {
+               /* FSC(Hz) = 4,433,618.75 Hz */
+               SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL,
+       },
+};
+
+static int adv7393_setstd(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+       struct adv7393_state *state = to_state(sd);
+       const struct adv7393_std_info *std_info;
+       int num_std;
+       u8 reg;
+       u32 val;
+       int err = 0;
+       int i;
+
+       num_std = ARRAY_SIZE(stdinfo);
+
+       for (i = 0; i < num_std; i++) {
+               if (stdinfo[i].stdid & std)
+                       break;
+       }
+
+       if (i == num_std) {
+               v4l2_dbg(1, debug, sd,
+                               "Invalid std or std is not supported: %llx\n",
+                                               (unsigned long long)std);
+               return -EINVAL;
+       }
+
+       std_info = &stdinfo[i];
+
+       /* Set the standard */
+       val = state->reg80 & ~SD_STD_MASK;
+       val |= std_info->standard_val3;
+       err = adv7393_write(sd, ADV7393_SD_MODE_REG1, val);
+       if (err < 0)
+               goto setstd_exit;
+
+       state->reg80 = val;
+
+       /* Configure the input mode register */
+       val = state->reg01 & ~INPUT_MODE_MASK;
+       val |= SD_INPUT_MODE;
+       err = adv7393_write(sd, ADV7393_MODE_SELECT_REG, val);
+       if (err < 0)
+               goto setstd_exit;
+
+       state->reg01 = val;
+
+       /* Program the sub carrier frequency registers */
+       val = std_info->fsc_val;
+       for (reg = ADV7393_FSC_REG0; reg <= ADV7393_FSC_REG3; reg++) {
+               err = adv7393_write(sd, reg, val);
+               if (err < 0)
+                       goto setstd_exit;
+               val >>= 8;
+       }
+
+       val = state->reg82;
+
+       /* Pedestal settings */
+       if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443))
+               val |= SD_PEDESTAL_EN;
+       else
+               val &= SD_PEDESTAL_DI;
+
+       err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val);
+       if (err < 0)
+               goto setstd_exit;
+
+       state->reg82 = val;
+
+setstd_exit:
+       if (err != 0)
+               v4l2_err(sd, "Error setting std, write failed\n");
+
+       return err;
+}
+
+static int adv7393_setoutput(struct v4l2_subdev *sd, u32 output_type)
+{
+       struct adv7393_state *state = to_state(sd);
+       u8 val;
+       int err = 0;
+
+       if (output_type > ADV7393_SVIDEO_ID) {
+               v4l2_dbg(1, debug, sd,
+                       "Invalid output type or output type not supported:%d\n",
+                                                               output_type);
+               return -EINVAL;
+       }
+
+       /* Enable Appropriate DAC */
+       val = state->reg00 & 0x03;
+
+       if (output_type == ADV7393_COMPOSITE_ID)
+               val |= ADV7393_COMPOSITE_POWER_VALUE;
+       else if (output_type == ADV7393_COMPONENT_ID)
+               val |= ADV7393_COMPONENT_POWER_VALUE;
+       else
+               val |= ADV7393_SVIDEO_POWER_VALUE;
+
+       err = adv7393_write(sd, ADV7393_POWER_MODE_REG, val);
+       if (err < 0)
+               goto setoutput_exit;
+
+       state->reg00 = val;
+
+       /* Enable YUV output */
+       val = state->reg02 | YUV_OUTPUT_SELECT;
+       err = adv7393_write(sd, ADV7393_MODE_REG0, val);
+       if (err < 0)
+               goto setoutput_exit;
+
+       state->reg02 = val;
+
+       /* configure SD DAC Output 1 bit */
+       val = state->reg82;
+       if (output_type == ADV7393_COMPONENT_ID)
+               val &= SD_DAC_OUT1_DI;
+       else
+               val |= SD_DAC_OUT1_EN;
+       err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val);
+       if (err < 0)
+               goto setoutput_exit;
+
+       state->reg82 = val;
+
+       /* configure ED/HD Color DAC Swap bit to zero */
+       val = state->reg35 & HD_DAC_SWAP_DI;
+       err = adv7393_write(sd, ADV7393_HD_MODE_REG6, val);
+       if (err < 0)
+               goto setoutput_exit;
+
+       state->reg35 = val;
+
+setoutput_exit:
+       if (err != 0)
+               v4l2_err(sd, "Error setting output, write failed\n");
+
+       return err;
+}
+
+static int adv7393_log_status(struct v4l2_subdev *sd)
+{
+       struct adv7393_state *state = to_state(sd);
+
+       v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std);
+       v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" :
+                       ((state->output == 1) ? "Component" : "S-Video"));
+       return 0;
+}
+
+static int adv7393_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct v4l2_subdev *sd = to_sd(ctrl);
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               return adv7393_write(sd, ADV7393_SD_BRIGHTNESS_WSS,
+                                       ctrl->val & SD_BRIGHTNESS_VALUE_MASK);
+
+       case V4L2_CID_HUE:
+               return adv7393_write(sd, ADV7393_SD_HUE_ADJUST,
+                                       ctrl->val - ADV7393_HUE_MIN);
+
+       case V4L2_CID_GAIN:
+               return adv7393_write(sd, ADV7393_DAC123_OUTPUT_LEVEL,
+                                       ctrl->val);
+       }
+       return -EINVAL;
+}
+
+static int adv7393_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_ADV7393, 0);
+}
+
+static const struct v4l2_ctrl_ops adv7393_ctrl_ops = {
+       .s_ctrl = adv7393_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops adv7393_core_ops = {
+       .log_status = adv7393_log_status,
+       .g_chip_ident = adv7393_g_chip_ident,
+       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+       .g_ctrl = v4l2_subdev_g_ctrl,
+       .s_ctrl = v4l2_subdev_s_ctrl,
+       .queryctrl = v4l2_subdev_queryctrl,
+       .querymenu = v4l2_subdev_querymenu,
+};
+
+static int adv7393_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+       struct adv7393_state *state = to_state(sd);
+       int err = 0;
+
+       if (state->std == std)
+               return 0;
+
+       err = adv7393_setstd(sd, std);
+       if (!err)
+               state->std = std;
+
+       return err;
+}
+
+static int adv7393_s_routing(struct v4l2_subdev *sd,
+               u32 input, u32 output, u32 config)
+{
+       struct adv7393_state *state = to_state(sd);
+       int err = 0;
+
+       if (state->output == output)
+               return 0;
+
+       err = adv7393_setoutput(sd, output);
+       if (!err)
+               state->output = output;
+
+       return err;
+}
+
+static const struct v4l2_subdev_video_ops adv7393_video_ops = {
+       .s_std_output   = adv7393_s_std_output,
+       .s_routing      = adv7393_s_routing,
+};
+
+static const struct v4l2_subdev_ops adv7393_ops = {
+       .core   = &adv7393_core_ops,
+       .video  = &adv7393_video_ops,
+};
+
+static int adv7393_initialize(struct v4l2_subdev *sd)
+{
+       struct adv7393_state *state = to_state(sd);
+       int err = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(adv7393_init_reg_val); i += 2) {
+
+               err = adv7393_write(sd, adv7393_init_reg_val[i],
+                                       adv7393_init_reg_val[i+1]);
+               if (err) {
+                       v4l2_err(sd, "Error initializing\n");
+                       return err;
+               }
+       }
+
+       /* Configure for default video standard */
+       err = adv7393_setoutput(sd, state->output);
+       if (err < 0) {
+               v4l2_err(sd, "Error setting output during init\n");
+               return -EINVAL;
+       }
+
+       err = adv7393_setstd(sd, state->std);
+       if (err < 0) {
+               v4l2_err(sd, "Error setting std during init\n");
+               return -EINVAL;
+       }
+
+       return err;
+}
+
+static int adv7393_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       struct adv7393_state *state;
+       int err;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -ENODEV;
+
+       v4l_info(client, "chip found @ 0x%x (%s)\n",
+                       client->addr << 1, client->adapter->name);
+
+       state = kzalloc(sizeof(struct adv7393_state), GFP_KERNEL);
+       if (state == NULL)
+               return -ENOMEM;
+
+       state->reg00    = ADV7393_POWER_MODE_REG_DEFAULT;
+       state->reg01    = 0x00;
+       state->reg02    = 0x20;
+       state->reg35    = ADV7393_HD_MODE_REG6_DEFAULT;
+       state->reg80    = ADV7393_SD_MODE_REG1_DEFAULT;
+       state->reg82    = ADV7393_SD_MODE_REG2_DEFAULT;
+
+       state->output = ADV7393_COMPOSITE_ID;
+       state->std = V4L2_STD_NTSC;
+
+       v4l2_i2c_subdev_init(&state->sd, client, &adv7393_ops);
+
+       v4l2_ctrl_handler_init(&state->hdl, 3);
+       v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, ADV7393_BRIGHTNESS_MIN,
+                                            ADV7393_BRIGHTNESS_MAX, 1,
+                                            ADV7393_BRIGHTNESS_DEF);
+       v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops,
+                       V4L2_CID_HUE, ADV7393_HUE_MIN,
+                                     ADV7393_HUE_MAX, 1,
+                                     ADV7393_HUE_DEF);
+       v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops,
+                       V4L2_CID_GAIN, ADV7393_GAIN_MIN,
+                                      ADV7393_GAIN_MAX, 1,
+                                      ADV7393_GAIN_DEF);
+       state->sd.ctrl_handler = &state->hdl;
+       if (state->hdl.error) {
+               int err = state->hdl.error;
+
+               v4l2_ctrl_handler_free(&state->hdl);
+               kfree(state);
+               return err;
+       }
+       v4l2_ctrl_handler_setup(&state->hdl);
+
+       err = adv7393_initialize(&state->sd);
+       if (err) {
+               v4l2_ctrl_handler_free(&state->hdl);
+               kfree(state);
+       }
+       return err;
+}
+
+static int adv7393_remove(struct i2c_client *client)
+{
+       struct v4l2_subdev *sd = i2c_get_clientdata(client);
+       struct adv7393_state *state = to_state(sd);
+
+       v4l2_device_unregister_subdev(sd);
+       v4l2_ctrl_handler_free(&state->hdl);
+       kfree(state);
+
+       return 0;
+}
+
+static const struct i2c_device_id adv7393_id[] = {
+       {"adv7393", 0},
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, adv7393_id);
+
+static struct i2c_driver adv7393_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "adv7393",
+       },
+       .probe          = adv7393_probe,
+       .remove         = adv7393_remove,
+       .id_table       = adv7393_id,
+};
+module_i2c_driver(adv7393_driver);
diff --git a/drivers/media/video/adv7393_regs.h b/drivers/media/video/adv7393_regs.h
new file mode 100644 (file)
index 0000000..7896833
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * ADV7393 encoder related structure and register definitions
+ *
+ * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/
+ * Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
+ *
+ * Based on ADV7343 driver,
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - 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 ADV7393_REGS_H
+#define ADV7393_REGS_H
+
+struct adv7393_std_info {
+       u32 standard_val3;
+       u32 fsc_val;
+       v4l2_std_id stdid;
+};
+
+/* Register offset macros */
+#define ADV7393_POWER_MODE_REG         (0x00)
+#define ADV7393_MODE_SELECT_REG                (0x01)
+#define ADV7393_MODE_REG0              (0x02)
+
+#define ADV7393_DAC123_OUTPUT_LEVEL    (0x0B)
+
+#define ADV7393_SOFT_RESET             (0x17)
+
+#define ADV7393_HD_MODE_REG1           (0x30)
+#define ADV7393_HD_MODE_REG2           (0x31)
+#define ADV7393_HD_MODE_REG3           (0x32)
+#define ADV7393_HD_MODE_REG4           (0x33)
+#define ADV7393_HD_MODE_REG5           (0x34)
+#define ADV7393_HD_MODE_REG6           (0x35)
+
+#define ADV7393_HD_MODE_REG7           (0x39)
+
+#define ADV7393_SD_MODE_REG1           (0x80)
+#define ADV7393_SD_MODE_REG2           (0x82)
+#define ADV7393_SD_MODE_REG3           (0x83)
+#define ADV7393_SD_MODE_REG4           (0x84)
+#define ADV7393_SD_MODE_REG5           (0x86)
+#define ADV7393_SD_MODE_REG6           (0x87)
+#define ADV7393_SD_MODE_REG7           (0x88)
+#define ADV7393_SD_MODE_REG8           (0x89)
+
+#define ADV7393_SD_TIMING_REG0         (0x8A)
+
+#define ADV7393_FSC_REG0               (0x8C)
+#define ADV7393_FSC_REG1               (0x8D)
+#define ADV7393_FSC_REG2               (0x8E)
+#define ADV7393_FSC_REG3               (0x8F)
+
+#define ADV7393_SD_CGMS_WSS0           (0x99)
+
+#define ADV7393_SD_HUE_ADJUST          (0xA0)
+#define ADV7393_SD_BRIGHTNESS_WSS      (0xA1)
+
+/* Default values for the registers */
+#define ADV7393_POWER_MODE_REG_DEFAULT         (0x10)
+#define ADV7393_HD_MODE_REG1_DEFAULT           (0x3C)  /* Changed Default
+                                                          720p EAV/SAV code*/
+#define ADV7393_HD_MODE_REG2_DEFAULT           (0x01)  /* Changed Pixel data
+                                                          valid */
+#define ADV7393_HD_MODE_REG3_DEFAULT           (0x00)  /* Color delay 0 clks */
+#define ADV7393_HD_MODE_REG4_DEFAULT           (0xEC)  /* Changed */
+#define ADV7393_HD_MODE_REG5_DEFAULT           (0x08)
+#define ADV7393_HD_MODE_REG6_DEFAULT           (0x00)
+#define ADV7393_HD_MODE_REG7_DEFAULT           (0x00)
+#define ADV7393_SOFT_RESET_DEFAULT             (0x02)
+#define ADV7393_COMPOSITE_POWER_VALUE          (0x10)
+#define ADV7393_COMPONENT_POWER_VALUE          (0x1C)
+#define ADV7393_SVIDEO_POWER_VALUE             (0x0C)
+#define ADV7393_SD_HUE_ADJUST_DEFAULT          (0x80)
+#define ADV7393_SD_BRIGHTNESS_WSS_DEFAULT      (0x00)
+
+#define ADV7393_SD_CGMS_WSS0_DEFAULT           (0x10)
+
+#define ADV7393_SD_MODE_REG1_DEFAULT           (0x10)
+#define ADV7393_SD_MODE_REG2_DEFAULT           (0xC9)
+#define ADV7393_SD_MODE_REG3_DEFAULT           (0x00)
+#define ADV7393_SD_MODE_REG4_DEFAULT           (0x00)
+#define ADV7393_SD_MODE_REG5_DEFAULT           (0x02)
+#define ADV7393_SD_MODE_REG6_DEFAULT           (0x8C)
+#define ADV7393_SD_MODE_REG7_DEFAULT           (0x14)
+#define ADV7393_SD_MODE_REG8_DEFAULT           (0x00)
+
+#define ADV7393_SD_TIMING_REG0_DEFAULT         (0x0C)
+
+/* Bit masks for Mode Select Register */
+#define INPUT_MODE_MASK                        (0x70)
+#define SD_INPUT_MODE                  (0x00)
+#define HD_720P_INPUT_MODE             (0x10)
+#define HD_1080I_INPUT_MODE            (0x10)
+
+/* Bit masks for Mode Register 0 */
+#define TEST_PATTERN_BLACK_BAR_EN      (0x04)
+#define YUV_OUTPUT_SELECT              (0x20)
+#define RGB_OUTPUT_SELECT              (0xDF)
+
+/* Bit masks for SD brightness/WSS */
+#define SD_BRIGHTNESS_VALUE_MASK       (0x7F)
+#define SD_BLANK_WSS_DATA_MASK         (0x80)
+
+/* Bit masks for soft reset register */
+#define SOFT_RESET                     (0x02)
+
+/* Bit masks for HD Mode Register 1 */
+#define OUTPUT_STD_MASK                (0x03)
+#define OUTPUT_STD_SHIFT       (0)
+#define OUTPUT_STD_EIA0_2      (0x00)
+#define OUTPUT_STD_EIA0_1      (0x01)
+#define OUTPUT_STD_FULL                (0x02)
+#define EMBEDDED_SYNC          (0x04)
+#define EXTERNAL_SYNC          (0xFB)
+#define STD_MODE_MASK          (0x1F)
+#define STD_MODE_SHIFT         (3)
+#define STD_MODE_720P          (0x05)
+#define STD_MODE_720P_25       (0x08)
+#define STD_MODE_720P_30       (0x07)
+#define STD_MODE_720P_50       (0x06)
+#define STD_MODE_1080I         (0x0D)
+#define STD_MODE_1080I_25      (0x0E)
+#define STD_MODE_1080P_24      (0x11)
+#define STD_MODE_1080P_25      (0x10)
+#define STD_MODE_1080P_30      (0x0F)
+#define STD_MODE_525P          (0x00)
+#define STD_MODE_625P          (0x03)
+
+/* Bit masks for SD Mode Register 1 */
+#define SD_STD_MASK            (0x03)
+#define SD_STD_NTSC            (0x00)
+#define SD_STD_PAL_BDGHI       (0x01)
+#define SD_STD_PAL_M           (0x02)
+#define SD_STD_PAL_N           (0x03)
+#define SD_LUMA_FLTR_MASK      (0x07)
+#define SD_LUMA_FLTR_SHIFT     (2)
+#define SD_CHROMA_FLTR_MASK    (0x07)
+#define SD_CHROMA_FLTR_SHIFT   (5)
+
+/* Bit masks for SD Mode Register 2 */
+#define SD_PRPB_SSAF_EN                (0x01)
+#define SD_PRPB_SSAF_DI                (0xFE)
+#define SD_DAC_OUT1_EN         (0x02)
+#define SD_DAC_OUT1_DI         (0xFD)
+#define SD_PEDESTAL_EN         (0x08)
+#define SD_PEDESTAL_DI         (0xF7)
+#define SD_SQUARE_PIXEL_EN     (0x10)
+#define SD_SQUARE_PIXEL_DI     (0xEF)
+#define SD_PIXEL_DATA_VALID    (0x40)
+#define SD_ACTIVE_EDGE_EN      (0x80)
+#define SD_ACTIVE_EDGE_DI      (0x7F)
+
+/* Bit masks for HD Mode Register 6 */
+#define HD_PRPB_SYNC_EN                (0x04)
+#define HD_PRPB_SYNC_DI                (0xFB)
+#define HD_DAC_SWAP_EN         (0x08)
+#define HD_DAC_SWAP_DI         (0xF7)
+#define HD_GAMMA_CURVE_A       (0xEF)
+#define HD_GAMMA_CURVE_B       (0x10)
+#define HD_GAMMA_EN            (0x20)
+#define HD_GAMMA_DI            (0xDF)
+#define HD_ADPT_FLTR_MODEA     (0xBF)
+#define HD_ADPT_FLTR_MODEB     (0x40)
+#define HD_ADPT_FLTR_EN                (0x80)
+#define HD_ADPT_FLTR_DI                (0x7F)
+
+#define ADV7393_BRIGHTNESS_MAX (63)
+#define ADV7393_BRIGHTNESS_MIN (-64)
+#define ADV7393_BRIGHTNESS_DEF (0)
+#define ADV7393_HUE_MAX                (127)
+#define ADV7393_HUE_MIN                (-128)
+#define ADV7393_HUE_DEF                (0)
+#define ADV7393_GAIN_MAX       (64)
+#define ADV7393_GAIN_MIN       (-64)
+#define ADV7393_GAIN_DEF       (0)
+
+#endif
index 856ab962cd63516b79865d47414a2b7b59fa0926..38952faaffda3a44ed81d7d268bc7c98306d3b64 100644 (file)
@@ -345,7 +345,7 @@ static struct CARD {
        { 0x15401836, BTTV_BOARD_PV183,         "Provideo PV183-7" },
        { 0x15401837, BTTV_BOARD_PV183,         "Provideo PV183-8" },
        { 0x3116f200, BTTV_BOARD_TVT_TD3116,    "Tongwei Video Technology TD-3116" },
-
+       { 0x02280279, BTTV_BOARD_APOSONIC_WDVR, "Aposonic W-DVR" },
        { 0, -1, NULL }
 };
 
@@ -676,6 +676,7 @@ struct tvcard bttv_tvcards[] = {
                .tuner_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .has_remote     = 1,
+               .has_radio      = 1,  /* not every card has radio */
        },
        [BTTV_BOARD_VOBIS_BOOSTAR] = {
                .name           = "Terratec TerraTV+ Version 1.0 (Bt848)/ Terra TValue Version 1.0/ Vobis TV-Boostar",
@@ -2817,6 +2818,14 @@ struct tvcard bttv_tvcards[] = {
                .pll            = PLL_28,
                .tuner_type     = TUNER_ABSENT,
        },
+       [BTTV_BOARD_APOSONIC_WDVR] = {
+               .name           = "Aposonic W-DVR",
+               .video_inputs   = 4,
+               .svhs           = NO_SVHS,
+               .muxsel         = MUXSEL(2, 3, 1, 0),
+               .tuner_type     = TUNER_ABSENT,
+       },
+
 };
 
 static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
index ff7a589d8e0f5e0486dd72c8870e4d965a8ce50e..b58ff87db771750dfa7a511d7f382b924dba8814 100644 (file)
@@ -557,12 +557,6 @@ static const struct bttv_format formats[] = {
                .btformat = BT848_COLOR_FMT_YUY2,
                .depth    = 16,
                .flags    = FORMAT_FLAGS_PACKED,
-       },{
-               .name     = "4:2:2, packed, YUYV",
-               .fourcc   = V4L2_PIX_FMT_YUYV,
-               .btformat = BT848_COLOR_FMT_YUY2,
-               .depth    = 16,
-               .flags    = FORMAT_FLAGS_PACKED,
        },{
                .name     = "4:2:2, packed, UYVY",
                .fourcc   = V4L2_PIX_FMT_UYVY,
index acfe2f3b92d9abdbb050a6586a7d9f4f310409e6..79a11240a5901b41e785b36f43913b19314ab955 100644 (file)
 #define BTTV_BOARD_GEOVISION_GV800S_SL    0x9e
 #define BTTV_BOARD_PV183                   0x9f
 #define BTTV_BOARD_TVT_TD3116             0xa0
-
+#define BTTV_BOARD_APOSONIC_WDVR           0xa1
 
 /* more card-specific defines */
 #define PT2254_L_CHANNEL 0x10
index 55e92902a76c6292900b62f4eec8c233b18ce8fe..a62a7b739991806567231870dc143ceb3cac675a 100644 (file)
@@ -932,7 +932,7 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
        buf->sequence = cam->buffers[buf->index].seq;
        buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer;
        buf->length = cam->frame_size;
-       buf->input = 0;
+       buf->reserved2 = 0;
        buf->reserved = 0;
        memset(&buf->timecode, 0, sizeof(buf->timecode));
 
diff --git a/drivers/media/video/cs8420.h b/drivers/media/video/cs8420.h
deleted file mode 100644 (file)
index 621c0c6..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/* cs8420.h - cs8420 initializations
-   Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- */
-#ifndef __CS8420_H__
-#define __CS8420_H__
-
-/* Initialization Sequence */
-
-static __u8 init8420[] = {
-       1, 0x01,        2, 0x02,        3, 0x00,        4, 0x46,
-       5, 0x24,        6, 0x84,        18, 0x18,       19, 0x13,
-};
-
-#define INIT8420LEN    (sizeof(init8420)/2)
-
-static __u8 mode8420pro[] = {  /* professional output mode */
-       32, 0xa1,       33, 0x00,       34, 0x00,       35, 0x00,
-       36, 0x00,       37, 0x00,       38, 0x00,       39, 0x00,
-       40, 0x00,       41, 0x00,       42, 0x00,       43, 0x00,
-       44, 0x00,       45, 0x00,       46, 0x00,       47, 0x00,
-       48, 0x00,       49, 0x00,       50, 0x00,       51, 0x00,
-       52, 0x00,       53, 0x00,       54, 0x00,       55, 0x00,
-};
-#define MODE8420LEN    (sizeof(mode8420pro)/2)
-
-static __u8 mode8420con[] = {  /* consumer output mode */
-       32, 0x20,       33, 0x00,       34, 0x00,       35, 0x48,
-       36, 0x00,       37, 0x00,       38, 0x00,       39, 0x00,
-       40, 0x00,       41, 0x00,       42, 0x00,       43, 0x00,
-       44, 0x00,       45, 0x00,       46, 0x00,       47, 0x00,
-       48, 0x00,       49, 0x00,       50, 0x00,       51, 0x00,
-       52, 0x00,       53, 0x00,       54, 0x00,       55, 0x00,
-};
-
-#endif
index 35fde4e931f5c4c4b7c97e7303d229acf3b35500..e9912db3b496ae69618b411896fe054ba35d4827 100644 (file)
@@ -1142,24 +1142,6 @@ static long cx18_default(struct file *file, void *fh, bool valid_prio,
        return 0;
 }
 
-long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd,
-                   unsigned long arg)
-{
-       struct video_device *vfd = video_devdata(filp);
-       struct cx18_open_id *id = file2id(filp);
-       struct cx18 *cx = id->cx;
-       long res;
-
-       mutex_lock(&cx->serialize_lock);
-
-       if (cx18_debug & CX18_DBGFLG_IOCTL)
-               vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
-       res = video_ioctl2(filp, cmd, arg);
-       vfd->debug = 0;
-       mutex_unlock(&cx->serialize_lock);
-       return res;
-}
-
 static const struct v4l2_ioctl_ops cx18_ioctl_ops = {
        .vidioc_querycap                = cx18_querycap,
        .vidioc_s_audio                 = cx18_s_audio,
index dcb2559ad52050dbaeb0420bff74ed8ebae660f4..2f9dd591ee0f9421d95610756b91b43fbb419674 100644 (file)
@@ -29,5 +29,3 @@ void cx18_set_funcs(struct video_device *vdev);
 int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std);
 int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
 int cx18_s_input(struct file *file, void *fh, unsigned int inp);
-long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd,
-                   unsigned long arg);
index 4185bcb80ca3fb01c45c0d9b97b9139c087b94b8..9d598ab88615c07a82103010e6cb9c5a1e19600d 100644 (file)
@@ -40,8 +40,7 @@ static struct v4l2_file_operations cx18_v4l2_enc_fops = {
        .owner = THIS_MODULE,
        .read = cx18_v4l2_read,
        .open = cx18_v4l2_open,
-       /* FIXME change to video_ioctl2 if serialization lock can be removed */
-       .unlocked_ioctl = cx18_v4l2_ioctl,
+       .unlocked_ioctl = video_ioctl2,
        .release = cx18_v4l2_close,
        .poll = cx18_v4l2_enc_poll,
        .mmap = cx18_v4l2_mmap,
@@ -376,6 +375,7 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
        s->video_dev->fops = &cx18_v4l2_enc_fops;
        s->video_dev->release = video_device_release;
        s->video_dev->tvnorms = V4L2_STD_ALL;
+       s->video_dev->lock = &cx->serialize_lock;
        set_bit(V4L2_FL_USE_FH_PRIO, &s->video_dev->flags);
        cx18_set_funcs(s->video_dev);
        return 0;
index b085a3c6dc048fb29514669046d4d145ee55351e..447148eff9588658f954ba5e3d90cdbe6c1fbc76 100644 (file)
@@ -89,7 +89,7 @@ void initGPIO(struct cx231xx *dev)
        verve_read_byte(dev, 0x07, &val);
        cx231xx_info(" verve_read_byte address0x07=0x%x\n", val);
 
-       cx231xx_capture_start(dev, 1, 2);
+       cx231xx_capture_start(dev, 1, Vbi);
 
        cx231xx_mode_register(dev, EP_MODE_SET, 0x0500FE00);
        cx231xx_mode_register(dev, GBULK_BIT_EN, 0xFFFDFFFF);
@@ -99,7 +99,7 @@ void uninitGPIO(struct cx231xx *dev)
 {
        u8 value[4] = { 0, 0, 0, 0 };
 
-       cx231xx_capture_start(dev, 0, 2);
+       cx231xx_capture_start(dev, 0, Vbi);
        verve_write_byte(dev, 0x07, 0x14);
        cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
                        0x68, value, 4);
@@ -2516,29 +2516,29 @@ int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type)
 
        if (dev->udev->speed == USB_SPEED_HIGH) {
                switch (media_type) {
-               case 81: /* audio */
+               case Audio:
                        cx231xx_info("%s: Audio enter HANC\n", __func__);
                        status =
                            cx231xx_mode_register(dev, TS_MODE_REG, 0x9300);
                        break;
 
-               case 2: /* vbi */
+               case Vbi:
                        cx231xx_info("%s: set vanc registers\n", __func__);
                        status = cx231xx_mode_register(dev, TS_MODE_REG, 0x300);
                        break;
 
-               case 3: /* sliced cc */
+               case Sliced_cc:
                        cx231xx_info("%s: set hanc registers\n", __func__);
                        status =
                            cx231xx_mode_register(dev, TS_MODE_REG, 0x1300);
                        break;
 
-               case 0: /* video */
+               case Raw_Video:
                        cx231xx_info("%s: set video registers\n", __func__);
                        status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100);
                        break;
 
-               case 4: /* ts1 */
+               case TS1_serial_mode:
                        cx231xx_info("%s: set ts1 registers", __func__);
 
                if (dev->board.has_417) {
@@ -2569,7 +2569,7 @@ int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type)
                }
                        break;
 
-               case 6: /* ts1 parallel mode */
+               case TS1_parallel_mode:
                        cx231xx_info("%s: set ts1 parallel mode registers\n",
                                     __func__);
                        status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100);
@@ -2592,52 +2592,28 @@ int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type)
        /* get EP for media type */
        pcb_config = (struct pcb_config *)&dev->current_pcb_config;
 
-       if (pcb_config->config_num == 1) {
+       if (pcb_config->config_num) {
                switch (media_type) {
-               case 0: /* Video */
+               case Raw_Video:
                        ep_mask = ENABLE_EP4;   /* ep4  [00:1000] */
                        break;
-               case 1: /* Audio */
+               case Audio:
                        ep_mask = ENABLE_EP3;   /* ep3  [00:0100] */
                        break;
-               case 2: /* Vbi */
+               case Vbi:
                        ep_mask = ENABLE_EP5;   /* ep5 [01:0000] */
                        break;
-               case 3: /* Sliced_cc */
+               case Sliced_cc:
                        ep_mask = ENABLE_EP6;   /* ep6 [10:0000] */
                        break;
-               case 4: /* ts1 */
-               case 6: /* ts1 parallel mode */
+               case TS1_serial_mode:
+               case TS1_parallel_mode:
                        ep_mask = ENABLE_EP1;   /* ep1 [00:0001] */
                        break;
-               case 5: /* ts2 */
+               case TS2:
                        ep_mask = ENABLE_EP2;   /* ep2 [00:0010] */
                        break;
                }
-
-       } else if (pcb_config->config_num > 1) {
-               switch (media_type) {
-               case 0: /* Video */
-                       ep_mask = ENABLE_EP4;   /* ep4  [00:1000] */
-                       break;
-               case 1: /* Audio */
-                       ep_mask = ENABLE_EP3;   /* ep3  [00:0100] */
-                       break;
-               case 2: /* Vbi */
-                       ep_mask = ENABLE_EP5;   /* ep5 [01:0000] */
-                       break;
-               case 3: /* Sliced_cc */
-                       ep_mask = ENABLE_EP6;   /* ep6 [10:0000] */
-                       break;
-               case 4: /* ts1 */
-               case 6: /* ts1 parallel mode */
-                       ep_mask = ENABLE_EP1;   /* ep1 [00:0001] */
-                       break;
-               case 5: /* ts2 */
-                       ep_mask = ENABLE_EP2;   /* ep2 [00:0010] */
-                       break;
-               }
-
        }
 
        if (start) {
index 8ed460d692e0485925c156d0314e9ee50756c634..02d4d36735d39e6d68a1f8b82d3f3896f4a8e468 100644 (file)
@@ -1023,7 +1023,6 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
        int nr = 0, ifnum;
        int i, isoc_pipe = 0;
        char *speed;
-       char descr[255] = "";
        struct usb_interface_assoc_descriptor *assoc_desc;
 
        udev = usb_get_dev(interface_to_usbdev(interface));
@@ -1098,20 +1097,10 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
                speed = "unknown";
        }
 
-       if (udev->manufacturer)
-               strlcpy(descr, udev->manufacturer, sizeof(descr));
-
-       if (udev->product) {
-               if (*descr)
-                       strlcat(descr, " ", sizeof(descr));
-               strlcat(descr, udev->product, sizeof(descr));
-       }
-       if (*descr)
-               strlcat(descr, " ", sizeof(descr));
-
-       cx231xx_info("New device %s@ %s Mbps "
+       cx231xx_info("New device %s %s @ %s Mbps "
             "(%04x:%04x) with %d interfaces\n",
-            descr,
+            udev->manufacturer ? udev->manufacturer : "",
+            udev->product ? udev->product : "",
             speed,
             le16_to_cpu(udev->descriptor.idVendor),
             le16_to_cpu(udev->descriptor.idProduct),
index 925f3a04e53c40daf32e28970a9e7df22397f81c..781feed406f72747bb510cb8e4f2eed70afadd77 100644 (file)
@@ -499,16 +499,12 @@ int cx231xx_i2c_register(struct cx231xx_i2c *bus)
 
        BUG_ON(!dev->cx231xx_send_usb_command);
 
-       memcpy(&bus->i2c_adap, &cx231xx_adap_template, sizeof(bus->i2c_adap));
-       memcpy(&bus->i2c_algo, &cx231xx_algo, sizeof(bus->i2c_algo));
-       memcpy(&bus->i2c_client, &cx231xx_client_template,
-              sizeof(bus->i2c_client));
-
+       bus->i2c_adap = cx231xx_adap_template;
+       bus->i2c_client = cx231xx_client_template;
        bus->i2c_adap.dev.parent = &dev->udev->dev;
 
        strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
 
-       bus->i2c_algo.data = bus;
        bus->i2c_adap.algo_data = bus;
        i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
        i2c_add_adapter(&bus->i2c_adap);
index e17447554a0d6eed2de8deb94360019e20114cea..a89d020de94836d9cdbac3795760ae2d355e67a6 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/types.h>
 #include <linux/ioctl.h>
 #include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
 #include <linux/workqueue.h>
 #include <linux/mutex.h>
 
@@ -481,7 +480,6 @@ struct cx231xx_i2c {
 
        /* i2c i/o */
        struct i2c_adapter i2c_adap;
-       struct i2c_algo_bit_data i2c_algo;
        struct i2c_client i2c_client;
        u32 i2c_rc;
 
index be1e21d8295c8b382c0289d3265d7038abb2f73a..4887314339cbfa9071db9a898e985fae2495a9c8 100644 (file)
@@ -316,19 +316,13 @@ int cx23885_i2c_register(struct cx23885_i2c *bus)
 
        dprintk(1, "%s(bus = %d)\n", __func__, bus->nr);
 
-       memcpy(&bus->i2c_adap, &cx23885_i2c_adap_template,
-              sizeof(bus->i2c_adap));
-       memcpy(&bus->i2c_algo, &cx23885_i2c_algo_template,
-              sizeof(bus->i2c_algo));
-       memcpy(&bus->i2c_client, &cx23885_i2c_client_template,
-              sizeof(bus->i2c_client));
-
+       bus->i2c_adap = cx23885_i2c_adap_template;
+       bus->i2c_client = cx23885_i2c_client_template;
        bus->i2c_adap.dev.parent = &dev->pci->dev;
 
        strlcpy(bus->i2c_adap.name, bus->dev->name,
                sizeof(bus->i2c_adap.name));
 
-       bus->i2c_algo.data = bus;
        bus->i2c_adap.algo_data = bus;
        i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
        i2c_add_adapter(&bus->i2c_adap);
index 13c37ec07ae7e250b54694aad7c821c670fd584f..5d560c747e09346d7f90b70b6eccb24a6c949ea1 100644 (file)
@@ -21,7 +21,6 @@
 
 #include <linux/pci.h>
 #include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
 #include <linux/kdev_t.h>
 #include <linux/slab.h>
 
@@ -247,7 +246,6 @@ struct cx23885_i2c {
 
        /* i2c i/o */
        struct i2c_adapter         i2c_adap;
-       struct i2c_algo_bit_data   i2c_algo;
        struct i2c_client          i2c_client;
        u32                        i2c_rc;
 
index 6311180f430c43e93c126b8e219c9198fa354e7b..9844549764c96a8c10e288513149baf60908c2c7 100644 (file)
@@ -305,18 +305,12 @@ int cx25821_i2c_register(struct cx25821_i2c *bus)
 
        dprintk(1, "%s(bus = %d)\n", __func__, bus->nr);
 
-       memcpy(&bus->i2c_adap, &cx25821_i2c_adap_template,
-              sizeof(bus->i2c_adap));
-       memcpy(&bus->i2c_algo, &cx25821_i2c_algo_template,
-              sizeof(bus->i2c_algo));
-       memcpy(&bus->i2c_client, &cx25821_i2c_client_template,
-              sizeof(bus->i2c_client));
-
+       bus->i2c_adap = cx25821_i2c_adap_template;
+       bus->i2c_client = cx25821_i2c_client_template;
        bus->i2c_adap.dev.parent = &dev->pci->dev;
 
        strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
 
-       bus->i2c_algo.data = bus;
        bus->i2c_adap.algo_data = bus;
        i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
        i2c_add_adapter(&bus->i2c_adap);
index 313fb20a0b47a01b1094e45d227d382a9f2ad296..6a92e5c70c2a95b10d16e57cfea87e2651f77069 100644 (file)
@@ -499,7 +499,7 @@ static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder,
        mutex_lock(&dev->lock);
 
        /* no support */
-       if (decoder < VDEC_A && decoder > VDEC_H) {
+       if (decoder < VDEC_A || decoder > VDEC_H) {
                mutex_unlock(&dev->lock);
                return;
        }
index 029f2934a6d88bccdb409becdfd17849b99dff3b..8a9c0c869412aa36d6fb0ce558755aa0e62711f1 100644 (file)
@@ -26,7 +26,6 @@
 
 #include <linux/pci.h>
 #include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/sched.h>
@@ -213,7 +212,6 @@ struct cx25821_i2c {
 
        /* i2c i/o */
        struct i2c_adapter i2c_adap;
-       struct i2c_algo_bit_data i2c_algo;
        struct i2c_client i2c_client;
        u32 i2c_rc;
 
index 04bf6627d3629c824e174e4679de2525ff16ee38..dfac6e34859fc2d7b8647ac4c8afdd70f6e2de8b 100644 (file)
@@ -585,13 +585,10 @@ static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol,
 {
        snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
        struct cx88_core *core = chip->core;
-       struct v4l2_control client_ctl;
        int left = value->value.integer.value[0];
        int right = value->value.integer.value[1];
        int v, b;
 
-       memset(&client_ctl, 0, sizeof(client_ctl));
-
        /* Pass volume & balance onto any WM8775 */
        if (left >= right) {
                v = left << 10;
@@ -600,13 +597,8 @@ static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol,
                v = right << 10;
                b = right ? 0xffff - (0x8000 * left) / right : 0x8000;
        }
-       client_ctl.value = v;
-       client_ctl.id = V4L2_CID_AUDIO_VOLUME;
-       call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
-
-       client_ctl.value = b;
-       client_ctl.id = V4L2_CID_AUDIO_BALANCE;
-       call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+       wm8775_s_ctrl(core, V4L2_CID_AUDIO_VOLUME, v);
+       wm8775_s_ctrl(core, V4L2_CID_AUDIO_BALANCE, b);
 }
 
 /* OK - TODO: test it */
@@ -687,14 +679,8 @@ static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol,
                cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol);
                /* Pass mute onto any WM8775 */
                if ((core->board.audio_chip == V4L2_IDENT_WM8775) &&
-                   ((1<<6) == bit)) {
-                       struct v4l2_control client_ctl;
-
-                       memset(&client_ctl, 0, sizeof(client_ctl));
-                       client_ctl.value = 0 != (vol & bit);
-                       client_ctl.id = V4L2_CID_AUDIO_MUTE;
-                       call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
-               }
+                   ((1<<6) == bit))
+                       wm8775_s_ctrl(core, V4L2_CID_AUDIO_MUTE, 0 != (vol & bit));
                ret = 1;
        }
        spin_unlock_irq(&chip->reg_lock);
@@ -724,13 +710,10 @@ static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol,
 {
        snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
        struct cx88_core *core = chip->core;
-       struct v4l2_control client_ctl;
-
-       memset(&client_ctl, 0, sizeof(client_ctl));
-       client_ctl.id = V4L2_CID_AUDIO_LOUDNESS;
-       call_hw(core, WM8775_GID, core, g_ctrl, &client_ctl);
-       value->value.integer.value[0] = client_ctl.value ? 1 : 0;
+       s32 val;
 
+       val = wm8775_g_ctrl(core, V4L2_CID_AUDIO_LOUDNESS);
+       value->value.integer.value[0] = val ? 1 : 0;
        return 0;
 }
 
index ed7b2aa1ed831d4582c68679476ce8b08b733da7..843ffd9e533b06a04e4eb7720743ec35624e4eb0 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/firmware.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
 #include <media/cx2341x.h>
 
 #include "cx88.h"
@@ -523,11 +524,10 @@ static void blackbird_codec_settings(struct cx8802_dev *dev)
        blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
                                dev->height, dev->width);
 
-       dev->params.width = dev->width;
-       dev->params.height = dev->height;
-       dev->params.is_50hz = (dev->core->tvnorm & V4L2_STD_625_50) != 0;
-
-       cx2341x_update(dev, blackbird_mbox_func, NULL, &dev->params);
+       dev->cxhdl.width = dev->width;
+       dev->cxhdl.height = dev->height;
+       cx2341x_handler_set_50hz(&dev->cxhdl, dev->core->tvnorm & V4L2_STD_625_50);
+       cx2341x_handler_setup(&dev->cxhdl);
 }
 
 static int blackbird_initialize_codec(struct cx8802_dev *dev)
@@ -618,6 +618,8 @@ static int blackbird_start_codec(struct file *file, void *priv)
        /* initialize the video input */
        blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
 
+       cx2341x_handler_set_busy(&dev->cxhdl, 1);
+
        /* start capturing to the host interface */
        blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
                        BLACKBIRD_MPEG_CAPTURE,
@@ -636,6 +638,8 @@ static int blackbird_stop_codec(struct cx8802_dev *dev)
                        BLACKBIRD_RAW_BITS_NONE
                );
 
+       cx2341x_handler_set_busy(&dev->cxhdl, 0);
+
        dev->mpeg_active = 0;
        return 0;
 }
@@ -685,58 +689,15 @@ static struct videobuf_queue_ops blackbird_qops = {
 
 /* ------------------------------------------------------------------ */
 
-static const u32 *ctrl_classes[] = {
-       cx88_user_ctrls,
-       cx2341x_mpeg_ctrls,
-       NULL
-};
-
-static int blackbird_queryctrl(struct cx8802_dev *dev, struct v4l2_queryctrl *qctrl)
-{
-       qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
-       if (qctrl->id == 0)
-               return -EINVAL;
-
-       /* Standard V4L2 controls */
-       if (cx8800_ctrl_query(dev->core, qctrl) == 0)
-               return 0;
-
-       /* MPEG V4L2 controls */
-       if (cx2341x_ctrl_query(&dev->params, qctrl))
-               qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
-       return 0;
-}
-
-/* ------------------------------------------------------------------ */
-/* IOCTL Handlers                                                     */
-
-static int vidioc_querymenu (struct file *file, void *priv,
-                               struct v4l2_querymenu *qmenu)
-{
-       struct cx8802_dev *dev  = ((struct cx8802_fh *)priv)->dev;
-       struct v4l2_queryctrl qctrl;
-
-       qctrl.id = qmenu->id;
-       blackbird_queryctrl(dev, &qctrl);
-       return v4l2_ctrl_query_menu(qmenu, &qctrl,
-                       cx2341x_ctrl_get_menu(&dev->params, qmenu->id));
-}
-
-static int vidioc_querycap (struct file *file, void  *priv,
+static int vidioc_querycap(struct file *file, void  *priv,
                                        struct v4l2_capability *cap)
 {
        struct cx8802_dev *dev  = ((struct cx8802_fh *)priv)->dev;
        struct cx88_core  *core = dev->core;
 
        strcpy(cap->driver, "cx88_blackbird");
-       strlcpy(cap->card, core->board.name, sizeof(cap->card));
-       sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
-       cap->capabilities =
-               V4L2_CAP_VIDEO_CAPTURE |
-               V4L2_CAP_READWRITE     |
-               V4L2_CAP_STREAMING;
-       if (UNSET != core->board.tuner_type)
-               cap->capabilities |= V4L2_CAP_TUNER;
+       sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+       cx88_querycap(file, core, cap);
        return 0;
 }
 
@@ -748,6 +709,7 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void  *priv,
 
        strlcpy(f->description, "MPEG", sizeof(f->description));
        f->pixelformat = V4L2_PIX_FMT_MPEG;
+       f->flags = V4L2_FMT_FLAG_COMPRESSED;
        return 0;
 }
 
@@ -759,12 +721,12 @@ static int vidioc_g_fmt_vid_cap (struct file *file, void *priv,
 
        f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
        f->fmt.pix.bytesperline = 0;
-       f->fmt.pix.sizeimage    = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */
-       f->fmt.pix.colorspace   = 0;
+       f->fmt.pix.sizeimage    = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
+       f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
        f->fmt.pix.width        = dev->width;
        f->fmt.pix.height       = dev->height;
        f->fmt.pix.field        = fh->mpegq.field;
-       dprintk(0,"VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
+       dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
                dev->width, dev->height, fh->mpegq.field );
        return 0;
 }
@@ -777,9 +739,9 @@ static int vidioc_try_fmt_vid_cap (struct file *file, void *priv,
 
        f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
        f->fmt.pix.bytesperline = 0;
-       f->fmt.pix.sizeimage    = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */;
-       f->fmt.pix.colorspace   = 0;
-       dprintk(0,"VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
+       f->fmt.pix.sizeimage    = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
+       f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
+       dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
                dev->width, dev->height, fh->mpegq.field );
        return 0;
 }
@@ -793,15 +755,15 @@ static int vidioc_s_fmt_vid_cap (struct file *file, void *priv,
 
        f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
        f->fmt.pix.bytesperline = 0;
-       f->fmt.pix.sizeimage    = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */;
-       f->fmt.pix.colorspace   = 0;
+       f->fmt.pix.sizeimage    = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
+       f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
        dev->width              = f->fmt.pix.width;
        dev->height             = f->fmt.pix.height;
        fh->mpegq.field         = f->fmt.pix.field;
        cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
        blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
                                f->fmt.pix.height, f->fmt.pix.width);
-       dprintk(0,"VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+       dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
                f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field );
        return 0;
 }
@@ -834,60 +796,21 @@ static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
 static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
 {
        struct cx8802_fh  *fh   = priv;
+       struct cx8802_dev *dev  = fh->dev;
+
+       if (!dev->mpeg_active)
+               blackbird_start_codec(file, fh);
        return videobuf_streamon(&fh->mpegq);
 }
 
 static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
 {
        struct cx8802_fh  *fh   = priv;
-       return videobuf_streamoff(&fh->mpegq);
-}
-
-static int vidioc_g_ext_ctrls (struct file *file, void *priv,
-                              struct v4l2_ext_controls *f)
-{
-       struct cx8802_dev *dev  = ((struct cx8802_fh *)priv)->dev;
-
-       if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
-               return -EINVAL;
-       return cx2341x_ext_ctrls(&dev->params, 0, f, VIDIOC_G_EXT_CTRLS);
-}
-
-static int vidioc_s_ext_ctrls (struct file *file, void *priv,
-                              struct v4l2_ext_controls *f)
-{
-       struct cx8802_dev *dev  = ((struct cx8802_fh *)priv)->dev;
-       struct cx2341x_mpeg_params p;
-       int err;
-
-       if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
-               return -EINVAL;
+       struct cx8802_dev *dev  = fh->dev;
 
        if (dev->mpeg_active)
                blackbird_stop_codec(dev);
-
-       p = dev->params;
-       err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS);
-       if (!err) {
-               err = cx2341x_update(dev, blackbird_mbox_func, &dev->params, &p);
-               dev->params = p;
-       }
-       return err;
-}
-
-static int vidioc_try_ext_ctrls (struct file *file, void *priv,
-                              struct v4l2_ext_controls *f)
-{
-       struct cx8802_dev *dev  = ((struct cx8802_fh *)priv)->dev;
-       struct cx2341x_mpeg_params p;
-       int err;
-
-       if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
-               return -EINVAL;
-       p = dev->params;
-       err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
-
-       return err;
+       return videobuf_streamoff(&fh->mpegq);
 }
 
 static int vidioc_s_frequency (struct file *file, void *priv,
@@ -897,6 +820,10 @@ static int vidioc_s_frequency (struct file *file, void *priv,
        struct cx8802_dev *dev  = fh->dev;
        struct cx88_core  *core = dev->core;
 
+       if (unlikely(UNSET == core->board.tuner_type))
+               return -EINVAL;
+       if (unlikely(f->tuner != 0))
+               return -EINVAL;
        if (dev->mpeg_active)
                blackbird_stop_codec(dev);
 
@@ -914,29 +841,11 @@ static int vidioc_log_status (struct file *file, void *priv)
        char name[32 + 2];
 
        snprintf(name, sizeof(name), "%s/2", core->name);
-       printk("%s/2: ============  START LOG STATUS  ============\n",
-               core->name);
        call_all(core, core, log_status);
-       cx2341x_log_status(&dev->params, name);
-       printk("%s/2: =============  END LOG STATUS  =============\n",
-               core->name);
+       v4l2_ctrl_handler_log_status(&dev->cxhdl.hdl, name);
        return 0;
 }
 
-static int vidioc_queryctrl (struct file *file, void *priv,
-                               struct v4l2_queryctrl *qctrl)
-{
-       struct cx8802_dev *dev  = ((struct cx8802_fh *)priv)->dev;
-
-       if (blackbird_queryctrl(dev, qctrl) == 0)
-               return 0;
-
-       qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
-       if (unlikely(qctrl->id == 0))
-               return -EINVAL;
-       return cx8800_ctrl_query(dev->core, qctrl);
-}
-
 static int vidioc_enum_input (struct file *file, void *priv,
                                struct v4l2_input *i)
 {
@@ -944,22 +853,6 @@ static int vidioc_enum_input (struct file *file, void *priv,
        return cx88_enum_input (core,i);
 }
 
-static int vidioc_g_ctrl (struct file *file, void *priv,
-                               struct v4l2_control *ctl)
-{
-       struct cx88_core  *core = ((struct cx8802_fh *)priv)->dev->core;
-       return
-               cx88_get_control(core,ctl);
-}
-
-static int vidioc_s_ctrl (struct file *file, void *priv,
-                               struct v4l2_control *ctl)
-{
-       struct cx88_core  *core = ((struct cx8802_fh *)priv)->dev->core;
-       return
-               cx88_set_control(core,ctl);
-}
-
 static int vidioc_g_frequency (struct file *file, void *priv,
                                struct v4l2_frequency *f)
 {
@@ -968,8 +861,9 @@ static int vidioc_g_frequency (struct file *file, void *priv,
 
        if (unlikely(UNSET == core->board.tuner_type))
                return -EINVAL;
+       if (unlikely(f->tuner != 0))
+               return -EINVAL;
 
-       f->type = V4L2_TUNER_ANALOG_TV;
        f->frequency = core->freq;
        call_all(core, tuner, g_frequency, f);
 
@@ -990,6 +884,8 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
 
        if (i >= 4)
                return -EINVAL;
+       if (0 == INPUT(i).type)
+               return -EINVAL;
 
        mutex_lock(&core->lock);
        cx88_newstation(core);
@@ -1010,9 +906,9 @@ static int vidioc_g_tuner (struct file *file, void *priv,
                return -EINVAL;
 
        strcpy(t->name, "Television");
-       t->type       = V4L2_TUNER_ANALOG_TV;
        t->capability = V4L2_TUNER_CAP_NORM;
        t->rangehigh  = 0xffffffffUL;
+       call_all(core, tuner, g_tuner, t);
 
        cx88_get_stereo(core ,t);
        reg = cx_read(MO_DEVICE_STATUS);
@@ -1034,6 +930,14 @@ static int vidioc_s_tuner (struct file *file, void *priv,
        return 0;
 }
 
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
+{
+       struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+
+       *tvnorm = core->tvnorm;
+       return 0;
+}
+
 static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id)
 {
        struct cx88_core  *core = ((struct cx8802_fh *)priv)->dev->core;
@@ -1087,6 +991,7 @@ static int mpeg_open(struct file *file)
                mutex_unlock(&dev->core->lock);
                return -ENOMEM;
        }
+       v4l2_fh_init(&fh->fh, vdev);
        file->private_data = fh;
        fh->dev      = dev;
 
@@ -1103,6 +1008,7 @@ static int mpeg_open(struct file *file)
 
        dev->core->mpeg_users++;
        mutex_unlock(&dev->core->lock);
+       v4l2_fh_add(&fh->fh);
        return 0;
 }
 
@@ -1123,6 +1029,8 @@ static int mpeg_release(struct file *file)
 
        videobuf_mmap_free(&fh->mpegq);
 
+       v4l2_fh_del(&fh->fh);
+       v4l2_fh_exit(&fh->fh);
        file->private_data = NULL;
        kfree(fh);
 
@@ -1155,13 +1063,14 @@ mpeg_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
 static unsigned int
 mpeg_poll(struct file *file, struct poll_table_struct *wait)
 {
+       unsigned long req_events = poll_requested_events(wait);
        struct cx8802_fh *fh = file->private_data;
        struct cx8802_dev *dev = fh->dev;
 
-       if (!dev->mpeg_active)
+       if (!dev->mpeg_active && (req_events & (POLLIN | POLLRDNORM)))
                blackbird_start_codec(file, fh);
 
-       return videobuf_poll_stream(file, &fh->mpegq, wait);
+       return v4l2_ctrl_poll(file, wait) | videobuf_poll_stream(file, &fh->mpegq, wait);
 }
 
 static int
@@ -1180,11 +1089,10 @@ static const struct v4l2_file_operations mpeg_fops =
        .read          = mpeg_read,
        .poll          = mpeg_poll,
        .mmap          = mpeg_mmap,
-       .ioctl         = video_ioctl2,
+       .unlocked_ioctl = video_ioctl2,
 };
 
 static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
-       .vidioc_querymenu     = vidioc_querymenu,
        .vidioc_querycap      = vidioc_querycap,
        .vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
        .vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
@@ -1196,21 +1104,18 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
        .vidioc_dqbuf         = vidioc_dqbuf,
        .vidioc_streamon      = vidioc_streamon,
        .vidioc_streamoff     = vidioc_streamoff,
-       .vidioc_g_ext_ctrls   = vidioc_g_ext_ctrls,
-       .vidioc_s_ext_ctrls   = vidioc_s_ext_ctrls,
-       .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
        .vidioc_s_frequency   = vidioc_s_frequency,
        .vidioc_log_status    = vidioc_log_status,
-       .vidioc_queryctrl     = vidioc_queryctrl,
        .vidioc_enum_input    = vidioc_enum_input,
-       .vidioc_g_ctrl        = vidioc_g_ctrl,
-       .vidioc_s_ctrl        = vidioc_s_ctrl,
        .vidioc_g_frequency   = vidioc_g_frequency,
        .vidioc_g_input       = vidioc_g_input,
        .vidioc_s_input       = vidioc_s_input,
        .vidioc_g_tuner       = vidioc_g_tuner,
        .vidioc_s_tuner       = vidioc_s_tuner,
+       .vidioc_g_std         = vidioc_g_std,
        .vidioc_s_std         = vidioc_s_std,
+       .vidioc_subscribe_event      = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event    = v4l2_event_unsubscribe,
 };
 
 static struct video_device cx8802_mpeg_template = {
@@ -1218,7 +1123,6 @@ static struct video_device cx8802_mpeg_template = {
        .fops                 = &mpeg_fops,
        .ioctl_ops            = &mpeg_ioctl_ops,
        .tvnorms              = CX88_NORMS,
-       .current_norm         = V4L2_STD_NTSC_M,
 };
 
 /* ------------------------------------------------------------------ */
@@ -1286,6 +1190,7 @@ static int blackbird_register_video(struct cx8802_dev *dev)
 
        dev->mpeg_dev = cx88_vdev_init(dev->core,dev->pci,
                                       &cx8802_mpeg_template,"mpeg");
+       dev->mpeg_dev->ctrl_handler = &dev->cxhdl.hdl;
        video_set_drvdata(dev->mpeg_dev, dev);
        err = video_register_device(dev->mpeg_dev,VFL_TYPE_GRABBER, -1);
        if (err < 0) {
@@ -1318,17 +1223,20 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv)
                goto fail_core;
 
        dev->width = 720;
-       dev->height = 576;
-       cx2341x_fill_defaults(&dev->params);
-       dev->params.port = CX2341X_PORT_STREAMING;
-
-       cx8802_mpeg_template.current_norm = core->tvnorm;
-
        if (core->tvnorm & V4L2_STD_525_60) {
                dev->height = 480;
        } else {
                dev->height = 576;
        }
+       dev->cxhdl.port = CX2341X_PORT_STREAMING;
+       dev->cxhdl.width = dev->width;
+       dev->cxhdl.height = dev->height;
+       dev->cxhdl.func = blackbird_mbox_func;
+       dev->cxhdl.priv = dev;
+       err = cx2341x_handler_init(&dev->cxhdl, 36);
+       if (err)
+               goto fail_core;
+       v4l2_ctrl_add_handler(&dev->cxhdl.hdl, &core->video_hdl);
 
        /* blackbird stuff */
        printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n",
@@ -1336,12 +1244,14 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv)
        host_setup(dev->core);
 
        blackbird_initialize_codec(dev);
-       blackbird_register_video(dev);
 
        /* initial device configuration: needed ? */
 //     init_controls(core);
        cx88_set_tvnorm(core,core->tvnorm);
        cx88_video_mux(core,0);
+       cx2341x_handler_set_50hz(&dev->cxhdl, dev->height == 576);
+       cx2341x_handler_setup(&dev->cxhdl);
+       blackbird_register_video(dev);
 
        return 0;
 
@@ -1351,8 +1261,12 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv)
 
 static int cx8802_blackbird_remove(struct cx8802_driver *drv)
 {
+       struct cx88_core *core = drv->core;
+       struct cx8802_dev *dev = core->dvbdev;
+
        /* blackbird */
        blackbird_unregister_video(drv->core->dvbdev);
+       v4l2_ctrl_handler_free(&dev->cxhdl.hdl);
 
        return 0;
 }
index cbd5d119a2c660ebd7f8e9aba9b4206795d290e9..4e9d4f7229602e3e3a4f3fbc1a321507a07d894a 100644 (file)
@@ -3693,7 +3693,22 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
                return NULL;
        }
 
+       if (v4l2_ctrl_handler_init(&core->video_hdl, 13)) {
+               v4l2_device_unregister(&core->v4l2_dev);
+               kfree(core);
+               return NULL;
+       }
+
+       if (v4l2_ctrl_handler_init(&core->audio_hdl, 13)) {
+               v4l2_ctrl_handler_free(&core->video_hdl);
+               v4l2_device_unregister(&core->v4l2_dev);
+               kfree(core);
+               return NULL;
+       }
+
        if (0 != cx88_get_resources(core, pci)) {
+               v4l2_ctrl_handler_free(&core->video_hdl);
+               v4l2_ctrl_handler_free(&core->audio_hdl);
                v4l2_device_unregister(&core->v4l2_dev);
                kfree(core);
                return NULL;
@@ -3706,6 +3721,11 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
        core->bmmio = (u8 __iomem *)core->lmmio;
 
        if (core->lmmio == NULL) {
+               release_mem_region(pci_resource_start(pci, 0),
+                          pci_resource_len(pci, 0));
+               v4l2_ctrl_handler_free(&core->video_hdl);
+               v4l2_ctrl_handler_free(&core->audio_hdl);
+               v4l2_device_unregister(&core->v4l2_dev);
                kfree(core);
                return NULL;
        }
index fbfdd8067937604ab31482727a3204de970a6167..e81c735f012a263b918bfdb324aff12578b06c1d 100644 (file)
@@ -1012,6 +1012,9 @@ int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
        // tell i2c chips
        call_all(core, core, s_std, norm);
 
+       /* The chroma_agc control should be inaccessible if the video format is SECAM */
+       v4l2_ctrl_grab(core->chroma_agc, cxiformat == VideoFormatSECAM);
+
        // done
        return 0;
 }
@@ -1030,10 +1033,10 @@ struct video_device *cx88_vdev_init(struct cx88_core *core,
                return NULL;
        *vfd = *template_;
        vfd->v4l2_dev = &core->v4l2_dev;
-       vfd->parent = &pci->dev;
        vfd->release = video_device_release;
        snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
                 core->name, type, core->board.name);
+       set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
        return vfd;
 }
 
@@ -1086,6 +1089,8 @@ void cx88_core_put(struct cx88_core *core, struct pci_dev *pci)
        iounmap(core->lmmio);
        cx88_devcount--;
        mutex_unlock(&devlist);
+       v4l2_ctrl_handler_free(&core->video_hdl);
+       v4l2_ctrl_handler_free(&core->audio_hdl);
        v4l2_device_unregister(&core->v4l2_dev);
        kfree(core);
 }
index 921c56d115d6e06ec228f4edd7d301f4b5fe338c..f6fcc7e763ab9c1964bbd9eab1d18b6bb6749eec 100644 (file)
@@ -40,6 +40,7 @@
 #include "cx88.h"
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
 #include <media/wm8775.h>
 
 MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
@@ -155,219 +156,147 @@ static const struct cx8800_fmt* format_by_fourcc(unsigned int fourcc)
 
 /* ------------------------------------------------------------------- */
 
-static const struct v4l2_queryctrl no_ctl = {
-       .name  = "42",
-       .flags = V4L2_CTRL_FLAG_DISABLED,
+struct cx88_ctrl {
+       /* control information */
+       u32 id;
+       s32 minimum;
+       s32 maximum;
+       u32 step;
+       s32 default_value;
+
+       /* control register information */
+       u32 off;
+       u32 reg;
+       u32 sreg;
+       u32 mask;
+       u32 shift;
 };
 
-static const struct cx88_ctrl cx8800_ctls[] = {
+static const struct cx88_ctrl cx8800_vid_ctls[] = {
        /* --- video --- */
        {
-               .v = {
-                       .id            = V4L2_CID_BRIGHTNESS,
-                       .name          = "Brightness",
-                       .minimum       = 0x00,
-                       .maximum       = 0xff,
-                       .step          = 1,
-                       .default_value = 0x7f,
-                       .type          = V4L2_CTRL_TYPE_INTEGER,
-               },
-               .off                   = 128,
-               .reg                   = MO_CONTR_BRIGHT,
-               .mask                  = 0x00ff,
-               .shift                 = 0,
+               .id            = V4L2_CID_BRIGHTNESS,
+               .minimum       = 0x00,
+               .maximum       = 0xff,
+               .step          = 1,
+               .default_value = 0x7f,
+               .off           = 128,
+               .reg           = MO_CONTR_BRIGHT,
+               .mask          = 0x00ff,
+               .shift         = 0,
        },{
-               .v = {
-                       .id            = V4L2_CID_CONTRAST,
-                       .name          = "Contrast",
-                       .minimum       = 0,
-                       .maximum       = 0xff,
-                       .step          = 1,
-                       .default_value = 0x3f,
-                       .type          = V4L2_CTRL_TYPE_INTEGER,
-               },
-               .off                   = 0,
-               .reg                   = MO_CONTR_BRIGHT,
-               .mask                  = 0xff00,
-               .shift                 = 8,
+               .id            = V4L2_CID_CONTRAST,
+               .minimum       = 0,
+               .maximum       = 0xff,
+               .step          = 1,
+               .default_value = 0x3f,
+               .off           = 0,
+               .reg           = MO_CONTR_BRIGHT,
+               .mask          = 0xff00,
+               .shift         = 8,
        },{
-               .v = {
-                       .id            = V4L2_CID_HUE,
-                       .name          = "Hue",
-                       .minimum       = 0,
-                       .maximum       = 0xff,
-                       .step          = 1,
-                       .default_value = 0x7f,
-                       .type          = V4L2_CTRL_TYPE_INTEGER,
-               },
-               .off                   = 128,
-               .reg                   = MO_HUE,
-               .mask                  = 0x00ff,
-               .shift                 = 0,
+               .id            = V4L2_CID_HUE,
+               .minimum       = 0,
+               .maximum       = 0xff,
+               .step          = 1,
+               .default_value = 0x7f,
+               .off           = 128,
+               .reg           = MO_HUE,
+               .mask          = 0x00ff,
+               .shift         = 0,
        },{
                /* strictly, this only describes only U saturation.
                 * V saturation is handled specially through code.
                 */
-               .v = {
-                       .id            = V4L2_CID_SATURATION,
-                       .name          = "Saturation",
-                       .minimum       = 0,
-                       .maximum       = 0xff,
-                       .step          = 1,
-                       .default_value = 0x7f,
-                       .type          = V4L2_CTRL_TYPE_INTEGER,
-               },
-               .off                   = 0,
-               .reg                   = MO_UV_SATURATION,
-               .mask                  = 0x00ff,
-               .shift                 = 0,
+               .id            = V4L2_CID_SATURATION,
+               .minimum       = 0,
+               .maximum       = 0xff,
+               .step          = 1,
+               .default_value = 0x7f,
+               .off           = 0,
+               .reg           = MO_UV_SATURATION,
+               .mask          = 0x00ff,
+               .shift         = 0,
        }, {
-               .v = {
-                       .id            = V4L2_CID_SHARPNESS,
-                       .name          = "Sharpness",
-                       .minimum       = 0,
-                       .maximum       = 4,
-                       .step          = 1,
-                       .default_value = 0x0,
-                       .type          = V4L2_CTRL_TYPE_INTEGER,
-               },
-               .off                   = 0,
+               .id            = V4L2_CID_SHARPNESS,
+               .minimum       = 0,
+               .maximum       = 4,
+               .step          = 1,
+               .default_value = 0x0,
+               .off           = 0,
                /* NOTE: the value is converted and written to both even
                   and odd registers in the code */
-               .reg                   = MO_FILTER_ODD,
-               .mask                  = 7 << 7,
-               .shift                 = 7,
+               .reg           = MO_FILTER_ODD,
+               .mask          = 7 << 7,
+               .shift         = 7,
        }, {
-               .v = {
-                       .id            = V4L2_CID_CHROMA_AGC,
-                       .name          = "Chroma AGC",
-                       .minimum       = 0,
-                       .maximum       = 1,
-                       .default_value = 0x1,
-                       .type          = V4L2_CTRL_TYPE_BOOLEAN,
-               },
-               .reg                   = MO_INPUT_FORMAT,
-               .mask                  = 1 << 10,
-               .shift                 = 10,
+               .id            = V4L2_CID_CHROMA_AGC,
+               .minimum       = 0,
+               .maximum       = 1,
+               .default_value = 0x1,
+               .reg           = MO_INPUT_FORMAT,
+               .mask          = 1 << 10,
+               .shift         = 10,
        }, {
-               .v = {
-                       .id            = V4L2_CID_COLOR_KILLER,
-                       .name          = "Color killer",
-                       .minimum       = 0,
-                       .maximum       = 1,
-                       .default_value = 0x1,
-                       .type          = V4L2_CTRL_TYPE_BOOLEAN,
-               },
-               .reg                   = MO_INPUT_FORMAT,
-               .mask                  = 1 << 9,
-               .shift                 = 9,
+               .id            = V4L2_CID_COLOR_KILLER,
+               .minimum       = 0,
+               .maximum       = 1,
+               .default_value = 0x1,
+               .reg           = MO_INPUT_FORMAT,
+               .mask          = 1 << 9,
+               .shift         = 9,
        }, {
-               .v = {
-                       .id            = V4L2_CID_BAND_STOP_FILTER,
-                       .name          = "Notch filter",
-                       .minimum       = 0,
-                       .maximum       = 1,
-                       .step          = 1,
-                       .default_value = 0x0,
-                       .type          = V4L2_CTRL_TYPE_INTEGER,
-               },
-               .off                   = 0,
-               .reg                   = MO_HTOTAL,
-               .mask                  = 3 << 11,
-               .shift                 = 11,
-       }, {
-       /* --- audio --- */
-               .v = {
-                       .id            = V4L2_CID_AUDIO_MUTE,
-                       .name          = "Mute",
-                       .minimum       = 0,
-                       .maximum       = 1,
-                       .default_value = 1,
-                       .type          = V4L2_CTRL_TYPE_BOOLEAN,
-               },
-               .reg                   = AUD_VOL_CTL,
-               .sreg                  = SHADOW_AUD_VOL_CTL,
-               .mask                  = (1 << 6),
-               .shift                 = 6,
+               .id            = V4L2_CID_BAND_STOP_FILTER,
+               .minimum       = 0,
+               .maximum       = 1,
+               .step          = 1,
+               .default_value = 0x0,
+               .off           = 0,
+               .reg           = MO_HTOTAL,
+               .mask          = 3 << 11,
+               .shift         = 11,
+       }
+};
+
+static const struct cx88_ctrl cx8800_aud_ctls[] = {
+       {
+               /* --- audio --- */
+               .id            = V4L2_CID_AUDIO_MUTE,
+               .minimum       = 0,
+               .maximum       = 1,
+               .default_value = 1,
+               .reg           = AUD_VOL_CTL,
+               .sreg          = SHADOW_AUD_VOL_CTL,
+               .mask          = (1 << 6),
+               .shift         = 6,
        },{
-               .v = {
-                       .id            = V4L2_CID_AUDIO_VOLUME,
-                       .name          = "Volume",
-                       .minimum       = 0,
-                       .maximum       = 0x3f,
-                       .step          = 1,
-                       .default_value = 0x3f,
-                       .type          = V4L2_CTRL_TYPE_INTEGER,
-               },
-               .reg                   = AUD_VOL_CTL,
-               .sreg                  = SHADOW_AUD_VOL_CTL,
-               .mask                  = 0x3f,
-               .shift                 = 0,
+               .id            = V4L2_CID_AUDIO_VOLUME,
+               .minimum       = 0,
+               .maximum       = 0x3f,
+               .step          = 1,
+               .default_value = 0x3f,
+               .reg           = AUD_VOL_CTL,
+               .sreg          = SHADOW_AUD_VOL_CTL,
+               .mask          = 0x3f,
+               .shift         = 0,
        },{
-               .v = {
-                       .id            = V4L2_CID_AUDIO_BALANCE,
-                       .name          = "Balance",
-                       .minimum       = 0,
-                       .maximum       = 0x7f,
-                       .step          = 1,
-                       .default_value = 0x40,
-                       .type          = V4L2_CTRL_TYPE_INTEGER,
-               },
-               .reg                   = AUD_BAL_CTL,
-               .sreg                  = SHADOW_AUD_BAL_CTL,
-               .mask                  = 0x7f,
-               .shift                 = 0,
+               .id            = V4L2_CID_AUDIO_BALANCE,
+               .minimum       = 0,
+               .maximum       = 0x7f,
+               .step          = 1,
+               .default_value = 0x40,
+               .reg           = AUD_BAL_CTL,
+               .sreg          = SHADOW_AUD_BAL_CTL,
+               .mask          = 0x7f,
+               .shift         = 0,
        }
 };
-enum { CX8800_CTLS = ARRAY_SIZE(cx8800_ctls) };
-
-/* Must be sorted from low to high control ID! */
-const u32 cx88_user_ctrls[] = {
-       V4L2_CID_USER_CLASS,
-       V4L2_CID_BRIGHTNESS,
-       V4L2_CID_CONTRAST,
-       V4L2_CID_SATURATION,
-       V4L2_CID_HUE,
-       V4L2_CID_AUDIO_VOLUME,
-       V4L2_CID_AUDIO_BALANCE,
-       V4L2_CID_AUDIO_MUTE,
-       V4L2_CID_SHARPNESS,
-       V4L2_CID_CHROMA_AGC,
-       V4L2_CID_COLOR_KILLER,
-       V4L2_CID_BAND_STOP_FILTER,
-       0
-};
-EXPORT_SYMBOL(cx88_user_ctrls);
 
-static const u32 * const ctrl_classes[] = {
-       cx88_user_ctrls,
-       NULL
+enum {
+       CX8800_VID_CTLS = ARRAY_SIZE(cx8800_vid_ctls),
+       CX8800_AUD_CTLS = ARRAY_SIZE(cx8800_aud_ctls),
 };
 
-int cx8800_ctrl_query(struct cx88_core *core, struct v4l2_queryctrl *qctrl)
-{
-       int i;
-
-       if (qctrl->id < V4L2_CID_BASE ||
-           qctrl->id >= V4L2_CID_LASTP1)
-               return -EINVAL;
-       for (i = 0; i < CX8800_CTLS; i++)
-               if (cx8800_ctls[i].v.id == qctrl->id)
-                       break;
-       if (i == CX8800_CTLS) {
-               *qctrl = no_ctl;
-               return 0;
-       }
-       *qctrl = cx8800_ctls[i].v;
-       /* Report chroma AGC as inactive when SECAM is selected */
-       if (cx8800_ctls[i].v.id == V4L2_CID_CHROMA_AGC &&
-           core->tvnorm & V4L2_STD_SECAM)
-               qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
-
-       return 0;
-}
-EXPORT_SYMBOL(cx8800_ctrl_query);
-
 /* ------------------------------------------------------------------- */
 /* resource management                                                 */
 
@@ -591,8 +520,9 @@ static int
 buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
 {
        struct cx8800_fh *fh = q->priv_data;
+       struct cx8800_dev  *dev = fh->dev;
 
-       *size = fh->fmt->depth*fh->width*fh->height >> 3;
+       *size = dev->fmt->depth * dev->width * dev->height >> 3;
        if (0 == *count)
                *count = 32;
        if (*size * *count > vid_limit * 1024 * 1024)
@@ -611,21 +541,21 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
        struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
        int rc, init_buffer = 0;
 
-       BUG_ON(NULL == fh->fmt);
-       if (fh->width  < 48 || fh->width  > norm_maxw(core->tvnorm) ||
-           fh->height < 32 || fh->height > norm_maxh(core->tvnorm))
+       BUG_ON(NULL == dev->fmt);
+       if (dev->width  < 48 || dev->width  > norm_maxw(core->tvnorm) ||
+           dev->height < 32 || dev->height > norm_maxh(core->tvnorm))
                return -EINVAL;
-       buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+       buf->vb.size = (dev->width * dev->height * dev->fmt->depth) >> 3;
        if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
                return -EINVAL;
 
-       if (buf->fmt       != fh->fmt    ||
-           buf->vb.width  != fh->width  ||
-           buf->vb.height != fh->height ||
+       if (buf->fmt       != dev->fmt    ||
+           buf->vb.width  != dev->width  ||
+           buf->vb.height != dev->height ||
            buf->vb.field  != field) {
-               buf->fmt       = fh->fmt;
-               buf->vb.width  = fh->width;
-               buf->vb.height = fh->height;
+               buf->fmt       = dev->fmt;
+               buf->vb.width  = dev->width;
+               buf->vb.height = dev->height;
                buf->vb.field  = field;
                init_buffer = 1;
        }
@@ -675,7 +605,7 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
        }
        dprintk(2,"[%p/%d] buffer_prepare - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
                buf, buf->vb.i,
-               fh->width, fh->height, fh->fmt->depth, fh->fmt->name,
+               dev->width, dev->height, dev->fmt->depth, dev->fmt->name,
                (unsigned long)buf->risc.dma);
 
        buf->vb.state = VIDEOBUF_PREPARED;
@@ -755,12 +685,15 @@ static const struct videobuf_queue_ops cx8800_video_qops = {
 
 /* ------------------------------------------------------------------ */
 
-static struct videobuf_queue* get_queue(struct cx8800_fh *fh)
+static struct videobuf_queue *get_queue(struct file *file)
 {
-       switch (fh->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+       struct video_device *vdev = video_devdata(file);
+       struct cx8800_fh *fh = file->private_data;
+
+       switch (vdev->vfl_type) {
+       case VFL_TYPE_GRABBER:
                return &fh->vidq;
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case VFL_TYPE_VBI:
                return &fh->vbiq;
        default:
                BUG();
@@ -768,12 +701,14 @@ static struct videobuf_queue* get_queue(struct cx8800_fh *fh)
        }
 }
 
-static int get_ressource(struct cx8800_fh *fh)
+static int get_resource(struct file *file)
 {
-       switch (fh->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+       struct video_device *vdev = video_devdata(file);
+
+       switch (vdev->vfl_type) {
+       case VFL_TYPE_GRABBER:
                return RESOURCE_VIDEO;
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case VFL_TYPE_VBI:
                return RESOURCE_VBI;
        default:
                BUG();
@@ -810,13 +745,9 @@ static int video_open(struct file *file)
        if (unlikely(!fh))
                return -ENOMEM;
 
+       v4l2_fh_init(&fh->fh, vdev);
        file->private_data = fh;
        fh->dev      = dev;
-       fh->radio    = radio;
-       fh->type     = type;
-       fh->width    = 320;
-       fh->height   = 240;
-       fh->fmt      = format_by_fourcc(V4L2_PIX_FMT_BGR24);
 
        mutex_lock(&core->lock);
 
@@ -833,7 +764,7 @@ static int video_open(struct file *file)
                            sizeof(struct cx88_buffer),
                            fh, NULL);
 
-       if (fh->radio) {
+       if (vdev->vfl_type == VFL_TYPE_RADIO) {
                dprintk(1,"video_open: setting radio device\n");
                cx_write(MO_GP3_IO, core->board.radio.gpio3);
                cx_write(MO_GP0_IO, core->board.radio.gpio0);
@@ -859,6 +790,7 @@ static int video_open(struct file *file)
 
        core->users++;
        mutex_unlock(&core->lock);
+       v4l2_fh_add(&fh->fh);
 
        return 0;
 }
@@ -866,15 +798,16 @@ static int video_open(struct file *file)
 static ssize_t
 video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
 {
+       struct video_device *vdev = video_devdata(file);
        struct cx8800_fh *fh = file->private_data;
 
-       switch (fh->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+       switch (vdev->vfl_type) {
+       case VFL_TYPE_GRABBER:
                if (res_locked(fh->dev,RESOURCE_VIDEO))
                        return -EBUSY;
                return videobuf_read_one(&fh->vidq, data, count, ppos,
                                         file->f_flags & O_NONBLOCK);
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case VFL_TYPE_VBI:
                if (!res_get(fh->dev,fh,RESOURCE_VBI))
                        return -EBUSY;
                return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1,
@@ -888,16 +821,16 @@ video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
 static unsigned int
 video_poll(struct file *file, struct poll_table_struct *wait)
 {
+       struct video_device *vdev = video_devdata(file);
        struct cx8800_fh *fh = file->private_data;
        struct cx88_buffer *buf;
-       unsigned int rc = POLLERR;
+       unsigned int rc = v4l2_ctrl_poll(file, wait);
 
-       if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+       if (vdev->vfl_type == VFL_TYPE_VBI) {
                if (!res_get(fh->dev,fh,RESOURCE_VBI))
-                       return POLLERR;
-               return videobuf_poll_stream(file, &fh->vbiq, wait);
+                       return rc | POLLERR;
+               return rc | videobuf_poll_stream(file, &fh->vbiq, wait);
        }
-
        mutex_lock(&fh->vidq.vb_lock);
        if (res_check(fh,RESOURCE_VIDEO)) {
                /* streaming capture */
@@ -913,9 +846,7 @@ video_poll(struct file *file, struct poll_table_struct *wait)
        poll_wait(file, &buf->vb.done, wait);
        if (buf->vb.state == VIDEOBUF_DONE ||
            buf->vb.state == VIDEOBUF_ERROR)
-               rc = POLLIN|POLLRDNORM;
-       else
-               rc = 0;
+               rc |= POLLIN|POLLRDNORM;
 done:
        mutex_unlock(&fh->vidq.vb_lock);
        return rc;
@@ -952,6 +883,8 @@ static int video_release(struct file *file)
        videobuf_mmap_free(&fh->vbiq);
 
        mutex_lock(&dev->core->lock);
+       v4l2_fh_del(&fh->fh);
+       v4l2_fh_exit(&fh->fh);
        file->private_data = NULL;
        kfree(fh);
 
@@ -966,156 +899,104 @@ static int video_release(struct file *file)
 static int
 video_mmap(struct file *file, struct vm_area_struct * vma)
 {
-       struct cx8800_fh *fh = file->private_data;
-
-       return videobuf_mmap_mapper(get_queue(fh), vma);
+       return videobuf_mmap_mapper(get_queue(file), vma);
 }
 
 /* ------------------------------------------------------------------ */
 /* VIDEO CTRL IOCTLS                                                  */
 
-int cx88_get_control (struct cx88_core  *core, struct v4l2_control *ctl)
+static int cx8800_s_vid_ctrl(struct v4l2_ctrl *ctrl)
 {
-       const struct cx88_ctrl  *c    = NULL;
-       u32 value;
-       int i;
+       struct cx88_core *core =
+               container_of(ctrl->handler, struct cx88_core, video_hdl);
+       const struct cx88_ctrl *cc = ctrl->priv;
+       u32 value, mask;
 
-       for (i = 0; i < CX8800_CTLS; i++)
-               if (cx8800_ctls[i].v.id == ctl->id)
-                       c = &cx8800_ctls[i];
-       if (unlikely(NULL == c))
-               return -EINVAL;
+       mask = cc->mask;
+       switch (ctrl->id) {
+       case V4L2_CID_SATURATION:
+               /* special v_sat handling */
 
-       value = c->sreg ? cx_sread(c->sreg) : cx_read(c->reg);
-       switch (ctl->id) {
-       case V4L2_CID_AUDIO_BALANCE:
-               ctl->value = ((value & 0x7f) < 0x40) ? ((value & 0x7f) + 0x40)
-                                       : (0x7f - (value & 0x7f));
-               break;
-       case V4L2_CID_AUDIO_VOLUME:
-               ctl->value = 0x3f - (value & 0x3f);
+               value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
+
+               if (core->tvnorm & V4L2_STD_SECAM) {
+                       /* For SECAM, both U and V sat should be equal */
+                       value = value << 8 | value;
+               } else {
+                       /* Keeps U Saturation proportional to V Sat */
+                       value = (value * 0x5a) / 0x7f << 8 | value;
+               }
+               mask = 0xffff;
                break;
        case V4L2_CID_SHARPNESS:
-               ctl->value = ((value & 0x0200) ? (((value & 0x0180) >> 7) + 1)
-                                                : 0);
+               /* 0b000, 0b100, 0b101, 0b110, or 0b111 */
+               value = (ctrl->val < 1 ? 0 : ((ctrl->val + 3) << 7));
+               /* needs to be set for both fields */
+               cx_andor(MO_FILTER_EVEN, mask, value);
+               break;
+       case V4L2_CID_CHROMA_AGC:
+               value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
                break;
        default:
-               ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift;
+               value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
                break;
        }
-       dprintk(1,"get_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
-                               ctl->id, c->v.name, ctl->value, c->reg,
-                               value,c->mask, c->sreg ? " [shadowed]" : "");
+       dprintk(1, "set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
+                               ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
+                               mask, cc->sreg ? " [shadowed]" : "");
+       if (cc->sreg)
+               cx_sandor(cc->sreg, cc->reg, mask, value);
+       else
+               cx_andor(cc->reg, mask, value);
        return 0;
 }
-EXPORT_SYMBOL(cx88_get_control);
 
-int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl)
+static int cx8800_s_aud_ctrl(struct v4l2_ctrl *ctrl)
 {
-       const struct cx88_ctrl *c = NULL;
+       struct cx88_core *core =
+               container_of(ctrl->handler, struct cx88_core, audio_hdl);
+       const struct cx88_ctrl *cc = ctrl->priv;
        u32 value,mask;
-       int i;
-
-       for (i = 0; i < CX8800_CTLS; i++) {
-               if (cx8800_ctls[i].v.id == ctl->id) {
-                       c = &cx8800_ctls[i];
-               }
-       }
-       if (unlikely(NULL == c))
-               return -EINVAL;
-
-       if (ctl->value < c->v.minimum)
-               ctl->value = c->v.minimum;
-       if (ctl->value > c->v.maximum)
-               ctl->value = c->v.maximum;
 
        /* Pass changes onto any WM8775 */
        if (core->board.audio_chip == V4L2_IDENT_WM8775) {
-               struct v4l2_control client_ctl;
-               memset(&client_ctl, 0, sizeof(client_ctl));
-               client_ctl.id = ctl->id;
-
-               switch (ctl->id) {
+               switch (ctrl->id) {
                case V4L2_CID_AUDIO_MUTE:
-                       client_ctl.value = ctl->value;
+                       wm8775_s_ctrl(core, ctrl->id, ctrl->val);
                        break;
                case V4L2_CID_AUDIO_VOLUME:
-                       client_ctl.value = (ctl->value) ?
-                               (0x90 + ctl->value) << 8 : 0;
+                       wm8775_s_ctrl(core, ctrl->id, (ctrl->val) ?
+                                               (0x90 + ctrl->val) << 8 : 0);
                        break;
                case V4L2_CID_AUDIO_BALANCE:
-                       client_ctl.value = ctl->value << 9;
+                       wm8775_s_ctrl(core, ctrl->id, ctrl->val << 9);
                        break;
                default:
-                       client_ctl.id = 0;
                        break;
                }
-               if (client_ctl.id)
-                       call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
        }
 
-       mask=c->mask;
-       switch (ctl->id) {
+       mask = cc->mask;
+       switch (ctrl->id) {
        case V4L2_CID_AUDIO_BALANCE:
-               value = (ctl->value < 0x40) ? (0x7f - ctl->value) : (ctl->value - 0x40);
+               value = (ctrl->val < 0x40) ? (0x7f - ctrl->val) : (ctrl->val - 0x40);
                break;
        case V4L2_CID_AUDIO_VOLUME:
-               value = 0x3f - (ctl->value & 0x3f);
-               break;
-       case V4L2_CID_SATURATION:
-               /* special v_sat handling */
-
-               value = ((ctl->value - c->off) << c->shift) & c->mask;
-
-               if (core->tvnorm & V4L2_STD_SECAM) {
-                       /* For SECAM, both U and V sat should be equal */
-                       value=value<<8|value;
-               } else {
-                       /* Keeps U Saturation proportional to V Sat */
-                       value=(value*0x5a)/0x7f<<8|value;
-               }
-               mask=0xffff;
-               break;
-       case V4L2_CID_SHARPNESS:
-               /* 0b000, 0b100, 0b101, 0b110, or 0b111 */
-               value = (ctl->value < 1 ? 0 : ((ctl->value + 3) << 7));
-               /* needs to be set for both fields */
-               cx_andor(MO_FILTER_EVEN, mask, value);
-               break;
-       case V4L2_CID_CHROMA_AGC:
-               /* Do not allow chroma AGC to be enabled for SECAM */
-               value = ((ctl->value - c->off) << c->shift) & c->mask;
-               if (core->tvnorm & V4L2_STD_SECAM && value)
-                       return -EINVAL;
+               value = 0x3f - (ctrl->val & 0x3f);
                break;
        default:
-               value = ((ctl->value - c->off) << c->shift) & c->mask;
+               value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
                break;
        }
        dprintk(1,"set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
-                               ctl->id, c->v.name, ctl->value, c->reg, value,
-                               mask, c->sreg ? " [shadowed]" : "");
-       if (c->sreg) {
-               cx_sandor(c->sreg, c->reg, mask, value);
-       } else {
-               cx_andor(c->reg, mask, value);
-       }
+                               ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
+                               mask, cc->sreg ? " [shadowed]" : "");
+       if (cc->sreg)
+               cx_sandor(cc->sreg, cc->reg, mask, value);
+       else
+               cx_andor(cc->reg, mask, value);
        return 0;
 }
-EXPORT_SYMBOL(cx88_set_control);
-
-static void init_controls(struct cx88_core *core)
-{
-       struct v4l2_control ctrl;
-       int i;
-
-       for (i = 0; i < CX8800_CTLS; i++) {
-               ctrl.id=cx8800_ctls[i].v.id;
-               ctrl.value=cx8800_ctls[i].v.default_value;
-
-               cx88_set_control(core, &ctrl);
-       }
-}
 
 /* ------------------------------------------------------------------ */
 /* VIDEO IOCTLS                                                       */
@@ -1124,15 +1005,17 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct cx8800_fh  *fh   = priv;
+       struct cx8800_dev *dev = fh->dev;
 
-       f->fmt.pix.width        = fh->width;
-       f->fmt.pix.height       = fh->height;
+       f->fmt.pix.width        = dev->width;
+       f->fmt.pix.height       = dev->height;
        f->fmt.pix.field        = fh->vidq.field;
-       f->fmt.pix.pixelformat  = fh->fmt->fourcc;
+       f->fmt.pix.pixelformat  = dev->fmt->fourcc;
        f->fmt.pix.bytesperline =
-               (f->fmt.pix.width * fh->fmt->depth) >> 3;
+               (f->fmt.pix.width * dev->fmt->depth) >> 3;
        f->fmt.pix.sizeimage =
                f->fmt.pix.height * f->fmt.pix.bytesperline;
+       f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
        return 0;
 }
 
@@ -1184,33 +1067,54 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct cx8800_fh  *fh   = priv;
+       struct cx8800_dev *dev = fh->dev;
        int err = vidioc_try_fmt_vid_cap (file,priv,f);
 
        if (0 != err)
                return err;
-       fh->fmt        = format_by_fourcc(f->fmt.pix.pixelformat);
-       fh->width      = f->fmt.pix.width;
-       fh->height     = f->fmt.pix.height;
+       dev->fmt        = format_by_fourcc(f->fmt.pix.pixelformat);
+       dev->width      = f->fmt.pix.width;
+       dev->height     = f->fmt.pix.height;
        fh->vidq.field = f->fmt.pix.field;
        return 0;
 }
 
-static int vidioc_querycap (struct file *file, void  *priv,
+void cx88_querycap(struct file *file, struct cx88_core *core,
+               struct v4l2_capability *cap)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       strlcpy(cap->card, core->board.name, sizeof(cap->card));
+       cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+       if (UNSET != core->board.tuner_type)
+               cap->device_caps |= V4L2_CAP_TUNER;
+       switch (vdev->vfl_type) {
+       case VFL_TYPE_RADIO:
+               cap->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
+               break;
+       case VFL_TYPE_GRABBER:
+               cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
+               break;
+       case VFL_TYPE_VBI:
+               cap->device_caps |= V4L2_CAP_VBI_CAPTURE;
+               break;
+       }
+       cap->capabilities = cap->device_caps | V4L2_CAP_VIDEO_CAPTURE |
+               V4L2_CAP_VBI_CAPTURE | V4L2_CAP_DEVICE_CAPS;
+       if (core->board.radio.type == CX88_RADIO)
+               cap->capabilities |= V4L2_CAP_RADIO;
+}
+EXPORT_SYMBOL(cx88_querycap);
+
+static int vidioc_querycap(struct file *file, void  *priv,
                                        struct v4l2_capability *cap)
 {
        struct cx8800_dev *dev  = ((struct cx8800_fh *)priv)->dev;
        struct cx88_core  *core = dev->core;
 
        strcpy(cap->driver, "cx8800");
-       strlcpy(cap->card, core->board.name, sizeof(cap->card));
-       sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
-       cap->capabilities =
-               V4L2_CAP_VIDEO_CAPTURE |
-               V4L2_CAP_READWRITE     |
-               V4L2_CAP_STREAMING     |
-               V4L2_CAP_VBI_CAPTURE;
-       if (UNSET != core->board.tuner_type)
-               cap->capabilities |= V4L2_CAP_TUNER;
+       sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+       cx88_querycap(file, core, cap);
        return 0;
 }
 
@@ -1228,69 +1132,67 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void  *priv,
 
 static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p)
 {
-       struct cx8800_fh  *fh   = priv;
-       return (videobuf_reqbufs(get_queue(fh), p));
+       return videobuf_reqbufs(get_queue(file), p);
 }
 
 static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p)
 {
-       struct cx8800_fh  *fh   = priv;
-       return (videobuf_querybuf(get_queue(fh), p));
+       return videobuf_querybuf(get_queue(file), p);
 }
 
 static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
 {
-       struct cx8800_fh  *fh   = priv;
-       return (videobuf_qbuf(get_queue(fh), p));
+       return videobuf_qbuf(get_queue(file), p);
 }
 
 static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
 {
-       struct cx8800_fh  *fh   = priv;
-       return (videobuf_dqbuf(get_queue(fh), p,
-                               file->f_flags & O_NONBLOCK));
+       return videobuf_dqbuf(get_queue(file), p,
+                               file->f_flags & O_NONBLOCK);
 }
 
 static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
 {
+       struct video_device *vdev = video_devdata(file);
        struct cx8800_fh  *fh   = priv;
        struct cx8800_dev *dev  = fh->dev;
 
-       /* We should remember that this driver also supports teletext,  */
-       /* so we have to test if the v4l2_buf_type is VBI capture data. */
-       if (unlikely((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
-                    (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)))
-               return -EINVAL;
-
-       if (unlikely(i != fh->type))
+       if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+           (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE))
                return -EINVAL;
 
-       if (unlikely(!res_get(dev,fh,get_ressource(fh))))
+       if (unlikely(!res_get(dev, fh, get_resource(file))))
                return -EBUSY;
-       return videobuf_streamon(get_queue(fh));
+       return videobuf_streamon(get_queue(file));
 }
 
 static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
 {
+       struct video_device *vdev = video_devdata(file);
        struct cx8800_fh  *fh   = priv;
        struct cx8800_dev *dev  = fh->dev;
        int               err, res;
 
-       if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
-           (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
+       if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+           (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE))
                return -EINVAL;
 
-       if (i != fh->type)
-               return -EINVAL;
-
-       res = get_ressource(fh);
-       err = videobuf_streamoff(get_queue(fh));
+       res = get_resource(file);
+       err = videobuf_streamoff(get_queue(file));
        if (err < 0)
                return err;
        res_free(dev,fh,res);
        return 0;
 }
 
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
+{
+       struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+       *tvnorm = core->tvnorm;
+       return 0;
+}
+
 static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *tvnorms)
 {
        struct cx88_core  *core = ((struct cx8800_fh *)priv)->dev->core;
@@ -1327,8 +1229,8 @@ int cx88_enum_input (struct cx88_core  *core,struct v4l2_input *i)
        if ((CX88_VMUX_TELEVISION == INPUT(n).type) ||
            (CX88_VMUX_CABLE      == INPUT(n).type)) {
                i->type = V4L2_INPUT_TYPE_TUNER;
-               i->std = CX88_NORMS;
        }
+       i->std = CX88_NORMS;
        return 0;
 }
 EXPORT_SYMBOL(cx88_enum_input);
@@ -1354,6 +1256,8 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
 
        if (i >= 4)
                return -EINVAL;
+       if (0 == INPUT(i).type)
+               return -EINVAL;
 
        mutex_lock(&core->lock);
        cx88_newstation(core);
@@ -1362,35 +1266,6 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
        return 0;
 }
 
-
-
-static int vidioc_queryctrl (struct file *file, void *priv,
-                               struct v4l2_queryctrl *qctrl)
-{
-       struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
-
-       qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
-       if (unlikely(qctrl->id == 0))
-               return -EINVAL;
-       return cx8800_ctrl_query(core, qctrl);
-}
-
-static int vidioc_g_ctrl (struct file *file, void *priv,
-                               struct v4l2_control *ctl)
-{
-       struct cx88_core  *core = ((struct cx8800_fh *)priv)->dev->core;
-       return
-               cx88_get_control(core,ctl);
-}
-
-static int vidioc_s_ctrl (struct file *file, void *priv,
-                               struct v4l2_control *ctl)
-{
-       struct cx88_core  *core = ((struct cx8800_fh *)priv)->dev->core;
-       return
-               cx88_set_control(core,ctl);
-}
-
 static int vidioc_g_tuner (struct file *file, void *priv,
                                struct v4l2_tuner *t)
 {
@@ -1403,9 +1278,9 @@ static int vidioc_g_tuner (struct file *file, void *priv,
                return -EINVAL;
 
        strcpy(t->name, "Television");
-       t->type       = V4L2_TUNER_ANALOG_TV;
        t->capability = V4L2_TUNER_CAP_NORM;
        t->rangehigh  = 0xffffffffUL;
+       call_all(core, tuner, g_tuner, t);
 
        cx88_get_stereo(core ,t);
        reg = cx_read(MO_DEVICE_STATUS);
@@ -1435,9 +1310,9 @@ static int vidioc_g_frequency (struct file *file, void *priv,
 
        if (unlikely(UNSET == core->board.tuner_type))
                return -EINVAL;
+       if (f->tuner)
+               return -EINVAL;
 
-       /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */
-       f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
        f->frequency = core->freq;
 
        call_all(core, tuner, g_frequency, f);
@@ -1454,9 +1329,10 @@ int cx88_set_freq (struct cx88_core  *core,
                return -EINVAL;
 
        mutex_lock(&core->lock);
-       core->freq = f->frequency;
        cx88_newstation(core);
        call_all(core, tuner, s_frequency, f);
+       call_all(core, tuner, g_frequency, f);
+       core->freq = f->frequency;
 
        /* When changing channels it is required to reset TVAUDIO */
        msleep (10);
@@ -1474,13 +1350,17 @@ static int vidioc_s_frequency (struct file *file, void *priv,
        struct cx8800_fh  *fh   = priv;
        struct cx88_core  *core = fh->dev->core;
 
-       if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
-               return -EINVAL;
-       if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
-               return -EINVAL;
+       return cx88_set_freq(core, f);
+}
 
-       return
-               cx88_set_freq (core,f);
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+                               struct v4l2_dbg_chip_ident *chip)
+{
+       if (!v4l2_chip_match_host(&chip->match))
+               return -EINVAL;
+       chip->revision = 0;
+       chip->ident = V4L2_IDENT_UNKNOWN;
+       return 0;
 }
 
 #ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -1513,19 +1393,6 @@ static int vidioc_s_register (struct file *file, void *fh,
 /* RADIO ESPECIFIC IOCTLS                                      */
 /* ----------------------------------------------------------- */
 
-static int radio_querycap (struct file *file, void  *priv,
-                                       struct v4l2_capability *cap)
-{
-       struct cx8800_dev *dev  = ((struct cx8800_fh *)priv)->dev;
-       struct cx88_core  *core = dev->core;
-
-       strcpy(cap->driver, "cx8800");
-       strlcpy(cap->card, core->board.name, sizeof(cap->card));
-       sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci));
-       cap->capabilities = V4L2_CAP_TUNER;
-       return 0;
-}
-
 static int radio_g_tuner (struct file *file, void *priv,
                                struct v4l2_tuner *t)
 {
@@ -1535,32 +1402,11 @@ static int radio_g_tuner (struct file *file, void *priv,
                return -EINVAL;
 
        strcpy(t->name, "Radio");
-       t->type = V4L2_TUNER_RADIO;
 
        call_all(core, tuner, g_tuner, t);
        return 0;
 }
 
-static int radio_enum_input (struct file *file, void *priv,
-                               struct v4l2_input *i)
-{
-       if (i->index != 0)
-               return -EINVAL;
-       strcpy(i->name,"Radio");
-       i->type = V4L2_INPUT_TYPE_TUNER;
-
-       return 0;
-}
-
-static int radio_g_audio (struct file *file, void *priv, struct v4l2_audio *a)
-{
-       if (unlikely(a->index))
-               return -EINVAL;
-
-       strcpy(a->name,"Radio");
-       return 0;
-}
-
 /* FIXME: Should add a standard for radio */
 
 static int radio_s_tuner (struct file *file, void *priv,
@@ -1570,46 +1416,14 @@ static int radio_s_tuner (struct file *file, void *priv,
 
        if (0 != t->index)
                return -EINVAL;
+       if (t->audmode > V4L2_TUNER_MODE_STEREO)
+               t->audmode = V4L2_TUNER_MODE_STEREO;
 
        call_all(core, tuner, s_tuner, t);
 
        return 0;
 }
 
-static int radio_s_audio (struct file *file, void *fh,
-                         struct v4l2_audio *a)
-{
-       return 0;
-}
-
-static int radio_s_input (struct file *file, void *fh, unsigned int i)
-{
-       return 0;
-}
-
-static int radio_queryctrl (struct file *file, void *priv,
-                           struct v4l2_queryctrl *c)
-{
-       int i;
-
-       if (c->id <  V4L2_CID_BASE ||
-               c->id >= V4L2_CID_LASTP1)
-               return -EINVAL;
-       if (c->id == V4L2_CID_AUDIO_MUTE ||
-               c->id == V4L2_CID_AUDIO_VOLUME ||
-               c->id == V4L2_CID_AUDIO_BALANCE) {
-               for (i = 0; i < CX8800_CTLS; i++) {
-                       if (cx8800_ctls[i].v.id == c->id)
-                               break;
-               }
-               if (i == CX8800_CTLS)
-                       return -EINVAL;
-               *c = cx8800_ctls[i].v;
-       } else
-               *c = no_ctl;
-       return 0;
-}
-
 /* ----------------------------------------------------------- */
 
 static void cx8800_vid_timeout(unsigned long data)
@@ -1752,63 +1566,89 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
        .vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
        .vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
        .vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
-       .vidioc_g_fmt_vbi_cap     = cx8800_vbi_fmt,
-       .vidioc_try_fmt_vbi_cap   = cx8800_vbi_fmt,
-       .vidioc_s_fmt_vbi_cap     = cx8800_vbi_fmt,
        .vidioc_reqbufs       = vidioc_reqbufs,
        .vidioc_querybuf      = vidioc_querybuf,
        .vidioc_qbuf          = vidioc_qbuf,
        .vidioc_dqbuf         = vidioc_dqbuf,
+       .vidioc_g_std         = vidioc_g_std,
        .vidioc_s_std         = vidioc_s_std,
        .vidioc_enum_input    = vidioc_enum_input,
        .vidioc_g_input       = vidioc_g_input,
        .vidioc_s_input       = vidioc_s_input,
-       .vidioc_queryctrl     = vidioc_queryctrl,
-       .vidioc_g_ctrl        = vidioc_g_ctrl,
-       .vidioc_s_ctrl        = vidioc_s_ctrl,
        .vidioc_streamon      = vidioc_streamon,
        .vidioc_streamoff     = vidioc_streamoff,
        .vidioc_g_tuner       = vidioc_g_tuner,
        .vidioc_s_tuner       = vidioc_s_tuner,
        .vidioc_g_frequency   = vidioc_g_frequency,
        .vidioc_s_frequency   = vidioc_s_frequency,
+       .vidioc_subscribe_event      = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event    = v4l2_event_unsubscribe,
+       .vidioc_g_chip_ident  = vidioc_g_chip_ident,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        .vidioc_g_register    = vidioc_g_register,
        .vidioc_s_register    = vidioc_s_register,
 #endif
 };
 
-static struct video_device cx8800_vbi_template;
-
 static const struct video_device cx8800_video_template = {
        .name                 = "cx8800-video",
        .fops                 = &video_fops,
        .ioctl_ops            = &video_ioctl_ops,
        .tvnorms              = CX88_NORMS,
-       .current_norm         = V4L2_STD_NTSC_M,
+};
+
+static const struct v4l2_ioctl_ops vbi_ioctl_ops = {
+       .vidioc_querycap      = vidioc_querycap,
+       .vidioc_g_fmt_vbi_cap     = cx8800_vbi_fmt,
+       .vidioc_try_fmt_vbi_cap   = cx8800_vbi_fmt,
+       .vidioc_s_fmt_vbi_cap     = cx8800_vbi_fmt,
+       .vidioc_reqbufs       = vidioc_reqbufs,
+       .vidioc_querybuf      = vidioc_querybuf,
+       .vidioc_qbuf          = vidioc_qbuf,
+       .vidioc_dqbuf         = vidioc_dqbuf,
+       .vidioc_g_std         = vidioc_g_std,
+       .vidioc_s_std         = vidioc_s_std,
+       .vidioc_enum_input    = vidioc_enum_input,
+       .vidioc_g_input       = vidioc_g_input,
+       .vidioc_s_input       = vidioc_s_input,
+       .vidioc_streamon      = vidioc_streamon,
+       .vidioc_streamoff     = vidioc_streamoff,
+       .vidioc_g_tuner       = vidioc_g_tuner,
+       .vidioc_s_tuner       = vidioc_s_tuner,
+       .vidioc_g_frequency   = vidioc_g_frequency,
+       .vidioc_s_frequency   = vidioc_s_frequency,
+       .vidioc_g_chip_ident  = vidioc_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .vidioc_g_register    = vidioc_g_register,
+       .vidioc_s_register    = vidioc_s_register,
+#endif
+};
+
+static const struct video_device cx8800_vbi_template = {
+       .name                 = "cx8800-vbi",
+       .fops                 = &video_fops,
+       .ioctl_ops            = &vbi_ioctl_ops,
+       .tvnorms              = CX88_NORMS,
 };
 
 static const struct v4l2_file_operations radio_fops =
 {
        .owner         = THIS_MODULE,
        .open          = video_open,
+       .poll          = v4l2_ctrl_poll,
        .release       = video_release,
        .unlocked_ioctl = video_ioctl2,
 };
 
 static const struct v4l2_ioctl_ops radio_ioctl_ops = {
-       .vidioc_querycap      = radio_querycap,
+       .vidioc_querycap      = vidioc_querycap,
        .vidioc_g_tuner       = radio_g_tuner,
-       .vidioc_enum_input    = radio_enum_input,
-       .vidioc_g_audio       = radio_g_audio,
        .vidioc_s_tuner       = radio_s_tuner,
-       .vidioc_s_audio       = radio_s_audio,
-       .vidioc_s_input       = radio_s_input,
-       .vidioc_queryctrl     = radio_queryctrl,
-       .vidioc_g_ctrl        = vidioc_g_ctrl,
-       .vidioc_s_ctrl        = vidioc_s_ctrl,
        .vidioc_g_frequency   = vidioc_g_frequency,
        .vidioc_s_frequency   = vidioc_s_frequency,
+       .vidioc_subscribe_event      = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event    = v4l2_event_unsubscribe,
+       .vidioc_g_chip_ident  = vidioc_g_chip_ident,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        .vidioc_g_register    = vidioc_g_register,
        .vidioc_s_register    = vidioc_s_register,
@@ -1821,6 +1661,14 @@ static const struct video_device cx8800_radio_template = {
        .ioctl_ops            = &radio_ioctl_ops,
 };
 
+static const struct v4l2_ctrl_ops cx8800_ctrl_vid_ops = {
+       .s_ctrl = cx8800_s_vid_ctrl,
+};
+
+static const struct v4l2_ctrl_ops cx8800_ctrl_aud_ops = {
+       .s_ctrl = cx8800_s_aud_ctrl,
+};
+
 /* ----------------------------------------------------------- */
 
 static void cx8800_unregister_video(struct cx8800_dev *dev)
@@ -1853,8 +1701,8 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
 {
        struct cx8800_dev *dev;
        struct cx88_core *core;
-
        int err;
+       int i;
 
        dev = kzalloc(sizeof(*dev),GFP_KERNEL);
        if (NULL == dev)
@@ -1888,14 +1736,9 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
                goto fail_core;
        }
 
-       /* Initialize VBI template */
-       memcpy( &cx8800_vbi_template, &cx8800_video_template,
-               sizeof(cx8800_vbi_template) );
-       strcpy(cx8800_vbi_template.name,"cx8800-vbi");
-
        /* initialize driver struct */
        spin_lock_init(&dev->slock);
-       core->tvnorm = cx8800_video_template.current_norm;
+       core->tvnorm = V4L2_STD_NTSC_M;
 
        /* init video dma queues */
        INIT_LIST_HEAD(&dev->vidq.active);
@@ -1925,6 +1768,35 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
        }
        cx_set(MO_PCI_INTMSK, core->pci_irqmask);
 
+       for (i = 0; i < CX8800_AUD_CTLS; i++) {
+               const struct cx88_ctrl *cc = &cx8800_aud_ctls[i];
+               struct v4l2_ctrl *vc;
+
+               vc = v4l2_ctrl_new_std(&core->audio_hdl, &cx8800_ctrl_aud_ops,
+                       cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value);
+               if (vc == NULL) {
+                       err = core->audio_hdl.error;
+                       goto fail_core;
+               }
+               vc->priv = (void *)cc;
+       }
+
+       for (i = 0; i < CX8800_VID_CTLS; i++) {
+               const struct cx88_ctrl *cc = &cx8800_vid_ctls[i];
+               struct v4l2_ctrl *vc;
+
+               vc = v4l2_ctrl_new_std(&core->video_hdl, &cx8800_ctrl_vid_ops,
+                       cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value);
+               if (vc == NULL) {
+                       err = core->video_hdl.error;
+                       goto fail_core;
+               }
+               vc->priv = (void *)cc;
+               if (vc->id == V4L2_CID_CHROMA_AGC)
+                       core->chroma_agc = vc;
+       }
+       v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl);
+
        /* load and configure helper modules */
 
        if (core->board.audio_chip == V4L2_IDENT_WM8775) {
@@ -1942,8 +1814,10 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
 
                sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap,
                                &wm8775_info, NULL);
-               if (sd != NULL)
+               if (sd != NULL) {
+                       core->sd_wm8775 = sd;
                        sd->grp_id = WM8775_GID;
+               }
        }
 
        if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) {
@@ -1971,16 +1845,22 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
        /* Sets device info at pci_dev */
        pci_set_drvdata(pci_dev, dev);
 
+       dev->width   = 320;
+       dev->height  = 240;
+       dev->fmt     = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+
        /* initial device configuration */
        mutex_lock(&core->lock);
        cx88_set_tvnorm(core, core->tvnorm);
-       init_controls(core);
+       v4l2_ctrl_handler_setup(&core->video_hdl);
+       v4l2_ctrl_handler_setup(&core->audio_hdl);
        cx88_video_mux(core, 0);
 
        /* register v4l devices */
        dev->video_dev = cx88_vdev_init(core,dev->pci,
                                        &cx8800_video_template,"video");
        video_set_drvdata(dev->video_dev, dev);
+       dev->video_dev->ctrl_handler = &core->video_hdl;
        err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
                                    video_nr[core->nr]);
        if (err < 0) {
@@ -2007,6 +1887,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
                dev->radio_dev = cx88_vdev_init(core,dev->pci,
                                                &cx8800_radio_template,"radio");
                video_set_drvdata(dev->radio_dev, dev);
+               dev->radio_dev->ctrl_handler = &core->audio_hdl;
                err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
                                            radio_nr[core->nr]);
                if (err < 0) {
index c9659def2a787d79ecfa97c5cc56ff62b4521e47..0cae0fd9e1647d50f73b4826c2a35861f71a771c 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/kdev_t.h>
 
 #include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
 #include <media/tuner.h>
 #include <media/tveeprom.h>
 #include <media/videobuf-dma-sg.h>
@@ -115,15 +116,6 @@ struct cx8800_fmt {
        u32   cxformat;
 };
 
-struct cx88_ctrl {
-       struct v4l2_queryctrl  v;
-       u32                    off;
-       u32                    reg;
-       u32                    sreg;
-       u32                    mask;
-       u32                    shift;
-};
-
 /* ----------------------------------------------------------- */
 /* SRAM memory management data (see cx88-core.c)               */
 
@@ -359,6 +351,10 @@ struct cx88_core {
 
        /* config info -- analog */
        struct v4l2_device         v4l2_dev;
+       struct v4l2_ctrl_handler   video_hdl;
+       struct v4l2_ctrl           *chroma_agc;
+       struct v4l2_ctrl_handler   audio_hdl;
+       struct v4l2_subdev         *sd_wm8775;
        struct i2c_client          *i2c_rtc;
        unsigned int               boardnr;
        struct cx88_board          board;
@@ -409,8 +405,6 @@ static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev)
        return container_of(v4l2_dev, struct cx88_core, v4l2_dev);
 }
 
-#define WM8775_GID     (1 << 0)
-
 #define call_hw(core, grpid, o, f, args...) \
        do {                                                    \
                if (!core->i2c_rc) {                            \
@@ -424,6 +418,36 @@ static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev)
 
 #define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args)
 
+#define WM8775_GID      (1 << 0)
+
+#define wm8775_s_ctrl(core, id, val) \
+       do {                                                                    \
+               struct v4l2_ctrl *ctrl_ =                                       \
+                       v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id);      \
+               if (ctrl_ && !core->i2c_rc) {                                   \
+                       if (core->gate_ctrl)                                    \
+                               core->gate_ctrl(core, 1);                       \
+                       v4l2_ctrl_s_ctrl(ctrl_, val);                           \
+                       if (core->gate_ctrl)                                    \
+                               core->gate_ctrl(core, 0);                       \
+               }                                                               \
+       } while (0)
+
+#define wm8775_g_ctrl(core, id) \
+       ({                                                                      \
+               struct v4l2_ctrl *ctrl_ =                                       \
+                       v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id);      \
+               s32 val = 0;                                                    \
+               if (ctrl_ && !core->i2c_rc) {                                   \
+                       if (core->gate_ctrl)                                    \
+                               core->gate_ctrl(core, 1);                       \
+                       val = v4l2_ctrl_g_ctrl(ctrl_);                          \
+                       if (core->gate_ctrl)                                    \
+                               core->gate_ctrl(core, 0);                       \
+               }                                                               \
+               val;                                                            \
+       })
+
 struct cx8800_dev;
 struct cx8802_dev;
 
@@ -431,19 +455,11 @@ struct cx8802_dev;
 /* function 0: video stuff                                     */
 
 struct cx8800_fh {
+       struct v4l2_fh             fh;
        struct cx8800_dev          *dev;
-       enum v4l2_buf_type         type;
-       int                        radio;
        unsigned int               resources;
 
-       /* video overlay */
-       struct v4l2_window         win;
-       struct v4l2_clip           *clips;
-       unsigned int               nclips;
-
        /* video capture */
-       const struct cx8800_fmt    *fmt;
-       unsigned int               width,height;
        struct videobuf_queue      vidq;
 
        /* vbi capture */
@@ -468,6 +484,8 @@ struct cx8800_dev {
        struct pci_dev             *pci;
        unsigned char              pci_rev,pci_lat;
 
+       const struct cx8800_fmt    *fmt;
+       unsigned int               width, height;
 
        /* capture queues */
        struct cx88_dmaqueue       vidq;
@@ -488,6 +506,7 @@ struct cx8800_dev {
 /* function 2: mpeg stuff                                      */
 
 struct cx8802_fh {
+       struct v4l2_fh             fh;
        struct cx8802_dev          *dev;
        struct videobuf_queue      mpegq;
 };
@@ -552,7 +571,7 @@ struct cx8802_dev {
        unsigned char              mpeg_active; /* nonzero if mpeg encoder is active */
 
        /* mpeg params */
-       struct cx2341x_mpeg_params params;
+       struct cx2341x_handler     cxhdl;
 #endif
 
 #if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
@@ -722,11 +741,8 @@ void cx8802_cancel_buffers(struct cx8802_dev *dev);
 
 /* ----------------------------------------------------------- */
 /* cx88-video.c*/
-extern const u32 cx88_user_ctrls[];
-extern int cx8800_ctrl_query(struct cx88_core *core,
-                            struct v4l2_queryctrl *qctrl);
 int cx88_enum_input (struct cx88_core  *core,struct v4l2_input *i);
 int cx88_set_freq (struct cx88_core  *core,struct v4l2_frequency *f);
-int cx88_get_control(struct cx88_core *core, struct v4l2_control *ctl);
-int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl);
 int cx88_video_mux(struct cx88_core *core, unsigned int input);
+void cx88_querycap(struct file *file, struct cx88_core *core,
+               struct v4l2_capability *cap);
index 9337b5605c906272a12c6c1acf6a1e7001366412..52c5ca68cb3d38941acd97c1e00c7ae5f3048ba2 100644 (file)
@@ -1,30 +1,34 @@
-config DISPLAY_DAVINCI_DM646X_EVM
-       tristate "DM646x EVM Video Display"
-       depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM
-       select VIDEOBUF_DMA_CONTIG
+config VIDEO_DAVINCI_VPIF_DISPLAY
+       tristate "DM646x/DA850/OMAPL138 EVM Video Display"
+       depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM)
+       select VIDEOBUF2_DMA_CONTIG
        select VIDEO_DAVINCI_VPIF
-       select VIDEO_ADV7343
-       select VIDEO_THS7303
+       select VIDEO_ADV7343 if VIDEO_HELPER_CHIPS_AUTO
+       select VIDEO_THS7303 if VIDEO_HELPER_CHIPS_AUTO
        help
-         Support for DM6467 based display device.
+         Enables Davinci VPIF module used for display devices.
+         This module is common for following DM6467/DA850/OMAPL138
+         based display devices.
 
          To compile this driver as a module, choose M here: the
          module will be called vpif_display.
 
-config CAPTURE_DAVINCI_DM646X_EVM
-       tristate "DM646x EVM Video Capture"
-       depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM
-       select VIDEOBUF_DMA_CONTIG
+config VIDEO_DAVINCI_VPIF_CAPTURE
+       tristate "DM646x/DA850/OMAPL138 EVM Video Capture"
+       depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM)
+       select VIDEOBUF2_DMA_CONTIG
        select VIDEO_DAVINCI_VPIF
        help
-         Support for DM6467 based capture device.
+         Enables Davinci VPIF module used for captur devices.
+         This module is common for following DM6467/DA850/OMAPL138
+         based capture devices.
 
          To compile this driver as a module, choose M here: the
          module will be called vpif_capture.
 
 config VIDEO_DAVINCI_VPIF
        tristate "DaVinci VPIF Driver"
-       depends on DISPLAY_DAVINCI_DM646X_EVM
+       depends on VIDEO_DAVINCI_VPIF_DISPLAY || VIDEO_DAVINCI_VPIF_CAPTURE
        help
          Support for DaVinci VPIF Driver.
 
index ae7dafb689ab9917678f16a81fb00072f337bff4..74ed92d09257c6ca828467202c817af627c64e74 100644 (file)
@@ -5,10 +5,10 @@
 # VPIF
 obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o
 
-#DM646x EVM Display driver
-obj-$(CONFIG_DISPLAY_DAVINCI_DM646X_EVM) += vpif_display.o
-#DM646x EVM Capture driver
-obj-$(CONFIG_CAPTURE_DAVINCI_DM646X_EVM) += vpif_capture.o
+#VPIF Display driver
+obj-$(CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY) += vpif_display.o
+#VPIF Capture driver
+obj-$(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE) += vpif_capture.o
 
 # Capture: DM6446 and DM355
 obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o
index e106b72810a947f7d78b0f01681fe02c1d6d2934..6fe7034bea7c427e9da4503ea42b4b83991bb1ec 100644 (file)
@@ -1083,7 +1083,7 @@ vpbe_display_s_dv_preset(struct file *file, void *priv,
        }
 
        /* Set the given standard in the encoder */
-       if (NULL != vpbe_dev->ops.s_dv_preset)
+       if (!vpbe_dev->ops.s_dv_preset)
                return -EINVAL;
 
        ret = vpbe_dev->ops.s_dv_preset(vpbe_dev, preset);
@@ -1517,6 +1517,8 @@ static int vpbe_display_g_register(struct file *file, void *priv,
                        struct v4l2_dbg_register *reg)
 {
        struct v4l2_dbg_match *match = &reg->match;
+       struct vpbe_fh *fh = file->private_data;
+       struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
 
        if (match->type >= 2) {
                v4l2_subdev_call(vpbe_dev->venc,
index af9680273ff9723b56f2285532a084dd2bbc49c2..b3637aff8fee85be4eff1d5e23eeb5c6881efcdf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * vpif - DM646x Video Port Interface driver
+ * vpif - Video Port Interface driver
  * VPIF is a receiver and transmitter for video data. It has two channels(0, 1)
  * that receiveing video byte stream and two channels(2, 3) for video output.
  * The hardware supports SDTV, HDTV formats, raw data capture.
@@ -23,6 +23,8 @@
 #include <linux/spinlock.h>
 #include <linux/kernel.h>
 #include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
 #include <mach/hardware.h>
 
 #include "vpif.h"
@@ -40,6 +42,7 @@ static struct resource        *res;
 spinlock_t vpif_lock;
 
 void __iomem *vpif_base;
+struct clk *vpif_clk;
 
 /**
  * ch_params: video standard configuration parameters for vpif
@@ -346,7 +349,7 @@ static void config_vpif_params(struct vpif_params *vpifparams,
 
                        value = regr(reg);
                        /* Set data width */
-                       value &= ((~(unsigned int)(0x3)) <<
+                       value &= ~(0x3u <<
                                        VPIF_CH_DATA_WIDTH_BIT);
                        value |= ((vpifparams->params.data_sz) <<
                                                     VPIF_CH_DATA_WIDTH_BIT);
@@ -434,10 +437,19 @@ static int __init vpif_probe(struct platform_device *pdev)
                goto fail;
        }
 
+       vpif_clk = clk_get(&pdev->dev, "vpif");
+       if (IS_ERR(vpif_clk)) {
+               status = PTR_ERR(vpif_clk);
+               goto clk_fail;
+       }
+       clk_enable(vpif_clk);
+
        spin_lock_init(&vpif_lock);
        dev_info(&pdev->dev, "vpif probe success\n");
        return 0;
 
+clk_fail:
+       iounmap(vpif_base);
 fail:
        release_mem_region(res->start, res_len);
        return status;
@@ -445,15 +457,44 @@ fail:
 
 static int __devexit vpif_remove(struct platform_device *pdev)
 {
+       if (vpif_clk) {
+               clk_disable(vpif_clk);
+               clk_put(vpif_clk);
+       }
+
        iounmap(vpif_base);
        release_mem_region(res->start, res_len);
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int vpif_suspend(struct device *dev)
+{
+       clk_disable(vpif_clk);
+       return 0;
+}
+
+static int vpif_resume(struct device *dev)
+{
+       clk_enable(vpif_clk);
+       return 0;
+}
+
+static const struct dev_pm_ops vpif_pm = {
+       .suspend        = vpif_suspend,
+       .resume         = vpif_resume,
+};
+
+#define vpif_pm_ops (&vpif_pm)
+#else
+#define vpif_pm_ops NULL
+#endif
+
 static struct platform_driver vpif_driver = {
        .driver = {
                .name   = "vpif",
                .owner = THIS_MODULE,
+               .pm     = vpif_pm_ops,
        },
        .remove = __devexit_p(vpif_remove),
        .probe = vpif_probe,
index 8bcac65f9294e4abc4c886441e887fe0f3b35453..c2ce4d97c279cd8aaf24983da4ea29124a6f48ce 100644 (file)
@@ -211,6 +211,12 @@ static inline void vpif_clr_bit(u32 reg, u32 bit)
 #define VPIF_CH3_INT_CTRL_SHIFT        (6)
 #define VPIF_CH_INT_CTRL_SHIFT (6)
 
+#define VPIF_CH2_CLIP_ANC_EN   14
+#define VPIF_CH2_CLIP_ACTIVE_EN        13
+
+#define VPIF_CH3_CLIP_ANC_EN   14
+#define VPIF_CH3_CLIP_ACTIVE_EN        13
+
 /* enabled interrupt on both the fields on vpid_ch0_ctrl register */
 #define channel0_intr_assert() (regw((regr(VPIF_CH0_CTRL)|\
        (VPIF_INT_BOTH << VPIF_CH0_INT_CTRL_SHIFT)), VPIF_CH0_CTRL))
@@ -515,6 +521,30 @@ static inline void channel3_raw_enable(int enable, u8 index)
                vpif_clr_bit(VPIF_CH3_CTRL, mask);
 }
 
+/* function to enable clipping (for both active and blanking regions) on ch 2 */
+static inline void channel2_clipping_enable(int enable)
+{
+       if (enable) {
+               vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN);
+               vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN);
+       } else {
+               vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN);
+               vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN);
+       }
+}
+
+/* function to enable clipping (for both active and blanking regions) on ch 2 */
+static inline void channel3_clipping_enable(int enable)
+{
+       if (enable) {
+               vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN);
+               vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN);
+       } else {
+               vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN);
+               vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN);
+       }
+}
+
 /* inline function to set buffer addresses in case of Y/C non mux mode */
 static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma,
                                                 unsigned long btm_strt_luma,
@@ -569,6 +599,21 @@ static inline void ch3_set_vbi_addr(unsigned long top_strt_luma,
        regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_VANC);
 }
 
+static inline int vpif_intr_status(int channel)
+{
+       int status = 0;
+       int mask;
+
+       if (channel < 0 || channel > 3)
+               return 0;
+
+       mask = 1 << channel;
+       status = regr(VPIF_STATUS) & mask;
+       regw(status, VPIF_STATUS_CLR);
+
+       return status;
+}
+
 #define VPIF_MAX_NAME  (30)
 
 /* This structure will store size parameters as per the mode selected by user */
index 96046957bf21cb3832375215bd8dc13a4cb70ca9..266025e5d81d424b90ceedd4a1de6fe9c1d016e8 100644 (file)
@@ -80,108 +80,45 @@ static struct vpif_config_params config_params = {
 /* global variables */
 static struct vpif_device vpif_obj = { {NULL} };
 static struct device *vpif_dev;
-
-/**
- * vpif_uservirt_to_phys : translate user/virtual address to phy address
- * @virtp: user/virtual address
- *
- * This inline function is used to convert user space virtual address to
- * physical address.
- */
-static inline u32 vpif_uservirt_to_phys(u32 virtp)
-{
-       unsigned long physp = 0;
-       struct mm_struct *mm = current->mm;
-       struct vm_area_struct *vma;
-
-       vma = find_vma(mm, virtp);
-
-       /* For kernel direct-mapped memory, take the easy way */
-       if (virtp >= PAGE_OFFSET)
-               physp = virt_to_phys((void *)virtp);
-       else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff))
-               /**
-                * this will catch, kernel-allocated, mmaped-to-usermode
-                * addresses
-                */
-               physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
-       else {
-               /* otherwise, use get_user_pages() for general userland pages */
-               int res, nr_pages = 1;
-                       struct page *pages;
-
-               down_read(&current->mm->mmap_sem);
-
-               res = get_user_pages(current, current->mm,
-                                    virtp, nr_pages, 1, 0, &pages, NULL);
-               up_read(&current->mm->mmap_sem);
-
-               if (res == nr_pages)
-                       physp = __pa(page_address(&pages[0]) +
-                                    (virtp & ~PAGE_MASK));
-               else {
-                       vpif_err("get_user_pages failed\n");
-                       return 0;
-               }
-       }
-       return physp;
-}
+static void vpif_calculate_offsets(struct channel_obj *ch);
+static void vpif_config_addr(struct channel_obj *ch, int muxmode);
 
 /**
  * buffer_prepare :  callback function for buffer prepare
- * @q : buffer queue ptr
- * @vb: ptr to video buffer
- * @field: field info
+ * @vb: ptr to vb2_buffer
  *
- * This is the callback function for buffer prepare when videobuf_qbuf()
+ * This is the callback function for buffer prepare when vb2_qbuf()
  * function is called. The buffer is prepared and user space virtual address
  * or user address is converted into  physical address
  */
-static int vpif_buffer_prepare(struct videobuf_queue *q,
-                              struct videobuf_buffer *vb,
-                              enum v4l2_field field)
+static int vpif_buffer_prepare(struct vb2_buffer *vb)
 {
        /* Get the file handle object and channel object */
-       struct vpif_fh *fh = q->priv_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+       struct vb2_queue *q = vb->vb2_queue;
        struct channel_obj *ch = fh->channel;
        struct common_obj *common;
        unsigned long addr;
 
-
        vpif_dbg(2, debug, "vpif_buffer_prepare\n");
 
        common = &ch->common[VPIF_VIDEO_INDEX];
 
-       /* If buffer is not initialized, initialize it */
-       if (VIDEOBUF_NEEDS_INIT == vb->state) {
-               vb->width = common->width;
-               vb->height = common->height;
-               vb->size = vb->width * vb->height;
-               vb->field = field;
-       }
-       vb->state = VIDEOBUF_PREPARED;
-       /**
-        * if user pointer memory mechanism is used, get the physical
-        * address of the buffer
-        */
-       if (V4L2_MEMORY_USERPTR == common->memory) {
-               if (0 == vb->baddr) {
-                       vpif_dbg(1, debug, "buffer address is 0\n");
-                       return -EINVAL;
-
-               }
-               vb->boff = vpif_uservirt_to_phys(vb->baddr);
-               if (!IS_ALIGNED(vb->boff, 8))
+       if (vb->state != VB2_BUF_STATE_ACTIVE &&
+               vb->state != VB2_BUF_STATE_PREPARED) {
+               vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
+               if (vb2_plane_vaddr(vb, 0) &&
+               vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
                        goto exit;
-       }
+               addr = vb2_dma_contig_plane_dma_addr(vb, 0);
 
-       addr = vb->boff;
-       if (q->streaming) {
-               if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
-                   !IS_ALIGNED((addr + common->ybtm_off), 8) ||
-                   !IS_ALIGNED((addr + common->ctop_off), 8) ||
-                   !IS_ALIGNED((addr + common->cbtm_off), 8))
-                       goto exit;
+               if (q->streaming) {
+                       if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
+                               !IS_ALIGNED((addr + common->ybtm_off), 8) ||
+                               !IS_ALIGNED((addr + common->ctop_off), 8) ||
+                               !IS_ALIGNED((addr + common->cbtm_off), 8))
+                               goto exit;
+               }
        }
        return 0;
 exit:
@@ -190,49 +127,79 @@ exit:
 }
 
 /**
- * vpif_buffer_setup : Callback function for buffer setup.
- * @q: buffer queue ptr
- * @count: number of buffers
- * @size: size of the buffer
+ * vpif_buffer_queue_setup : Callback function for buffer setup.
+ * @vq: vb2_queue ptr
+ * @fmt: v4l2 format
+ * @nbuffers: ptr to number of buffers requested by application
+ * @nplanes:: contains number of distinct video planes needed to hold a frame
+ * @sizes[]: contains the size (in bytes) of each plane.
+ * @alloc_ctxs: ptr to allocation context
  *
  * This callback function is called when reqbuf() is called to adjust
  * the buffer count and buffer size
  */
-static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count,
-                            unsigned int *size)
+static int vpif_buffer_queue_setup(struct vb2_queue *vq,
+                               const struct v4l2_format *fmt,
+                               unsigned int *nbuffers, unsigned int *nplanes,
+                               unsigned int sizes[], void *alloc_ctxs[])
 {
        /* Get the file handle object and channel object */
-       struct vpif_fh *fh = q->priv_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
        struct channel_obj *ch = fh->channel;
        struct common_obj *common;
+       unsigned long size;
 
        common = &ch->common[VPIF_VIDEO_INDEX];
 
        vpif_dbg(2, debug, "vpif_buffer_setup\n");
 
        /* If memory type is not mmap, return */
-       if (V4L2_MEMORY_MMAP != common->memory)
-               return 0;
+       if (V4L2_MEMORY_MMAP == common->memory) {
+               /* Calculate the size of the buffer */
+               size = config_params.channel_bufsize[ch->channel_id];
+               /*
+                * Checking if the buffer size exceeds the available buffer
+                * ycmux_mode = 0 means 1 channel mode HD and
+                * ycmux_mode = 1 means 2 channels mode SD
+                */
+               if (ch->vpifparams.std_info.ycmux_mode == 0) {
+                       if (config_params.video_limit[ch->channel_id])
+                               while (size * *nbuffers >
+                                       (config_params.video_limit[0]
+                                               + config_params.video_limit[1]))
+                                       (*nbuffers)--;
+               } else {
+                       if (config_params.video_limit[ch->channel_id])
+                               while (size * *nbuffers >
+                               config_params.video_limit[ch->channel_id])
+                                       (*nbuffers)--;
+               }
+
+       } else {
+               size = common->fmt.fmt.pix.sizeimage;
+       }
+
+       if (*nbuffers < config_params.min_numbuffers)
+               *nbuffers = config_params.min_numbuffers;
 
-       /* Calculate the size of the buffer */
-       *size = config_params.channel_bufsize[ch->channel_id];
+       *nplanes = 1;
+       sizes[0] = size;
+       alloc_ctxs[0] = common->alloc_ctx;
 
-       if (*count < config_params.min_numbuffers)
-               *count = config_params.min_numbuffers;
        return 0;
 }
 
 /**
  * vpif_buffer_queue : Callback function to add buffer to DMA queue
- * @q: ptr to videobuf_queue
- * @vb: ptr to videobuf_buffer
+ * @vb: ptr to vb2_buffer
  */
-static void vpif_buffer_queue(struct videobuf_queue *q,
-                             struct videobuf_buffer *vb)
+static void vpif_buffer_queue(struct vb2_buffer *vb)
 {
        /* Get the file handle object and channel object */
-       struct vpif_fh *fh = q->priv_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
        struct channel_obj *ch = fh->channel;
+       struct vpif_cap_buffer *buf = container_of(vb,
+                               struct vpif_cap_buffer, vb);
        struct common_obj *common;
 
        common = &ch->common[VPIF_VIDEO_INDEX];
@@ -240,43 +207,189 @@ static void vpif_buffer_queue(struct videobuf_queue *q,
        vpif_dbg(2, debug, "vpif_buffer_queue\n");
 
        /* add the buffer to the DMA queue */
-       list_add_tail(&vb->queue, &common->dma_queue);
-       /* Change state of the buffer */
-       vb->state = VIDEOBUF_QUEUED;
+       list_add_tail(&buf->list, &common->dma_queue);
 }
 
 /**
- * vpif_buffer_release : Callback function to free buffer
- * @q: buffer queue ptr
- * @vb: ptr to video buffer
+ * vpif_buf_cleanup : Callback function to free buffer
+ * @vb: ptr to vb2_buffer
  *
- * This function is called from the videobuf layer to free memory
+ * This function is called from the videobuf2 layer to free memory
  * allocated to  the buffers
  */
-static void vpif_buffer_release(struct videobuf_queue *q,
-                               struct videobuf_buffer *vb)
+static void vpif_buf_cleanup(struct vb2_buffer *vb)
 {
        /* Get the file handle object and channel object */
-       struct vpif_fh *fh = q->priv_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+       struct vpif_cap_buffer *buf = container_of(vb,
+                                       struct vpif_cap_buffer, vb);
        struct channel_obj *ch = fh->channel;
        struct common_obj *common;
+       unsigned long flags;
 
        common = &ch->common[VPIF_VIDEO_INDEX];
 
-       videobuf_dma_contig_free(q, vb);
-       vb->state = VIDEOBUF_NEEDS_INIT;
+       spin_lock_irqsave(&common->irqlock, flags);
+       if (vb->state == VB2_BUF_STATE_ACTIVE)
+               list_del_init(&buf->list);
+       spin_unlock_irqrestore(&common->irqlock, flags);
+
 }
 
-static struct videobuf_queue_ops video_qops = {
-       .buf_setup = vpif_buffer_setup,
-       .buf_prepare = vpif_buffer_prepare,
-       .buf_queue = vpif_buffer_queue,
-       .buf_release = vpif_buffer_release,
-};
+static void vpif_wait_prepare(struct vb2_queue *vq)
+{
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
+       struct channel_obj *ch = fh->channel;
+       struct common_obj *common;
+
+       common = &ch->common[VPIF_VIDEO_INDEX];
+       mutex_unlock(&common->lock);
+}
+
+static void vpif_wait_finish(struct vb2_queue *vq)
+{
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
+       struct channel_obj *ch = fh->channel;
+       struct common_obj *common;
+
+       common = &ch->common[VPIF_VIDEO_INDEX];
+       mutex_lock(&common->lock);
+}
+
+static int vpif_buffer_init(struct vb2_buffer *vb)
+{
+       struct vpif_cap_buffer *buf = container_of(vb,
+                                       struct vpif_cap_buffer, vb);
+
+       INIT_LIST_HEAD(&buf->list);
+
+       return 0;
+}
 
 static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] =
        { {1, 1} };
 
+static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+       struct vpif_capture_config *vpif_config_data =
+                                       vpif_dev->platform_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
+       struct channel_obj *ch = fh->channel;
+       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+       struct vpif_params *vpif = &ch->vpifparams;
+       unsigned long addr = 0;
+       int ret;
+
+               /* If buffer queue is empty, return error */
+       if (list_empty(&common->dma_queue)) {
+               vpif_dbg(1, debug, "buffer queue is empty\n");
+               return -EIO;
+       }
+
+       /* Get the next frame from the buffer queue */
+       common->cur_frm = common->next_frm = list_entry(common->dma_queue.next,
+                                   struct vpif_cap_buffer, list);
+       /* Remove buffer from the buffer queue */
+       list_del(&common->cur_frm->list);
+       /* Mark state of the current frame to active */
+       common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+       /* Initialize field_id and started member */
+       ch->field_id = 0;
+       common->started = 1;
+       addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
+
+       /* Calculate the offset for Y and C data in the buffer */
+       vpif_calculate_offsets(ch);
+
+       if ((vpif->std_info.frm_fmt &&
+           ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) &&
+            (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) ||
+           (!vpif->std_info.frm_fmt &&
+            (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
+               vpif_dbg(1, debug, "conflict in field format and std format\n");
+               return -EINVAL;
+       }
+
+       /* configure 1 or 2 channel mode */
+       ret = vpif_config_data->setup_input_channel_mode
+                                       (vpif->std_info.ycmux_mode);
+
+       if (ret < 0) {
+               vpif_dbg(1, debug, "can't set vpif channel mode\n");
+               return ret;
+       }
+
+       /* Call vpif_set_params function to set the parameters and addresses */
+       ret = vpif_set_video_params(vpif, ch->channel_id);
+
+       if (ret < 0) {
+               vpif_dbg(1, debug, "can't set video params\n");
+               return ret;
+       }
+
+       common->started = ret;
+       vpif_config_addr(ch, ret);
+
+       common->set_addr(addr + common->ytop_off,
+                        addr + common->ybtm_off,
+                        addr + common->ctop_off,
+                        addr + common->cbtm_off);
+
+       /**
+        * Set interrupt for both the fields in VPIF Register enable channel in
+        * VPIF register
+        */
+       if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) {
+               channel0_intr_assert();
+               channel0_intr_enable(1);
+               enable_channel0(1);
+       }
+       if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
+           (common->started == 2)) {
+               channel1_intr_assert();
+               channel1_intr_enable(1);
+               enable_channel1(1);
+       }
+       channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
+
+       return 0;
+}
+
+/* abort streaming and wait for last buffer */
+static int vpif_stop_streaming(struct vb2_queue *vq)
+{
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
+       struct channel_obj *ch = fh->channel;
+       struct common_obj *common;
+
+       if (!vb2_is_streaming(vq))
+               return 0;
+
+       common = &ch->common[VPIF_VIDEO_INDEX];
+
+       /* release all active buffers */
+       while (!list_empty(&common->dma_queue)) {
+               common->next_frm = list_entry(common->dma_queue.next,
+                                               struct vpif_cap_buffer, list);
+               list_del(&common->next_frm->list);
+               vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
+       }
+
+       return 0;
+}
+
+static struct vb2_ops video_qops = {
+       .queue_setup            = vpif_buffer_queue_setup,
+       .wait_prepare           = vpif_wait_prepare,
+       .wait_finish            = vpif_wait_finish,
+       .buf_init               = vpif_buffer_init,
+       .buf_prepare            = vpif_buffer_prepare,
+       .start_streaming        = vpif_start_streaming,
+       .stop_streaming         = vpif_stop_streaming,
+       .buf_cleanup            = vpif_buf_cleanup,
+       .buf_queue              = vpif_buffer_queue,
+};
+
 /**
  * vpif_process_buffer_complete: process a completed buffer
  * @common: ptr to common channel object
@@ -287,9 +400,9 @@ static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] =
  */
 static void vpif_process_buffer_complete(struct common_obj *common)
 {
-       do_gettimeofday(&common->cur_frm->ts);
-       common->cur_frm->state = VIDEOBUF_DONE;
-       wake_up_interruptible(&common->cur_frm->done);
+       do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp);
+       vb2_buffer_done(&common->cur_frm->vb,
+                                           VB2_BUF_STATE_DONE);
        /* Make curFrm pointing to nextFrm */
        common->cur_frm = common->next_frm;
 }
@@ -307,14 +420,11 @@ static void vpif_schedule_next_buffer(struct common_obj *common)
        unsigned long addr = 0;
 
        common->next_frm = list_entry(common->dma_queue.next,
-                                    struct videobuf_buffer, queue);
+                                    struct vpif_cap_buffer, list);
        /* Remove that buffer from the buffer queue */
-       list_del(&common->next_frm->queue);
-       common->next_frm->state = VIDEOBUF_ACTIVE;
-       if (V4L2_MEMORY_USERPTR == common->memory)
-               addr = common->next_frm->boff;
-       else
-               addr = videobuf_to_dma_contig(common->next_frm);
+       list_del(&common->next_frm->list);
+       common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+       addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
 
        /* Set top and bottom field addresses in VPIF registers */
        common->set_addr(addr + common->ytop_off,
@@ -341,6 +451,9 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
        int fid = -1, i;
 
        channel_id = *(int *)(dev_id);
+       if (!vpif_intr_status(channel_id))
+               return IRQ_NONE;
+
        ch = dev->dev[channel_id];
 
        field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
@@ -485,10 +598,7 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
        } else
                vid_ch->buf_field = common->fmt.fmt.pix.field;
 
-       if (V4L2_MEMORY_USERPTR == common->memory)
-               sizeimage = common->fmt.fmt.pix.sizeimage;
-       else
-               sizeimage = config_params.channel_bufsize[ch->channel_id];
+       sizeimage = common->fmt.fmt.pix.sizeimage;
 
        hpitch = common->fmt.fmt.pix.bytesperline;
        vpitch = sizeimage / (hpitch * 2);
@@ -640,10 +750,7 @@ static int vpif_check_format(struct channel_obj *ch,
                hpitch = vpif_params->std_info.width;
        }
 
-       if (V4L2_MEMORY_USERPTR == common->memory)
-               sizeimage = pixfmt->sizeimage;
-       else
-               sizeimage = config_params.channel_bufsize[ch->channel_id];
+       sizeimage = pixfmt->sizeimage;
 
        vpitch = sizeimage / (hpitch * 2);
 
@@ -703,7 +810,7 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode)
 }
 
 /**
- * vpfe_mmap : It is used to map kernel space buffers into user spaces
+ * vpif_mmap : It is used to map kernel space buffers into user spaces
  * @filep: file pointer
  * @vma: ptr to vm_area_struct
  */
@@ -716,7 +823,7 @@ static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
 
        vpif_dbg(2, debug, "vpif_mmap\n");
 
-       return videobuf_mmap_mapper(&common->buffer_queue, vma);
+       return vb2_mmap(&common->buffer_queue, vma);
 }
 
 /**
@@ -733,7 +840,7 @@ static unsigned int vpif_poll(struct file *filep, poll_table * wait)
        vpif_dbg(2, debug, "vpif_poll\n");
 
        if (common->started)
-               return videobuf_poll_stream(filep, &common->buffer_queue, wait);
+               return vb2_poll(&common->buffer_queue, filep, wait);
        return 0;
 }
 
@@ -812,7 +919,7 @@ static int vpif_open(struct file *filep)
  * vpif_release : function to clean up file close
  * @filep: file pointer
  *
- * This function deletes buffer queue, frees the buffers and the vpfe file
+ * This function deletes buffer queue, frees the buffers and the vpif file
  * handle
  */
 static int vpif_release(struct file *filep)
@@ -841,8 +948,8 @@ static int vpif_release(struct file *filep)
                }
                common->started = 0;
                /* Free buffers allocated */
-               videobuf_queue_cancel(&common->buffer_queue);
-               videobuf_mmap_free(&common->buffer_queue);
+               vb2_queue_release(&common->buffer_queue);
+               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
        }
 
        /* Decrement channel usrs counter */
@@ -872,6 +979,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
        struct channel_obj *ch = fh->channel;
        struct common_obj *common;
        u8 index = 0;
+       struct vb2_queue *q;
 
        vpif_dbg(2, debug, "vpif_reqbufs\n");
 
@@ -887,7 +995,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
                }
        }
 
-       if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type)
+       if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type || !vpif_dev)
                return -EINVAL;
 
        index = VPIF_VIDEO_INDEX;
@@ -897,14 +1005,21 @@ static int vpif_reqbufs(struct file *file, void *priv,
        if (0 != common->io_usrs)
                return -EBUSY;
 
-       /* Initialize videobuf queue as per the buffer type */
-       videobuf_queue_dma_contig_init(&common->buffer_queue,
-                                           &video_qops, NULL,
-                                           &common->irqlock,
-                                           reqbuf->type,
-                                           common->fmt.fmt.pix.field,
-                                           sizeof(struct videobuf_buffer), fh,
-                                           &common->lock);
+       /* Initialize videobuf2 queue as per the buffer type */
+       common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
+       if (!common->alloc_ctx) {
+               vpif_err("Failed to get the context\n");
+               return -EINVAL;
+       }
+       q = &common->buffer_queue;
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       q->io_modes = VB2_MMAP | VB2_USERPTR;
+       q->drv_priv = fh;
+       q->ops = &video_qops;
+       q->mem_ops = &vb2_dma_contig_memops;
+       q->buf_struct_size = sizeof(struct vpif_cap_buffer);
+
+       vb2_queue_init(q);
 
        /* Set io allowed member of file handle to TRUE */
        fh->io_allowed[index] = 1;
@@ -915,7 +1030,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
        INIT_LIST_HEAD(&common->dma_queue);
 
        /* Allocate buffers */
-       return videobuf_reqbufs(&common->buffer_queue, reqbuf);
+       return vb2_reqbufs(&common->buffer_queue, reqbuf);
 }
 
 /**
@@ -941,7 +1056,7 @@ static int vpif_querybuf(struct file *file, void *priv,
                return -EINVAL;
        }
 
-       return videobuf_querybuf(&common->buffer_queue, buf);
+       return vb2_querybuf(&common->buffer_queue, buf);
 }
 
 /**
@@ -957,10 +1072,6 @@ static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
        struct channel_obj *ch = fh->channel;
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
        struct v4l2_buffer tbuf = *buf;
-       struct videobuf_buffer *buf1;
-       unsigned long addr = 0;
-       unsigned long flags;
-       int ret = 0;
 
        vpif_dbg(2, debug, "vpif_qbuf\n");
 
@@ -970,76 +1081,11 @@ static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
        }
 
        if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
-               vpif_err("fh io not allowed \n");
+               vpif_err("fh io not allowed\n");
                return -EACCES;
        }
 
-       if (!(list_empty(&common->dma_queue)) ||
-           (common->cur_frm != common->next_frm) ||
-           !common->started ||
-           (common->started && (0 == ch->field_id)))
-               return videobuf_qbuf(&common->buffer_queue, buf);
-
-       /* bufferqueue is empty store buffer address in VPIF registers */
-       mutex_lock(&common->buffer_queue.vb_lock);
-       buf1 = common->buffer_queue.bufs[tbuf.index];
-
-       if ((buf1->state == VIDEOBUF_QUEUED) ||
-           (buf1->state == VIDEOBUF_ACTIVE)) {
-               vpif_err("invalid state\n");
-               goto qbuf_exit;
-       }
-
-       switch (buf1->memory) {
-       case V4L2_MEMORY_MMAP:
-               if (buf1->baddr == 0)
-                       goto qbuf_exit;
-               break;
-
-       case V4L2_MEMORY_USERPTR:
-               if (tbuf.length < buf1->bsize)
-                       goto qbuf_exit;
-
-               if ((VIDEOBUF_NEEDS_INIT != buf1->state)
-                           && (buf1->baddr != tbuf.m.userptr)) {
-                       vpif_buffer_release(&common->buffer_queue, buf1);
-                       buf1->baddr = tbuf.m.userptr;
-               }
-               break;
-
-       default:
-               goto qbuf_exit;
-       }
-
-       local_irq_save(flags);
-       ret = vpif_buffer_prepare(&common->buffer_queue, buf1,
-                                       common->buffer_queue.field);
-       if (ret < 0) {
-               local_irq_restore(flags);
-               goto qbuf_exit;
-       }
-
-       buf1->state = VIDEOBUF_ACTIVE;
-
-       if (V4L2_MEMORY_USERPTR == common->memory)
-               addr = buf1->boff;
-       else
-               addr = videobuf_to_dma_contig(buf1);
-
-       common->next_frm = buf1;
-       common->set_addr(addr + common->ytop_off,
-                        addr + common->ybtm_off,
-                        addr + common->ctop_off,
-                        addr + common->cbtm_off);
-
-       local_irq_restore(flags);
-       list_add_tail(&buf1->stream, &common->buffer_queue.stream);
-       mutex_unlock(&common->buffer_queue.vb_lock);
-       return 0;
-
-qbuf_exit:
-       mutex_unlock(&common->buffer_queue.vb_lock);
-       return -EINVAL;
+       return vb2_qbuf(&common->buffer_queue, buf);
 }
 
 /**
@@ -1056,8 +1102,8 @@ static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
 
        vpif_dbg(2, debug, "vpif_dqbuf\n");
 
-       return videobuf_dqbuf(&common->buffer_queue, buf,
-                                       file->f_flags & O_NONBLOCK);
+       return vb2_dqbuf(&common->buffer_queue, buf,
+                        (file->f_flags & O_NONBLOCK));
 }
 
 /**
@@ -1070,13 +1116,11 @@ static int vpif_streamon(struct file *file, void *priv,
                                enum v4l2_buf_type buftype)
 {
 
-       struct vpif_capture_config *config = vpif_dev->platform_data;
        struct vpif_fh *fh = priv;
        struct channel_obj *ch = fh->channel;
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
        struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
        struct vpif_params *vpif;
-       unsigned long addr = 0;
        int ret = 0;
 
        vpif_dbg(2, debug, "vpif_streamon\n");
@@ -1122,95 +1166,13 @@ static int vpif_streamon(struct file *file, void *priv,
                return ret;
        }
 
-       /* Call videobuf_streamon to start streaming in videobuf */
-       ret = videobuf_streamon(&common->buffer_queue);
+       /* Call vb2_streamon to start streaming in videobuf2 */
+       ret = vb2_streamon(&common->buffer_queue, buftype);
        if (ret) {
-               vpif_dbg(1, debug, "videobuf_streamon\n");
+               vpif_dbg(1, debug, "vb2_streamon\n");
                return ret;
        }
 
-       /* If buffer queue is empty, return error */
-       if (list_empty(&common->dma_queue)) {
-               vpif_dbg(1, debug, "buffer queue is empty\n");
-               ret = -EIO;
-               goto exit;
-       }
-
-       /* Get the next frame from the buffer queue */
-       common->cur_frm = list_entry(common->dma_queue.next,
-                                   struct videobuf_buffer, queue);
-       common->next_frm = common->cur_frm;
-
-       /* Remove buffer from the buffer queue */
-       list_del(&common->cur_frm->queue);
-       /* Mark state of the current frame to active */
-       common->cur_frm->state = VIDEOBUF_ACTIVE;
-       /* Initialize field_id and started member */
-       ch->field_id = 0;
-       common->started = 1;
-
-       if (V4L2_MEMORY_USERPTR == common->memory)
-               addr = common->cur_frm->boff;
-       else
-               addr = videobuf_to_dma_contig(common->cur_frm);
-
-       /* Calculate the offset for Y and C data in the buffer */
-       vpif_calculate_offsets(ch);
-
-       if ((vpif->std_info.frm_fmt &&
-           ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) &&
-            (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) ||
-           (!vpif->std_info.frm_fmt &&
-            (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
-               vpif_dbg(1, debug, "conflict in field format and std format\n");
-               ret = -EINVAL;
-               goto exit;
-       }
-
-       /* configure 1 or 2 channel mode */
-       ret = config->setup_input_channel_mode(vpif->std_info.ycmux_mode);
-
-       if (ret < 0) {
-               vpif_dbg(1, debug, "can't set vpif channel mode\n");
-               goto exit;
-       }
-
-       /* Call vpif_set_params function to set the parameters and addresses */
-       ret = vpif_set_video_params(vpif, ch->channel_id);
-
-       if (ret < 0) {
-               vpif_dbg(1, debug, "can't set video params\n");
-               goto exit;
-       }
-
-       common->started = ret;
-       vpif_config_addr(ch, ret);
-
-       common->set_addr(addr + common->ytop_off,
-                        addr + common->ybtm_off,
-                        addr + common->ctop_off,
-                        addr + common->cbtm_off);
-
-       /**
-        * Set interrupt for both the fields in VPIF Register enable channel in
-        * VPIF register
-        */
-       if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) {
-               channel0_intr_assert();
-               channel0_intr_enable(1);
-               enable_channel0(1);
-       }
-       if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
-           (common->started == 2)) {
-               channel1_intr_assert();
-               channel1_intr_enable(1);
-               enable_channel1(1);
-       }
-       channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
-       return ret;
-
-exit:
-       videobuf_streamoff(&common->buffer_queue);
        return ret;
 }
 
@@ -1265,7 +1227,7 @@ static int vpif_streamoff(struct file *file, void *priv,
        if (ret && (ret != -ENOIOCTLCMD))
                vpif_dbg(1, debug, "stream off failed in subdev\n");
 
-       return videobuf_streamoff(&common->buffer_queue);
+       return vb2_streamoff(&common->buffer_queue, buftype);
 }
 
 /**
@@ -1679,7 +1641,7 @@ static int vpif_querycap(struct file *file, void  *priv,
 
        cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
        strlcpy(cap->driver, "vpif capture", sizeof(cap->driver));
-       strlcpy(cap->bus_info, "DM646x Platform", sizeof(cap->bus_info));
+       strlcpy(cap->bus_info, "VPIF Platform", sizeof(cap->bus_info));
        strlcpy(cap->card, config->card_name, sizeof(cap->card));
 
        return 0;
@@ -2168,6 +2130,7 @@ static __init int vpif_probe(struct platform_device *pdev)
        struct video_device *vfd;
        struct resource *res;
        int subdev_count;
+       size_t size;
 
        vpif_dev = &pdev->dev;
 
@@ -2186,8 +2149,8 @@ static __init int vpif_probe(struct platform_device *pdev)
        k = 0;
        while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) {
                for (i = res->start; i <= res->end; i++) {
-                       if (request_irq(i, vpif_channel_isr, IRQF_DISABLED,
-                                       "DM646x_Capture",
+                       if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
+                                       "VPIF_Capture",
                                (void *)(&vpif_obj.dev[k]->channel_id))) {
                                err = -EBUSY;
                                i--;
@@ -2216,12 +2179,29 @@ static __init int vpif_probe(struct platform_device *pdev)
                vfd->v4l2_dev = &vpif_obj.v4l2_dev;
                vfd->release = video_device_release;
                snprintf(vfd->name, sizeof(vfd->name),
-                        "DM646x_VPIFCapture_DRIVER_V%s",
+                        "VPIF_Capture_DRIVER_V%s",
                         VPIF_CAPTURE_VERSION);
                /* Set video_dev to the video device */
                ch->video_dev = vfd;
        }
 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res) {
+               size = resource_size(res);
+               /* The resources are divided into two equal memory and when we
+                * have HD output we can add them together
+                */
+               for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
+                       ch = vpif_obj.dev[j];
+                       ch->channel_id = j;
+                       /* only enabled if second resource exists */
+                       config_params.video_limit[ch->channel_id] = 0;
+                       if (size)
+                               config_params.video_limit[ch->channel_id] =
+                                                                       size/2;
+               }
+       }
+
        for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
                ch = vpif_obj.dev[j];
                ch->channel_id = j;
@@ -2275,8 +2255,7 @@ static __init int vpif_probe(struct platform_device *pdev)
                        vpif_obj.sd[i]->grp_id = 1 << i;
        }
 
-       v4l2_info(&vpif_obj.v4l2_dev,
-                       "DM646x VPIF capture driver initialized\n");
+       v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n");
        return 0;
 
 probe_subdev_out:
@@ -2333,26 +2312,70 @@ static int vpif_remove(struct platform_device *device)
        return 0;
 }
 
+#ifdef CONFIG_PM
 /**
  * vpif_suspend: vpif device suspend
- *
- * TODO: Add suspend code here
  */
-static int
-vpif_suspend(struct device *dev)
+static int vpif_suspend(struct device *dev)
 {
-       return -1;
+
+       struct common_obj *common;
+       struct channel_obj *ch;
+       int i;
+
+       for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+               /* Get the pointer to the channel object */
+               ch = vpif_obj.dev[i];
+               common = &ch->common[VPIF_VIDEO_INDEX];
+               mutex_lock(&common->lock);
+               if (ch->usrs && common->io_usrs) {
+                       /* Disable channel */
+                       if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
+                               enable_channel0(0);
+                               channel0_intr_enable(0);
+                       }
+                       if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
+                           common->started == 2) {
+                               enable_channel1(0);
+                               channel1_intr_enable(0);
+                       }
+               }
+               mutex_unlock(&common->lock);
+       }
+
+       return 0;
 }
 
-/**
+/*
  * vpif_resume: vpif device suspend
- *
- * TODO: Add resume code here
  */
-static int
-vpif_resume(struct device *dev)
+static int vpif_resume(struct device *dev)
 {
-       return -1;
+       struct common_obj *common;
+       struct channel_obj *ch;
+       int i;
+
+       for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+               /* Get the pointer to the channel object */
+               ch = vpif_obj.dev[i];
+               common = &ch->common[VPIF_VIDEO_INDEX];
+               mutex_lock(&common->lock);
+               if (ch->usrs && common->io_usrs) {
+                       /* Disable channel */
+                       if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
+                               enable_channel0(1);
+                               channel0_intr_enable(1);
+                       }
+                       if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
+                           common->started == 2) {
+                               enable_channel1(1);
+                               channel1_intr_enable(1);
+                       }
+               }
+               mutex_unlock(&common->lock);
+       }
+
+       return 0;
 }
 
 static const struct dev_pm_ops vpif_dev_pm_ops = {
@@ -2360,11 +2383,16 @@ static const struct dev_pm_ops vpif_dev_pm_ops = {
        .resume = vpif_resume,
 };
 
+#define vpif_pm_ops (&vpif_dev_pm_ops)
+#else
+#define vpif_pm_ops NULL
+#endif
+
 static __refdata struct platform_driver vpif_driver = {
        .driver = {
                .name   = "vpif_capture",
                .owner  = THIS_MODULE,
-               .pm = &vpif_dev_pm_ops,
+               .pm     = vpif_pm_ops,
        },
        .probe = vpif_probe,
        .remove = vpif_remove,
index a693d4ebda557ea0ee972813f8a5877d40e79efb..3511510f43ee32098732e0f480f1067579404a69 100644 (file)
@@ -26,7 +26,7 @@
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
 #include <media/videobuf-core.h>
-#include <media/videobuf-dma-contig.h>
+#include <media/videobuf2-dma-contig.h>
 #include <media/davinci/vpif_types.h>
 
 #include "vpif.h"
@@ -60,11 +60,16 @@ struct video_obj {
        u32 input_idx;
 };
 
+struct vpif_cap_buffer {
+       struct vb2_buffer vb;
+       struct list_head list;
+};
+
 struct common_obj {
        /* Pointer pointing to current v4l2_buffer */
-       struct videobuf_buffer *cur_frm;
+       struct vpif_cap_buffer *cur_frm;
        /* Pointer pointing to current v4l2_buffer */
-       struct videobuf_buffer *next_frm;
+       struct vpif_cap_buffer *next_frm;
        /*
         * This field keeps track of type of buffer exchange mechanism
         * user has selected
@@ -73,7 +78,9 @@ struct common_obj {
        /* Used to store pixel format */
        struct v4l2_format fmt;
        /* Buffer queue used in video-buf */
-       struct videobuf_queue buffer_queue;
+       struct vb2_queue buffer_queue;
+       /* allocator-specific contexts for each plane */
+       struct vb2_alloc_ctx *alloc_ctx;
        /* Queue of filled frames */
        struct list_head dma_queue;
        /* Used in video-buf */
@@ -151,6 +158,7 @@ struct vpif_config_params {
        u32 min_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
        u32 channel_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
        u8 default_device[VPIF_CAPTURE_NUM_CHANNELS];
+       u32 video_limit[VPIF_CAPTURE_NUM_CHANNELS];
        u8 max_device_type;
 };
 /* Struct which keeps track of the line numbers for the sliced vbi service */
index e6488ee7db1877510a490724c7311f5f98f16e78..e129c98921ad493b0b8ea90562d2878325218fc5 100644 (file)
@@ -46,7 +46,7 @@ MODULE_DESCRIPTION("TI DaVinci VPIF Display driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(VPIF_DISPLAY_VERSION);
 
-#define DM646X_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
+#define VPIF_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
 
 #define vpif_err(fmt, arg...)  v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
 #define vpif_dbg(level, debug, fmt, arg...)    \
@@ -82,89 +82,38 @@ static struct vpif_config_params config_params = {
 
 static struct vpif_device vpif_obj = { {NULL} };
 static struct device *vpif_dev;
+static void vpif_calculate_offsets(struct channel_obj *ch);
+static void vpif_config_addr(struct channel_obj *ch, int muxmode);
 
 /*
- * vpif_uservirt_to_phys: This function is used to convert user
- * space virtual address to physical address.
- */
-static u32 vpif_uservirt_to_phys(u32 virtp)
-{
-       struct mm_struct *mm = current->mm;
-       unsigned long physp = 0;
-       struct vm_area_struct *vma;
-
-       vma = find_vma(mm, virtp);
-
-       /* For kernel direct-mapped memory, take the easy way */
-       if (virtp >= PAGE_OFFSET) {
-               physp = virt_to_phys((void *)virtp);
-       } else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) {
-               /* this will catch, kernel-allocated, mmaped-to-usermode addr */
-               physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
-       } else {
-               /* otherwise, use get_user_pages() for general userland pages */
-               int res, nr_pages = 1;
-               struct page *pages;
-               down_read(&current->mm->mmap_sem);
-
-               res = get_user_pages(current, current->mm,
-                                    virtp, nr_pages, 1, 0, &pages, NULL);
-               up_read(&current->mm->mmap_sem);
-
-               if (res == nr_pages) {
-                       physp = __pa(page_address(&pages[0]) +
-                                                       (virtp & ~PAGE_MASK));
-               } else {
-                       vpif_err("get_user_pages failed\n");
-                       return 0;
-               }
-       }
-
-       return physp;
-}
-
-/*
- * buffer_prepare: This is the callback function called from videobuf_qbuf()
+ * buffer_prepare: This is the callback function called from vb2_qbuf()
  * function the buffer is prepared and user space virtual address is converted
  * into physical address
  */
-static int vpif_buffer_prepare(struct videobuf_queue *q,
-                              struct videobuf_buffer *vb,
-                              enum v4l2_field field)
+static int vpif_buffer_prepare(struct vb2_buffer *vb)
 {
-       struct vpif_fh *fh = q->priv_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+       struct vb2_queue *q = vb->vb2_queue;
        struct common_obj *common;
        unsigned long addr;
 
        common = &fh->channel->common[VPIF_VIDEO_INDEX];
-       if (VIDEOBUF_NEEDS_INIT == vb->state) {
-               vb->width       = common->width;
-               vb->height      = common->height;
-               vb->size        = vb->width * vb->height;
-               vb->field       = field;
-       }
-       vb->state = VIDEOBUF_PREPARED;
-
-       /* if user pointer memory mechanism is used, get the physical
-        * address of the buffer */
-       if (V4L2_MEMORY_USERPTR == common->memory) {
-               if (!vb->baddr) {
-                       vpif_err("buffer_address is 0\n");
-                       return -EINVAL;
-               }
-
-               vb->boff = vpif_uservirt_to_phys(vb->baddr);
-               if (!ISALIGNED(vb->boff))
+       if (vb->state != VB2_BUF_STATE_ACTIVE &&
+               vb->state != VB2_BUF_STATE_PREPARED) {
+               vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
+               if (vb2_plane_vaddr(vb, 0) &&
+               vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
                        goto buf_align_exit;
-       }
 
-       addr = vb->boff;
-       if (q->streaming && (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) {
-               if (!ISALIGNED(addr + common->ytop_off) ||
-                   !ISALIGNED(addr + common->ybtm_off) ||
-                   !ISALIGNED(addr + common->ctop_off) ||
-                   !ISALIGNED(addr + common->cbtm_off))
-                       goto buf_align_exit;
+               addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+               if (q->streaming &&
+                       (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) {
+                       if (!ISALIGNED(addr + common->ytop_off) ||
+                       !ISALIGNED(addr + common->ybtm_off) ||
+                       !ISALIGNED(addr + common->ctop_off) ||
+                       !ISALIGNED(addr + common->cbtm_off))
+                               goto buf_align_exit;
+               }
        }
        return 0;
 
@@ -174,86 +123,255 @@ buf_align_exit:
 }
 
 /*
- * vpif_buffer_setup: This function allocates memory for the buffers
+ * vpif_buffer_queue_setup: This function allocates memory for the buffers
  */
-static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count,
-                               unsigned int *size)
+static int vpif_buffer_queue_setup(struct vb2_queue *vq,
+                               const struct v4l2_format *fmt,
+                               unsigned int *nbuffers, unsigned int *nplanes,
+                               unsigned int sizes[], void *alloc_ctxs[])
 {
-       struct vpif_fh *fh = q->priv_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
        struct channel_obj *ch = fh->channel;
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+       unsigned long size;
+
+       if (V4L2_MEMORY_MMAP == common->memory) {
+               size = config_params.channel_bufsize[ch->channel_id];
+               /*
+               * Checking if the buffer size exceeds the available buffer
+               * ycmux_mode = 0 means 1 channel mode HD and
+               * ycmux_mode = 1 means 2 channels mode SD
+               */
+               if (ch->vpifparams.std_info.ycmux_mode == 0) {
+                       if (config_params.video_limit[ch->channel_id])
+                               while (size * *nbuffers >
+                                       (config_params.video_limit[0]
+                                               + config_params.video_limit[1]))
+                                       (*nbuffers)--;
+               } else {
+                       if (config_params.video_limit[ch->channel_id])
+                               while (size * *nbuffers >
+                               config_params.video_limit[ch->channel_id])
+                                       (*nbuffers)--;
+               }
+       } else {
+               size = common->fmt.fmt.pix.sizeimage;
+       }
 
-       if (V4L2_MEMORY_MMAP != common->memory)
-               return 0;
-
-       *size = config_params.channel_bufsize[ch->channel_id];
-       if (*count < config_params.min_numbuffers)
-               *count = config_params.min_numbuffers;
+       if (*nbuffers < config_params.min_numbuffers)
+                       *nbuffers = config_params.min_numbuffers;
 
+       *nplanes = 1;
+       sizes[0] = size;
+       alloc_ctxs[0] = common->alloc_ctx;
        return 0;
 }
 
 /*
  * vpif_buffer_queue: This function adds the buffer to DMA queue
  */
-static void vpif_buffer_queue(struct videobuf_queue *q,
-                             struct videobuf_buffer *vb)
+static void vpif_buffer_queue(struct vb2_buffer *vb)
 {
-       struct vpif_fh *fh = q->priv_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+       struct vpif_disp_buffer *buf = container_of(vb,
+                               struct vpif_disp_buffer, vb);
+       struct channel_obj *ch = fh->channel;
        struct common_obj *common;
 
-       common = &fh->channel->common[VPIF_VIDEO_INDEX];
+       common = &ch->common[VPIF_VIDEO_INDEX];
 
        /* add the buffer to the DMA queue */
-       list_add_tail(&vb->queue, &common->dma_queue);
-       vb->state = VIDEOBUF_QUEUED;
+       list_add_tail(&buf->list, &common->dma_queue);
 }
 
 /*
- * vpif_buffer_release: This function is called from the videobuf layer to
+ * vpif_buf_cleanup: This function is called from the videobuf2 layer to
  * free memory allocated to the buffers
  */
-static void vpif_buffer_release(struct videobuf_queue *q,
-                               struct videobuf_buffer *vb)
+static void vpif_buf_cleanup(struct vb2_buffer *vb)
+{
+       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+       struct vpif_disp_buffer *buf = container_of(vb,
+                                       struct vpif_disp_buffer, vb);
+       struct channel_obj *ch = fh->channel;
+       struct common_obj *common;
+       unsigned long flags;
+
+       common = &ch->common[VPIF_VIDEO_INDEX];
+
+       spin_lock_irqsave(&common->irqlock, flags);
+       if (vb->state == VB2_BUF_STATE_ACTIVE)
+               list_del_init(&buf->list);
+       spin_unlock_irqrestore(&common->irqlock, flags);
+}
+
+static void vpif_wait_prepare(struct vb2_queue *vq)
 {
-       struct vpif_fh *fh = q->priv_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
        struct channel_obj *ch = fh->channel;
        struct common_obj *common;
-       unsigned int buf_size = 0;
 
        common = &ch->common[VPIF_VIDEO_INDEX];
+       mutex_unlock(&common->lock);
+}
 
-       videobuf_dma_contig_free(q, vb);
-       vb->state = VIDEOBUF_NEEDS_INIT;
+static void vpif_wait_finish(struct vb2_queue *vq)
+{
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
+       struct channel_obj *ch = fh->channel;
+       struct common_obj *common;
 
-       if (V4L2_MEMORY_MMAP != common->memory)
-               return;
+       common = &ch->common[VPIF_VIDEO_INDEX];
+       mutex_lock(&common->lock);
+}
 
-       buf_size = config_params.channel_bufsize[ch->channel_id];
+static int vpif_buffer_init(struct vb2_buffer *vb)
+{
+       struct vpif_disp_buffer *buf = container_of(vb,
+                                       struct vpif_disp_buffer, vb);
+
+       INIT_LIST_HEAD(&buf->list);
+
+       return 0;
 }
 
-static struct videobuf_queue_ops video_qops = {
-       .buf_setup      = vpif_buffer_setup,
-       .buf_prepare    = vpif_buffer_prepare,
-       .buf_queue      = vpif_buffer_queue,
-       .buf_release    = vpif_buffer_release,
-};
 static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} };
 
+static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+       struct vpif_display_config *vpif_config_data =
+                                       vpif_dev->platform_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
+       struct channel_obj *ch = fh->channel;
+       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+       struct vpif_params *vpif = &ch->vpifparams;
+       unsigned long addr = 0;
+       int ret;
+
+       /* If buffer queue is empty, return error */
+       if (list_empty(&common->dma_queue)) {
+               vpif_err("buffer queue is empty\n");
+               return -EIO;
+       }
+
+       /* Get the next frame from the buffer queue */
+       common->next_frm = common->cur_frm =
+                           list_entry(common->dma_queue.next,
+                                      struct vpif_disp_buffer, list);
+
+       list_del(&common->cur_frm->list);
+       /* Mark state of the current frame to active */
+       common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+
+       /* Initialize field_id and started member */
+       ch->field_id = 0;
+       common->started = 1;
+       addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
+       /* Calculate the offset for Y and C data  in the buffer */
+       vpif_calculate_offsets(ch);
+
+       if ((ch->vpifparams.std_info.frm_fmt &&
+               ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE)
+               && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY)))
+               || (!ch->vpifparams.std_info.frm_fmt
+               && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
+               vpif_err("conflict in field format and std format\n");
+               return -EINVAL;
+       }
+
+       /* clock settings */
+       ret =
+           vpif_config_data->set_clock(ch->vpifparams.std_info.ycmux_mode,
+                                       ch->vpifparams.std_info.hd_sd);
+       if (ret < 0) {
+               vpif_err("can't set clock\n");
+               return ret;
+       }
+
+       /* set the parameters and addresses */
+       ret = vpif_set_video_params(vpif, ch->channel_id + 2);
+       if (ret < 0)
+               return ret;
+
+       common->started = ret;
+       vpif_config_addr(ch, ret);
+       common->set_addr((addr + common->ytop_off),
+                           (addr + common->ybtm_off),
+                           (addr + common->ctop_off),
+                           (addr + common->cbtm_off));
+
+       /* Set interrupt for both the fields in VPIF
+           Register enable channel in VPIF register */
+       if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
+               channel2_intr_assert();
+               channel2_intr_enable(1);
+               enable_channel2(1);
+               if (vpif_config_data->ch2_clip_en)
+                       channel2_clipping_enable(1);
+       }
+
+       if ((VPIF_CHANNEL3_VIDEO == ch->channel_id)
+               || (common->started == 2)) {
+               channel3_intr_assert();
+               channel3_intr_enable(1);
+               enable_channel3(1);
+               if (vpif_config_data->ch3_clip_en)
+                       channel3_clipping_enable(1);
+       }
+       channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
+
+       return 0;
+}
+
+/* abort streaming and wait for last buffer */
+static int vpif_stop_streaming(struct vb2_queue *vq)
+{
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
+       struct channel_obj *ch = fh->channel;
+       struct common_obj *common;
+
+       if (!vb2_is_streaming(vq))
+               return 0;
+
+       common = &ch->common[VPIF_VIDEO_INDEX];
+
+       /* release all active buffers */
+       while (!list_empty(&common->dma_queue)) {
+               common->next_frm = list_entry(common->dma_queue.next,
+                                               struct vpif_disp_buffer, list);
+               list_del(&common->next_frm->list);
+               vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
+       }
+
+       return 0;
+}
+
+static struct vb2_ops video_qops = {
+       .queue_setup            = vpif_buffer_queue_setup,
+       .wait_prepare           = vpif_wait_prepare,
+       .wait_finish            = vpif_wait_finish,
+       .buf_init               = vpif_buffer_init,
+       .buf_prepare            = vpif_buffer_prepare,
+       .start_streaming        = vpif_start_streaming,
+       .stop_streaming         = vpif_stop_streaming,
+       .buf_cleanup            = vpif_buf_cleanup,
+       .buf_queue              = vpif_buffer_queue,
+};
+
 static void process_progressive_mode(struct common_obj *common)
 {
        unsigned long addr = 0;
 
        /* Get the next buffer from buffer queue */
        common->next_frm = list_entry(common->dma_queue.next,
-                               struct videobuf_buffer, queue);
+                               struct vpif_disp_buffer, list);
        /* Remove that buffer from the buffer queue */
-       list_del(&common->next_frm->queue);
+       list_del(&common->next_frm->list);
        /* Mark status of the buffer as active */
-       common->next_frm->state = VIDEOBUF_ACTIVE;
+       common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
 
        /* Set top and bottom field addrs in VPIF registers */
-       addr = videobuf_to_dma_contig(common->next_frm);
+       addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
        common->set_addr(addr + common->ytop_off,
                                 addr + common->ybtm_off,
                                 addr + common->ctop_off,
@@ -271,11 +389,10 @@ static void process_interlaced_mode(int fid, struct common_obj *common)
                /* one frame is displayed If next frame is
                 *  available, release cur_frm and move on */
                /* Copy frame display time */
-               do_gettimeofday(&common->cur_frm->ts);
+               do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp);
                /* Change status of the cur_frm */
-               common->cur_frm->state = VIDEOBUF_DONE;
-               /* unlock semaphore on cur_frm */
-               wake_up_interruptible(&common->cur_frm->done);
+               vb2_buffer_done(&common->cur_frm->vb,
+                                           VB2_BUF_STATE_DONE);
                /* Make cur_frm pointing to next_frm */
                common->cur_frm = common->next_frm;
 
@@ -307,6 +424,9 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
        int channel_id = 0;
 
        channel_id = *(int *)(dev_id);
+       if (!vpif_intr_status(channel_id + 2))
+               return IRQ_NONE;
+
        ch = dev->dev[channel_id];
        field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
        for (i = 0; i < VPIF_NUMOBJECTS; i++) {
@@ -323,9 +443,10 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
                        if (!channel_first_int[i][channel_id]) {
                                /* Mark status of the cur_frm to
                                 * done and unlock semaphore on it */
-                               do_gettimeofday(&common->cur_frm->ts);
-                               common->cur_frm->state = VIDEOBUF_DONE;
-                               wake_up_interruptible(&common->cur_frm->done);
+                               do_gettimeofday(&common->cur_frm->vb.
+                                               v4l2_buf.timestamp);
+                               vb2_buffer_done(&common->cur_frm->vb,
+                                           VB2_BUF_STATE_DONE);
                                /* Make cur_frm pointing to next_frm */
                                common->cur_frm = common->next_frm;
                        }
@@ -443,10 +564,7 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
                vid_ch->buf_field = common->fmt.fmt.pix.field;
        }
 
-       if (V4L2_MEMORY_USERPTR == common->memory)
-               sizeimage = common->fmt.fmt.pix.sizeimage;
-       else
-               sizeimage = config_params.channel_bufsize[ch->channel_id];
+       sizeimage = common->fmt.fmt.pix.sizeimage;
 
        hpitch = common->fmt.fmt.pix.bytesperline;
        vpitch = sizeimage / (hpitch * 2);
@@ -523,10 +641,7 @@ static int vpif_check_format(struct channel_obj *ch,
        if (pixfmt->bytesperline <= 0)
                goto invalid_pitch_exit;
 
-       if (V4L2_MEMORY_USERPTR == common->memory)
-               sizeimage = pixfmt->sizeimage;
-       else
-               sizeimage = config_params.channel_bufsize[ch->channel_id];
+       sizeimage = pixfmt->sizeimage;
 
        if (vpif_update_resolution(ch))
                return -EINVAL;
@@ -583,7 +698,7 @@ static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
 
        vpif_dbg(2, debug, "vpif_mmap\n");
 
-       return videobuf_mmap_mapper(&common->buffer_queue, vma);
+       return vb2_mmap(&common->buffer_queue, vma);
 }
 
 /*
@@ -596,7 +711,7 @@ static unsigned int vpif_poll(struct file *filep, poll_table *wait)
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
 
        if (common->started)
-               return videobuf_poll_stream(filep, &common->buffer_queue, wait);
+               return vb2_poll(&common->buffer_queue, filep, wait);
 
        return 0;
 }
@@ -665,9 +780,11 @@ static int vpif_release(struct file *filep)
                        channel3_intr_enable(0);
                }
                common->started = 0;
+
                /* Free buffers allocated */
-               videobuf_queue_cancel(&common->buffer_queue);
-               videobuf_mmap_free(&common->buffer_queue);
+               vb2_queue_release(&common->buffer_queue);
+               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
+
                common->numbuffers =
                    config_params.numbuffers[ch->channel_id];
        }
@@ -806,6 +923,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
        struct channel_obj *ch = fh->channel;
        struct common_obj *common;
        enum v4l2_field field;
+       struct vb2_queue *q;
        u8 index = 0;
 
        /* This file handle has not initialized the channel,
@@ -825,9 +943,8 @@ static int vpif_reqbufs(struct file *file, void *priv,
 
        common = &ch->common[index];
 
-       if (common->fmt.type != reqbuf->type)
+       if (common->fmt.type != reqbuf->type || !vpif_dev)
                return -EINVAL;
-
        if (0 != common->io_usrs)
                return -EBUSY;
 
@@ -839,14 +956,21 @@ static int vpif_reqbufs(struct file *file, void *priv,
        } else {
                field = V4L2_VBI_INTERLACED;
        }
+       /* Initialize videobuf2 queue as per the buffer type */
+       common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
+       if (!common->alloc_ctx) {
+               vpif_err("Failed to get the context\n");
+               return -EINVAL;
+       }
+       q = &common->buffer_queue;
+       q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+       q->io_modes = VB2_MMAP | VB2_USERPTR;
+       q->drv_priv = fh;
+       q->ops = &video_qops;
+       q->mem_ops = &vb2_dma_contig_memops;
+       q->buf_struct_size = sizeof(struct vpif_disp_buffer);
 
-       /* Initialize videobuf queue as per the buffer type */
-       videobuf_queue_dma_contig_init(&common->buffer_queue,
-                                           &video_qops, NULL,
-                                           &common->irqlock,
-                                           reqbuf->type, field,
-                                           sizeof(struct videobuf_buffer), fh,
-                                           &common->lock);
+       vb2_queue_init(q);
 
        /* Set io allowed member of file handle to TRUE */
        fh->io_allowed[index] = 1;
@@ -855,9 +979,8 @@ static int vpif_reqbufs(struct file *file, void *priv,
        /* Store type of memory requested in channel object */
        common->memory = reqbuf->memory;
        INIT_LIST_HEAD(&common->dma_queue);
-
        /* Allocate buffers */
-       return videobuf_reqbufs(&common->buffer_queue, reqbuf);
+       return vb2_reqbufs(&common->buffer_queue, reqbuf);
 }
 
 static int vpif_querybuf(struct file *file, void *priv,
@@ -870,22 +993,25 @@ static int vpif_querybuf(struct file *file, void *priv,
        if (common->fmt.type != tbuf->type)
                return -EINVAL;
 
-       return videobuf_querybuf(&common->buffer_queue, tbuf);
+       return vb2_querybuf(&common->buffer_queue, tbuf);
 }
 
 static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
 {
+       struct vpif_fh *fh = NULL;
+       struct channel_obj *ch = NULL;
+       struct common_obj *common = NULL;
 
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       struct v4l2_buffer tbuf = *buf;
-       struct videobuf_buffer *buf1;
-       unsigned long addr = 0;
-       unsigned long flags;
-       int ret = 0;
+       if (!buf || !priv)
+               return -EINVAL;
+
+       fh = priv;
+       ch = fh->channel;
+       if (!ch)
+               return -EINVAL;
 
-       if (common->fmt.type != tbuf.type)
+       common = &(ch->common[VPIF_VIDEO_INDEX]);
+       if (common->fmt.type != buf->type)
                return -EINVAL;
 
        if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
@@ -893,73 +1019,7 @@ static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
                return -EACCES;
        }
 
-       if (!(list_empty(&common->dma_queue)) ||
-           (common->cur_frm != common->next_frm) ||
-           !(common->started) ||
-           (common->started && (0 == ch->field_id)))
-               return videobuf_qbuf(&common->buffer_queue, buf);
-
-       /* bufferqueue is empty store buffer address in VPIF registers */
-       mutex_lock(&common->buffer_queue.vb_lock);
-       buf1 = common->buffer_queue.bufs[tbuf.index];
-       if (buf1->memory != tbuf.memory) {
-               vpif_err("invalid buffer type\n");
-               goto qbuf_exit;
-       }
-
-       if ((buf1->state == VIDEOBUF_QUEUED) ||
-           (buf1->state == VIDEOBUF_ACTIVE)) {
-               vpif_err("invalid state\n");
-               goto qbuf_exit;
-       }
-
-       switch (buf1->memory) {
-       case V4L2_MEMORY_MMAP:
-               if (buf1->baddr == 0)
-                       goto qbuf_exit;
-               break;
-
-       case V4L2_MEMORY_USERPTR:
-               if (tbuf.length < buf1->bsize)
-                       goto qbuf_exit;
-
-               if ((VIDEOBUF_NEEDS_INIT != buf1->state)
-                           && (buf1->baddr != tbuf.m.userptr)) {
-                       vpif_buffer_release(&common->buffer_queue, buf1);
-                       buf1->baddr = tbuf.m.userptr;
-               }
-               break;
-
-       default:
-               goto qbuf_exit;
-       }
-
-       local_irq_save(flags);
-       ret = vpif_buffer_prepare(&common->buffer_queue, buf1,
-                                       common->buffer_queue.field);
-       if (ret < 0) {
-               local_irq_restore(flags);
-               goto qbuf_exit;
-       }
-
-       buf1->state = VIDEOBUF_ACTIVE;
-       addr = buf1->boff;
-       common->next_frm = buf1;
-       if (tbuf.type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
-               common->set_addr((addr + common->ytop_off),
-                                (addr + common->ybtm_off),
-                                (addr + common->ctop_off),
-                                (addr + common->cbtm_off));
-       }
-
-       local_irq_restore(flags);
-       list_add_tail(&buf1->stream, &common->buffer_queue.stream);
-       mutex_unlock(&common->buffer_queue.vb_lock);
-       return 0;
-
-qbuf_exit:
-       mutex_unlock(&common->buffer_queue.vb_lock);
-       return -EINVAL;
+       return vb2_qbuf(&common->buffer_queue, buf);
 }
 
 static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
@@ -969,7 +1029,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
        int ret = 0;
 
-       if (!(*std_id & DM646X_V4L2_STD))
+       if (!(*std_id & VPIF_V4L2_STD))
                return -EINVAL;
 
        if (common->started) {
@@ -1026,7 +1086,7 @@ static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
        struct channel_obj *ch = fh->channel;
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
 
-       return videobuf_dqbuf(&common->buffer_queue, p,
+       return vb2_dqbuf(&common->buffer_queue, p,
                                        (file->f_flags & O_NONBLOCK));
 }
 
@@ -1037,10 +1097,6 @@ static int vpif_streamon(struct file *file, void *priv,
        struct channel_obj *ch = fh->channel;
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
        struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
-       struct vpif_params *vpif = &ch->vpifparams;
-       struct vpif_display_config *vpif_config_data =
-                                       vpif_dev->platform_data;
-       unsigned long addr = 0;
        int ret = 0;
 
        if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
@@ -1072,82 +1128,13 @@ static int vpif_streamon(struct file *file, void *priv,
        if (ret < 0)
                return ret;
 
-       /* Call videobuf_streamon to start streaming in videobuf */
-       ret = videobuf_streamon(&common->buffer_queue);
+       /* Call vb2_streamon to start streaming in videobuf2 */
+       ret = vb2_streamon(&common->buffer_queue, buftype);
        if (ret < 0) {
-               vpif_err("videobuf_streamon\n");
+               vpif_err("vb2_streamon\n");
                return ret;
        }
 
-       /* If buffer queue is empty, return error */
-       if (list_empty(&common->dma_queue)) {
-               vpif_err("buffer queue is empty\n");
-               return -EIO;
-       }
-
-       /* Get the next frame from the buffer queue */
-       common->next_frm = common->cur_frm =
-                           list_entry(common->dma_queue.next,
-                                      struct videobuf_buffer, queue);
-
-       list_del(&common->cur_frm->queue);
-       /* Mark state of the current frame to active */
-       common->cur_frm->state = VIDEOBUF_ACTIVE;
-
-       /* Initialize field_id and started member */
-       ch->field_id = 0;
-       common->started = 1;
-       if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
-               addr = common->cur_frm->boff;
-               /* Calculate the offset for Y and C data  in the buffer */
-               vpif_calculate_offsets(ch);
-
-               if ((ch->vpifparams.std_info.frm_fmt &&
-                       ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE)
-                       && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY)))
-                       || (!ch->vpifparams.std_info.frm_fmt
-                       && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
-                       vpif_err("conflict in field format and std format\n");
-                       return -EINVAL;
-               }
-
-               /* clock settings */
-               ret =
-                vpif_config_data->set_clock(ch->vpifparams.std_info.ycmux_mode,
-                                               ch->vpifparams.std_info.hd_sd);
-               if (ret < 0) {
-                       vpif_err("can't set clock\n");
-                       return ret;
-               }
-
-               /* set the parameters and addresses */
-               ret = vpif_set_video_params(vpif, ch->channel_id + 2);
-               if (ret < 0)
-                       return ret;
-
-               common->started = ret;
-               vpif_config_addr(ch, ret);
-               common->set_addr((addr + common->ytop_off),
-                                (addr + common->ybtm_off),
-                                (addr + common->ctop_off),
-                                (addr + common->cbtm_off));
-
-               /* Set interrupt for both the fields in VPIF
-                  Register enable channel in VPIF register */
-               if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
-                       channel2_intr_assert();
-                       channel2_intr_enable(1);
-                       enable_channel2(1);
-               }
-
-               if ((VPIF_CHANNEL3_VIDEO == ch->channel_id)
-                       || (common->started == 2)) {
-                       channel3_intr_assert();
-                       channel3_intr_enable(1);
-                       enable_channel3(1);
-               }
-               channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
-       }
        return ret;
 }
 
@@ -1157,6 +1144,8 @@ static int vpif_streamoff(struct file *file, void *priv,
        struct vpif_fh *fh = priv;
        struct channel_obj *ch = fh->channel;
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+       struct vpif_display_config *vpif_config_data =
+                                       vpif_dev->platform_data;
 
        if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
                vpif_err("buffer type not supported\n");
@@ -1176,18 +1165,22 @@ static int vpif_streamoff(struct file *file, void *priv,
        if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
                /* disable channel */
                if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
+                       if (vpif_config_data->ch2_clip_en)
+                               channel2_clipping_enable(0);
                        enable_channel2(0);
                        channel2_intr_enable(0);
                }
                if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
                                        (2 == common->started)) {
+                       if (vpif_config_data->ch3_clip_en)
+                               channel3_clipping_enable(0);
                        enable_channel3(0);
                        channel3_intr_enable(0);
                }
        }
 
        common->started = 0;
-       return videobuf_streamoff(&common->buffer_queue);
+       return vb2_streamoff(&common->buffer_queue, buftype);
 }
 
 static int vpif_cropcap(struct file *file, void *priv,
@@ -1220,7 +1213,7 @@ static int vpif_enum_output(struct file *file, void *fh,
 
        strcpy(output->name, config->output[output->index]);
        output->type = V4L2_OUTPUT_TYPE_ANALOG;
-       output->std = DM646X_V4L2_STD;
+       output->std = VPIF_V4L2_STD;
 
        return 0;
 }
@@ -1605,7 +1598,7 @@ static struct video_device vpif_video_template = {
        .name           = "vpif",
        .fops           = &vpif_fops,
        .ioctl_ops      = &vpif_ioctl_ops,
-       .tvnorms        = DM646X_V4L2_STD,
+       .tvnorms        = VPIF_V4L2_STD,
        .current_norm   = V4L2_STD_625_50,
 
 };
@@ -1687,9 +1680,9 @@ static __init int vpif_probe(struct platform_device *pdev)
        struct video_device *vfd;
        struct resource *res;
        int subdev_count;
+       size_t size;
 
        vpif_dev = &pdev->dev;
-
        err = initialize_vpif();
 
        if (err) {
@@ -1706,8 +1699,8 @@ static __init int vpif_probe(struct platform_device *pdev)
        k = 0;
        while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) {
                for (i = res->start; i <= res->end; i++) {
-                       if (request_irq(i, vpif_channel_isr, IRQF_DISABLED,
-                                       "DM646x_Display",
+                       if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
+                                       "VPIF_Display",
                                (void *)(&vpif_obj.dev[k]->channel_id))) {
                                err = -EBUSY;
                                goto vpif_int_err;
@@ -1737,13 +1730,31 @@ static __init int vpif_probe(struct platform_device *pdev)
                vfd->v4l2_dev = &vpif_obj.v4l2_dev;
                vfd->release = video_device_release;
                snprintf(vfd->name, sizeof(vfd->name),
-                        "DM646x_VPIFDisplay_DRIVER_V%s",
+                        "VPIF_Display_DRIVER_V%s",
                         VPIF_DISPLAY_VERSION);
 
                /* Set video_dev to the video device */
                ch->video_dev = vfd;
        }
 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res) {
+               size = resource_size(res);
+               /* The resources are divided into two equal memory and when
+                * we have HD output we can add them together
+                */
+               for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
+                       ch = vpif_obj.dev[j];
+                       ch->channel_id = j;
+
+                       /* only enabled if second resource exists */
+                       config_params.video_limit[ch->channel_id] = 0;
+                       if (size)
+                               config_params.video_limit[ch->channel_id] =
+                                                                       size/2;
+               }
+       }
+
        for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
                ch = vpif_obj.dev[j];
                /* Initialize field of the channel objects */
@@ -1823,7 +1834,7 @@ static __init int vpif_probe(struct platform_device *pdev)
        }
 
        v4l2_info(&vpif_obj.v4l2_dev,
-                       "DM646x VPIF display driver initialized\n");
+                       " VPIF display driver initialized\n");
        return 0;
 
 probe_subdev_out:
@@ -1871,10 +1882,81 @@ static int vpif_remove(struct platform_device *device)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int vpif_suspend(struct device *dev)
+{
+       struct common_obj *common;
+       struct channel_obj *ch;
+       int i;
+
+       for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
+               /* Get the pointer to the channel object */
+               ch = vpif_obj.dev[i];
+               common = &ch->common[VPIF_VIDEO_INDEX];
+               mutex_lock(&common->lock);
+               if (atomic_read(&ch->usrs) && common->io_usrs) {
+                       /* Disable channel */
+                       if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
+                               enable_channel2(0);
+                               channel2_intr_enable(0);
+                       }
+                       if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
+                                       common->started == 2) {
+                               enable_channel3(0);
+                               channel3_intr_enable(0);
+                       }
+               }
+               mutex_unlock(&common->lock);
+       }
+
+       return 0;
+}
+
+static int vpif_resume(struct device *dev)
+{
+
+       struct common_obj *common;
+       struct channel_obj *ch;
+       int i;
+
+       for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
+               /* Get the pointer to the channel object */
+               ch = vpif_obj.dev[i];
+               common = &ch->common[VPIF_VIDEO_INDEX];
+               mutex_lock(&common->lock);
+               if (atomic_read(&ch->usrs) && common->io_usrs) {
+                       /* Enable channel */
+                       if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
+                               enable_channel2(1);
+                               channel2_intr_enable(1);
+                       }
+                       if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
+                                       common->started == 2) {
+                               enable_channel3(1);
+                               channel3_intr_enable(1);
+                       }
+               }
+               mutex_unlock(&common->lock);
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops vpif_pm = {
+       .suspend        = vpif_suspend,
+       .resume         = vpif_resume,
+};
+
+#define vpif_pm_ops (&vpif_pm)
+#else
+#define vpif_pm_ops NULL
+#endif
+
 static __refdata struct platform_driver vpif_driver = {
        .driver = {
                        .name   = "vpif_display",
                        .owner  = THIS_MODULE,
+                       .pm     = vpif_pm_ops,
        },
        .probe  = vpif_probe,
        .remove = vpif_remove,
index 56879d1a0684b3c6452b9f34c17b5f7261a5e99e..8967ffb4405846ec6ffa0213eb747a19448af232 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * DM646x display header file
+ * VPIF display header file
  *
  * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
  *
@@ -21,7 +21,7 @@
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
 #include <media/videobuf-core.h>
-#include <media/videobuf-dma-contig.h>
+#include <media/videobuf2-dma-contig.h>
 #include <media/davinci/vpif_types.h>
 
 #include "vpif.h"
@@ -73,21 +73,29 @@ struct vbi_obj {
                                                 * vbi data */
 };
 
+struct vpif_disp_buffer {
+       struct vb2_buffer vb;
+       struct list_head list;
+};
+
 struct common_obj {
        /* Buffer specific parameters */
        u8 *fbuffers[VIDEO_MAX_FRAME];          /* List of buffer pointers for
                                                 * storing frames */
        u32 numbuffers;                         /* number of buffers */
-       struct videobuf_buffer *cur_frm;        /* Pointer pointing to current
-                                                * videobuf_buffer */
-       struct videobuf_buffer *next_frm;       /* Pointer pointing to next
-                                                * videobuf_buffer */
+       struct vpif_disp_buffer *cur_frm;       /* Pointer pointing to current
+                                                * vb2_buffer */
+       struct vpif_disp_buffer *next_frm;      /* Pointer pointing to next
+                                                * vb2_buffer */
        enum v4l2_memory memory;                /* This field keeps track of
                                                 * type of buffer exchange
                                                 * method user has selected */
        struct v4l2_format fmt;                 /* Used to store the format */
-       struct videobuf_queue buffer_queue;     /* Buffer queue used in
+       struct vb2_queue buffer_queue;          /* Buffer queue used in
                                                 * video-buf */
+       /* allocator-specific contexts for each plane */
+       struct vb2_alloc_ctx *alloc_ctx;
+
        struct list_head dma_queue;             /* Queue of filled frames */
        spinlock_t irqlock;                     /* Used in video-buf */
 
@@ -158,6 +166,7 @@ struct vpif_config_params {
        u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
        u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
        u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS];
+       u32 video_limit[VPIF_DISPLAY_NUM_CHANNELS];
        u8 min_numbuffers;
 };
 
index d7e2a3dc5525a758fb8effc554a4b1dc1c2ee231..07dc594e79f04bea706c8cebfba1b514a8a3623e 100644 (file)
@@ -42,6 +42,7 @@
 #include <sound/initval.h>
 #include <sound/control.h>
 #include <sound/tlv.h>
+#include <sound/ac97_codec.h>
 #include <media/v4l2-common.h>
 #include "em28xx.h"
 
@@ -679,19 +680,19 @@ static int em28xx_audio_init(struct em28xx *dev)
        INIT_WORK(&dev->wq_trigger, audio_trigger);
 
        if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
-               em28xx_cvol_new(card, dev, "Video", AC97_VIDEO_VOL);
-               em28xx_cvol_new(card, dev, "Line In", AC97_LINEIN_VOL);
-               em28xx_cvol_new(card, dev, "Phone", AC97_PHONE_VOL);
-               em28xx_cvol_new(card, dev, "Microphone", AC97_PHONE_VOL);
-               em28xx_cvol_new(card, dev, "CD", AC97_CD_VOL);
-               em28xx_cvol_new(card, dev, "AUX", AC97_AUX_VOL);
-               em28xx_cvol_new(card, dev, "PCM", AC97_PCM_OUT_VOL);
-
-               em28xx_cvol_new(card, dev, "Master", AC97_MASTER_VOL);
-               em28xx_cvol_new(card, dev, "Line", AC97_LINE_LEVEL_VOL);
-               em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO_VOL);
-               em28xx_cvol_new(card, dev, "LFE", AC97_LFE_MASTER_VOL);
-               em28xx_cvol_new(card, dev, "Surround", AC97_SURR_MASTER_VOL);
+               em28xx_cvol_new(card, dev, "Video", AC97_VIDEO);
+               em28xx_cvol_new(card, dev, "Line In", AC97_LINE);
+               em28xx_cvol_new(card, dev, "Phone", AC97_PHONE);
+               em28xx_cvol_new(card, dev, "Microphone", AC97_MIC);
+               em28xx_cvol_new(card, dev, "CD", AC97_CD);
+               em28xx_cvol_new(card, dev, "AUX", AC97_AUX);
+               em28xx_cvol_new(card, dev, "PCM", AC97_PCM);
+
+               em28xx_cvol_new(card, dev, "Master", AC97_MASTER);
+               em28xx_cvol_new(card, dev, "Line", AC97_HEADPHONE);
+               em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO);
+               em28xx_cvol_new(card, dev, "LFE", AC97_CENTER_LFE_MASTER);
+               em28xx_cvol_new(card, dev, "Surround", AC97_SURROUND_MASTER);
        }
 
        err = snd_card_register(card);
index 862c6575c55791fa7a6f4488d7cd933226fd02cb..ca62b9981380427b587efb684d36a60f88105a21 100644 (file)
@@ -975,12 +975,7 @@ struct em28xx_board em28xx_boards[] = {
                .name         = "Terratec Cinergy HTC Stick",
                .has_dvb      = 1,
                .ir_codes     = RC_MAP_NEC_TERRATEC_CINERGY_XS,
-#if 0
-               .tuner_type   = TUNER_PHILIPS_TDA8290,
-               .tuner_addr   = 0x41,
-               .dvb_gpio     = terratec_h5_digital, /* FIXME: probably wrong */
-               .tuner_gpio   = terratec_h5_gpio,
-#endif
+               .tuner_type   = TUNER_ABSENT,
                .i2c_speed    = EM2874_I2C_SECONDARY_BUS_SELECT |
                                EM28XX_I2C_CLK_WAIT_ENABLE |
                                EM28XX_I2C_FREQ_400_KHZ,
index 5717bdee8f1bec6f7b344563482a6c064fdba60c..de2cb20ad2cc1d676c6aa0c532e36bc251c4273e 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/slab.h>
 #include <linux/usb.h>
 #include <linux/vmalloc.h>
+#include <sound/ac97_codec.h>
 #include <media/v4l2-common.h>
 
 #include "em28xx.h"
@@ -326,13 +327,13 @@ struct em28xx_vol_itable {
 };
 
 static struct em28xx_vol_itable inputs[] = {
-       { EM28XX_AMUX_VIDEO,    AC97_VIDEO_VOL   },
-       { EM28XX_AMUX_LINE_IN,  AC97_LINEIN_VOL  },
-       { EM28XX_AMUX_PHONE,    AC97_PHONE_VOL   },
-       { EM28XX_AMUX_MIC,      AC97_MIC_VOL     },
-       { EM28XX_AMUX_CD,       AC97_CD_VOL      },
-       { EM28XX_AMUX_AUX,      AC97_AUX_VOL     },
-       { EM28XX_AMUX_PCM_OUT,  AC97_PCM_OUT_VOL },
+       { EM28XX_AMUX_VIDEO,    AC97_VIDEO      },
+       { EM28XX_AMUX_LINE_IN,  AC97_LINE       },
+       { EM28XX_AMUX_PHONE,    AC97_PHONE      },
+       { EM28XX_AMUX_MIC,      AC97_MIC        },
+       { EM28XX_AMUX_CD,       AC97_CD         },
+       { EM28XX_AMUX_AUX,      AC97_AUX        },
+       { EM28XX_AMUX_PCM_OUT,  AC97_PCM        },
 };
 
 static int set_ac97_input(struct em28xx *dev)
@@ -415,11 +416,11 @@ struct em28xx_vol_otable {
 };
 
 static const struct em28xx_vol_otable outputs[] = {
-       { EM28XX_AOUT_MASTER, AC97_MASTER_VOL      },
-       { EM28XX_AOUT_LINE,   AC97_LINE_LEVEL_VOL  },
-       { EM28XX_AOUT_MONO,   AC97_MASTER_MONO_VOL },
-       { EM28XX_AOUT_LFE,    AC97_LFE_MASTER_VOL  },
-       { EM28XX_AOUT_SURR,   AC97_SURR_MASTER_VOL },
+       { EM28XX_AOUT_MASTER, AC97_MASTER               },
+       { EM28XX_AOUT_LINE,   AC97_HEADPHONE            },
+       { EM28XX_AOUT_MONO,   AC97_MASTER_MONO          },
+       { EM28XX_AOUT_LFE,    AC97_CENTER_LFE_MASTER    },
+       { EM28XX_AOUT_SURR,   AC97_SURROUND_MASTER      },
 };
 
 int em28xx_audio_analog_set(struct em28xx *dev)
@@ -459,9 +460,9 @@ int em28xx_audio_analog_set(struct em28xx *dev)
        if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
                int vol;
 
-               em28xx_write_ac97(dev, AC97_POWER_DOWN_CTRL, 0x4200);
-               em28xx_write_ac97(dev, AC97_EXT_AUD_CTRL, 0x0031);
-               em28xx_write_ac97(dev, AC97_PCM_IN_SRATE, 0xbb80);
+               em28xx_write_ac97(dev, AC97_POWERDOWN, 0x4200);
+               em28xx_write_ac97(dev, AC97_EXTENDED_STATUS, 0x0031);
+               em28xx_write_ac97(dev, AC97_PCM_LR_ADC_RATE, 0xbb80);
 
                /* LSB: left channel - both channels with the same level */
                vol = (0x1f - dev->volume) | ((0x1f - dev->volume) << 8);
@@ -487,7 +488,7 @@ int em28xx_audio_analog_set(struct em28xx *dev)
                           channels */
                        sel |= (sel << 8);
 
-                       em28xx_write_ac97(dev, AC97_RECORD_SELECT, sel);
+                       em28xx_write_ac97(dev, AC97_REC_SEL, sel);
                }
        }
 
index 16410ac20092272b894d7aeb4e98aba6705d80f4..a16531fa937a1ec395e3490e741e0d46d0a3cccd 100644 (file)
@@ -310,31 +310,47 @@ static struct drxd_config em28xx_drxd = {
        .disable_i2c_gate_ctrl = 1,
 };
 
-struct drxk_config terratec_h5_drxk = {
+static struct drxk_config terratec_h5_drxk = {
        .adr = 0x29,
        .single_master = 1,
        .no_i2c_bridge = 1,
        .microcode_name = "dvb-usb-terratec-h5-drxk.fw",
+       .qam_demod_parameter_count = 2,
 };
 
-struct drxk_config hauppauge_930c_drxk = {
+static struct drxk_config hauppauge_930c_drxk = {
        .adr = 0x29,
        .single_master = 1,
        .no_i2c_bridge = 1,
        .microcode_name = "dvb-usb-hauppauge-hvr930c-drxk.fw",
        .chunk_size = 56,
+       .qam_demod_parameter_count = 2,
 };
 
-struct drxk_config maxmedia_ub425_tc_drxk = {
+struct drxk_config terratec_htc_stick_drxk = {
        .adr = 0x29,
        .single_master = 1,
        .no_i2c_bridge = 1,
+       .microcode_name = "dvb-usb-terratec-htc-stick-drxk.fw",
+       .chunk_size = 54,
+       .qam_demod_parameter_count = 2,
+       /* Required for the antenna_gpio to disable LNA. */
+       .antenna_dvbt = true,
+       /* The windows driver uses the same. This will disable LNA. */
+       .antenna_gpio = 0x6,
 };
 
-struct drxk_config pctv_520e_drxk = {
+static struct drxk_config maxmedia_ub425_tc_drxk = {
+       .adr = 0x29,
+       .single_master = 1,
+       .no_i2c_bridge = 1,
+};
+
+static struct drxk_config pctv_520e_drxk = {
        .adr = 0x29,
        .single_master = 1,
        .microcode_name = "dvb-demod-drxk-pctv.fw",
+       .qam_demod_parameter_count = 2,
        .chunk_size = 58,
        .antenna_dvbt = true, /* disable LNA */
        .antenna_gpio = (1 << 2), /* disable LNA */
@@ -473,6 +489,57 @@ static void terratec_h5_init(struct em28xx *dev)
        em28xx_gpio_set(dev, terratec_h5_end);
 };
 
+static void terratec_htc_stick_init(struct em28xx *dev)
+{
+       int i;
+
+       /*
+        * GPIO configuration:
+        * 0xff: unknown (does not affect DVB-T).
+        * 0xf6: DRX-K (demodulator).
+        * 0xe6: unknown (does not affect DVB-T).
+        * 0xb6: unknown (does not affect DVB-T).
+        */
+       struct em28xx_reg_seq terratec_htc_stick_init[] = {
+               {EM28XX_R08_GPIO,       0xff,   0xff,   10},
+               {EM2874_R80_GPIO,       0xf6,   0xff,   100},
+               {EM2874_R80_GPIO,       0xe6,   0xff,   50},
+               {EM2874_R80_GPIO,       0xf6,   0xff,   100},
+               { -1,                   -1,     -1,     -1},
+       };
+       struct em28xx_reg_seq terratec_htc_stick_end[] = {
+               {EM2874_R80_GPIO,       0xb6,   0xff,   100},
+               {EM2874_R80_GPIO,       0xf6,   0xff,   50},
+               { -1,                   -1,     -1,     -1},
+       };
+
+       /* Init the analog decoder? */
+       struct {
+               unsigned char r[4];
+               int len;
+       } regs[] = {
+               {{ 0x06, 0x02, 0x00, 0x31 }, 4},
+               {{ 0x01, 0x02 }, 2},
+               {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
+               {{ 0x01, 0x00 }, 2},
+               {{ 0x01, 0x00, 0xff, 0xaf }, 4},
+       };
+
+       em28xx_gpio_set(dev, terratec_htc_stick_init);
+
+       em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
+       msleep(10);
+       em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44);
+       msleep(10);
+
+       dev->i2c_client.addr = 0x82 >> 1;
+
+       for (i = 0; i < ARRAY_SIZE(regs); i++)
+               i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
+
+       em28xx_gpio_set(dev, terratec_htc_stick_end);
+};
+
 static void pctv_520e_init(struct em28xx *dev)
 {
        /*
@@ -944,7 +1011,6 @@ static int em28xx_dvb_init(struct em28xx *dev)
                break;
        }
        case EM2884_BOARD_TERRATEC_H5:
-       case EM2884_BOARD_CINERGY_HTC_STICK:
                terratec_h5_init(dev);
 
                dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap);
@@ -1021,6 +1087,25 @@ static int em28xx_dvb_init(struct em28xx *dev)
                        }
                }
                break;
+       case EM2884_BOARD_CINERGY_HTC_STICK:
+               terratec_htc_stick_init(dev);
+
+               /* attach demodulator */
+               dvb->fe[0] = dvb_attach(drxk_attach, &terratec_htc_stick_drxk,
+                                       &dev->i2c_adap);
+               if (!dvb->fe[0]) {
+                       result = -EINVAL;
+                       goto out_free;
+               }
+
+               /* Attach the demodulator. */
+               if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
+                               &dev->i2c_adap,
+                               &em28xx_cxd2820r_tda18271_config)) {
+                       result = -EINVAL;
+                       goto out_free;
+               }
+               break;
        default:
                em28xx_errdev("/2: The frontend of your DVB/ATSC card"
                                " isn't supported yet\n");
index 185db65b766ef5b58ab2f124d3c20491e8aaceeb..1683bd9d51eed67ecaef8832832bd1eafeaa1ca5 100644 (file)
@@ -475,6 +475,7 @@ static struct i2c_client em28xx_client_template = {
  */
 static char *i2c_devs[128] = {
        [0x4a >> 1] = "saa7113h",
+       [0x52 >> 1] = "drxk",
        [0x60 >> 1] = "remote IR sensor",
        [0x8e >> 1] = "remote IR sensor",
        [0x86 >> 1] = "tda9887",
index 5e30c4f3f248ac5427caf86201eaec09f83efc02..97d36b4f19db95094bda60d549888e6e564315cf 100644 (file)
@@ -345,7 +345,7 @@ static void em28xx_ir_stop(struct rc_dev *rc)
        cancel_delayed_work_sync(&ir->work);
 }
 
-int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type)
+static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type)
 {
        int rc = 0;
        struct em28xx_IR *ir = rc_dev->priv;
index 2f626850572643a18e6f5c79d41468f893fade2e..6ff368297f6eaef94f93b26528f59bacc6010a53 100644 (file)
@@ -211,58 +211,9 @@ enum em28xx_chip_id {
 };
 
 /*
- * Registers used by em202 and other AC97 chips
+ * Registers used by em202
  */
 
-/* Standard AC97 registers */
-#define AC97_RESET               0x00
-
-       /* Output volumes */
-#define AC97_MASTER_VOL          0x02
-#define AC97_LINE_LEVEL_VOL      0x04  /* Some devices use for headphones */
-#define AC97_MASTER_MONO_VOL     0x06
-
-       /* Input volumes */
-#define AC97_PC_BEEP_VOL         0x0a
-#define AC97_PHONE_VOL           0x0c
-#define AC97_MIC_VOL             0x0e
-#define AC97_LINEIN_VOL          0x10
-#define AC97_CD_VOL              0x12
-#define AC97_VIDEO_VOL           0x14
-#define AC97_AUX_VOL             0x16
-#define AC97_PCM_OUT_VOL         0x18
-
-       /* capture registers */
-#define AC97_RECORD_SELECT       0x1a
-#define AC97_RECORD_GAIN         0x1c
-
-       /* control registers */
-#define AC97_GENERAL_PURPOSE     0x20
-#define AC97_3D_CTRL             0x22
-#define AC97_AUD_INT_AND_PAG     0x24
-#define AC97_POWER_DOWN_CTRL     0x26
-#define AC97_EXT_AUD_ID          0x28
-#define AC97_EXT_AUD_CTRL        0x2a
-
-/* Supported rate varies for each AC97 device
-   if write an unsupported value, it will return the closest one
- */
-#define AC97_PCM_OUT_FRONT_SRATE 0x2c
-#define AC97_PCM_OUT_SURR_SRATE  0x2e
-#define AC97_PCM_OUT_LFE_SRATE   0x30
-#define AC97_PCM_IN_SRATE        0x32
-
-       /* For devices with more than 2 channels, extra output volumes */
-#define AC97_LFE_MASTER_VOL      0x36
-#define AC97_SURR_MASTER_VOL     0x38
-
-       /* Digital SPDIF output control */
-#define AC97_SPDIF_OUT_CTRL      0x3a
-
-       /* Vendor ID identifier */
-#define AC97_VENDOR_ID1          0x7c
-#define AC97_VENDOR_ID2          0x7e
-
 /* EMP202 vendor registers */
 #define EM202_EXT_MODEM_CTRL     0x3e
 #define EM202_GPIO_CONF          0x4c
index 9769f17915c0546c41391025addcaa942b12a68d..352f32190e68014c85ed988a344010db83e302f2 100644 (file)
@@ -33,10 +33,6 @@ struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
 };
 
-/* V4L2 controls supported by the driver */
-static const struct ctrl sd_ctrls[] = {
-};
-
 static const struct v4l2_pix_format vga_mode[] = {
        {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
                .bytesperline = 320,
@@ -256,8 +252,6 @@ static void sd_isoc_irq(struct urb *urb)
 /* 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,
@@ -288,6 +282,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index f39fee0fd10f2ba14a3cb71cb630a84680cad1b1..c9052f20435ef00c2fc17cc2c870ebbfaedc0896 100644 (file)
@@ -31,74 +31,18 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
 MODULE_DESCRIPTION("GSPCA USB Conexant Camera Driver");
 MODULE_LICENSE("GPL");
 
+#define QUALITY 50
+
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
-
-       unsigned char brightness;
-       unsigned char contrast;
-       unsigned char colors;
-       u8 quality;
-#define QUALITY_MIN 30
-#define QUALITY_MAX 60
-#define QUALITY_DEF 40
+       struct v4l2_ctrl *brightness;
+       struct v4l2_ctrl *contrast;
+       struct v4l2_ctrl *sat;
 
        u8 jpeg_hdr[JPEG_HDR_SZ];
 };
 
-/* 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 const struct ctrl sd_ctrls[] = {
-       {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-#define BRIGHTNESS_DEF 0xd4
-               .default_value = BRIGHTNESS_DEF,
-           },
-           .set = sd_setbrightness,
-           .get = sd_getbrightness,
-       },
-       {
-           {
-               .id      = V4L2_CID_CONTRAST,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Contrast",
-               .minimum = 0x0a,
-               .maximum = 0x1f,
-               .step    = 1,
-#define CONTRAST_DEF 0x0c
-               .default_value = CONTRAST_DEF,
-           },
-           .set = sd_setcontrast,
-           .get = sd_getcontrast,
-       },
-       {
-           {
-               .id      = V4L2_CID_SATURATION,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Color",
-               .minimum = 0,
-               .maximum = 7,
-               .step    = 1,
-#define COLOR_DEF 3
-               .default_value = COLOR_DEF,
-           },
-           .set = sd_setcolors,
-           .get = sd_getcolors,
-       },
-};
-
 static const struct v4l2_pix_format vga_mode[] = {
        {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
                .bytesperline = 176,
@@ -817,17 +761,11 @@ static void cx11646_init1(struct gspca_dev *gspca_dev)
 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;
        cam->cam_mode = vga_mode;
        cam->nmodes = ARRAY_SIZE(vga_mode);
-
-       sd->brightness = BRIGHTNESS_DEF;
-       sd->contrast = CONTRAST_DEF;
-       sd->colors = COLOR_DEF;
-       sd->quality = QUALITY_DEF;
        return 0;
 }
 
@@ -849,7 +787,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
        /* create the JPEG header */
        jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
                        0x22);          /* JPEG 411 */
-       jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+       jpeg_set_qual(sd->jpeg_hdr, QUALITY);
 
        cx11646_initsize(gspca_dev);
        cx11646_fw(gspca_dev);
@@ -903,142 +841,99 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val, s32 sat)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        __u8 regE5cbx[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 };
        __u8 reg51c[2];
-       __u8 bright;
-       __u8 colors;
 
-       bright = sd->brightness;
-       regE5cbx[2] = bright;
+       regE5cbx[2] = val;
        reg_w(gspca_dev, 0x00e5, regE5cbx, 8);
        reg_r(gspca_dev, 0x00e8, 8);
        reg_w(gspca_dev, 0x00e5, regE5c, 4);
        reg_r(gspca_dev, 0x00e8, 1);            /* 0x00 */
 
-       colors = sd->colors;
        reg51c[0] = 0x77;
-       reg51c[1] = colors;
+       reg51c[1] = sat;
        reg_w(gspca_dev, 0x0051, reg51c, 2);
        reg_w(gspca_dev, 0x0010, reg10, 2);
        reg_w_val(gspca_dev, 0x0070, reg70);
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val, s32 sat)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        __u8 regE5acx[] = { 0x88, 0x0a, 0x0c, 0x01 };   /* seem MSB */
 /*     __u8 regE5bcx[] = { 0x88, 0x0b, 0x12, 0x01};     * LSB */
        __u8 reg51c[2];
 
-       regE5acx[2] = sd->contrast;
+       regE5acx[2] = val;
        reg_w(gspca_dev, 0x00e5, regE5acx, 4);
        reg_r(gspca_dev, 0x00e8, 1);            /* 0x00 */
        reg51c[0] = 0x77;
-       reg51c[1] = sd->colors;
+       reg51c[1] = sat;
        reg_w(gspca_dev, 0x0051, reg51c, 2);
        reg_w(gspca_dev, 0x0010, reg10, 2);
        reg_w_val(gspca_dev, 0x0070, reg70);
 }
 
-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)
-               setbrightness(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)
-               setcontrast(gspca_dev);
-       return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
 
-       *val = sd->contrast;
-       return 0;
-}
+       gspca_dev->usb_err = 0;
 
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       if (!gspca_dev->streaming)
+               return 0;
 
-       sd->colors = val;
-       if (gspca_dev->streaming) {
-               setbrightness(gspca_dev);
-               setcontrast(gspca_dev);
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val, sd->sat->cur.val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val, sd->sat->cur.val);
+               break;
+       case V4L2_CID_SATURATION:
+               setbrightness(gspca_dev, sd->brightness->cur.val, ctrl->val);
+               setcontrast(gspca_dev, sd->contrast->cur.val, ctrl->val);
+               break;
        }
-       return 0;
+       return gspca_dev->usb_err;
 }
 
-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_set_jcomp(struct gspca_dev *gspca_dev,
-                       struct v4l2_jpegcompression *jcomp)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (jcomp->quality < QUALITY_MIN)
-               sd->quality = QUALITY_MIN;
-       else if (jcomp->quality > QUALITY_MAX)
-               sd->quality = QUALITY_MAX;
-       else
-               sd->quality = jcomp->quality;
-       if (gspca_dev->streaming)
-               jpeg_set_qual(sd->jpeg_hdr, sd->quality);
-       return 0;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
-                       struct v4l2_jpegcompression *jcomp)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       memset(jcomp, 0, sizeof *jcomp);
-       jcomp->quality = sd->quality;
-       jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
-                       | V4L2_JPEG_MARKER_DQT;
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 3);
+       sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 0xd4);
+       sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0x0a, 0x1f, 1, 0x0c);
+       sd->sat = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 7, 1, 3);
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
        return 0;
 }
 
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stop0 = sd_stop0,
        .pkt_scan = sd_pkt_scan,
-       .get_jcomp = sd_get_jcomp,
-       .set_jcomp = sd_set_jcomp,
 };
 
 /* -- module initialisation -- */
@@ -1064,6 +959,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 8f33bbd091ad2d2976a46a7fe6e660ad3af4f1f9..2499a881d9a3ab68c8a4190f9ac19fb6d41ad3ed 100644 (file)
@@ -225,6 +225,15 @@ MODULE_LICENSE("GPL");
 #define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \
                                sd->params.version.firmwareRevision == (y))
 
+#define CPIA1_CID_COMP_TARGET (V4L2_CTRL_CLASS_USER + 0x1000)
+#define BRIGHTNESS_DEF 50
+#define CONTRAST_DEF 48
+#define SATURATION_DEF 50
+#define FREQ_DEF V4L2_CID_POWER_LINE_FREQUENCY_50HZ
+#define ILLUMINATORS_1_DEF 0
+#define ILLUMINATORS_2_DEF 0
+#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
+
 /* Developer's Guide Table 5 p 3-34
  * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
 static u8 flicker_jumps[2][2][4] =
@@ -360,135 +369,9 @@ struct sd {
        atomic_t fps;
        int exposure_count;
        u8 exposure_status;
+       struct v4l2_ctrl *freq;
        u8 mainsFreq;                           /* 0 = 50hz, 1 = 60hz */
        u8 first_frame;
-       u8 freq;
-};
-
-/* 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_setsaturation(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsaturation(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 int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getilluminator1(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getilluminator2(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
-       {
-#define BRIGHTNESS_IDX 0
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 100,
-               .step = 1,
-#define BRIGHTNESS_DEF 50
-               .default_value = BRIGHTNESS_DEF,
-               .flags = 0,
-           },
-           .set = sd_setbrightness,
-           .get = sd_getbrightness,
-       },
-#define CONTRAST_IDX 1
-       {
-           {
-               .id      = V4L2_CID_CONTRAST,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Contrast",
-               .minimum = 0,
-               .maximum = 96,
-               .step    = 8,
-#define CONTRAST_DEF 48
-               .default_value = CONTRAST_DEF,
-           },
-           .set = sd_setcontrast,
-           .get = sd_getcontrast,
-       },
-#define SATURATION_IDX 2
-       {
-           {
-               .id      = V4L2_CID_SATURATION,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Saturation",
-               .minimum = 0,
-               .maximum = 100,
-               .step    = 1,
-#define SATURATION_DEF 50
-               .default_value = SATURATION_DEF,
-           },
-           .set = sd_setsaturation,
-           .get = sd_getsaturation,
-       },
-#define POWER_LINE_FREQUENCY_IDX 3
-       {
-               {
-                       .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 1
-                       .default_value = FREQ_DEF,
-               },
-               .set = sd_setfreq,
-               .get = sd_getfreq,
-       },
-#define ILLUMINATORS_1_IDX 4
-       {
-               {
-                       .id      = V4L2_CID_ILLUMINATORS_1,
-                       .type    = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name    = "Illuminator 1",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-#define ILLUMINATORS_1_DEF 0
-                       .default_value = ILLUMINATORS_1_DEF,
-               },
-               .set = sd_setilluminator1,
-               .get = sd_getilluminator1,
-       },
-#define ILLUMINATORS_2_IDX 5
-       {
-               {
-                       .id      = V4L2_CID_ILLUMINATORS_2,
-                       .type    = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name    = "Illuminator 2",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-#define ILLUMINATORS_2_DEF 0
-                       .default_value = ILLUMINATORS_2_DEF,
-               },
-               .set = sd_setilluminator2,
-               .get = sd_getilluminator2,
-       },
-#define COMP_TARGET_IDX 6
-       {
-               {
-#define V4L2_CID_COMP_TARGET V4L2_CID_PRIVATE_BASE
-                       .id      = V4L2_CID_COMP_TARGET,
-                       .type    = V4L2_CTRL_TYPE_MENU,
-                       .name    = "Compression Target",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
-                       .default_value = COMP_TARGET_DEF,
-               },
-               .set = sd_setcomptarget,
-               .get = sd_getcomptarget,
-       },
 };
 
 static const struct v4l2_pix_format mode[] = {
@@ -770,15 +653,6 @@ static void reset_camera_params(struct gspca_dev *gspca_dev)
        params->apcor.gain2 = 0x16;
        params->apcor.gain4 = 0x24;
        params->apcor.gain8 = 0x34;
-       params->flickerControl.flickerMode = 0;
-       params->flickerControl.disabled = 1;
-
-       params->flickerControl.coarseJump =
-               flicker_jumps[sd->mainsFreq]
-                            [params->sensorFps.baserate]
-                            [params->sensorFps.divisor];
-       params->flickerControl.allowableOverExposure =
-               find_over_exposure(params->colourParams.brightness);
        params->vlOffset.gain1 = 20;
        params->vlOffset.gain2 = 24;
        params->vlOffset.gain4 = 26;
@@ -798,6 +672,15 @@ static void reset_camera_params(struct gspca_dev *gspca_dev)
        params->sensorFps.divisor = 1;
        params->sensorFps.baserate = 1;
 
+       params->flickerControl.flickerMode = 0;
+       params->flickerControl.disabled = 1;
+       params->flickerControl.coarseJump =
+               flicker_jumps[sd->mainsFreq]
+                            [params->sensorFps.baserate]
+                            [params->sensorFps.divisor];
+       params->flickerControl.allowableOverExposure =
+               find_over_exposure(params->colourParams.brightness);
+
        params->yuvThreshold.yThreshold = 6; /* From windows driver */
        params->yuvThreshold.uvThreshold = 6; /* From windows driver */
 
@@ -1110,9 +993,6 @@ static int command_setlights(struct gspca_dev *gspca_dev)
        struct sd *sd = (struct sd *) gspca_dev;
        int ret, p1, p2;
 
-       if (!sd->params.qx3.qx3_detected)
-               return 0;
-
        p1 = (sd->params.qx3.bottomlight == 0) << 1;
        p2 = (sd->params.qx3.toplight == 0) << 3;
 
@@ -1551,8 +1431,10 @@ static void restart_flicker(struct gspca_dev *gspca_dev)
 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->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
        reset_camera_params(gspca_dev);
 
        PDEBUG(D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)",
@@ -1562,8 +1444,25 @@ static int sd_config(struct gspca_dev *gspca_dev,
        cam->cam_mode = mode;
        cam->nmodes = ARRAY_SIZE(mode);
 
-       sd_setfreq(gspca_dev, FREQ_DEF);
+       goto_low_power(gspca_dev);
+       /* Check the firmware version. */
+       sd->params.version.firmwareVersion = 0;
+       get_version_information(gspca_dev);
+       if (sd->params.version.firmwareVersion != 1) {
+               PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)",
+                      sd->params.version.firmwareVersion);
+               return -ENODEV;
+       }
 
+       /* A bug in firmware 1-02 limits gainMode to 2 */
+       if (sd->params.version.firmwareRevision <= 2 &&
+           sd->params.exposure.gainMode > 2) {
+               sd->params.exposure.gainMode = 2;
+       }
+
+       /* set QX3 detected flag */
+       sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
+                                      sd->params.pnpID.product == 0x0001);
        return 0;
 }
 
@@ -1602,21 +1501,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
        /* Check the firmware version. */
        sd->params.version.firmwareVersion = 0;
        get_version_information(gspca_dev);
-       if (sd->params.version.firmwareVersion != 1) {
-               PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)",
-                      sd->params.version.firmwareVersion);
-               return -ENODEV;
-       }
-
-       /* A bug in firmware 1-02 limits gainMode to 2 */
-       if (sd->params.version.firmwareRevision <= 2 &&
-           sd->params.exposure.gainMode > 2) {
-               sd->params.exposure.gainMode = 2;
-       }
-
-       /* set QX3 detected flag */
-       sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
-                                      sd->params.pnpID.product == 0x0001);
 
        /* The fatal error checking should be done after
         * the camera powers up (developer's guide p 3-38) */
@@ -1785,9 +1669,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
           or disable the illuminator controls, if this isn't a QX3 */
        if (sd->params.qx3.qx3_detected)
                command_setlights(gspca_dev);
-       else
-               gspca_dev->ctrl_dis |=
-                       ((1 << ILLUMINATORS_1_IDX) | (1 << ILLUMINATORS_2_IDX));
 
        sd_stopN(gspca_dev);
 
@@ -1871,235 +1752,123 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
        do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
 }
 
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       int ret;
-
-       sd->params.colourParams.brightness = val;
-       sd->params.flickerControl.allowableOverExposure =
-               find_over_exposure(sd->params.colourParams.brightness);
-       if (gspca_dev->streaming) {
-               ret = command_setcolourparams(gspca_dev);
-               if (ret)
-                       return ret;
-               return command_setflickerctrl(gspca_dev);
-       }
-       return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->params.colourParams.brightness;
-       return 0;
-}
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
 
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       gspca_dev->usb_err = 0;
 
-       sd->params.colourParams.contrast = val;
-       if (gspca_dev->streaming)
-               return command_setcolourparams(gspca_dev);
-
-       return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->params.colourParams.contrast;
-       return 0;
-}
-
-static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->params.colourParams.saturation = val;
-       if (gspca_dev->streaming)
-               return command_setcolourparams(gspca_dev);
-
-       return 0;
-}
-
-static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->params.colourParams.saturation;
-       return 0;
-}
-
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       int on;
+       if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
+               return 0;
 
-       switch (val) {
-       case 0:         /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
-               on = 0;
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               sd->params.colourParams.brightness = ctrl->val;
+               sd->params.flickerControl.allowableOverExposure =
+                       find_over_exposure(sd->params.colourParams.brightness);
+               gspca_dev->usb_err = command_setcolourparams(gspca_dev);
+               if (!gspca_dev->usb_err)
+                       gspca_dev->usb_err = command_setflickerctrl(gspca_dev);
                break;
-       case 1:         /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
-               on = 1;
-               sd->mainsFreq = 0;
+       case V4L2_CID_CONTRAST:
+               sd->params.colourParams.contrast = ctrl->val;
+               gspca_dev->usb_err = command_setcolourparams(gspca_dev);
                break;
-       case 2:         /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
-               on = 1;
-               sd->mainsFreq = 1;
+       case V4L2_CID_SATURATION:
+               sd->params.colourParams.saturation = ctrl->val;
+               gspca_dev->usb_err = command_setcolourparams(gspca_dev);
                break;
-       default:
-               return -EINVAL;
-       }
-
-       sd->freq = val;
-       sd->params.flickerControl.coarseJump =
-               flicker_jumps[sd->mainsFreq]
-                            [sd->params.sensorFps.baserate]
-                            [sd->params.sensorFps.divisor];
-
-       return set_flicker(gspca_dev, on, gspca_dev->streaming);
-}
-
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->freq;
-       return 0;
-}
-
-static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->params.compressionTarget.frTargeting = val;
-       if (gspca_dev->streaming)
-               return command_setcompressiontarget(gspca_dev);
-
-       return 0;
-}
-
-static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->params.compressionTarget.frTargeting;
-       return 0;
-}
-
-static int sd_setilluminator(struct gspca_dev *gspca_dev, __s32 val, int n)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       int ret;
-
-       if (!sd->params.qx3.qx3_detected)
-               return -EINVAL;
-
-       switch (n) {
-       case 1:
-               sd->params.qx3.bottomlight = val ? 1 : 0;
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               sd->mainsFreq = ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
+               sd->params.flickerControl.coarseJump =
+                       flicker_jumps[sd->mainsFreq]
+                       [sd->params.sensorFps.baserate]
+                       [sd->params.sensorFps.divisor];
+
+               gspca_dev->usb_err = set_flicker(gspca_dev,
+                       ctrl->val != V4L2_CID_POWER_LINE_FREQUENCY_DISABLED,
+                       gspca_dev->streaming);
                break;
-       case 2:
-               sd->params.qx3.toplight = val ? 1 : 0;
+       case V4L2_CID_ILLUMINATORS_1:
+               sd->params.qx3.bottomlight = ctrl->val;
+               gspca_dev->usb_err = command_setlights(gspca_dev);
                break;
-       default:
-               return -EINVAL;
-       }
-
-       ret = command_setlights(gspca_dev);
-       if (ret && ret != -EINVAL)
-               ret = -EBUSY;
-
-       return ret;
-}
-
-static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val)
-{
-       return sd_setilluminator(gspca_dev, val, 1);
-}
-
-static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val)
-{
-       return sd_setilluminator(gspca_dev, val, 2);
-}
-
-static int sd_getilluminator(struct gspca_dev *gspca_dev, __s32 *val, int n)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (!sd->params.qx3.qx3_detected)
-               return -EINVAL;
-
-       switch (n) {
-       case 1:
-               *val = sd->params.qx3.bottomlight;
+       case V4L2_CID_ILLUMINATORS_2:
+               sd->params.qx3.toplight = ctrl->val;
+               gspca_dev->usb_err = command_setlights(gspca_dev);
                break;
-       case 2:
-               *val = sd->params.qx3.toplight;
+       case CPIA1_CID_COMP_TARGET:
+               sd->params.compressionTarget.frTargeting = ctrl->val;
+               gspca_dev->usb_err = command_setcompressiontarget(gspca_dev);
                break;
-       default:
-               return -EINVAL;
        }
-       return 0;
+       return gspca_dev->usb_err;
 }
 
-static int sd_getilluminator1(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       return sd_getilluminator(gspca_dev, val, 1);
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-static int sd_getilluminator2(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
-       return sd_getilluminator(gspca_dev, val, 2);
-}
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+       static const char * const comp_target_menu[] = {
+               "Quality",
+               "Framerate",
+               NULL
+       };
+       static const struct v4l2_ctrl_config comp_target = {
+               .ops = &sd_ctrl_ops,
+               .id = CPIA1_CID_COMP_TARGET,
+               .type = V4L2_CTRL_TYPE_MENU,
+               .name = "Compression Target",
+               .qmenu = comp_target_menu,
+               .max = 1,
+               .def = COMP_TARGET_DEF,
+       };
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 7);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 100, 1, BRIGHTNESS_DEF);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 96, 8, CONTRAST_DEF);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 100, 1, SATURATION_DEF);
+       sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+                       V4L2_CID_POWER_LINE_FREQUENCY,
+                       V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+                       FREQ_DEF);
+       if (sd->params.qx3.qx3_detected) {
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                               V4L2_CID_ILLUMINATORS_1, 0, 1, 1,
+                               ILLUMINATORS_1_DEF);
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                               V4L2_CID_ILLUMINATORS_2, 0, 1, 1,
+                               ILLUMINATORS_2_DEF);
+       }
+       v4l2_ctrl_new_custom(hdl, &comp_target, NULL);
 
-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;
-       case V4L2_CID_COMP_TARGET:
-               switch (menu->index) {
-               case CPIA_COMPRESSION_TARGET_QUALITY:
-                       strcpy((char *) menu->name, "Quality");
-                       return 0;
-               case CPIA_COMPRESSION_TARGET_FRAMERATE:
-                       strcpy((char *) menu->name, "Framerate");
-                       return 0;
-               }
-               break;
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
        }
-       return -EINVAL;
+       return 0;
 }
 
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .dq_callback = sd_dq_callback,
        .pkt_scan = sd_pkt_scan,
-       .querymenu = sd_querymenu,
 #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
        .other_input = 1,
 #endif
@@ -2129,6 +1898,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 81a4adbd9f7cd90f2e212b78cf55dd466b3a76b6..38f68e11c3a23084ede78e3091dbda8c8302359f 100644 (file)
@@ -32,9 +32,6 @@ MODULE_LICENSE("GPL");
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
 
-       unsigned char brightness;
-       unsigned char contrast;
-       unsigned char colors;
        unsigned char autogain;
 
        char sensor;
@@ -44,76 +41,6 @@ struct sd {
 #define AG_CNT_START 13
 };
 
-/* 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 const struct ctrl sd_ctrls[] = {
-       {
-        {
-         .id = V4L2_CID_BRIGHTNESS,
-         .type = V4L2_CTRL_TYPE_INTEGER,
-         .name = "Brightness",
-         .minimum = 1,
-         .maximum = 127,
-         .step = 1,
-#define BRIGHTNESS_DEF 63
-         .default_value = BRIGHTNESS_DEF,
-         },
-        .set = sd_setbrightness,
-        .get = sd_getbrightness,
-        },
-       {
-        {
-         .id = V4L2_CID_CONTRAST,
-         .type = V4L2_CTRL_TYPE_INTEGER,
-         .name = "Contrast",
-         .minimum = 0,
-         .maximum = 255,
-         .step = 1,
-#define CONTRAST_DEF 127
-         .default_value = CONTRAST_DEF,
-         },
-        .set = sd_setcontrast,
-        .get = sd_getcontrast,
-        },
-#define COLOR_IDX 2
-       {
-        {
-         .id = V4L2_CID_SATURATION,
-         .type = V4L2_CTRL_TYPE_INTEGER,
-         .name = "Color",
-         .minimum = 0,
-         .maximum = 15,
-         .step = 1,
-#define COLOR_DEF 7
-         .default_value = COLOR_DEF,
-         },
-        .set = sd_setcolors,
-        .get = sd_getcolors,
-        },
-       {
-        {
-         .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,
-        },
-};
-
 static const struct v4l2_pix_format vga_mode[] = {
        {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
                .bytesperline = 320,
@@ -464,36 +391,31 @@ static void Et_init2(struct gspca_dev *gspca_dev)
        reg_w_val(gspca_dev, 0x80, 0x20);       /* 0x20; */
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        int i;
-       __u8 brightness = sd->brightness;
 
        for (i = 0; i < 4; i++)
-               reg_w_val(gspca_dev, ET_O_RED + i, brightness);
+               reg_w_val(gspca_dev, ET_O_RED + i, val);
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        __u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 };
-       __u8 contrast = sd->contrast;
 
-       memset(RGBG, contrast, sizeof(RGBG) - 2);
+       memset(RGBG, val, sizeof(RGBG) - 2);
        reg_w(gspca_dev, ET_G_RED, RGBG, 6);
 }
 
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        __u8 I2cc[] = { 0x05, 0x02, 0x02, 0x05, 0x0d };
        __u8 i2cflags = 0x01;
        /* __u8 green = 0; */
-       __u8 colors = sd->colors;
 
-       I2cc[3] = colors;       /* red */
-       I2cc[0] = 15 - colors;  /* blue */
+       I2cc[3] = val;  /* red */
+       I2cc[0] = 15 - val;     /* blue */
        /* green = 15 - ((((7*I2cc[0]) >> 2 ) + I2cc[3]) >> 1); */
        /* I2cc[1] = I2cc[2] = green; */
        if (sd->sensor == SENSOR_PAS106) {
@@ -504,15 +426,16 @@ static void setcolors(struct gspca_dev *gspca_dev)
                I2cc[3], I2cc[0], green); */
 }
 
-static void getcolors(struct gspca_dev *gspca_dev)
+static s32 getcolors(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
        if (sd->sensor == SENSOR_PAS106) {
 /*             i2c_r(gspca_dev, PAS106_REG9);           * blue */
                i2c_r(gspca_dev, PAS106_REG9 + 3);      /* red */
-               sd->colors = gspca_dev->usb_buf[0] & 0x0f;
+               return gspca_dev->usb_buf[0] & 0x0f;
        }
+       return 0;
 }
 
 static void setautogain(struct gspca_dev *gspca_dev)
@@ -622,8 +545,7 @@ static void Et_init1(struct gspca_dev *gspca_dev)
        i2c_w(gspca_dev, PAS106_REG7, I2c4, sizeof I2c4, 1);
        /* now set by fifo the whole colors setting */
        reg_w(gspca_dev, ET_G_RED, GainRGBG, 6);
-       getcolors(gspca_dev);
-       setcolors(gspca_dev);
+       setcolors(gspca_dev, getcolors(gspca_dev));
 }
 
 /* this function is called at probe time */
@@ -641,12 +563,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
        } else {
                cam->cam_mode = vga_mode;
                cam->nmodes = ARRAY_SIZE(vga_mode);
-               gspca_dev->ctrl_dis = (1 << COLOR_IDX);
        }
-       sd->brightness = BRIGHTNESS_DEF;
-       sd->contrast = CONTRAST_DEF;
-       sd->colors = COLOR_DEF;
-       sd->autogain = AUTOGAIN_DEF;
        sd->ag_cnt = -1;
        return 0;
 }
@@ -780,85 +697,68 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        }
 }
 
-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)
-               setbrightness(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)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->contrast = val;
-       if (gspca_dev->streaming)
-               setcontrast(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_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->autogain = val;
-       if (gspca_dev->streaming)
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
+
+       gspca_dev->usb_err = 0;
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SATURATION:
+               setcolors(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               sd->autogain = ctrl->val;
                setautogain(gspca_dev);
-       return 0;
+               break;
+       }
+       return gspca_dev->usb_err;
 }
 
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-       *val = sd->autogain;
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 4);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 1, 127, 1, 63);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 255, 1, 127);
+       if (sd->sensor == SENSOR_PAS106)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 15, 1, 7);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
        return 0;
 }
 
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .pkt_scan = sd_pkt_scan,
@@ -892,6 +792,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 6e26c93b4656934aa3c3de03f29766f77db7ca4c..c8f2201cc35ad96403230150b999363a7f362a93 100644 (file)
@@ -299,6 +299,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume  = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index c549574c1c7ea195744e083bf5c94b2aa8d36239..ced3b71f14e5d52628e464c8601d7a41a3438925 100644 (file)
@@ -521,6 +521,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend    = gspca_suspend,
        .resume     = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 31721eadc597de48e749f6202a98ddb819910cbf..d4e8343f5b1013b17b1d9541bbaf287c193a6b48 100644 (file)
@@ -930,6 +930,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
                        goto out;
                }
                gspca_dev->streaming = 1;
+               v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
 
                /* some bulk transfers are started by the subdriver */
                if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0)
@@ -1049,12 +1050,6 @@ static int vidioc_g_register(struct file *file, void *priv,
 {
        struct gspca_dev *gspca_dev = video_drvdata(file);
 
-       if (!gspca_dev->sd_desc->get_chip_ident)
-               return -ENOTTY;
-
-       if (!gspca_dev->sd_desc->get_register)
-               return -ENOTTY;
-
        gspca_dev->usb_err = 0;
        return gspca_dev->sd_desc->get_register(gspca_dev, reg);
 }
@@ -1064,12 +1059,6 @@ static int vidioc_s_register(struct file *file, void *priv,
 {
        struct gspca_dev *gspca_dev = video_drvdata(file);
 
-       if (!gspca_dev->sd_desc->get_chip_ident)
-               return -ENOTTY;
-
-       if (!gspca_dev->sd_desc->set_register)
-               return -ENOTTY;
-
        gspca_dev->usb_err = 0;
        return gspca_dev->sd_desc->set_register(gspca_dev, reg);
 }
@@ -1080,9 +1069,6 @@ static int vidioc_g_chip_ident(struct file *file, void *priv,
 {
        struct gspca_dev *gspca_dev = video_drvdata(file);
 
-       if (!gspca_dev->sd_desc->get_chip_ident)
-               return -ENOTTY;
-
        gspca_dev->usb_err = 0;
        return gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip);
 }
@@ -1136,8 +1122,10 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
        int mode;
 
        mode = gspca_dev->curr_mode;
-       memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode],
-               sizeof fmt->fmt.pix);
+       fmt->fmt.pix = gspca_dev->cam.cam_mode[mode];
+       /* some drivers use priv internally, zero it before giving it to
+          userspace */
+       fmt->fmt.pix.priv = 0;
        return 0;
 }
 
@@ -1168,8 +1156,10 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
 /*             else
                        ;                * no chance, return this mode */
        }
-       memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode],
-               sizeof fmt->fmt.pix);
+       fmt->fmt.pix = gspca_dev->cam.cam_mode[mode];
+       /* some drivers use priv internally, zero it before giving it to
+          userspace */
+       fmt->fmt.pix.priv = 0;
        return mode;                    /* used when s_fmt */
 }
 
@@ -1284,9 +1274,6 @@ static void gspca_release(struct v4l2_device *v4l2_device)
        struct gspca_dev *gspca_dev =
                container_of(v4l2_device, struct gspca_dev, v4l2_dev);
 
-       PDEBUG(D_PROBE, "%s released",
-               video_device_node_name(&gspca_dev->vdev));
-
        v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler);
        v4l2_device_unregister(&gspca_dev->v4l2_dev);
        kfree(gspca_dev->usb_buf);
@@ -1694,8 +1681,6 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv,
 {
        struct gspca_dev *gspca_dev = video_drvdata(file);
 
-       if (!gspca_dev->sd_desc->get_jcomp)
-               return -ENOTTY;
        gspca_dev->usb_err = 0;
        return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
 }
@@ -1705,8 +1690,6 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv,
 {
        struct gspca_dev *gspca_dev = video_drvdata(file);
 
-       if (!gspca_dev->sd_desc->set_jcomp)
-               return -ENOTTY;
        gspca_dev->usb_err = 0;
        return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
 }
@@ -2290,6 +2273,20 @@ int gspca_dev_probe2(struct usb_interface *intf,
        v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF);
        v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF);
        v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF);
+       if (!gspca_dev->sd_desc->get_chip_ident)
+               v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_CHIP_IDENT);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       if (!gspca_dev->sd_desc->get_chip_ident ||
+           !gspca_dev->sd_desc->get_register)
+               v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER);
+       if (!gspca_dev->sd_desc->get_chip_ident ||
+           !gspca_dev->sd_desc->set_register)
+               v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_S_REGISTER);
+#endif
+       if (!gspca_dev->sd_desc->get_jcomp)
+               v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_G_JPEGCOMP);
+       if (!gspca_dev->sd_desc->set_jcomp)
+               v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_S_JPEGCOMP);
 
        /* init video stuff */
        ret = video_register_device(&gspca_dev->vdev,
@@ -2429,7 +2426,6 @@ int gspca_resume(struct usb_interface *intf)
         */
        streaming = gspca_dev->streaming;
        gspca_dev->streaming = 0;
-       v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
        if (streaming)
                ret = gspca_init_transfer(gspca_dev);
        mutex_unlock(&gspca_dev->usb_lock);
index 5ab3f7e12760dcf4c8490fb77d9cdeac46ec9c2e..26b99310d628f50d4be973e317f7a84104d0c449 100644 (file)
@@ -54,21 +54,13 @@ enum {
 #define CAMQUALITY_MIN 0       /* highest cam quality */
 #define CAMQUALITY_MAX 97      /* lowest cam quality  */
 
-enum e_ctrl {
-       LIGHTFREQ,
-       AUTOGAIN,
-       RED,
-       GREEN,
-       BLUE,
-       NCTRLS          /* number of controls */
-};
-
 /* Structure to hold all of our device specific stuff */
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
-       struct gspca_ctrl ctrls[NCTRLS];
        int blocks_left;
        const struct v4l2_pix_format *cap_mode;
+       struct v4l2_ctrl *freq;
+       struct v4l2_ctrl *jpegqual;
        /* Driver stuff */
        u8 type;
        u8 quality;                              /* image quality */
@@ -139,23 +131,21 @@ static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
        }
 }
 
-static void setfreq(struct gspca_dev *gspca_dev)
+static void setfreq(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 freq_commands[][2] = {
                {0x71, 0x80},
                {0x70, 0x07}
        };
 
-       freq_commands[0][1] |= (sd->ctrls[LIGHTFREQ].val >> 1);
+       freq_commands[0][1] |= val >> 1;
 
        jlj_write2(gspca_dev, freq_commands[0]);
        jlj_write2(gspca_dev, freq_commands[1]);
 }
 
-static void setcamquality(struct gspca_dev *gspca_dev)
+static void setcamquality(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 quality_commands[][2] = {
                {0x71, 0x1E},
                {0x70, 0x06}
@@ -163,7 +153,7 @@ static void setcamquality(struct gspca_dev *gspca_dev)
        u8 camquality;
 
        /* adapt camera quality from jpeg quality */
-       camquality = ((QUALITY_MAX - sd->quality) * CAMQUALITY_MAX)
+       camquality = ((QUALITY_MAX - val) * CAMQUALITY_MAX)
                / (QUALITY_MAX - QUALITY_MIN);
        quality_commands[0][1] += camquality;
 
@@ -171,130 +161,58 @@ static void setcamquality(struct gspca_dev *gspca_dev)
        jlj_write2(gspca_dev, quality_commands[1]);
 }
 
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 autogain_commands[][2] = {
                {0x94, 0x02},
                {0xcf, 0x00}
        };
 
-       autogain_commands[1][1] = (sd->ctrls[AUTOGAIN].val << 4);
+       autogain_commands[1][1] = val << 4;
 
        jlj_write2(gspca_dev, autogain_commands[0]);
        jlj_write2(gspca_dev, autogain_commands[1]);
 }
 
-static void setred(struct gspca_dev *gspca_dev)
+static void setred(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 setred_commands[][2] = {
                {0x94, 0x02},
                {0xe6, 0x00}
        };
 
-       setred_commands[1][1] = sd->ctrls[RED].val;
+       setred_commands[1][1] = val;
 
        jlj_write2(gspca_dev, setred_commands[0]);
        jlj_write2(gspca_dev, setred_commands[1]);
 }
 
-static void setgreen(struct gspca_dev *gspca_dev)
+static void setgreen(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 setgreen_commands[][2] = {
                {0x94, 0x02},
                {0xe7, 0x00}
        };
 
-       setgreen_commands[1][1] = sd->ctrls[GREEN].val;
+       setgreen_commands[1][1] = val;
 
        jlj_write2(gspca_dev, setgreen_commands[0]);
        jlj_write2(gspca_dev, setgreen_commands[1]);
 }
 
-static void setblue(struct gspca_dev *gspca_dev)
+static void setblue(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 setblue_commands[][2] = {
                {0x94, 0x02},
                {0xe9, 0x00}
        };
 
-       setblue_commands[1][1] = sd->ctrls[BLUE].val;
+       setblue_commands[1][1] = val;
 
        jlj_write2(gspca_dev, setblue_commands[0]);
        jlj_write2(gspca_dev, setblue_commands[1]);
 }
 
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[LIGHTFREQ] = {
-           {
-               .id      = V4L2_CID_POWER_LINE_FREQUENCY,
-               .type    = V4L2_CTRL_TYPE_MENU,
-               .name    = "Light frequency filter",
-               .minimum = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, /* 1 */
-               .maximum = V4L2_CID_POWER_LINE_FREQUENCY_60HZ, /* 2 */
-               .step    = 1,
-               .default_value = V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
-           },
-           .set_control = setfreq
-       },
-[AUTOGAIN] = {
-           {
-               .id = V4L2_CID_AUTOGAIN,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Automatic Gain (and Exposure)",
-               .minimum = 0,
-               .maximum = 3,
-               .step = 1,
-#define AUTOGAIN_DEF 0
-               .default_value = AUTOGAIN_DEF,
-          },
-          .set_control = setautogain
-       },
-[RED] = {
-           {
-               .id = V4L2_CID_RED_BALANCE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "red balance",
-               .minimum = 0,
-               .maximum = 3,
-               .step = 1,
-#define RED_BALANCE_DEF 2
-               .default_value = RED_BALANCE_DEF,
-          },
-          .set_control = setred
-       },
-
-[GREEN]        = {
-           {
-               .id = V4L2_CID_GAIN,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "green balance",
-               .minimum = 0,
-               .maximum = 3,
-               .step = 1,
-#define GREEN_BALANCE_DEF 2
-               .default_value = GREEN_BALANCE_DEF,
-          },
-          .set_control = setgreen
-       },
-[BLUE] = {
-           {
-               .id = V4L2_CID_BLUE_BALANCE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "blue balance",
-               .minimum = 0,
-               .maximum = 3,
-               .step = 1,
-#define BLUE_BALANCE_DEF 2
-               .default_value = BLUE_BALANCE_DEF,
-          },
-          .set_control = setblue
-       },
-};
-
 static int jlj_start(struct gspca_dev *gspca_dev)
 {
        int i;
@@ -344,9 +262,9 @@ static int jlj_start(struct gspca_dev *gspca_dev)
                if (start_commands[i].ack_wanted)
                        jlj_read1(gspca_dev, response);
        }
-       setcamquality(gspca_dev);
+       setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
        msleep(2);
-       setfreq(gspca_dev);
+       setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq));
        if (gspca_dev->usb_err < 0)
                PDEBUG(D_ERR, "Start streaming command failed");
        return gspca_dev->usb_err;
@@ -403,7 +321,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
        struct sd *dev  = (struct sd *) gspca_dev;
 
        dev->type = id->driver_info;
-       gspca_dev->cam.ctrls = dev->ctrls;
        dev->quality = QUALITY_DEF;
 
        cam->cam_mode = jlj_mode;
@@ -479,25 +396,81 @@ static const struct usb_device_id device_table[] = {
 
 MODULE_DEVICE_TABLE(usb, device_table);
 
-static int sd_querymenu(struct gspca_dev *gspca_dev,
-                       struct v4l2_querymenu *menu)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       switch (menu->id) {
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
+
+       gspca_dev->usb_err = 0;
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
        case V4L2_CID_POWER_LINE_FREQUENCY:
-               switch (menu->index) {
-               case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
-                       strcpy((char *) menu->name, "disable");
-                       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;
-               }
+               setfreq(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_RED_BALANCE:
+               setred(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_GAIN:
+               setgreen(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               setblue(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               setautogain(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+               jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
+               setcamquality(gspca_dev, ctrl->val);
                break;
        }
-       return -EINVAL;
+       return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+       static const struct v4l2_ctrl_config custom_autogain = {
+               .ops = &sd_ctrl_ops,
+               .id = V4L2_CID_AUTOGAIN,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Automatic Gain (and Exposure)",
+               .max = 3,
+               .step = 1,
+               .def = 0,
+       };
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 6);
+       sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+                       V4L2_CID_POWER_LINE_FREQUENCY,
+                       V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
+                       V4L2_CID_POWER_LINE_FREQUENCY_60HZ);
+       v4l2_ctrl_new_custom(hdl, &custom_autogain, NULL);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_RED_BALANCE, 0, 3, 1, 2);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, 0, 3, 1, 2);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BLUE_BALANCE, 0, 3, 1, 2);
+       sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_JPEG_COMPRESSION_QUALITY,
+                       QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       return 0;
 }
 
 static int sd_set_jcomp(struct gspca_dev *gspca_dev,
@@ -505,16 +478,7 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       if (jcomp->quality < QUALITY_MIN)
-               sd->quality = QUALITY_MIN;
-       else if (jcomp->quality > QUALITY_MAX)
-               sd->quality = QUALITY_MAX;
-       else
-               sd->quality = jcomp->quality;
-       if (gspca_dev->streaming) {
-               jpeg_set_qual(sd->jpeg_hdr, sd->quality);
-               setcamquality(gspca_dev);
-       }
+       v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
        return 0;
 }
 
@@ -524,7 +488,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
        struct sd *sd = (struct sd *) gspca_dev;
 
        memset(jcomp, 0, sizeof *jcomp);
-       jcomp->quality = sd->quality;
+       jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
        jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
                        | V4L2_JPEG_MARKER_DQT;
        return 0;
@@ -546,12 +510,10 @@ static const struct sd_desc sd_desc_sportscam_dv15 = {
        .name   = MODULE_NAME,
        .config = sd_config,
        .init   = sd_init,
+       .init_controls = sd_init_controls,
        .start  = sd_start,
        .stopN  = sd_stopN,
        .pkt_scan = sd_pkt_scan,
-       .ctrls = sd_ctrls,
-       .nctrls = ARRAY_SIZE(sd_ctrls),
-       .querymenu = sd_querymenu,
        .get_jcomp = sd_get_jcomp,
        .set_jcomp = sd_set_jcomp,
 };
@@ -579,6 +541,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume  = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 9c591c7c6f545b02a49b14c7b231045f29e0d531..cf9d9fca5b84005cd37e24cba853c9d132e504be 100644 (file)
@@ -505,8 +505,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
 /* sub-driver description */
 static const struct sd_desc sd_desc = {
        .name = MODULE_NAME,
-       /* .ctrls = none have been detected */
-       /* .nctrls = ARRAY_SIZE(sd_ctrls),  */
        .config = sd_config,
        .init = sd_init,
        .start = sd_start,
@@ -536,6 +534,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index e8e8f2fe9166efc3a9b717e2f9bb458f995f042d..40ad6687ee5dee1e53cad6489a386438f5d2a483 100644 (file)
@@ -63,12 +63,6 @@ struct sd {
        uint8_t ibuf[0x200];        /* input buffer for control commands */
 };
 
-/* V4L2 controls supported by the driver */
-/* controls prototypes here */
-
-static const struct ctrl sd_ctrls[] = {
-};
-
 #define MODE_640x480   0x0001
 #define MODE_640x488   0x0002
 #define MODE_1280x1024 0x0004
@@ -373,15 +367,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int 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,
        .pkt_scan  = sd_pkt_scan,
        /*
-       .querymenu = sd_querymenu,
        .get_streamparm = sd_get_streamparm,
        .set_streamparm = sd_set_streamparm,
        */
@@ -410,6 +401,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend    = gspca_suspend,
        .resume     = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index f0c0d74dfe928b781aeb486b7ea1d19ed27d138f..bbf91e07e38b759cb428e254a92c8f79e61b8ca6 100644 (file)
@@ -50,107 +50,8 @@ struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
        struct urb *last_data_urb;
        u8 snapshot_pressed;
-       u8 brightness;
-       u8 contrast;
-       u8 saturation;
-       u8 whitebal;
-       u8 sharpness;
 };
 
-/* 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_setsaturation(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setwhitebal(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getwhitebal(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 const struct ctrl sd_ctrls[] = {
-#define SD_BRIGHTNESS 0
-       {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 9,
-               .step = 1,
-#define BRIGHTNESS_DEFAULT 4
-               .default_value = BRIGHTNESS_DEFAULT,
-               .flags = 0,
-           },
-           .set = sd_setbrightness,
-           .get = sd_getbrightness,
-       },
-#define SD_CONTRAST 1
-       {
-           {
-               .id = V4L2_CID_CONTRAST,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Contrast",
-               .minimum = 0,
-               .maximum = 9,
-               .step = 4,
-#define CONTRAST_DEFAULT 10
-               .default_value = CONTRAST_DEFAULT,
-               .flags = 0,
-           },
-           .set = sd_setcontrast,
-           .get = sd_getcontrast,
-       },
-#define SD_SATURATION 2
-       {
-           {
-               .id     = V4L2_CID_SATURATION,
-               .type   = V4L2_CTRL_TYPE_INTEGER,
-               .name   = "Saturation",
-               .minimum = 0,
-               .maximum = 9,
-               .step   = 1,
-#define SATURATION_DEFAULT 4
-               .default_value = SATURATION_DEFAULT,
-               .flags = 0,
-           },
-           .set = sd_setsaturation,
-           .get = sd_getsaturation,
-       },
-#define SD_WHITEBAL 3
-       {
-           {
-               .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "White Balance",
-               .minimum = 0,
-               .maximum = 33,
-               .step = 1,
-#define WHITEBAL_DEFAULT 25
-               .default_value = WHITEBAL_DEFAULT,
-               .flags = 0,
-           },
-           .set = sd_setwhitebal,
-           .get = sd_getwhitebal,
-       },
-#define SD_SHARPNESS 4
-       {
-           {
-               .id = V4L2_CID_SHARPNESS,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Sharpness",
-               .minimum = 0,
-               .maximum = 9,
-               .step = 1,
-#define SHARPNESS_DEFAULT 4
-               .default_value = SHARPNESS_DEFAULT,
-               .flags = 0,
-           },
-           .set = sd_setsharpness,
-           .get = sd_getsharpness,
-       },
-};
 
 /* .priv is what goes to register 8 for this mode, known working values:
    0x00 -> 176x144, cropped
@@ -202,7 +103,8 @@ static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
                        0,
                        1000);
        if (ret < 0) {
-               pr_err("reg_w err %d\n", ret);
+               pr_err("reg_w err writing %02x to %02x: %d\n",
+                      value, index, ret);
                gspca_dev->usb_err = ret;
        }
 }
@@ -223,7 +125,7 @@ static void reg_r(struct gspca_dev *gspca_dev, u16 value, u16 index)
                        2,
                        1000);
        if (ret < 0) {
-               pr_err("reg_w err %d\n", ret);
+               pr_err("reg_r err %d\n", ret);
                gspca_dev->usb_err = ret;
        }
 }
@@ -242,34 +144,33 @@ static void konica_stream_off(struct gspca_dev *gspca_dev)
 static int sd_config(struct gspca_dev *gspca_dev,
                        const struct usb_device_id *id)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
        gspca_dev->cam.cam_mode = vga_mode;
        gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
        gspca_dev->cam.no_urb_create = 1;
 
-       sd->brightness  = BRIGHTNESS_DEFAULT;
-       sd->contrast    = CONTRAST_DEFAULT;
-       sd->saturation  = SATURATION_DEFAULT;
-       sd->whitebal    = WHITEBAL_DEFAULT;
-       sd->sharpness   = SHARPNESS_DEFAULT;
-
        return 0;
 }
 
 /* this function is called at probe and resume time */
 static int sd_init(struct gspca_dev *gspca_dev)
 {
-       /* HDG not sure if these 2 reads are needed */
-       reg_r(gspca_dev, 0, 0x10);
-       PDEBUG(D_PROBE, "Reg 0x10 reads: %02x %02x",
-              gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
-       reg_r(gspca_dev, 0, 0x10);
-       PDEBUG(D_PROBE, "Reg 0x10 reads: %02x %02x",
-              gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+       int i;
+
+       /*
+        * The konica needs a freaking large time to "boot" (approx 6.5 sec.),
+        * and does not want to be bothered while doing so :|
+        * Register 0x10 counts from 1 - 3, with 3 being "ready"
+        */
+       msleep(6000);
+       for (i = 0; i < 20; i++) {
+               reg_r(gspca_dev, 0, 0x10);
+               if (gspca_dev->usb_buf[0] == 3)
+                       break;
+               msleep(100);
+       }
        reg_w(gspca_dev, 0, 0x0d);
 
-       return 0;
+       return gspca_dev->usb_err;
 }
 
 static int sd_start(struct gspca_dev *gspca_dev)
@@ -289,12 +190,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
        packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
 
-       reg_w(gspca_dev, sd->brightness, BRIGHTNESS_REG);
-       reg_w(gspca_dev, sd->whitebal, WHITEBAL_REG);
-       reg_w(gspca_dev, sd->contrast, CONTRAST_REG);
-       reg_w(gspca_dev, sd->saturation, SATURATION_REG);
-       reg_w(gspca_dev, sd->sharpness, SHARPNESS_REG);
-
        n = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
        reg_w(gspca_dev, n, 0x08);
 
@@ -479,125 +374,82 @@ resubmit:
                pr_err("usb_submit_urb(status_urb) ret %d\n", st);
 }
 
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->brightness = val;
-       if (gspca_dev->streaming) {
-               konica_stream_off(gspca_dev);
-               reg_w(gspca_dev, sd->brightness, BRIGHTNESS_REG);
-               konica_stream_on(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;
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
 
-       return 0;
-}
+       gspca_dev->usb_err = 0;
 
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       if (!gspca_dev->streaming)
+               return 0;
 
-       sd->contrast = val;
-       if (gspca_dev->streaming) {
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
                konica_stream_off(gspca_dev);
-               reg_w(gspca_dev, sd->contrast, CONTRAST_REG);
+               reg_w(gspca_dev, ctrl->val, BRIGHTNESS_REG);
                konica_stream_on(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_setsaturation(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->saturation = val;
-       if (gspca_dev->streaming) {
+               break;
+       case V4L2_CID_CONTRAST:
                konica_stream_off(gspca_dev);
-               reg_w(gspca_dev, sd->saturation, SATURATION_REG);
+               reg_w(gspca_dev, ctrl->val, CONTRAST_REG);
                konica_stream_on(gspca_dev);
-       }
-       return 0;
-}
-
-static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->saturation;
-
-       return 0;
-}
-
-static int sd_setwhitebal(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->whitebal = val;
-       if (gspca_dev->streaming) {
+               break;
+       case V4L2_CID_SATURATION:
                konica_stream_off(gspca_dev);
-               reg_w(gspca_dev, sd->whitebal, WHITEBAL_REG);
+               reg_w(gspca_dev, ctrl->val, SATURATION_REG);
                konica_stream_on(gspca_dev);
-       }
-       return 0;
-}
-
-static int sd_getwhitebal(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->whitebal;
-
-       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) {
+               break;
+       case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
                konica_stream_off(gspca_dev);
-               reg_w(gspca_dev, sd->sharpness, SHARPNESS_REG);
+               reg_w(gspca_dev, ctrl->val, WHITEBAL_REG);
                konica_stream_on(gspca_dev);
+               break;
+       case V4L2_CID_SHARPNESS:
+               konica_stream_off(gspca_dev);
+               reg_w(gspca_dev, ctrl->val, SHARPNESS_REG);
+               konica_stream_on(gspca_dev);
+               break;
        }
-       return 0;
+       return gspca_dev->usb_err;
 }
 
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->sharpness;
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 5);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 9, 1, 4);
+       /* Needs to be verified */
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 9, 1, 4);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 9, 1, 4);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+                       0, 33, 1, 25);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SHARPNESS, 0, 9, 1, 4);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
        return 0;
 }
 
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
 #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
@@ -628,6 +480,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 0c4493675438ee1e0cabd3b6bb87a85153713de7..ed22638978ce158f91b98155a7794b38bd770170 100644 (file)
@@ -400,6 +400,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
        .disconnect = m5602_disconnect
 };
index ec7b21ee79fb24db35390fccd30cd7114a323377..ff2c5abf115ba635d59a363f190d04acb21ceb66 100644 (file)
@@ -30,6 +30,8 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
 MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver");
 MODULE_LICENSE("GPL");
 
+#define QUALITY 50
+
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
@@ -42,13 +44,6 @@ struct sd {
                struct v4l2_ctrl *illum_top;
                struct v4l2_ctrl *illum_bottom;
        };
-       struct v4l2_ctrl *jpegqual;
-
-       u8 quality;
-#define QUALITY_MIN 40
-#define QUALITY_MAX 70
-#define QUALITY_DEF 50
-
        u8 jpeg_hdr[JPEG_HDR_SZ];
 };
 
@@ -194,9 +189,6 @@ static int mars_s_ctrl(struct v4l2_ctrl *ctrl)
        case V4L2_CID_SHARPNESS:
                setsharpness(gspca_dev, ctrl->val);
                break;
-       case V4L2_CID_JPEG_COMPRESSION_QUALITY:
-               jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
-               break;
        default:
                return -EINVAL;
        }
@@ -214,7 +206,7 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
        struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
 
        gspca_dev->vdev.ctrl_handler = hdl;
-       v4l2_ctrl_handler_init(hdl, 7);
+       v4l2_ctrl_handler_init(hdl, 6);
        sd->brightness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
                        V4L2_CID_BRIGHTNESS, 0, 30, 1, 15);
        sd->saturation = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
@@ -229,9 +221,6 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
        sd->illum_bottom = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
                        V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0);
        sd->illum_bottom->flags |= V4L2_CTRL_FLAG_UPDATE;
-       sd->jpegqual = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
-                       V4L2_CID_JPEG_COMPRESSION_QUALITY,
-                       QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
        if (hdl->error) {
                pr_err("Could not initialize controls\n");
                return hdl->error;
@@ -244,13 +233,11 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
 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;
        cam->cam_mode = vga_mode;
        cam->nmodes = ARRAY_SIZE(vga_mode);
-       sd->quality = QUALITY_DEF;
        return 0;
 }
 
@@ -269,7 +256,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
        /* create the JPEG header */
        jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
                        0x21);          /* JPEG 422 */
-       jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual));
+       jpeg_set_qual(sd->jpeg_hdr, QUALITY);
 
        data = gspca_dev->usb_buf;
 
@@ -411,31 +398,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
-                       struct v4l2_jpegcompression *jcomp)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       int ret;
-
-       ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
-       if (ret)
-               return ret;
-       jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
-       return 0;
-}
-
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
-                       struct v4l2_jpegcompression *jcomp)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       memset(jcomp, 0, sizeof *jcomp);
-       jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
-       jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
-                       | V4L2_JPEG_MARKER_DQT;
-       return 0;
-}
-
 /* sub-driver description */
 static const struct sd_desc sd_desc = {
        .name = MODULE_NAME,
@@ -445,8 +407,6 @@ static const struct sd_desc sd_desc = {
        .start = sd_start,
        .stopN = sd_stopN,
        .pkt_scan = sd_pkt_scan,
-       .get_jcomp = sd_get_jcomp,
-       .set_jcomp = sd_set_jcomp,
 };
 
 /* -- module initialisation -- */
index d73e5bd3dbf7e52095e723fea53dac63f8ff6dde..8f4714df599035574fd258c2257573ccfe591c36 100644 (file)
@@ -67,6 +67,7 @@
 #define MR97310A_CS_GAIN_MAX           0x7ff
 #define MR97310A_CS_GAIN_DEFAULT       0x110
 
+#define MR97310A_CID_CLOCKDIV (V4L2_CTRL_CLASS_USER + 0x1000)
 #define MR97310A_MIN_CLOCKDIV_MIN      3
 #define MR97310A_MIN_CLOCKDIV_MAX      8
 #define MR97310A_MIN_CLOCKDIV_DEFAULT  3
@@ -84,17 +85,15 @@ MODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)");
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;  /* !! must be the first item */
+       struct { /* exposure/min_clockdiv control cluster */
+               struct v4l2_ctrl *exposure;
+               struct v4l2_ctrl *min_clockdiv;
+       };
        u8 sof_read;
        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;
-       u32 gain;
-       u8 contrast;
-       u8 min_clockdiv;
 };
 
 struct sensor_w_data {
@@ -105,132 +104,6 @@ struct sensor_w_data {
 };
 
 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_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(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);
-static void setcontrast(struct gspca_dev *gspca_dev);
-
-/* V4L2 controls supported by the driver */
-static const struct ctrl sd_ctrls[] = {
-/* Separate brightness control description for Argus QuickClix as it has
- * different limits from the other mr97310a cameras, and separate gain
- * control for Sakar CyberPix camera. */
-       {
-#define NORM_BRIGHTNESS_IDX 0
-               {
-                       .id = V4L2_CID_BRIGHTNESS,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Brightness",
-                       .minimum = -254,
-                       .maximum = 255,
-                       .step = 1,
-                       .default_value = MR97310A_BRIGHTNESS_DEFAULT,
-                       .flags = 0,
-               },
-               .set = sd_setbrightness,
-               .get = sd_getbrightness,
-       },
-       {
-#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,
-                       .name = "Exposure",
-                       .minimum = MR97310A_EXPOSURE_MIN,
-                       .maximum = MR97310A_EXPOSURE_MAX,
-                       .step = 1,
-                       .default_value = MR97310A_EXPOSURE_DEFAULT,
-                       .flags = 0,
-               },
-               .set = sd_setexposure,
-               .get = sd_getexposure,
-       },
-       {
-#define GAIN_IDX 3
-               {
-                       .id = V4L2_CID_GAIN,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Gain",
-                       .minimum = MR97310A_GAIN_MIN,
-                       .maximum = MR97310A_GAIN_MAX,
-                       .step = 1,
-                       .default_value = MR97310A_GAIN_DEFAULT,
-                       .flags = 0,
-               },
-               .set = sd_setgain,
-               .get = sd_getgain,
-       },
-       {
-#define SAKAR_CS_GAIN_IDX 4
-               {
-                       .id = V4L2_CID_GAIN,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Gain",
-                       .minimum = MR97310A_CS_GAIN_MIN,
-                       .maximum = MR97310A_CS_GAIN_MAX,
-                       .step = 1,
-                       .default_value = MR97310A_CS_GAIN_DEFAULT,
-                       .flags = 0,
-               },
-               .set = sd_setgain,
-               .get = sd_getgain,
-       },
-       {
-#define CONTRAST_IDX 5
-               {
-                       .id = V4L2_CID_CONTRAST,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Contrast",
-                       .minimum = MR97310A_CONTRAST_MIN,
-                       .maximum = MR97310A_CONTRAST_MAX,
-                       .step = 1,
-                       .default_value = MR97310A_CONTRAST_DEFAULT,
-                       .flags = 0,
-               },
-               .set = sd_setcontrast,
-               .get = sd_getcontrast,
-       },
-       {
-#define MIN_CLOCKDIV_IDX 6
-               {
-                       .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[] = {
        {160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
@@ -481,7 +354,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
 {
        struct sd *sd = (struct sd *) gspca_dev;
        struct cam *cam;
-       int gain_default = MR97310A_GAIN_DEFAULT;
        int err_code;
 
        cam = &gspca_dev->cam;
@@ -615,52 +487,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
                       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 << NORM_BRIGHTNESS_IDX) |
-                                             (1 << ARGUS_QC_BRIGHTNESS_IDX) |
-                                             (1 << CONTRAST_IDX) |
-                                             (1 << SAKAR_CS_GAIN_IDX);
-               else
-                       gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
-                                             (1 << CONTRAST_IDX) |
-                                             (1 << SAKAR_CS_GAIN_IDX) |
-                                             (1 << MIN_CLOCKDIV_IDX);
-       } else {
-               /* 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 << CONTRAST_IDX) |
-                                             (1 << SAKAR_CS_GAIN_IDX) |
-                                             (1 << MIN_CLOCKDIV_IDX);
-               else if (sd->sensor_type == 2) {
-                       gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
-                                             (1 << ARGUS_QC_BRIGHTNESS_IDX) |
-                                             (1 << GAIN_IDX) |
-                                             (1 << MIN_CLOCKDIV_IDX);
-                       gain_default = MR97310A_CS_GAIN_DEFAULT;
-               } else if (sd->do_lcd_stop)
-                       /* Argus QuickClix has different brightness limits */
-                       gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
-                                             (1 << CONTRAST_IDX) |
-                                             (1 << SAKAR_CS_GAIN_IDX);
-               else
-                       gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
-                                             (1 << CONTRAST_IDX) |
-                                             (1 << SAKAR_CS_GAIN_IDX);
-       }
-
-       sd->brightness = MR97310A_BRIGHTNESS_DEFAULT;
-       sd->exposure = MR97310A_EXPOSURE_DEFAULT;
-       sd->gain = gain_default;
-       sd->contrast = MR97310A_CONTRAST_DEFAULT;
-       sd->min_clockdiv = MR97310A_MIN_CLOCKDIV_DEFAULT;
-
        return 0;
 }
 
@@ -952,11 +778,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
        if (err_code < 0)
                return err_code;
 
-       setbrightness(gspca_dev);
-       setcontrast(gspca_dev);
-       setexposure(gspca_dev);
-       setgain(gspca_dev);
-
        return isoc_enable(gspca_dev);
 }
 
@@ -971,37 +792,25 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
                lcd_stop(gspca_dev);
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u8 val;
        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 */
        static 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;
-
        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) {
+       if (val > 0) {
                sensor_write1(gspca_dev, sign_reg, 0x00);
-               val = sd->brightness;
        } else {
                sensor_write1(gspca_dev, sign_reg, 0x01);
-               val = (257 - sd->brightness);
+               val = 257 - val;
        }
        /* Use lookup table for funky Argus QuickClix brightness */
        if (sd->do_lcd_stop)
@@ -1010,23 +819,20 @@ static void setbrightness(struct gspca_dev *gspca_dev)
        sensor_write1(gspca_dev, value_reg, val);
 }
 
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int exposure = MR97310A_EXPOSURE_DEFAULT;
        u8 buf[2];
 
-       if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
-               return;
-
        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;
+               exposure = (expo * 9267) / 10000 + 300;
                sensor_write1(gspca_dev, 3, exposure >> 4);
                sensor_write1(gspca_dev, 4, exposure & 0x0f);
        } else if (sd->sensor_type == 2) {
-               exposure = sd->exposure;
+               exposure = expo;
                exposure >>= 3;
                sensor_write1(gspca_dev, 3, exposure >> 8);
                sensor_write1(gspca_dev, 4, exposure & 0xff);
@@ -1038,11 +844,11 @@ static void setexposure(struct gspca_dev *gspca_dev)
 
                   Note our 0 - 4095 exposure is mapped to 0 - 511
                   milliseconds exposure time */
-               u8 clockdiv = (60 * sd->exposure + 7999) / 8000;
+               u8 clockdiv = (60 * expo + 7999) / 8000;
 
                /* Limit framerate to not exceed usb bandwidth */
-               if (clockdiv < sd->min_clockdiv && gspca_dev->width >= 320)
-                       clockdiv = sd->min_clockdiv;
+               if (clockdiv < min_clockdiv && gspca_dev->width >= 320)
+                       clockdiv = min_clockdiv;
                else if (clockdiv < 2)
                        clockdiv = 2;
 
@@ -1051,7 +857,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
 
                /* Frame exposure time in ms = 1000 * clockdiv / 60 ->
                exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */
-               exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv);
+               exposure = (60 * 511 * expo) / (8000 * clockdiv);
                if (exposure > 511)
                        exposure = 511;
 
@@ -1065,125 +871,148 @@ static void setexposure(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u8 gainreg;
 
-       if ((gspca_dev->ctrl_dis & (1 << GAIN_IDX)) &&
-           (gspca_dev->ctrl_dis & (1 << SAKAR_CS_GAIN_IDX)))
-               return;
-
        if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1)
-               sensor_write1(gspca_dev, 0x0e, sd->gain);
+               sensor_write1(gspca_dev, 0x0e, val);
        else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2)
                for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) {
-                       sensor_write1(gspca_dev, gainreg, sd->gain >> 8);
-                       sensor_write1(gspca_dev, gainreg + 1, sd->gain & 0xff);
+                       sensor_write1(gspca_dev, gainreg, val >> 8);
+                       sensor_write1(gspca_dev, gainreg + 1, val & 0xff);
                }
        else
-               sensor_write1(gspca_dev, 0x10, sd->gain);
-}
-
-static void setcontrast(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX))
-               return;
-
-       sensor_write1(gspca_dev, 0x1c, sd->contrast);
-}
-
-
-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)
-               setbrightness(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_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;
+               sensor_write1(gspca_dev, 0x10, val);
 }
 
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->exposure;
-       return 0;
+       sensor_write1(gspca_dev, 0x1c, val);
 }
 
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
 
-       sd->gain = val;
-       if (gspca_dev->streaming)
-               setgain(gspca_dev);
-       return 0;
-}
+       gspca_dev->usb_err = 0;
 
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->gain;
-       return 0;
-}
+       if (!gspca_dev->streaming)
+               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)
-               setcontrast(gspca_dev);
-       return 0;
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_EXPOSURE:
+               setexposure(gspca_dev, sd->exposure->val,
+                           sd->min_clockdiv ? sd->min_clockdiv->val : 0);
+               break;
+       case V4L2_CID_GAIN:
+               setgain(gspca_dev, ctrl->val);
+               break;
+       }
+       return gspca_dev->usb_err;
 }
 
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-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_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+       static const struct v4l2_ctrl_config clockdiv = {
+               .ops = &sd_ctrl_ops,
+               .id = MR97310A_CID_CLOCKDIV,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Minimum Clock Divider",
+               .min = MR97310A_MIN_CLOCKDIV_MIN,
+               .max = MR97310A_MIN_CLOCKDIV_MAX,
+               .step = 1,
+               .def = MR97310A_MIN_CLOCKDIV_DEFAULT,
+       };
+       bool has_brightness = false;
+       bool has_argus_brightness = false;
+       bool has_contrast = false;
+       bool has_gain = false;
+       bool has_cs_gain = false;
+       bool has_exposure = false;
+       bool has_clockdiv = false;
 
-       sd->min_clockdiv = val;
-       if (gspca_dev->streaming)
-               setexposure(gspca_dev);
-       return 0;
-}
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 4);
 
-static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       /* Setup controls depending on camera type */
+       if (sd->cam_type == CAM_TYPE_CIF) {
+               /* No brightness for sensor_type 0 */
+               if (sd->sensor_type == 0)
+                       has_exposure = has_gain = has_clockdiv = true;
+               else
+                       has_exposure = has_gain = has_brightness = true;
+       } else {
+               /* All controls need to be disabled if VGA sensor_type is 0 */
+               if (sd->sensor_type == 0)
+                       ; /* no controls! */
+               else if (sd->sensor_type == 2)
+                       has_exposure = has_cs_gain = has_contrast = true;
+               else if (sd->do_lcd_stop)
+                       has_exposure = has_gain = has_argus_brightness =
+                               has_clockdiv = true;
+               else
+                       has_exposure = has_gain = has_brightness =
+                               has_clockdiv = true;
+       }
 
-       *val = sd->min_clockdiv;
+       /* Separate brightness control description for Argus QuickClix as it has
+        * different limits from the other mr97310a cameras, and separate gain
+        * control for Sakar CyberPix camera. */
+       /*
+        * 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 (has_brightness)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, -254, 255, 1,
+                       MR97310A_BRIGHTNESS_DEFAULT);
+       else if (has_argus_brightness)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 15, 1,
+                       MR97310A_BRIGHTNESS_DEFAULT);
+       if (has_contrast)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, MR97310A_CONTRAST_MIN,
+                       MR97310A_CONTRAST_MAX, 1, MR97310A_CONTRAST_DEFAULT);
+       if (has_gain)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, MR97310A_GAIN_MIN, MR97310A_GAIN_MAX,
+                       1, MR97310A_GAIN_DEFAULT);
+       else if (has_cs_gain)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN,
+                       MR97310A_CS_GAIN_MIN, MR97310A_CS_GAIN_MAX,
+                       1, MR97310A_CS_GAIN_DEFAULT);
+       if (has_exposure)
+               sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_EXPOSURE, MR97310A_EXPOSURE_MIN,
+                       MR97310A_EXPOSURE_MAX, 1, MR97310A_EXPOSURE_DEFAULT);
+       if (has_clockdiv)
+               sd->min_clockdiv = v4l2_ctrl_new_custom(hdl, &clockdiv, NULL);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       if (has_exposure && has_clockdiv)
+               v4l2_ctrl_cluster(2, &sd->exposure);
        return 0;
 }
 
@@ -1221,10 +1050,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .pkt_scan = sd_pkt_scan,
@@ -1256,6 +1084,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 42e021931e60287893638655daadb9c1e37487e5..44c9964b1b3e206085ac1ea3dea8e23048aaead2 100644 (file)
@@ -32,22 +32,10 @@ MODULE_LICENSE("GPL");
 
 static int webcam;
 
-/* controls */
-enum e_ctrl {
-       GAIN,
-       EXPOSURE,
-       AUTOGAIN,
-       NCTRLS          /* number of controls */
-};
-
-#define AUTOGAIN_DEF 1
-
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
 
-       struct gspca_ctrl ctrls[NCTRLS];
-
        u32 ae_res;
        s8 ag_cnt;
 #define AG_CNT_START 13
@@ -1667,17 +1655,13 @@ static int swap_bits(int v)
        return r;
 }
 
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, u8 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u8 val, v[2];
+       u8 v[2];
 
-       val = sd->ctrls[GAIN].val;
        switch (sd->webcam) {
        case P35u:
-               /* Note the control goes from 0-255 not 0-127, but anything
-                  above 127 just means amplifying noise */
-               val >>= 1;                      /* 0 - 255 -> 0 - 127 */
                reg_w(gspca_dev, 0x1026, &val, 1);
                break;
        case Kr651us:
@@ -1690,13 +1674,11 @@ static void setgain(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       s16 val;
        u8 v[2];
 
-       val = sd->ctrls[EXPOSURE].val;
        switch (sd->webcam) {
        case P35u:
                v[0] = ((9 - val) << 3) | 0x01;
@@ -1713,14 +1695,12 @@ static void setexposure(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int w, h;
 
-       if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
-               return;
-       if (!sd->ctrls[AUTOGAIN].val) {
+       if (!val) {
                sd->ag_cnt = -1;
                return;
        }
@@ -1763,7 +1743,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
        if ((unsigned) webcam >= NWEBCAMS)
                webcam = 0;
        sd->webcam = webcam;
-       gspca_dev->cam.ctrls = sd->ctrls;
        gspca_dev->cam.needs_full_bandwidth = 1;
        sd->ag_cnt = -1;
 
@@ -1834,33 +1813,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
                        break;
                }
        }
-       switch (sd->webcam) {
-       case P35u:
-/*             sd->ctrls[EXPOSURE].max = 9;
- *             sd->ctrls[EXPOSURE].def = 9; */
-               /* coarse expo auto gain function gain minimum, to avoid
-                * a large settings jump the first auto adjustment */
-               sd->ctrls[GAIN].def = 255 / 5 * 2;
-               break;
-       case Cvideopro:
-       case DvcV6:
-       case Kritter:
-               gspca_dev->ctrl_dis = (1 << GAIN) | (1 << AUTOGAIN);
-               /* fall thru */
-       case Kr651us:
-               sd->ctrls[EXPOSURE].max = 315;
-               sd->ctrls[EXPOSURE].def = 150;
-               break;
-       default:
-               gspca_dev->ctrl_dis = (1 << GAIN) | (1 << EXPOSURE)
-                                        | (1 << AUTOGAIN);
-               break;
-       }
 
-#if AUTOGAIN_DEF
-       if (!(gspca_dev->ctrl_dis & (1 << AUTOGAIN)))
-               gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
-#endif
        return gspca_dev->usb_err;
 }
 
@@ -1925,9 +1878,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
                break;
        }
 
-       setgain(gspca_dev);
-       setexposure(gspca_dev);
-       setautogain(gspca_dev);
        sd->exp_too_high_cnt = 0;
        sd->exp_too_low_cnt = 0;
        return gspca_dev->usb_err;
@@ -1987,24 +1937,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        }
 }
 
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->ctrls[AUTOGAIN].val = val;
-       if (val)
-               gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
-       else
-               gspca_dev->ctrl_inac = 0;
-       if (gspca_dev->streaming)
-               setautogain(gspca_dev);
-       return gspca_dev->usb_err;
-}
-
-#define WANT_REGULAR_AUTOGAIN
-#define WANT_COARSE_EXPO_AUTOGAIN
-#include "autogain_functions.h"
-
 static void do_autogain(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -2024,62 +1956,100 @@ static void do_autogain(struct gspca_dev *gspca_dev)
 
        switch (sd->webcam) {
        case P35u:
-               coarse_grained_expo_autogain(gspca_dev, luma, 100, 5);
+               gspca_coarse_grained_expo_autogain(gspca_dev, luma, 100, 5);
                break;
        default:
-               auto_gain_n_exposure(gspca_dev, luma, 100, 5, 230, 0);
+               gspca_expo_autogain(gspca_dev, luma, 100, 5, 230, 0);
                break;
        }
 }
 
-/* V4L2 controls supported by the driver */
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[GAIN] = {
-           {
-               .id      = V4L2_CID_GAIN,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Gain",
-               .minimum = 0,
-               .maximum = 253,
-               .step    = 1,
-               .default_value = 128
-           },
-           .set_control = setgain
-       },
-[EXPOSURE] = {
-           {
-               .id      = V4L2_CID_EXPOSURE,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Exposure",
-               .minimum = 0,
-               .maximum = 9,
-               .step    = 1,
-               .default_value = 9
-           },
-           .set_control = setexposure
-       },
-[AUTOGAIN] = {
-           {
-               .id      = V4L2_CID_AUTOGAIN,
-               .type    = V4L2_CTRL_TYPE_BOOLEAN,
-               .name    = "Auto Gain",
-               .minimum = 0,
-               .maximum = 1,
-               .step    = 1,
-               .default_value = AUTOGAIN_DEF,
-               .flags   = V4L2_CTRL_FLAG_UPDATE
-           },
-           .set = sd_setautogain
-       },
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+       gspca_dev->usb_err = 0;
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       /* autogain/gain/exposure control cluster */
+       case V4L2_CID_AUTOGAIN:
+               if (ctrl->is_new)
+                       setautogain(gspca_dev, ctrl->val);
+               if (!ctrl->val) {
+                       if (gspca_dev->gain->is_new)
+                               setgain(gspca_dev, gspca_dev->gain->val);
+                       if (gspca_dev->exposure->is_new)
+                               setexposure(gspca_dev,
+                                           gspca_dev->exposure->val);
+               }
+               break;
+       /* Some webcams only have exposure, so handle that separately from the
+          autogain/gain/exposure cluster in the previous case. */
+       case V4L2_CID_EXPOSURE:
+               setexposure(gspca_dev, gspca_dev->exposure->val);
+               break;
+       }
+       return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
 };
 
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 3);
+       switch (sd->webcam) {
+       case P35u:
+               gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+               /* For P35u choose coarse expo auto gain function gain minimum,
+                * to avoid a large settings jump the first auto adjustment */
+               gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, 0, 127, 1, 127 / 5 * 2);
+               gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_EXPOSURE, 0, 9, 1, 9);
+               break;
+       case Kr651us:
+               gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+               gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, 0, 253, 1, 128);
+               /* fall through */
+       case Cvideopro:
+       case DvcV6:
+       case Kritter:
+               gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_EXPOSURE, 0, 315, 1, 150);
+               break;
+       default:
+               break;
+       }
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       if (gspca_dev->autogain)
+               v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+       return 0;
+}
+
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .pkt_scan = sd_pkt_scan,
@@ -2117,6 +2087,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 183457c5cfdb96c371a18dd638a3ac9bab247a71..bfc7cefa59f8ac2a0dd55f2b706133a020307047 100644 (file)
@@ -60,25 +60,20 @@ static int frame_rate;
  * are getting "Failed to read sensor ID..." */
 static int i2c_detect_tries = 10;
 
-/* controls */
-enum e_ctrl {
-       BRIGHTNESS,
-       CONTRAST,
-       EXPOSURE,
-       COLORS,
-       HFLIP,
-       VFLIP,
-       AUTOBRIGHT,
-       AUTOGAIN,
-       FREQ,
-       NCTRL           /* number of controls */
-};
-
 /* ov519 device descriptor */
 struct sd {
        struct gspca_dev gspca_dev;             /* !! must be the first item */
 
-       struct gspca_ctrl ctrls[NCTRL];
+       struct v4l2_ctrl *jpegqual;
+       struct v4l2_ctrl *freq;
+       struct { /* h/vflip control cluster */
+               struct v4l2_ctrl *hflip;
+               struct v4l2_ctrl *vflip;
+       };
+       struct { /* autobrightness/brightness control cluster */
+               struct v4l2_ctrl *autobright;
+               struct v4l2_ctrl *brightness;
+       };
 
        u8 packet_nr;
 
@@ -101,7 +96,6 @@ struct sd {
        /* Determined by sensor type */
        u8 sif;
 
-       u8 quality;
 #define QUALITY_MIN 50
 #define QUALITY_MAX 70
 #define QUALITY_DEF 50
@@ -145,209 +139,112 @@ enum sensors {
    really should move the sensor drivers to v4l2 sub drivers. */
 #include "w996Xcf.c"
 
-/* V4L2 controls supported by the driver */
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setcontrast(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static void setcolors(struct gspca_dev *gspca_dev);
-static void sethvflip(struct gspca_dev *gspca_dev);
-static void setautobright(struct gspca_dev *gspca_dev);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static void setfreq(struct gspca_dev *gspca_dev);
-static void setfreq_i(struct sd *sd);
-
-static const struct ctrl sd_ctrls[] = {
-[BRIGHTNESS] = {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-               .default_value = 127,
-           },
-           .set_control = setbrightness,
+/* table of the disabled controls */
+struct ctrl_valid {
+       int has_brightness:1;
+       int has_contrast:1;
+       int has_exposure:1;
+       int has_autogain:1;
+       int has_sat:1;
+       int has_hvflip:1;
+       int has_autobright:1;
+       int has_freq:1;
+};
+
+static const struct ctrl_valid valid_controls[] = {
+       [SEN_OV2610] = {
+               .has_exposure = 1,
+               .has_autogain = 1,
        },
-[CONTRAST] = {
-           {
-               .id      = V4L2_CID_CONTRAST,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Contrast",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-               .default_value = 127,
-           },
-           .set_control = setcontrast,
+       [SEN_OV2610AE] = {
+               .has_exposure = 1,
+               .has_autogain = 1,
        },
-[EXPOSURE] = {
-           {
-               .id      = V4L2_CID_EXPOSURE,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Exposure",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-               .default_value = 127,
-           },
-           .set_control = setexposure,
+       [SEN_OV3610] = {
+               /* No controls */
        },
-[COLORS] = {
-           {
-               .id      = V4L2_CID_SATURATION,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Color",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-               .default_value = 127,
-           },
-           .set_control = setcolors,
+       [SEN_OV6620] = {
+               .has_brightness = 1,
+               .has_contrast = 1,
+               .has_sat = 1,
+               .has_autobright = 1,
+               .has_freq = 1,
        },
-/* The flip controls work for sensors ov7660 and ov7670 only */
-[HFLIP] = {
-           {
-               .id      = V4L2_CID_HFLIP,
-               .type    = V4L2_CTRL_TYPE_BOOLEAN,
-               .name    = "Mirror",
-               .minimum = 0,
-               .maximum = 1,
-               .step    = 1,
-               .default_value = 0,
-           },
-           .set_control = sethvflip,
+       [SEN_OV6630] = {
+               .has_brightness = 1,
+               .has_contrast = 1,
+               .has_sat = 1,
+               .has_autobright = 1,
+               .has_freq = 1,
        },
-[VFLIP] = {
-           {
-               .id      = V4L2_CID_VFLIP,
-               .type    = V4L2_CTRL_TYPE_BOOLEAN,
-               .name    = "Vflip",
-               .minimum = 0,
-               .maximum = 1,
-               .step    = 1,
-               .default_value = 0,
-           },
-           .set_control = sethvflip,
+       [SEN_OV66308AF] = {
+               .has_brightness = 1,
+               .has_contrast = 1,
+               .has_sat = 1,
+               .has_autobright = 1,
+               .has_freq = 1,
        },
-[AUTOBRIGHT] = {
-           {
-               .id      = V4L2_CID_AUTOBRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_BOOLEAN,
-               .name    = "Auto Brightness",
-               .minimum = 0,
-               .maximum = 1,
-               .step    = 1,
-               .default_value = 1,
-           },
-           .set_control = setautobright,
+       [SEN_OV7610] = {
+               .has_brightness = 1,
+               .has_contrast = 1,
+               .has_sat = 1,
+               .has_autobright = 1,
+               .has_freq = 1,
        },
-[AUTOGAIN] = {
-           {
-               .id      = V4L2_CID_AUTOGAIN,
-               .type    = V4L2_CTRL_TYPE_BOOLEAN,
-               .name    = "Auto Gain",
-               .minimum = 0,
-               .maximum = 1,
-               .step    = 1,
-               .default_value = 1,
-               .flags   = V4L2_CTRL_FLAG_UPDATE
-           },
-           .set = sd_setautogain,
+       [SEN_OV7620] = {
+               .has_brightness = 1,
+               .has_contrast = 1,
+               .has_sat = 1,
+               .has_autobright = 1,
+               .has_freq = 1,
        },
-[FREQ] = {
-           {
-               .id      = V4L2_CID_POWER_LINE_FREQUENCY,
-               .type    = V4L2_CTRL_TYPE_MENU,
-               .name    = "Light frequency filter",
-               .minimum = 0,
-               .maximum = 2,   /* 0: no flicker, 1: 50Hz, 2:60Hz, 3: auto */
-               .step    = 1,
-               .default_value = 0,
-           },
-           .set_control = setfreq,
+       [SEN_OV7620AE] = {
+               .has_brightness = 1,
+               .has_contrast = 1,
+               .has_sat = 1,
+               .has_autobright = 1,
+               .has_freq = 1,
+       },
+       [SEN_OV7640] = {
+               .has_brightness = 1,
+               .has_sat = 1,
+               .has_freq = 1,
+       },
+       [SEN_OV7648] = {
+               .has_brightness = 1,
+               .has_sat = 1,
+               .has_freq = 1,
+       },
+       [SEN_OV7660] = {
+               .has_brightness = 1,
+               .has_contrast = 1,
+               .has_sat = 1,
+               .has_hvflip = 1,
+               .has_freq = 1,
+       },
+       [SEN_OV7670] = {
+               .has_brightness = 1,
+               .has_contrast = 1,
+               .has_hvflip = 1,
+               .has_freq = 1,
+       },
+       [SEN_OV76BE] = {
+               .has_brightness = 1,
+               .has_contrast = 1,
+               .has_sat = 1,
+               .has_autobright = 1,
+               .has_freq = 1,
+       },
+       [SEN_OV8610] = {
+               .has_brightness = 1,
+               .has_contrast = 1,
+               .has_sat = 1,
+               .has_autobright = 1,
+       },
+       [SEN_OV9600] = {
+               .has_exposure = 1,
+               .has_autogain = 1,
        },
-};
-
-/* table of the disabled controls */
-static const unsigned ctrl_dis[] = {
-[SEN_OV2610] =         ((1 << NCTRL) - 1)      /* no control */
-                       ^ ((1 << EXPOSURE)      /* but exposure */
-                        | (1 << AUTOGAIN)),    /* and autogain */
-
-[SEN_OV2610AE] =       ((1 << NCTRL) - 1)      /* no control */
-                       ^ ((1 << EXPOSURE)      /* but exposure */
-                        | (1 << AUTOGAIN)),    /* and autogain */
-
-[SEN_OV3610] =         (1 << NCTRL) - 1,       /* no control */
-
-[SEN_OV6620] =         (1 << HFLIP) |
-                       (1 << VFLIP) |
-                       (1 << EXPOSURE) |
-                       (1 << AUTOGAIN),
-
-[SEN_OV6630] =         (1 << HFLIP) |
-                       (1 << VFLIP) |
-                       (1 << EXPOSURE) |
-                       (1 << AUTOGAIN),
-
-[SEN_OV66308AF] =      (1 << HFLIP) |
-                       (1 << VFLIP) |
-                       (1 << EXPOSURE) |
-                       (1 << AUTOGAIN),
-
-[SEN_OV7610] =         (1 << HFLIP) |
-                       (1 << VFLIP) |
-                       (1 << EXPOSURE) |
-                       (1 << AUTOGAIN),
-
-[SEN_OV7620] =         (1 << HFLIP) |
-                       (1 << VFLIP) |
-                       (1 << EXPOSURE) |
-                       (1 << AUTOGAIN),
-
-[SEN_OV7620AE] =       (1 << HFLIP) |
-                       (1 << VFLIP) |
-                       (1 << EXPOSURE) |
-                       (1 << AUTOGAIN),
-
-[SEN_OV7640] =         (1 << HFLIP) |
-                       (1 << VFLIP) |
-                       (1 << AUTOBRIGHT) |
-                       (1 << CONTRAST) |
-                       (1 << EXPOSURE) |
-                       (1 << AUTOGAIN),
-
-[SEN_OV7648] =         (1 << HFLIP) |
-                       (1 << VFLIP) |
-                       (1 << AUTOBRIGHT) |
-                       (1 << CONTRAST) |
-                       (1 << EXPOSURE) |
-                       (1 << AUTOGAIN),
-
-[SEN_OV7660] =         (1 << AUTOBRIGHT) |
-                       (1 << EXPOSURE) |
-                       (1 << AUTOGAIN),
-
-[SEN_OV7670] =         (1 << COLORS) |
-                       (1 << AUTOBRIGHT) |
-                       (1 << EXPOSURE) |
-                       (1 << AUTOGAIN),
-
-[SEN_OV76BE] =         (1 << HFLIP) |
-                       (1 << VFLIP) |
-                       (1 << EXPOSURE) |
-                       (1 << AUTOGAIN),
-
-[SEN_OV8610] =         (1 << HFLIP) |
-                       (1 << VFLIP) |
-                       (1 << EXPOSURE) |
-                       (1 << AUTOGAIN) |
-                       (1 << FREQ),
-[SEN_OV9600] =         ((1 << NCTRL) - 1)      /* no control */
-                       ^ ((1 << EXPOSURE)      /* but exposure */
-                        | (1 << AUTOGAIN)),    /* and autogain */
-
 };
 
 static const struct v4l2_pix_format ov519_vga_mode[] = {
@@ -3306,11 +3203,11 @@ static void ov519_set_fr(struct sd *sd)
        ov518_i2c_w(sd, OV7670_R11_CLKRC, clock);
 }
 
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       i2c_w_mask(sd, 0x13, sd->ctrls[AUTOGAIN].val ? 0x05 : 0x00, 0x05);
+       i2c_w_mask(sd, 0x13, val ? 0x05 : 0x00, 0x05);
 }
 
 /* this function is called at probe time */
@@ -3351,8 +3248,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
                break;
        }
 
-       gspca_dev->cam.ctrls = sd->ctrls;
-       sd->quality = QUALITY_DEF;
        sd->frame_rate = 15;
 
        return 0;
@@ -3467,8 +3362,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
                break;
        }
 
-       gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
-
        /* initialize the sensor */
        switch (sd->sensor) {
        case SEN_OV2610:
@@ -3494,8 +3387,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
                break;
        case SEN_OV6630:
        case SEN_OV66308AF:
-               sd->ctrls[CONTRAST].def = 200;
-                                /* The default is too low for the ov6630 */
                write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30));
                break;
        default:
@@ -3522,26 +3413,12 @@ static int sd_init(struct gspca_dev *gspca_dev)
                sd->gspca_dev.curr_mode = 1;    /* 640x480 */
                ov519_set_mode(sd);
                ov519_set_fr(sd);
-               sd->ctrls[COLORS].max = 4;      /* 0..4 */
-               sd->ctrls[COLORS].val =
-                       sd->ctrls[COLORS].def = 2;
-               setcolors(gspca_dev);
-               sd->ctrls[CONTRAST].max = 6;    /* 0..6 */
-               sd->ctrls[CONTRAST].val =
-                       sd->ctrls[CONTRAST].def = 3;
-               setcontrast(gspca_dev);
-               sd->ctrls[BRIGHTNESS].max = 6;  /* 0..6 */
-               sd->ctrls[BRIGHTNESS].val =
-                       sd->ctrls[BRIGHTNESS].def = 3;
-               setbrightness(gspca_dev);
                sd_reset_snapshot(gspca_dev);
                ov51x_restart(sd);
                ov51x_stop(sd);                 /* not in win traces */
                ov51x_led_control(sd, 0);
                break;
        case SEN_OV7670:
-               sd->ctrls[FREQ].max = 3;        /* auto */
-               sd->ctrls[FREQ].def = 3;
                write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670));
                break;
        case SEN_OV8610:
@@ -4177,15 +4054,14 @@ static void mode_init_ov_sensor_regs(struct sd *sd)
 }
 
 /* this function works for bridge ov519 and sensors ov7660 and ov7670 only */
-static void sethvflip(struct gspca_dev *gspca_dev)
+static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
        if (sd->gspca_dev.streaming)
                reg_w(sd, OV519_R51_RESET1, 0x0f);      /* block stream */
        i2c_w_mask(sd, OV7670_R1E_MVFP,
-               OV7670_MVFP_MIRROR * sd->ctrls[HFLIP].val
-                       | OV7670_MVFP_VFLIP * sd->ctrls[VFLIP].val,
+               OV7670_MVFP_MIRROR * hflip | OV7670_MVFP_VFLIP * vflip,
                OV7670_MVFP_MIRROR | OV7670_MVFP_VFLIP);
        if (sd->gspca_dev.streaming)
                reg_w(sd, OV519_R51_RESET1, 0x00);      /* restart stream */
@@ -4333,23 +4209,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
        set_ov_sensor_window(sd);
 
-       if (!(sd->gspca_dev.ctrl_dis & (1 << CONTRAST)))
-               setcontrast(gspca_dev);
-       if (!(sd->gspca_dev.ctrl_dis & (1 << BRIGHTNESS)))
-               setbrightness(gspca_dev);
-       if (!(sd->gspca_dev.ctrl_dis & (1 << EXPOSURE)))
-               setexposure(gspca_dev);
-       if (!(sd->gspca_dev.ctrl_dis & (1 << COLORS)))
-               setcolors(gspca_dev);
-       if (!(sd->gspca_dev.ctrl_dis & ((1 << HFLIP) | (1 << VFLIP))))
-               sethvflip(gspca_dev);
-       if (!(sd->gspca_dev.ctrl_dis & (1 << AUTOBRIGHT)))
-               setautobright(gspca_dev);
-       if (!(sd->gspca_dev.ctrl_dis & (1 << AUTOGAIN)))
-               setautogain(gspca_dev);
-       if (!(sd->gspca_dev.ctrl_dis & (1 << FREQ)))
-               setfreq_i(sd);
-
        /* Force clear snapshot state in case the snapshot button was
           pressed while we weren't streaming */
        sd->snapshot_needs_reset = 1;
@@ -4605,10 +4464,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
 
 /* -- management routines -- */
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int val;
        static const struct ov_i2c_regvals brit_7660[][7] = {
                {{0x0f, 0x6a}, {0x24, 0x40}, {0x25, 0x2b}, {0x26, 0x90},
                        {0x27, 0xe0}, {0x28, 0xe0}, {0x2c, 0xe0}},
@@ -4626,7 +4484,6 @@ static void setbrightness(struct gspca_dev *gspca_dev)
                        {0x27, 0x60}, {0x28, 0x60}, {0x2c, 0x60}}
        };
 
-       val = sd->ctrls[BRIGHTNESS].val;
        switch (sd->sensor) {
        case SEN_OV8610:
        case SEN_OV7610:
@@ -4640,9 +4497,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
                break;
        case SEN_OV7620:
        case SEN_OV7620AE:
-               /* 7620 doesn't like manual changes when in auto mode */
-               if (!sd->ctrls[AUTOBRIGHT].val)
-                       i2c_w(sd, OV7610_REG_BRT, val);
+               i2c_w(sd, OV7610_REG_BRT, val);
                break;
        case SEN_OV7660:
                write_i2c_regvals(sd, brit_7660[val],
@@ -4656,10 +4511,9 @@ static void setbrightness(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int val;
        static const struct ov_i2c_regvals contrast_7660[][31] = {
                {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0xa0},
                 {0x70, 0x58}, {0x71, 0x38}, {0x72, 0x30}, {0x73, 0x30},
@@ -4719,7 +4573,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
                 {0x88, 0xf1}, {0x89, 0xf9}, {0x8a, 0xfd}},
        };
 
-       val = sd->ctrls[CONTRAST].val;
        switch (sd->sensor) {
        case SEN_OV7610:
        case SEN_OV6620:
@@ -4760,18 +4613,16 @@ static void setcontrast(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       if (!sd->ctrls[AUTOGAIN].val)
-               i2c_w(sd, 0x10, sd->ctrls[EXPOSURE].val);
+       i2c_w(sd, 0x10, val);
 }
 
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int val;
        static const struct ov_i2c_regvals colors_7660[][6] = {
                {{0x4f, 0x28}, {0x50, 0x2a}, {0x51, 0x02}, {0x52, 0x0a},
                 {0x53, 0x19}, {0x54, 0x23}},
@@ -4785,7 +4636,6 @@ static void setcolors(struct gspca_dev *gspca_dev)
                 {0x53, 0x66}, {0x54, 0x8e}},
        };
 
-       val = sd->ctrls[COLORS].val;
        switch (sd->sensor) {
        case SEN_OV8610:
        case SEN_OV7610:
@@ -4819,34 +4669,18 @@ static void setcolors(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setautobright(struct gspca_dev *gspca_dev)
+static void setautobright(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       i2c_w_mask(sd, 0x2d, sd->ctrls[AUTOBRIGHT].val ? 0x10 : 0x00, 0x10);
+       i2c_w_mask(sd, 0x2d, val ? 0x10 : 0x00, 0x10);
 }
 
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->ctrls[AUTOGAIN].val = val;
-       if (val) {
-               gspca_dev->ctrl_inac |= (1 << EXPOSURE);
-       } else {
-               gspca_dev->ctrl_inac &= ~(1 << EXPOSURE);
-               sd->ctrls[EXPOSURE].val = i2c_r(sd, 0x10);
-       }
-       if (gspca_dev->streaming)
-               setautogain(gspca_dev);
-       return gspca_dev->usb_err;
-}
-
-static void setfreq_i(struct sd *sd)
+static void setfreq_i(struct sd *sd, s32 val)
 {
        if (sd->sensor == SEN_OV7660
         || sd->sensor == SEN_OV7670) {
-               switch (sd->ctrls[FREQ].val) {
+               switch (val) {
                case 0: /* Banding filter disabled */
                        i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_BFILT);
                        break;
@@ -4868,7 +4702,7 @@ static void setfreq_i(struct sd *sd)
                        break;
                }
        } else {
-               switch (sd->ctrls[FREQ].val) {
+               switch (val) {
                case 0: /* Banding filter disabled */
                        i2c_w_mask(sd, 0x2d, 0x00, 0x04);
                        i2c_w_mask(sd, 0x2a, 0x00, 0x80);
@@ -4900,56 +4734,28 @@ static void setfreq_i(struct sd *sd)
                }
        }
 }
-static void setfreq(struct gspca_dev *gspca_dev)
+
+static void setfreq(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       setfreq_i(sd);
+       setfreq_i(sd, val);
 
        /* Ugly but necessary */
        if (sd->bridge == BRIDGE_W9968CF)
                w9968cf_set_crop_window(sd);
 }
 
-static int sd_querymenu(struct gspca_dev *gspca_dev,
-                       struct v4l2_querymenu *menu)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       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;
-               case 3:
-                       if (sd->sensor != SEN_OV7670)
-                               return -EINVAL;
-
-                       strcpy((char *) menu->name, "Automatic");
-                       return 0;
-               }
-               break;
-       }
-       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;
+               return -ENOTTY;
 
        memset(jcomp, 0, sizeof *jcomp);
-       jcomp->quality = sd->quality;
+       jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
        jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT |
                              V4L2_JPEG_MARKER_DRI;
        return 0;
@@ -4961,38 +4767,161 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
        struct sd *sd = (struct sd *) gspca_dev;
 
        if (sd->bridge != BRIDGE_W9968CF)
-               return -EINVAL;
+               return -ENOTTY;
+
+       v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+       return 0;
+}
 
-       if (gspca_dev->streaming)
-               return -EBUSY;
+static int sd_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
 
-       if (jcomp->quality < QUALITY_MIN)
-               sd->quality = QUALITY_MIN;
-       else if (jcomp->quality > QUALITY_MAX)
-               sd->quality = QUALITY_MAX;
-       else
-               sd->quality = jcomp->quality;
+       gspca_dev->usb_err = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_AUTOGAIN:
+               gspca_dev->exposure->val = i2c_r(sd, 0x10);
+               break;
+       }
+       return 0;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
+
+       gspca_dev->usb_err = 0;
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               setfreq(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_AUTOBRIGHTNESS:
+               if (ctrl->is_new)
+                       setautobright(gspca_dev, ctrl->val);
+               if (!ctrl->val && sd->brightness->is_new)
+                       setbrightness(gspca_dev, sd->brightness->val);
+               break;
+       case V4L2_CID_SATURATION:
+               setcolors(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_HFLIP:
+               sethvflip(gspca_dev, ctrl->val, sd->vflip->val);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               if (ctrl->is_new)
+                       setautogain(gspca_dev, ctrl->val);
+               if (!ctrl->val && gspca_dev->exposure->is_new)
+                       setexposure(gspca_dev, gspca_dev->exposure->val);
+               break;
+       case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+               return -EBUSY; /* Should never happen, as we grab the ctrl */
+       }
+       return gspca_dev->usb_err;
+}
 
-       /* Return resulting jcomp params to app */
-       sd_get_jcomp(gspca_dev, jcomp);
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .g_volatile_ctrl = sd_g_volatile_ctrl,
+       .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 10);
+       if (valid_controls[sd->sensor].has_brightness)
+               sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0,
+                       sd->sensor == SEN_OV7660 ? 6 : 255, 1,
+                       sd->sensor == SEN_OV7660 ? 3 : 127);
+       if (valid_controls[sd->sensor].has_contrast) {
+               if (sd->sensor == SEN_OV7660)
+                       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                               V4L2_CID_CONTRAST, 0, 6, 1, 3);
+               else
+                       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                               V4L2_CID_CONTRAST, 0, 255, 1,
+                               (sd->sensor == SEN_OV6630 ||
+                                sd->sensor == SEN_OV66308AF) ? 200 : 127);
+       }
+       if (valid_controls[sd->sensor].has_sat)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SATURATION, 0,
+                       sd->sensor == SEN_OV7660 ? 4 : 255, 1,
+                       sd->sensor == SEN_OV7660 ? 2 : 127);
+       if (valid_controls[sd->sensor].has_exposure)
+               gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_EXPOSURE, 0, 255, 1, 127);
+       if (valid_controls[sd->sensor].has_hvflip) {
+               sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_HFLIP, 0, 1, 1, 0);
+               sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_VFLIP, 0, 1, 1, 0);
+       }
+       if (valid_controls[sd->sensor].has_autobright)
+               sd->autobright = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_AUTOBRIGHTNESS, 0, 1, 1, 1);
+       if (valid_controls[sd->sensor].has_autogain)
+               gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+       if (valid_controls[sd->sensor].has_freq) {
+               if (sd->sensor == SEN_OV7670)
+                       sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+                               V4L2_CID_POWER_LINE_FREQUENCY,
+                               V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
+                               V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
+               else
+                       sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+                               V4L2_CID_POWER_LINE_FREQUENCY,
+                               V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
+       }
+       if (sd->bridge == BRIDGE_W9968CF)
+               sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_JPEG_COMPRESSION_QUALITY,
+                       QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
 
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       if (gspca_dev->autogain)
+               v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, true);
+       if (sd->autobright)
+               v4l2_ctrl_auto_cluster(2, &sd->autobright, 0, false);
+       if (sd->hflip)
+               v4l2_ctrl_cluster(2, &sd->hflip);
        return 0;
 }
 
 /* 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,
+       .init_controls = sd_init_controls,
        .isoc_init = sd_isoc_init,
        .start = sd_start,
        .stopN = sd_stopN,
        .stop0 = sd_stop0,
        .pkt_scan = sd_pkt_scan,
        .dq_callback = sd_reset_snapshot,
-       .querymenu = sd_querymenu,
        .get_jcomp = sd_get_jcomp,
        .set_jcomp = sd_set_jcomp,
 #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
@@ -5052,6 +4981,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 80c81dd6d68b3ce4d6a40566449b89a110ce05a4..bb09d7884b8904a3597427451738f5da47987385 100644 (file)
@@ -35,6 +35,7 @@
 #include "gspca.h"
 
 #include <linux/fixp-arith.h>
+#include <media/v4l2-ctrls.h>
 
 #define OV534_REG_ADDRESS      0xf1    /* sensor address */
 #define OV534_REG_SUBADDR      0xf2
@@ -53,29 +54,28 @@ MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
 MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver");
 MODULE_LICENSE("GPL");
 
-/* controls */
-enum e_ctrl {
-       HUE,
-       SATURATION,
-       BRIGHTNESS,
-       CONTRAST,
-       GAIN,
-       EXPOSURE,
-       AGC,
-       AWB,
-       AEC,
-       SHARPNESS,
-       HFLIP,
-       VFLIP,
-       LIGHTFREQ,
-       NCTRLS          /* number of controls */
-};
-
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
 
-       struct gspca_ctrl ctrls[NCTRLS];
+       struct v4l2_ctrl_handler ctrl_handler;
+       struct v4l2_ctrl *hue;
+       struct v4l2_ctrl *saturation;
+       struct v4l2_ctrl *brightness;
+       struct v4l2_ctrl *contrast;
+       struct { /* gain control cluster */
+               struct v4l2_ctrl *autogain;
+               struct v4l2_ctrl *gain;
+       };
+       struct v4l2_ctrl *autowhitebalance;
+       struct { /* exposure control cluster */
+               struct v4l2_ctrl *autoexposure;
+               struct v4l2_ctrl *exposure;
+       };
+       struct v4l2_ctrl *sharpness;
+       struct v4l2_ctrl *hflip;
+       struct v4l2_ctrl *vflip;
+       struct v4l2_ctrl *plfreq;
 
        __u32 last_pts;
        u16 last_fid;
@@ -89,181 +89,9 @@ enum sensors {
        NSENSORS
 };
 
-/* V4L2 controls supported by the driver */
-static void sethue(struct gspca_dev *gspca_dev);
-static void setsaturation(struct gspca_dev *gspca_dev);
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setcontrast(struct gspca_dev *gspca_dev);
-static void setgain(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static void setagc(struct gspca_dev *gspca_dev);
-static void setawb(struct gspca_dev *gspca_dev);
-static void setaec(struct gspca_dev *gspca_dev);
-static void setsharpness(struct gspca_dev *gspca_dev);
-static void sethvflip(struct gspca_dev *gspca_dev);
-static void setlightfreq(struct gspca_dev *gspca_dev);
-
 static int sd_start(struct gspca_dev *gspca_dev);
 static void sd_stopN(struct gspca_dev *gspca_dev);
 
-static const struct ctrl sd_ctrls[] = {
-[HUE] = {
-               {
-                       .id      = V4L2_CID_HUE,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Hue",
-                       .minimum = -90,
-                       .maximum = 90,
-                       .step    = 1,
-                       .default_value = 0,
-               },
-               .set_control = sethue
-       },
-[SATURATION] = {
-               {
-                       .id      = V4L2_CID_SATURATION,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Saturation",
-                       .minimum = 0,
-                       .maximum = 255,
-                       .step    = 1,
-                       .default_value = 64,
-               },
-               .set_control = setsaturation
-       },
-[BRIGHTNESS] = {
-               {
-                       .id      = V4L2_CID_BRIGHTNESS,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Brightness",
-                       .minimum = 0,
-                       .maximum = 255,
-                       .step    = 1,
-                       .default_value = 0,
-               },
-               .set_control = setbrightness
-       },
-[CONTRAST] = {
-               {
-                       .id      = V4L2_CID_CONTRAST,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Contrast",
-                       .minimum = 0,
-                       .maximum = 255,
-                       .step    = 1,
-                       .default_value = 32,
-               },
-               .set_control = setcontrast
-       },
-[GAIN] = {
-               {
-                       .id      = V4L2_CID_GAIN,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Main Gain",
-                       .minimum = 0,
-                       .maximum = 63,
-                       .step    = 1,
-                       .default_value = 20,
-               },
-               .set_control = setgain
-       },
-[EXPOSURE] = {
-               {
-                       .id      = V4L2_CID_EXPOSURE,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Exposure",
-                       .minimum = 0,
-                       .maximum = 255,
-                       .step    = 1,
-                       .default_value = 120,
-               },
-               .set_control = setexposure
-       },
-[AGC] = {
-               {
-                       .id      = V4L2_CID_AUTOGAIN,
-                       .type    = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name    = "Auto Gain",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-                       .default_value = 1,
-               },
-               .set_control = setagc
-       },
-[AWB] = {
-               {
-                       .id      = V4L2_CID_AUTO_WHITE_BALANCE,
-                       .type    = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name    = "Auto White Balance",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-                       .default_value = 1,
-               },
-               .set_control = setawb
-       },
-[AEC] = {
-               {
-                       .id      = V4L2_CID_EXPOSURE_AUTO,
-                       .type    = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name    = "Auto Exposure",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-                       .default_value = 1,
-               },
-               .set_control = setaec
-       },
-[SHARPNESS] = {
-               {
-                       .id      = V4L2_CID_SHARPNESS,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Sharpness",
-                       .minimum = 0,
-                       .maximum = 63,
-                       .step    = 1,
-                       .default_value = 0,
-               },
-               .set_control = setsharpness
-       },
-[HFLIP] = {
-               {
-                       .id      = V4L2_CID_HFLIP,
-                       .type    = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name    = "HFlip",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-                       .default_value = 0,
-               },
-               .set_control = sethvflip
-       },
-[VFLIP] = {
-               {
-                       .id      = V4L2_CID_VFLIP,
-                       .type    = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name    = "VFlip",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-                       .default_value = 0,
-               },
-               .set_control = sethvflip
-       },
-[LIGHTFREQ] = {
-               {
-                       .id      = V4L2_CID_POWER_LINE_FREQUENCY,
-                       .type    = V4L2_CTRL_TYPE_MENU,
-                       .name    = "Light Frequency Filter",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-                       .default_value = 0,
-               },
-               .set_control = setlightfreq
-       },
-};
 
 static const struct v4l2_pix_format ov772x_mode[] = {
        {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
@@ -972,12 +800,10 @@ static void set_frame_rate(struct gspca_dev *gspca_dev)
        PDEBUG(D_PROBE, "frame_rate: %d", r->fps);
 }
 
-static void sethue(struct gspca_dev *gspca_dev)
+static void sethue(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int val;
 
-       val = sd->ctrls[HUE].val;
        if (sd->sensor == SENSOR_OV767x) {
                /* TBD */
        } else {
@@ -1014,12 +840,10 @@ static void sethue(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setsaturation(struct gspca_dev *gspca_dev)
+static void setsaturation(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int val;
 
-       val = sd->ctrls[SATURATION].val;
        if (sd->sensor == SENSOR_OV767x) {
                int i;
                static u8 color_tb[][6] = {
@@ -1040,12 +864,10 @@ static void setsaturation(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int val;
 
-       val = sd->ctrls[BRIGHTNESS].val;
        if (sd->sensor == SENSOR_OV767x) {
                if (val < 0)
                        val = 0x80 - val;
@@ -1055,27 +877,18 @@ static void setbrightness(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u8 val;
 
-       val = sd->ctrls[CONTRAST].val;
        if (sd->sensor == SENSOR_OV767x)
                sccb_reg_write(gspca_dev, 0x56, val);   /* contras */
        else
                sccb_reg_write(gspca_dev, 0x9c, val);
 }
 
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       u8 val;
-
-       if (sd->ctrls[AGC].val)
-               return;
-
-       val = sd->ctrls[GAIN].val;
        switch (val & 0x30) {
        case 0x00:
                val &= 0x0f;
@@ -1097,15 +910,15 @@ static void setgain(struct gspca_dev *gspca_dev)
        sccb_reg_write(gspca_dev, 0x00, val);
 }
 
-static void setexposure(struct gspca_dev *gspca_dev)
+static s32 getgain(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       u8 val;
+       return sccb_reg_read(gspca_dev, 0x00);
+}
 
-       if (sd->ctrls[AEC].val)
-               return;
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
 
-       val = sd->ctrls[EXPOSURE].val;
        if (sd->sensor == SENSOR_OV767x) {
 
                /* set only aec[9:2] */
@@ -1123,11 +936,23 @@ static void setexposure(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setagc(struct gspca_dev *gspca_dev)
+static s32 getexposure(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       if (sd->ctrls[AGC].val) {
+       if (sd->sensor == SENSOR_OV767x) {
+               /* get only aec[9:2] */
+               return sccb_reg_read(gspca_dev, 0x10);  /* aech */
+       } else {
+               u8 hi = sccb_reg_read(gspca_dev, 0x08);
+               u8 lo = sccb_reg_read(gspca_dev, 0x10);
+               return (hi << 8 | lo) >> 1;
+       }
+}
+
+static void setagc(struct gspca_dev *gspca_dev, s32 val)
+{
+       if (val) {
                sccb_reg_write(gspca_dev, 0x13,
                                sccb_reg_read(gspca_dev, 0x13) | 0x04);
                sccb_reg_write(gspca_dev, 0x64,
@@ -1137,16 +962,14 @@ static void setagc(struct gspca_dev *gspca_dev)
                                sccb_reg_read(gspca_dev, 0x13) & ~0x04);
                sccb_reg_write(gspca_dev, 0x64,
                                sccb_reg_read(gspca_dev, 0x64) & ~0x03);
-
-               setgain(gspca_dev);
        }
 }
 
-static void setawb(struct gspca_dev *gspca_dev)
+static void setawb(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       if (sd->ctrls[AWB].val) {
+       if (val) {
                sccb_reg_write(gspca_dev, 0x13,
                                sccb_reg_read(gspca_dev, 0x13) | 0x02);
                if (sd->sensor == SENSOR_OV772x)
@@ -1161,7 +984,7 @@ static void setawb(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setaec(struct gspca_dev *gspca_dev)
+static void setaec(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u8 data;
@@ -1169,31 +992,25 @@ static void setaec(struct gspca_dev *gspca_dev)
        data = sd->sensor == SENSOR_OV767x ?
                        0x05 :          /* agc + aec */
                        0x01;           /* agc */
-       if (sd->ctrls[AEC].val)
+       switch (val) {
+       case V4L2_EXPOSURE_AUTO:
                sccb_reg_write(gspca_dev, 0x13,
                                sccb_reg_read(gspca_dev, 0x13) | data);
-       else {
+               break;
+       case V4L2_EXPOSURE_MANUAL:
                sccb_reg_write(gspca_dev, 0x13,
                                sccb_reg_read(gspca_dev, 0x13) & ~data);
-               if (sd->sensor == SENSOR_OV767x)
-                       sd->ctrls[EXPOSURE].val =
-                               sccb_reg_read(gspca_dev, 10);   /* aech */
-               else
-                       setexposure(gspca_dev);
+               break;
        }
 }
 
-static void setsharpness(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       u8 val;
-
-       val = sd->ctrls[SHARPNESS].val;
        sccb_reg_write(gspca_dev, 0x91, val);   /* Auto de-noise threshold */
        sccb_reg_write(gspca_dev, 0x8e, val);   /* De-noise threshold */
 }
 
-static void sethvflip(struct gspca_dev *gspca_dev)
+static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u8 val;
@@ -1201,28 +1018,27 @@ static void sethvflip(struct gspca_dev *gspca_dev)
        if (sd->sensor == SENSOR_OV767x) {
                val = sccb_reg_read(gspca_dev, 0x1e);   /* mvfp */
                val &= ~0x30;
-               if (sd->ctrls[HFLIP].val)
+               if (hflip)
                        val |= 0x20;
-               if (sd->ctrls[VFLIP].val)
+               if (vflip)
                        val |= 0x10;
                sccb_reg_write(gspca_dev, 0x1e, val);
        } else {
                val = sccb_reg_read(gspca_dev, 0x0c);
                val &= ~0xc0;
-               if (sd->ctrls[HFLIP].val == 0)
+               if (hflip == 0)
                        val |= 0x40;
-               if (sd->ctrls[VFLIP].val == 0)
+               if (vflip == 0)
                        val |= 0x80;
                sccb_reg_write(gspca_dev, 0x0c, val);
        }
 }
 
-static void setlightfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u8 val;
 
-       val = sd->ctrls[LIGHTFREQ].val ? 0x9e : 0x00;
+       val = val ? 0x9e : 0x00;
        if (sd->sensor == SENSOR_OV767x) {
                sccb_reg_write(gspca_dev, 0x2a, 0x00);
                if (val)
@@ -1241,8 +1057,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
 
        cam = &gspca_dev->cam;
 
-       cam->ctrls = sd->ctrls;
-
        cam->cam_mode = ov772x_mode;
        cam->nmodes = ARRAY_SIZE(ov772x_mode);
 
@@ -1251,6 +1065,195 @@ static int sd_config(struct gspca_dev *gspca_dev,
        return 0;
 }
 
+static int ov534_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler);
+       struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+       switch (ctrl->id) {
+       case V4L2_CID_AUTOGAIN:
+               gspca_dev->usb_err = 0;
+               if (ctrl->val && sd->gain && gspca_dev->streaming)
+                       sd->gain->val = getgain(gspca_dev);
+               return gspca_dev->usb_err;
+
+       case V4L2_CID_EXPOSURE_AUTO:
+               gspca_dev->usb_err = 0;
+               if (ctrl->val == V4L2_EXPOSURE_AUTO && sd->exposure &&
+                   gspca_dev->streaming)
+                       sd->exposure->val = getexposure(gspca_dev);
+               return gspca_dev->usb_err;
+       }
+       return -EINVAL;
+}
+
+static int ov534_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler);
+       struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+       gspca_dev->usb_err = 0;
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_HUE:
+               sethue(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SATURATION:
+               setsaturation(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_AUTOGAIN:
+       /* case V4L2_CID_GAIN: */
+               setagc(gspca_dev, ctrl->val);
+               if (!gspca_dev->usb_err && !ctrl->val && sd->gain)
+                       setgain(gspca_dev, sd->gain->val);
+               break;
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               setawb(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_EXPOSURE_AUTO:
+       /* case V4L2_CID_EXPOSURE: */
+               setaec(gspca_dev, ctrl->val);
+               if (!gspca_dev->usb_err && ctrl->val == V4L2_EXPOSURE_MANUAL &&
+                   sd->exposure)
+                       setexposure(gspca_dev, sd->exposure->val);
+               break;
+       case V4L2_CID_SHARPNESS:
+               setsharpness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_HFLIP:
+               sethvflip(gspca_dev, ctrl->val, sd->vflip->val);
+               break;
+       case V4L2_CID_VFLIP:
+               sethvflip(gspca_dev, sd->hflip->val, ctrl->val);
+               break;
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               setlightfreq(gspca_dev, ctrl->val);
+               break;
+       }
+       return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops ov534_ctrl_ops = {
+       .g_volatile_ctrl = ov534_g_volatile_ctrl,
+       .s_ctrl = ov534_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler;
+       /* parameters with different values between the supported sensors */
+       int saturation_min;
+       int saturation_max;
+       int saturation_def;
+       int brightness_min;
+       int brightness_max;
+       int brightness_def;
+       int contrast_max;
+       int contrast_def;
+       int exposure_min;
+       int exposure_max;
+       int exposure_def;
+       int hflip_def;
+
+       if (sd->sensor == SENSOR_OV767x) {
+               saturation_min = 0,
+               saturation_max = 6,
+               saturation_def = 3,
+               brightness_min = -127;
+               brightness_max = 127;
+               brightness_def = 0;
+               contrast_max = 0x80;
+               contrast_def = 0x40;
+               exposure_min = 0x08;
+               exposure_max = 0x60;
+               exposure_def = 0x13;
+               hflip_def = 1;
+       } else {
+               saturation_min = 0,
+               saturation_max = 255,
+               saturation_def = 64,
+               brightness_min = 0;
+               brightness_max = 255;
+               brightness_def = 0;
+               contrast_max = 255;
+               contrast_def = 32;
+               exposure_min = 0;
+               exposure_max = 255;
+               exposure_def = 120;
+               hflip_def = 0;
+       }
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+
+       v4l2_ctrl_handler_init(hdl, 13);
+
+       if (sd->sensor == SENSOR_OV772x)
+               sd->hue = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+                               V4L2_CID_HUE, -90, 90, 1, 0);
+
+       sd->saturation = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+                       V4L2_CID_SATURATION, saturation_min, saturation_max, 1,
+                       saturation_def);
+       sd->brightness = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, brightness_min, brightness_max, 1,
+                       brightness_def);
+       sd->contrast = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, contrast_max, 1, contrast_def);
+
+       if (sd->sensor == SENSOR_OV772x) {
+               sd->autogain = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+                               V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+               sd->gain = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+                               V4L2_CID_GAIN, 0, 63, 1, 20);
+       }
+
+       sd->autoexposure = v4l2_ctrl_new_std_menu(hdl, &ov534_ctrl_ops,
+                       V4L2_CID_EXPOSURE_AUTO,
+                       V4L2_EXPOSURE_MANUAL, 0,
+                       V4L2_EXPOSURE_AUTO);
+       sd->exposure = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+                       V4L2_CID_EXPOSURE, exposure_min, exposure_max, 1,
+                       exposure_def);
+
+       sd->autowhitebalance = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+                       V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+
+       if (sd->sensor == SENSOR_OV772x)
+               sd->sharpness = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+                               V4L2_CID_SHARPNESS, 0, 63, 1, 0);
+
+       sd->hflip = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+                       V4L2_CID_HFLIP, 0, 1, 1, hflip_def);
+       sd->vflip = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+                       V4L2_CID_VFLIP, 0, 1, 1, 0);
+       sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &ov534_ctrl_ops,
+                       V4L2_CID_POWER_LINE_FREQUENCY,
+                       V4L2_CID_POWER_LINE_FREQUENCY_50HZ, 0,
+                       V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+
+       if (sd->sensor == SENSOR_OV772x)
+               v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true);
+
+       v4l2_ctrl_auto_cluster(2, &sd->autoexposure, V4L2_EXPOSURE_MANUAL,
+                              true);
+
+       return 0;
+}
+
 /* this function is called at probe and resume time */
 static int sd_init(struct gspca_dev *gspca_dev)
 {
@@ -1286,24 +1289,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
 
        if ((sensor_id & 0xfff0) == 0x7670) {
                sd->sensor = SENSOR_OV767x;
-               gspca_dev->ctrl_dis = (1 << HUE) |
-                                       (1 << GAIN) |
-                                       (1 << AGC) |
-                                       (1 << SHARPNESS);       /* auto */
-               sd->ctrls[SATURATION].min = 0,
-               sd->ctrls[SATURATION].max = 6,
-               sd->ctrls[SATURATION].def = 3,
-               sd->ctrls[BRIGHTNESS].min = -127;
-               sd->ctrls[BRIGHTNESS].max = 127;
-               sd->ctrls[BRIGHTNESS].def = 0;
-               sd->ctrls[CONTRAST].max = 0x80;
-               sd->ctrls[CONTRAST].def = 0x40;
-               sd->ctrls[EXPOSURE].min = 0x08;
-               sd->ctrls[EXPOSURE].max = 0x60;
-               sd->ctrls[EXPOSURE].def = 0x13;
-               sd->ctrls[SHARPNESS].max = 9;
-               sd->ctrls[SHARPNESS].def = 4;
-               sd->ctrls[HFLIP].def = 1;
                gspca_dev->cam.cam_mode = ov767x_mode;
                gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode);
        } else {
@@ -1366,22 +1351,23 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
        set_frame_rate(gspca_dev);
 
-       if (!(gspca_dev->ctrl_dis & (1 << HUE)))
-               sethue(gspca_dev);
-       setsaturation(gspca_dev);
-       if (!(gspca_dev->ctrl_dis & (1 << AGC)))
-               setagc(gspca_dev);
-       setawb(gspca_dev);
-       setaec(gspca_dev);
-       if (!(gspca_dev->ctrl_dis & (1 << GAIN)))
-               setgain(gspca_dev);
-       setexposure(gspca_dev);
-       setbrightness(gspca_dev);
-       setcontrast(gspca_dev);
-       if (!(gspca_dev->ctrl_dis & (1 << SHARPNESS)))
-               setsharpness(gspca_dev);
-       sethvflip(gspca_dev);
-       setlightfreq(gspca_dev);
+       if (sd->hue)
+               sethue(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue));
+       setsaturation(gspca_dev, v4l2_ctrl_g_ctrl(sd->saturation));
+       if (sd->autogain)
+               setagc(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
+       setawb(gspca_dev, v4l2_ctrl_g_ctrl(sd->autowhitebalance));
+       setaec(gspca_dev, v4l2_ctrl_g_ctrl(sd->autoexposure));
+       if (sd->gain)
+               setgain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain));
+       setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
+       setbrightness(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness));
+       setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast));
+       if (sd->sharpness)
+               setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
+       sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip),
+                 v4l2_ctrl_g_ctrl(sd->vflip));
+       setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq));
 
        ov534_set_led(gspca_dev, 1);
        ov534_reg_write(gspca_dev, 0xe0, 0x00);
@@ -1483,25 +1469,6 @@ scan_next:
        } while (remaining_len > 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, "Disabled");
-                       return 0;
-               case 1:         /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
-                       strcpy((char *) menu->name, "50 Hz");
-                       return 0;
-               }
-               break;
-       }
-
-       return -EINVAL;
-}
-
 /* get stream parameters (framerate) */
 static void sd_get_streamparm(struct gspca_dev *gspca_dev,
                             struct v4l2_streamparm *parm)
@@ -1536,14 +1503,12 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev,
 /* 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,
+       .init_controls = sd_init_controls,
        .start    = sd_start,
        .stopN    = sd_stopN,
        .pkt_scan = sd_pkt_scan,
-       .querymenu = sd_querymenu,
        .get_streamparm = sd_get_streamparm,
        .set_streamparm = sd_set_streamparm,
 };
@@ -1572,6 +1537,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend    = gspca_suspend,
        .resume     = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 1fd41f0d2e9514a408db394e9a613bc4ee2badd7..c4cd028fe0b4c8ea0c662ab82fbf98695f23a722 100644 (file)
@@ -47,22 +47,9 @@ MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
 MODULE_DESCRIPTION("GSPCA/OV534_9 USB Camera Driver");
 MODULE_LICENSE("GPL");
 
-/* controls */
-enum e_ctrl {
-       BRIGHTNESS,
-       CONTRAST,
-       AUTOGAIN,
-       EXPOSURE,
-       SHARPNESS,
-       SATUR,
-       LIGHTFREQ,
-       NCTRLS          /* number of controls */
-};
-
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
-       struct gspca_ctrl ctrls[NCTRLS];
        __u32 last_pts;
        u8 last_fid;
 
@@ -75,103 +62,6 @@ enum sensors {
        NSENSORS
 };
 
-/* V4L2 controls supported by the driver */
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setcontrast(struct gspca_dev *gspca_dev);
-static void setautogain(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static void setsharpness(struct gspca_dev *gspca_dev);
-static void setsatur(struct gspca_dev *gspca_dev);
-static void setlightfreq(struct gspca_dev *gspca_dev);
-
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[BRIGHTNESS] = {
-       {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 15,
-               .step    = 1,
-               .default_value = 7
-       },
-       .set_control = setbrightness
-    },
-[CONTRAST] = {
-       {
-               .id      = V4L2_CID_CONTRAST,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Contrast",
-               .minimum = 0,
-               .maximum = 15,
-               .step    = 1,
-               .default_value = 3
-       },
-       .set_control = setcontrast
-    },
-[AUTOGAIN] = {
-       {
-               .id      = V4L2_CID_AUTOGAIN,
-               .type    = V4L2_CTRL_TYPE_BOOLEAN,
-               .name    = "Autogain",
-               .minimum = 0,
-               .maximum = 1,
-               .step    = 1,
-#define AUTOGAIN_DEF 1
-               .default_value = AUTOGAIN_DEF,
-       },
-       .set_control = setautogain
-    },
-[EXPOSURE] = {
-       {
-               .id      = V4L2_CID_EXPOSURE,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Exposure",
-               .minimum = 0,
-               .maximum = 3,
-               .step    = 1,
-               .default_value = 0
-       },
-       .set_control = setexposure
-    },
-[SHARPNESS] = {
-       {
-               .id      = V4L2_CID_SHARPNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Sharpness",
-               .minimum = -1,          /* -1 = auto */
-               .maximum = 4,
-               .step    = 1,
-               .default_value = -1
-       },
-       .set_control = setsharpness
-    },
-[SATUR] = {
-       {
-               .id      = V4L2_CID_SATURATION,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Saturation",
-               .minimum = 0,
-               .maximum = 4,
-               .step    = 1,
-               .default_value = 2
-       },
-       .set_control = setsatur
-    },
-[LIGHTFREQ] = {
-       {
-               .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,
-               .default_value = 0
-       },
-       .set_control = setlightfreq
-    },
-};
-
 static const struct v4l2_pix_format ov965x_mode[] = {
 #define QVGA_MODE 0
        {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
@@ -1104,16 +994,14 @@ static void set_led(struct gspca_dev *gspca_dev, int status)
        }
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u8 val;
        s8 sval;
 
-       if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS))
-               return;
        if (sd->sensor == SENSOR_OV562x) {
-               sval = sd->ctrls[BRIGHTNESS].val;
+               sval = brightness;
                val = 0x76;
                val += sval;
                sccb_write(gspca_dev, 0x24, val);
@@ -1128,7 +1016,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
                        val = 0xe6;
                sccb_write(gspca_dev, 0x26, val);
        } else {
-               val = sd->ctrls[BRIGHTNESS].val;
+               val = brightness;
                if (val < 8)
                        val = 15 - val;         /* f .. 8 */
                else
@@ -1138,43 +1026,32 @@ static void setbrightness(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (gspca_dev->ctrl_dis & (1 << CONTRAST))
-               return;
        sccb_write(gspca_dev, 0x56,     /* cnst1 - contrast 1 ctrl coeff */
-                       sd->ctrls[CONTRAST].val << 4);
+                       val << 4);
 }
 
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 autogain)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 val;
 
-       if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
-               return;
 /*fixme: should adjust agc/awb/aec by different controls */
        val = sccb_read(gspca_dev, 0x13);               /* com8 */
        sccb_write(gspca_dev, 0xff, 0x00);
-       if (sd->ctrls[AUTOGAIN].val)
+       if (autogain)
                val |= 0x05;            /* agc & aec */
        else
                val &= 0xfa;
        sccb_write(gspca_dev, 0x13, val);
 }
 
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 exposure)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       u8 val;
        static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e};
+       u8 val;
 
-       if (gspca_dev->ctrl_dis & (1 << EXPOSURE))
-               return;
-       sccb_write(gspca_dev, 0x10,                     /* aec[9:2] */
-                       expo[sd->ctrls[EXPOSURE].val]);
+       sccb_write(gspca_dev, 0x10, expo[exposure]);    /* aec[9:2] */
 
        val = sccb_read(gspca_dev, 0x13);               /* com8 */
        sccb_write(gspca_dev, 0xff, 0x00);
@@ -1185,14 +1062,8 @@ static void setexposure(struct gspca_dev *gspca_dev)
        sccb_write(gspca_dev, 0xa1, val & 0xe0);        /* aec[15:10] = 0 */
 }
 
-static void setsharpness(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       s8 val;
-
-       if (gspca_dev->ctrl_dis & (1 << SHARPNESS))
-               return;
-       val = sd->ctrls[SHARPNESS].val;
        if (val < 0) {                          /* auto */
                val = sccb_read(gspca_dev, 0x42);       /* com17 */
                sccb_write(gspca_dev, 0xff, 0x00);
@@ -1209,9 +1080,8 @@ static void setsharpness(struct gspca_dev *gspca_dev)
        sccb_write(gspca_dev, 0x42, val & 0xbf);
 }
 
-static void setsatur(struct gspca_dev *gspca_dev)
+static void setsatur(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 val1, val2, val3;
        static const u8 matrix[5][2] = {
                {0x14, 0x38},
@@ -1221,10 +1091,8 @@ static void setsatur(struct gspca_dev *gspca_dev)
                {0x48, 0x90}
        };
 
-       if (gspca_dev->ctrl_dis & (1 << SATUR))
-               return;
-       val1 = matrix[sd->ctrls[SATUR].val][0];
-       val2 = matrix[sd->ctrls[SATUR].val][1];
+       val1 = matrix[val][0];
+       val2 = matrix[val][1];
        val3 = val1 + val2;
        sccb_write(gspca_dev, 0x4f, val3);      /* matrix coeff */
        sccb_write(gspca_dev, 0x50, val3);
@@ -1239,16 +1107,13 @@ static void setsatur(struct gspca_dev *gspca_dev)
        sccb_write(gspca_dev, 0x41, val1);
 }
 
-static void setlightfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 freq)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 val;
 
-       if (gspca_dev->ctrl_dis & (1 << LIGHTFREQ))
-               return;
        val = sccb_read(gspca_dev, 0x13);               /* com8 */
        sccb_write(gspca_dev, 0xff, 0x00);
-       if (sd->ctrls[LIGHTFREQ].val == 0) {
+       if (freq == 0) {
                sccb_write(gspca_dev, 0x13, val & 0xdf);
                return;
        }
@@ -1256,7 +1121,7 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
 
        val = sccb_read(gspca_dev, 0x42);               /* com17 */
        sccb_write(gspca_dev, 0xff, 0x00);
-       if (sd->ctrls[LIGHTFREQ].val == 1)
+       if (freq == 1)
                val |= 0x01;
        else
                val &= 0xfe;
@@ -1267,13 +1132,6 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
 static int sd_config(struct gspca_dev *gspca_dev,
                     const struct usb_device_id *id)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       gspca_dev->cam.ctrls = sd->ctrls;
-
-#if AUTOGAIN_DEF != 0
-       gspca_dev->ctrl_inac |= (1 << EXPOSURE);
-#endif
        return 0;
 }
 
@@ -1330,9 +1188,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
                gspca_dev->cam.cam_mode = ov971x_mode;
                gspca_dev->cam.nmodes = ARRAY_SIZE(ov971x_mode);
 
-               /* no control yet */
-               gspca_dev->ctrl_dis = (1 << NCTRLS) - 1;
-
                gspca_dev->cam.bulk = 1;
                gspca_dev->cam.bulk_size = 16384;
                gspca_dev->cam.bulk_nurbs = 2;
@@ -1358,16 +1213,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
                        reg_w(gspca_dev, 0x56, 0x17);
        } else if ((sensor_id & 0xfff0) == 0x5620) {
                sd->sensor = SENSOR_OV562x;
-               gspca_dev->ctrl_dis = (1 << CONTRAST) |
-                                       (1 << AUTOGAIN) |
-                                       (1 << EXPOSURE) |
-                                       (1 << SHARPNESS) |
-                                       (1 << SATUR) |
-                                       (1 << LIGHTFREQ);
-
-               sd->ctrls[BRIGHTNESS].min = -90;
-               sd->ctrls[BRIGHTNESS].max = 90;
-               sd->ctrls[BRIGHTNESS].def = 0;
                gspca_dev->cam.cam_mode = ov562x_mode;
                gspca_dev->cam.nmodes = ARRAY_SIZE(ov562x_mode);
 
@@ -1390,10 +1235,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
        if (sd->sensor == SENSOR_OV971x)
                return gspca_dev->usb_err;
-       else if (sd->sensor == SENSOR_OV562x) {
-               setbrightness(gspca_dev);
+       if (sd->sensor == SENSOR_OV562x)
                return gspca_dev->usb_err;
-       }
+
        switch (gspca_dev->curr_mode) {
        case QVGA_MODE:                 /* 320x240 */
                sccb_w_array(gspca_dev, ov965x_start_1_vga,
@@ -1437,13 +1281,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
                                ARRAY_SIZE(ov965x_start_2_sxga));
                break;
        }
-       setlightfreq(gspca_dev);
-       setautogain(gspca_dev);
-       setbrightness(gspca_dev);
-       setcontrast(gspca_dev);
-       setexposure(gspca_dev);
-       setsharpness(gspca_dev);
-       setsatur(gspca_dev);
 
        reg_w(gspca_dev, 0xe0, 0x00);
        reg_w(gspca_dev, 0xe0, 0x00);
@@ -1541,38 +1378,94 @@ scan_next:
        } while (remaining_len > 0);
 }
 
-static int sd_querymenu(struct gspca_dev *gspca_dev,
-                       struct v4l2_querymenu *menu)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       switch (menu->id) {
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+       gspca_dev->usb_err = 0;
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SATURATION:
+               setsatur(gspca_dev, ctrl->val);
+               break;
        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;
-               }
+               setlightfreq(gspca_dev, ctrl->val);
                break;
+       case V4L2_CID_SHARPNESS:
+               setsharpness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               if (ctrl->is_new)
+                       setautogain(gspca_dev, ctrl->val);
+               if (!ctrl->val && gspca_dev->exposure->is_new)
+                       setexposure(gspca_dev, gspca_dev->exposure->val);
+               break;
+       }
+       return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       if (sd->sensor == SENSOR_OV971x)
+               return 0;
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 7);
+       if (sd->sensor == SENSOR_OV562x) {
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, -90, 90, 1, 0);
+       } else {
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 15, 1, 7);
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 15, 1, 3);
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 4, 1, 2);
+               /* -1 = auto */
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SHARPNESS, -1, 4, 1, -1);
+               gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+               gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_EXPOSURE, 0, 3, 1, 0);
+               v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+                       V4L2_CID_POWER_LINE_FREQUENCY,
+                       V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
+               v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
        }
-       return -EINVAL;
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       return 0;
 }
 
 /* sub-driver description */
 static const struct sd_desc sd_desc = {
        .name     = MODULE_NAME,
-       .ctrls    = sd_ctrls,
-       .nctrls   = NCTRLS,
        .config   = sd_config,
        .init     = sd_init,
+       .init_controls = sd_init_controls,
        .start    = sd_start,
        .stopN    = sd_stopN,
        .pkt_scan = sd_pkt_scan,
-       .querymenu = sd_querymenu,
 };
 
 /* -- module initialisation -- */
@@ -1600,6 +1493,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend    = gspca_suspend,
        .resume     = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index fa661c6d6d55ea0263c9aabe9c99c67ded5c3b46..d236d1791f78da1ff0bdf965b8dde6389d169155 100644 (file)
@@ -462,6 +462,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index a0369a58c4bb79697851e7299e5113bfcf02d949..4877f7ab3d597eba3574abbab1a3945b0b18cca6 100644 (file)
 /* Include pac common sof detection functions */
 #include "pac_common.h"
 
+#define PAC7302_GAIN_DEFAULT      15
+#define PAC7302_GAIN_KNEE         42
+#define PAC7302_EXPOSURE_DEFAULT  66 /* 33 ms / 30 fps */
+#define PAC7302_EXPOSURE_KNEE    133 /* 66 ms / 15 fps */
+
 MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
                "Thomas Kaiser thomas@kaiser-linux.li");
 MODULE_DESCRIPTION("Pixart PAC7302");
 MODULE_LICENSE("GPL");
 
-enum e_ctrl {
-       BRIGHTNESS,
-       CONTRAST,
-       COLORS,
-       WHITE_BALANCE,
-       RED_BALANCE,
-       BLUE_BALANCE,
-       GAIN,
-       AUTOGAIN,
-       EXPOSURE,
-       VFLIP,
-       HFLIP,
-       NCTRLS          /* number of controls */
-};
-
 struct sd {
        struct gspca_dev gspca_dev;             /* !! must be the first item */
 
-       struct gspca_ctrl ctrls[NCTRLS];
-
+       struct { /* brightness / contrast cluster */
+               struct v4l2_ctrl *brightness;
+               struct v4l2_ctrl *contrast;
+       };
+       struct v4l2_ctrl *saturation;
+       struct v4l2_ctrl *white_balance;
+       struct v4l2_ctrl *red_balance;
+       struct v4l2_ctrl *blue_balance;
+       struct { /* flip cluster */
+               struct v4l2_ctrl *hflip;
+               struct v4l2_ctrl *vflip;
+       };
        u8 flags;
 #define FL_HFLIP 0x01          /* mirrored by default */
 #define FL_VFLIP 0x02          /* vertical flipped by default */
@@ -119,160 +119,6 @@ struct sd {
        atomic_t avg_lum;
 };
 
-/* V4L2 controls supported by the driver */
-static void setbrightcont(struct gspca_dev *gspca_dev);
-static void setcolors(struct gspca_dev *gspca_dev);
-static void setwhitebalance(struct gspca_dev *gspca_dev);
-static void setredbalance(struct gspca_dev *gspca_dev);
-static void setbluebalance(struct gspca_dev *gspca_dev);
-static void setgain(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static void setautogain(struct gspca_dev *gspca_dev);
-static void sethvflip(struct gspca_dev *gspca_dev);
-
-static const struct ctrl sd_ctrls[] = {
-[BRIGHTNESS] = {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-#define BRIGHTNESS_MAX 0x20
-               .maximum = BRIGHTNESS_MAX,
-               .step    = 1,
-               .default_value = 0x10,
-           },
-           .set_control = setbrightcont
-       },
-[CONTRAST] = {
-           {
-               .id      = V4L2_CID_CONTRAST,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Contrast",
-               .minimum = 0,
-#define CONTRAST_MAX 255
-               .maximum = CONTRAST_MAX,
-               .step    = 1,
-               .default_value = 127,
-           },
-           .set_control = setbrightcont
-       },
-[COLORS] = {
-           {
-               .id      = V4L2_CID_SATURATION,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Saturation",
-               .minimum = 0,
-#define COLOR_MAX 255
-               .maximum = COLOR_MAX,
-               .step    = 1,
-               .default_value = 127
-           },
-           .set_control = setcolors
-       },
-[WHITE_BALANCE] = {
-           {
-               .id      = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "White Balance",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-               .default_value = 4,
-           },
-           .set_control = setwhitebalance
-       },
-[RED_BALANCE] = {
-           {
-               .id      = V4L2_CID_RED_BALANCE,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Red",
-               .minimum = 0,
-               .maximum = 3,
-               .step    = 1,
-               .default_value = 1,
-           },
-           .set_control = setredbalance
-       },
-[BLUE_BALANCE] = {
-           {
-               .id      = V4L2_CID_BLUE_BALANCE,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Blue",
-               .minimum = 0,
-               .maximum = 3,
-               .step    = 1,
-               .default_value = 1,
-           },
-           .set_control = setbluebalance
-       },
-[GAIN] = {
-           {
-               .id      = V4L2_CID_GAIN,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Gain",
-               .minimum = 0,
-               .maximum = 62,
-               .step    = 1,
-#define GAIN_DEF 15
-#define GAIN_KNEE 46
-               .default_value = GAIN_DEF,
-           },
-           .set_control = setgain
-       },
-[EXPOSURE] = {
-           {
-               .id      = V4L2_CID_EXPOSURE,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Exposure",
-               .minimum = 0,
-               .maximum = 1023,
-               .step    = 1,
-#define EXPOSURE_DEF  66  /*  33 ms / 30 fps */
-#define EXPOSURE_KNEE 133 /*  66 ms / 15 fps */
-               .default_value = EXPOSURE_DEF,
-           },
-           .set_control = setexposure
-       },
-[AUTOGAIN] = {
-           {
-               .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_control = setautogain,
-       },
-[HFLIP] = {
-           {
-               .id      = V4L2_CID_HFLIP,
-               .type    = V4L2_CTRL_TYPE_BOOLEAN,
-               .name    = "Mirror",
-               .minimum = 0,
-               .maximum = 1,
-               .step    = 1,
-               .default_value = 0,
-           },
-           .set_control = sethvflip,
-       },
-[VFLIP] = {
-           {
-               .id      = V4L2_CID_VFLIP,
-               .type    = V4L2_CTRL_TYPE_BOOLEAN,
-               .name    = "Vflip",
-               .minimum = 0,
-               .maximum = 1,
-               .step    = 1,
-               .default_value = 0,
-           },
-           .set_control = sethvflip
-       },
-};
-
 static const struct v4l2_pix_format vga_mode[] = {
        {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
                .bytesperline = 640,
@@ -516,8 +362,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
        cam->cam_mode = vga_mode;       /* only 640x480 */
        cam->nmodes = ARRAY_SIZE(vga_mode);
 
-       gspca_dev->cam.ctrls = sd->ctrls;
-
        sd->flags = id->driver_info;
        return 0;
 }
@@ -536,9 +380,9 @@ static void setbrightcont(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, 0xff, 0x00);           /* page 0 */
        for (i = 0; i < 10; i++) {
                v = max[i];
-               v += (sd->ctrls[BRIGHTNESS].val - BRIGHTNESS_MAX)
-                       * 150 / BRIGHTNESS_MAX;         /* 200 ? */
-               v -= delta[i] * sd->ctrls[CONTRAST].val / CONTRAST_MAX;
+               v += (sd->brightness->val - sd->brightness->maximum)
+                       * 150 / sd->brightness->maximum; /* 200 ? */
+               v -= delta[i] * sd->contrast->val / sd->contrast->maximum;
                if (v < 0)
                        v = 0;
                else if (v > 0xff)
@@ -561,7 +405,8 @@ static void setcolors(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, 0x11, 0x01);
        reg_w(gspca_dev, 0xff, 0x00);                   /* page 0 */
        for (i = 0; i < 9; i++) {
-               v = a[i] * sd->ctrls[COLORS].val / COLOR_MAX + b[i];
+               v = a[i] * sd->saturation->val / sd->saturation->maximum;
+               v += b[i];
                reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
                reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
        }
@@ -573,7 +418,7 @@ static void setwhitebalance(struct gspca_dev *gspca_dev)
        struct sd *sd = (struct sd *) gspca_dev;
 
        reg_w(gspca_dev, 0xff, 0x00);           /* page 0 */
-       reg_w(gspca_dev, 0xc6, sd->ctrls[WHITE_BALANCE].val);
+       reg_w(gspca_dev, 0xc6, sd->white_balance->val);
 
        reg_w(gspca_dev, 0xdc, 0x01);
 }
@@ -583,7 +428,7 @@ static void setredbalance(struct gspca_dev *gspca_dev)
        struct sd *sd = (struct sd *) gspca_dev;
 
        reg_w(gspca_dev, 0xff, 0x00);           /* page 0 */
-       reg_w(gspca_dev, 0xc5, sd->ctrls[RED_BALANCE].val);
+       reg_w(gspca_dev, 0xc5, sd->red_balance->val);
 
        reg_w(gspca_dev, 0xdc, 0x01);
 }
@@ -593,22 +438,21 @@ static void setbluebalance(struct gspca_dev *gspca_dev)
        struct sd *sd = (struct sd *) gspca_dev;
 
        reg_w(gspca_dev, 0xff, 0x00);                   /* page 0 */
-       reg_w(gspca_dev, 0xc7, sd->ctrls[BLUE_BALANCE].val);
+       reg_w(gspca_dev, 0xc7, sd->blue_balance->val);
 
        reg_w(gspca_dev, 0xdc, 0x01);
 }
 
 static void setgain(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 reg10, reg12;
 
-       if (sd->ctrls[GAIN].val < 32) {
-               reg10 = sd->ctrls[GAIN].val;
+       if (gspca_dev->gain->val < 32) {
+               reg10 = gspca_dev->gain->val;
                reg12 = 0;
        } else {
                reg10 = 31;
-               reg12 = sd->ctrls[GAIN].val - 31;
+               reg12 = gspca_dev->gain->val - 31;
        }
 
        reg_w(gspca_dev, 0xff, 0x03);                   /* page 3 */
@@ -621,7 +465,6 @@ static void setgain(struct gspca_dev *gspca_dev)
 
 static void setexposure(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 clockdiv;
        u16 exposure;
 
@@ -630,7 +473,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
         * no fps according to the formula: 90 / reg. sd->exposure is the
         * desired exposure time in 0.5 ms.
         */
-       clockdiv = (90 * sd->ctrls[EXPOSURE].val + 1999) / 2000;
+       clockdiv = (90 * gspca_dev->exposure->val + 1999) / 2000;
 
        /*
         * Note clockdiv = 3 also works, but when running at 30 fps, depending
@@ -655,7 +498,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
         * frame exposure time in ms = 1000 * clockdiv / 90    ->
         * exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90)
         */
-       exposure = (sd->ctrls[EXPOSURE].val * 45 * 448) / (1000 * clockdiv);
+       exposure = (gspca_dev->exposure->val * 45 * 448) / (1000 * clockdiv);
        /* 0 = use full frametime, 448 = no exposure, reverse it */
        exposure = 448 - exposure;
 
@@ -668,37 +511,15 @@ static void setexposure(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, 0x11, 0x01);
 }
 
-static void setautogain(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       /*
-        * 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->ctrls[AUTOGAIN].val) {
-               sd->ctrls[EXPOSURE].val = EXPOSURE_DEF;
-               sd->ctrls[GAIN].val = GAIN_DEF;
-               sd->autogain_ignore_frames =
-                               PAC_AUTOGAIN_IGNORE_FRAMES;
-       } else {
-               sd->autogain_ignore_frames = -1;
-       }
-       setexposure(gspca_dev);
-       setgain(gspca_dev);
-}
-
 static void sethvflip(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u8 data, hflip, vflip;
 
-       hflip = sd->ctrls[HFLIP].val;
+       hflip = sd->hflip->val;
        if (sd->flags & FL_HFLIP)
                hflip = !hflip;
-       vflip = sd->ctrls[VFLIP].val;
+       vflip = sd->vflip->val;
        if (sd->flags & FL_VFLIP)
                vflip = !vflip;
 
@@ -717,6 +538,112 @@ static int sd_init(struct gspca_dev *gspca_dev)
        return gspca_dev->usb_err;
 }
 
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
+
+       gspca_dev->usb_err = 0;
+
+       if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->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. */
+               gspca_dev->exposure->val    = PAC7302_EXPOSURE_DEFAULT;
+               gspca_dev->gain->val        = PAC7302_GAIN_DEFAULT;
+               sd->autogain_ignore_frames  = PAC_AUTOGAIN_IGNORE_FRAMES;
+       }
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightcont(gspca_dev);
+               break;
+       case V4L2_CID_SATURATION:
+               setcolors(gspca_dev);
+               break;
+       case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+               setwhitebalance(gspca_dev);
+               break;
+       case V4L2_CID_RED_BALANCE:
+               setredbalance(gspca_dev);
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               setbluebalance(gspca_dev);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
+                       setexposure(gspca_dev);
+               if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
+                       setgain(gspca_dev);
+               break;
+       case V4L2_CID_HFLIP:
+               sethvflip(gspca_dev);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
+
+/* this function is called at probe time */
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 11);
+
+       sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_BRIGHTNESS, 0, 32, 1, 16);
+       sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_CONTRAST, 0, 255, 1, 127);
+
+       sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_SATURATION, 0, 255, 1, 127);
+       sd->white_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+                                       0, 255, 1, 4);
+       sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_RED_BALANCE, 0, 3, 1, 1);
+       sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_RED_BALANCE, 0, 3, 1, 1);
+
+       gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+       gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_EXPOSURE, 0, 1023, 1,
+                                       PAC7302_EXPOSURE_DEFAULT);
+       gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_GAIN, 0, 62, 1,
+                                       PAC7302_GAIN_DEFAULT);
+
+       sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+               V4L2_CID_HFLIP, 0, 1, 1, 0);
+       sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+               V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+
+       v4l2_ctrl_cluster(2, &sd->brightness);
+       v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+       v4l2_ctrl_cluster(2, &sd->hflip);
+       return 0;
+}
+
+/* -- start the camera -- */
 static int sd_start(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -728,11 +655,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
        setwhitebalance(gspca_dev);
        setredbalance(gspca_dev);
        setbluebalance(gspca_dev);
-       setautogain(gspca_dev);
+       setexposure(gspca_dev);
+       setgain(gspca_dev);
        sethvflip(gspca_dev);
 
        sd->sof_read = 0;
-       atomic_set(&sd->avg_lum, 270 + sd->ctrls[BRIGHTNESS].val);
+       sd->autogain_ignore_frames = 0;
+       atomic_set(&sd->avg_lum, 270 + sd->brightness->val);
 
        /* start stream */
        reg_w(gspca_dev, 0xff, 0x01);
@@ -758,9 +687,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, 0x78, 0x40);
 }
 
-#define WANT_REGULAR_AUTOGAIN
-#include "autogain_functions.h"
-
 static void do_autogain(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -774,11 +700,13 @@ static void do_autogain(struct gspca_dev *gspca_dev)
        if (sd->autogain_ignore_frames > 0) {
                sd->autogain_ignore_frames--;
        } else {
-               desired_lum = 270 + sd->ctrls[BRIGHTNESS].val;
+               desired_lum = 270 + sd->brightness->val;
 
-               auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
-                               deadzone, GAIN_KNEE, EXPOSURE_KNEE);
-               sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+               if (gspca_expo_autogain(gspca_dev, avg_lum, desired_lum,
+                                       deadzone, PAC7302_GAIN_KNEE,
+                                       PAC7302_EXPOSURE_KNEE))
+                       sd->autogain_ignore_frames =
+                                               PAC_AUTOGAIN_IGNORE_FRAMES;
        }
 }
 
@@ -944,10 +872,9 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
 /* sub-driver description for pac7302 */
 static const struct sd_desc sd_desc = {
        .name = KBUILD_MODNAME,
-       .ctrls = sd_ctrls,
-       .nctrls = ARRAY_SIZE(sd_ctrls),
        .config = sd_config,
        .init = sd_init,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .stop0 = sd_stop0,
@@ -998,6 +925,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 115da169f32af62de40570cdb25f179abad21fba..ba3558d3f017167738f39af45e976daaa990e295 100644 (file)
@@ -694,6 +694,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index bb70092c2229f3eca85d3795058595a4f981fd9e..a33cb78a839cb4ad0d89082171bfe52ae2bc7717 100644 (file)
@@ -45,15 +45,6 @@ MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
 MODULE_DESCRIPTION("Endpoints se401");
 MODULE_LICENSE("GPL");
 
-/* controls */
-enum e_ctrl {
-       BRIGHTNESS,
-       GAIN,
-       EXPOSURE,
-       FREQ,
-       NCTRL   /* number of controls */
-};
-
 /* exposure change state machine states */
 enum {
        EXPO_CHANGED,
@@ -64,7 +55,11 @@ enum {
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
-       struct gspca_ctrl ctrls[NCTRL];
+       struct { /* exposure/freq control cluster */
+               struct v4l2_ctrl *exposure;
+               struct v4l2_ctrl *freq;
+       };
+       bool has_brightness;
        struct v4l2_pix_format fmts[MAX_MODES];
        int pixels_read;
        int packet_read;
@@ -77,60 +72,6 @@ struct sd {
        int expo_change_state;
 };
 
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setgain(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-
-static const struct ctrl sd_ctrls[NCTRL] = {
-[BRIGHTNESS] = {
-               {
-                       .id      = V4L2_CID_BRIGHTNESS,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Brightness",
-                       .minimum = 0,
-                       .maximum = 255,
-                       .step    = 1,
-                       .default_value = 15,
-               },
-               .set_control = setbrightness
-       },
-[GAIN] = {
-               {
-                       .id      = V4L2_CID_GAIN,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Gain",
-                       .minimum = 0,
-                       .maximum = 50, /* Really 63 but > 50 is not pretty */
-                       .step    = 1,
-                       .default_value = 25,
-               },
-               .set_control = setgain
-       },
-[EXPOSURE] = {
-               {
-                       .id = V4L2_CID_EXPOSURE,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Exposure",
-                       .minimum = 0,
-                       .maximum = 32767,
-                       .step = 1,
-                       .default_value = 15000,
-               },
-               .set_control = setexposure
-       },
-[FREQ] = {
-               {
-                       .id      = V4L2_CID_POWER_LINE_FREQUENCY,
-                       .type    = V4L2_CTRL_TYPE_MENU,
-                       .name    = "Light frequency filter",
-                       .minimum = 0,
-                       .maximum = 2,
-                       .step    = 1,
-                       .default_value = 0,
-               },
-               .set_control = setexposure
-       },
-};
 
 static void se401_write_req(struct gspca_dev *gspca_dev, u16 req, u16 value,
                            int silent)
@@ -224,22 +165,15 @@ static int se401_get_feature(struct gspca_dev *gspca_dev, u16 selector)
        return gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS))
-               return;
-
        /* HDG: this does not seem to do anything on my cam */
-       se401_write_req(gspca_dev, SE401_REQ_SET_BRT,
-                       sd->ctrls[BRIGHTNESS].val, 0);
+       se401_write_req(gspca_dev, SE401_REQ_SET_BRT, val, 0);
 }
 
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       u16 gain = 63 - sd->ctrls[GAIN].val;
+       u16 gain = 63 - val;
 
        /* red color gain */
        se401_set_feature(gspca_dev, HV7131_REG_ARCG, gain);
@@ -249,10 +183,10 @@ static void setgain(struct gspca_dev *gspca_dev)
        se401_set_feature(gspca_dev, HV7131_REG_ABCG, gain);
 }
 
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val, s32 freq)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int integration = sd->ctrls[EXPOSURE].val << 6;
+       int integration = val << 6;
        u8 expose_h, expose_m, expose_l;
 
        /* Do this before the set_feature calls, for proper timing wrt
@@ -262,9 +196,9 @@ static void setexposure(struct gspca_dev *gspca_dev)
           through so be it */
        sd->expo_change_state = EXPO_CHANGED;
 
-       if (sd->ctrls[FREQ].val == V4L2_CID_POWER_LINE_FREQUENCY_50HZ)
+       if (freq == V4L2_CID_POWER_LINE_FREQUENCY_50HZ)
                integration = integration - integration % 106667;
-       if (sd->ctrls[FREQ].val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ)
+       if (freq == V4L2_CID_POWER_LINE_FREQUENCY_60HZ)
                integration = integration - integration % 88889;
 
        expose_h = (integration >> 16);
@@ -375,15 +309,12 @@ static int sd_config(struct gspca_dev *gspca_dev,
        cam->bulk = 1;
        cam->bulk_size = BULK_SIZE;
        cam->bulk_nurbs = 4;
-       cam->ctrls = sd->ctrls;
        sd->resetlevel = 0x2d; /* Set initial resetlevel */
 
        /* See if the camera supports brightness */
        se401_read_req(gspca_dev, SE401_REQ_GET_BRT, 1);
-       if (gspca_dev->usb_err) {
-               gspca_dev->ctrl_dis = (1 << BRIGHTNESS);
-               gspca_dev->usb_err = 0;
-       }
+       sd->has_brightness = !!gspca_dev->usb_err;
+       gspca_dev->usb_err = 0;
 
        return 0;
 }
@@ -442,9 +373,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
        }
        se401_set_feature(gspca_dev, SE401_OPERATINGMODE, mode);
 
-       setbrightness(gspca_dev);
-       setgain(gspca_dev);
-       setexposure(gspca_dev);
        se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel);
 
        sd->packet_read = 0;
@@ -666,27 +594,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
                sd_pkt_scan_janggu(gspca_dev, data, len);
 }
 
-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 V4L2_CID_POWER_LINE_FREQUENCY_DISABLED:
-                       strcpy((char *) menu->name, "NoFliker");
-                       return 0;
-               case V4L2_CID_POWER_LINE_FREQUENCY_50HZ:
-                       strcpy((char *) menu->name, "50 Hz");
-                       return 0;
-               case V4L2_CID_POWER_LINE_FREQUENCY_60HZ:
-                       strcpy((char *) menu->name, "60 Hz");
-                       return 0;
-               }
-               break;
-       }
-       return -EINVAL;
-}
-
 #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
 {
@@ -714,19 +621,73 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
 }
 #endif
 
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
+
+       gspca_dev->usb_err = 0;
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_GAIN:
+               setgain(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_EXPOSURE:
+               setexposure(gspca_dev, ctrl->val, sd->freq->val);
+               break;
+       }
+       return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 4);
+       if (sd->has_brightness)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 15);
+       /* max is really 63 but > 50 is not pretty */
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, 0, 50, 1, 25);
+       sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_EXPOSURE, 0, 32767, 1, 15000);
+       sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+                       V4L2_CID_POWER_LINE_FREQUENCY,
+                       V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       v4l2_ctrl_cluster(2, &sd->exposure);
+       return 0;
+}
+
 /* 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,
+       .init_controls = sd_init_controls,
        .isoc_init = sd_isoc_init,
        .start = sd_start,
        .stopN = sd_stopN,
        .dq_callback = sd_dq_callback,
        .pkt_scan = sd_pkt_scan,
-       .querymenu = sd_querymenu,
 #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
        .int_pkt_scan = sd_int_pkt_scan,
 #endif
@@ -769,6 +730,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
        .pre_reset = sd_pre_reset,
        .post_reset = sd_post_reset,
index 478533cb1152ef15e7af49c9adef7b4f6c5d9a8a..03fa3fd940b4385d9a46ed5029d2b77639af6080 100644 (file)
@@ -40,10 +40,6 @@ struct init_command {
        unsigned char to_read; /* length to read. 0 means no reply requested */
 };
 
-/* V4L2 controls supported by the driver */
-static const struct ctrl sd_ctrls[] = {
-};
-
 /* How to change the resolution of any of the VGA cams is unknown */
 static const struct v4l2_pix_format vga_mode[] = {
        {640, 480, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE,
@@ -695,8 +691,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
 /* 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,
@@ -734,6 +728,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index e2bdf8f632f42f00e90bb064ffab3ebdd99b72d9..fd1f8d2d3b0b4f52b6640b4f540ac1cf3226898f 100644 (file)
@@ -56,26 +56,16 @@ MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
 MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver");
 MODULE_LICENSE("GPL");
 
-/* controls */
-enum e_ctrl {
-       BRIGHTNESS,
-       GAIN,
-       EXPOSURE,
-       AUTOGAIN,
-       FREQ,
-       NCTRLS          /* number of controls */
-};
-
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
 
-       struct gspca_ctrl ctrls[NCTRLS];
+       struct v4l2_ctrl *brightness;
+       struct v4l2_ctrl *plfreq;
 
        atomic_t avg_lum;
        int prev_avg_lum;
-       int exp_too_low_cnt;
-       int exp_too_high_cnt;
+       int exposure_knee;
        int header_read;
        u8 header[12]; /* Header without sof marker */
 
@@ -107,24 +97,16 @@ struct sensor_data {
        sensor_init_t *sensor_init;
        int sensor_init_size;
        int flags;
-       unsigned ctrl_dis;
        __u8 sensor_addr;
 };
 
 /* sensor_data flags */
-#define F_GAIN 0x01            /* has gain */
-#define F_SIF  0x02            /* sif or vga */
-#define F_COARSE_EXPO 0x04     /* exposure control is coarse */
+#define F_SIF          0x01    /* sif or vga */
 
 /* priv field of struct v4l2_pix_format flags (do not use low nibble!) */
 #define MODE_RAW 0x10          /* raw bayer mode */
 #define MODE_REDUCED_SIF 0x20  /* vga mode (320x240 / 160x120) on sif cam */
 
-/* ctrl_dis helper macros */
-#define NO_EXPO ((1 << EXPOSURE) | (1 << AUTOGAIN))
-#define NO_FREQ (1 << FREQ)
-#define NO_BRIGHTNESS (1 << BRIGHTNESS)
-
 #define COMP 0xc7              /* 0x87 //0x07 */
 #define COMP1 0xc9             /* 0x89 //0x09 */
 
@@ -133,12 +115,12 @@ struct sensor_data {
 
 #define SYS_CLK 0x04
 
-#define SENS(bridge, sensor, _flags, _ctrl_dis, _sensor_addr) \
+#define SENS(bridge, sensor, _flags, _sensor_addr) \
 { \
        .bridge_init = bridge, \
        .sensor_init = sensor, \
        .sensor_init_size = sizeof(sensor), \
-       .flags = _flags, .ctrl_dis = _ctrl_dis, .sensor_addr = _sensor_addr \
+       .flags = _flags, .sensor_addr = _sensor_addr \
 }
 
 /* We calculate the autogain at the end of the transfer of a frame, at this
@@ -147,87 +129,6 @@ struct sensor_data {
    the new settings to come into effect before doing any other adjustments. */
 #define AUTOGAIN_IGNORE_FRAMES 1
 
-/* V4L2 controls supported by the driver */
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setgain(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static void setfreq(struct gspca_dev *gspca_dev);
-
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[BRIGHTNESS] = {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-               .default_value = 127,
-           },
-           .set_control = setbrightness
-       },
-[GAIN] = {
-           {
-               .id      = V4L2_CID_GAIN,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Gain",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-#define GAIN_KNEE 230
-               .default_value = 127,
-           },
-           .set_control = setgain
-       },
-[EXPOSURE] = {
-               {
-                       .id = V4L2_CID_EXPOSURE,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Exposure",
-                       .minimum = 0,
-                       .maximum = 1023,
-                       .step = 1,
-                       .default_value = 66,
-                               /*  33 ms / 30 fps (except on PASXXX) */
-#define EXPOSURE_KNEE 200      /* 100 ms / 10 fps (except on PASXXX) */
-                       .flags = 0,
-               },
-               .set_control = setexposure
-       },
-/* for coarse exposure */
-#define COARSE_EXPOSURE_MIN 2
-#define COARSE_EXPOSURE_MAX 15
-#define COARSE_EXPOSURE_DEF  2 /* 30 fps */
-[AUTOGAIN] = {
-               {
-                       .id = V4L2_CID_AUTOGAIN,
-                       .type = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name = "Automatic Gain (and Exposure)",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step = 1,
-#define AUTOGAIN_DEF 1
-                       .default_value = AUTOGAIN_DEF,
-                       .flags = V4L2_CTRL_FLAG_UPDATE
-               },
-               .set = sd_setautogain,
-       },
-[FREQ] = {
-               {
-                       .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_control = setfreq
-       },
-};
-
 static const struct v4l2_pix_format vga_mode[] = {
        {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
                .bytesperline = 160,
@@ -532,25 +433,27 @@ static const __u8 tas5130_sensor_init[][8] = {
 };
 
 static const struct sensor_data sensor_data[] = {
-SENS(initHv7131d, hv7131d_sensor_init, F_GAIN, NO_BRIGHTNESS|NO_FREQ, 0),
-SENS(initHv7131r, hv7131r_sensor_init, 0, NO_BRIGHTNESS|NO_EXPO|NO_FREQ, 0),
-SENS(initOv6650, ov6650_sensor_init, F_GAIN|F_SIF, 0, 0x60),
-SENS(initOv7630, ov7630_sensor_init, F_GAIN, 0, 0x21),
-SENS(initPas106, pas106_sensor_init, F_GAIN|F_SIF, NO_FREQ, 0),
-SENS(initPas202, pas202_sensor_init, F_GAIN, NO_FREQ, 0),
-SENS(initTas5110c, tas5110c_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO,
-       NO_BRIGHTNESS|NO_FREQ, 0),
-SENS(initTas5110d, tas5110d_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO,
-       NO_BRIGHTNESS|NO_FREQ, 0),
-SENS(initTas5130, tas5130_sensor_init, F_GAIN,
-       NO_BRIGHTNESS|NO_EXPO|NO_FREQ, 0),
+       SENS(initHv7131d, hv7131d_sensor_init, 0, 0),
+       SENS(initHv7131r, hv7131r_sensor_init, 0, 0),
+       SENS(initOv6650, ov6650_sensor_init, F_SIF, 0x60),
+       SENS(initOv7630, ov7630_sensor_init, 0, 0x21),
+       SENS(initPas106, pas106_sensor_init, F_SIF, 0),
+       SENS(initPas202, pas202_sensor_init, 0, 0),
+       SENS(initTas5110c, tas5110c_sensor_init, F_SIF, 0),
+       SENS(initTas5110d, tas5110d_sensor_init, F_SIF, 0),
+       SENS(initTas5130, tas5130_sensor_init, 0, 0),
 };
 
 /* get one byte in gspca_dev->usb_buf */
 static void reg_r(struct gspca_dev *gspca_dev,
                  __u16 value)
 {
-       usb_control_msg(gspca_dev->dev,
+       int res;
+
+       if (gspca_dev->usb_err < 0)
+               return;
+
+       res = usb_control_msg(gspca_dev->dev,
                        usb_rcvctrlpipe(gspca_dev->dev, 0),
                        0,                      /* request */
                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
@@ -558,6 +461,12 @@ static void reg_r(struct gspca_dev *gspca_dev,
                        0,                      /* index */
                        gspca_dev->usb_buf, 1,
                        500);
+
+       if (res < 0) {
+               dev_err(gspca_dev->v4l2_dev.dev,
+                       "Error reading register %02x: %d\n", value, res);
+               gspca_dev->usb_err = res;
+       }
 }
 
 static void reg_w(struct gspca_dev *gspca_dev,
@@ -565,14 +474,13 @@ static void reg_w(struct gspca_dev *gspca_dev,
                  const __u8 *buffer,
                  int len)
 {
-#ifdef GSPCA_DEBUG
-       if (len > USB_BUF_SZ) {
-               PDEBUG(D_ERR|D_PACK, "reg_w: buffer overflow");
+       int res;
+
+       if (gspca_dev->usb_err < 0)
                return;
-       }
-#endif
+
        memcpy(gspca_dev->usb_buf, buffer, len);
-       usb_control_msg(gspca_dev->dev,
+       res = usb_control_msg(gspca_dev->dev,
                        usb_sndctrlpipe(gspca_dev->dev, 0),
                        0x08,                   /* request */
                        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
@@ -580,30 +488,48 @@ static void reg_w(struct gspca_dev *gspca_dev,
                        0,                      /* index */
                        gspca_dev->usb_buf, len,
                        500);
+
+       if (res < 0) {
+               dev_err(gspca_dev->v4l2_dev.dev,
+                       "Error writing register %02x: %d\n", value, res);
+               gspca_dev->usb_err = res;
+       }
 }
 
-static int i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer)
+static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer)
 {
        int retry = 60;
 
+       if (gspca_dev->usb_err < 0)
+               return;
+
        /* is i2c ready */
        reg_w(gspca_dev, 0x08, buffer, 8);
        while (retry--) {
+               if (gspca_dev->usb_err < 0)
+                       return;
                msleep(10);
                reg_r(gspca_dev, 0x08);
                if (gspca_dev->usb_buf[0] & 0x04) {
-                       if (gspca_dev->usb_buf[0] & 0x08)
-                               return -1;
-                       return 0;
+                       if (gspca_dev->usb_buf[0] & 0x08) {
+                               dev_err(gspca_dev->v4l2_dev.dev,
+                                       "i2c write error\n");
+                               gspca_dev->usb_err = -EIO;
+                       }
+                       return;
                }
        }
-       return -1;
+
+       dev_err(gspca_dev->v4l2_dev.dev, "i2c write timeout\n");
+       gspca_dev->usb_err = -EIO;
 }
 
 static void i2c_w_vector(struct gspca_dev *gspca_dev,
                        const __u8 buffer[][8], int len)
 {
        for (;;) {
+               if (gspca_dev->usb_err < 0)
+                       return;
                reg_w(gspca_dev, 0x08, *buffer, 8);
                len -= 8;
                if (len <= 0)
@@ -624,11 +550,10 @@ static void setbrightness(struct gspca_dev *gspca_dev)
 
                /* change reg 0x06 */
                i2cOV[1] = sensor_data[sd->sensor].sensor_addr;
-               i2cOV[3] = sd->ctrls[BRIGHTNESS].val;
-               if (i2c_w(gspca_dev, i2cOV) < 0)
-                       goto err;
+               i2cOV[3] = sd->brightness->val;
+               i2c_w(gspca_dev, i2cOV);
                break;
-           }
+       }
        case SENSOR_PAS106:
        case SENSOR_PAS202: {
                __u8 i2cpbright[] =
@@ -642,54 +567,49 @@ static void setbrightness(struct gspca_dev *gspca_dev)
                        i2cpdoit[2] = 0x13;
                }
 
-               if (sd->ctrls[BRIGHTNESS].val < 127) {
+               if (sd->brightness->val < 127) {
                        /* change reg 0x0b, signreg */
                        i2cpbright[3] = 0x01;
                        /* set reg 0x0c, offset */
-                       i2cpbright[4] = 127 - sd->ctrls[BRIGHTNESS].val;
+                       i2cpbright[4] = 127 - sd->brightness->val;
                } else
-                       i2cpbright[4] = sd->ctrls[BRIGHTNESS].val - 127;
+                       i2cpbright[4] = sd->brightness->val - 127;
 
-               if (i2c_w(gspca_dev, i2cpbright) < 0)
-                       goto err;
-               if (i2c_w(gspca_dev, i2cpdoit) < 0)
-                       goto err;
+               i2c_w(gspca_dev, i2cpbright);
+               i2c_w(gspca_dev, i2cpdoit);
+               break;
+       }
+       default:
                break;
-           }
        }
-       return;
-err:
-       PDEBUG(D_ERR, "i2c error brightness");
 }
 
-static void setsensorgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u8 gain = sd->ctrls[GAIN].val;
+       u8 gain = gspca_dev->gain->val;
 
        switch (sd->sensor) {
        case SENSOR_HV7131D: {
                __u8 i2c[] =
                        {0xc0, 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x17};
 
-               i2c[3] = 0x3f - (gain / 4);
-               i2c[4] = 0x3f - (gain / 4);
-               i2c[5] = 0x3f - (gain / 4);
+               i2c[3] = 0x3f - gain;
+               i2c[4] = 0x3f - gain;
+               i2c[5] = 0x3f - gain;
 
-               if (i2c_w(gspca_dev, i2c) < 0)
-                       goto err;
+               i2c_w(gspca_dev, i2c);
                break;
-           }
+       }
        case SENSOR_TAS5110C:
        case SENSOR_TAS5130CXX: {
                __u8 i2c[] =
                        {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10};
 
                i2c[4] = 255 - gain;
-               if (i2c_w(gspca_dev, i2c) < 0)
-                       goto err;
+               i2c_w(gspca_dev, i2c);
                break;
-           }
+       }
        case SENSOR_TAS5110D: {
                __u8 i2c[] = {
                        0xb0, 0x61, 0x02, 0x00, 0x10, 0x00, 0x00, 0x17 };
@@ -703,23 +623,25 @@ static void setsensorgain(struct gspca_dev *gspca_dev)
                i2c[3] |= (gain & 0x04) << 3;
                i2c[3] |= (gain & 0x02) << 5;
                i2c[3] |= (gain & 0x01) << 7;
-               if (i2c_w(gspca_dev, i2c) < 0)
-                       goto err;
+               i2c_w(gspca_dev, i2c);
                break;
-           }
-
+       }
        case SENSOR_OV6650:
-               gain >>= 1;
-               /* fall thru */
        case SENSOR_OV7630: {
                __u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
 
+               /*
+                * The ov7630's gain is weird, at 32 the gain drops to the
+                * same level as at 16, so skip 32-47 (of the 0-63 scale).
+                */
+               if (sd->sensor == SENSOR_OV7630 && gain >= 32)
+                       gain += 16;
+
                i2c[1] = sensor_data[sd->sensor].sensor_addr;
-               i2c[3] = gain >> 2;
-               if (i2c_w(gspca_dev, i2c) < 0)
-                       goto err;
+               i2c[3] = gain;
+               i2c_w(gspca_dev, i2c);
                break;
-           }
+       }
        case SENSOR_PAS106:
        case SENSOR_PAS202: {
                __u8 i2cpgain[] =
@@ -737,49 +659,27 @@ static void setsensorgain(struct gspca_dev *gspca_dev)
                        i2cpdoit[2] = 0x13;
                }
 
-               i2cpgain[3] = gain >> 3;
-               i2cpcolorgain[3] = gain >> 4;
-               i2cpcolorgain[4] = gain >> 4;
-               i2cpcolorgain[5] = gain >> 4;
-               i2cpcolorgain[6] = gain >> 4;
-
-               if (i2c_w(gspca_dev, i2cpgain) < 0)
-                       goto err;
-               if (i2c_w(gspca_dev, i2cpcolorgain) < 0)
-                       goto err;
-               if (i2c_w(gspca_dev, i2cpdoit) < 0)
-                       goto err;
-               break;
-           }
-       }
-       return;
-err:
-       PDEBUG(D_ERR, "i2c error gain");
-}
-
-static void setgain(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       __u8 gain;
-       __u8 buf[3] = { 0, 0, 0 };
+               i2cpgain[3] = gain;
+               i2cpcolorgain[3] = gain >> 1;
+               i2cpcolorgain[4] = gain >> 1;
+               i2cpcolorgain[5] = gain >> 1;
+               i2cpcolorgain[6] = gain >> 1;
 
-       if (sensor_data[sd->sensor].flags & F_GAIN) {
-               /* Use the sensor gain to do the actual gain */
-               setsensorgain(gspca_dev);
-               return;
+               i2c_w(gspca_dev, i2cpgain);
+               i2c_w(gspca_dev, i2cpcolorgain);
+               i2c_w(gspca_dev, i2cpdoit);
+               break;
        }
-
-       if (sd->bridge == BRIDGE_103) {
-               gain = sd->ctrls[GAIN].val >> 1;
-               buf[0] = gain; /* Red */
-               buf[1] = gain; /* Green */
-               buf[2] = gain; /* Blue */
-               reg_w(gspca_dev, 0x05, buf, 3);
-       } else {
-               gain = sd->ctrls[GAIN].val >> 4;
-               buf[0] = gain << 4 | gain; /* Red and blue */
-               buf[1] = gain; /* Green */
-               reg_w(gspca_dev, 0x10, buf, 2);
+       default:
+               if (sd->bridge == BRIDGE_103) {
+                       u8 buf[3] = { gain, gain, gain }; /* R, G, B */
+                       reg_w(gspca_dev, 0x05, buf, 3);
+               } else {
+                       u8 buf[2];
+                       buf[0] = gain << 4 | gain; /* Red and blue */
+                       buf[1] = gain; /* Green */
+                       reg_w(gspca_dev, 0x10, buf, 2);
+               }
        }
 }
 
@@ -792,31 +692,24 @@ static void setexposure(struct gspca_dev *gspca_dev)
                /* Note the datasheet wrongly says line mode exposure uses reg
                   0x26 and 0x27, testing has shown 0x25 + 0x26 */
                __u8 i2c[] = {0xc0, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x17};
-               /* The HV7131D's exposure goes from 0 - 65535, we scale our
-                  exposure of 0-1023 to 0-6138. There are 2 reasons for this:
-                  1) This puts our exposure knee of 200 at approx the point
-                     where the framerate starts dropping
-                  2) At 6138 the framerate has already dropped to 2 fps,
-                     going any lower makes little sense */
-               u16 reg = sd->ctrls[EXPOSURE].val * 6;
+               u16 reg = gspca_dev->exposure->val;
 
                i2c[3] = reg >> 8;
                i2c[4] = reg & 0xff;
-               if (i2c_w(gspca_dev, i2c) != 0)
-                       goto err;
+               i2c_w(gspca_dev, i2c);
                break;
-           }
+       }
        case SENSOR_TAS5110C:
        case SENSOR_TAS5110D: {
                /* register 19's high nibble contains the sn9c10x clock divider
                   The high nibble configures the no fps according to the
                   formula: 60 / high_nibble. With a maximum of 30 fps */
-               u8 reg = sd->ctrls[EXPOSURE].val;
+               u8 reg = gspca_dev->exposure->val;
 
                reg = (reg << 4) | 0x0b;
                reg_w(gspca_dev, 0x19, &reg, 1);
                break;
-           }
+       }
        case SENSOR_OV6650:
        case SENSOR_OV7630: {
                /* The ov6650 / ov7630 have 2 registers which both influence
@@ -848,7 +741,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
                } else
                        reg10_max = 0x41;
 
-               reg11 = (15 * sd->ctrls[EXPOSURE].val + 999) / 1000;
+               reg11 = (15 * gspca_dev->exposure->val + 999) / 1000;
                if (reg11 < 1)
                        reg11 = 1;
                else if (reg11 > 16)
@@ -861,16 +754,16 @@ static void setexposure(struct gspca_dev *gspca_dev)
                        reg11 = 4;
 
                /* frame exposure time in ms = 1000 * reg11 / 30    ->
-               reg10 = (sd->ctrls[EXPOSURE].val / 2) * reg10_max
+               reg10 = (gspca_dev->exposure->val / 2) * reg10_max
                                / (1000 * reg11 / 30) */
-               reg10 = (sd->ctrls[EXPOSURE].val * 15 * reg10_max)
+               reg10 = (gspca_dev->exposure->val * 15 * reg10_max)
                                / (1000 * reg11);
 
                /* Don't allow this to get below 10 when using autogain, the
                   steps become very large (relatively) when below 10 causing
                   the image to oscilate from much too dark, to much too bright
                   and back again. */
-               if (sd->ctrls[AUTOGAIN].val && reg10 < 10)
+               if (gspca_dev->autogain->val && reg10 < 10)
                        reg10 = 10;
                else if (reg10 > reg10_max)
                        reg10 = reg10_max;
@@ -884,12 +777,11 @@ static void setexposure(struct gspca_dev *gspca_dev)
                if (sd->reg11 == reg11)
                        i2c[0] = 0xa0;
 
-               if (i2c_w(gspca_dev, i2c) == 0)
+               i2c_w(gspca_dev, i2c);
+               if (gspca_dev->usb_err == 0)
                        sd->reg11 = reg11;
-               else
-                       goto err;
                break;
-           }
+       }
        case SENSOR_PAS202: {
                __u8 i2cpframerate[] =
                        {0xb0, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x16};
@@ -909,28 +801,25 @@ static void setexposure(struct gspca_dev *gspca_dev)
                   frame exposure times (like we are doing with the ov chips),
                   as that sometimes leads to jumps in the exposure control,
                   which are bad for auto exposure. */
-               if (sd->ctrls[EXPOSURE].val < 200) {
-                       i2cpexpo[3] = 255 - (sd->ctrls[EXPOSURE].val * 255)
+               if (gspca_dev->exposure->val < 200) {
+                       i2cpexpo[3] = 255 - (gspca_dev->exposure->val * 255)
                                                / 200;
                        framerate_ctrl = 500;
                } else {
                        /* The PAS202's exposure control goes from 0 - 4095,
                           but anything below 500 causes vsync issues, so scale
                           our 200-1023 to 500-4095 */
-                       framerate_ctrl = (sd->ctrls[EXPOSURE].val - 200)
+                       framerate_ctrl = (gspca_dev->exposure->val - 200)
                                                        * 1000 / 229 +  500;
                }
 
                i2cpframerate[3] = framerate_ctrl >> 6;
                i2cpframerate[4] = framerate_ctrl & 0x3f;
-               if (i2c_w(gspca_dev, i2cpframerate) < 0)
-                       goto err;
-               if (i2c_w(gspca_dev, i2cpexpo) < 0)
-                       goto err;
-               if (i2c_w(gspca_dev, i2cpdoit) < 0)
-                       goto err;
+               i2c_w(gspca_dev, i2cpframerate);
+               i2c_w(gspca_dev, i2cpexpo);
+               i2c_w(gspca_dev, i2cpdoit);
                break;
-           }
+       }
        case SENSOR_PAS106: {
                __u8 i2cpframerate[] =
                        {0xb1, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14};
@@ -942,46 +831,40 @@ static void setexposure(struct gspca_dev *gspca_dev)
 
                /* For values below 150 use partial frame exposure, above
                   that use framerate ctrl */
-               if (sd->ctrls[EXPOSURE].val < 150) {
-                       i2cpexpo[3] = 150 - sd->ctrls[EXPOSURE].val;
+               if (gspca_dev->exposure->val < 150) {
+                       i2cpexpo[3] = 150 - gspca_dev->exposure->val;
                        framerate_ctrl = 300;
                } else {
                        /* The PAS106's exposure control goes from 0 - 4095,
                           but anything below 300 causes vsync issues, so scale
                           our 150-1023 to 300-4095 */
-                       framerate_ctrl = (sd->ctrls[EXPOSURE].val - 150)
+                       framerate_ctrl = (gspca_dev->exposure->val - 150)
                                                * 1000 / 230 + 300;
                }
 
                i2cpframerate[3] = framerate_ctrl >> 4;
                i2cpframerate[4] = framerate_ctrl & 0x0f;
-               if (i2c_w(gspca_dev, i2cpframerate) < 0)
-                       goto err;
-               if (i2c_w(gspca_dev, i2cpexpo) < 0)
-                       goto err;
-               if (i2c_w(gspca_dev, i2cpdoit) < 0)
-                       goto err;
+               i2c_w(gspca_dev, i2cpframerate);
+               i2c_w(gspca_dev, i2cpexpo);
+               i2c_w(gspca_dev, i2cpdoit);
+               break;
+       }
+       default:
                break;
-           }
        }
-       return;
-err:
-       PDEBUG(D_ERR, "i2c error exposure");
 }
 
 static void setfreq(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       switch (sd->sensor) {
-       case SENSOR_OV6650:
-       case SENSOR_OV7630: {
+       if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630) {
                /* Framerate adjust register for artificial light 50 hz flicker
                   compensation, for the ov6650 this is identical to ov6630
                   0x2b register, see ov6630 datasheet.
                   0x4f / 0x8a -> (30 fps -> 25 fps), 0x00 -> no adjustment */
                __u8 i2c[] = {0xa0, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10};
-               switch (sd->ctrls[FREQ].val) {
+               switch (sd->plfreq->val) {
                default:
 /*             case 0:                  * no filter*/
 /*             case 2:                  * 60 hz */
@@ -993,25 +876,17 @@ static void setfreq(struct gspca_dev *gspca_dev)
                        break;
                }
                i2c[1] = sensor_data[sd->sensor].sensor_addr;
-               if (i2c_w(gspca_dev, i2c) < 0)
-                       PDEBUG(D_ERR, "i2c error setfreq");
-               break;
-           }
+               i2c_w(gspca_dev, i2c);
        }
 }
 
-#define WANT_REGULAR_AUTOGAIN
-#define WANT_COARSE_EXPO_AUTOGAIN
-#include "autogain_functions.h"
-
 static void do_autogain(struct gspca_dev *gspca_dev)
 {
-       int deadzone, desired_avg_lum, result;
        struct sd *sd = (struct sd *) gspca_dev;
-       int avg_lum = atomic_read(&sd->avg_lum);
+       int deadzone, desired_avg_lum, avg_lum;
 
-       if ((gspca_dev->ctrl_dis & (1 << AUTOGAIN)) ||
-           avg_lum == -1 || !sd->ctrls[AUTOGAIN].val)
+       avg_lum = atomic_read(&sd->avg_lum);
+       if (avg_lum == -1)
                return;
 
        if (sd->autogain_ignore_frames > 0) {
@@ -1030,22 +905,18 @@ static void do_autogain(struct gspca_dev *gspca_dev)
                desired_avg_lum = 13000;
        }
 
-       if (sensor_data[sd->sensor].flags & F_COARSE_EXPO)
-               result = coarse_grained_expo_autogain(gspca_dev, avg_lum,
-                               sd->ctrls[BRIGHTNESS].val
-                                               * desired_avg_lum / 127,
-                               deadzone);
-       else
-               result = auto_gain_n_exposure(gspca_dev, avg_lum,
-                               sd->ctrls[BRIGHTNESS].val
-                                               * desired_avg_lum / 127,
-                               deadzone, GAIN_KNEE, EXPOSURE_KNEE);
-
-       if (result) {
-               PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d",
-                       (int) sd->ctrls[GAIN].val,
-                       (int) sd->ctrls[EXPOSURE].val);
-               sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
+       if (sd->brightness)
+               desired_avg_lum = sd->brightness->val * desired_avg_lum / 127;
+
+       if (gspca_dev->exposure->maximum < 500) {
+               if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum,
+                               desired_avg_lum, deadzone))
+                       sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
+       } else {
+               int gain_knee = gspca_dev->gain->maximum * 9 / 10;
+               if (gspca_expo_autogain(gspca_dev, avg_lum, desired_avg_lum,
+                               deadzone, gain_knee, sd->exposure_knee))
+                       sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
        }
 }
 
@@ -1064,14 +935,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
        sd->sensor = id->driver_info >> 8;
        sd->bridge = id->driver_info & 0xff;
 
-       gspca_dev->ctrl_dis = sensor_data[sd->sensor].ctrl_dis;
-#if AUTOGAIN_DEF
-       if (!(gspca_dev->ctrl_dis & (1 << AUTOGAIN)))
-               gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
-#endif
-
        cam = &gspca_dev->cam;
-       cam->ctrls = sd->ctrls;
        if (!(sensor_data[sd->sensor].flags & F_SIF)) {
                cam->cam_mode = vga_mode;
                cam->nmodes = ARRAY_SIZE(vga_mode);
@@ -1087,18 +951,143 @@ static int sd_config(struct gspca_dev *gspca_dev,
 /* 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;
        const __u8 stop = 0x09; /* Disable stream turn of LED */
 
-       if (sensor_data[sd->sensor].flags & F_COARSE_EXPO) {
-               sd->ctrls[EXPOSURE].min = COARSE_EXPOSURE_MIN;
-               sd->ctrls[EXPOSURE].max = COARSE_EXPOSURE_MAX;
-               sd->ctrls[EXPOSURE].def = COARSE_EXPOSURE_DEF;
-               if (sd->ctrls[EXPOSURE].val > COARSE_EXPOSURE_MAX)
-                       sd->ctrls[EXPOSURE].val = COARSE_EXPOSURE_DEF;
+       reg_w(gspca_dev, 0x01, &stop, 1);
+
+       return gspca_dev->usb_err;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
+
+       gspca_dev->usb_err = 0;
+
+       if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->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. */
+               gspca_dev->gain->val = gspca_dev->gain->default_value;
+               gspca_dev->exposure->val = gspca_dev->exposure->default_value;
+               sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
        }
 
-       reg_w(gspca_dev, 0x01, &stop, 1);
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
+                       setexposure(gspca_dev);
+               if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
+                       setgain(gspca_dev);
+               break;
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               setfreq(gspca_dev);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
+
+/* this function is called at probe time */
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 5);
+
+       if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630 ||
+           sd->sensor == SENSOR_PAS106 || sd->sensor == SENSOR_PAS202)
+               sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+
+       /* Gain range is sensor dependent */
+       switch (sd->sensor) {
+       case SENSOR_OV6650:
+       case SENSOR_PAS106:
+       case SENSOR_PAS202:
+               gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_GAIN, 0, 31, 1, 15);
+               break;
+       case SENSOR_OV7630:
+               gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_GAIN, 0, 47, 1, 31);
+               break;
+       case SENSOR_HV7131D:
+               gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_GAIN, 0, 63, 1, 31);
+               break;
+       case SENSOR_TAS5110C:
+       case SENSOR_TAS5110D:
+       case SENSOR_TAS5130CXX:
+               gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_GAIN, 0, 255, 1, 127);
+               break;
+       default:
+               if (sd->bridge == BRIDGE_103) {
+                       gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                               V4L2_CID_GAIN, 0, 127, 1, 63);
+               } else {
+                       gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                               V4L2_CID_GAIN, 0, 15, 1, 7);
+               }
+       }
+
+       /* Exposure range is sensor dependent, and not all have exposure */
+       switch (sd->sensor) {
+       case SENSOR_HV7131D:
+               gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_EXPOSURE, 0, 8191, 1, 482);
+               sd->exposure_knee = 964;
+               break;
+       case SENSOR_OV6650:
+       case SENSOR_OV7630:
+       case SENSOR_PAS106:
+       case SENSOR_PAS202:
+               gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_EXPOSURE, 0, 1023, 1, 66);
+               sd->exposure_knee = 200;
+               break;
+       case SENSOR_TAS5110C:
+       case SENSOR_TAS5110D:
+               gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                       V4L2_CID_EXPOSURE, 2, 15, 1, 2);
+               break;
+       }
+
+       if (gspca_dev->exposure) {
+               gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                                               V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+       }
+
+       if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630)
+               sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+                       V4L2_CID_POWER_LINE_FREQUENCY,
+                       V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+                       V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+
+       if (gspca_dev->autogain)
+               v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
 
        return 0;
 }
@@ -1242,10 +1231,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
        sd->frames_to_drop = 0;
        sd->autogain_ignore_frames = 0;
-       sd->exp_too_high_cnt = 0;
-       sd->exp_too_low_cnt = 0;
+       gspca_dev->exp_too_high_cnt = 0;
+       gspca_dev->exp_too_low_cnt = 0;
        atomic_set(&sd->avg_lum, -1);
-       return 0;
+       return gspca_dev->usb_err;
 }
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -1387,37 +1376,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        }
 }
 
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->ctrls[AUTOGAIN].val = val;
-       sd->exp_too_high_cnt = 0;
-       sd->exp_too_low_cnt = 0;
-
-       /* 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->ctrls[AUTOGAIN].val
-        && !(sensor_data[sd->sensor].flags & F_COARSE_EXPO)) {
-               sd->ctrls[EXPOSURE].val = sd->ctrls[EXPOSURE].def;
-               sd->ctrls[GAIN].val = sd->ctrls[GAIN].def;
-               if (gspca_dev->streaming) {
-                       sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
-                       setexposure(gspca_dev);
-                       setgain(gspca_dev);
-               }
-       }
-
-       if (sd->ctrls[AUTOGAIN].val)
-               gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
-       else
-               gspca_dev->ctrl_inac = 0;
-
-       return 0;
-}
-
 static int sd_querymenu(struct gspca_dev *gspca_dev,
                        struct v4l2_querymenu *menu)
 {
@@ -1461,10 +1419,9 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .pkt_scan = sd_pkt_scan,
@@ -1529,6 +1486,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index f38faa9b37c3078270abdf408a29b65560e23fde..150b2df40f7f56e54e29a5acffd7c7b42f3b3d13 100644 (file)
@@ -3199,6 +3199,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 070b9c33b5177a3f917633721f29f372740e0f70..14d635277d71dffb79b0f52c323837e0fe158ce9 100644 (file)
@@ -33,102 +33,11 @@ MODULE_LICENSE("GPL");
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
 
-       u8 brightness;
-       u8 contrast;
-       u8 hue;
-       u8 color;
-       u8 sharpness;
-
        u8 pkt_seq;
 
        u8 jpeg_hdr[JPEG_HDR_SZ];
 };
 
-/* 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_sethue(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolor(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolor(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 const struct ctrl sd_ctrls[] = {
-       {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-#define BRIGHTNESS_DEF 128
-               .default_value = BRIGHTNESS_DEF,
-           },
-           .set = sd_setbrightness,
-           .get = sd_getbrightness,
-       },
-       {
-           {
-               .id      = V4L2_CID_CONTRAST,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Contrast",
-               .minimum = 0,
-               .maximum = 8,
-               .step    = 1,
-#define CONTRAST_DEF 1
-               .default_value = CONTRAST_DEF,
-           },
-           .set = sd_setcontrast,
-           .get = sd_getcontrast,
-       },
-       {
-           {
-               .id      = V4L2_CID_HUE,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Hue",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-#define HUE_DEF 0
-               .default_value = HUE_DEF,
-           },
-           .set = sd_sethue,
-           .get = sd_gethue,
-       },
-       {
-           {
-               .id      = V4L2_CID_SATURATION,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Saturation",
-               .minimum = 0,
-               .maximum = 8,
-               .step    = 1,
-#define COLOR_DEF 1
-               .default_value = COLOR_DEF,
-           },
-           .set = sd_setcolor,
-           .get = sd_getcolor,
-       },
-       {
-           {
-               .id      = V4L2_CID_SHARPNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Sharpness",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-#define SHARPNESS_DEF 0
-               .default_value = SHARPNESS_DEF,
-           },
-           .set = sd_setsharpness,
-           .get = sd_getsharpness,
-       },
-};
-
 static const struct v4l2_pix_format vga_mode[] = {
 /*             (does not work correctly)
        {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
@@ -259,58 +168,40 @@ static void wait_status_1(struct gspca_dev *gspca_dev)
        gspca_dev->usb_err = -ETIME;
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, sd->brightness);
+       reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, val);
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, sd->contrast);
+       reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, val);
 }
 
-static void sethue(struct gspca_dev *gspca_dev)
+static void sethue(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, sd->hue);
+       reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, val);
 }
 
-static void setcolor(struct gspca_dev *gspca_dev)
+static void setcolor(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, sd->color);
+       reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, val);
 }
 
-static void setsharpness(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, sd->sharpness);
+       reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, 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;
-
        gspca_dev->cam.cam_mode = vga_mode;
        gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
        gspca_dev->cam.npkt = 128; /* number of packets per ISOC message */
                        /*fixme: 256 in ms-win traces*/
 
-       sd->brightness = BRIGHTNESS_DEF;
-       sd->contrast = CONTRAST_DEF;
-       sd->hue = HUE_DEF;
-       sd->color = COLOR_DEF;
-       sd->sharpness = SHARPNESS_DEF;
-
        return 0;
 }
 
@@ -370,14 +261,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
        /* the JPEG quality shall be 85% */
        jpeg_set_qual(sd->jpeg_hdr, 85);
 
-       /* set the controls */
-       setbrightness(gspca_dev);
-       setcontrast(gspca_dev);
-       sethue(gspca_dev);
-       setcolor(gspca_dev);
-       setsharpness(gspca_dev);
-
-       msleep(5);
        reg_r(gspca_dev, 0x00, 0x2520, 1);
        msleep(8);
 
@@ -457,103 +340,70 @@ err:
        gspca_dev->last_packet_type = DISCARD_PACKET;
 }
 
-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)
-               setbrightness(gspca_dev);
-       return gspca_dev->usb_err;
-}
-
-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)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->contrast = val;
-       if (gspca_dev->streaming)
-               setcontrast(gspca_dev);
-       return gspca_dev->usb_err;
-}
-
-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_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 gspca_dev->usb_err;
-}
-
-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_setcolor(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->color = val;
-       if (gspca_dev->streaming)
-               setcolor(gspca_dev);
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+       gspca_dev->usb_err = 0;
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_HUE:
+               sethue(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SATURATION:
+               setcolor(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SHARPNESS:
+               setsharpness(gspca_dev, ctrl->val);
+               break;
+       }
        return gspca_dev->usb_err;
 }
 
-static int sd_getcolor(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->color;
-       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)
-               setsharpness(gspca_dev);
-       return gspca_dev->usb_err;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->sharpness;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 5);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 8, 1, 1);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_HUE, 0, 255, 1, 0);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 8, 1, 1);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SHARPNESS, 0, 255, 1, 0);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
        return 0;
 }
 
 /* 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,
+       .init_controls = sd_init_controls,
        .isoc_init = sd_isoc_init,
        .start = sd_start,
        .stopN = sd_stopN,
@@ -587,6 +437,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 103984708c771a9555ff4661cc13c0e1b5467fa7..25cb68d0556d0e1440cbeab06dc21c4730a1bdc9 100644 (file)
@@ -30,18 +30,12 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
 MODULE_DESCRIPTION("GSPCA/SPCA500 USB Camera Driver");
 MODULE_LICENSE("GPL");
 
+#define QUALITY 85
+
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;             /* !! must be the first item */
 
-       unsigned char brightness;
-       unsigned char contrast;
-       unsigned char colors;
-       u8 quality;
-#define QUALITY_MIN 70
-#define QUALITY_MAX 95
-#define QUALITY_DEF 85
-
        char subtype;
 #define AgfaCl20 0
 #define AiptekPocketDV 1
@@ -62,59 +56,6 @@ struct sd {
        u8 jpeg_hdr[JPEG_HDR_SZ];
 };
 
-/* 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 const struct ctrl sd_ctrls[] = {
-       {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-#define BRIGHTNESS_DEF 127
-               .default_value = BRIGHTNESS_DEF,
-           },
-           .set = sd_setbrightness,
-           .get = sd_getbrightness,
-       },
-       {
-           {
-               .id      = V4L2_CID_CONTRAST,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Contrast",
-               .minimum = 0,
-               .maximum = 63,
-               .step    = 1,
-#define CONTRAST_DEF 31
-               .default_value = CONTRAST_DEF,
-           },
-           .set = sd_setcontrast,
-           .get = sd_getcontrast,
-       },
-       {
-           {
-               .id      = V4L2_CID_SATURATION,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Color",
-               .minimum = 0,
-               .maximum = 63,
-               .step    = 1,
-#define COLOR_DEF 31
-               .default_value = COLOR_DEF,
-           },
-           .set = sd_setcolors,
-           .get = sd_getcolors,
-       },
-};
-
 static const struct v4l2_pix_format vga_mode[] = {
        {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
                .bytesperline = 320,
@@ -641,10 +582,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
                cam->cam_mode = sif_mode;
                cam->nmodes = ARRAY_SIZE(sif_mode);
        }
-       sd->brightness = BRIGHTNESS_DEF;
-       sd->contrast = CONTRAST_DEF;
-       sd->colors = COLOR_DEF;
-       sd->quality = QUALITY_DEF;
        return 0;
 }
 
@@ -673,7 +610,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
        /* create the JPEG header */
        jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
                        0x22);          /* JPEG 411 */
-       jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+       jpeg_set_qual(sd->jpeg_hdr, QUALITY);
 
        if (sd->subtype == LogitechClickSmart310) {
                xmult = 0x16;
@@ -934,122 +871,79 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
        reg_w(gspca_dev, 0x00, 0x8167,
-                       (__u8) (sd->brightness - 128));
+                       (__u8) (val - 128));
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       reg_w(gspca_dev, 0x00, 0x8168, sd->contrast);
+       reg_w(gspca_dev, 0x00, 0x8168, val);
 }
 
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       reg_w(gspca_dev, 0x00, 0x8169, sd->colors);
+       reg_w(gspca_dev, 0x00, 0x8169, val);
 }
 
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
 
-       sd->brightness = val;
-       if (gspca_dev->streaming)
-               setbrightness(gspca_dev);
-       return 0;
-}
+       gspca_dev->usb_err = 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)
-               setcontrast(gspca_dev);
-       return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       if (!gspca_dev->streaming)
+               return 0;
 
-       *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;
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SATURATION:
+               setcolors(gspca_dev, ctrl->val);
+               break;
+       }
+       return gspca_dev->usb_err;
 }
 
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
-                       struct v4l2_jpegcompression *jcomp)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (jcomp->quality < QUALITY_MIN)
-               sd->quality = QUALITY_MIN;
-       else if (jcomp->quality > QUALITY_MAX)
-               sd->quality = QUALITY_MAX;
-       else
-               sd->quality = jcomp->quality;
-       if (gspca_dev->streaming)
-               jpeg_set_qual(sd->jpeg_hdr, sd->quality);
-       return 0;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
-                       struct v4l2_jpegcompression *jcomp)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       memset(jcomp, 0, sizeof *jcomp);
-       jcomp->quality = sd->quality;
-       jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
-                       | V4L2_JPEG_MARKER_DQT;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 3);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 63, 1, 31);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 63, 1, 31);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
        return 0;
 }
 
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .pkt_scan = sd_pkt_scan,
-       .get_jcomp = sd_get_jcomp,
-       .set_jcomp = sd_set_jcomp,
 };
 
 /* -- module initialisation -- */
@@ -1089,6 +983,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 9c16821addd481e16561e6746ebbbe774e8ee47a..3b7f777785b4c912dd2e393e75b77e099a1291ce 100644 (file)
@@ -49,91 +49,6 @@ struct sd {
 #define ViewQuestM318B 6
 };
 
-/* 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_setblue_balance(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
-#define MY_BRIGHTNESS 0
-       {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 127,
-               .step    = 1,
-               .default_value = 0,
-           },
-           .set = sd_setbrightness,
-           .get = sd_getbrightness,
-       },
-#define MY_CONTRAST 1
-       {
-           {
-               .id      = V4L2_CID_CONTRAST,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Contrast",
-               .minimum = 0,
-               .maximum = 64725,
-               .step    = 1,
-               .default_value = 64725,
-           },
-           .set = sd_setcontrast,
-           .get = sd_getcontrast,
-       },
-#define MY_COLOR 2
-       {
-           {
-               .id      = V4L2_CID_SATURATION,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Color",
-               .minimum = 0,
-               .maximum = 63,
-               .step    = 1,
-               .default_value = 20,
-           },
-           .set = sd_setcolors,
-           .get = sd_getcolors,
-       },
-#define MY_BLUE_BALANCE 3
-       {
-           {
-               .id      = V4L2_CID_BLUE_BALANCE,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Blue Balance",
-               .minimum = 0,
-               .maximum = 127,
-               .step    = 1,
-               .default_value = 0,
-           },
-           .set = sd_setblue_balance,
-           .get = sd_getblue_balance,
-       },
-#define MY_RED_BALANCE 4
-       {
-           {
-               .id      = V4L2_CID_RED_BALANCE,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Red Balance",
-               .minimum = 0,
-               .maximum = 127,
-               .step    = 1,
-               .default_value = 0,
-           },
-           .set = sd_setred_balance,
-           .get = sd_getred_balance,
-       },
-};
-
 static const struct v4l2_pix_format vga_mode[] = {
        {160, 120, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE,
                .bytesperline = 160,
@@ -1878,42 +1793,32 @@ static int write_vector(struct gspca_dev *gspca_dev,
        return 0;
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness);
+       reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, val);
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
        reg_write(gspca_dev->dev, 0x00, 0x00,
-                                 (sd->contrast >> 8) & 0xff);
+                                 (val >> 8) & 0xff);
        reg_write(gspca_dev->dev, 0x00, 0x01,
-                                 sd->contrast & 0xff);
+                                 val & 0xff);
 }
 
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, sd->colors);
+       reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, val);
 }
 
-static void setblue_balance(struct gspca_dev *gspca_dev)
+static void setblue_balance(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->blue_balance);
+       reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, val);
 }
 
-static void setred_balance(struct gspca_dev *gspca_dev)
+static void setred_balance(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->red_balance);
+       reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, val);
 }
 
 /* this function is called at probe time */
@@ -1927,9 +1832,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
        cam->cam_mode = vga_mode;
        cam->nmodes = ARRAY_SIZE(vga_mode);
        sd->subtype = id->driver_info;
-       sd->brightness = sd_ctrls[MY_BRIGHTNESS].qctrl.default_value;
-       sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value;
-       sd->colors = sd_ctrls[MY_COLOR].qctrl.default_value;
 
        return 0;
 }
@@ -2008,13 +1910,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
        }
        reg_write(dev, SPCA501_REG_CTLRL, 0x01, 0x02);
 
-       /* HDG atleast the Intel CreateAndShare needs to have one of its
-        * brightness / contrast / color set otherwise it assumes what seems
-        * max contrast. Note that strange enough setting any of these is
-        * enough to fix the max contrast problem, to be sure we set all 3 */
-       setbrightness(gspca_dev);
-       setcontrast(gspca_dev);
-       setcolors(gspca_dev);
        return 0;
 }
 
@@ -2053,103 +1948,70 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
 
-       sd->brightness = val;
-       if (gspca_dev->streaming)
-               setbrightness(gspca_dev);
-       return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       gspca_dev->usb_err = 0;
 
-       *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)
-               setcontrast(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;
+       if (!gspca_dev->streaming)
+               return 0;
 
-       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_setblue_balance(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->blue_balance = val;
-       if (gspca_dev->streaming)
-               setblue_balance(gspca_dev);
-       return 0;
-}
-
-static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->blue_balance;
-       return 0;
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SATURATION:
+               setcolors(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               setblue_balance(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_RED_BALANCE:
+               setred_balance(gspca_dev, ctrl->val);
+               break;
+       }
+       return gspca_dev->usb_err;
 }
 
-static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->red_balance = val;
-       if (gspca_dev->streaming)
-               setred_balance(gspca_dev);
-       return 0;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->red_balance;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 5);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 127, 1, 0);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 64725, 1, 64725);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 63, 1, 20);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_RED_BALANCE, 0, 127, 1, 0);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
        return 0;
 }
 
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .stop0 = sd_stop0,
@@ -2185,6 +2047,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 1320f35e39f26d1b22f5de830d5225ad1257bcca..bc7d67c3cb043a98264db4b86f1293cf3563f452 100644 (file)
@@ -33,34 +33,11 @@ MODULE_LICENSE("GPL");
 struct sd {
        struct gspca_dev gspca_dev;             /* !! must be the first item */
 
-       u8 brightness;
-
        u8 subtype;
 #define IntelPCCameraPro 0
 #define Nxultra 1
 };
 
-/* 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 const struct ctrl sd_ctrls[] = {
-       {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-#define BRIGHTNESS_DEF 127
-               .default_value = BRIGHTNESS_DEF,
-           },
-           .set = sd_setbrightness,
-           .get = sd_getbrightness,
-       },
-};
-
 static const struct v4l2_pix_format vga_mode[] = {
        {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
                .bytesperline = 160,
@@ -633,7 +610,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
                cam->nmodes = ARRAY_SIZE(vga_mode);
        else                    /* no 640x480 for IntelPCCameraPro */
                cam->nmodes = ARRAY_SIZE(vga_mode) - 1;
-       sd->brightness = BRIGHTNESS_DEF;
 
        return 0;
 }
@@ -651,11 +627,8 @@ static int sd_init(struct gspca_dev *gspca_dev)
        return 0;
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       u8 brightness = sd->brightness;
-
        reg_write(gspca_dev->dev, 0x05, 0x00, (255 - brightness) >> 6);
        reg_write(gspca_dev->dev, 0x05, 0x01, (255 - brightness) << 2);
 }
@@ -706,13 +679,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
        reg_write(dev, SPCA50X_REG_COMPRESS, 0x06, mode_tb[mode][1]);
        reg_write(dev, SPCA50X_REG_COMPRESS, 0x07, mode_tb[mode][2]);
 
-       ret = reg_write(dev, SPCA50X_REG_USB,
+       return reg_write(dev, SPCA50X_REG_USB,
                         SPCA50X_USB_CTRL,
                         SPCA50X_CUSB_ENABLE);
-
-       setbrightness(gspca_dev);
-
-       return ret;
 }
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -756,30 +725,49 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        }
 }
 
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
 
-       sd->brightness = val;
-       if (gspca_dev->streaming)
-               setbrightness(gspca_dev);
-       return 0;
+       gspca_dev->usb_err = 0;
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       }
+       return gspca_dev->usb_err;
 }
 
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
 
-       *val = sd->brightness;
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 5);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
        return 0;
 }
 
 /* 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_controls = sd_init_controls,
        .init = sd_init,
        .start = sd_start,
        .stopN = sd_stopN,
@@ -812,6 +800,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 54eed87672d2c7397a4cf40e1ddbde26d7f9846c..969bb5a4cd9307812cc45f2224f3be5bd1fa5617 100644 (file)
@@ -33,83 +33,10 @@ MODULE_LICENSE("GPL");
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
 
-       unsigned char brightness;
-       unsigned char contrast;
-       unsigned char colors;
-       unsigned char hue;
        char norme;
        char channel;
 };
 
-/* 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_sethue(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
-#define SD_BRIGHTNESS 0
-       {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 0xff,
-               .step    = 1,
-               .default_value = 0x80,
-           },
-           .set = sd_setbrightness,
-           .get = sd_getbrightness,
-       },
-#define SD_CONTRAST 1
-       {
-           {
-               .id      = V4L2_CID_CONTRAST,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Contrast",
-               .minimum = 0,
-               .maximum = 0xff,
-               .step    = 1,
-               .default_value = 0x47,
-           },
-           .set = sd_setcontrast,
-           .get = sd_getcontrast,
-       },
-#define SD_COLOR 2
-       {
-           {
-               .id      = V4L2_CID_SATURATION,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Saturation",
-               .minimum = 0,
-               .maximum = 0xff,
-               .step    = 1,
-               .default_value = 0x40,
-           },
-           .set = sd_setcolors,
-           .get = sd_getcolors,
-       },
-#define SD_HUE 3
-       {
-           {
-               .id      = V4L2_CID_HUE,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Hue",
-               .minimum = 0,
-               .maximum = 0xff,
-               .step    = 1,
-               .default_value = 0,
-           },
-           .set = sd_sethue,
-           .get = sd_gethue,
-       },
-};
-
 static const struct v4l2_pix_format vga_mode[] = {
        {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
                .bytesperline = 160,
@@ -281,16 +208,11 @@ static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code,
 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;
        cam->cam_mode = vga_mode;
        cam->nmodes = ARRAY_SIZE(vga_mode);
-       sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
-       sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
-       sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value;
-       sd->hue = sd_ctrls[SD_HUE].qctrl.default_value;
        return 0;
 }
 
@@ -564,121 +486,93 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        }
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
        spca506_Initi2c(gspca_dev);
-       spca506_WriteI2c(gspca_dev, sd->brightness, SAA7113_bright);
+       spca506_WriteI2c(gspca_dev, val, SAA7113_bright);
        spca506_WriteI2c(gspca_dev, 0x01, 0x09);
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
        spca506_Initi2c(gspca_dev);
-       spca506_WriteI2c(gspca_dev, sd->contrast, SAA7113_contrast);
+       spca506_WriteI2c(gspca_dev, val, SAA7113_contrast);
        spca506_WriteI2c(gspca_dev, 0x01, 0x09);
 }
 
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
        spca506_Initi2c(gspca_dev);
-       spca506_WriteI2c(gspca_dev, sd->colors, SAA7113_saturation);
+       spca506_WriteI2c(gspca_dev, val, SAA7113_saturation);
        spca506_WriteI2c(gspca_dev, 0x01, 0x09);
 }
 
-static void sethue(struct gspca_dev *gspca_dev)
+static void sethue(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
        spca506_Initi2c(gspca_dev);
-       spca506_WriteI2c(gspca_dev, sd->hue, SAA7113_hue);
+       spca506_WriteI2c(gspca_dev, val, SAA7113_hue);
        spca506_WriteI2c(gspca_dev, 0x01, 0x09);
 }
 
-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)
-               setbrightness(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)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
 
-       sd->contrast = val;
-       if (gspca_dev->streaming)
-               setcontrast(gspca_dev);
-       return 0;
-}
+       gspca_dev->usb_err = 0;
 
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       if (!gspca_dev->streaming)
+               return 0;
 
-       *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;
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SATURATION:
+               setcolors(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_HUE:
+               sethue(gspca_dev, ctrl->val);
+               break;
+       }
+       return gspca_dev->usb_err;
 }
 
-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 const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->hue;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 4);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 255, 1, 0x47);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 255, 1, 0x40);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_HUE, 0, 255, 1, 0);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
        return 0;
 }
 
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .pkt_scan = sd_pkt_scan,
@@ -711,6 +605,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index df4e16996461e7ecd97eebd96f84ef02e1b09922..1286b4170b88cd110ad6d539955f40013a02ae83 100644 (file)
@@ -32,8 +32,6 @@ MODULE_LICENSE("GPL");
 struct sd {
        struct gspca_dev gspca_dev;             /* !! must be the first item */
 
-       u8 brightness;
-
        u8 subtype;
 #define CreativeVista 0
 #define HamaUSBSightcam 1
@@ -43,27 +41,6 @@ struct sd {
 #define ViewQuestVQ110 5
 };
 
-/* 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 const struct ctrl sd_ctrls[] = {
-       {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-#define BRIGHTNESS_DEF 128
-               .default_value = BRIGHTNESS_DEF,
-           },
-           .set = sd_setbrightness,
-           .get = sd_getbrightness,
-       },
-};
-
 static const struct v4l2_pix_format sif_mode[] = {
        {160, 120, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE,
                .bytesperline = 160,
@@ -1411,7 +1388,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
        cam->nmodes = ARRAY_SIZE(sif_mode);
 
        sd->subtype = id->driver_info;
-       sd->brightness = BRIGHTNESS_DEF;
 
        init_data = init_data_tb[sd->subtype];
        return write_vector(gspca_dev, init_data);
@@ -1471,11 +1447,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        }
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       u8 brightness = sd->brightness;
-
        /* MX seem contrast */
        reg_write(gspca_dev->dev, 0x8651, brightness);
        reg_write(gspca_dev->dev, 0x8652, brightness);
@@ -1483,31 +1456,50 @@ static void setbrightness(struct gspca_dev *gspca_dev)
        reg_write(gspca_dev->dev, 0x8654, brightness);
 }
 
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
 
-       sd->brightness = val;
-       if (gspca_dev->streaming)
-               setbrightness(gspca_dev);
-       return 0;
+       gspca_dev->usb_err = 0;
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       }
+       return gspca_dev->usb_err;
 }
 
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
 
-       *val = sd->brightness;
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 5);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
        return 0;
 }
 
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .pkt_scan = sd_pkt_scan,
@@ -1541,6 +1533,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 4a5f209ce719aa878ae082d30ca82f9faa9586f3..cfe71dd6747ddf0477f2367058dd05fb25189f1a 100644 (file)
@@ -31,39 +31,17 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
 MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver");
 MODULE_LICENSE("GPL");
 
+#define EXPOSURE_MAX (2047 + 325)
+
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
 
-       __u16 exposure;                 /* rev12a only */
-#define EXPOSURE_MIN 1
-#define EXPOSURE_DEF 700               /* == 10 fps */
-#define EXPOSURE_MAX (2047 + 325)      /* see setexposure */
-
-       __u8 contrast;                  /* rev72a only */
-#define CONTRAST_MIN 0x00
-#define CONTRAST_DEF 0x20
-#define CONTRAST_MAX 0x3f
-
-       __u8 brightness;                /* rev72a only */
-#define BRIGHTNESS_MIN 0
-#define BRIGHTNESS_DEF 0x20
-#define BRIGHTNESS_MAX 0x3f
-
-       __u8 white;
-#define HUE_MIN 1
-#define HUE_DEF 0x40
-#define HUE_MAX 0x7f
-
-       __u8 autogain;
-#define AUTOGAIN_MIN 0
-#define AUTOGAIN_DEF 1
-#define AUTOGAIN_MAX 1
-
-       __u8 gain;                      /* rev12a only */
-#define GAIN_MIN 0
-#define GAIN_DEF 63
-#define GAIN_MAX 255
+       struct { /* hue/contrast control cluster */
+               struct v4l2_ctrl *contrast;
+               struct v4l2_ctrl *hue;
+       };
+       struct v4l2_ctrl *autogain;
 
 #define EXPO12A_DEF 3
        __u8 expo12a;           /* expo/gain? for rev 12a */
@@ -461,12 +439,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
                cam->cam_mode = sif_072a_mode;
                cam->nmodes = ARRAY_SIZE(sif_072a_mode);
        }
-       sd->brightness = BRIGHTNESS_DEF;
-       sd->contrast = CONTRAST_DEF;
-       sd->white = HUE_DEF;
-       sd->exposure = EXPOSURE_DEF;
-       sd->autogain = AUTOGAIN_DEF;
-       sd->gain = GAIN_DEF;
        sd->expo12a = EXPO12A_DEF;
        return 0;
 }
@@ -491,66 +463,49 @@ static int sd_init_72a(struct gspca_dev *gspca_dev)
        return 0;
 }
 
-/* rev 72a only */
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        struct usb_device *dev = gspca_dev->dev;
-       __u8 value;
+       __u16 reg;
 
-       value = sd->brightness;
+       if (sd->chip_revision == Rev012A)
+               reg = 0x8610;
+       else
+               reg = 0x8611;
 
-       /* offsets for white balance */
-       reg_w_val(dev, 0x8611, value);          /* R */
-       reg_w_val(dev, 0x8612, value);          /* Gr */
-       reg_w_val(dev, 0x8613, value);          /* B */
-       reg_w_val(dev, 0x8614, value);          /* Gb */
+       reg_w_val(dev, reg + 0, val);           /* R */
+       reg_w_val(dev, reg + 1, val);           /* Gr */
+       reg_w_val(dev, reg + 2, val);           /* B */
+       reg_w_val(dev, reg + 3, val);           /* Gb */
 }
 
-static void setwhite(struct gspca_dev *gspca_dev)
+static void setwhite(struct gspca_dev *gspca_dev, s32 white, s32 contrast)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       __u16 white;
+       struct usb_device *dev = gspca_dev->dev;
        __u8 blue, red;
        __u16 reg;
 
        /* try to emulate MS-win as possible */
-       white = sd->white;
        red = 0x20 + white * 3 / 8;
        blue = 0x90 - white * 5 / 8;
        if (sd->chip_revision == Rev012A) {
                reg = 0x8614;
        } else {
                reg = 0x8651;
-               red += sd->contrast - 0x20;
-               blue += sd->contrast - 0x20;
+               red += contrast - 0x20;
+               blue += contrast - 0x20;
+               reg_w_val(dev, 0x8652, contrast + 0x20); /* Gr */
+               reg_w_val(dev, 0x8654, contrast + 0x20); /* Gb */
        }
-       reg_w_val(gspca_dev->dev, reg, red);
-       reg_w_val(gspca_dev->dev, reg + 2, blue);
-}
-
-static void setcontrast(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       struct usb_device *dev = gspca_dev->dev;
-       __u8 value;
-
-       if (sd->chip_revision != Rev072A)
-               return;
-       value = sd->contrast + 0x20;
-
-       /* gains for white balance */
-       setwhite(gspca_dev);
-/*     reg_w_val(dev, 0x8651, value);           * R - done by setwhite */
-       reg_w_val(dev, 0x8652, value);          /* Gr */
-/*     reg_w_val(dev, 0x8653, value);           * B - done by setwhite */
-       reg_w_val(dev, 0x8654, value);          /* Gb */
+       reg_w_val(dev, reg, red);
+       reg_w_val(dev, reg + 2, blue);
 }
 
 /* rev 12a only */
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        int i, expo = 0;
 
        /* Register 0x8309 controls exposure for the spca561,
@@ -572,8 +527,8 @@ static void setexposure(struct gspca_dev *gspca_dev)
        int table[] =  { 0, 450, 550, 625, EXPOSURE_MAX };
 
        for (i = 0; i < ARRAY_SIZE(table) - 1; i++) {
-               if (sd->exposure <= table[i + 1]) {
-                       expo  = sd->exposure - table[i];
+               if (val <= table[i + 1]) {
+                       expo  = val - table[i];
                        if (i)
                                expo += 300;
                        expo |= i << 11;
@@ -587,29 +542,27 @@ static void setexposure(struct gspca_dev *gspca_dev)
 }
 
 /* rev 12a only */
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
        /* gain reg low 6 bits  0-63 gain, bit 6 and 7, both double the
           sensitivity when set, so 31 + one of them set == 63, and 15
           with both of them set == 63 */
-       if (sd->gain < 64)
-               gspca_dev->usb_buf[0] = sd->gain;
-       else if (sd->gain < 128)
-               gspca_dev->usb_buf[0] = (sd->gain / 2) | 0x40;
+       if (val < 64)
+               gspca_dev->usb_buf[0] = val;
+       else if (val < 128)
+               gspca_dev->usb_buf[0] = (val / 2) | 0x40;
        else
-               gspca_dev->usb_buf[0] = (sd->gain / 4) | 0xc0;
+               gspca_dev->usb_buf[0] = (val / 4) | 0xc0;
 
        gspca_dev->usb_buf[1] = 0;
        reg_w_buf(gspca_dev, 0x8335, 2);
 }
 
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       if (sd->autogain)
+       if (val)
                sd->ag_cnt = AG_CNT_START;
        else
                sd->ag_cnt = -1;
@@ -644,9 +597,6 @@ static int sd_start_12a(struct gspca_dev *gspca_dev)
        memcpy(gspca_dev->usb_buf, Reg8391, 8);
        reg_w_buf(gspca_dev, 0x8391, 8);
        reg_w_buf(gspca_dev, 0x8390, 8);
-       setwhite(gspca_dev);
-       setgain(gspca_dev);
-       setexposure(gspca_dev);
 
        /* Led ON (bit 3 -> 0 */
        reg_w_val(gspca_dev->dev, 0x8114, 0x00);
@@ -654,6 +604,7 @@ static int sd_start_12a(struct gspca_dev *gspca_dev)
 }
 static int sd_start_72a(struct gspca_dev *gspca_dev)
 {
+       struct sd *sd = (struct sd *) gspca_dev;
        struct usb_device *dev = gspca_dev->dev;
        int Clck;
        int mode;
@@ -683,9 +634,10 @@ static int sd_start_72a(struct gspca_dev *gspca_dev)
        reg_w_val(dev, 0x8702, 0x81);
        reg_w_val(dev, 0x8500, mode);   /* mode */
        write_sensor_72a(gspca_dev, rev72a_init_sensor2);
-       setcontrast(gspca_dev);
+       setwhite(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue),
+                       v4l2_ctrl_g_ctrl(sd->contrast));
 /*     setbrightness(gspca_dev);        * fixme: bad values */
-       setautogain(gspca_dev);
+       setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
        reg_w_val(dev, 0x8112, 0x10 | 0x20);
        return 0;
 }
@@ -819,221 +771,96 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
-/* rev 72a only */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
 
-       sd->brightness = val;
-       if (gspca_dev->streaming)
-               setbrightness(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;
-}
-
-/* rev 72a only */
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       gspca_dev->usb_err = 0;
 
-       sd->contrast = val;
-       if (gspca_dev->streaming)
-               setcontrast(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_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       if (!gspca_dev->streaming)
+               return 0;
 
-       sd->autogain = val;
-       if (gspca_dev->streaming)
-               setautogain(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_setwhite(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->white = val;
-       if (gspca_dev->streaming)
-               setwhite(gspca_dev);
-       return 0;
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               /* hue/contrast control cluster for 72a */
+               setwhite(gspca_dev, sd->hue->val, ctrl->val);
+               break;
+       case V4L2_CID_HUE:
+               /* just plain hue control for 12a */
+               setwhite(gspca_dev, ctrl->val, 0);
+               break;
+       case V4L2_CID_EXPOSURE:
+               setexposure(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_GAIN:
+               setgain(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               setautogain(gspca_dev, ctrl->val);
+               break;
+       }
+       return gspca_dev->usb_err;
 }
 
-static int sd_getwhite(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->white;
-       return 0;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-/* rev12a only */
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_init_controls_12a(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
 
-       sd->exposure = val;
-       if (gspca_dev->streaming)
-               setexposure(gspca_dev);
-       return 0;
-}
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 3);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_HUE, 1, 0x7f, 1, 0x40);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_EXPOSURE, 1, EXPOSURE_MAX, 1, 700);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, 0, 255, 1, 63);
 
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->exposure;
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
        return 0;
 }
 
-/* rev12a only */
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_init_controls_72a(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->gain = val;
-       if (gspca_dev->streaming)
-               setgain(gspca_dev);
-       return 0;
-}
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
 
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 4);
+       sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x20);
+       sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_HUE, 1, 0x7f, 1, 0x40);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 0x3f, 1, 0x20);
+       sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
 
-       *val = sd->gain;
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       v4l2_ctrl_cluster(2, &sd->contrast);
        return 0;
 }
 
-/* control tables */
-static const struct ctrl sd_ctrls_12a[] = {
-       {
-           {
-               .id = V4L2_CID_HUE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Hue",
-               .minimum = HUE_MIN,
-               .maximum = HUE_MAX,
-               .step = 1,
-               .default_value = HUE_DEF,
-           },
-           .set = sd_setwhite,
-           .get = sd_getwhite,
-       },
-       {
-           {
-               .id = V4L2_CID_EXPOSURE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Exposure",
-               .minimum = EXPOSURE_MIN,
-               .maximum = EXPOSURE_MAX,
-               .step = 1,
-               .default_value = EXPOSURE_DEF,
-           },
-           .set = sd_setexposure,
-           .get = sd_getexposure,
-       },
-       {
-           {
-               .id = V4L2_CID_GAIN,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Gain",
-               .minimum = GAIN_MIN,
-               .maximum = GAIN_MAX,
-               .step = 1,
-               .default_value = GAIN_DEF,
-           },
-           .set = sd_setgain,
-           .get = sd_getgain,
-       },
-};
-
-static const struct ctrl sd_ctrls_72a[] = {
-       {
-           {
-               .id = V4L2_CID_HUE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Hue",
-               .minimum = HUE_MIN,
-               .maximum = HUE_MAX,
-               .step = 1,
-               .default_value = HUE_DEF,
-           },
-           .set = sd_setwhite,
-           .get = sd_getwhite,
-       },
-       {
-          {
-               .id = V4L2_CID_BRIGHTNESS,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Brightness",
-               .minimum = BRIGHTNESS_MIN,
-               .maximum = BRIGHTNESS_MAX,
-               .step = 1,
-               .default_value = BRIGHTNESS_DEF,
-           },
-           .set = sd_setbrightness,
-           .get = sd_getbrightness,
-       },
-       {
-           {
-               .id = V4L2_CID_CONTRAST,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Contrast",
-               .minimum = CONTRAST_MIN,
-               .maximum = CONTRAST_MAX,
-               .step = 1,
-               .default_value = CONTRAST_DEF,
-           },
-           .set = sd_setcontrast,
-           .get = sd_getcontrast,
-       },
-       {
-           {
-               .id = V4L2_CID_AUTOGAIN,
-               .type = V4L2_CTRL_TYPE_BOOLEAN,
-               .name = "Auto Gain",
-               .minimum = AUTOGAIN_MIN,
-               .maximum = AUTOGAIN_MAX,
-               .step = 1,
-               .default_value = AUTOGAIN_DEF,
-           },
-           .set = sd_setautogain,
-           .get = sd_getautogain,
-       },
-};
-
 /* sub-driver description */
 static const struct sd_desc sd_desc_12a = {
        .name = MODULE_NAME,
-       .ctrls = sd_ctrls_12a,
-       .nctrls = ARRAY_SIZE(sd_ctrls_12a),
+       .init_controls = sd_init_controls_12a,
        .config = sd_config,
        .init = sd_init_12a,
        .start = sd_start_12a,
@@ -1045,8 +872,7 @@ static const struct sd_desc sd_desc_12a = {
 };
 static const struct sd_desc sd_desc_72a = {
        .name = MODULE_NAME,
-       .ctrls = sd_ctrls_72a,
-       .nctrls = ARRAY_SIZE(sd_ctrls_72a),
+       .init_controls = sd_init_controls_72a,
        .config = sd_config,
        .init = sd_init_72a,
        .start = sd_start_72a,
@@ -1103,6 +929,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 04f54654a0264743162a56c95dc0e2c910ab942d..a8ac97931ad68abe6986ab19fc959bc2596e0d5a 100644 (file)
@@ -433,6 +433,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume  = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index f34ddb0570c86a7f41242fc6d4082d3e07890890..2c2f3d2f357f99afda2554d48feb89ede3fb8940 100644 (file)
@@ -340,6 +340,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume  = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 1a8ba9b3550a08ce86b1f03a0cf1b80e9d3b1d09..3e1e486af883dfaddc6cd8527c8c03a80dfd90c3 100644 (file)
@@ -36,8 +36,10 @@ MODULE_LICENSE("GPL");
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
 
-       u16 expo;
-       u8 gain;
+       struct { /* exposure/gain control cluster */
+               struct v4l2_ctrl *exposure;
+               struct v4l2_ctrl *gain;
+       };
 
        u8 do_ctrl;
        u8 gpio[2];
@@ -55,42 +57,6 @@ enum sensors {
        SENSOR_OV9630,
 };
 
-static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getexpo(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 const struct ctrl sd_ctrls[] = {
-       {
-           {
-               .id = V4L2_CID_EXPOSURE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Exposure",
-               .minimum = 0x0001,
-               .maximum = 0x0fff,
-               .step = 1,
-#define EXPO_DEF 0x0356
-               .default_value = EXPO_DEF,
-           },
-           .set = sd_setexpo,
-           .get = sd_getexpo,
-       },
-       {
-           {
-               .id = V4L2_CID_GAIN,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Gain",
-               .minimum = 0x01,
-               .maximum = 0xff,
-               .step = 1,
-#define GAIN_DEF 0x8d
-               .default_value = GAIN_DEF,
-           },
-           .set = sd_setgain,
-           .get = sd_getgain,
-       },
-};
-
 static struct v4l2_pix_format vga_mode[] = {
        {320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
                .bytesperline = 320,
@@ -791,7 +757,7 @@ static void lz24bp_ppl(struct sd *sd, u16 ppl)
        ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2);
 }
 
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int i, integclks, intstartclk, frameclks, min_frclk;
@@ -799,7 +765,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
        u16 cmd;
        u8 buf[15];
 
-       integclks = sd->expo;
+       integclks = expo;
        i = 0;
        cmd = SQ930_CTRL_SET_EXPOSURE;
 
@@ -818,7 +784,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
                buf[i++] = intstartclk;
                buf[i++] = frameclks >> 8;
                buf[i++] = frameclks;
-               buf[i++] = sd->gain;
+               buf[i++] = gain;
                break;
        default:                                /* cmos */
 /*     case SENSOR_MI0360: */
@@ -834,7 +800,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
                buf[i++] = 0x35;        /* reg = global gain */
                buf[i++] = 0x00;        /* val H */
                buf[i++] = sensor->i2c_dum;
-               buf[i++] = 0x80 + sd->gain / 2; /* val L */
+               buf[i++] = 0x80 + gain / 2; /* val L */
                buf[i++] = 0x00;
                buf[i++] = 0x00;
                buf[i++] = 0x00;
@@ -860,9 +826,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
 
        cam->bulk = 1;
 
-       sd->gain = GAIN_DEF;
-       sd->expo = EXPO_DEF;
-
        return 0;
 }
 
@@ -1089,7 +1052,8 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
                return;
        sd->do_ctrl = 0;
 
-       setexposure(gspca_dev);
+       setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure),
+                       v4l2_ctrl_g_ctrl(sd->gain));
 
        gspca_dev->cam.bulk_nurbs = 1;
        ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC);
@@ -1113,48 +1077,55 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
 }
 
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
        struct sd *sd = (struct sd *) gspca_dev;
 
-       sd->gain = val;
-       if (gspca_dev->streaming)
-               sd->do_ctrl = 1;
-       return 0;
-}
+       gspca_dev->usb_err = 0;
 
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       if (!gspca_dev->streaming)
+               return 0;
 
-       *val = sd->gain;
-       return 0;
+       switch (ctrl->id) {
+       case V4L2_CID_EXPOSURE:
+               setexposure(gspca_dev, ctrl->val, sd->gain->val);
+               break;
+       }
+       return gspca_dev->usb_err;
 }
-static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
 
-       sd->expo = val;
-       if (gspca_dev->streaming)
-               sd->do_ctrl = 1;
-       return 0;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
        struct sd *sd = (struct sd *) gspca_dev;
 
-       *val = sd->expo;
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 2);
+       sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_EXPOSURE, 1, 0xfff, 1, 0x356);
+       sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, 1, 255, 1, 0x8d);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       v4l2_ctrl_cluster(2, &sd->exposure);
        return 0;
 }
 
 /* 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,
+       .init_controls = sd_init_controls,
        .isoc_init = sd_isoc_init,
        .start  = sd_start,
        .stopN  = sd_stopN,
@@ -1194,6 +1165,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend    = gspca_suspend,
        .resume     = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 4ae7cc8f463a8c2cf5bc51bcf79d06f05a91534c..8c0982607f25c0ca8b14518be8ebee8b40b50278 100644 (file)
@@ -29,86 +29,14 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
 MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver");
 MODULE_LICENSE("GPL");
 
-/* controls */
-enum e_ctrl {
-       BRIGHTNESS,
-       CONTRAST,
-       COLORS,
-       LIGHTFREQ,
-       NCTRLS          /* number of controls */
-};
+#define QUALITY 50
 
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
-
-       struct gspca_ctrl ctrls[NCTRLS];
-
-       u8 quality;
-#define QUALITY_MIN 70
-#define QUALITY_MAX 95
-#define QUALITY_DEF 88
-
        u8 jpeg_hdr[JPEG_HDR_SZ];
 };
 
-/* V4L2 controls supported by the driver */
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setcontrast(struct gspca_dev *gspca_dev);
-static void setcolors(struct gspca_dev *gspca_dev);
-static void setlightfreq(struct gspca_dev *gspca_dev);
-
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[BRIGHTNESS] = {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-               .default_value = 127,
-           },
-           .set_control = setbrightness
-       },
-[CONTRAST] = {
-           {
-               .id      = V4L2_CID_CONTRAST,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Contrast",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-               .default_value = 127,
-           },
-           .set_control = setcontrast
-       },
-[COLORS] = {
-           {
-               .id      = V4L2_CID_SATURATION,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Color",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-               .default_value = 127,
-           },
-           .set_control = setcolors
-       },
-[LIGHTFREQ] = {
-           {
-               .id      = V4L2_CID_POWER_LINE_FREQUENCY,
-               .type    = V4L2_CTRL_TYPE_MENU,
-               .name    = "Light frequency filter",
-               .minimum = 1,
-               .maximum = 2,   /* 0: 0, 1: 50Hz, 2:60Hz */
-               .step    = 1,
-               .default_value = 1,
-           },
-           .set_control = setlightfreq
-       },
-};
-
 static const struct v4l2_pix_format vga_mode[] = {
        {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
                .bytesperline = 320,
@@ -255,41 +183,36 @@ static void set_par(struct gspca_dev *gspca_dev,
        snd_val(gspca_dev, 0x003f08, parval);
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        int parval;
 
        parval = 0x06000000             /* whiteness */
-               + (sd->ctrls[BRIGHTNESS].val << 16);
+               + (val << 16);
        set_par(gspca_dev, parval);
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        int parval;
 
        parval = 0x07000000             /* contrast */
-               + (sd->ctrls[CONTRAST].val << 16);
+               + (val << 16);
        set_par(gspca_dev, parval);
 }
 
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        int parval;
 
        parval = 0x08000000             /* saturation */
-               + (sd->ctrls[COLORS].val << 16);
+               + (val << 16);
        set_par(gspca_dev, parval);
 }
 
-static void setlightfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       set_par(gspca_dev, sd->ctrls[LIGHTFREQ].val == 1
+       set_par(gspca_dev, val == 1
                        ? 0x33640000            /* 50 Hz */
                        : 0x33780000);          /* 60 Hz */
 }
@@ -298,12 +221,8 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
 static int sd_config(struct gspca_dev *gspca_dev,
                        const struct usb_device_id *id)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
        gspca_dev->cam.cam_mode = vga_mode;
        gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
-       gspca_dev->cam.ctrls = sd->ctrls;
-       sd->quality = QUALITY_DEF;
        return 0;
 }
 
@@ -333,7 +252,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
        /* create the JPEG header */
        jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
                        0x22);          /* JPEG 411 */
-       jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+       jpeg_set_qual(sd->jpeg_hdr, QUALITY);
 
        /* work on alternate 1 */
        usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
@@ -365,14 +284,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, 0x0640, 0);
        reg_w(gspca_dev, 0x0650, 0);
        reg_w(gspca_dev, 0x0660, 0);
-       setbrightness(gspca_dev);               /* whiteness */
-       setcontrast(gspca_dev);                 /* contrast */
-       setcolors(gspca_dev);                   /* saturation */
        set_par(gspca_dev, 0x09800000);         /* Red ? */
        set_par(gspca_dev, 0x0a800000);         /* Green ? */
        set_par(gspca_dev, 0x0b800000);         /* Blue ? */
        set_par(gspca_dev, 0x0d030000);         /* Gamma ? */
-       setlightfreq(gspca_dev);
 
        /* start the video flow */
        set_par(gspca_dev, 0x01000000);
@@ -435,62 +350,70 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
-static int sd_querymenu(struct gspca_dev *gspca_dev,
-                       struct v4l2_querymenu *menu)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"};
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
 
-       switch (menu->id) {
-       case V4L2_CID_POWER_LINE_FREQUENCY:
-               if ((unsigned) menu->index >= ARRAY_SIZE(freq_nm))
-                       break;
-               strcpy((char *) menu->name, freq_nm[menu->index]);
-               return 0;
-       }
-       return -EINVAL;
-}
+       gspca_dev->usb_err = 0;
 
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
-                       struct v4l2_jpegcompression *jcomp)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       if (!gspca_dev->streaming)
+               return 0;
 
-       if (jcomp->quality < QUALITY_MIN)
-               sd->quality = QUALITY_MIN;
-       else if (jcomp->quality > QUALITY_MAX)
-               sd->quality = QUALITY_MAX;
-       else
-               sd->quality = jcomp->quality;
-       if (gspca_dev->streaming)
-               jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SATURATION:
+               setcolors(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               setlightfreq(gspca_dev, ctrl->val);
+               break;
+       }
        return gspca_dev->usb_err;
 }
 
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
-                       struct v4l2_jpegcompression *jcomp)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-       memset(jcomp, 0, sizeof *jcomp);
-       jcomp->quality = sd->quality;
-       jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
-                       | V4L2_JPEG_MARKER_DQT;
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 4);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 255, 1, 127);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 255, 1, 127);
+       v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+                       V4L2_CID_POWER_LINE_FREQUENCY,
+                       V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
+                       V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
        return 0;
 }
 
 /* sub-driver description */
 static const struct sd_desc sd_desc = {
        .name = MODULE_NAME,
-       .ctrls = sd_ctrls,
-       .nctrls = NCTRLS,
        .config = sd_config,
        .init = sd_init,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .pkt_scan = sd_pkt_scan,
-       .querymenu = sd_querymenu,
-       .get_jcomp = sd_get_jcomp,
-       .set_jcomp = sd_set_jcomp,
 };
 
 /* -- module initialisation -- */
@@ -516,6 +439,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 461ed645f309ff893549576b338ae895421ca480..67605272aaa8aac6a8c5a8fce2d381f069213a1a 100644 (file)
@@ -46,10 +46,6 @@ struct sd {
        u8 current_mode;
 };
 
-/* V4L2 controls supported by the driver */
-static const struct ctrl sd_ctrls[] = {
-};
-
 static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val,
                       int size)
 {
@@ -318,8 +314,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
 /* 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,
@@ -352,6 +346,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index c80f0c0c75b6e6905bde114d148470cf6d95ee5c..9ccfcb1c6479917ba6bde2d0a7e3401c1d632d56 100644 (file)
@@ -30,18 +30,13 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
 MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver");
 MODULE_LICENSE("GPL");
 
+#define QUALITY 85
+
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
 
-       s8 brightness;
-       u8 contrast;
-       u8 colors;
-       u8 autogain;
-       u8 quality;
-#define QUALITY_MIN 70
-#define QUALITY_MAX 95
-#define QUALITY_DEF 85
+       bool autogain;
 
        u8 bridge;
 #define BRIDGE_SPCA504 0
@@ -59,75 +54,6 @@ struct sd {
        u8 jpeg_hdr[JPEG_HDR_SZ];
 };
 
-/* 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 const struct ctrl sd_ctrls[] = {
-       {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = -128,
-               .maximum = 127,
-               .step    = 1,
-#define BRIGHTNESS_DEF 0
-               .default_value = BRIGHTNESS_DEF,
-           },
-           .set = sd_setbrightness,
-           .get = sd_getbrightness,
-       },
-       {
-           {
-               .id      = V4L2_CID_CONTRAST,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Contrast",
-               .minimum = 0,
-               .maximum = 0xff,
-               .step    = 1,
-#define CONTRAST_DEF 0x20
-               .default_value = CONTRAST_DEF,
-           },
-           .set = sd_setcontrast,
-           .get = sd_getcontrast,
-       },
-       {
-           {
-               .id      = V4L2_CID_SATURATION,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Color",
-               .minimum = 0,
-               .maximum = 0xff,
-               .step    = 1,
-#define COLOR_DEF 0x1a
-               .default_value = COLOR_DEF,
-           },
-           .set = sd_setcolors,
-           .get = sd_getcolors,
-       },
-       {
-           {
-               .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,
-       },
-};
-
 static const struct v4l2_pix_format vga_mode[] = {
        {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
                .bytesperline = 320,
@@ -597,31 +523,31 @@ static void spca504B_setQtable(struct gspca_dev *gspca_dev)
        spca504B_PollingDataReady(gspca_dev);
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u16 reg;
 
        reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7;
-       reg_w_riv(gspca_dev, 0x00, reg, sd->brightness);
+       reg_w_riv(gspca_dev, 0x00, reg, val);
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u16 reg;
 
        reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8;
-       reg_w_riv(gspca_dev, 0x00, reg, sd->contrast);
+       reg_w_riv(gspca_dev, 0x00, reg, val);
 }
 
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u16 reg;
 
        reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae;
-       reg_w_riv(gspca_dev, 0x00, reg, sd->colors);
+       reg_w_riv(gspca_dev, 0x00, reg, val);
 }
 
 static void init_ctl_reg(struct gspca_dev *gspca_dev)
@@ -629,10 +555,6 @@ static void init_ctl_reg(struct gspca_dev *gspca_dev)
        struct sd *sd = (struct sd *) gspca_dev;
        int pollreg = 1;
 
-       setbrightness(gspca_dev);
-       setcontrast(gspca_dev);
-       setcolors(gspca_dev);
-
        switch (sd->bridge) {
        case BRIDGE_SPCA504:
        case BRIDGE_SPCA504C:
@@ -704,11 +626,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
                cam->nmodes = ARRAY_SIZE(vga_mode2);
                break;
        }
-       sd->brightness = BRIGHTNESS_DEF;
-       sd->contrast = CONTRAST_DEF;
-       sd->colors = COLOR_DEF;
-       sd->autogain = AUTOGAIN_DEF;
-       sd->quality = QUALITY_DEF;
        return 0;
 }
 
@@ -807,7 +724,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
        /* create the JPEG header */
        jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
                        0x22);          /* JPEG 411 */
-       jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+       jpeg_set_qual(sd->jpeg_hdr, QUALITY);
 
        if (sd->bridge == BRIDGE_SPCA504B)
                spca504B_setQtable(gspca_dev);
@@ -1012,116 +929,69 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        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)
-               setbrightness(gspca_dev);
-       return gspca_dev->usb_err;
-}
-
-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)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
 
-       sd->contrast = val;
-       if (gspca_dev->streaming)
-               setcontrast(gspca_dev);
-       return gspca_dev->usb_err;
-}
+       gspca_dev->usb_err = 0;
 
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       if (!gspca_dev->streaming)
+               return 0;
 
-       *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);
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SATURATION:
+               setcolors(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               sd->autogain = ctrl->val;
+               break;
+       }
        return gspca_dev->usb_err;
 }
 
-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_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->autogain = val;
-       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_set_jcomp(struct gspca_dev *gspca_dev,
-                       struct v4l2_jpegcompression *jcomp)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (jcomp->quality < QUALITY_MIN)
-               sd->quality = QUALITY_MIN;
-       else if (jcomp->quality > QUALITY_MAX)
-               sd->quality = QUALITY_MAX;
-       else
-               sd->quality = jcomp->quality;
-       if (gspca_dev->streaming)
-               jpeg_set_qual(sd->jpeg_hdr, sd->quality);
-       return gspca_dev->usb_err;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
-                       struct v4l2_jpegcompression *jcomp)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       memset(jcomp, 0, sizeof *jcomp);
-       jcomp->quality = sd->quality;
-       jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
-                       | V4L2_JPEG_MARKER_DQT;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 4);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 255, 1, 0x20);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 255, 1, 0x1a);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
        return 0;
 }
 
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .pkt_scan = sd_pkt_scan,
-       .get_jcomp = sd_get_jcomp,
-       .set_jcomp = sd_set_jcomp,
 };
 
 /* -- module initialisation -- */
@@ -1208,6 +1078,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 9b9f85a8e60e750f9d2fba5cb4a0deab8e4f3b63..8bc6c3ceec2cf7c70df161990101f423d8952daf 100644 (file)
 #include <linux/slab.h>
 #include "gspca.h"
 
-#define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 0)
-
 MODULE_AUTHOR("Leandro Costantino <le_costantino@pixartargentina.com.ar>");
 MODULE_DESCRIPTION("GSPCA/T613 (JPEG Compliance) USB Camera Driver");
 MODULE_LICENSE("GPL");
 
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
-
-       u8 brightness;
-       u8 contrast;
-       u8 colors;
-       u8 autogain;
-       u8 gamma;
-       u8 sharpness;
-       u8 freq;
-       u8 red_gain;
-       u8 blue_gain;
-       u8 green_gain;
-       u8 awb; /* set default r/g/b and activate */
-       u8 mirror;
-       u8 effect;
+       struct v4l2_ctrl *freq;
+       struct { /* awb / color gains control cluster */
+               struct v4l2_ctrl *awb;
+               struct v4l2_ctrl *gain;
+               struct v4l2_ctrl *red_balance;
+               struct v4l2_ctrl *blue_balance;
+       };
 
        u8 sensor;
        u8 button_pressed;
@@ -67,245 +58,31 @@ enum sensors {
        SENSOR_LT168G,          /* must verify if this is the actual model */
 };
 
-/* 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_setlowlight(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgamma(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_setfreq(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getfreq(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_setblue_gain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getblue_gain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setred_gain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getred_gain(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_setmirror(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getmirror(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
-       {
-        {
-         .id = V4L2_CID_BRIGHTNESS,
-         .type = V4L2_CTRL_TYPE_INTEGER,
-         .name = "Brightness",
-         .minimum = 0,
-         .maximum = 14,
-         .step = 1,
-#define BRIGHTNESS_DEF 8
-         .default_value = BRIGHTNESS_DEF,
-         },
-        .set = sd_setbrightness,
-        .get = sd_getbrightness,
-        },
-       {
-        {
-         .id = V4L2_CID_CONTRAST,
-         .type = V4L2_CTRL_TYPE_INTEGER,
-         .name = "Contrast",
-         .minimum = 0,
-         .maximum = 0x0d,
-         .step = 1,
-#define CONTRAST_DEF 0x07
-         .default_value = CONTRAST_DEF,
-         },
-        .set = sd_setcontrast,
-        .get = sd_getcontrast,
-        },
-       {
-        {
-         .id = V4L2_CID_SATURATION,
-         .type = V4L2_CTRL_TYPE_INTEGER,
-         .name = "Color",
-         .minimum = 0,
-         .maximum = 0x0f,
-         .step = 1,
-#define COLORS_DEF 0x05
-         .default_value = COLORS_DEF,
-         },
-        .set = sd_setcolors,
-        .get = sd_getcolors,
-        },
-#define GAMMA_MAX 16
-#define GAMMA_DEF 10
-       {
-        {
-         .id = V4L2_CID_GAMMA, /* (gamma on win) */
-         .type = V4L2_CTRL_TYPE_INTEGER,
-         .name = "Gamma",
-         .minimum = 0,
-         .maximum = GAMMA_MAX - 1,
-         .step = 1,
-         .default_value = GAMMA_DEF,
-         },
-        .set = sd_setgamma,
-        .get = sd_getgamma,
-        },
-       {
-        {
-         .id = V4L2_CID_BACKLIGHT_COMPENSATION, /* Activa lowlight,
-                                * some apps dont bring up the
-                                * backligth_compensation control) */
-         .type = V4L2_CTRL_TYPE_INTEGER,
-         .name = "Low Light",
-         .minimum = 0,
-         .maximum = 1,
-         .step = 1,
-#define AUTOGAIN_DEF 0x01
-         .default_value = AUTOGAIN_DEF,
-         },
-        .set = sd_setlowlight,
-        .get = sd_getlowlight,
-        },
-       {
-        {
-         .id = V4L2_CID_HFLIP,
-         .type = V4L2_CTRL_TYPE_BOOLEAN,
-         .name = "Mirror Image",
-         .minimum = 0,
-         .maximum = 1,
-         .step = 1,
-#define MIRROR_DEF 0
-         .default_value = MIRROR_DEF,
-         },
-        .set = sd_setmirror,
-        .get = sd_getmirror
-       },
-       {
-        {
-         .id = V4L2_CID_POWER_LINE_FREQUENCY,
-         .type = V4L2_CTRL_TYPE_MENU,
-         .name = "Light Frequency Filter",
-         .minimum = 1,         /* 1 -> 0x50, 2->0x60 */
-         .maximum = 2,
-         .step = 1,
-#define FREQ_DEF 1
-         .default_value = FREQ_DEF,
-         },
-        .set = sd_setfreq,
-        .get = sd_getfreq},
-
-       {
-        {
-         .id =  V4L2_CID_AUTO_WHITE_BALANCE,
-         .type = V4L2_CTRL_TYPE_INTEGER,
-         .name = "Auto White Balance",
-         .minimum = 0,
-         .maximum = 1,
-         .step = 1,
-#define AWB_DEF 0
-         .default_value = AWB_DEF,
-         },
-        .set = sd_setawb,
-        .get = sd_getawb
-       },
-       {
-        {
-         .id = V4L2_CID_SHARPNESS,
-         .type = V4L2_CTRL_TYPE_INTEGER,
-         .name = "Sharpness",
-         .minimum = 0,
-         .maximum = 15,
-         .step = 1,
-#define SHARPNESS_DEF 0x06
-         .default_value = SHARPNESS_DEF,
-         },
-        .set = sd_setsharpness,
-        .get = sd_getsharpness,
-        },
-       {
-        {
-         .id = V4L2_CID_EFFECTS,
-         .type = V4L2_CTRL_TYPE_MENU,
-         .name = "Webcam Effects",
-         .minimum = 0,
-         .maximum = 4,
-         .step = 1,
-#define EFFECTS_DEF 0
-         .default_value = EFFECTS_DEF,
-         },
-        .set = sd_seteffect,
-        .get = sd_geteffect
-       },
-       {
-        {
-           .id      = V4L2_CID_BLUE_BALANCE,
-           .type    = V4L2_CTRL_TYPE_INTEGER,
-           .name    = "Blue Balance",
-           .minimum = 0x10,
-           .maximum = 0x40,
-           .step    = 1,
-#define BLUE_GAIN_DEF 0x20
-           .default_value = BLUE_GAIN_DEF,
-        },
-       .set = sd_setblue_gain,
-       .get = sd_getblue_gain,
-       },
-       {
-        {
-           .id      = V4L2_CID_RED_BALANCE,
-           .type    = V4L2_CTRL_TYPE_INTEGER,
-           .name    = "Red Balance",
-           .minimum = 0x10,
-           .maximum = 0x40,
-           .step    = 1,
-#define RED_GAIN_DEF 0x20
-           .default_value = RED_GAIN_DEF,
-        },
-       .set = sd_setred_gain,
-       .get = sd_getred_gain,
-       },
-       {
-        {
-           .id      = V4L2_CID_GAIN,
-           .type    = V4L2_CTRL_TYPE_INTEGER,
-           .name    = "Gain",
-           .minimum = 0x10,
-           .maximum = 0x40,
-           .step    = 1,
-#define GAIN_DEF  0x20
-           .default_value = GAIN_DEF,
-        },
-       .set = sd_setgain,
-       .get = sd_getgain,
-       },
-};
-
 static const struct v4l2_pix_format vga_mode_t16[] = {
        {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
                .bytesperline = 160,
                .sizeimage = 160 * 120 * 4 / 8 + 590,
                .colorspace = V4L2_COLORSPACE_JPEG,
                .priv = 4},
+#if 0 /* HDG: broken with my test cam, so lets disable it */
        {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
                .bytesperline = 176,
                .sizeimage = 176 * 144 * 3 / 8 + 590,
                .colorspace = V4L2_COLORSPACE_JPEG,
                .priv = 3},
+#endif
        {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
                .bytesperline = 320,
                .sizeimage = 320 * 240 * 3 / 8 + 590,
                .colorspace = V4L2_COLORSPACE_JPEG,
                .priv = 2},
+#if 0 /* HDG: broken with my test cam, so lets disable it */
        {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
                .bytesperline = 352,
                .sizeimage = 352 * 288 * 3 / 8 + 590,
                .colorspace = V4L2_COLORSPACE_JPEG,
                .priv = 1},
+#endif
        {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
                .bytesperline = 640,
                .sizeimage = 640 * 480 * 3 / 8 + 590,
@@ -454,17 +231,6 @@ static const struct additional_sensor_data sensor_data[] = {
 };
 
 #define MAX_EFFECTS 7
-/* easily done by soft, this table could be removed,
- * i keep it here just in case */
-static char *effects_control[MAX_EFFECTS] = {
-       "Normal",
-       "Emboss",               /* disabled */
-       "Monochrome",
-       "Sepia",
-       "Sketch",
-       "Sun Effect",           /* disabled */
-       "Negative",
-};
 static const u8 effects_table[MAX_EFFECTS][6] = {
        {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00},   /* Normal */
        {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04},   /* Repujar */
@@ -475,7 +241,8 @@ static const u8 effects_table[MAX_EFFECTS][6] = {
        {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x40},   /* Negative */
 };
 
-static const u8 gamma_table[GAMMA_MAX][17] = {
+#define GAMMA_MAX (15)
+static const u8 gamma_table[GAMMA_MAX+1][17] = {
 /* gamma table from cam1690.ini */
        {0x00, 0x00, 0x01, 0x04, 0x08, 0x0e, 0x16, 0x21,        /* 0 */
         0x2e, 0x3d, 0x50, 0x65, 0x7d, 0x99, 0xb8, 0xdb,
@@ -683,38 +450,18 @@ static void om6802_sensor_init(struct gspca_dev *gspca_dev)
 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;
+       struct cam *cam  = &gspca_dev->cam;
 
        cam->cam_mode = vga_mode_t16;
        cam->nmodes = ARRAY_SIZE(vga_mode_t16);
 
-       sd->brightness = BRIGHTNESS_DEF;
-       sd->contrast = CONTRAST_DEF;
-       sd->colors = COLORS_DEF;
-       sd->gamma = GAMMA_DEF;
-       sd->autogain = AUTOGAIN_DEF;
-       sd->mirror = MIRROR_DEF;
-       sd->freq = FREQ_DEF;
-       sd->awb = AWB_DEF;
-       sd->sharpness = SHARPNESS_DEF;
-       sd->effect = EFFECTS_DEF;
-       sd->red_gain = RED_GAIN_DEF;
-       sd->blue_gain = BLUE_GAIN_DEF;
-       sd->green_gain = GAIN_DEF * 3 - RED_GAIN_DEF - BLUE_GAIN_DEF;
-
        return 0;
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       unsigned int brightness;
        u8 set6[4] = { 0x8f, 0x24, 0xc3, 0x00 };
 
-       brightness = sd->brightness;
        if (brightness < 7) {
                set6[1] = 0x26;
                set6[3] = 0x70 - brightness * 0x10;
@@ -725,10 +472,8 @@ static void setbrightness(struct gspca_dev *gspca_dev)
        reg_w_buf(gspca_dev, set6, sizeof set6);
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 contrast)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       unsigned int contrast = sd->contrast;
        u16 reg_to_write;
 
        if (contrast < 7)
@@ -739,89 +484,62 @@ static void setcontrast(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, reg_to_write);
 }
 
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u16 reg_to_write;
 
-       reg_to_write = 0x80bb + sd->colors * 0x100;     /* was 0xc0 */
+       reg_to_write = 0x80bb + val * 0x100;    /* was 0xc0 */
        reg_w(gspca_dev, reg_to_write);
 }
 
-static void setgamma(struct gspca_dev *gspca_dev)
+static void setgamma(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
        PDEBUG(D_CONF, "Gamma: %d", sd->gamma);
        reg_w_ixbuf(gspca_dev, 0x90,
-               gamma_table[sd->gamma], sizeof gamma_table[0]);
+               gamma_table[val], sizeof gamma_table[0]);
 }
 
-static void setRGB(struct gspca_dev *gspca_dev)
+static void setawb_n_RGB(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u8 all_gain_reg[6] =
-               {0x87, 0x00, 0x88, 0x00, 0x89, 0x00};
+       u8 all_gain_reg[8] = {
+               0x87, 0x00, 0x88, 0x00, 0x89, 0x00, 0x80, 0x00 };
+       s32 red_gain, blue_gain, green_gain;
+
+       green_gain = sd->gain->val;
+
+       red_gain = green_gain + sd->red_balance->val;
+       if (red_gain > 0x40)
+               red_gain = 0x40;
+       else if (red_gain < 0x10)
+               red_gain = 0x10;
+
+       blue_gain = green_gain + sd->blue_balance->val;
+       if (blue_gain > 0x40)
+               blue_gain = 0x40;
+       else if (blue_gain < 0x10)
+               blue_gain = 0x10;
+
+       all_gain_reg[1] = red_gain;
+       all_gain_reg[3] = blue_gain;
+       all_gain_reg[5] = green_gain;
+       all_gain_reg[7] = sensor_data[sd->sensor].reg80;
+       if (!sd->awb->val)
+               all_gain_reg[7] &= ~0x04; /* AWB off */
 
-       all_gain_reg[1] = sd->red_gain;
-       all_gain_reg[3] = sd->blue_gain;
-       all_gain_reg[5] = sd->green_gain;
        reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg);
 }
 
-/* Generic fnc for r/b balance, exposure and awb */
-static void setawb(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       u16 reg80;
-
-       reg80 = (sensor_data[sd->sensor].reg80 << 8) | 0x80;
-
-       /* on awb leave defaults values */
-       if (!sd->awb) {
-               /* shoud we wait here.. */
-               /* update and reset RGB gains with webcam values */
-               sd->red_gain = reg_r(gspca_dev, 0x0087);
-               sd->blue_gain = reg_r(gspca_dev, 0x0088);
-               sd->green_gain = reg_r(gspca_dev, 0x0089);
-               reg80 &= ~0x0400;               /* AWB off */
-       }
-       reg_w(gspca_dev, reg80);
-       reg_w(gspca_dev, reg80);
-}
-
-static void init_gains(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       u16 reg80;
-       u8 all_gain_reg[8] =
-               {0x87, 0x00, 0x88, 0x00, 0x89, 0x00, 0x80, 0x00};
-
-       all_gain_reg[1] = sd->red_gain;
-       all_gain_reg[3] = sd->blue_gain;
-       all_gain_reg[5] = sd->green_gain;
-       reg80 = sensor_data[sd->sensor].reg80;
-       if (!sd->awb)
-               reg80 &= ~0x04;
-       all_gain_reg[7] = reg80;
-       reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg);
-
-       reg_w(gspca_dev, (sd->red_gain  << 8) + 0x87);
-       reg_w(gspca_dev, (sd->blue_gain << 8) + 0x88);
-       reg_w(gspca_dev, (sd->green_gain  << 8) + 0x89);
-}
-
-static void setsharpness(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
        u16 reg_to_write;
 
-       reg_to_write = 0x0aa6 + 0x1000 * sd->sharpness;
+       reg_to_write = 0x0aa6 + 0x1000 * val;
 
        reg_w(gspca_dev, reg_to_write);
 }
 
-static void setfreq(struct gspca_dev *gspca_dev)
+static void setfreq(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u8 reg66;
@@ -829,7 +547,7 @@ static void setfreq(struct gspca_dev *gspca_dev)
 
        switch (sd->sensor) {
        case SENSOR_LT168G:
-               if (sd->freq != 0)
+               if (val != 0)
                        freq[3] = 0xa8;
                reg66 = 0x41;
                break;
@@ -840,7 +558,7 @@ static void setfreq(struct gspca_dev *gspca_dev)
                reg66 = 0x40;
                break;
        }
-       switch (sd->freq) {
+       switch (val) {
        case 0:                         /* no flicker */
                freq[3] = 0xf0;
                break;
@@ -941,14 +659,9 @@ static int sd_init(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80);
        reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80);
        reg_w(gspca_dev, (sensor->reg8e << 8) + 0x8e);
-
-       setbrightness(gspca_dev);
-       setcontrast(gspca_dev);
-       setgamma(gspca_dev);
-       setcolors(gspca_dev);
-       setsharpness(gspca_dev);
-       init_gains(gspca_dev);
-       setfreq(gspca_dev);
+       reg_w(gspca_dev, (0x20 << 8) + 0x87);
+       reg_w(gspca_dev, (0x20 << 8) + 0x88);
+       reg_w(gspca_dev, (0x20 << 8) + 0x89);
 
        reg_w_buf(gspca_dev, sensor->data5, sizeof sensor->data5);
        reg_w_buf(gspca_dev, sensor->nset8, sizeof sensor->nset8);
@@ -968,31 +681,44 @@ static int sd_init(struct gspca_dev *gspca_dev)
        return 0;
 }
 
-static void setmirror(struct gspca_dev *gspca_dev)
+static void setmirror(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 hflipcmd[8] =
                {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09};
 
-       if (sd->mirror)
+       if (val)
                hflipcmd[3] = 0x01;
 
        reg_w_buf(gspca_dev, hflipcmd, sizeof hflipcmd);
 }
 
-static void seteffect(struct gspca_dev *gspca_dev)
+static void seteffect(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       int idx = 0;
 
-       reg_w_buf(gspca_dev, effects_table[sd->effect],
-                               sizeof effects_table[0]);
-       if (sd->effect == 1 || sd->effect == 5) {
-               PDEBUG(D_CONF,
-                      "This effect have been disabled for webcam \"safety\"");
-               return;
+       switch (val) {
+       case V4L2_COLORFX_NONE:
+               break;
+       case V4L2_COLORFX_BW:
+               idx = 2;
+               break;
+       case V4L2_COLORFX_SEPIA:
+               idx = 3;
+               break;
+       case V4L2_COLORFX_SKETCH:
+               idx = 4;
+               break;
+       case V4L2_COLORFX_NEGATIVE:
+               idx = 6;
+               break;
+       default:
+               break;
        }
 
-       if (sd->effect == 1 || sd->effect == 4)
+       reg_w_buf(gspca_dev, effects_table[idx],
+                               sizeof effects_table[0]);
+
+       if (val == V4L2_COLORFX_SKETCH)
                reg_w(gspca_dev, 0x4aa6);
        else
                reg_w(gspca_dev, 0xfaa6);
@@ -1070,7 +796,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
                break;
        }
        sensor = &sensor_data[sd->sensor];
-       setfreq(gspca_dev);
+       setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq));
        reg_r(gspca_dev, 0x0012);
        reg_w_buf(gspca_dev, t2, sizeof t2);
        reg_w_ixbuf(gspca_dev, 0xb3, t3, sizeof t3);
@@ -1142,296 +868,157 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        gspca_frame_add(gspca_dev, pkt_type, data, len);
 }
 
-static int sd_setblue_gain(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->blue_gain = val;
-       if (gspca_dev->streaming)
-               reg_w(gspca_dev, (val << 8) + 0x88);
-       return 0;
-}
-
-static int sd_getblue_gain(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->blue_gain;
-       return 0;
-}
-
-static int sd_setred_gain(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->red_gain = val;
-       if (gspca_dev->streaming)
-               reg_w(gspca_dev, (val << 8) + 0x87);
-
-       return 0;
-}
-
-static int sd_getred_gain(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->red_gain;
-       return 0;
-}
-
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       u16 psg, nsg;
-
-       psg = sd->red_gain + sd->blue_gain + sd->green_gain;
-       nsg = val * 3;
-       sd->red_gain = sd->red_gain * nsg / psg;
-       if (sd->red_gain > 0x40)
-               sd->red_gain = 0x40;
-       else if (sd->red_gain < 0x10)
-               sd->red_gain = 0x10;
-       sd->blue_gain = sd->blue_gain * nsg / psg;
-       if (sd->blue_gain > 0x40)
-               sd->blue_gain = 0x40;
-       else if (sd->blue_gain < 0x10)
-               sd->blue_gain = 0x10;
-       sd->green_gain = sd->green_gain * nsg / psg;
-       if (sd->green_gain > 0x40)
-               sd->green_gain = 0x40;
-       else if (sd->green_gain < 0x10)
-               sd->green_gain = 0x10;
-
-       if (gspca_dev->streaming)
-               setRGB(gspca_dev);
-       return 0;
-}
-
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = (sd->red_gain + sd->blue_gain + sd->green_gain) / 3;
-       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)
-               setbrightness(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 *val;
-}
-
-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 *val;
-}
-
-static int sd_setmirror(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->mirror = val;
-       if (gspca_dev->streaming)
-               setmirror(gspca_dev);
-       return 0;
-}
-
-static int sd_getmirror(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->mirror;
-       return *val;
-}
-
-static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->effect = val;
-       if (gspca_dev->streaming)
-               seteffect(gspca_dev);
-       return 0;
-}
-
-static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->effect;
-       return *val;
-}
-
-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)
-               setcontrast(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 *val;
-}
-
-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_setgamma(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->gamma = val;
-       if (gspca_dev->streaming)
-               setgamma(gspca_dev);
-       return 0;
-}
-
-static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->gamma;
-       return 0;
-}
-
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->freq = val;
-       if (gspca_dev->streaming)
-               setfreq(gspca_dev);
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
+       s32 red_gain, blue_gain, green_gain;
+
+       gspca_dev->usb_err = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               red_gain = reg_r(gspca_dev, 0x0087);
+               if (red_gain > 0x40)
+                       red_gain = 0x40;
+               else if (red_gain < 0x10)
+                       red_gain = 0x10;
+
+               blue_gain = reg_r(gspca_dev, 0x0088);
+               if (blue_gain > 0x40)
+                       blue_gain = 0x40;
+               else if (blue_gain < 0x10)
+                       blue_gain = 0x10;
+
+               green_gain = reg_r(gspca_dev, 0x0089);
+               if (green_gain > 0x40)
+                       green_gain = 0x40;
+               else if (green_gain < 0x10)
+                       green_gain = 0x10;
+
+               sd->gain->val = green_gain;
+               sd->red_balance->val = red_gain - green_gain;
+               sd->blue_balance->val = blue_gain - green_gain;
+               break;
+       }
        return 0;
 }
 
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
 
-       *val = sd->freq;
-       return 0;
-}
+       gspca_dev->usb_err = 0;
 
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       if (!gspca_dev->streaming)
+               return 0;
 
-       sd->sharpness = val;
-       if (gspca_dev->streaming)
-               setsharpness(gspca_dev);
-       return 0;
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SATURATION:
+               setcolors(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_GAMMA:
+               setgamma(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_HFLIP:
+               setmirror(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SHARPNESS:
+               setsharpness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               setfreq(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_BACKLIGHT_COMPENSATION:
+               reg_w(gspca_dev, ctrl->val ? 0xf48e : 0xb48e);
+               break;
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               setawb_n_RGB(gspca_dev);
+               break;
+       case V4L2_CID_COLORFX:
+               seteffect(gspca_dev, ctrl->val);
+               break;
+       }
+       return gspca_dev->usb_err;
 }
 
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->sharpness;
-       return 0;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .g_volatile_ctrl = sd_g_volatile_ctrl,
+       .s_ctrl = sd_s_ctrl,
+};
 
-/* Low Light set  here......*/
-static int sd_setlowlight(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 12);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 14, 1, 8);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 0x0d, 1, 7);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 0xf, 1, 5);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAMMA, 0, GAMMA_MAX, 1, 10);
+       /* Activate lowlight, some apps dont bring up the
+          backlight_compensation control) */
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BACKLIGHT_COMPENSATION, 0, 1, 1, 1);
+       if (sd->sensor == SENSOR_TAS5130A)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                               V4L2_CID_HFLIP, 0, 1, 1, 0);
+       sd->awb = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+       sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, 0x10, 0x40, 1, 0x20);
+       sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BLUE_BALANCE, -0x30, 0x30, 1, 0);
+       sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_RED_BALANCE, -0x30, 0x30, 1, 0);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SHARPNESS, 0, 15, 1, 6);
+       v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+                       V4L2_CID_COLORFX, V4L2_COLORFX_SKETCH,
+                       ~((1 << V4L2_COLORFX_NONE) |
+                         (1 << V4L2_COLORFX_BW) |
+                         (1 << V4L2_COLORFX_SEPIA) |
+                         (1 << V4L2_COLORFX_SKETCH) |
+                         (1 << V4L2_COLORFX_NEGATIVE)),
+                       V4L2_COLORFX_NONE);
+       sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+                       V4L2_CID_POWER_LINE_FREQUENCY,
+                       V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
+                       V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
 
-       sd->autogain = val;
-       if (val != 0)
-               reg_w(gspca_dev, 0xf48e);
-       else
-               reg_w(gspca_dev, 0xb48e);
-       return 0;
-}
+       v4l2_ctrl_auto_cluster(4, &sd->awb, 0, true);
 
-static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->autogain;
        return 0;
 }
 
-static int sd_querymenu(struct gspca_dev *gspca_dev,
-                       struct v4l2_querymenu *menu)
-{
-       static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"};
-
-       switch (menu->id) {
-       case V4L2_CID_POWER_LINE_FREQUENCY:
-               if ((unsigned) menu->index >= ARRAY_SIZE(freq_nm))
-                       break;
-               strcpy((char *) menu->name, freq_nm[menu->index]);
-               return 0;
-       case V4L2_CID_EFFECTS:
-               if ((unsigned) menu->index < ARRAY_SIZE(effects_control)) {
-                       strlcpy((char *) menu->name,
-                               effects_control[menu->index],
-                               sizeof menu->name);
-                       return 0;
-               }
-               break;
-       }
-       return -EINVAL;
-}
-
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .pkt_scan = sd_pkt_scan,
-       .querymenu = sd_querymenu,
 #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
        .other_input = 1,
 #endif
@@ -1460,6 +1047,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index c6326d177a3df6952a0cfb30db042638963f496c..a6055246cb9d720c13dbd880c167a6aec94ba79c 100644 (file)
@@ -120,24 +120,13 @@ static const u8 jpeg_head[] = {
 #define JPEG_HDR_SZ 521
 };
 
-enum e_ctrl {
-       EXPOSURE,
-       QUALITY,
-       SHARPNESS,
-       RGAIN,
-       GAIN,
-       BGAIN,
-       GAMMA,
-       AUTOGAIN,
-       NCTRLS          /* number of controls */
-};
-
-#define AUTOGAIN_DEF 1
-
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
-
-       struct gspca_ctrl ctrls[NCTRLS];
+       struct v4l2_ctrl *jpegqual;
+       struct v4l2_ctrl *sharpness;
+       struct v4l2_ctrl *gamma;
+       struct v4l2_ctrl *blue;
+       struct v4l2_ctrl *red;
 
        u8 framerate;
        u8 quality;             /* webcam current JPEG quality (0..16) */
@@ -1415,32 +1404,33 @@ static void soi763a_6810_init(struct gspca_dev *gspca_dev)
 }
 
 /* set the gain and exposure */
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain,
+                                                       s32 blue, s32 red)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
        if (sd->sensor == SENSOR_CX0342) {
-               int expo;
-
-               expo = (sd->ctrls[EXPOSURE].val << 2) - 1;
+               expo = (expo << 2) - 1;
                i2c_w(gspca_dev, CX0342_EXPO_LINE_L, expo);
                i2c_w(gspca_dev, CX0342_EXPO_LINE_H, expo >> 8);
                if (sd->bridge == BRIDGE_TP6800)
                        i2c_w(gspca_dev, CX0342_RAW_GBGAIN_H,
-                                               sd->ctrls[GAIN].val >> 8);
-               i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, sd->ctrls[GAIN].val);
+                                               gain >> 8);
+               i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, gain);
                if (sd->bridge == BRIDGE_TP6800)
                        i2c_w(gspca_dev, CX0342_RAW_GRGAIN_H,
-                                               sd->ctrls[GAIN].val >> 8);
-               i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, sd->ctrls[GAIN].val);
-               if (sd->bridge == BRIDGE_TP6800)
-                       i2c_w(gspca_dev, CX0342_RAW_BGAIN_H,
-                                               sd->ctrls[BGAIN].val >> 8);
-               i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, sd->ctrls[BGAIN].val);
-               if (sd->bridge == BRIDGE_TP6800)
-                       i2c_w(gspca_dev, CX0342_RAW_RGAIN_H,
-                                               sd->ctrls[RGAIN].val >> 8);
-               i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, sd->ctrls[RGAIN].val);
+                                       gain >> 8);
+               i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, gain);
+               if (sd->sensor == SENSOR_CX0342) {
+                       if (sd->bridge == BRIDGE_TP6800)
+                               i2c_w(gspca_dev, CX0342_RAW_BGAIN_H,
+                                               blue >> 8);
+                       i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, blue);
+                       if (sd->bridge == BRIDGE_TP6800)
+                               i2c_w(gspca_dev, CX0342_RAW_RGAIN_H,
+                                               red >> 8);
+                       i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, red);
+               }
                i2c_w(gspca_dev, CX0342_SYS_CTRL_0,
                                sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81);
                return;
@@ -1448,10 +1438,10 @@ static void setexposure(struct gspca_dev *gspca_dev)
 
        /* soi763a */
        i2c_w(gspca_dev, 0x10,          /* AEC_H (exposure time) */
-                        sd->ctrls[EXPOSURE].val);
+                        expo);
 /*     i2c_w(gspca_dev, 0x76, 0x02);    * AEC_L ([1:0] */
        i2c_w(gspca_dev, 0x00,          /* gain */
-                        sd->ctrls[GAIN].val);
+                        gain);
 }
 
 /* set the JPEG quantization tables */
@@ -1472,12 +1462,10 @@ static void set_dqt(struct gspca_dev *gspca_dev, u8 q)
 }
 
 /* set the JPEG compression quality factor */
-static void setquality(struct gspca_dev *gspca_dev)
+static void setquality(struct gspca_dev *gspca_dev, s32 q)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u16 q;
 
-       q = sd->ctrls[QUALITY].val;
        if (q != 16)
                q = 15 - q;
 
@@ -1508,10 +1496,9 @@ static const u8 color_gain[NSENSORS][18] = {
         0xd5, 0x00, 0x46, 0x03, 0xdc, 0x03},   /* V R/G/B */
 };
 
-static void setgamma(struct gspca_dev *gspca_dev)
+static void setgamma(struct gspca_dev *gspca_dev, s32 gamma)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int gamma;
 #define NGAMMA 6
        static const u8 gamma_tb[NGAMMA][3][1024] = {
            {                           /* gamma 0 - from tp6800 + soi763a */
@@ -3836,7 +3823,6 @@ static void setgamma(struct gspca_dev *gspca_dev)
        if (sd->bridge == BRIDGE_TP6810)
                reg_w(gspca_dev, 0x02, 0x28);
 /*     msleep(50); */
-       gamma = sd->ctrls[GAMMA].val;
        bulk_w(gspca_dev, 0x00, gamma_tb[gamma][0], 1024);
        bulk_w(gspca_dev, 0x01, gamma_tb[gamma][1], 1024);
        bulk_w(gspca_dev, 0x02, gamma_tb[gamma][2], 1024);
@@ -3864,43 +3850,35 @@ static void setgamma(struct gspca_dev *gspca_dev)
 /*     msleep(50); */
 }
 
-static void setsharpness(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u8 val;
 
        if (sd->bridge == BRIDGE_TP6800) {
-               val = sd->ctrls[SHARPNESS].val
-                               | 0x08;         /* grid compensation enable */
+               val |= 0x08;            /* grid compensation enable */
                if (gspca_dev->width == 640)
                        reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
                else
                        val |= 0x04;            /* scaling down enable */
                reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, val);
        } else {
-               val = (sd->ctrls[SHARPNESS].val << 5) | 0x08;
+               val = (val << 5) | 0x08;
                reg_w(gspca_dev, 0x59, val);
        }
 }
 
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
-               return;
-       if (sd->ctrls[AUTOGAIN].val) {
-               sd->ag_cnt = AG_CNT_START;
-               gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
-       } else {
-               sd->ag_cnt = -1;
-               gspca_dev->ctrl_inac &= ~((1 << EXPOSURE) | (1 << GAIN));
-       }
+       sd->ag_cnt = val ? AG_CNT_START : -1;
 }
 
 /* set the resolution for sensor cx0342 */
 static void set_resolution(struct gspca_dev *gspca_dev)
 {
+       struct sd *sd = (struct sd *) gspca_dev;
+
        reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
        if (gspca_dev->width == 320) {
                reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x06);
@@ -3926,8 +3904,9 @@ static void set_resolution(struct gspca_dev *gspca_dev)
        i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
        bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
                                ARRAY_SIZE(color_gain[0]));
-       setgamma(gspca_dev);
-       setquality(gspca_dev);
+       setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
+       if (sd->sensor == SENSOR_SOI763A)
+               setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
 }
 
 /* convert the frame rate to a tp68x0 value */
@@ -3963,7 +3942,7 @@ static int get_fr_idx(struct gspca_dev *gspca_dev)
        return i;
 }
 
-static void setframerate(struct gspca_dev *gspca_dev)
+static void setframerate(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u8 fr_idx;
@@ -3974,7 +3953,7 @@ static void setframerate(struct gspca_dev *gspca_dev)
                reg_r(gspca_dev, 0x7b);
                reg_w(gspca_dev, 0x7b,
                        sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90);
-               if (sd->ctrls[EXPOSURE].val >= 128)
+               if (val >= 128)
                        fr_idx = 0xf0;          /* lower frame rate */
        }
 
@@ -3984,43 +3963,43 @@ static void setframerate(struct gspca_dev *gspca_dev)
                i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
 }
 
-static void setrgain(struct gspca_dev *gspca_dev)
+static void setrgain(struct gspca_dev *gspca_dev, s32 rgain)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       int rgain;
-
-       rgain = sd->ctrls[RGAIN].val;
        i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, rgain >> 8);
        i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, rgain);
        i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
 }
 
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_setgain(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
+       s32 val = gspca_dev->gain->val;
 
        if (sd->sensor == SENSOR_CX0342) {
-               sd->ctrls[BGAIN].val = sd->ctrls[BGAIN].val
-                                       * val / sd->ctrls[GAIN].val;
-               if (sd->ctrls[BGAIN].val > 4095)
-                       sd->ctrls[BGAIN].val = 4095;
-               sd->ctrls[RGAIN].val = sd->ctrls[RGAIN].val
-                                       * val / sd->ctrls[GAIN].val;
-               if (sd->ctrls[RGAIN].val > 4095)
-                       sd->ctrls[RGAIN].val = 4095;
+               s32 old = gspca_dev->gain->cur.val ?
+                                       gspca_dev->gain->cur.val : 1;
+
+               sd->blue->val = sd->blue->val * val / old;
+               if (sd->blue->val > 4095)
+                       sd->blue->val = 4095;
+               sd->red->val = sd->red->val * val / old;
+               if (sd->red->val > 4095)
+                       sd->red->val = 4095;
+       }
+       if (gspca_dev->streaming) {
+               if (sd->sensor == SENSOR_CX0342)
+                       setexposure(gspca_dev, gspca_dev->exposure->val,
+                                       gspca_dev->gain->val,
+                                       sd->blue->val, sd->red->val);
+               else
+                       setexposure(gspca_dev, gspca_dev->exposure->val,
+                                       gspca_dev->gain->val, 0, 0);
        }
-       sd->ctrls[GAIN].val = val;
-       if (gspca_dev->streaming)
-               setexposure(gspca_dev);
        return gspca_dev->usb_err;
 }
 
-static void setbgain(struct gspca_dev *gspca_dev)
+static void setbgain(struct gspca_dev *gspca_dev, s32 bgain)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       int bgain;
-
-       bgain = sd->ctrls[BGAIN].val;
        i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, bgain >> 8);
        i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, bgain);
        i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
@@ -4040,7 +4019,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
                        framerates : framerates_6810;
 
        sd->framerate = 30;             /* default: 30 fps */
-       gspca_dev->cam.ctrls = sd->ctrls;
        return 0;
 }
 
@@ -4108,32 +4086,16 @@ static int sd_init(struct gspca_dev *gspca_dev)
        }
        if (sd->sensor == SENSOR_SOI763A) {
                pr_info("Sensor soi763a\n");
-               sd->ctrls[GAMMA].def = sd->bridge == BRIDGE_TP6800 ? 0 : 1;
-               sd->ctrls[GAIN].max = 15;
-               sd->ctrls[GAIN].def = 3;
-               gspca_dev->ctrl_dis = (1 << RGAIN) | (1 << BGAIN);
                if (sd->bridge == BRIDGE_TP6810) {
                        soi763a_6810_init(gspca_dev);
-#if AUTOGAIN_DEF
-                       gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
-#endif
-               } else {
-                       gspca_dev->ctrl_dis |= (1 << AUTOGAIN);
                }
        } else {
                pr_info("Sensor cx0342\n");
                if (sd->bridge == BRIDGE_TP6810) {
                        cx0342_6810_init(gspca_dev);
-#if AUTOGAIN_DEF
-                       gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
-#endif
-               } else {
-                       gspca_dev->ctrl_dis |= (1 << AUTOGAIN);
                }
        }
 
-       if (sd->bridge == BRIDGE_TP6810)
-               sd->ctrls[QUALITY].def = 0;     /* auto quality */
        set_dqt(gspca_dev, 0);
        return 0;
 }
@@ -4207,8 +4169,9 @@ static void set_led(struct gspca_dev *gspca_dev, int on)
 
 static void cx0342_6800_start(struct gspca_dev *gspca_dev)
 {
+       struct sd *sd = (struct sd *) gspca_dev;
        static const struct cmd reg_init[] = {
-/*fixme: is this usefull?*/
+               /* fixme: is this useful? */
                {TP6800_R17_GPIO_IO, 0x9f},
                {TP6800_R16_GPIO_PD, 0x40},
                {TP6800_R10_SIF_TYPE, 0x00},    /* i2c 8 bits */
@@ -4279,13 +4242,21 @@ static void cx0342_6800_start(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
        i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
        i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
-       setexposure(gspca_dev);
+       if (sd->sensor == SENSOR_CX0342)
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain),
+                       v4l2_ctrl_g_ctrl(sd->blue),
+                       v4l2_ctrl_g_ctrl(sd->red));
+       else
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
        set_led(gspca_dev, 1);
        set_resolution(gspca_dev);
 }
 
 static void cx0342_6810_start(struct gspca_dev *gspca_dev)
 {
+       struct sd *sd = (struct sd *) gspca_dev;
        static const struct cmd sensor_init_2[] = {
                {CX0342_EXPO_LINE_L, 0x6f},
                {CX0342_EXPO_LINE_H, 0x02},
@@ -4366,10 +4337,10 @@ static void cx0342_6810_start(struct gspca_dev *gspca_dev)
                reg_w(gspca_dev, 0x07, 0x85);
                reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01);      /* qvga */
        }
-       setgamma(gspca_dev);
+       setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
        reg_w_buf(gspca_dev, tp6810_bridge_start,
                        ARRAY_SIZE(tp6810_bridge_start));
-       setsharpness(gspca_dev);
+       setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
        bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
                                ARRAY_SIZE(color_gain[0]));
        reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
@@ -4380,11 +4351,12 @@ static void cx0342_6810_start(struct gspca_dev *gspca_dev)
        i2c_w_buf(gspca_dev, sensor_init_5, ARRAY_SIZE(sensor_init_5));
 
        set_led(gspca_dev, 1);
-/*     setquality(gspca_dev); */
+/*     setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); */
 }
 
 static void soi763a_6800_start(struct gspca_dev *gspca_dev)
 {
+       struct sd *sd = (struct sd *) gspca_dev;
        static const struct cmd reg_init[] = {
                {TP6800_R79_QUALITY, 0x04},
                {TP6800_R79_QUALITY, 0x01},
@@ -4484,19 +4456,28 @@ static void soi763a_6800_start(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
        reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
 
-       setsharpness(gspca_dev);
+       setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
 
        bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
                                ARRAY_SIZE(color_gain[0]));
 
        set_led(gspca_dev, 1);
-       setexposure(gspca_dev);
-       setquality(gspca_dev);
-       setgamma(gspca_dev);
+       if (sd->sensor == SENSOR_CX0342)
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain),
+                       v4l2_ctrl_g_ctrl(sd->blue),
+                       v4l2_ctrl_g_ctrl(sd->red));
+       else
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+       if (sd->sensor == SENSOR_SOI763A)
+               setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
+       setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
 }
 
 static void soi763a_6810_start(struct gspca_dev *gspca_dev)
 {
+       struct sd *sd = (struct sd *) gspca_dev;
        static const struct cmd bridge_init_2[] = {
                {TP6800_R7A_BLK_THRLD, 0x00},
                {TP6800_R79_QUALITY, 0x04},
@@ -4520,7 +4501,14 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, 0x22, gspca_dev->alt);
        bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
        reg_w(gspca_dev, 0x59, 0x40);
-       setexposure(gspca_dev);
+       if (sd->sensor == SENSOR_CX0342)
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain),
+                       v4l2_ctrl_g_ctrl(sd->blue),
+                       v4l2_ctrl_g_ctrl(sd->red));
+       else
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
        reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
        reg_w_buf(gspca_dev, tp6810_ov_init_common,
                        ARRAY_SIZE(tp6810_ov_init_common));
@@ -4534,7 +4522,7 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)
                reg_w(gspca_dev, 0x07, 0x85);
                reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01);      /* qvga */
        }
-       setgamma(gspca_dev);
+       setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
        reg_w_buf(gspca_dev, tp6810_bridge_start,
                        ARRAY_SIZE(tp6810_bridge_start));
 
@@ -4545,12 +4533,19 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)
 
        reg_w(gspca_dev, 0x00, 0x00);
 
-       setsharpness(gspca_dev);
+       setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
        bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
                                ARRAY_SIZE(color_gain[0]));
        set_led(gspca_dev, 1);
        reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0xf0);
-       setexposure(gspca_dev);
+       if (sd->sensor == SENSOR_CX0342)
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain),
+                       v4l2_ctrl_g_ctrl(sd->blue),
+                       v4l2_ctrl_g_ctrl(sd->red));
+       else
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
        reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6));
 }
 
@@ -4576,12 +4571,25 @@ static int sd_start(struct gspca_dev *gspca_dev)
                reg_w(gspca_dev, 0x80, 0x03);
                reg_w(gspca_dev, 0x82, gspca_dev->curr_mode ? 0x0a : 0x0e);
 
-               setexposure(gspca_dev);
-               setquality(gspca_dev);
-               setautogain(gspca_dev);
+               if (sd->sensor == SENSOR_CX0342)
+                       setexposure(gspca_dev,
+                               v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                               v4l2_ctrl_g_ctrl(gspca_dev->gain),
+                               v4l2_ctrl_g_ctrl(sd->blue),
+                               v4l2_ctrl_g_ctrl(sd->red));
+               else
+                       setexposure(gspca_dev,
+                               v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                               v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+               if (sd->sensor == SENSOR_SOI763A)
+                       setquality(gspca_dev,
+                                  v4l2_ctrl_g_ctrl(sd->jpegqual));
+               if (sd->bridge == BRIDGE_TP6810)
+                       setautogain(gspca_dev,
+                                   v4l2_ctrl_g_ctrl(gspca_dev->autogain));
        }
 
-       setframerate(gspca_dev);
+       setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
 
        return gspca_dev->usb_err;
 }
@@ -4672,12 +4680,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        }
 }
 
-/* -- do autogain -- */
-/* gain setting is done in setexposure() for tp6810 */
-static void setgain(struct gspca_dev *gspca_dev) {}
-#define WANT_REGULAR_AUTOGAIN
-#include "autogain_functions.h"
-
 static void sd_dq_callback(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -4739,17 +4741,19 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
                        luma /= 4;
                reg_w(gspca_dev, 0x7d, 0x00);
 
-               expo = sd->ctrls[EXPOSURE].val;
-               ret = auto_gain_n_exposure(gspca_dev, luma,
+               expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+               ret = gspca_expo_autogain(gspca_dev, luma,
                                60,     /* desired luma */
                                6,      /* dead zone */
                                2,      /* gain knee */
                                70);    /* expo knee */
                sd->ag_cnt = AG_CNT_START;
                if (sd->bridge == BRIDGE_TP6810) {
-                       if ((expo >= 128 && sd->ctrls[EXPOSURE].val < 128)
-                        || (expo < 128 && sd->ctrls[EXPOSURE].val >= 128))
-                               setframerate(gspca_dev);
+                       int new_expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+
+                       if ((expo >= 128 && new_expo < 128)
+                        || (expo < 128 && new_expo >= 128))
+                               setframerate(gspca_dev, new_expo);
                }
                break;
        }
@@ -4789,7 +4793,7 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev,
 
        sd->framerate = tpf->denominator / tpf->numerator;
        if (gspca_dev->streaming)
-               setframerate(gspca_dev);
+               setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
 
        /* Return the actual framerate */
        i = get_fr_idx(gspca_dev);
@@ -4806,12 +4810,10 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       if (sd->sensor == SENSOR_SOI763A)
-               jpeg_set_qual(sd->jpeg_hdr, jcomp->quality);
-/*     else
-               fixme: TODO
-*/
-       return gspca_dev->usb_err;
+       if (sd->sensor != SENSOR_SOI763A)
+               return -ENOTTY;
+       v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+       return 0;
 }
 
 static int sd_get_jcomp(struct gspca_dev *gspca_dev,
@@ -4819,118 +4821,109 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
+       if (sd->sensor != SENSOR_SOI763A)
+               return -ENOTTY;
        memset(jcomp, 0, sizeof *jcomp);
-       jcomp->quality = jpeg_q[sd->quality];
+       jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
        jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
                        | V4L2_JPEG_MARKER_DQT;
        return 0;
 }
 
-static struct ctrl sd_ctrls[NCTRLS] = {
-[EXPOSURE] = {
-           {
-               .id = V4L2_CID_EXPOSURE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Exposure",
-               .minimum = 0x01,
-               .maximum = 0xdc,
-               .step = 1,
-               .default_value = 0x4e,
-           },
-           .set_control = setexposure
-       },
-[QUALITY] = {
-           {
-               .id = V4L2_CID_PRIVATE_BASE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Compression quality",
-               .minimum = 0,
-               .maximum = 15,
-               .step = 1,
-               .default_value = 13,
-           },
-           .set_control = setquality
-       },
-[RGAIN] = {
-           {
-               .id = V4L2_CID_RED_BALANCE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Red balance",
-               .minimum = 0,
-               .maximum = 4095,
-               .step = 1,
-               .default_value = 256,
-           },
-           .set_control = setrgain
-       },
-[GAIN] = {
-           {
-               .id = V4L2_CID_GAIN,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Gain",
-               .minimum = 0,
-               .maximum = 4095,
-               .step = 1,
-               .default_value = 256,
-           },
-           .set = sd_setgain
-       },
-[BGAIN] = {
-           {
-               .id = V4L2_CID_BLUE_BALANCE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Blue balance",
-               .minimum = 0,
-               .maximum = 4095,
-               .step = 1,
-               .default_value = 256,
-           },
-           .set_control = setbgain
-       },
-[SHARPNESS] = {
-           {
-               .id      = V4L2_CID_SHARPNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Sharpness",
-               .minimum = 0,
-               .maximum = 3,
-               .step    = 1,
-               .default_value = 2,
-           },
-           .set_control = setsharpness
-       },
-[GAMMA] = {
-           {
-               .id      = V4L2_CID_GAMMA,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Gamma",
-               .minimum = 0,
-               .maximum = NGAMMA - 1,
-               .step    = 1,
-               .default_value = 1,
-           },
-           .set_control = setgamma
-       },
-[AUTOGAIN] = {
-           {
-               .id      = V4L2_CID_AUTOGAIN,
-               .type    = V4L2_CTRL_TYPE_BOOLEAN,
-               .name    = "Auto Gain",
-               .minimum = 0,
-               .maximum = 1,
-               .step    = 1,
-               .default_value = AUTOGAIN_DEF
-           },
-           .set_control = setautogain
-       },
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
+
+       gspca_dev->usb_err = 0;
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_SHARPNESS:
+               setsharpness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_GAMMA:
+               setgamma(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               setbgain(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_RED_BALANCE:
+               setrgain(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_EXPOSURE:
+               sd_setgain(gspca_dev);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               if (ctrl->val)
+                       break;
+               sd_setgain(gspca_dev);
+               break;
+       case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+               jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
+               break;
+       }
+       return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
 };
 
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 4);
+       gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_EXPOSURE, 1, 0xdc, 1, 0x4e);
+       if (sd->sensor == SENSOR_CX0342) {
+               sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_RED_BALANCE, 0, 4095, 1, 256);
+               sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BLUE_BALANCE, 0, 4095, 1, 256);
+       }
+       if (sd->sensor == SENSOR_SOI763A)
+               gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, 0, 15, 1, 3);
+       else
+               gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, 0, 4095, 1, 256);
+       sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SHARPNESS, 0, 3, 1, 2);
+       sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAMMA, 0, NGAMMA - 1, 1,
+                       (sd->sensor == SENSOR_SOI763A &&
+                        sd->bridge == BRIDGE_TP6800) ? 0 : 1);
+       if (sd->bridge == BRIDGE_TP6810)
+               gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+       if (sd->sensor == SENSOR_SOI763A)
+               sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_JPEG_COMPRESSION_QUALITY,
+                       0, 15, 1, (sd->bridge == BRIDGE_TP6810) ? 0 : 13);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       if (gspca_dev->autogain)
+               v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+       else
+               v4l2_ctrl_cluster(2, &gspca_dev->exposure);
+       return 0;
+}
+
 static const struct sd_desc sd_desc = {
        .name = KBUILD_MODNAME,
-       .ctrls = sd_ctrls,
-       .nctrls = NCTRLS,
        .config = sd_config,
        .init = sd_init,
+       .init_controls = sd_init_controls,
        .isoc_init = sd_isoc_init,
        .start = sd_start,
        .stopN = sd_stopN,
index c8922c5ffbf57f4d1216dd7d8e1b325084868d69..8591324a53e15edc5582d73d9c90715969ffe0f4 100644 (file)
@@ -30,49 +30,9 @@ MODULE_LICENSE("GPL");
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
 
-       __u16 exposure;
-       __u16 gain;
-
        __u8 packet;
 };
 
-/* V4L2 controls supported by the driver */
-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 const struct ctrl sd_ctrls[] = {
-       {
-        {
-         .id = V4L2_CID_EXPOSURE,
-         .type = V4L2_CTRL_TYPE_INTEGER,
-         .name = "Exposure",
-         .minimum = 1,
-         .maximum = 0x18f,
-         .step = 1,
-#define EXPOSURE_DEF 0x18f
-         .default_value = EXPOSURE_DEF,
-         },
-        .set = sd_setexposure,
-        .get = sd_getexposure,
-        },
-       {
-        {
-         .id = V4L2_CID_GAIN,
-         .type = V4L2_CTRL_TYPE_INTEGER,
-         .name = "Gain",
-         .minimum = 0,
-         .maximum = 0x7ff,
-         .step = 1,
-#define GAIN_DEF 0x100
-         .default_value = GAIN_DEF,
-         },
-        .set = sd_setgain,
-        .get = sd_getgain,
-        },
-};
-
 static const struct v4l2_pix_format sif_mode[] = {
        {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
                .bytesperline = 176,
@@ -202,15 +162,12 @@ static void tv_8532WriteEEprom(struct gspca_dev *gspca_dev)
 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;
        cam->cam_mode = sif_mode;
        cam->nmodes = ARRAY_SIZE(sif_mode);
 
-       sd->exposure = EXPOSURE_DEF;
-       sd->gain = GAIN_DEF;
        return 0;
 }
 
@@ -241,23 +198,19 @@ static int sd_init(struct gspca_dev *gspca_dev)
        return 0;
 }
 
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, sd->exposure);
+       reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, val);
        reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
                                                /* 0x84 */
 }
 
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       reg_w2(gspca_dev, R20_GAIN_G1L, sd->gain);
-       reg_w2(gspca_dev, R22_GAIN_RL, sd->gain);
-       reg_w2(gspca_dev, R24_GAIN_BL, sd->gain);
-       reg_w2(gspca_dev, R26_GAIN_G2L, sd->gain);
+       reg_w2(gspca_dev, R20_GAIN_G1L, val);
+       reg_w2(gspca_dev, R22_GAIN_RL, val);
+       reg_w2(gspca_dev, R24_GAIN_BL, val);
+       reg_w2(gspca_dev, R26_GAIN_G2L, val);
 }
 
 /* -- start the camera -- */
@@ -289,9 +242,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
        tv_8532_setReg(gspca_dev);
 
-       setexposure(gspca_dev);
-       setgain(gspca_dev);
-
        /************************************************/
        reg_w1(gspca_dev, R31_UPD, 0x01);       /* update registers */
        msleep(200);
@@ -339,49 +289,55 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                        data + gspca_dev->width + 5, gspca_dev->width);
 }
 
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
 
-       sd->exposure = val;
-       if (gspca_dev->streaming)
-               setexposure(gspca_dev);
-       return 0;
-}
+       gspca_dev->usb_err = 0;
 
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       if (!gspca_dev->streaming)
+               return 0;
 
-       *val = sd->exposure;
-       return 0;
+       switch (ctrl->id) {
+       case V4L2_CID_EXPOSURE:
+               setexposure(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_GAIN:
+               setgain(gspca_dev, ctrl->val);
+               break;
+       }
+       return gspca_dev->usb_err;
 }
 
-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 const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->gain;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 2);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_EXPOSURE, 0, 0x18f, 1, 0x18f);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, 0, 0x7ff, 1, 0x100);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
        return 0;
 }
 
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .pkt_scan = sd_pkt_scan,
@@ -415,6 +371,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 208f6b2d512a0caed25674865491706e0a32f9e4..f21fd1677c388a383cea3583de2b7114bd460209 100644 (file)
@@ -33,18 +33,10 @@ MODULE_LICENSE("GPL");
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
-
-       u8 brightness;
-       u8 contrast;
-       u8 colors;
-       u8 hflip;
-       u8 vflip;
-       u8 lightfreq;
-       s8 sharpness;
-       u16 exposure;
-       u8 gain;
-       u8 autogain;
-       u8 backlight;
+       struct { /* hvflip cluster */
+               struct v4l2_ctrl *hflip;
+               struct v4l2_ctrl *vflip;
+       };
 
        u8 image_offset;
 
@@ -73,252 +65,6 @@ enum sensors {
        NSENSORS
 };
 
-/* 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_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_setfreq(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getfreq(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_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_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
-#define BRIGHTNESS_IDX 0
-       {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-#define BRIGHTNESS_DEF 128
-               .default_value = BRIGHTNESS_DEF,
-           },
-           .set = sd_setbrightness,
-           .get = sd_getbrightness,
-       },
-#define CONTRAST_IDX 1
-       {
-           {
-               .id      = V4L2_CID_CONTRAST,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Contrast",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-#define CONTRAST_DEF 127
-               .default_value = CONTRAST_DEF,
-           },
-           .set = sd_setcontrast,
-           .get = sd_getcontrast,
-       },
-#define COLORS_IDX 2
-       {
-           {
-               .id      = V4L2_CID_SATURATION,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Saturation",
-               .minimum = 1,
-               .maximum = 127,
-               .step    = 1,
-#define COLOR_DEF 63
-               .default_value = COLOR_DEF,
-           },
-           .set = sd_setcolors,
-           .get = sd_getcolors,
-       },
-/* next 2 controls work with some sensors only */
-#define HFLIP_IDX 3
-       {
-           {
-               .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,
-       },
-#define VFLIP_IDX 4
-       {
-           {
-               .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,
-       },
-#define LIGHTFREQ_IDX 5
-       {
-           {
-               .id      = V4L2_CID_POWER_LINE_FREQUENCY,
-               .type    = V4L2_CTRL_TYPE_MENU,
-               .name    = "Light frequency filter",
-               .minimum = 0,
-               .maximum = 2,   /* 0: No, 1: 50Hz, 2:60Hz */
-               .step    = 1,
-#define FREQ_DEF 1
-               .default_value = FREQ_DEF,
-           },
-           .set = sd_setfreq,
-           .get = sd_getfreq,
-       },
-#define SHARPNESS_IDX 6
-       {
-        {
-         .id = V4L2_CID_SHARPNESS,
-         .type = V4L2_CTRL_TYPE_INTEGER,
-         .name = "Sharpness",
-         .minimum = -1,
-         .maximum = 2,
-         .step = 1,
-#define SHARPNESS_DEF -1
-         .default_value = SHARPNESS_DEF,
-         },
-        .set = sd_setsharpness,
-        .get = sd_getsharpness,
-        },
-#define GAIN_IDX 7
-       {
-           {
-               .id      = V4L2_CID_GAIN,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Gain",
-               .minimum = 0,
-               .maximum = 78,
-               .step    = 1,
-#define GAIN_DEF 0
-               .default_value = GAIN_DEF,
-           },
-           .set = sd_setgain,
-           .get = sd_getgain,
-       },
-#define EXPOSURE_IDX 8
-       {
-               {
-                       .id = V4L2_CID_EXPOSURE,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Exposure",
-#define EXPOSURE_DEF 450
-                       .minimum = 0,
-                       .maximum = 4095,
-                       .step = 1,
-                       .default_value = EXPOSURE_DEF,
-               },
-               .set = sd_setexposure,
-               .get = sd_getexposure,
-       },
-#define AUTOGAIN_IDX 9
-       {
-               {
-                       .id = V4L2_CID_AUTOGAIN,
-                       .type = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name = "Automatic Gain and Exposure",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step = 1,
-#define AUTOGAIN_DEF 1
-                       .default_value = AUTOGAIN_DEF,
-               },
-               .set = sd_setautogain,
-               .get = sd_getautogain,
-       },
-#define BACKLIGHT_IDX 10
-       {
-               {
-                       .id = V4L2_CID_BACKLIGHT_COMPENSATION,
-                       .type = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name = "Backlight Compensation",
-                       .minimum = 0,
-                       .maximum = 15,
-                       .step = 1,
-#define BACKLIGHT_DEF 15
-                       .default_value = BACKLIGHT_DEF,
-               },
-               .set = sd_setbacklight,
-               .get = sd_getbacklight,
-       },
-};
-
-/* table of the disabled controls */
-static u32 ctrl_dis[NSENSORS] = {
-    [SENSOR_HV7131R] =
-       (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
-               | (1 << SHARPNESS_IDX)
-               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
-               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
-    [SENSOR_MI0360] =
-       (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
-               | (1 << SHARPNESS_IDX)
-               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
-               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
-    [SENSOR_MI1310_SOC] =
-       (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
-               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
-               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
-    [SENSOR_MI1320] =
-       (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
-               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
-               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
-    [SENSOR_MI1320_SOC] =
-       (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
-               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
-               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
-    [SENSOR_OV7660] =
-       (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
-               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
-               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
-    [SENSOR_OV7670] =
-       (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << SHARPNESS_IDX)
-               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
-               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
-    [SENSOR_PO1200] =
-       (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << LIGHTFREQ_IDX)
-               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
-               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
-    [SENSOR_PO3130NC] =
-       (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
-               | (1 << SHARPNESS_IDX)
-               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
-               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
-    [SENSOR_POxxxx] =
-       (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX),
-};
 
 static const struct v4l2_pix_format vc0321_mode[] = {
        {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
@@ -3405,18 +3151,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
            (id->idProduct == 0x0892 || id->idProduct == 0x0896))
                sd->sensor = SENSOR_POxxxx;     /* no probe */
 
-       sd->brightness = BRIGHTNESS_DEF;
-       sd->contrast = CONTRAST_DEF;
-       sd->colors = COLOR_DEF;
-       sd->hflip = HFLIP_DEF;
-       sd->vflip = VFLIP_DEF;
-       sd->lightfreq = FREQ_DEF;
-       sd->sharpness = SHARPNESS_DEF;
-       sd->gain = GAIN_DEF;
-       sd->exposure = EXPOSURE_DEF;
-       sd->autogain = AUTOGAIN_DEF;
-       sd->backlight = BACKLIGHT_DEF;
-
        return 0;
 }
 
@@ -3512,7 +3246,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
                }
        }
        cam->npkt = npkt[sd->sensor];
-       gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
 
        if (sd->sensor == SENSOR_OV7670)
                sd->flags |= FL_HFLIP | FL_VFLIP;
@@ -3534,14 +3267,11 @@ static int sd_init(struct gspca_dev *gspca_dev)
        return gspca_dev->usb_err;
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 data;
 
-       if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX))
-               return;
-       data = sd->brightness;
+       data = val;
        if (data >= 0x80)
                data &= 0x7f;
        else
@@ -3549,36 +3279,27 @@ static void setbrightness(struct gspca_dev *gspca_dev)
        i2c_write(gspca_dev, 0x98, &data, 1);
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, u8 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX))
-               return;
-       i2c_write(gspca_dev, 0x99, &sd->contrast, 1);
+       i2c_write(gspca_dev, 0x99, &val, 1);
 }
 
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, u8 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 data;
 
-       if (gspca_dev->ctrl_dis & (1 << COLORS_IDX))
-               return;
-       data = sd->colors - (sd->colors >> 3) - 1;
+       data = val - (val >> 3) - 1;
        i2c_write(gspca_dev, 0x94, &data, 1);
-       i2c_write(gspca_dev, 0x95, &sd->colors, 1);
+       i2c_write(gspca_dev, 0x95, &val, 1);
 }
 
-static void sethvflip(struct gspca_dev *gspca_dev)
+static void sethvflip(struct gspca_dev *gspca_dev, bool hflip, bool vflip)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u8 data[2], hflip, vflip;
+       u8 data[2];
 
-       hflip = sd->hflip;
        if (sd->flags & FL_HFLIP)
                hflip = !hflip;
-       vflip = sd->vflip;
        if (sd->flags & FL_VFLIP)
                vflip = !vflip;
        switch (sd->sensor) {
@@ -3610,7 +3331,7 @@ static void sethvflip(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setlightfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        static const u8 (*ov7660_freq_tb[3])[4] =
@@ -3618,10 +3339,10 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
 
        if (sd->sensor != SENSOR_OV7660)
                return;
-       usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]);
+       usb_exchange(gspca_dev, ov7660_freq_tb[val]);
 }
 
-static void setsharpness(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u8 data;
@@ -3630,51 +3351,41 @@ static void setsharpness(struct gspca_dev *gspca_dev)
        case SENSOR_PO1200:
                data = 0;
                i2c_write(gspca_dev, 0x03, &data, 1);
-               if (sd->sharpness < 0)
+               if (val < 0)
                        data = 0x6a;
                else
-                       data = 0xb5 + sd->sharpness * 3;
+                       data = 0xb5 + val * 3;
                i2c_write(gspca_dev, 0x61, &data, 1);
                break;
        case SENSOR_POxxxx:
-               if (sd->sharpness < 0)
+               if (val < 0)
                        data = 0x7e;    /* def = max */
                else
-                       data = 0x60 + sd->sharpness * 0x0f;
+                       data = 0x60 + val * 0x0f;
                i2c_write(gspca_dev, 0x59, &data, 1);
                break;
        }
 }
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, u8 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (gspca_dev->ctrl_dis & (1 << GAIN_IDX))
-               return;
-       i2c_write(gspca_dev, 0x15, &sd->gain, 1);
+       i2c_write(gspca_dev, 0x15, &val, 1);
 }
 
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u8 data;
 
-       if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
-               return;
-       data = sd->exposure >> 8;
+       data = val >> 8;
        i2c_write(gspca_dev, 0x1a, &data, 1);
-       data = sd->exposure;
+       data = val;
        i2c_write(gspca_dev, 0x1b, &data, 1);
 }
 
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        static const u8 data[2] = {0x28, 0x3c};
 
-       if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX))
-               return;
-       i2c_write(gspca_dev, 0xd1, &data[sd->autogain], 1);
+       i2c_write(gspca_dev, 0xd1, &data[val], 1);
 }
 
 static void setgamma(struct gspca_dev *gspca_dev)
@@ -3683,30 +3394,29 @@ static void setgamma(struct gspca_dev *gspca_dev)
        usb_exchange(gspca_dev, poxxxx_gamma);
 }
 
-static void setbacklight(struct gspca_dev *gspca_dev)
+static void setbacklight(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        u16 v;
        u8 data;
 
-       data = (sd->backlight << 4) | 0x0f;
+       data = (val << 4) | 0x0f;
        i2c_write(gspca_dev, 0xaa, &data, 1);
-       v = 613 + 12 * sd->backlight;
+       v = 613 + 12 * val;
        data = v >> 8;
        i2c_write(gspca_dev, 0xc4, &data, 1);
        data = v;
        i2c_write(gspca_dev, 0xc5, &data, 1);
-       v = 1093 - 12 * sd->backlight;
+       v = 1093 - 12 * val;
        data = v >> 8;
        i2c_write(gspca_dev, 0xc6, &data, 1);
        data = v;
        i2c_write(gspca_dev, 0xc7, &data, 1);
-       v = 342 + 9 * sd->backlight;
+       v = 342 + 9 * val;
        data = v >> 8;
        i2c_write(gspca_dev, 0xc8, &data, 1);
        data = v;
        i2c_write(gspca_dev, 0xc9, &data, 1);
-       v = 702 - 9 * sd->backlight;
+       v = 702 - 9 * val;
        data = v >> 8;
        i2c_write(gspca_dev, 0xca, &data, 1);
        data = v;
@@ -3833,14 +3543,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
 /*     case SENSOR_POxxxx: */
                usb_exchange(gspca_dev, poxxxx_init_common);
                setgamma(gspca_dev);
-               setbacklight(gspca_dev);
-               setbrightness(gspca_dev);
-               setcontrast(gspca_dev);
-               setcolors(gspca_dev);
-               setsharpness(gspca_dev);
-               setautogain(gspca_dev);
-               setexposure(gspca_dev);
-               setgain(gspca_dev);
                usb_exchange(gspca_dev, poxxxx_init_start_3);
                if (mode)
                        init = poxxxx_initQVGA;
@@ -3873,8 +3575,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
                        break;
                }
                msleep(100);
-               sethvflip(gspca_dev);
-               setlightfreq(gspca_dev);
        }
        switch (sd->sensor) {
        case SENSOR_OV7670:
@@ -3960,233 +3660,152 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        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)
-               setbrightness(gspca_dev);
-       return gspca_dev->usb_err;
-}
-
-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)
-               setcontrast(gspca_dev);
-       return gspca_dev->usb_err;
-}
-
-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)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->colors = val;
-       if (gspca_dev->streaming)
-               setcolors(gspca_dev);
-       return gspca_dev->usb_err;
-}
-
-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_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 gspca_dev->usb_err;
-}
-
-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 gspca_dev->usb_err;
-}
-
-static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
 
-       *val = sd->vflip;
-       return 0;
-}
+       gspca_dev->usb_err = 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)
-               setlightfreq(gspca_dev);
-       return gspca_dev->usb_err;
-}
-
-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_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->sharpness = val;
-       if (gspca_dev->streaming)
-               setsharpness(gspca_dev);
-       return gspca_dev->usb_err;
-}
-
-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_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 gspca_dev->usb_err;
-}
-
-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 gspca_dev->usb_err;
-}
-
-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;
-       if (gspca_dev->streaming)
-               setautogain(gspca_dev);
+       if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
+               return 0;
 
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SATURATION:
+               setcolors(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_HFLIP:
+               sethvflip(gspca_dev, sd->hflip->val, sd->vflip->val);
+               break;
+       case V4L2_CID_SHARPNESS:
+               setsharpness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               setautogain(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_GAIN:
+               setgain(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_EXPOSURE:
+               setexposure(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_BACKLIGHT_COMPENSATION:
+               setbacklight(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               setlightfreq(gspca_dev, ctrl->val);
+               break;
+       }
        return gspca_dev->usb_err;
 }
 
-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_setbacklight(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->backlight = val;
-       if (gspca_dev->streaming)
-               setbacklight(gspca_dev);
-
-       return gspca_dev->usb_err;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+       bool has_brightness = false;
+       bool has_contrast = false;
+       bool has_sat = false;
+       bool has_hvflip = false;
+       bool has_freq = false;
+       bool has_backlight = false;
+       bool has_exposure = false;
+       bool has_autogain = false;
+       bool has_gain = false;
+       bool has_sharpness = false;
 
-       *val = sd->backlight;
-       return 0;
-}
-
-static int sd_querymenu(struct gspca_dev *gspca_dev,
-                       struct v4l2_querymenu *menu)
-{
-       static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"};
+       switch (sd->sensor) {
+       case SENSOR_HV7131R:
+       case SENSOR_MI0360:
+       case SENSOR_PO3130NC:
+               break;
+       case SENSOR_MI1310_SOC:
+       case SENSOR_MI1320:
+       case SENSOR_MI1320_SOC:
+       case SENSOR_OV7660:
+               has_hvflip = true;
+               break;
+       case SENSOR_OV7670:
+               has_hvflip = has_freq = true;
+               break;
+       case SENSOR_PO1200:
+               has_hvflip = has_sharpness = true;
+               break;
+       case SENSOR_POxxxx:
+               has_brightness = has_contrast = has_sat = has_backlight =
+                       has_exposure = has_autogain = has_gain =
+                       has_sharpness = true;
+               break;
+       }
 
-       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;
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 8);
+       if (has_brightness)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+       if (has_contrast)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 255, 1, 127);
+       if (has_sat)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SATURATION, 1, 127, 1, 63);
+       if (has_hvflip) {
+               sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_HFLIP, 0, 1, 1, 0);
+               sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_VFLIP, 0, 1, 1, 0);
        }
-       return -EINVAL;
+       if (has_sharpness)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SHARPNESS, -1, 2, 1, -1);
+       if (has_freq)
+               v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+                       V4L2_CID_POWER_LINE_FREQUENCY,
+                       V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+                       V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
+       if (has_autogain)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+       if (has_gain)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, 0, 78, 1, 0);
+       if (has_exposure)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_EXPOSURE, 0, 4095, 1, 450);
+       if (has_backlight)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BACKLIGHT_COMPENSATION, 0, 15, 1, 15);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       if (sd->hflip)
+               v4l2_ctrl_cluster(2, &sd->hflip);
+       return 0;
 }
 
 /* sub-driver description */
 static const struct sd_desc sd_desc = {
        .name = MODULE_NAME,
-       .ctrls = sd_ctrls,
-       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .init_controls = sd_init_controls,
        .config = sd_config,
        .init = sd_init,
        .start = sd_start,
        .stopN = sd_stopN,
        .stop0 = sd_stop0,
        .pkt_scan = sd_pkt_scan,
-       .querymenu = sd_querymenu,
 };
 
 /* -- module initialisation -- */
@@ -4227,6 +3846,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 15a30f7a4b2ab73593da9d28988effee6f165287..b1a64b912666a000647ef28af79bf3d8f67475a6 100644 (file)
@@ -44,17 +44,10 @@ MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver");
 MODULE_LICENSE("GPL");
 MODULE_FIRMWARE(VICAM_FIRMWARE);
 
-enum e_ctrl {
-       GAIN,
-       EXPOSURE,
-       NCTRL           /* number of controls */
-};
-
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
        struct work_struct work_struct;
        struct workqueue_struct *work_thread;
-       struct gspca_ctrl ctrls[NCTRL];
 };
 
 /* The vicam sensor has a resolution of 512 x 244, with I believe square
@@ -86,31 +79,6 @@ static struct v4l2_pix_format vicam_mode[] = {
                .colorspace = V4L2_COLORSPACE_SRGB,},
 };
 
-static const struct ctrl sd_ctrls[] = {
-[GAIN] = {
-           {
-               .id      = V4L2_CID_GAIN,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Gain",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-               .default_value = 200,
-           },
-       },
-[EXPOSURE] = {
-           {
-               .id      = V4L2_CID_EXPOSURE,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Exposure",
-               .minimum = 0,
-               .maximum = 2047,
-               .step    = 1,
-               .default_value = 256,
-           },
-       },
-};
-
 static int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request,
        u16 value, u16 index, u8 *data, u16 len)
 {
@@ -146,12 +114,13 @@ static int vicam_set_camera_power(struct gspca_dev *gspca_dev, int state)
  */
 static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size)
 {
-       struct sd *sd = (struct sd *)gspca_dev;
        int ret, unscaled_height, act_len = 0;
        u8 *req_data = gspca_dev->usb_buf;
+       s32 expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+       s32 gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
 
        memset(req_data, 0, 16);
-       req_data[0] = sd->ctrls[GAIN].val;
+       req_data[0] = gain;
        if (gspca_dev->width == 256)
                req_data[1] |= 0x01; /* low nibble x-scale */
        if (gspca_dev->height <= 122) {
@@ -167,9 +136,9 @@ static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size)
        else /* Up to 244 lines with req_data[3] == 0x08 */
                req_data[3] = 0x08; /* vend? */
 
-       if (sd->ctrls[EXPOSURE].val < 256) {
+       if (expo < 256) {
                /* Frame rate maxed out, use partial frame expo time */
-               req_data[4] = 255 - sd->ctrls[EXPOSURE].val;
+               req_data[4] = 255 - expo;
                req_data[5] = 0x00;
                req_data[6] = 0x00;
                req_data[7] = 0x01;
@@ -177,8 +146,8 @@ static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size)
                /* Modify frame rate */
                req_data[4] = 0x00;
                req_data[5] = 0x00;
-               req_data[6] = sd->ctrls[EXPOSURE].val & 0xFF;
-               req_data[7] = sd->ctrls[EXPOSURE].val >> 8;
+               req_data[6] = expo & 0xFF;
+               req_data[7] = expo >> 8;
        }
        req_data[8] = ((244 - unscaled_height) / 2) & ~0x01; /* vstart */
        /* bytes 9-15 do not seem to affect exposure or image quality */
@@ -260,7 +229,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
        cam->bulk_size = 64;
        cam->cam_mode = vicam_mode;
        cam->nmodes = ARRAY_SIZE(vicam_mode);
-       cam->ctrls = sd->ctrls;
 
        INIT_WORK(&sd->work_struct, vicam_dostream);
 
@@ -335,6 +303,24 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
                vicam_set_camera_power(gspca_dev, 0);
 }
 
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 2);
+       gspca_dev->exposure = v4l2_ctrl_new_std(hdl, NULL,
+                       V4L2_CID_EXPOSURE, 0, 2047, 1, 256);
+       gspca_dev->gain = v4l2_ctrl_new_std(hdl, NULL,
+                       V4L2_CID_GAIN, 0, 255, 1, 200);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       return 0;
+}
+
 /* Table of supported USB devices */
 static const struct usb_device_id device_table[] = {
        {USB_DEVICE(0x04c1, 0x009d)},
@@ -347,10 +333,9 @@ MODULE_DEVICE_TABLE(usb, device_table);
 /* 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,
+       .init_controls = sd_init_controls,
        .start  = sd_start,
        .stop0  = sd_stop0,
 };
@@ -373,6 +358,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume  = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
index 27d2cef0692a8dafa834bb5ba408614a0cd785e3..9e3a909e0a004daba37a8810cf3bc3d0cd1fe65e 100644 (file)
@@ -404,9 +404,14 @@ static void w9968cf_set_crop_window(struct sd *sd)
        }
 
        if (sd->sensor == SEN_OV7620) {
-               /* Sigh, this is dependend on the clock / framerate changes
-                  made by the frequency control, sick. */
-               if (sd->ctrls[FREQ].val == 1) {
+               /*
+                * Sigh, this is dependend on the clock / framerate changes
+                * made by the frequency control, sick.
+                *
+                * Note we cannot use v4l2_ctrl_g_ctrl here, as we get called
+                * from ov519.c:setfreq() with the ctrl lock held!
+                */
+               if (sd->freq->val == 1) {
                        start_cropx = 277;
                        start_cropy = 37;
                } else {
@@ -474,8 +479,9 @@ static void w9968cf_mode_init_regs(struct sd *sd)
                /* We may get called multiple times (usb isoc bw negotiat.) */
                jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height,
                            sd->gspca_dev.width, 0x22); /* JPEG 420 */
-               jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+               jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual));
                w9968cf_upload_quantizationtables(sd);
+               v4l2_ctrl_grab(sd->jpegqual, true);
        }
 
        /* Video Capture Control Register */
@@ -514,6 +520,7 @@ static void w9968cf_mode_init_regs(struct sd *sd)
 
 static void w9968cf_stop0(struct sd *sd)
 {
+       v4l2_ctrl_grab(sd->jpegqual, false);
        reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */
        reg_w(sd, 0x16, 0x0000); /* stop video capture */
 }
index ecada178bceb9b27bc0c3d6d0a2d505100ccb3b4..13b8d395d21072040c089f13a41090f117d8e55f 100644 (file)
@@ -53,6 +53,7 @@ MODULE_PARM_DESC(rca_input,
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;             /* !! must be the first item */
+       struct v4l2_ctrl *lighting;
        u8 model;
 #define CIT_MODEL0 0 /* bcd version 0.01 cams ie the xvp-500 */
 #define CIT_MODEL1 1 /* The model 1 - 4 nomenclature comes from the old */
@@ -65,127 +66,10 @@ struct sd {
        u8 stop_on_control_change;
        u8 sof_read;
        u8 sof_len;
-       u8 contrast;
-       u8 brightness;
-       u8 hue;
-       u8 sharpness;
-       u8 lighting;
-       u8 hflip;
 };
 
-/* 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_sethue(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_gethue(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_setlighting(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getlighting(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 void sd_stop0(struct gspca_dev *gspca_dev);
 
-static const struct ctrl sd_ctrls[] = {
-#define SD_BRIGHTNESS 0
-       {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 63,
-               .step = 1,
-#define BRIGHTNESS_DEFAULT 32
-               .default_value = BRIGHTNESS_DEFAULT,
-               .flags = 0,
-           },
-           .set = sd_setbrightness,
-           .get = sd_getbrightness,
-       },
-#define SD_CONTRAST 1
-       {
-           {
-               .id = V4L2_CID_CONTRAST,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "contrast",
-               .minimum = 0,
-               .maximum = 20,
-               .step = 1,
-#define CONTRAST_DEFAULT 10
-               .default_value = CONTRAST_DEFAULT,
-               .flags = 0,
-           },
-           .set = sd_setcontrast,
-           .get = sd_getcontrast,
-       },
-#define SD_HUE 2
-       {
-           {
-               .id     = V4L2_CID_HUE,
-               .type   = V4L2_CTRL_TYPE_INTEGER,
-               .name   = "Hue",
-               .minimum = 0,
-               .maximum = 127,
-               .step   = 1,
-#define HUE_DEFAULT 63
-               .default_value = HUE_DEFAULT,
-               .flags = 0,
-           },
-           .set = sd_sethue,
-           .get = sd_gethue,
-       },
-#define SD_SHARPNESS 3
-       {
-           {
-               .id = V4L2_CID_SHARPNESS,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Sharpness",
-               .minimum = 0,
-               .maximum = 6,
-               .step = 1,
-#define SHARPNESS_DEFAULT 3
-               .default_value = SHARPNESS_DEFAULT,
-               .flags = 0,
-           },
-           .set = sd_setsharpness,
-           .get = sd_getsharpness,
-       },
-#define SD_LIGHTING 4
-       {
-           {
-               .id = V4L2_CID_BACKLIGHT_COMPENSATION,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Lighting",
-               .minimum = 0,
-               .maximum = 2,
-               .step = 1,
-#define LIGHTING_DEFAULT 1
-               .default_value = LIGHTING_DEFAULT,
-               .flags = 0,
-           },
-           .set = sd_setlighting,
-           .get = sd_getlighting,
-       },
-#define SD_HFLIP 5
-       {
-           {
-               .id      = V4L2_CID_HFLIP,
-               .type    = V4L2_CTRL_TYPE_BOOLEAN,
-               .name    = "Mirror",
-               .minimum = 0,
-               .maximum = 1,
-               .step    = 1,
-#define HFLIP_DEFAULT 0
-               .default_value = HFLIP_DEFAULT,
-           },
-           .set = sd_sethflip,
-           .get = sd_gethflip,
-       },
-};
-
 static const struct v4l2_pix_format cif_yuv_mode[] = {
        {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
                .bytesperline = 176,
@@ -995,56 +879,36 @@ static int sd_config(struct gspca_dev *gspca_dev,
        case CIT_MODEL0:
                cam->cam_mode = model0_mode;
                cam->nmodes = ARRAY_SIZE(model0_mode);
-               gspca_dev->ctrl_dis = ~((1 << SD_CONTRAST) | (1 << SD_HFLIP));
                sd->sof_len = 4;
                break;
        case CIT_MODEL1:
                cam->cam_mode = cif_yuv_mode;
                cam->nmodes = ARRAY_SIZE(cif_yuv_mode);
-               gspca_dev->ctrl_dis = (1 << SD_HUE) | (1 << SD_HFLIP);
                sd->sof_len = 4;
                break;
        case CIT_MODEL2:
                cam->cam_mode = model2_mode + 1; /* no 160x120 */
                cam->nmodes = 3;
-               gspca_dev->ctrl_dis = (1 << SD_CONTRAST) |
-                                     (1 << SD_SHARPNESS) |
-                                     (1 << SD_HFLIP);
                break;
        case CIT_MODEL3:
                cam->cam_mode = vga_yuv_mode;
                cam->nmodes = ARRAY_SIZE(vga_yuv_mode);
-               gspca_dev->ctrl_dis = (1 << SD_HUE) |
-                                     (1 << SD_LIGHTING) |
-                                     (1 << SD_HFLIP);
                sd->stop_on_control_change = 1;
                sd->sof_len = 4;
                break;
        case CIT_MODEL4:
                cam->cam_mode = model2_mode;
                cam->nmodes = ARRAY_SIZE(model2_mode);
-               gspca_dev->ctrl_dis = (1 << SD_CONTRAST) |
-                                     (1 << SD_SHARPNESS) |
-                                     (1 << SD_LIGHTING) |
-                                     (1 << SD_HFLIP);
                break;
        case CIT_IBM_NETCAM_PRO:
                cam->cam_mode = vga_yuv_mode;
                cam->nmodes = 2; /* no 640 x 480 */
                cam->input_flags = V4L2_IN_ST_VFLIP;
-               gspca_dev->ctrl_dis = ~(1 << SD_CONTRAST);
                sd->stop_on_control_change = 1;
                sd->sof_len = 4;
                break;
        }
 
-       sd->brightness = BRIGHTNESS_DEFAULT;
-       sd->contrast = CONTRAST_DEFAULT;
-       sd->hue = HUE_DEFAULT;
-       sd->sharpness = SHARPNESS_DEFAULT;
-       sd->lighting = LIGHTING_DEFAULT;
-       sd->hflip = HFLIP_DEFAULT;
-
        return 0;
 }
 
@@ -1287,7 +1151,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
        return 0;
 }
 
-static int cit_set_brightness(struct gspca_dev *gspca_dev)
+static int cit_set_brightness(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int i;
@@ -1299,19 +1163,19 @@ static int cit_set_brightness(struct gspca_dev *gspca_dev)
                break;
        case CIT_MODEL1:
                /* Model 1: Brightness range 0 - 63 */
-               cit_Packet_Format1(gspca_dev, 0x0031, sd->brightness);
-               cit_Packet_Format1(gspca_dev, 0x0032, sd->brightness);
-               cit_Packet_Format1(gspca_dev, 0x0033, sd->brightness);
+               cit_Packet_Format1(gspca_dev, 0x0031, val);
+               cit_Packet_Format1(gspca_dev, 0x0032, val);
+               cit_Packet_Format1(gspca_dev, 0x0033, val);
                break;
        case CIT_MODEL2:
                /* Model 2: Brightness range 0x60 - 0xee */
                /* Scale 0 - 63 to 0x60 - 0xee */
-               i = 0x60 + sd->brightness * 2254 / 1000;
+               i = 0x60 + val * 2254 / 1000;
                cit_model2_Packet1(gspca_dev, 0x001a, i);
                break;
        case CIT_MODEL3:
                /* Model 3: Brightness range 'i' in [0x0C..0x3F] */
-               i = sd->brightness;
+               i = val;
                if (i < 0x0c)
                        i = 0x0c;
                cit_model3_Packet1(gspca_dev, 0x0036, i);
@@ -1319,7 +1183,7 @@ static int cit_set_brightness(struct gspca_dev *gspca_dev)
        case CIT_MODEL4:
                /* Model 4: Brightness range 'i' in [0x04..0xb4] */
                /* Scale 0 - 63 to 0x04 - 0xb4 */
-               i = 0x04 + sd->brightness * 2794 / 1000;
+               i = 0x04 + val * 2794 / 1000;
                cit_model4_BrightnessPacket(gspca_dev, i);
                break;
        }
@@ -1327,7 +1191,7 @@ static int cit_set_brightness(struct gspca_dev *gspca_dev)
        return 0;
 }
 
-static int cit_set_contrast(struct gspca_dev *gspca_dev)
+static int cit_set_contrast(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
@@ -1335,16 +1199,16 @@ static int cit_set_contrast(struct gspca_dev *gspca_dev)
        case CIT_MODEL0: {
                int i;
                /* gain 0-15, 0-20 -> 0-15 */
-               i = sd->contrast * 1000 / 1333;
+               i = val * 1000 / 1333;
                cit_write_reg(gspca_dev, i, 0x0422);
                /* gain 0-31, may not be lower then 0x0422, 0-20 -> 0-31 */
-               i = sd->contrast * 2000 / 1333;
+               i = val * 2000 / 1333;
                cit_write_reg(gspca_dev, i, 0x0423);
                /* gain 0-127, may not be lower then 0x0423, 0-20 -> 0-63  */
-               i = sd->contrast * 4000 / 1333;
+               i = val * 4000 / 1333;
                cit_write_reg(gspca_dev, i, 0x0424);
                /* gain 0-127, may not be lower then 0x0424, , 0-20 -> 0-127 */
-               i = sd->contrast * 8000 / 1333;
+               i = val * 8000 / 1333;
                cit_write_reg(gspca_dev, i, 0x0425);
                break;
        }
@@ -1355,7 +1219,7 @@ static int cit_set_contrast(struct gspca_dev *gspca_dev)
        case CIT_MODEL1:
        {
                /* Scale 0 - 20 to 15 - 0 */
-               int i, new_contrast = (20 - sd->contrast) * 1000 / 1333;
+               int i, new_contrast = (20 - val) * 1000 / 1333;
                for (i = 0; i < cit_model1_ntries; i++) {
                        cit_Packet_Format1(gspca_dev, 0x0014, new_contrast);
                        cit_send_FF_04_02(gspca_dev);
@@ -1377,20 +1241,20 @@ static int cit_set_contrast(struct gspca_dev *gspca_dev)
                        { 0x01, 0x0e, 0x16 },
                        { 0x01, 0x10, 0x16 }    /* Maximum */
                };
-               int i = sd->contrast / 3;
+               int i = val / 3;
                cit_model3_Packet1(gspca_dev, 0x0067, cv[i].cv1);
                cit_model3_Packet1(gspca_dev, 0x005b, cv[i].cv2);
                cit_model3_Packet1(gspca_dev, 0x005c, cv[i].cv3);
                break;
        }
        case CIT_IBM_NETCAM_PRO:
-               cit_model3_Packet1(gspca_dev, 0x005b, sd->contrast + 1);
+               cit_model3_Packet1(gspca_dev, 0x005b, val + 1);
                break;
        }
        return 0;
 }
 
-static int cit_set_hue(struct gspca_dev *gspca_dev)
+static int cit_set_hue(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
@@ -1401,7 +1265,7 @@ static int cit_set_hue(struct gspca_dev *gspca_dev)
                /* No hue control for these models */
                break;
        case CIT_MODEL2:
-               cit_model2_Packet1(gspca_dev, 0x0024, sd->hue);
+               cit_model2_Packet1(gspca_dev, 0x0024, val);
                /* cit_model2_Packet1(gspca_dev, 0x0020, sat); */
                break;
        case CIT_MODEL3: {
@@ -1409,7 +1273,7 @@ static int cit_set_hue(struct gspca_dev *gspca_dev)
                /* TESTME according to the ibmcam driver this does not work */
                if (0) {
                        /* Scale 0 - 127 to 0x05 - 0x37 */
-                       int i = 0x05 + sd->hue * 1000 / 2540;
+                       int i = 0x05 + val * 1000 / 2540;
                        cit_model3_Packet1(gspca_dev, 0x007e, i);
                }
                break;
@@ -1435,14 +1299,14 @@ static int cit_set_hue(struct gspca_dev *gspca_dev)
                cit_write_reg(gspca_dev,    160, 0x012e);  /* Red gain */
                cit_write_reg(gspca_dev,    160, 0x0130);  /* Blue gain */
                cit_write_reg(gspca_dev, 0x8a28, 0x0124);
-               cit_write_reg(gspca_dev, sd->hue, 0x012d); /* Hue */
+               cit_write_reg(gspca_dev, val, 0x012d); /* Hue */
                cit_write_reg(gspca_dev, 0xf545, 0x0124);
                break;
        }
        return 0;
 }
 
-static int cit_set_sharpness(struct gspca_dev *gspca_dev)
+static int cit_set_sharpness(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
@@ -1459,7 +1323,7 @@ static int cit_set_sharpness(struct gspca_dev *gspca_dev)
                        0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a };
 
                for (i = 0; i < cit_model1_ntries; i++)
-                       cit_PacketFormat2(gspca_dev, 0x0013, sa[sd->sharpness]);
+                       cit_PacketFormat2(gspca_dev, 0x0013, sa[val]);
                break;
        }
        case CIT_MODEL3:
@@ -1482,10 +1346,10 @@ static int cit_set_sharpness(struct gspca_dev *gspca_dev)
                        { 0x03, 0x06, 0x05, 0x14 },
                        { 0x03, 0x07, 0x05, 0x14 }      /* Sharpest */
                };
-               cit_model3_Packet1(gspca_dev, 0x0060, sv[sd->sharpness].sv1);
-               cit_model3_Packet1(gspca_dev, 0x0061, sv[sd->sharpness].sv2);
-               cit_model3_Packet1(gspca_dev, 0x0062, sv[sd->sharpness].sv3);
-               cit_model3_Packet1(gspca_dev, 0x0063, sv[sd->sharpness].sv4);
+               cit_model3_Packet1(gspca_dev, 0x0060, sv[val].sv1);
+               cit_model3_Packet1(gspca_dev, 0x0061, sv[val].sv2);
+               cit_model3_Packet1(gspca_dev, 0x0062, sv[val].sv3);
+               cit_model3_Packet1(gspca_dev, 0x0063, sv[val].sv4);
                break;
        }
        }
@@ -1510,7 +1374,7 @@ static int cit_set_sharpness(struct gspca_dev *gspca_dev)
  * 1/5/00   Created.
  * 2/20/00  Added support for Model 2 cameras.
  */
-static void cit_set_lighting(struct gspca_dev *gspca_dev)
+static void cit_set_lighting(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
@@ -1524,19 +1388,19 @@ static void cit_set_lighting(struct gspca_dev *gspca_dev)
        case CIT_MODEL1: {
                int i;
                for (i = 0; i < cit_model1_ntries; i++)
-                       cit_Packet_Format1(gspca_dev, 0x0027, sd->lighting);
+                       cit_Packet_Format1(gspca_dev, 0x0027, val);
                break;
        }
        }
 }
 
-static void cit_set_hflip(struct gspca_dev *gspca_dev)
+static void cit_set_hflip(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
        switch (sd->model) {
        case CIT_MODEL0:
-               if (sd->hflip)
+               if (val)
                        cit_write_reg(gspca_dev, 0x0020, 0x0115);
                else
                        cit_write_reg(gspca_dev, 0x0040, 0x0115);
@@ -1831,7 +1695,8 @@ static int cit_start_model1(struct gspca_dev *gspca_dev)
                        cit_PacketFormat2(gspca_dev, 0x13, 0x1a);
 
                /* Default lighting conditions */
-               cit_Packet_Format1(gspca_dev, 0x0027, sd->lighting);
+               cit_Packet_Format1(gspca_dev, 0x0027,
+                                  v4l2_ctrl_g_ctrl(sd->lighting));
        }
 
        /* Assorted init */
@@ -2049,9 +1914,10 @@ static int cit_start_model2(struct gspca_dev *gspca_dev)
                break;
        }
 
-       /* FIXME this cannot be changed while streaming, so we
-          should report a grabbed flag for this control. */
-       cit_model2_Packet1(gspca_dev, 0x0028, sd->lighting);
+       cit_model2_Packet1(gspca_dev, 0x0028, v4l2_ctrl_g_ctrl(sd->lighting));
+       /* model2 cannot change the backlight compensation while streaming */
+       v4l2_ctrl_grab(sd->lighting, true);
+
        /* color balance rg2 */
        cit_model2_Packet1(gspca_dev, 0x001e, 0x002f);
        /* saturation */
@@ -2755,13 +2621,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
                break;
        }
 
-       cit_set_brightness(gspca_dev);
-       cit_set_contrast(gspca_dev);
-       cit_set_hue(gspca_dev);
-       cit_set_sharpness(gspca_dev);
-       cit_set_lighting(gspca_dev);
-       cit_set_hflip(gspca_dev);
-
        /* Program max isoc packet size */
        cit_write_reg(gspca_dev, packet_size >> 8, 0x0106);
        cit_write_reg(gspca_dev, packet_size & 0xff, 0x0107);
@@ -2857,6 +2716,8 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
                cit_write_reg(gspca_dev, 0x81, 0x0100); /* LED Off */
                break;
        case CIT_MODEL2:
+               v4l2_ctrl_grab(sd->lighting, false);
+               /* Fall through! */
        case CIT_MODEL4:
                cit_model2_Packet1(gspca_dev, 0x0030, 0x0004);
 
@@ -3055,152 +2916,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        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) {
-               if (sd->stop_on_control_change)
-                       sd_stopN(gspca_dev);
-               cit_set_brightness(gspca_dev);
-               if (sd->stop_on_control_change)
-                       cit_restart_stream(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->stop_on_control_change)
-                       sd_stopN(gspca_dev);
-               cit_set_contrast(gspca_dev);
-               if (sd->stop_on_control_change)
-                       cit_restart_stream(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_sethue(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->hue = val;
-       if (gspca_dev->streaming) {
-               if (sd->stop_on_control_change)
-                       sd_stopN(gspca_dev);
-               cit_set_hue(gspca_dev);
-               if (sd->stop_on_control_change)
-                       cit_restart_stream(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_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->sharpness = val;
-       if (gspca_dev->streaming) {
-               if (sd->stop_on_control_change)
-                       sd_stopN(gspca_dev);
-               cit_set_sharpness(gspca_dev);
-               if (sd->stop_on_control_change)
-                       cit_restart_stream(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_setlighting(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->lighting = val;
-       if (gspca_dev->streaming) {
-               if (sd->stop_on_control_change)
-                       sd_stopN(gspca_dev);
-               cit_set_lighting(gspca_dev);
-               if (sd->stop_on_control_change)
-                       cit_restart_stream(gspca_dev);
-       }
-       return 0;
-}
-
-static int sd_getlighting(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->lighting;
-
-       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) {
-               if (sd->stop_on_control_change)
-                       sd_stopN(gspca_dev);
-               cit_set_hflip(gspca_dev);
-               if (sd->stop_on_control_change)
-                       cit_restart_stream(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;
-}
-
 #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 static void cit_check_button(struct gspca_dev *gspca_dev)
 {
@@ -3234,13 +2949,117 @@ static void cit_check_button(struct gspca_dev *gspca_dev)
 }
 #endif
 
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
+
+       gspca_dev->usb_err = 0;
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       if (sd->stop_on_control_change)
+               sd_stopN(gspca_dev);
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               cit_set_brightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               cit_set_contrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_HUE:
+               cit_set_hue(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_HFLIP:
+               cit_set_hflip(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SHARPNESS:
+               cit_set_sharpness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_BACKLIGHT_COMPENSATION:
+               cit_set_lighting(gspca_dev, ctrl->val);
+               break;
+       }
+       if (sd->stop_on_control_change)
+               cit_restart_stream(gspca_dev);
+       return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+       bool has_brightness;
+       bool has_contrast;
+       bool has_hue;
+       bool has_sharpness;
+       bool has_lighting;
+       bool has_hflip;
+
+       has_brightness = has_contrast = has_hue =
+               has_sharpness = has_hflip = has_lighting = false;
+       switch (sd->model) {
+       case CIT_MODEL0:
+               has_contrast = has_hflip = true;
+               break;
+       case CIT_MODEL1:
+               has_brightness = has_contrast =
+                       has_sharpness = has_lighting = true;
+               break;
+       case CIT_MODEL2:
+               has_brightness = has_hue = has_lighting = true;
+               break;
+       case CIT_MODEL3:
+               has_brightness = has_contrast = has_sharpness = true;
+               break;
+       case CIT_MODEL4:
+               has_brightness = has_hue = true;
+               break;
+       case CIT_IBM_NETCAM_PRO:
+               has_brightness = has_hue =
+                       has_sharpness = has_hflip = has_lighting = true;
+               break;
+       }
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 5);
+       if (has_brightness)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 63, 1, 32);
+       if (has_contrast)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 20, 1, 10);
+       if (has_hue)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_HUE, 0, 127, 1, 63);
+       if (has_sharpness)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SHARPNESS, 0, 6, 1, 3);
+       if (has_lighting)
+               sd->lighting = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BACKLIGHT_COMPENSATION, 0, 2, 1, 1);
+       if (has_hflip)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       return 0;
+}
+
 /* 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,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .stopN = sd_stopN,
        .stop0 = sd_stop0,
@@ -3253,10 +3072,9 @@ static const struct sd_desc sd_desc = {
 
 static const struct sd_desc sd_desc_isoc_nego = {
        .name = MODULE_NAME,
-       .ctrls = sd_ctrls,
-       .nctrls = ARRAY_SIZE(sd_ctrls),
        .config = sd_config,
        .init = sd_init,
+       .init_controls = sd_init_controls,
        .start = sd_start,
        .isoc_init = sd_isoc_init,
        .isoc_nego = sd_isoc_nego,
@@ -3320,6 +3138,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };
 
diff --git a/drivers/media/video/ibmmpeg2.h b/drivers/media/video/ibmmpeg2.h
deleted file mode 100644 (file)
index 68e1038..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/* ibmmpeg2.h - IBM MPEGCD21 definitions */
-
-#ifndef __IBM_MPEG2__
-#define __IBM_MPEG2__
-
-/* Define all MPEG Decoder registers */
-/* Chip Control and Status */
-#define IBM_MP2_CHIP_CONTROL   0x200*2
-#define IBM_MP2_CHIP_MODE      0x201*2
-/* Timer Control and Status */
-#define IBM_MP2_SYNC_STC2      0x202*2
-#define IBM_MP2_SYNC_STC1      0x203*2
-#define IBM_MP2_SYNC_STC0      0x204*2
-#define IBM_MP2_SYNC_PTS2      0x205*2
-#define IBM_MP2_SYNC_PTS1      0x206*2
-#define IBM_MP2_SYNC_PTS0      0x207*2
-/* Video FIFO Control */
-#define IBM_MP2_FIFO           0x208*2
-#define IBM_MP2_FIFOW          0x100*2
-#define IBM_MP2_FIFO_STAT      0x209*2
-#define IBM_MP2_RB_THRESHOLD   0x22b*2
-/* Command buffer */
-#define IBM_MP2_COMMAND                0x20a*2
-#define IBM_MP2_CMD_DATA       0x20b*2
-#define IBM_MP2_CMD_STAT       0x20c*2
-#define IBM_MP2_CMD_ADDR       0x20d*2
-/* Internal Processor Control and Status */
-#define IBM_MP2_PROC_IADDR     0x20e*2
-#define IBM_MP2_PROC_IDATA     0x20f*2
-#define IBM_MP2_WR_PROT                0x235*2
-/* DRAM Access */
-#define IBM_MP2_DRAM_ADDR      0x210*2
-#define IBM_MP2_DRAM_DATA      0x212*2
-#define IBM_MP2_DRAM_CMD_STAT  0x213*2
-#define IBM_MP2_BLOCK_SIZE     0x23b*2
-#define IBM_MP2_SRC_ADDR       0x23c*2
-/* Onscreen Display */
-#define IBM_MP2_OSD_ADDR       0x214*2
-#define IBM_MP2_OSD_DATA       0x215*2
-#define IBM_MP2_OSD_MODE       0x217*2
-#define IBM_MP2_OSD_LINK_ADDR  0x229*2
-#define IBM_MP2_OSD_SIZE       0x22a*2
-/* Interrupt Control */
-#define IBM_MP2_HOST_INT       0x218*2
-#define IBM_MP2_MASK0          0x219*2
-#define IBM_MP2_HOST_INT1      0x23e*2
-#define IBM_MP2_MASK1          0x23f*2
-/* Audio Control */
-#define IBM_MP2_AUD_IADDR      0x21a*2
-#define IBM_MP2_AUD_IDATA      0x21b*2
-#define IBM_MP2_AUD_FIFO       0x21c*2
-#define IBM_MP2_AUD_FIFOW      0x101*2
-#define IBM_MP2_AUD_CTL                0x21d*2
-#define IBM_MP2_BEEP_CTL       0x21e*2
-#define IBM_MP2_FRNT_ATTEN     0x22d*2
-/* Display Control */
-#define IBM_MP2_DISP_MODE      0x220*2
-#define IBM_MP2_DISP_DLY       0x221*2
-#define IBM_MP2_VBI_CTL                0x222*2
-#define IBM_MP2_DISP_LBOR      0x223*2
-#define IBM_MP2_DISP_TBOR      0x224*2
-/* Polarity Control */
-#define IBM_MP2_INFC_CTL       0x22c*2
-
-/* control commands */
-#define IBM_MP2_PLAY           0
-#define IBM_MP2_PAUSE          1
-#define IBM_MP2_SINGLE_FRAME   2
-#define IBM_MP2_FAST_FORWARD   3
-#define IBM_MP2_SLOW_MOTION    4
-#define IBM_MP2_IMED_NORM_PLAY 5
-#define IBM_MP2_RESET_WINDOW   6
-#define IBM_MP2_FREEZE_FRAME   7
-#define IBM_MP2_RESET_VID_RATE 8
-#define IBM_MP2_CONFIG_DECODER 9
-#define IBM_MP2_CHANNEL_SWITCH 10
-#define IBM_MP2_RESET_AUD_RATE 11
-#define IBM_MP2_PRE_OP_CHN_SW  12
-#define IBM_MP2_SET_STILL_MODE 14
-
-/* Define Xilinx FPGA Internal Registers */
-
-/* general control register 0 */
-#define XILINX_CTL0            0x600
-/* genlock delay resister 1 */
-#define XILINX_GLDELAY         0x602
-/* send 16 bits to CS3310 port */
-#define XILINX_CS3310          0x604
-/* send 16 bits to CS3310 and complete */
-#define XILINX_CS3310_CMPLT    0x60c
-/* pulse width modulator control */
-#define XILINX_PWM             0x606
-
-#endif
index f7d57b3f28429dc0efeaa49eee89f6aebb348a34..32a591062d0b0b3b59f925c496d9cc60aa6ef230 100644 (file)
@@ -1830,18 +1830,6 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio,
        return 0;
 }
 
-long ivtv_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
-{
-       struct video_device *vfd = video_devdata(filp);
-       long ret;
-
-       if (ivtv_debug & IVTV_DBGFLG_IOCTL)
-               vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
-       ret = video_ioctl2(filp, cmd, arg);
-       vfd->debug = 0;
-       return ret;
-}
-
 static const struct v4l2_ioctl_ops ivtv_ioctl_ops = {
        .vidioc_querycap                    = ivtv_querycap,
        .vidioc_s_audio                     = ivtv_s_audio,
index 89185caeafae0a5c72c8423d6b3ad6b242947227..7c553d16579bec13adb67a6e8361be22c56f5ac9 100644 (file)
@@ -31,6 +31,5 @@ void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id *std);
 void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std);
 int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
 int ivtv_s_input(struct file *file, void *fh, unsigned int inp);
-long ivtv_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
 
 #endif
index 6738592aa35d6c78bd30893c421a0adbe04eea52..87990c5f0910331d9f692fa7cbd828ada8325a77 100644 (file)
@@ -50,7 +50,7 @@ static const struct v4l2_file_operations ivtv_v4l2_enc_fops = {
        .read = ivtv_v4l2_read,
        .write = ivtv_v4l2_write,
        .open = ivtv_v4l2_open,
-       .unlocked_ioctl = ivtv_v4l2_ioctl,
+       .unlocked_ioctl = video_ioctl2,
        .release = ivtv_v4l2_close,
        .poll = ivtv_v4l2_enc_poll,
 };
@@ -60,7 +60,7 @@ static const struct v4l2_file_operations ivtv_v4l2_dec_fops = {
        .read = ivtv_v4l2_read,
        .write = ivtv_v4l2_write,
        .open = ivtv_v4l2_open,
-       .unlocked_ioctl = ivtv_v4l2_ioctl,
+       .unlocked_ioctl = video_ioctl2,
        .release = ivtv_v4l2_close,
        .poll = ivtv_v4l2_dec_poll,
 };
index 302dc3d70193f9718bd6ed7bda4e9260e33338e0..dc8c2505907e73b10a337743371bbf862c771a3f 100644 (file)
@@ -1,5 +1,6 @@
 config VIDEO_M5MOLS
        tristate "Fujitsu M-5MOLS 8MP sensor support"
        depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+       depends on MEDIA_CAMERA_SUPPORT
        ---help---
          This driver supports Fujitsu M-5MOLS camera sensor with ISP
index 392a028730e2db657ca1f506893ff65a537fe3cc..fdbc205a29698a1836b94d1fb9b6d9f8de0e798a 100644 (file)
@@ -527,8 +527,8 @@ static const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
 
 /* Supported manual ISO values */
 static const s64 iso_qmenu[] = {
-       /* AE_ISO: 0x01...0x07 */
-       50, 100, 200, 400, 800, 1600, 3200
+       /* AE_ISO: 0x01...0x07 (ISO: 50...3200) */
+       50000, 100000, 200000, 400000, 800000, 1600000, 3200000
 };
 
 /* Supported Exposure Bias values, -2.0EV...+2.0EV */
index 3945556f573384f77fb19752ac05b4bfdba23464..7efe9ad7acc7f90f0942644ee4d909b8b9705618 100644 (file)
@@ -27,6 +27,8 @@
 #include <media/v4l2-mem2mem.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
 #include <media/videobuf2-vmalloc.h>
 
 #define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev"
@@ -60,6 +62,10 @@ MODULE_VERSION("0.1.1");
 #define MEM2MEM_COLOR_STEP     (0xff >> 4)
 #define MEM2MEM_NUM_TILES      8
 
+/* Flags that indicate processing mode */
+#define MEM2MEM_HFLIP  (1 << 0)
+#define MEM2MEM_VFLIP  (1 << 1)
+
 #define dprintk(dev, fmt, arg...) \
        v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
 
@@ -97,6 +103,8 @@ static struct m2mtest_fmt formats[] = {
        },
 };
 
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
 /* Per-queue, driver-specific private data */
 struct m2mtest_q_data {
        unsigned int            width;
@@ -110,32 +118,8 @@ enum {
        V4L2_M2M_DST = 1,
 };
 
-#define V4L2_CID_TRANS_TIME_MSEC       V4L2_CID_PRIVATE_BASE
-#define V4L2_CID_TRANS_NUM_BUFS                (V4L2_CID_PRIVATE_BASE + 1)
-
-static struct v4l2_queryctrl m2mtest_ctrls[] = {
-       {
-               .id             = V4L2_CID_TRANS_TIME_MSEC,
-               .type           = V4L2_CTRL_TYPE_INTEGER,
-               .name           = "Transaction time (msec)",
-               .minimum        = 1,
-               .maximum        = 10000,
-               .step           = 100,
-               .default_value  = 1000,
-               .flags          = 0,
-       }, {
-               .id             = V4L2_CID_TRANS_NUM_BUFS,
-               .type           = V4L2_CTRL_TYPE_INTEGER,
-               .name           = "Buffers per transaction",
-               .minimum        = 1,
-               .maximum        = MEM2MEM_DEF_NUM_BUFS,
-               .step           = 1,
-               .default_value  = 1,
-               .flags          = 0,
-       },
-};
-
-#define NUM_FORMATS ARRAY_SIZE(formats)
+#define V4L2_CID_TRANS_TIME_MSEC       (V4L2_CID_USER_BASE + 0x1000)
+#define V4L2_CID_TRANS_NUM_BUFS                (V4L2_CID_USER_BASE + 0x1001)
 
 static struct m2mtest_fmt *find_format(struct v4l2_format *f)
 {
@@ -168,8 +152,11 @@ struct m2mtest_dev {
 };
 
 struct m2mtest_ctx {
+       struct v4l2_fh          fh;
        struct m2mtest_dev      *dev;
 
+       struct v4l2_ctrl_handler hdl;
+
        /* Processed buffers in this transaction */
        u8                      num_processed;
 
@@ -181,12 +168,22 @@ struct m2mtest_ctx {
        /* Abort requested by m2m */
        int                     aborting;
 
+       /* Processing mode */
+       int                     mode;
+
+       enum v4l2_colorspace    colorspace;
+
        struct v4l2_m2m_ctx     *m2m_ctx;
 
        /* Source and destination queue data */
        struct m2mtest_q_data   q_data[2];
 };
 
+static inline struct m2mtest_ctx *file2ctx(struct file *file)
+{
+       return container_of(file->private_data, struct m2mtest_ctx, fh);
+}
+
 static struct m2mtest_q_data *get_q_data(struct m2mtest_ctx *ctx,
                                         enum v4l2_buf_type type)
 {
@@ -202,18 +199,6 @@ static struct m2mtest_q_data *get_q_data(struct m2mtest_ctx *ctx,
 }
 
 
-static struct v4l2_queryctrl *get_ctrl(int id)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(m2mtest_ctrls); ++i) {
-               if (id == m2mtest_ctrls[i].id)
-                       return &m2mtest_ctrls[i];
-       }
-
-       return NULL;
-}
-
 static int device_process(struct m2mtest_ctx *ctx,
                          struct vb2_buffer *in_vb,
                          struct vb2_buffer *out_vb)
@@ -249,19 +234,84 @@ static int device_process(struct m2mtest_ctx *ctx,
        bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;
        w = 0;
 
-       for (y = 0; y < height; ++y) {
-               for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
-                       if (w & 0x1) {
-                               for (x = 0; x < tile_w; ++x)
-                                       *p_out++ = *p_in++ + MEM2MEM_COLOR_STEP;
-                       } else {
-                               for (x = 0; x < tile_w; ++x)
-                                       *p_out++ = *p_in++ - MEM2MEM_COLOR_STEP;
+       switch (ctx->mode) {
+       case MEM2MEM_HFLIP | MEM2MEM_VFLIP:
+               p_out += bytesperline * height - bytes_left;
+               for (y = 0; y < height; ++y) {
+                       for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
+                               if (w & 0x1) {
+                                       for (x = 0; x < tile_w; ++x)
+                                               *--p_out = *p_in++ +
+                                                       MEM2MEM_COLOR_STEP;
+                               } else {
+                                       for (x = 0; x < tile_w; ++x)
+                                               *--p_out = *p_in++ -
+                                                       MEM2MEM_COLOR_STEP;
+                               }
+                               ++w;
                        }
-                       ++w;
+                       p_in += bytes_left;
+                       p_out -= bytes_left;
+               }
+               break;
+
+       case MEM2MEM_HFLIP:
+               for (y = 0; y < height; ++y) {
+                       p_out += MEM2MEM_NUM_TILES * tile_w;
+                       for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
+                               if (w & 0x01) {
+                                       for (x = 0; x < tile_w; ++x)
+                                               *--p_out = *p_in++ +
+                                                       MEM2MEM_COLOR_STEP;
+                               } else {
+                                       for (x = 0; x < tile_w; ++x)
+                                               *--p_out = *p_in++ -
+                                                       MEM2MEM_COLOR_STEP;
+                               }
+                               ++w;
+                       }
+                       p_in += bytes_left;
+                       p_out += bytesperline;
+               }
+               break;
+
+       case MEM2MEM_VFLIP:
+               p_out += bytesperline * (height - 1);
+               for (y = 0; y < height; ++y) {
+                       for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
+                               if (w & 0x1) {
+                                       for (x = 0; x < tile_w; ++x)
+                                               *p_out++ = *p_in++ +
+                                                       MEM2MEM_COLOR_STEP;
+                               } else {
+                                       for (x = 0; x < tile_w; ++x)
+                                               *p_out++ = *p_in++ -
+                                                       MEM2MEM_COLOR_STEP;
+                               }
+                               ++w;
+                       }
+                       p_in += bytes_left;
+                       p_out += bytes_left - 2 * bytesperline;
+               }
+               break;
+
+       default:
+               for (y = 0; y < height; ++y) {
+                       for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
+                               if (w & 0x1) {
+                                       for (x = 0; x < tile_w; ++x)
+                                               *p_out++ = *p_in++ +
+                                                       MEM2MEM_COLOR_STEP;
+                               } else {
+                                       for (x = 0; x < tile_w; ++x)
+                                               *p_out++ = *p_in++ -
+                                                       MEM2MEM_COLOR_STEP;
+                               }
+                               ++w;
+                       }
+                       p_in += bytes_left;
+                       p_out += bytes_left;
                }
-               p_in += bytes_left;
-               p_out += bytes_left;
        }
 
        return 0;
@@ -380,10 +430,9 @@ static int vidioc_querycap(struct file *file, void *priv,
 {
        strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
        strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
-       cap->bus_info[0] = 0;
-       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
-                         | V4L2_CAP_STREAMING;
-
+       strlcpy(cap->bus_info, MEM2MEM_NAME, sizeof(cap->bus_info));
+       cap->capabilities = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
        return 0;
 }
 
@@ -446,6 +495,7 @@ static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
        f->fmt.pix.pixelformat  = q_data->fmt->fourcc;
        f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3;
        f->fmt.pix.sizeimage    = q_data->sizeimage;
+       f->fmt.pix.colorspace   = ctx->colorspace;
 
        return 0;
 }
@@ -453,13 +503,13 @@ static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
 static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
-       return vidioc_g_fmt(priv, f);
+       return vidioc_g_fmt(file2ctx(file), f);
 }
 
 static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
-       return vidioc_g_fmt(priv, f);
+       return vidioc_g_fmt(file2ctx(file), f);
 }
 
 static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt)
@@ -498,7 +548,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                                  struct v4l2_format *f)
 {
        struct m2mtest_fmt *fmt;
-       struct m2mtest_ctx *ctx = priv;
+       struct m2mtest_ctx *ctx = file2ctx(file);
 
        fmt = find_format(f);
        if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
@@ -507,6 +557,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                         f->fmt.pix.pixelformat);
                return -EINVAL;
        }
+       f->fmt.pix.colorspace = ctx->colorspace;
 
        return vidioc_try_fmt(f, fmt);
 }
@@ -515,7 +566,7 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
                                  struct v4l2_format *f)
 {
        struct m2mtest_fmt *fmt;
-       struct m2mtest_ctx *ctx = priv;
+       struct m2mtest_ctx *ctx = file2ctx(file);
 
        fmt = find_format(f);
        if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
@@ -524,6 +575,8 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
                         f->fmt.pix.pixelformat);
                return -EINVAL;
        }
+       if (!f->fmt.pix.colorspace)
+               f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
 
        return vidioc_try_fmt(f, fmt);
 }
@@ -568,25 +621,29 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
        if (ret)
                return ret;
 
-       return vidioc_s_fmt(priv, f);
+       return vidioc_s_fmt(file2ctx(file), f);
 }
 
 static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
+       struct m2mtest_ctx *ctx = file2ctx(file);
        int ret;
 
        ret = vidioc_try_fmt_vid_out(file, priv, f);
        if (ret)
                return ret;
 
-       return vidioc_s_fmt(priv, f);
+       ret = vidioc_s_fmt(file2ctx(file), f);
+       if (!ret)
+               ctx->colorspace = f->fmt.pix.colorspace;
+       return ret;
 }
 
 static int vidioc_reqbufs(struct file *file, void *priv,
                          struct v4l2_requestbuffers *reqbufs)
 {
-       struct m2mtest_ctx *ctx = priv;
+       struct m2mtest_ctx *ctx = file2ctx(file);
 
        return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
 }
@@ -594,21 +651,21 @@ static int vidioc_reqbufs(struct file *file, void *priv,
 static int vidioc_querybuf(struct file *file, void *priv,
                           struct v4l2_buffer *buf)
 {
-       struct m2mtest_ctx *ctx = priv;
+       struct m2mtest_ctx *ctx = file2ctx(file);
 
        return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
 }
 
 static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
 {
-       struct m2mtest_ctx *ctx = priv;
+       struct m2mtest_ctx *ctx = file2ctx(file);
 
        return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
 }
 
 static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
 {
-       struct m2mtest_ctx *ctx = priv;
+       struct m2mtest_ctx *ctx = file2ctx(file);
 
        return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
 }
@@ -616,7 +673,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
 static int vidioc_streamon(struct file *file, void *priv,
                           enum v4l2_buf_type type)
 {
-       struct m2mtest_ctx *ctx = priv;
+       struct m2mtest_ctx *ctx = file2ctx(file);
 
        return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
 }
@@ -624,79 +681,37 @@ static int vidioc_streamon(struct file *file, void *priv,
 static int vidioc_streamoff(struct file *file, void *priv,
                            enum v4l2_buf_type type)
 {
-       struct m2mtest_ctx *ctx = priv;
+       struct m2mtest_ctx *ctx = file2ctx(file);
 
        return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-                           struct v4l2_queryctrl *qc)
+static int m2mtest_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct v4l2_queryctrl *c;
-
-       c = get_ctrl(qc->id);
-       if (!c)
-               return -EINVAL;
-
-       *qc = *c;
-       return 0;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-                        struct v4l2_control *ctrl)
-{
-       struct m2mtest_ctx *ctx = priv;
+       struct m2mtest_ctx *ctx =
+               container_of(ctrl->handler, struct m2mtest_ctx, hdl);
 
        switch (ctrl->id) {
-       case V4L2_CID_TRANS_TIME_MSEC:
-               ctrl->value = ctx->transtime;
+       case V4L2_CID_HFLIP:
+               if (ctrl->val)
+                       ctx->mode |= MEM2MEM_HFLIP;
+               else
+                       ctx->mode &= ~MEM2MEM_HFLIP;
                break;
 
-       case V4L2_CID_TRANS_NUM_BUFS:
-               ctrl->value = ctx->translen;
+       case V4L2_CID_VFLIP:
+               if (ctrl->val)
+                       ctx->mode |= MEM2MEM_VFLIP;
+               else
+                       ctx->mode &= ~MEM2MEM_VFLIP;
                break;
 
-       default:
-               v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int check_ctrl_val(struct m2mtest_ctx *ctx, struct v4l2_control *ctrl)
-{
-       struct v4l2_queryctrl *c;
-
-       c = get_ctrl(ctrl->id);
-       if (!c)
-               return -EINVAL;
-
-       if (ctrl->value < c->minimum || ctrl->value > c->maximum) {
-               v4l2_err(&ctx->dev->v4l2_dev, "Value out of range\n");
-               return -ERANGE;
-       }
-
-       return 0;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-                        struct v4l2_control *ctrl)
-{
-       struct m2mtest_ctx *ctx = priv;
-       int ret = 0;
-
-       ret = check_ctrl_val(ctx, ctrl);
-       if (ret != 0)
-               return ret;
-
-       switch (ctrl->id) {
        case V4L2_CID_TRANS_TIME_MSEC:
-               ctx->transtime = ctrl->value;
+               ctx->transtime = ctrl->val;
                break;
 
        case V4L2_CID_TRANS_NUM_BUFS:
-               ctx->translen = ctrl->value;
+               ctx->translen = ctrl->val;
                break;
 
        default:
@@ -707,6 +722,10 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
        return 0;
 }
 
+static const struct v4l2_ctrl_ops m2mtest_ctrl_ops = {
+       .s_ctrl = m2mtest_s_ctrl,
+};
+
 
 static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
        .vidioc_querycap        = vidioc_querycap,
@@ -729,10 +748,8 @@ static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
 
        .vidioc_streamon        = vidioc_streamon,
        .vidioc_streamoff       = vidioc_streamoff,
-
-       .vidioc_queryctrl       = vidioc_queryctrl,
-       .vidioc_g_ctrl          = vidioc_g_ctrl,
-       .vidioc_s_ctrl          = vidioc_s_ctrl,
+       .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
 };
 
 
@@ -844,6 +861,28 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
        return vb2_queue_init(dst_vq);
 }
 
+static const struct v4l2_ctrl_config m2mtest_ctrl_trans_time_msec = {
+       .ops = &m2mtest_ctrl_ops,
+       .id = V4L2_CID_TRANS_TIME_MSEC,
+       .name = "Transaction Time (msec)",
+       .type = V4L2_CTRL_TYPE_INTEGER,
+       .def = 1001,
+       .min = 1,
+       .max = 10001,
+       .step = 100,
+};
+
+static const struct v4l2_ctrl_config m2mtest_ctrl_trans_num_bufs = {
+       .ops = &m2mtest_ctrl_ops,
+       .id = V4L2_CID_TRANS_NUM_BUFS,
+       .name = "Buffers Per Transaction",
+       .type = V4L2_CTRL_TYPE_INTEGER,
+       .def = 1,
+       .min = 1,
+       .max = MEM2MEM_DEF_NUM_BUFS,
+       .step = 1,
+};
+
 /*
  * File operations
  */
@@ -851,29 +890,51 @@ static int m2mtest_open(struct file *file)
 {
        struct m2mtest_dev *dev = video_drvdata(file);
        struct m2mtest_ctx *ctx = NULL;
+       struct v4l2_ctrl_handler *hdl;
 
        ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
        if (!ctx)
                return -ENOMEM;
 
-       file->private_data = ctx;
+       v4l2_fh_init(&ctx->fh, video_devdata(file));
+       file->private_data = &ctx->fh;
        ctx->dev = dev;
-       ctx->translen = MEM2MEM_DEF_TRANSLEN;
-       ctx->transtime = MEM2MEM_DEF_TRANSTIME;
-       ctx->num_processed = 0;
+       hdl = &ctx->hdl;
+       v4l2_ctrl_handler_init(hdl, 4);
+       v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+       v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+       v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_time_msec, NULL);
+       v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_num_bufs, NULL);
+       if (hdl->error) {
+               int err = hdl->error;
+
+               v4l2_ctrl_handler_free(hdl);
+               return err;
+       }
+       ctx->fh.ctrl_handler = hdl;
+       v4l2_ctrl_handler_setup(hdl);
 
        ctx->q_data[V4L2_M2M_SRC].fmt = &formats[0];
-       ctx->q_data[V4L2_M2M_DST].fmt = &formats[0];
+       ctx->q_data[V4L2_M2M_SRC].width = 640;
+       ctx->q_data[V4L2_M2M_SRC].height = 480;
+       ctx->q_data[V4L2_M2M_SRC].sizeimage =
+               ctx->q_data[V4L2_M2M_SRC].width *
+               ctx->q_data[V4L2_M2M_SRC].height *
+               (ctx->q_data[V4L2_M2M_SRC].fmt->depth >> 3);
+       ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC];
+       ctx->colorspace = V4L2_COLORSPACE_REC709;
 
        ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
 
        if (IS_ERR(ctx->m2m_ctx)) {
                int ret = PTR_ERR(ctx->m2m_ctx);
 
+               v4l2_ctrl_handler_free(hdl);
                kfree(ctx);
                return ret;
        }
 
+       v4l2_fh_add(&ctx->fh);
        atomic_inc(&dev->num_inst);
 
        dprintk(dev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);
@@ -884,10 +945,13 @@ static int m2mtest_open(struct file *file)
 static int m2mtest_release(struct file *file)
 {
        struct m2mtest_dev *dev = video_drvdata(file);
-       struct m2mtest_ctx *ctx = file->private_data;
+       struct m2mtest_ctx *ctx = file2ctx(file);
 
        dprintk(dev, "Releasing instance %p\n", ctx);
 
+       v4l2_fh_del(&ctx->fh);
+       v4l2_fh_exit(&ctx->fh);
+       v4l2_ctrl_handler_free(&ctx->hdl);
        v4l2_m2m_ctx_release(ctx->m2m_ctx);
        kfree(ctx);
 
@@ -899,14 +963,14 @@ static int m2mtest_release(struct file *file)
 static unsigned int m2mtest_poll(struct file *file,
                                 struct poll_table_struct *wait)
 {
-       struct m2mtest_ctx *ctx = file->private_data;
+       struct m2mtest_ctx *ctx = file2ctx(file);
 
        return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
 }
 
 static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma)
 {
-       struct m2mtest_ctx *ctx = file->private_data;
+       struct m2mtest_ctx *ctx = file2ctx(file);
 
        return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
 }
index 7e648183f15778b3db83b904b76c63c899e30899..00583f5fd26bbc26c6dcd9861ff4fcf774118597 100644 (file)
@@ -22,7 +22,7 @@
 
 /*
  * mt9m001 i2c address 0x5d
- * The platform has to define ctruct i2c_board_info objects and link to them
+ * The platform has to define struct i2c_board_info objects and link to them
  * from struct soc_camera_link
  */
 
index 3c1e626139b79e06353062e9e65c7e279e3d95a7..445359c961136cd1595e59138c3e3a3b24ee90e2 100644 (file)
@@ -688,11 +688,17 @@ static const struct v4l2_subdev_ops mt9m032_ops = {
 static int mt9m032_probe(struct i2c_client *client,
                         const struct i2c_device_id *devid)
 {
+       struct mt9m032_platform_data *pdata = client->dev.platform_data;
        struct i2c_adapter *adapter = client->adapter;
        struct mt9m032 *sensor;
        int chip_version;
        int ret;
 
+       if (pdata == NULL) {
+               dev_err(&client->dev, "No platform data\n");
+               return -EINVAL;
+       }
+
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
                dev_warn(&client->dev,
                         "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
@@ -708,7 +714,7 @@ static int mt9m032_probe(struct i2c_client *client,
 
        mutex_init(&sensor->lock);
 
-       sensor->pdata = client->dev.platform_data;
+       sensor->pdata = pdata;
 
        v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops);
        sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
@@ -738,7 +744,7 @@ static int mt9m032_probe(struct i2c_client *client,
        sensor->format.field = V4L2_FIELD_NONE;
        sensor->format.colorspace = V4L2_COLORSPACE_SRGB;
 
-       v4l2_ctrl_handler_init(&sensor->ctrls, 4);
+       v4l2_ctrl_handler_init(&sensor->ctrls, 5);
 
        v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
                          V4L2_CID_GAIN, 0, 127, 1, 64);
@@ -754,6 +760,9 @@ static int mt9m032_probe(struct i2c_client *client,
                          V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN,
                          MT9M032_SHUTTER_WIDTH_MAX, 1,
                          MT9M032_SHUTTER_WIDTH_DEF);
+       v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
+                         V4L2_CID_PIXEL_RATE, pdata->pix_clock,
+                         pdata->pix_clock, 1, pdata->pix_clock);
 
        if (sensor->ctrls.error) {
                ret = sensor->ctrls.error;
index b0c5299643290a1e417d1cf75289185ff10f3ba7..863d722dda06ebf25440c0f2fbb491c7eb3d2ce9 100644 (file)
@@ -214,7 +214,6 @@ struct mt9m111 {
        int power_count;
        const struct mt9m111_datafmt *fmt;
        int lastpage;   /* PageMap cache value */
-       unsigned char datawidth;
 };
 
 /* Find a data format by a pixel code */
index 8f061d9ac4436e0c62d05a888f75b89c4d95c669..3be537ef22d2f4138067ca9d09d48ffe65fb7cda 100644 (file)
@@ -950,7 +950,7 @@ static int mt9p031_probe(struct i2c_client *client,
        mt9p031->model = did->driver_data;
        mt9p031->reset = -1;
 
-       v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 4);
+       v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 5);
 
        v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
                          V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN,
@@ -963,6 +963,9 @@ static int mt9p031_probe(struct i2c_client *client,
                          V4L2_CID_HFLIP, 0, 1, 1, 0);
        v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
                          V4L2_CID_VFLIP, 0, 1, 1, 0);
+       v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+                         V4L2_CID_PIXEL_RATE, pdata->target_freq,
+                         pdata->target_freq, 1, pdata->target_freq);
 
        for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i)
                v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL);
index 49ca3cbfc6f12da882a47fc1352aee443a59c61b..6d343adf891d1a5497b4cb4a4ecebaaf9328d1d0 100644 (file)
@@ -691,7 +691,7 @@ static int mt9t001_video_probe(struct i2c_client *client)
                return ret;
 
        /* Configure the pixel clock polarity */
-       if (pdata && pdata->clk_pol) {
+       if (pdata->clk_pol) {
                ret  = mt9t001_write(client, MT9T001_PIXEL_CLOCK,
                                     MT9T001_PIXEL_CLOCK_INVERT);
                if (ret < 0)
@@ -715,10 +715,16 @@ static int mt9t001_video_probe(struct i2c_client *client)
 static int mt9t001_probe(struct i2c_client *client,
                         const struct i2c_device_id *did)
 {
+       struct mt9t001_platform_data *pdata = client->dev.platform_data;
        struct mt9t001 *mt9t001;
        unsigned int i;
        int ret;
 
+       if (pdata == NULL) {
+               dev_err(&client->dev, "No platform data\n");
+               return -EINVAL;
+       }
+
        if (!i2c_check_functionality(client->adapter,
                                     I2C_FUNC_SMBUS_WORD_DATA)) {
                dev_warn(&client->adapter->dev,
@@ -735,7 +741,7 @@ static int mt9t001_probe(struct i2c_client *client,
                return -ENOMEM;
 
        v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) +
-                                               ARRAY_SIZE(mt9t001_gains) + 2);
+                                               ARRAY_SIZE(mt9t001_gains) + 3);
 
        v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops,
                          V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN,
@@ -743,6 +749,9 @@ static int mt9t001_probe(struct i2c_client *client,
                          MT9T001_SHUTTER_WIDTH_DEF);
        v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops,
                          V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1);
+       v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops,
+                         V4L2_CID_PIXEL_RATE, pdata->ext_clk, pdata->ext_clk,
+                         1, pdata->ext_clk);
 
        for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i)
                v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL);
index bf63417adb8fa5514c7ca22b870795a107ae6e03..72479247522a8fe923235a88b342145ed0d39cf3 100644 (file)
@@ -23,7 +23,7 @@
 
 /*
  * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c
- * The platform has to define ctruct i2c_board_info objects and link to them
+ * The platform has to define struct i2c_board_info objects and link to them
  * from struct soc_camera_link
  */
 
index 0bd5815de369413db01e4a023094591a18cbc91c..5f8a6f5b98f91c3af4e1bdbd06654e7b496d8d65 100644 (file)
@@ -396,9 +396,13 @@ static int vidioc_querycap(struct file *file, void *priv,
 {
        strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
        strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
-       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
-                         | V4L2_CAP_STREAMING;
-
+       /*
+        * This is only a mem-to-mem video device. The capture and output
+        * device capability flags are left only for backward compatibility
+        * and are scheduled for removal.
+        */
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
+                           V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
        return 0;
 }
 
index 7e32331b60fb0e8362b7cd50ea38680a59638336..f1220d3d4970d75fb2dec5c91ce77fc2f01f7aae 100644 (file)
@@ -2014,7 +2014,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
                return -EINVAL;
 
        switch (sel->target) {
-       case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+       case V4L2_SEL_TGT_CROP_BOUNDS:
                sel->r.left = 0;
                sel->r.top = 0;
                sel->r.width = INT_MAX;
@@ -2024,7 +2024,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
                ccdc_try_crop(ccdc, format, &sel->r);
                break;
 
-       case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+       case V4L2_SEL_TGT_CROP:
                sel->r = *__ccdc_get_crop(ccdc, fh, sel->which);
                break;
 
@@ -2052,7 +2052,7 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
        struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
        struct v4l2_mbus_framefmt *format;
 
-       if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL ||
+       if (sel->target != V4L2_SEL_TGT_CROP ||
            sel->pad != CCDC_PAD_SOURCE_OF)
                return -EINVAL;
 
@@ -2064,7 +2064,7 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
         * pad. If the KEEP_CONFIG flag is set, just return the current crop
         * rectangle.
         */
-       if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) {
+       if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) {
                sel->r = *__ccdc_get_crop(ccdc, fh, sel->which);
                return 0;
        }
index dd91da26f1b088f66bdb40b212fa9b1c84a46b5a..53f5a703e31abd218692a679cd4b0ccf9b16d98a 100644 (file)
@@ -1949,7 +1949,7 @@ static int preview_get_selection(struct v4l2_subdev *sd,
                return -EINVAL;
 
        switch (sel->target) {
-       case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+       case V4L2_SEL_TGT_CROP_BOUNDS:
                sel->r.left = 0;
                sel->r.top = 0;
                sel->r.width = INT_MAX;
@@ -1960,7 +1960,7 @@ static int preview_get_selection(struct v4l2_subdev *sd,
                preview_try_crop(prev, format, &sel->r);
                break;
 
-       case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+       case V4L2_SEL_TGT_CROP:
                sel->r = *__preview_get_crop(prev, fh, sel->which);
                break;
 
@@ -1988,7 +1988,7 @@ static int preview_set_selection(struct v4l2_subdev *sd,
        struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
        struct v4l2_mbus_framefmt *format;
 
-       if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL ||
+       if (sel->target != V4L2_SEL_TGT_CROP ||
            sel->pad != PREV_PAD_SINK)
                return -EINVAL;
 
@@ -2000,7 +2000,7 @@ static int preview_set_selection(struct v4l2_subdev *sd,
         * pad. If the KEEP_CONFIG flag is set, just return the current crop
         * rectangle.
         */
-       if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) {
+       if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) {
                sel->r = *__preview_get_crop(prev, fh, sel->which);
                return 0;
        }
index 14041c9c8643fdd85d9c295facd2ff54983e53d6..ae17d917f77b3657ef4553e26ea039768b9bc3e3 100644 (file)
@@ -1249,7 +1249,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd,
                                             sel->which);
 
        switch (sel->target) {
-       case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+       case V4L2_SEL_TGT_CROP_BOUNDS:
                sel->r.left = 0;
                sel->r.top = 0;
                sel->r.width = INT_MAX;
@@ -1259,7 +1259,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd,
                resizer_calc_ratios(res, &sel->r, format_source, &ratio);
                break;
 
-       case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+       case V4L2_SEL_TGT_CROP:
                sel->r = *__resizer_get_crop(res, fh, sel->which);
                resizer_calc_ratios(res, &sel->r, format_source, &ratio);
                break;
@@ -1293,7 +1293,7 @@ static int resizer_set_selection(struct v4l2_subdev *sd,
        struct v4l2_mbus_framefmt *format_sink, *format_source;
        struct resizer_ratio ratio;
 
-       if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL ||
+       if (sel->target != V4L2_SEL_TGT_CROP ||
            sel->pad != RESZ_PAD_SINK)
                return -EINVAL;
 
index 3c2c5d3bcc6b17363060701f18d432f496446d58..7c44d1fe3c87a8224823e2357c297abfa5b43968 100644 (file)
@@ -837,10 +837,8 @@ static int ov2640_g_fmt(struct v4l2_subdev *sd,
 
        if (!priv->win) {
                u32 width = W_SVGA, height = H_SVGA;
-               int ret = ov2640_set_params(client, &width, &height,
-                                           V4L2_MBUS_FMT_UYVY8_2X8);
-               if (ret < 0)
-                       return ret;
+               priv->win = ov2640_select_win(&width, &height);
+               priv->cfmt_code = V4L2_MBUS_FMT_UYVY8_2X8;
        }
 
        mf->width       = priv->win->width;
index 74e77d327ed864e03843898dfd4c22defc5d305c..6d79b89b860340dddaf2ae0c1f64f3d121ee1f2d 100644 (file)
@@ -880,15 +880,11 @@ static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
 static int ov772x_g_fmt(struct v4l2_subdev *sd,
                        struct v4l2_mbus_framefmt *mf)
 {
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
        struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
 
        if (!priv->win || !priv->cfmt) {
-               u32 width = VGA_WIDTH, height = VGA_HEIGHT;
-               int ret = ov772x_set_params(client, &width, &height,
-                                           V4L2_MBUS_FMT_YUYV8_2X8);
-               if (ret < 0)
-                       return ret;
+               priv->cfmt = &ov772x_cfmts[0];
+               priv->win = ov772x_select_win(VGA_WIDTH, VGA_HEIGHT);
        }
 
        mf->width       = priv->win->width;
index 23412debb36b30d6dada6f77a65278f7a6085b41..9ed4ba4236c47e4adf9920e7a88f974b513f3fc0 100644 (file)
@@ -605,6 +605,7 @@ static int ov9640_video_probe(struct i2c_client *client)
                devname         = "ov9640";
                priv->model     = V4L2_IDENT_OV9640;
                priv->revision  = 2;
+               break;
        case OV9640_V3:
                devname         = "ov9640";
                priv->model     = V4L2_IDENT_OV9640;
index b4c679b3fb0f7a052550a7d5a71ea42367e49ceb..77f9c92186f4f4da362152a1716a75f0751a84dc 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/ioport.h>
 #include <linux/init.h>
 #include <linux/mutex.h>
-#include <linux/slab.h>
 #include <linux/uaccess.h>
 #include <linux/isa.h>
 #include <asm/io.h>
index f9b6001e1dd71dba3f901badcdc120643301d3f5..25e412ecad2cf7bfb05087f2b4f6639485627690 100644 (file)
@@ -1,7 +1,6 @@
 config VIDEO_PVRUSB2
        tristate "Hauppauge WinTV-PVR USB2 support"
        depends on VIDEO_V4L2 && I2C
-       depends on VIDEO_MEDIA  # Avoids pvrusb = Y / DVB = M
        select VIDEO_TUNER
        select VIDEO_TVEEPROM
        select VIDEO_CX2341X
index 7bddfaeeafc30a775ac5e3319b958e32fe0df8fa..f344aed32a936cdcfcfd93c45c4ae377c79bdb7d 100644 (file)
@@ -226,13 +226,11 @@ static int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi)
        struct v4l2_input tmp;
        unsigned int cnt;
        int val;
-       int ret;
 
        cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
 
        memset(&tmp, 0, sizeof(tmp));
        tmp.index = vi->index;
-       ret = 0;
        if (vi->index >= fh->input_cnt)
                return -EINVAL;
        val = fh->input_map[vi->index];
@@ -556,9 +554,7 @@ static int pvr2_queryctrl(struct file *file, void *priv,
        struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
        struct pvr2_ctrl *cptr;
        int val;
-       int ret;
 
-       ret = 0;
        if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
                cptr = pvr2_hdw_get_ctrl_nextv4l(
                                hdw, (vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL));
@@ -705,11 +701,9 @@ static int pvr2_try_ext_ctrls(struct file *file, void *priv,
        struct v4l2_ext_control *ctrl;
        struct pvr2_ctrl *pctl;
        unsigned int idx;
-       int ret;
 
        /* For the moment just validate that the requested control
           actually exists. */
-       ret = 0;
        for (idx = 0; idx < ctls->count; idx++) {
                ctrl = ctls->controls + idx;
                pctl = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id);
@@ -770,12 +764,10 @@ static int pvr2_s_crop(struct file *file, void *priv, struct v4l2_crop *crop)
 {
        struct pvr2_v4l2_fh *fh = file->private_data;
        struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
-       struct v4l2_cropcap cap;
        int ret;
 
        if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
-       cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = pvr2_ctrl_set_value(
                        pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL),
                        crop->c.left);
@@ -965,7 +957,7 @@ static long pvr2_v4l2_ioctl(struct file *file,
        long ret = -EINVAL;
 
        if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL)
-               v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), cmd);
+               v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd);
 
        if (!pvr2_hdw_dev_ok(hdw)) {
                pvr2_trace(PVR2_TRACE_ERROR_LEGS,
@@ -998,7 +990,7 @@ static long pvr2_v4l2_ioctl(struct file *file,
                                pvr2_trace(PVR2_TRACE_V4LIOCTL,
                                           "pvr2_v4l2_do_ioctl failure, ret=%ld"
                                           " command was:", ret);
-                               v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),
+                               v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw),
                                                cmd);
                        }
                }
index ec4e2ef54e6570dc77121b0a7e650a35d2eb406e..de7c7ba99ef494bd60bf9ae659c0d1e72b80608b 100644 (file)
@@ -136,19 +136,13 @@ static int leds[2] = { 100, 0 };
 
 /***/
 
-static int pwc_video_close(struct file *file);
-static ssize_t pwc_video_read(struct file *file, char __user *buf,
-                         size_t count, loff_t *ppos);
-static unsigned int pwc_video_poll(struct file *file, poll_table *wait);
-static int  pwc_video_mmap(struct file *file, struct vm_area_struct *vma);
-
 static const struct v4l2_file_operations pwc_fops = {
        .owner =        THIS_MODULE,
        .open =         v4l2_fh_open,
-       .release =      pwc_video_close,
-       .read =         pwc_video_read,
-       .poll =         pwc_video_poll,
-       .mmap =         pwc_video_mmap,
+       .release =      vb2_fop_release,
+       .read =         vb2_fop_read,
+       .poll =         vb2_fop_poll,
+       .mmap =         vb2_fop_mmap,
        .unlocked_ioctl = video_ioctl2,
 };
 static struct video_device pwc_template = {
@@ -562,17 +556,6 @@ static const char *pwc_sensor_type_to_string(unsigned int sensor_type)
 /***************************************************************************/
 /* Video4Linux functions */
 
-int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file)
-{
-       if (pdev->capt_file != NULL &&
-           pdev->capt_file != file)
-               return -EBUSY;
-
-       pdev->capt_file = file;
-
-       return 0;
-}
-
 static void pwc_video_release(struct v4l2_device *v)
 {
        struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
@@ -583,113 +566,6 @@ static void pwc_video_release(struct v4l2_device *v)
        kfree(pdev);
 }
 
-static int pwc_video_close(struct file *file)
-{
-       struct pwc_device *pdev = video_drvdata(file);
-
-       /*
-        * If we're still streaming vb2_queue_release will call stream_stop
-        * so we must take both the v4l2_lock and the vb_queue_lock.
-        */
-       if (mutex_lock_interruptible(&pdev->v4l2_lock))
-               return -ERESTARTSYS;
-       if (mutex_lock_interruptible(&pdev->vb_queue_lock)) {
-               mutex_unlock(&pdev->v4l2_lock);
-               return -ERESTARTSYS;
-       }
-
-       if (pdev->capt_file == file) {
-               vb2_queue_release(&pdev->vb_queue);
-               pdev->capt_file = NULL;
-       }
-
-       mutex_unlock(&pdev->vb_queue_lock);
-       mutex_unlock(&pdev->v4l2_lock);
-
-       return v4l2_fh_release(file);
-}
-
-static ssize_t pwc_video_read(struct file *file, char __user *buf,
-                             size_t count, loff_t *ppos)
-{
-       struct pwc_device *pdev = video_drvdata(file);
-       int lock_v4l2 = 0;
-       ssize_t ret;
-
-       if (mutex_lock_interruptible(&pdev->vb_queue_lock))
-               return -ERESTARTSYS;
-
-       ret = pwc_test_n_set_capt_file(pdev, file);
-       if (ret)
-               goto out;
-
-       /* stream_start will get called so we must take the v4l2_lock */
-       if (pdev->vb_queue.fileio == NULL)
-               lock_v4l2 = 1;
-
-       /* Use try_lock, since we're taking the locks in the *wrong* order! */
-       if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock)) {
-               ret = -ERESTARTSYS;
-               goto out;
-       }
-       ret = vb2_read(&pdev->vb_queue, buf, count, ppos,
-                      file->f_flags & O_NONBLOCK);
-       if (lock_v4l2)
-               mutex_unlock(&pdev->v4l2_lock);
-out:
-       mutex_unlock(&pdev->vb_queue_lock);
-       return ret;
-}
-
-static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
-{
-       struct pwc_device *pdev = video_drvdata(file);
-       struct vb2_queue *q = &pdev->vb_queue;
-       unsigned long req_events = poll_requested_events(wait);
-       unsigned int ret = POLL_ERR;
-       int lock_v4l2 = 0;
-
-       if (mutex_lock_interruptible(&pdev->vb_queue_lock))
-               return POLL_ERR;
-
-       /* Will this start fileio and thus call start_stream? */
-       if ((req_events & (POLLIN | POLLRDNORM)) &&
-           q->num_buffers == 0 && !q->streaming && q->fileio == NULL) {
-               if (pwc_test_n_set_capt_file(pdev, file))
-                       goto out;
-               lock_v4l2 = 1;
-       }
-
-       /* Use try_lock, since we're taking the locks in the *wrong* order! */
-       if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock))
-               goto out;
-       ret = vb2_poll(&pdev->vb_queue, file, wait);
-       if (lock_v4l2)
-               mutex_unlock(&pdev->v4l2_lock);
-
-out:
-       if (!pdev->udev)
-               ret |= POLLHUP;
-       mutex_unlock(&pdev->vb_queue_lock);
-       return ret;
-}
-
-static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
-{
-       struct pwc_device *pdev = video_drvdata(file);
-       int ret;
-
-       if (mutex_lock_interruptible(&pdev->vb_queue_lock))
-               return -ERESTARTSYS;
-
-       ret = pwc_test_n_set_capt_file(pdev, file);
-       if (ret == 0)
-               ret = vb2_mmap(&pdev->vb_queue, vma);
-
-       mutex_unlock(&pdev->vb_queue_lock);
-       return ret;
-}
-
 /***************************************************************************/
 /* Videobuf2 operations */
 
@@ -782,6 +658,8 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
        if (!pdev->udev)
                return -ENODEV;
 
+       if (mutex_lock_interruptible(&pdev->v4l2_lock))
+               return -ERESTARTSYS;
        /* Turn on camera and set LEDS on */
        pwc_camera_power(pdev, 1);
        pwc_set_leds(pdev, leds[0], leds[1]);
@@ -794,6 +672,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
                /* And cleanup any queued bufs!! */
                pwc_cleanup_queued_bufs(pdev);
        }
+       mutex_unlock(&pdev->v4l2_lock);
 
        return r;
 }
@@ -802,6 +681,8 @@ static int stop_streaming(struct vb2_queue *vq)
 {
        struct pwc_device *pdev = vb2_get_drv_priv(vq);
 
+       if (mutex_lock_interruptible(&pdev->v4l2_lock))
+               return -ERESTARTSYS;
        if (pdev->udev) {
                pwc_set_leds(pdev, 0, 0);
                pwc_camera_power(pdev, 0);
@@ -809,22 +690,11 @@ static int stop_streaming(struct vb2_queue *vq)
        }
 
        pwc_cleanup_queued_bufs(pdev);
+       mutex_unlock(&pdev->v4l2_lock);
 
        return 0;
 }
 
-static void wait_prepare(struct vb2_queue *vq)
-{
-       struct pwc_device *pdev = vb2_get_drv_priv(vq);
-       mutex_unlock(&pdev->vb_queue_lock);
-}
-
-static void wait_finish(struct vb2_queue *vq)
-{
-       struct pwc_device *pdev = vb2_get_drv_priv(vq);
-       mutex_lock(&pdev->vb_queue_lock);
-}
-
 static struct vb2_ops pwc_vb_queue_ops = {
        .queue_setup            = queue_setup,
        .buf_init               = buffer_init,
@@ -834,8 +704,8 @@ static struct vb2_ops pwc_vb_queue_ops = {
        .buf_queue              = buffer_queue,
        .start_streaming        = start_streaming,
        .stop_streaming         = stop_streaming,
-       .wait_prepare           = wait_prepare,
-       .wait_finish            = wait_finish,
+       .wait_prepare           = vb2_ops_wait_prepare,
+       .wait_finish            = vb2_ops_wait_finish,
 };
 
 /***************************************************************************/
@@ -1136,6 +1006,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
        /* Init video_device structure */
        memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template));
        strcpy(pdev->vdev.name, name);
+       pdev->vdev.queue = &pdev->vb_queue;
+       pdev->vdev.queue->lock = &pdev->vb_queue_lock;
        set_bit(V4L2_FL_USE_FH_PRIO, &pdev->vdev.flags);
        video_set_drvdata(&pdev->vdev, pdev);
 
@@ -1190,15 +1062,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
        pdev->vdev.v4l2_dev = &pdev->v4l2_dev;
        pdev->vdev.lock = &pdev->v4l2_lock;
 
-       /*
-        * Don't take v4l2_lock for these ioctls. This improves latency if
-        * v4l2_lock is taken for a long time, e.g. when changing a control
-        * value, and a new frame is ready to be dequeued.
-        */
-       v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_DQBUF);
-       v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_QBUF);
-       v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_QUERYBUF);
-
        rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1);
        if (rc < 0) {
                PWC_ERROR("Failed to register as video device (%d).\n", rc);
@@ -1253,20 +1116,18 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
        struct v4l2_device *v = usb_get_intfdata(intf);
        struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
 
-       mutex_lock(&pdev->v4l2_lock);
-
        mutex_lock(&pdev->vb_queue_lock);
+       mutex_lock(&pdev->v4l2_lock);
        /* No need to keep the urbs around after disconnection */
        if (pdev->vb_queue.streaming)
                pwc_isoc_cleanup(pdev);
        pdev->udev = NULL;
        pwc_cleanup_queued_bufs(pdev);
-       mutex_unlock(&pdev->vb_queue_lock);
 
        v4l2_device_disconnect(&pdev->v4l2_dev);
        video_unregister_device(&pdev->vdev);
-
        mutex_unlock(&pdev->v4l2_lock);
+       mutex_unlock(pdev->vb_queue.lock);
 
 #ifdef CONFIG_USB_PWC_INPUT_EVDEV
        if (pdev->button_dev)
index c691e29cc36e69fc9ad4c0525eb0aba453a38609..545e9bbdeedef22540fa079d900218d0151530b0 100644 (file)
@@ -405,6 +405,7 @@ static void pwc_vidioc_fill_fmt(struct v4l2_format *f,
        f->fmt.pix.pixelformat  = pixfmt;
        f->fmt.pix.bytesperline = f->fmt.pix.width;
        f->fmt.pix.sizeimage    = f->fmt.pix.height * f->fmt.pix.width * 3 / 2;
+       f->fmt.pix.colorspace   = V4L2_COLORSPACE_SRGB;
        PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() "
                        "width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n",
                        f->fmt.pix.width,
@@ -468,17 +469,8 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
        if (ret < 0)
                return ret;
 
-       if (mutex_lock_interruptible(&pdev->vb_queue_lock))
-               return -ERESTARTSYS;
-
-       ret = pwc_test_n_set_capt_file(pdev, file);
-       if (ret)
-               goto leave;
-
-       if (pdev->vb_queue.streaming) {
-               ret = -EBUSY;
-               goto leave;
-       }
+       if (vb2_is_busy(&pdev->vb_queue))
+               return -EBUSY;
 
        pixelformat = f->fmt.pix.pixelformat;
 
@@ -496,8 +488,6 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
        PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret);
 
        pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt);
-leave:
-       mutex_unlock(&pdev->vb_queue_lock);
        return ret;
 }
 
@@ -508,10 +498,9 @@ static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap
        strcpy(cap->driver, PWC_NAME);
        strlcpy(cap->card, pdev->vdev.name, sizeof(cap->card));
        usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info));
-       cap->capabilities =
-               V4L2_CAP_VIDEO_CAPTURE  |
-               V4L2_CAP_STREAMING      |
-               V4L2_CAP_READWRITE;
+       cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+                                       V4L2_CAP_READWRITE;
+       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
        return 0;
 }
 
@@ -520,7 +509,8 @@ static int pwc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
        if (i->index)   /* Only one INPUT is supported */
                return -EINVAL;
 
-       strcpy(i->name, "usb");
+       strlcpy(i->name, "Camera", sizeof(i->name));
+       i->type = V4L2_INPUT_TYPE_CAMERA;
        return 0;
 }
 
@@ -933,104 +923,6 @@ static int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *
        return pwc_vidioc_try_fmt(pdev, f);
 }
 
-static int pwc_reqbufs(struct file *file, void *fh,
-                      struct v4l2_requestbuffers *rb)
-{
-       struct pwc_device *pdev = video_drvdata(file);
-       int ret;
-
-       if (mutex_lock_interruptible(&pdev->vb_queue_lock))
-               return -ERESTARTSYS;
-
-       ret = pwc_test_n_set_capt_file(pdev, file);
-       if (ret == 0)
-               ret = vb2_reqbufs(&pdev->vb_queue, rb);
-
-       mutex_unlock(&pdev->vb_queue_lock);
-       return ret;
-}
-
-static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
-{
-       struct pwc_device *pdev = video_drvdata(file);
-       int ret;
-
-       if (mutex_lock_interruptible(&pdev->vb_queue_lock))
-               return -ERESTARTSYS;
-
-       ret = pwc_test_n_set_capt_file(pdev, file);
-       if (ret == 0)
-               ret = vb2_querybuf(&pdev->vb_queue, buf);
-
-       mutex_unlock(&pdev->vb_queue_lock);
-       return ret;
-}
-
-static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
-{
-       struct pwc_device *pdev = video_drvdata(file);
-       int ret;
-
-       if (mutex_lock_interruptible(&pdev->vb_queue_lock))
-               return -ERESTARTSYS;
-
-       ret = pwc_test_n_set_capt_file(pdev, file);
-       if (ret == 0)
-               ret = vb2_qbuf(&pdev->vb_queue, buf);
-
-       mutex_unlock(&pdev->vb_queue_lock);
-       return ret;
-}
-
-static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
-{
-       struct pwc_device *pdev = video_drvdata(file);
-       int ret;
-
-       if (mutex_lock_interruptible(&pdev->vb_queue_lock))
-               return -ERESTARTSYS;
-
-       ret = pwc_test_n_set_capt_file(pdev, file);
-       if (ret == 0)
-               ret = vb2_dqbuf(&pdev->vb_queue, buf,
-                               file->f_flags & O_NONBLOCK);
-
-       mutex_unlock(&pdev->vb_queue_lock);
-       return ret;
-}
-
-static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
-{
-       struct pwc_device *pdev = video_drvdata(file);
-       int ret;
-
-       if (mutex_lock_interruptible(&pdev->vb_queue_lock))
-               return -ERESTARTSYS;
-
-       ret = pwc_test_n_set_capt_file(pdev, file);
-       if (ret == 0)
-               ret = vb2_streamon(&pdev->vb_queue, i);
-
-       mutex_unlock(&pdev->vb_queue_lock);
-       return ret;
-}
-
-static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
-{
-       struct pwc_device *pdev = video_drvdata(file);
-       int ret;
-
-       if (mutex_lock_interruptible(&pdev->vb_queue_lock))
-               return -ERESTARTSYS;
-
-       ret = pwc_test_n_set_capt_file(pdev, file);
-       if (ret == 0)
-               ret = vb2_streamoff(&pdev->vb_queue, i);
-
-       mutex_unlock(&pdev->vb_queue_lock);
-       return ret;
-}
-
 static int pwc_enum_framesizes(struct file *file, void *fh,
                                         struct v4l2_frmsizeenum *fsize)
 {
@@ -1112,32 +1004,27 @@ static int pwc_s_parm(struct file *file, void *fh,
        int compression = 0;
        int ret, fps;
 
-       if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
-           parm->parm.capture.timeperframe.numerator == 0)
+       if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
 
-       fps = parm->parm.capture.timeperframe.denominator /
-             parm->parm.capture.timeperframe.numerator;
-
-       if (mutex_lock_interruptible(&pdev->vb_queue_lock))
-               return -ERESTARTSYS;
+       /* If timeperframe == 0, then reset the framerate to the nominal value.
+          We pick a high framerate here, and let pwc_set_video_mode() figure
+          out the best match. */
+       if (parm->parm.capture.timeperframe.numerator == 0 ||
+           parm->parm.capture.timeperframe.denominator == 0)
+               fps = 30;
+       else
+               fps = parm->parm.capture.timeperframe.denominator /
+                     parm->parm.capture.timeperframe.numerator;
 
-       ret = pwc_test_n_set_capt_file(pdev, file);
-       if (ret)
-               goto leave;
-
-       if (pdev->vb_queue.streaming) {
-               ret = -EBUSY;
-               goto leave;
-       }
+       if (vb2_is_busy(&pdev->vb_queue))
+               return -EBUSY;
 
        ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt,
                                 fps, &compression, 0);
 
        pwc_g_parm(file, fh, parm);
 
-leave:
-       mutex_unlock(&pdev->vb_queue_lock);
        return ret;
 }
 
@@ -1150,12 +1037,12 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = {
        .vidioc_g_fmt_vid_cap               = pwc_g_fmt_vid_cap,
        .vidioc_s_fmt_vid_cap               = pwc_s_fmt_vid_cap,
        .vidioc_try_fmt_vid_cap             = pwc_try_fmt_vid_cap,
-       .vidioc_reqbufs                     = pwc_reqbufs,
-       .vidioc_querybuf                    = pwc_querybuf,
-       .vidioc_qbuf                        = pwc_qbuf,
-       .vidioc_dqbuf                       = pwc_dqbuf,
-       .vidioc_streamon                    = pwc_streamon,
-       .vidioc_streamoff                   = pwc_streamoff,
+       .vidioc_reqbufs                     = vb2_ioctl_reqbufs,
+       .vidioc_querybuf                    = vb2_ioctl_querybuf,
+       .vidioc_qbuf                        = vb2_ioctl_qbuf,
+       .vidioc_dqbuf                       = vb2_ioctl_dqbuf,
+       .vidioc_streamon                    = vb2_ioctl_streamon,
+       .vidioc_streamoff                   = vb2_ioctl_streamoff,
        .vidioc_log_status                  = v4l2_ctrl_log_status,
        .vidioc_enum_framesizes             = pwc_enum_framesizes,
        .vidioc_enum_frameintervals         = pwc_enum_frameintervals,
index d6b5b216b9d60d873922161fa90f6a021b7110b3..7a6a0d39c2c669777d8aa7cbd61ff57de4ff865c 100644 (file)
@@ -239,7 +239,6 @@ struct pwc_device
        int features;           /* feature bits */
 
        /*** Video data ***/
-       struct file *capt_file; /* file doing video capture */
        int vendpoint;          /* video isoc endpoint */
        int vcinterface;        /* video control interface */
        int valternate;         /* alternate interface needed */
@@ -355,8 +354,6 @@ struct pwc_device
 extern int pwc_trace;
 #endif
 
-int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file);
-
 /** Functions in pwc-misc.c */
 /* sizes in pixels */
 extern const int pwc_image_sizes[PSZ_MAX][2];
index 01c2179f052001350a68fd7a5ffa5e2ab5d52862..95007dda0c93e8ed8a2395d0dd63ba05f3064583 100644 (file)
@@ -2686,3 +2686,4 @@ MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
 MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(S2255_VERSION);
+MODULE_FIRMWARE(FIRMWARE_FILE_NAME);
index 725812aa0c3044f5ffa31190a734e75ffcc9c01b..8e413dd3c0b098a5811f87add3113efc5bd2844e 100644 (file)
@@ -480,48 +480,59 @@ static int fimc_capture_set_default_format(struct fimc_dev *fimc);
 static int fimc_capture_open(struct file *file)
 {
        struct fimc_dev *fimc = video_drvdata(file);
-       int ret;
+       int ret = -EBUSY;
 
        dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
 
+       if (mutex_lock_interruptible(&fimc->lock))
+               return -ERESTARTSYS;
+
        if (fimc_m2m_active(fimc))
-               return -EBUSY;
+               goto unlock;
 
        set_bit(ST_CAPT_BUSY, &fimc->state);
        ret = pm_runtime_get_sync(&fimc->pdev->dev);
        if (ret < 0)
-               return ret;
+               goto unlock;
 
        ret = v4l2_fh_open(file);
-       if (ret)
-               return ret;
-
-       if (++fimc->vid_cap.refcnt != 1)
-               return 0;
+       if (ret) {
+               pm_runtime_put(&fimc->pdev->dev);
+               goto unlock;
+       }
 
-       ret = fimc_pipeline_initialize(&fimc->pipeline,
+       if (++fimc->vid_cap.refcnt == 1) {
+               ret = fimc_pipeline_initialize(&fimc->pipeline,
                                       &fimc->vid_cap.vfd->entity, true);
-       if (ret < 0) {
-               clear_bit(ST_CAPT_BUSY, &fimc->state);
-               pm_runtime_put_sync(&fimc->pdev->dev);
-               fimc->vid_cap.refcnt--;
-               v4l2_fh_release(file);
-               return ret;
-       }
-       ret = fimc_capture_ctrls_create(fimc);
 
-       if (!ret && !fimc->vid_cap.user_subdev_api)
-               ret = fimc_capture_set_default_format(fimc);
+               if (!ret && !fimc->vid_cap.user_subdev_api)
+                       ret = fimc_capture_set_default_format(fimc);
 
+               if (!ret)
+                       ret = fimc_capture_ctrls_create(fimc);
+
+               if (ret < 0) {
+                       clear_bit(ST_CAPT_BUSY, &fimc->state);
+                       pm_runtime_put_sync(&fimc->pdev->dev);
+                       fimc->vid_cap.refcnt--;
+                       v4l2_fh_release(file);
+               }
+       }
+unlock:
+       mutex_unlock(&fimc->lock);
        return ret;
 }
 
 static int fimc_capture_close(struct file *file)
 {
        struct fimc_dev *fimc = video_drvdata(file);
+       int ret;
 
        dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
 
+       if (mutex_lock_interruptible(&fimc->lock))
+               return -ERESTARTSYS;
+
        if (--fimc->vid_cap.refcnt == 0) {
                clear_bit(ST_CAPT_BUSY, &fimc->state);
                fimc_stop_capture(fimc, false);
@@ -535,22 +546,40 @@ static int fimc_capture_close(struct file *file)
                vb2_queue_release(&fimc->vid_cap.vbq);
                fimc_ctrls_delete(fimc->vid_cap.ctx);
        }
-       return v4l2_fh_release(file);
+
+       ret = v4l2_fh_release(file);
+
+       mutex_unlock(&fimc->lock);
+       return ret;
 }
 
 static unsigned int fimc_capture_poll(struct file *file,
                                      struct poll_table_struct *wait)
 {
        struct fimc_dev *fimc = video_drvdata(file);
+       int ret;
 
-       return vb2_poll(&fimc->vid_cap.vbq, file, wait);
+       if (mutex_lock_interruptible(&fimc->lock))
+               return POLL_ERR;
+
+       ret = vb2_poll(&fimc->vid_cap.vbq, file, wait);
+       mutex_unlock(&fimc->lock);
+
+       return ret;
 }
 
 static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma)
 {
        struct fimc_dev *fimc = video_drvdata(file);
+       int ret;
 
-       return vb2_mmap(&fimc->vid_cap.vbq, vma);
+       if (mutex_lock_interruptible(&fimc->lock))
+               return -ERESTARTSYS;
+
+       ret = vb2_mmap(&fimc->vid_cap.vbq, vma);
+       mutex_unlock(&fimc->lock);
+
+       return ret;
 }
 
 static const struct v4l2_file_operations fimc_capture_fops = {
@@ -658,7 +687,7 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx,
                r->left   = r->top = 0;
                return;
        }
-       if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) {
+       if (target == V4L2_SEL_TGT_COMPOSE) {
                if (ctx->rotation != 90 && ctx->rotation != 270)
                        align_h = 1;
                max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3));
@@ -685,7 +714,7 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx,
                      rotate ? sink->f_height : sink->f_width);
        max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height);
 
-       if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) {
+       if (target == V4L2_SEL_TGT_COMPOSE) {
                min_w = min_t(u32, max_w, sink->f_width / max_sc_h);
                min_h = min_t(u32, max_h, sink->f_height / max_sc_v);
                if (rotate) {
@@ -1146,9 +1175,9 @@ static int fimc_cap_g_selection(struct file *file, void *fh,
                s->r.height = f->o_height;
                return 0;
 
-       case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+       case V4L2_SEL_TGT_COMPOSE:
                f = &ctx->d_frame;
-       case V4L2_SEL_TGT_CROP_ACTIVE:
+       case V4L2_SEL_TGT_CROP:
                s->r.left = f->offs_h;
                s->r.top = f->offs_v;
                s->r.width = f->width;
@@ -1160,7 +1189,7 @@ static int fimc_cap_g_selection(struct file *file, void *fh,
 }
 
 /* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
-int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
+static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
 {
        if (a->left < b->left || a->top < b->top)
                return 0;
@@ -1184,9 +1213,9 @@ static int fimc_cap_s_selection(struct file *file, void *fh,
        if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
                return -EINVAL;
 
-       if (s->target == V4L2_SEL_TGT_COMPOSE_ACTIVE)
+       if (s->target == V4L2_SEL_TGT_COMPOSE)
                f = &ctx->d_frame;
-       else if (s->target == V4L2_SEL_TGT_CROP_ACTIVE)
+       else if (s->target == V4L2_SEL_TGT_CROP)
                f = &ctx->s_frame;
        else
                return -EINVAL;
@@ -1428,9 +1457,9 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
        mutex_lock(&fimc->lock);
 
        switch (sel->target) {
-       case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS:
+       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
                f = &ctx->d_frame;
-       case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+       case V4L2_SEL_TGT_CROP_BOUNDS:
                r->width = f->o_width;
                r->height = f->o_height;
                r->left = 0;
@@ -1438,10 +1467,10 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
                mutex_unlock(&fimc->lock);
                return 0;
 
-       case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+       case V4L2_SEL_TGT_CROP:
                try_sel = v4l2_subdev_get_try_crop(fh, sel->pad);
                break;
-       case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+       case V4L2_SEL_TGT_COMPOSE:
                try_sel = v4l2_subdev_get_try_compose(fh, sel->pad);
                f = &ctx->d_frame;
                break;
@@ -1482,12 +1511,12 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
                return -EINVAL;
 
        mutex_lock(&fimc->lock);
-       fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP_ACTIVE);
+       fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP);
 
        switch (sel->target) {
-       case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS:
+       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
                f = &ctx->d_frame;
-       case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+       case V4L2_SEL_TGT_CROP_BOUNDS:
                r->width = f->o_width;
                r->height = f->o_height;
                r->left = 0;
@@ -1495,10 +1524,10 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
                mutex_unlock(&fimc->lock);
                return 0;
 
-       case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+       case V4L2_SEL_TGT_CROP:
                try_sel = v4l2_subdev_get_try_crop(fh, sel->pad);
                break;
-       case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+       case V4L2_SEL_TGT_COMPOSE:
                try_sel = v4l2_subdev_get_try_compose(fh, sel->pad);
                f = &ctx->d_frame;
                break;
@@ -1514,7 +1543,7 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
                set_frame_crop(f, r->left, r->top, r->width, r->height);
                set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
                spin_unlock_irqrestore(&fimc->slock, flags);
-               if (sel->target == V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL)
+               if (sel->target == V4L2_SEL_TGT_COMPOSE)
                        ctx->state |= FIMC_COMPOSE;
        }
 
@@ -1589,10 +1618,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc,
        vfd->minor      = -1;
        vfd->release    = video_device_release;
        vfd->lock       = &fimc->lock;
-       /* Locking in file operations other than ioctl should be done
-          by the driver, not the V4L2 core.
-          This driver needs auditing so that this flag can be removed. */
-       set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
+
        video_set_drvdata(vfd, fimc);
 
        vid_cap = &fimc->vid_cap;
index a4646ca1d56f31fb30eb35d7cd9829fa5aa9f46a..1a445404e73d2bea57a2e788e21717a7f21fb6ac 100644 (file)
@@ -463,7 +463,7 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
            f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v);
 }
 
-int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx)
+static int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx)
 {
        struct fimc_effect *effect = &ctx->effect;
 
index 95b27ae5cf27eea782cd2735db96f843c794b3cc..808ccc621846fee63d2c4aa762e4a81e63f4490e 100644 (file)
@@ -27,9 +27,6 @@
 #include <media/v4l2-mediabus.h>
 #include <media/s5p_fimc.h>
 
-#define err(fmt, args...) \
-       printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
-
 #define dbg(fmt, args...) \
        pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args)
 
index 419adfb7cdf9a18d9e5ba66130127a42c8150111..f996e94873f68d3d03803997497ff4458c9166bb 100644 (file)
@@ -215,7 +215,7 @@ void flite_hw_set_camera_bus(struct fimc_lite *dev,
        flite_hw_set_camera_port(dev, s_info->mux_id);
 }
 
-void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f)
+static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f)
 {
        static const u32 pixcode[4][2] = {
                { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CIODMAFMT_YCBYCR },
index 74ff310db30cd6755393b5b6eaf2a933495fa0e9..c5b57e805b683d072577ddfa242ea6f9a365583c 100644 (file)
@@ -902,7 +902,7 @@ static int fimc_lite_g_selection(struct file *file, void *fh,
                sel->r.height = f->f_height;
                return 0;
 
-       case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+       case V4L2_SEL_TGT_COMPOSE:
                sel->r = f->rect;
                return 0;
        }
@@ -919,7 +919,7 @@ static int fimc_lite_s_selection(struct file *file, void *fh,
        unsigned long flags;
 
        if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
-           sel->target != V4L2_SEL_TGT_COMPOSE_ACTIVE)
+           sel->target != V4L2_SEL_TGT_COMPOSE)
                return -EINVAL;
 
        fimc_lite_try_compose(fimc, &rect);
@@ -1117,9 +1117,9 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd,
        struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
        struct flite_frame *f = &fimc->inp_frame;
 
-       if ((sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL &&
-            sel->target != V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS) ||
-           sel->pad != FLITE_SD_PAD_SINK)
+       if ((sel->target != V4L2_SEL_TGT_CROP &&
+            sel->target != V4L2_SEL_TGT_CROP_BOUNDS) ||
+            sel->pad != FLITE_SD_PAD_SINK)
                return -EINVAL;
 
        if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
@@ -1128,7 +1128,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd,
        }
 
        mutex_lock(&fimc->lock);
-       if (sel->target == V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL) {
+       if (sel->target == V4L2_SEL_TGT_CROP) {
                sel->r = f->rect;
        } else {
                sel->r.left = 0;
@@ -1153,8 +1153,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd,
        struct flite_frame *f = &fimc->inp_frame;
        int ret = 0;
 
-       if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL ||
-           sel->pad != FLITE_SD_PAD_SINK)
+       if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != FLITE_SD_PAD_SINK)
                return -EINVAL;
 
        mutex_lock(&fimc->lock);
index 4c58e05709621a15e5487026a8f2fce6ac54c5e3..c587011d80eff668c8e1347d099e91d1fb33d427 100644 (file)
@@ -259,7 +259,12 @@ static int fimc_m2m_querycap(struct file *file, void *fh,
        strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
        strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
        cap->bus_info[0] = 0;
-       cap->capabilities = V4L2_CAP_STREAMING |
+       /*
+        * This is only a mem-to-mem video device. The capture and output
+        * device capability flags are left only for backward compatibility
+        * and are scheduled for removal.
+        */
+       cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE |
                V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
 
        return 0;
@@ -642,21 +647,25 @@ static int fimc_m2m_open(struct file *file)
 {
        struct fimc_dev *fimc = video_drvdata(file);
        struct fimc_ctx *ctx;
-       int ret;
+       int ret = -EBUSY;
 
        dbg("pid: %d, state: 0x%lx, refcnt: %d",
            task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt);
 
+       if (mutex_lock_interruptible(&fimc->lock))
+               return -ERESTARTSYS;
        /*
         * Return if the corresponding video capture node
         * is already opened.
         */
        if (fimc->vid_cap.refcnt > 0)
-               return -EBUSY;
+               goto unlock;
 
        ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
-       if (!ctx)
-               return -ENOMEM;
+       if (!ctx) {
+               ret = -ENOMEM;
+               goto unlock;
+       }
        v4l2_fh_init(&ctx->fh, fimc->m2m.vfd);
        ctx->fimc_dev = fimc;
 
@@ -687,6 +696,8 @@ static int fimc_m2m_open(struct file *file)
 
        if (fimc->m2m.refcnt++ == 0)
                set_bit(ST_M2M_RUN, &fimc->state);
+
+       mutex_unlock(&fimc->lock);
        return 0;
 
 error_c:
@@ -695,6 +706,8 @@ error_fh:
        v4l2_fh_del(&ctx->fh);
        v4l2_fh_exit(&ctx->fh);
        kfree(ctx);
+unlock:
+       mutex_unlock(&fimc->lock);
        return ret;
 }
 
@@ -706,6 +719,9 @@ static int fimc_m2m_release(struct file *file)
        dbg("pid: %d, state: 0x%lx, refcnt= %d",
                task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
 
+       if (mutex_lock_interruptible(&fimc->lock))
+               return -ERESTARTSYS;
+
        v4l2_m2m_ctx_release(ctx->m2m_ctx);
        fimc_ctrls_delete(ctx);
        v4l2_fh_del(&ctx->fh);
@@ -714,6 +730,8 @@ static int fimc_m2m_release(struct file *file)
        if (--fimc->m2m.refcnt <= 0)
                clear_bit(ST_M2M_RUN, &fimc->state);
        kfree(ctx);
+
+       mutex_unlock(&fimc->lock);
        return 0;
 }
 
@@ -721,16 +739,32 @@ static unsigned int fimc_m2m_poll(struct file *file,
                                  struct poll_table_struct *wait)
 {
        struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
+       struct fimc_dev *fimc = ctx->fimc_dev;
+       int ret;
+
+       if (mutex_lock_interruptible(&fimc->lock))
+               return -ERESTARTSYS;
+
+       ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+       mutex_unlock(&fimc->lock);
 
-       return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+       return ret;
 }
 
 
 static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
 {
        struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
+       struct fimc_dev *fimc = ctx->fimc_dev;
+       int ret;
+
+       if (mutex_lock_interruptible(&fimc->lock))
+               return -ERESTARTSYS;
 
-       return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+       ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+       mutex_unlock(&fimc->lock);
+
+       return ret;
 }
 
 static const struct v4l2_file_operations fimc_m2m_fops = {
@@ -772,10 +806,6 @@ int fimc_register_m2m_device(struct fimc_dev *fimc,
        vfd->minor = -1;
        vfd->release = video_device_release;
        vfd->lock = &fimc->lock;
-       /* Locking in file operations other than ioctl should be done
-          by the driver, not the V4L2 core.
-          This driver needs auditing so that this flag can be removed. */
-       set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
 
        snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id);
        video_set_drvdata(vfd, fimc);
index 52cef4865423ef2be451df098d0c2ada3412fe21..e65bb283fd8abfede983e8146d59df6085619aea 100644 (file)
@@ -180,7 +180,7 @@ EXPORT_SYMBOL_GPL(fimc_pipeline_initialize);
  * sensor clock.
  * Called with the graph mutex held.
  */
-int __fimc_pipeline_shutdown(struct fimc_pipeline *p)
+static int __fimc_pipeline_shutdown(struct fimc_pipeline *p)
 {
        int ret = 0;
 
@@ -1010,7 +1010,7 @@ static struct platform_driver fimc_md_driver = {
        }
 };
 
-int __init fimc_md_init(void)
+static int __init fimc_md_init(void)
 {
        int ret;
 
@@ -1021,7 +1021,8 @@ int __init fimc_md_init(void)
 
        return platform_driver_register(&fimc_md_driver);
 }
-void __exit fimc_md_exit(void)
+
+static void __exit fimc_md_exit(void)
 {
        platform_driver_unregister(&fimc_md_driver);
        fimc_unregister_driver();
index 1fc4ce8446f57468b25aa9568d9404179b0499bf..0e3eb9ce4f981857a805e80fed82629fd4c88100 100644 (file)
@@ -667,7 +667,8 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
                FIMC_REG_CIGCTRL_SELCAM_MIPI | FIMC_REG_CIGCTRL_CAMIF_SELWB |
                FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG);
 
-       if (cam->bus_type == FIMC_MIPI_CSI2) {
+       switch (cam->bus_type) {
+       case FIMC_MIPI_CSI2:
                cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI;
 
                if (cam->mux_id == 0)
@@ -683,23 +684,24 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
                        cfg |= FIMC_REG_CIGCTRL_CAM_JPEG;
                        break;
                default:
-                       v4l2_err(fimc->vid_cap.vfd,
-                                "Not supported camera pixel format: %d",
+                       v4l2_err(vid_cap->vfd,
+                                "Not supported camera pixel format: %#x\n",
                                 vid_cap->mf.code);
                        return -EINVAL;
                }
                tmp |= (csis_data_alignment == 32) << 8;
 
                writel(tmp, fimc->regs + FIMC_REG_CSIIMGFMT);
-
-       } else if (cam->bus_type == FIMC_ITU_601 ||
-                  cam->bus_type == FIMC_ITU_656) {
+               break;
+       case FIMC_ITU_601...FIMC_ITU_656:
                if (cam->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */
                        cfg |= FIMC_REG_CIGCTRL_SELCAM_ITU_A;
-       } else if (cam->bus_type == FIMC_LCD_WB) {
+               break;
+       case FIMC_LCD_WB:
                cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB;
-       } else {
-               err("invalid camera bus type selected\n");
+               break;
+       default:
+               v4l2_err(vid_cap->vfd, "Invalid camera bus type selected\n");
                return -EINVAL;
        }
        writel(cfg, fimc->regs + FIMC_REG_CIGCTRL);
index 7c98ee7377ee09d9a6742293b8225c8974a0a5fe..7c22004352068ffeec6493b2594710ad184438e5 100644 (file)
@@ -290,8 +290,13 @@ static int vidioc_querycap(struct file *file, void *priv,
        strncpy(cap->card, G2D_NAME, sizeof(cap->card) - 1);
        cap->bus_info[0] = 0;
        cap->version = KERNEL_VERSION(1, 0, 0);
-       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
-                                                       | V4L2_CAP_STREAMING;
+       /*
+        * This is only a mem-to-mem video device. The capture and output
+        * device capability flags are left only for backward compatibility
+        * and are scheduled for removal.
+        */
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
+                           V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
        return 0;
 }
 
index 28b5225d94f588bfc5387ec62a41a6ee960eec50..813b801238d1f72e910ab8eea63feeb586b477bb 100644 (file)
@@ -489,9 +489,13 @@ static int s5p_jpeg_querycap(struct file *file, void *priv,
                        sizeof(cap->card));
        }
        cap->bus_info[0] = 0;
-       cap->capabilities = V4L2_CAP_STREAMING |
-                           V4L2_CAP_VIDEO_CAPTURE |
-                           V4L2_CAP_VIDEO_OUTPUT;
+       /*
+        * This is only a mem-to-mem video device. The capture and output
+        * device capability flags are left only for backward compatibility
+        * and are scheduled for removal.
+        */
+       cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M |
+                           V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
        return 0;
 }
 
@@ -824,10 +828,10 @@ static int s5p_jpeg_g_selection(struct file *file, void *priv,
 
        /* For JPEG blob active == default == bounds */
        switch (s->target) {
-       case V4L2_SEL_TGT_CROP_ACTIVE:
+       case V4L2_SEL_TGT_CROP:
        case V4L2_SEL_TGT_CROP_BOUNDS:
        case V4L2_SEL_TGT_CROP_DEFAULT:
-       case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+       case V4L2_SEL_TGT_COMPOSE:
        case V4L2_SEL_TGT_COMPOSE_DEFAULT:
                s->r.width = ctx->out_q.w;
                s->r.height = ctx->out_q.h;
@@ -1503,29 +1507,7 @@ static struct platform_driver s5p_jpeg_driver = {
        },
 };
 
-static int __init
-s5p_jpeg_register(void)
-{
-       int ret;
-
-       pr_info("S5P JPEG V4L2 Driver, (c) 2011 Samsung Electronics\n");
-
-       ret = platform_driver_register(&s5p_jpeg_driver);
-
-       if (ret)
-               pr_err("%s: failed to register jpeg driver\n", __func__);
-
-       return ret;
-}
-
-static void __exit
-s5p_jpeg_unregister(void)
-{
-       platform_driver_unregister(&s5p_jpeg_driver);
-}
-
-module_init(s5p_jpeg_register);
-module_exit(s5p_jpeg_unregister);
+module_platform_driver(s5p_jpeg_driver);
 
 MODULE_AUTHOR("Andrzej Pietrasiewicz <andrzej.p@samsung.com>");
 MODULE_DESCRIPTION("Samsung JPEG codec driver");
index feea867f318c25a6bce189d2bf6e95fb91a42422..c5d567f87d77acad3a335683946877757c3adce7 100644 (file)
@@ -220,8 +220,14 @@ static int vidioc_querycap(struct file *file, void *priv,
        strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
        cap->bus_info[0] = 0;
        cap->version = KERNEL_VERSION(1, 0, 0);
-       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
-                       V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING;
+       /*
+        * This is only a mem-to-mem video device. The capture and output
+        * device capability flags are left only for backward compatibility
+        * and are scheduled for removal.
+        */
+       cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
+                           V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+                           V4L2_CAP_VIDEO_OUTPUT_MPLANE;
        return 0;
 }
 
index 158b78989b89dc43dd7ced55dec2ca91a6baa28e..aa1c244cf66eee087c6f89a372c18d6bd6be6b72 100644 (file)
@@ -779,9 +779,14 @@ static int vidioc_querycap(struct file *file, void *priv,
        strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
        cap->bus_info[0] = 0;
        cap->version = KERNEL_VERSION(1, 0, 0);
-       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE
-                         | V4L2_CAP_VIDEO_OUTPUT_MPLANE
-                         | V4L2_CAP_STREAMING;
+       /*
+        * This is only a mem-to-mem video device. The capture and output
+        * device capability flags are left only for backward compatibility
+        * and are scheduled for removal.
+        */
+       cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
+                           V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+                           V4L2_CAP_VIDEO_OUTPUT_MPLANE;
        return 0;
 }
 
index 33fde2a763ecf57da6549ffdd24900f97fc1e710..6c74b05d1f95382e691feb48f863e96b175d1a4b 100644 (file)
@@ -367,7 +367,7 @@ static int mxr_g_selection(struct file *file, void *fh,
                return -EINVAL;
 
        switch (s->target) {
-       case V4L2_SEL_TGT_CROP_ACTIVE:
+       case V4L2_SEL_TGT_CROP:
                s->r.left = geo->src.x_offset;
                s->r.top = geo->src.y_offset;
                s->r.width = geo->src.width;
@@ -380,7 +380,7 @@ static int mxr_g_selection(struct file *file, void *fh,
                s->r.width = geo->src.full_width;
                s->r.height = geo->src.full_height;
                break;
-       case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+       case V4L2_SEL_TGT_COMPOSE:
        case V4L2_SEL_TGT_COMPOSE_PADDED:
                s->r.left = geo->dst.x_offset;
                s->r.top = geo->dst.y_offset;
@@ -449,11 +449,11 @@ static int mxr_s_selection(struct file *file, void *fh,
                res.height = geo->dst.full_height;
                break;
 
-       case V4L2_SEL_TGT_CROP_ACTIVE:
+       case V4L2_SEL_TGT_CROP:
                target = &geo->src;
                stage = MXR_GEOMETRY_CROP;
                break;
-       case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+       case V4L2_SEL_TGT_COMPOSE:
        case V4L2_SEL_TGT_COMPOSE_PADDED:
                target = &geo->dst;
                stage = MXR_GEOMETRY_COMPOSE;
index 0f31eccd7b80e462c678368047294f8c8027f24a..6d348f90237af244c6d4436d7f094ba2d973dab3 100644 (file)
@@ -419,14 +419,4 @@ static struct i2c_driver sii9234_driver = {
        .id_table = sii9234_id,
 };
 
-static int __init sii9234_init(void)
-{
-       return i2c_add_driver(&sii9234_driver);
-}
-module_init(sii9234_init);
-
-static void __exit sii9234_exit(void)
-{
-       i2c_del_driver(&sii9234_driver);
-}
-module_exit(sii9234_exit);
+module_i2c_driver(sii9234_driver);
diff --git a/drivers/media/video/saa7121.h b/drivers/media/video/saa7121.h
deleted file mode 100644 (file)
index 66967ae..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-/* saa7121.h - saa7121 initializations
-   Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- */
-#ifndef __SAA7121_H__
-#define __SAA7121_H__
-
-#define NTSC_BURST_START       0x19    /* 28 */
-#define NTSC_BURST_END         0x1d    /* 29 */
-#define NTSC_CHROMA_PHASE      0x67    /* 5a */
-#define NTSC_GAINU             0x76    /* 5b */
-#define NTSC_GAINV             0xa5    /* 5c */
-#define NTSC_BLACK_LEVEL       0x2a    /* 5d */
-#define NTSC_BLANKING_LEVEL    0x2e    /* 5e */
-#define NTSC_VBI_BLANKING      0x2e    /* 5f */
-#define NTSC_DAC_CONTROL       0x11    /* 61 */
-#define NTSC_BURST_AMP         0x3f    /* 62 */
-#define NTSC_SUBC3             0x1f    /* 63 */
-#define NTSC_SUBC2             0x7c    /* 64 */
-#define NTSC_SUBC1             0xf0    /* 65 */
-#define NTSC_SUBC0             0x21    /* 66 */
-#define NTSC_HTRIG             0x72    /* 6c */
-#define NTSC_VTRIG             0x00    /* 6c */
-#define NTSC_MULTI             0x30    /* 6e */
-#define NTSC_CCTTX             0x11    /* 6f */
-#define NTSC_FIRST_ACTIVE      0x12    /* 7a */
-#define NTSC_LAST_ACTIVE       0x02    /* 7b */
-#define NTSC_MSB_VERTICAL      0x40    /* 7c */
-
-#define PAL_BURST_START                0x21    /* 28 */
-#define PAL_BURST_END          0x1d    /* 29 */
-#define PAL_CHROMA_PHASE       0x3f    /* 5a */
-#define PAL_GAINU              0x7d    /* 5b */
-#define PAL_GAINV              0xaf    /* 5c */
-#define PAL_BLACK_LEVEL                0x23    /* 5d */
-#define PAL_BLANKING_LEVEL     0x35    /* 5e */
-#define PAL_VBI_BLANKING       0x35    /* 5f */
-#define PAL_DAC_CONTROL                0x02    /* 61 */
-#define PAL_BURST_AMP          0x2f    /* 62 */
-#define PAL_SUBC3              0xcb    /* 63 */
-#define PAL_SUBC2              0x8a    /* 64 */
-#define PAL_SUBC1              0x09    /* 65 */
-#define PAL_SUBC0              0x2a    /* 66 */
-#define PAL_HTRIG              0x86    /* 6c */
-#define PAL_VTRIG              0x04    /* 6d */
-#define PAL_MULTI              0x20    /* 6e */
-#define PAL_CCTTX              0x15    /* 6f */
-#define PAL_FIRST_ACTIVE       0x16    /* 7a */
-#define PAL_LAST_ACTIVE                0x36    /* 7b */
-#define PAL_MSB_VERTICAL       0x40    /* 7c */
-
-/* Initialization Sequence */
-
-static __u8 init7121ntsc[] = {
-       0x26, 0x0,      0x27, 0x0,
-       0x28, NTSC_BURST_START,         0x29, NTSC_BURST_END,
-       0x2a, 0x0,      0x2b, 0x0,      0x2c, 0x0,      0x2d, 0x0,
-       0x2e, 0x0,      0x2f, 0x0,      0x30, 0x0,      0x31, 0x0,
-       0x32, 0x0,      0x33, 0x0,      0x34, 0x0,      0x35, 0x0,
-       0x36, 0x0,      0x37, 0x0,      0x38, 0x0,      0x39, 0x0,
-       0x3a, 0x03,     0x3b, 0x0,      0x3c, 0x0,      0x3d, 0x0,
-       0x3e, 0x0,      0x3f, 0x0,      0x40, 0x0,      0x41, 0x0,
-       0x42, 0x0,      0x43, 0x0,      0x44, 0x0,      0x45, 0x0,
-       0x46, 0x0,      0x47, 0x0,      0x48, 0x0,      0x49, 0x0,
-       0x4a, 0x0,      0x4b, 0x0,      0x4c, 0x0,      0x4d, 0x0,
-       0x4e, 0x0,      0x4f, 0x0,      0x50, 0x0,      0x51, 0x0,
-       0x52, 0x0,      0x53, 0x0,      0x54, 0x0,      0x55, 0x0,
-       0x56, 0x0,      0x57, 0x0,      0x58, 0x0,      0x59, 0x0,
-       0x5a, NTSC_CHROMA_PHASE,        0x5b, NTSC_GAINU,
-       0x5c, NTSC_GAINV,               0x5d, NTSC_BLACK_LEVEL,
-       0x5e, NTSC_BLANKING_LEVEL,      0x5f, NTSC_VBI_BLANKING,
-       0x60, 0x0,                      0x61, NTSC_DAC_CONTROL,
-       0x62, NTSC_BURST_AMP,           0x63, NTSC_SUBC3,
-       0x64, NTSC_SUBC2,               0x65, NTSC_SUBC1,
-       0x66, NTSC_SUBC0,               0x67, 0x80,     0x68, 0x80,
-       0x69, 0x80,     0x6a, 0x80,     0x6b, 0x29,
-       0x6c, NTSC_HTRIG,               0x6d, NTSC_VTRIG,
-       0x6e, NTSC_MULTI,               0x6f, NTSC_CCTTX,
-       0x70, 0xc9,     0x71, 0x68,     0x72, 0x60,     0x73, 0x0,
-       0x74, 0x0,      0x75, 0x0,      0x76, 0x0,      0x77, 0x0,
-       0x78, 0x0,      0x79, 0x0,      0x7a, NTSC_FIRST_ACTIVE,
-       0x7b, NTSC_LAST_ACTIVE,         0x7c, NTSC_MSB_VERTICAL,
-       0x7d, 0x0,      0x7e, 0x0,      0x7f, 0x0
-};
-#define INIT7121LEN    (sizeof(init7121ntsc)/2)
-
-static __u8 init7121pal[] = {
-       0x26, 0x0,      0x27, 0x0,
-       0x28, PAL_BURST_START,          0x29, PAL_BURST_END,
-       0x2a, 0x0,      0x2b, 0x0,      0x2c, 0x0,      0x2d, 0x0,
-       0x2e, 0x0,      0x2f, 0x0,      0x30, 0x0,      0x31, 0x0,
-       0x32, 0x0,      0x33, 0x0,      0x34, 0x0,      0x35, 0x0,
-       0x36, 0x0,      0x37, 0x0,      0x38, 0x0,      0x39, 0x0,
-       0x3a, 0x03,     0x3b, 0x0,      0x3c, 0x0,      0x3d, 0x0,
-       0x3e, 0x0,      0x3f, 0x0,      0x40, 0x0,      0x41, 0x0,
-       0x42, 0x0,      0x43, 0x0,      0x44, 0x0,      0x45, 0x0,
-       0x46, 0x0,      0x47, 0x0,      0x48, 0x0,      0x49, 0x0,
-       0x4a, 0x0,      0x4b, 0x0,      0x4c, 0x0,      0x4d, 0x0,
-       0x4e, 0x0,      0x4f, 0x0,      0x50, 0x0,      0x51, 0x0,
-       0x52, 0x0,      0x53, 0x0,      0x54, 0x0,      0x55, 0x0,
-       0x56, 0x0,      0x57, 0x0,      0x58, 0x0,      0x59, 0x0,
-       0x5a, PAL_CHROMA_PHASE,         0x5b, PAL_GAINU,
-       0x5c, PAL_GAINV,                0x5d, PAL_BLACK_LEVEL,
-       0x5e, PAL_BLANKING_LEVEL,       0x5f, PAL_VBI_BLANKING,
-       0x60, 0x0,                      0x61, PAL_DAC_CONTROL,
-       0x62, PAL_BURST_AMP,            0x63, PAL_SUBC3,
-       0x64, PAL_SUBC2,                0x65, PAL_SUBC1,
-       0x66, PAL_SUBC0,                0x67, 0x80,     0x68, 0x80,
-       0x69, 0x80,     0x6a, 0x80,     0x6b, 0x29,
-       0x6c, PAL_HTRIG,                0x6d, PAL_VTRIG,
-       0x6e, PAL_MULTI,                0x6f, PAL_CCTTX,
-       0x70, 0xc9,     0x71, 0x68,     0x72, 0x60,     0x73, 0x0,
-       0x74, 0x0,      0x75, 0x0,      0x76, 0x0,      0x77, 0x0,
-       0x78, 0x0,      0x79, 0x0,      0x7a, PAL_FIRST_ACTIVE,
-       0x7b, PAL_LAST_ACTIVE,          0x7c, PAL_MSB_VERTICAL,
-       0x7d, 0x0,      0x7e, 0x0,      0x7f, 0x0
-};
-#endif
index 5dfd826d734e82cffb9c791b790b5c9d8d373a36..cc7f3d6ee966f955cbaac74dfa3670d453e7c963 100644 (file)
@@ -1282,7 +1282,7 @@ static int dvb_init(struct saa7134_dev *dev)
        case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS:
                if (configure_tda827x_fe(dev, &tda827x_lifeview_config,
                                         &tda827x_cfg_0) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_PHILIPS_EUROPA:
        case SAA7134_BOARD_VIDEOMATE_DVBT_300:
@@ -1322,7 +1322,7 @@ static int dvb_init(struct saa7134_dev *dev)
        case SAA7134_BOARD_KWORLD_DVBT_210:
                if (configure_tda827x_fe(dev, &kworld_dvb_t_210_config,
                                         &tda827x_cfg_2) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_HAUPPAUGE_HVR1120:
                fe0->dvb.frontend = dvb_attach(tda10048_attach,
@@ -1340,17 +1340,17 @@ static int dvb_init(struct saa7134_dev *dev)
        case SAA7134_BOARD_PHILIPS_TIGER:
                if (configure_tda827x_fe(dev, &philips_tiger_config,
                                         &tda827x_cfg_0) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_PINNACLE_PCTV_310i:
                if (configure_tda827x_fe(dev, &pinnacle_pctv_310i_config,
                                         &tda827x_cfg_1) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_HAUPPAUGE_HVR1110:
                if (configure_tda827x_fe(dev, &hauppauge_hvr_1110_config,
                                         &tda827x_cfg_1) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_HAUPPAUGE_HVR1150:
                fe0->dvb.frontend = dvb_attach(lgdt3305_attach,
@@ -1368,30 +1368,30 @@ static int dvb_init(struct saa7134_dev *dev)
        case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
                if (configure_tda827x_fe(dev, &asus_p7131_dual_config,
                                         &tda827x_cfg_0) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_FLYDVBT_LR301:
                if (configure_tda827x_fe(dev, &tda827x_lifeview_config,
                                         &tda827x_cfg_0) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_FLYDVB_TRIO:
                if (!use_frontend) {    /* terrestrial */
                        if (configure_tda827x_fe(dev, &lifeview_trio_config,
                                                 &tda827x_cfg_0) < 0)
-                               goto dettach_frontend;
+                               goto detach_frontend;
                } else {                /* satellite */
                        fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap);
                        if (fe0->dvb.frontend) {
                                if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x63,
                                                                        &dev->i2c_adap, 0) == NULL) {
                                        wprintk("%s: Lifeview Trio, No tda826x found!\n", __func__);
-                                       goto dettach_frontend;
+                                       goto detach_frontend;
                                }
                                if (dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->i2c_adap,
                                                                                0x08, 0, 0) == NULL) {
                                        wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __func__);
-                                       goto dettach_frontend;
+                                       goto detach_frontend;
                                }
                        }
                }
@@ -1407,7 +1407,7 @@ static int dvb_init(struct saa7134_dev *dev)
                                                                &ads_duo_cfg) == NULL) {
                                wprintk("no tda827x tuner found at addr: %02x\n",
                                        ads_tech_duo_config.tuner_address);
-                               goto dettach_frontend;
+                               goto detach_frontend;
                        }
                } else
                        wprintk("failed to attach tda10046\n");
@@ -1415,13 +1415,13 @@ static int dvb_init(struct saa7134_dev *dev)
        case SAA7134_BOARD_TEVION_DVBT_220RF:
                if (configure_tda827x_fe(dev, &tevion_dvbt220rf_config,
                                         &tda827x_cfg_0) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_MEDION_MD8800_QUADRO:
                if (!use_frontend) {     /* terrestrial */
                        if (configure_tda827x_fe(dev, &md8800_dvbt_config,
                                                 &tda827x_cfg_0) < 0)
-                               goto dettach_frontend;
+                               goto detach_frontend;
                } else {        /* satellite */
                        fe0->dvb.frontend = dvb_attach(tda10086_attach,
                                                        &flydvbs, &dev->i2c_adap);
@@ -1435,7 +1435,7 @@ static int dvb_init(struct saa7134_dev *dev)
                                                0x60, &dev->i2c_adap, 0) == NULL) {
                                        wprintk("%s: Medion Quadro, no tda826x "
                                                "found !\n", __func__);
-                                       goto dettach_frontend;
+                                       goto detach_frontend;
                                }
                                if (dev_id != 0x08) {
                                        /* we need to open the i2c gate (we know it exists) */
@@ -1444,7 +1444,7 @@ static int dvb_init(struct saa7134_dev *dev)
                                                        &dev->i2c_adap, 0x08, 0, 0) == NULL) {
                                                wprintk("%s: Medion Quadro, no ISL6405 "
                                                        "found !\n", __func__);
-                                               goto dettach_frontend;
+                                               goto detach_frontend;
                                        }
                                        if (dev_id == 0x07) {
                                                /* fire up the 2nd section of the LNB supply since
@@ -1503,12 +1503,12 @@ static int dvb_init(struct saa7134_dev *dev)
                        if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60,
                                       &dev->i2c_adap, 0) == NULL) {
                                wprintk("%s: No tda826x found!\n", __func__);
-                               goto dettach_frontend;
+                               goto detach_frontend;
                        }
                        if (dvb_attach(isl6421_attach, fe0->dvb.frontend,
                                       &dev->i2c_adap, 0x08, 0, 0) == NULL) {
                                wprintk("%s: No ISL6421 found!\n", __func__);
-                               goto dettach_frontend;
+                               goto detach_frontend;
                        }
                }
                break;
@@ -1537,37 +1537,37 @@ static int dvb_init(struct saa7134_dev *dev)
        case SAA7134_BOARD_CINERGY_HT_PCMCIA:
                if (configure_tda827x_fe(dev, &cinergy_ht_config,
                                         &tda827x_cfg_0) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_CINERGY_HT_PCI:
                if (configure_tda827x_fe(dev, &cinergy_ht_pci_config,
                                         &tda827x_cfg_0) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_PHILIPS_TIGER_S:
                if (configure_tda827x_fe(dev, &philips_tiger_s_config,
                                         &tda827x_cfg_2) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_ASUS_P7131_4871:
                if (configure_tda827x_fe(dev, &asus_p7131_4871_config,
                                         &tda827x_cfg_2) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
                if (configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config,
                                         &tda827x_cfg_2) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_AVERMEDIA_SUPER_007:
                if (configure_tda827x_fe(dev, &avermedia_super_007_config,
                                         &tda827x_cfg_0) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_TWINHAN_DTV_DVB_3056:
                if (configure_tda827x_fe(dev, &twinhan_dtv_dvb_3056_config,
                                         &tda827x_cfg_2_sw42) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_PHILIPS_SNAKE:
                fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs,
@@ -1576,24 +1576,24 @@ static int dvb_init(struct saa7134_dev *dev)
                        if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60,
                                        &dev->i2c_adap, 0) == NULL) {
                                wprintk("%s: No tda826x found!\n", __func__);
-                               goto dettach_frontend;
+                               goto detach_frontend;
                        }
                        if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
                                        &dev->i2c_adap, 0, 0) == NULL) {
                                wprintk("%s: No lnbp21 found!\n", __func__);
-                               goto dettach_frontend;
+                               goto detach_frontend;
                        }
                }
                break;
        case SAA7134_BOARD_CREATIX_CTX953:
                if (configure_tda827x_fe(dev, &md8800_dvbt_config,
                                         &tda827x_cfg_0) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_MSI_TVANYWHERE_AD11:
                if (configure_tda827x_fe(dev, &philips_tiger_s_config,
                                         &tda827x_cfg_2) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
                dprintk("AverMedia E506R dvb setup\n");
@@ -1614,7 +1614,7 @@ static int dvb_init(struct saa7134_dev *dev)
                                  &dev->i2c_adap, DVB_PLL_PHILIPS_SD1878_TDA8261) == NULL) {
                                wprintk("%s: MD7134 DVB-S, no SD1878 "
                                        "found !\n", __func__);
-                               goto dettach_frontend;
+                               goto detach_frontend;
                        }
                        /* we need to open the i2c gate (we know it exists) */
                        fe = fe0->dvb.frontend;
@@ -1623,7 +1623,7 @@ static int dvb_init(struct saa7134_dev *dev)
                                        &dev->i2c_adap, 0x08, 0, 0) == NULL) {
                                wprintk("%s: MD7134 DVB-S, no ISL6405 "
                                        "found !\n", __func__);
-                               goto dettach_frontend;
+                               goto detach_frontend;
                        }
                        fe->ops.i2c_gate_ctrl(fe, 0);
                        dev->original_set_voltage = fe->ops.set_voltage;
@@ -1645,7 +1645,7 @@ static int dvb_init(struct saa7134_dev *dev)
                if (!use_frontend) {     /* terrestrial */
                        if (configure_tda827x_fe(dev, &asus_tiger_3in1_config,
                                                        &tda827x_cfg_2) < 0)
-                               goto dettach_frontend;
+                               goto detach_frontend;
                } else {                /* satellite */
                        fe0->dvb.frontend = dvb_attach(tda10086_attach,
                                                &flydvbs, &dev->i2c_adap);
@@ -1655,13 +1655,13 @@ static int dvb_init(struct saa7134_dev *dev)
                                                &dev->i2c_adap, 0) == NULL) {
                                        wprintk("%s: Asus Tiger 3in1, no "
                                                "tda826x found!\n", __func__);
-                                       goto dettach_frontend;
+                                       goto detach_frontend;
                                }
                                if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
                                                &dev->i2c_adap, 0, 0) == NULL) {
                                        wprintk("%s: Asus Tiger 3in1, no lnbp21"
                                                " found!\n", __func__);
-                                      goto dettach_frontend;
+                                      goto detach_frontend;
                               }
                       }
               }
@@ -1670,7 +1670,7 @@ static int dvb_init(struct saa7134_dev *dev)
                if (!use_frontend) {     /* terrestrial */
                        if (configure_tda827x_fe(dev, &asus_ps3_100_config,
                                                 &tda827x_cfg_2) < 0)
-                               goto dettach_frontend;
+                               goto detach_frontend;
               } else {                /* satellite */
                        fe0->dvb.frontend = dvb_attach(tda10086_attach,
                                                       &flydvbs, &dev->i2c_adap);
@@ -1680,13 +1680,13 @@ static int dvb_init(struct saa7134_dev *dev)
                                               &dev->i2c_adap, 0) == NULL) {
                                        wprintk("%s: Asus My Cinema PS3-100, no "
                                                "tda826x found!\n", __func__);
-                                       goto dettach_frontend;
+                                       goto detach_frontend;
                                }
                                if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
                                               &dev->i2c_adap, 0, 0) == NULL) {
                                        wprintk("%s: Asus My Cinema PS3-100, no lnbp21"
                                                " found!\n", __func__);
-                                       goto dettach_frontend;
+                                       goto detach_frontend;
                                }
                        }
                }
@@ -1694,7 +1694,7 @@ static int dvb_init(struct saa7134_dev *dev)
        case SAA7134_BOARD_ASUSTeK_TIGER:
                if (configure_tda827x_fe(dev, &philips_tiger_config,
                                         &tda827x_cfg_0) < 0)
-                       goto dettach_frontend;
+                       goto detach_frontend;
                break;
        case SAA7134_BOARD_BEHOLD_H6:
                fe0->dvb.frontend = dvb_attach(zl10353_attach,
@@ -1830,19 +1830,19 @@ static int dvb_init(struct saa7134_dev *dev)
                };
 
                if (!fe0->dvb.frontend)
-                       goto dettach_frontend;
+                       goto detach_frontend;
 
                fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg);
                if (!fe) {
                        printk(KERN_ERR "%s/2: xc3028 attach failed\n",
                               dev->name);
-                       goto dettach_frontend;
+                       goto detach_frontend;
                }
        }
 
        if (NULL == fe0->dvb.frontend) {
                printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name);
-               goto dettach_frontend;
+               goto detach_frontend;
        }
        /* define general-purpose callback pointer */
        fe0->dvb.frontend->callback = saa7134_tuner_callback;
@@ -1864,7 +1864,7 @@ static int dvb_init(struct saa7134_dev *dev)
        }
        return ret;
 
-dettach_frontend:
+detach_frontend:
        videobuf_dvb_dealloc_frontends(&dev->frontends);
        return -EINVAL;
 }
diff --git a/drivers/media/video/saa7146.h b/drivers/media/video/saa7146.h
deleted file mode 100644 (file)
index 9fadb33..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
-    saa7146.h - definitions philips saa7146 based cards
-    Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __SAA7146__
-#define __SAA7146__
-
-#define SAA7146_VERSION_CODE 0x000101
-
-#include <linux/types.h>
-#include <linux/wait.h>
-
-#ifndef O_NONCAP
-#define O_NONCAP       O_TRUNC
-#endif
-
-#define MAX_GBUFFERS   2
-#define FBUF_SIZE      0x190000
-
-#ifdef __KERNEL__
-
-struct saa7146_window
-{
-       int x, y;
-       ushort width, height;
-       ushort bpp, bpl;
-       ushort swidth, sheight;
-       short cropx, cropy;
-       ushort cropwidth, cropheight;
-       unsigned long vidadr;
-       int color_fmt;
-       ushort depth;
-};
-
-/*  Per-open data for handling multiple opens on one device */
-struct device_open
-{
-       int          isopen;
-       int          noncapturing;
-       struct saa7146  *dev;
-};
-#define MAX_OPENS 3
-
-struct saa7146
-{
-       struct video_device video_dev;
-       struct video_picture picture;
-       struct video_audio audio_dev;
-       struct video_info vidinfo;
-       int user;
-       int cap;
-       int capuser;
-       int irqstate;           /* irq routine is state driven */
-       int writemode;
-       int playmode;
-       unsigned int nr;
-       unsigned long irq;          /* IRQ used by SAA7146 card */
-       unsigned short id;
-       unsigned char revision;
-       unsigned char boardcfg[64];     /* 64 bytes of config from eeprom */
-       unsigned long saa7146_adr;   /* bus address of IO mem from PCI BIOS */
-       struct saa7146_window win;
-       unsigned char __iomem *saa7146_mem; /* pointer to mapped IO memory */
-       struct device_open open_data[MAX_OPENS];
-#define MAX_MARKS 16
-       /* for a/v sync */
-       int endmark[MAX_MARKS], endmarkhead, endmarktail;
-       u32 *dmaRPS1, *pageRPS1, *dmaRPS2, *pageRPS2, *dmavid1, *dmavid2,
-               *dmavid3, *dmaa1in, *dmaa1out, *dmaa2in, *dmaa2out,
-               *pagedebi, *pagevid1, *pagevid2, *pagevid3, *pagea1in,
-               *pagea1out, *pagea2in, *pagea2out;
-       wait_queue_head_t i2cq, debiq, audq, vidq;
-       u8  *vidbuf, *audbuf, *osdbuf, *dmadebi;
-       int audhead, vidhead, osdhead, audtail, vidtail, osdtail;
-       spinlock_t lock;        /* the device lock */
-};
-#endif
-
-#ifdef _ALPHA_SAA7146
-#define saawrite(dat,adr)    writel((dat), saa->saa7146_adr+(adr))
-#define saaread(adr)         readl(saa->saa7146_adr+(adr))
-#else
-#define saawrite(dat,adr)    writel((dat), saa->saa7146_mem+(adr))
-#define saaread(adr)         readl(saa->saa7146_mem+(adr))
-#endif
-
-#define saaand(dat,adr)      saawrite((dat) & saaread(adr), adr)
-#define saaor(dat,adr)       saawrite((dat) | saaread(adr), adr)
-#define saaaor(dat,mask,adr) saawrite((dat) | ((mask) & saaread(adr)), adr)
-
-/* bitmask of attached hardware found */
-#define SAA7146_UNKNOWN                0x00000000
-#define SAA7146_SAA7111                0x00000001
-#define SAA7146_SAA7121                0x00000002
-#define SAA7146_IBMMPEG                0x00000004
-
-#endif
diff --git a/drivers/media/video/saa7146reg.h b/drivers/media/video/saa7146reg.h
deleted file mode 100644 (file)
index 80ec2c1..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
-    saa7146.h - definitions philips saa7146 based cards
-    Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __SAA7146_REG__
-#define __SAA7146_REG__
-#define SAA7146_BASE_ODD1      0x00
-#define SAA7146_BASE_EVEN1     0x04
-#define SAA7146_PROT_ADDR1     0x08
-#define SAA7146_PITCH1         0x0c
-#define SAA7146_PAGE1          0x10
-#define SAA7146_NUM_LINE_BYTE1 0x14
-#define SAA7146_BASE_ODD2      0x18
-#define SAA7146_BASE_EVEN2     0x1c
-#define SAA7146_PROT_ADDR2     0x20
-#define SAA7146_PITCH2         0x24
-#define SAA7146_PAGE2          0x28
-#define SAA7146_NUM_LINE_BYTE2 0x2c
-#define SAA7146_BASE_ODD3      0x30
-#define SAA7146_BASE_EVEN3     0x34
-#define SAA7146_PROT_ADDR3     0x38
-#define SAA7146_PITCH3         0x3c
-#define SAA7146_PAGE3          0x40
-#define SAA7146_NUM_LINE_BYTE3 0x44
-#define SAA7146_PCI_BT_V1      0x48
-#define SAA7146_PCI_BT_V2      0x49
-#define SAA7146_PCI_BT_V3      0x4a
-#define SAA7146_PCI_BT_DEBI    0x4b
-#define SAA7146_PCI_BT_A       0x4c
-#define SAA7146_DD1_INIT       0x50
-#define SAA7146_DD1_STREAM_B   0x54
-#define SAA7146_DD1_STREAM_A   0x56
-#define SAA7146_BRS_CTRL       0x58
-#define SAA7146_HPS_CTRL       0x5c
-#define SAA7146_HPS_V_SCALE    0x60
-#define SAA7146_HPS_V_GAIN     0x64
-#define SAA7146_HPS_H_PRESCALE 0x68
-#define SAA7146_HPS_H_SCALE    0x6c
-#define SAA7146_BCS_CTRL       0x70
-#define SAA7146_CHROMA_KEY_RANGE       0x74
-#define SAA7146_CLIP_FORMAT_CTRL       0x78
-#define SAA7146_DEBI_CONFIG    0x7c
-#define SAA7146_DEBI_COMMAND   0x80
-#define SAA7146_DEBI_PAGE      0x84
-#define SAA7146_DEBI_AD                0x88
-#define SAA7146_I2C_TRANSFER   0x8c
-#define SAA7146_I2C_STATUS     0x90
-#define SAA7146_BASE_A1_IN     0x94
-#define SAA7146_PROT_A1_IN     0x98
-#define SAA7146_PAGE_A1_IN     0x9C
-#define SAA7146_BASE_A1_OUT    0xa0
-#define SAA7146_PROT_A1_OUT    0xa4
-#define SAA7146_PAGE_A1_OUT    0xa8
-#define SAA7146_BASE_A2_IN     0xac
-#define SAA7146_PROT_A2_IN     0xb0
-#define SAA7146_PAGE_A2_IN     0xb4
-#define SAA7146_BASE_A2_OUT    0xb8
-#define SAA7146_PROT_A2_OUT    0xbc
-#define SAA7146_PAGE_A2_OUT    0xc0
-#define SAA7146_RPS_PAGE0      0xc4
-#define SAA7146_RPS_PAGE1      0xc8
-#define SAA7146_RPS_THRESH0    0xcc
-#define SAA7146_RPS_THRESH1    0xd0
-#define SAA7146_RPS_TOV0       0xd4
-#define SAA7146_RPS_TOV1       0xd8
-#define SAA7146_IER            0xdc
-#define SAA7146_GPIO_CTRL      0xe0
-#define SAA7146_EC1SSR         0xe4
-#define SAA7146_EC2SSR         0xe8
-#define SAA7146_ECT1R          0xec
-#define SAA7146_ECT2R          0xf0
-#define SAA7146_ACON1          0xf4
-#define SAA7146_ACON2          0xf8
-#define SAA7146_MC1            0xfc
-#define SAA7146_MC2            0x100
-#define SAA7146_RPS_ADDR0      0x104
-#define SAA7146_RPS_ADDR1      0x108
-#define SAA7146_ISR            0x10c
-#define SAA7146_PSR            0x110
-#define SAA7146_SSR            0x114
-#define SAA7146_EC1R           0x118
-#define SAA7146_EC2R           0x11c
-#define SAA7146_VDP1           0x120
-#define SAA7146_VDP2           0x124
-#define SAA7146_VDP3           0x128
-#define SAA7146_ADP1           0x12c
-#define SAA7146_ADP2           0x130
-#define SAA7146_ADP3           0x134
-#define SAA7146_ADP4           0x138
-#define SAA7146_DDP            0x13c
-#define SAA7146_LEVEL_REP      0x140
-#define SAA7146_FB_BUFFER1     0x144
-#define SAA7146_FB_BUFFER2     0x148
-#define SAA7146_A_TIME_SLOT1   0x180
-#define SAA7146_A_TIME_SLOT2   0x1C0
-
-/* bitfield defines */
-#define MASK_31                        0x80000000
-#define MASK_30                        0x40000000
-#define MASK_29                        0x20000000
-#define MASK_28                        0x10000000
-#define MASK_27                        0x08000000
-#define MASK_26                        0x04000000
-#define MASK_25                        0x02000000
-#define MASK_24                        0x01000000
-#define MASK_23                        0x00800000
-#define MASK_22                        0x00400000
-#define MASK_21                        0x00200000
-#define MASK_20                        0x00100000
-#define MASK_19                        0x00080000
-#define MASK_18                        0x00040000
-#define MASK_17                        0x00020000
-#define MASK_16                        0x00010000
-#define MASK_15                        0x00008000
-#define MASK_14                        0x00004000
-#define MASK_13                        0x00002000
-#define MASK_12                        0x00001000
-#define MASK_11                        0x00000800
-#define MASK_10                        0x00000400
-#define MASK_09                        0x00000200
-#define MASK_08                        0x00000100
-#define MASK_07                        0x00000080
-#define MASK_06                        0x00000040
-#define MASK_05                        0x00000020
-#define MASK_04                        0x00000010
-#define MASK_03                        0x00000008
-#define MASK_02                        0x00000004
-#define MASK_01                        0x00000002
-#define MASK_00                        0x00000001
-#define MASK_B0                        0x000000ff
-#define MASK_B1                        0x0000ff00
-#define MASK_B2                        0x00ff0000
-#define MASK_B3                        0xff000000
-#define MASK_W0                        0x0000ffff
-#define MASK_W1                        0xffff0000
-#define MASK_PA                        0xfffffffc
-#define MASK_PR                        0xfffffffe
-#define MASK_ER                        0xffffffff
-#define MASK_NONE              0x00000000
-
-#define SAA7146_PAGE_MAP_EN    MASK_11
-/* main control register 1 */
-#define SAA7146_MC1_MRST_N     MASK_15
-#define SAA7146_MC1_ERPS1      MASK_13
-#define SAA7146_MC1_ERPS0      MASK_12
-#define SAA7146_MC1_EDP                MASK_11
-#define SAA7146_MC1_EVP                MASK_10
-#define SAA7146_MC1_EAP                MASK_09
-#define SAA7146_MC1_EI2C       MASK_08
-#define SAA7146_MC1_TR_E_DEBI  MASK_07
-#define SAA7146_MC1_TR_E_1     MASK_06
-#define SAA7146_MC1_TR_E_2     MASK_05
-#define SAA7146_MC1_TR_E_3     MASK_04
-#define SAA7146_MC1_TR_E_A2_OUT        MASK_03
-#define SAA7146_MC1_TR_E_A2_IN MASK_02
-#define SAA7146_MC1_TR_E_A1_OUT        MASK_01
-#define SAA7146_MC1_TR_E_A1_IN MASK_00
-/* main control register 2 */
-#define SAA7146_MC2_RPS_SIG4   MASK_15
-#define SAA7146_MC2_RPS_SIG3   MASK_14
-#define SAA7146_MC2_RPS_SIG2   MASK_13
-#define SAA7146_MC2_RPS_SIG1   MASK_12
-#define SAA7146_MC2_RPS_SIG0   MASK_11
-#define SAA7146_MC2_UPLD_D1_B  MASK_10
-#define SAA7146_MC2_UPLD_D1_A  MASK_09
-#define SAA7146_MC2_UPLD_BRS   MASK_08
-#define SAA7146_MC2_UPLD_HPS_H MASK_06
-#define SAA7146_MC2_UPLD_HPS_V MASK_05
-#define SAA7146_MC2_UPLD_DMA3  MASK_04
-#define SAA7146_MC2_UPLD_DMA2  MASK_03
-#define SAA7146_MC2_UPLD_DMA1  MASK_02
-#define SAA7146_MC2_UPLD_DEBI  MASK_01
-#define SAA7146_MC2_UPLD_I2C   MASK_00
-/* Primary Status Register and Interrupt Enable/Status Registers */
-#define SAA7146_PSR_PPEF       MASK_31
-#define SAA7146_PSR_PABO       MASK_30
-#define SAA7146_PSR_PPED       MASK_29
-#define SAA7146_PSR_RPS_I1     MASK_28
-#define SAA7146_PSR_RPS_I0     MASK_27
-#define SAA7146_PSR_RPS_LATE1  MASK_26
-#define SAA7146_PSR_RPS_LATE0  MASK_25
-#define SAA7146_PSR_RPS_E1     MASK_24
-#define SAA7146_PSR_RPS_E0     MASK_23
-#define SAA7146_PSR_RPS_TO1    MASK_22
-#define SAA7146_PSR_RPS_TO0    MASK_21
-#define SAA7146_PSR_UPLD       MASK_20
-#define SAA7146_PSR_DEBI_S     MASK_19
-#define SAA7146_PSR_DEBI_E     MASK_18
-#define SAA7146_PSR_I2C_S      MASK_17
-#define SAA7146_PSR_I2C_E      MASK_16
-#define SAA7146_PSR_A2_IN      MASK_15
-#define SAA7146_PSR_A2_OUT     MASK_14
-#define SAA7146_PSR_A1_IN      MASK_13
-#define SAA7146_PSR_A1_OUT     MASK_12
-#define SAA7146_PSR_AFOU       MASK_11
-#define SAA7146_PSR_V_PE       MASK_10
-#define SAA7146_PSR_VFOU       MASK_09
-#define SAA7146_PSR_FIDA       MASK_08
-#define SAA7146_PSR_FIDB       MASK_07
-#define SAA7146_PSR_PIN3       MASK_06
-#define SAA7146_PSR_PIN2       MASK_05
-#define SAA7146_PSR_PIN1       MASK_04
-#define SAA7146_PSR_PIN0       MASK_03
-#define SAA7146_PSR_ECS                MASK_02
-#define SAA7146_PSR_EC3S       MASK_01
-#define SAA7146_PSR_EC0S       MASK_00
-/* Secondary Status Register */
-#define SAA7146_SSR_PRQ                MASK_31
-#define SAA7146_SSR_PMA                MASK_30
-#define SAA7146_SSR_RPS_RE1    MASK_29
-#define SAA7146_SSR_RPS_PE1    MASK_28
-#define SAA7146_SSR_RPS_A1     MASK_27
-#define SAA7146_SSR_RPS_RE0    MASK_26
-#define SAA7146_SSR_RPS_PE0    MASK_25
-#define SAA7146_SSR_RPS_A0     MASK_24
-#define SAA7146_SSR_DEBI_TO    MASK_23
-#define SAA7146_SSR_DEBI_EF    MASK_22
-#define SAA7146_SSR_I2C_EA     MASK_21
-#define SAA7146_SSR_I2C_EW     MASK_20
-#define SAA7146_SSR_I2C_ER     MASK_19
-#define SAA7146_SSR_I2C_EL     MASK_18
-#define SAA7146_SSR_I2C_EF     MASK_17
-#define SAA7146_SSR_V3P                MASK_16
-#define SAA7146_SSR_V2P                MASK_15
-#define SAA7146_SSR_V1P                MASK_14
-#define SAA7146_SSR_VF3                MASK_13
-#define SAA7146_SSR_VF2                MASK_12
-#define SAA7146_SSR_VF1                MASK_11
-#define SAA7146_SSR_AF2_IN     MASK_10
-#define SAA7146_SSR_AF2_OUT    MASK_09
-#define SAA7146_SSR_AF1_IN     MASK_08
-#define SAA7146_SSR_AF1_OUT    MASK_07
-#define SAA7146_SSR_VGT                MASK_05
-#define SAA7146_SSR_LNQG       MASK_04
-#define SAA7146_SSR_EC5S       MASK_03
-#define SAA7146_SSR_EC4S       MASK_02
-#define SAA7146_SSR_EC2S       MASK_01
-#define SAA7146_SSR_EC1S       MASK_00
-/* I2C status register */
-#define SAA7146_I2C_ABORT      MASK_07
-#define SAA7146_I2C_SPERR      MASK_06
-#define SAA7146_I2C_APERR      MASK_05
-#define SAA7146_I2C_DTERR      MASK_04
-#define SAA7146_I2C_DRERR      MASK_03
-#define SAA7146_I2C_AL         MASK_02
-#define SAA7146_I2C_ERR                MASK_01
-#define SAA7146_I2C_BUSY       MASK_00
-/* output formats */
-#define SAA7146_YUV422 0
-#define SAA7146_RGB16  0
-#define SAA7146_YUV444 1
-#define SAA7146_RGB24  1
-#define SAA7146_ARGB32 2
-#define SAA7146_YUV411 3
-#define SAA7146_ARGB15  3
-#define SAA7146_YUV2   4
-#define SAA7146_RGAB15 4
-#define SAA7146_Y8     6
-#define SAA7146_YUV8   7
-#define SAA7146_RGB8   7
-#define SAA7146_YUV444p        8
-#define SAA7146_YUV422p        9
-#define SAA7146_YUV420p        10
-#define SAA7146_YUV1620        11
-#define SAA7146_Y1     13
-#define SAA7146_Y2     14
-#define SAA7146_YUV1   15
-#endif
index 8a98ab68239e10ee5f1155f4fa31e678b58f7e48..c8799fdaae67d074e0bdbe64ea2bac4f752edb2f 100644 (file)
@@ -1367,7 +1367,6 @@ int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg,
        struct saa7164_dev *dev = bus->dev;
        u16 len = 0;
        int unitid;
-       u32 regval;
        u8 buf[256];
        int ret;
 
@@ -1376,19 +1375,6 @@ int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg,
        if (reglen > 4)
                return -EIO;
 
-       if (reglen == 1)
-               regval = *(reg);
-       else
-       if (reglen == 2)
-               regval = ((*(reg) << 8) || *(reg+1));
-       else
-       if (reglen == 3)
-               regval = ((*(reg) << 16) | (*(reg+1) << 8) | *(reg+2));
-       else
-       if (reglen == 4)
-               regval = ((*(reg) << 24) | (*(reg+1) << 16) |
-                       (*(reg+2) << 8) | *(reg+3));
-
        /* Prepare the send buffer */
        /* Bytes 00-03 source register length
         *       04-07 source bytes to read
index 26148f76cba2640529a3012a1b4143211569def8..4f7e3b42263fcc6644294bbcb9b2d47a382c2077 100644 (file)
@@ -69,15 +69,6 @@ err:
        return retval;
 }
 
-void saa7164_call_i2c_clients(struct saa7164_i2c *bus, unsigned int cmd,
-       void *arg)
-{
-       if (bus->i2c_rc != 0)
-               return;
-
-       i2c_clients_command(&bus->i2c_adap, cmd, arg);
-}
-
 static u32 saa7164_functionality(struct i2c_adapter *adap)
 {
        return I2C_FUNC_I2C;
@@ -106,21 +97,14 @@ int saa7164_i2c_register(struct saa7164_i2c *bus)
 
        dprintk(DBGLVL_I2C, "%s(bus = %d)\n", __func__, bus->nr);
 
-       memcpy(&bus->i2c_adap, &saa7164_i2c_adap_template,
-              sizeof(bus->i2c_adap));
-
-       memcpy(&bus->i2c_algo, &saa7164_i2c_algo_template,
-              sizeof(bus->i2c_algo));
-
-       memcpy(&bus->i2c_client, &saa7164_i2c_client_template,
-              sizeof(bus->i2c_client));
+       bus->i2c_adap = saa7164_i2c_adap_template;
+       bus->i2c_client = saa7164_i2c_client_template;
 
        bus->i2c_adap.dev.parent = &dev->pci->dev;
 
        strlcpy(bus->i2c_adap.name, bus->dev->name,
                sizeof(bus->i2c_adap.name));
 
-       bus->i2c_algo.data = bus;
        bus->i2c_adap.algo_data = bus;
        i2c_set_adapdata(&bus->i2c_adap, bus);
        i2c_add_adapter(&bus->i2c_adap);
index 8d120e3baf70c851d8335f2ad4ec35f10bdbd98c..35219b9b0fbcc05844dac644aa82a268a5213fde 100644 (file)
@@ -46,7 +46,6 @@
 
 #include <linux/pci.h>
 #include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
 #include <linux/kdev_t.h>
 #include <linux/mutex.h>
 #include <linux/crc32.h>
@@ -251,7 +250,6 @@ struct saa7164_i2c {
 
        /* I2C I/O */
        struct i2c_adapter              i2c_adap;
-       struct i2c_algo_bit_data        i2c_algo;
        struct i2c_client               i2c_client;
        u32                             i2c_rc;
 };
index fb99ff18be077255810f43e71fd0a9163ee883b5..3149cda1d0dbb8202923ac4550b80f03becd2b9c 100644 (file)
@@ -1,6 +1,7 @@
 config VIDEO_SMIAPP
        tristate "SMIA++/SMIA sensor support"
        depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAVE_CLK
+       depends on MEDIA_CAMERA_SUPPORT
        select VIDEO_SMIAPP_PLL
        ---help---
          This is a generic driver for SMIA++/SMIA camera modules.
index 9cf5bda35fbe1cfe332e8b18ae3d9218b3e8f54b..bfd47c10613433fa50990ea6b13f6a00b09db5fd 100644 (file)
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/regulator/consumer.h>
-#include <linux/slab.h>
 #include <linux/v4l2-mediabus.h>
 #include <media/v4l2-device.h>
 
 #include "smiapp.h"
 
-#define SMIAPP_ALIGN_DIM(dim, flags)           \
-       ((flags) & V4L2_SUBDEV_SEL_FLAG_SIZE_GE \
-        ? ALIGN((dim), 2)                      \
+#define SMIAPP_ALIGN_DIM(dim, flags)   \
+       ((flags) & V4L2_SEL_FLAG_GE     \
+        ? ALIGN((dim), 2)              \
         : (dim) & ~1)
 
 /*
@@ -1631,7 +1630,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev,
        smiapp_get_crop_compose(subdev, fh, crops, &comp, which);
 
        switch (target) {
-       case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+       case V4L2_SEL_TGT_CROP:
                comp->width = crops[SMIAPP_PAD_SINK]->width;
                comp->height = crops[SMIAPP_PAD_SINK]->height;
                if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
@@ -1647,7 +1646,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev,
                        }
                }
                /* Fall through */
-       case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+       case V4L2_SEL_TGT_COMPOSE:
                *crops[SMIAPP_PAD_SRC] = *comp;
                break;
        default:
@@ -1723,7 +1722,7 @@ static int smiapp_set_format(struct v4l2_subdev *subdev,
        if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
                ssd->sink_fmt = *crops[ssd->sink_pad];
        smiapp_propagate(subdev, fh, fmt->which,
-                        V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL);
+                        V4L2_SEL_TGT_CROP);
 
        mutex_unlock(&sensor->mutex);
 
@@ -1748,14 +1747,14 @@ static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w,
        h &= ~1;
        ask_h &= ~1;
 
-       if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_GE) {
+       if (flags & V4L2_SEL_FLAG_GE) {
                if (w < ask_w)
                        val -= SCALING_GOODNESS;
                if (h < ask_h)
                        val -= SCALING_GOODNESS;
        }
 
-       if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_LE) {
+       if (flags & V4L2_SEL_FLAG_LE) {
                if (w > ask_w)
                        val -= SCALING_GOODNESS;
                if (h > ask_h)
@@ -1958,7 +1957,7 @@ static int smiapp_set_compose(struct v4l2_subdev *subdev,
 
        *comp = sel->r;
        smiapp_propagate(subdev, fh, sel->which,
-                        V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL);
+                        V4L2_SEL_TGT_COMPOSE);
 
        if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE)
                return smiapp_update_mode(sensor);
@@ -1974,8 +1973,8 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev,
 
        /* We only implement crop in three places. */
        switch (sel->target) {
-       case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
-       case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+       case V4L2_SEL_TGT_CROP:
+       case V4L2_SEL_TGT_CROP_BOUNDS:
                if (ssd == sensor->pixel_array
                    && sel->pad == SMIAPP_PA_PAD_SRC)
                        return 0;
@@ -1988,8 +1987,8 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev,
                    == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP)
                        return 0;
                return -EINVAL;
-       case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
-       case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS:
+       case V4L2_SEL_TGT_COMPOSE:
+       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
                if (sel->pad == ssd->source_pad)
                        return -EINVAL;
                if (ssd == sensor->binner)
@@ -2051,7 +2050,7 @@ static int smiapp_set_crop(struct v4l2_subdev *subdev,
 
        if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK)
                smiapp_propagate(subdev, fh, sel->which,
-                                V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL);
+                                V4L2_SEL_TGT_CROP);
 
        return 0;
 }
@@ -2085,7 +2084,7 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev,
        }
 
        switch (sel->target) {
-       case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+       case V4L2_SEL_TGT_CROP_BOUNDS:
                if (ssd == sensor->pixel_array) {
                        sel->r.width =
                                sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
@@ -2097,11 +2096,11 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev,
                        sel->r = *comp;
                }
                break;
-       case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
-       case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS:
+       case V4L2_SEL_TGT_CROP:
+       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
                sel->r = *crops[sel->pad];
                break;
-       case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+       case V4L2_SEL_TGT_COMPOSE:
                sel->r = *comp;
                break;
        }
@@ -2148,10 +2147,10 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev,
                              sel->r.height);
 
        switch (sel->target) {
-       case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+       case V4L2_SEL_TGT_CROP:
                ret = smiapp_set_crop(subdev, fh, sel);
                break;
-       case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+       case V4L2_SEL_TGT_COMPOSE:
                ret = smiapp_set_compose(subdev, fh, sel);
                break;
        default:
index 22ea211ab54f144cfb656c2080a17896c93d9538..2bc153e869befb7ab21df85ce809e2f0fde5efe9 100644 (file)
@@ -182,7 +182,7 @@ do {                                                                          \
 #      define V4LDBG(level, name, cmd)                                       \
 do {                                                                          \
        if (debug >= (level))                                                 \
-               v4l_print_ioctl(name, cmd);                                   \
+               v4l_printk_ioctl(name, cmd);                                  \
 } while (0)
 #      define KDBG(level, fmt, args...)                                      \
 do {                                                                          \
index 0421bf9453b4f43d707168e5b8d4e97911401fb7..b03ffecb7438002ee5aad631453d0fa9030c2edc 100644 (file)
@@ -62,7 +62,7 @@ static int soc_camera_power_on(struct soc_camera_device *icd,
        }
 
        if (icl->power) {
-               ret = icl->power(icd->pdev, 1);
+               ret = icl->power(icd->control, 1);
                if (ret < 0) {
                        dev_err(icd->pdev,
                                "Platform failed to power-on the camera.\n");
@@ -78,7 +78,7 @@ static int soc_camera_power_on(struct soc_camera_device *icd,
 
 esdpwr:
        if (icl->power)
-               icl->power(icd->pdev, 0);
+               icl->power(icd->control, 0);
 elinkpwr:
        regulator_bulk_disable(icl->num_regulators,
                               icl->regulators);
@@ -95,7 +95,7 @@ static int soc_camera_power_off(struct soc_camera_device *icd,
                return ret;
 
        if (icl->power) {
-               ret = icl->power(icd->pdev, 0);
+               ret = icl->power(icd->control, 0);
                if (ret < 0) {
                        dev_err(icd->pdev,
                                "Platform failed to power-off the camera.\n");
@@ -1518,6 +1518,7 @@ static int __devexit soc_camera_pdrv_remove(struct platform_device *pdev)
 }
 
 static struct platform_driver __refdata soc_camera_pdrv = {
+       .probe = soc_camera_pdrv_probe,
        .remove  = __devexit_p(soc_camera_pdrv_remove),
        .driver  = {
                .name   = "soc-camera-pdrv",
@@ -1527,7 +1528,7 @@ static struct platform_driver __refdata soc_camera_pdrv = {
 
 static int __init soc_camera_init(void)
 {
-       return platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe);
+       return platform_driver_register(&soc_camera_pdrv);
 }
 
 static void __exit soc_camera_exit(void)
index c096b3f742003bcbcfc40df79b49a2728f8b9539..7b1f6ebd0e2ca31792e042300ce160eb5389b933 100644 (file)
@@ -53,7 +53,8 @@ int debug_mode;
 module_param(debug_mode, int, 0644);
 MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose");
 
-static const char *firmware_name = "tlg2300_firmware.bin";
+#define TLG2300_FIRMWARE "tlg2300_firmware.bin"
+static const char *firmware_name = TLG2300_FIRMWARE;
 static struct usb_driver poseidon_driver;
 static LIST_HEAD(pd_device_list);
 
@@ -532,3 +533,4 @@ MODULE_AUTHOR("Telegent Systems");
 MODULE_DESCRIPTION("For tlg2300-based USB device ");
 MODULE_LICENSE("GPL");
 MODULE_VERSION("0.0.2");
+MODULE_FIRMWARE(TLG2300_FIRMWARE);
index 1ad5ab6ce5cf9ea46187d2ac03224033957b1247..b5a819af2b8c4c3c36ae00fdf2eb7334863de030 100644 (file)
@@ -228,6 +228,16 @@ static int fe_has_signal(struct dvb_frontend *fe)
        return strength;
 }
 
+static int fe_get_afc(struct dvb_frontend *fe)
+{
+       s32 afc = 0;
+
+       if (fe->ops.tuner_ops.get_afc)
+               fe->ops.tuner_ops.get_afc(fe, &afc);
+
+       return 0;
+}
+
 static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg)
 {
        struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
@@ -247,6 +257,7 @@ static struct analog_demod_ops tuner_analog_ops = {
        .set_params     = fe_set_params,
        .standby        = fe_standby,
        .has_signal     = fe_has_signal,
+       .get_afc        = fe_get_afc,
        .set_config     = fe_set_config,
        .tuner_status   = tuner_status
 };
@@ -1178,6 +1189,8 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
                return 0;
        if (vt->type == t->mode && analog_ops->get_afc)
                vt->afc = analog_ops->get_afc(&t->fe);
+       if (analog_ops->has_signal)
+               vt->signal = analog_ops->has_signal(&t->fe);
        if (vt->type != V4L2_TUNER_RADIO) {
                vt->capability |= V4L2_TUNER_CAP_NORM;
                vt->rangelow = tv_range[0] * 16;
@@ -1197,8 +1210,6 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
                                V4L2_TUNER_SUB_STEREO :
                                V4L2_TUNER_SUB_MONO;
                }
-               if (analog_ops->has_signal)
-                       vt->signal = analog_ops->has_signal(&t->fe);
                vt->audmode = t->audmode;
        }
        vt->capability |= V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
index c5b1a7365e4f60efc174e1bad6be3aac3f535807..321b3153df87abe1537bab907121d98012d25838 100644 (file)
@@ -59,8 +59,8 @@ struct CHIPSTATE;
 typedef int  (*getvalue)(int);
 typedef int  (*checkit)(struct CHIPSTATE*);
 typedef int  (*initialize)(struct CHIPSTATE*);
-typedef int  (*getmode)(struct CHIPSTATE*);
-typedef void (*setmode)(struct CHIPSTATE*, int mode);
+typedef int  (*getrxsubchans)(struct CHIPSTATE *);
+typedef void (*setaudmode)(struct CHIPSTATE*, int mode);
 
 /* i2c command */
 typedef struct AUDIOCMD {
@@ -96,8 +96,8 @@ struct CHIPDESC {
        getvalue volfunc,treblefunc,bassfunc;
 
        /* get/set mode */
-       getmode  getmode;
-       setmode  setmode;
+       getrxsubchans   getrxsubchans;
+       setaudmode      setaudmode;
 
        /* input switch register + values for v4l inputs */
        int  inputreg;
@@ -118,7 +118,7 @@ struct CHIPSTATE {
        audiocmd   shadow;
 
        /* current settings */
-       __u16 left,right,treble,bass,muted,mode;
+       __u16 left, right, treble, bass, muted;
        int prevmode;
        int radio;
        int input;
@@ -126,7 +126,6 @@ struct CHIPSTATE {
        /* thread */
        struct task_struct   *thread;
        struct timer_list    wt;
-       int                  watch_stereo;
        int                  audmode;
 };
 
@@ -288,7 +287,7 @@ static int chip_thread(void *data)
        struct CHIPSTATE *chip = data;
        struct CHIPDESC  *desc = chip->desc;
        struct v4l2_subdev *sd = &chip->sd;
-       int mode;
+       int mode, selected;
 
        v4l2_dbg(1, debug, sd, "thread started\n");
        set_freezable();
@@ -302,12 +301,12 @@ static int chip_thread(void *data)
                        break;
                v4l2_dbg(1, debug, sd, "thread wakeup\n");
 
-               /* don't do anything for radio or if mode != auto */
-               if (chip->radio || chip->mode != 0)
+               /* don't do anything for radio */
+               if (chip->radio)
                        continue;
 
                /* have a look what's going on */
-               mode = desc->getmode(chip);
+               mode = desc->getrxsubchans(chip);
                if (mode == chip->prevmode)
                        continue;
 
@@ -316,16 +315,32 @@ static int chip_thread(void *data)
 
                chip->prevmode = mode;
 
-               if (mode & V4L2_TUNER_MODE_STEREO)
-                       desc->setmode(chip, V4L2_TUNER_MODE_STEREO);
-               if (mode & V4L2_TUNER_MODE_LANG1_LANG2)
-                       desc->setmode(chip, V4L2_TUNER_MODE_STEREO);
-               else if (mode & V4L2_TUNER_MODE_LANG1)
-                       desc->setmode(chip, V4L2_TUNER_MODE_LANG1);
-               else if (mode & V4L2_TUNER_MODE_LANG2)
-                       desc->setmode(chip, V4L2_TUNER_MODE_LANG2);
-               else
-                       desc->setmode(chip, V4L2_TUNER_MODE_MONO);
+               selected = V4L2_TUNER_MODE_MONO;
+               switch (chip->audmode) {
+               case V4L2_TUNER_MODE_MONO:
+                       if (mode & V4L2_TUNER_SUB_LANG1)
+                               selected = V4L2_TUNER_MODE_LANG1;
+                       break;
+               case V4L2_TUNER_MODE_STEREO:
+               case V4L2_TUNER_MODE_LANG1:
+                       if (mode & V4L2_TUNER_SUB_LANG1)
+                               selected = V4L2_TUNER_MODE_LANG1;
+                       else if (mode & V4L2_TUNER_SUB_STEREO)
+                               selected = V4L2_TUNER_MODE_STEREO;
+                       break;
+               case V4L2_TUNER_MODE_LANG2:
+                       if (mode & V4L2_TUNER_SUB_LANG2)
+                               selected = V4L2_TUNER_MODE_LANG2;
+                       else if (mode & V4L2_TUNER_SUB_STEREO)
+                               selected = V4L2_TUNER_MODE_STEREO;
+                       break;
+               case V4L2_TUNER_MODE_LANG1_LANG2:
+                       if (mode & V4L2_TUNER_SUB_LANG2)
+                               selected = V4L2_TUNER_MODE_LANG1_LANG2;
+                       else if (mode & V4L2_TUNER_SUB_STEREO)
+                               selected = V4L2_TUNER_MODE_STEREO;
+               }
+               desc->setaudmode(chip, selected);
 
                /* schedule next check */
                mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
@@ -358,24 +373,25 @@ static int chip_thread(void *data)
 #define TDA9840_TEST_INT1SN 0x1 /* Integration time 0.5s when set */
 #define TDA9840_TEST_INTFU 0x02 /* Disables integrator function */
 
-static int tda9840_getmode(struct CHIPSTATE *chip)
+static int tda9840_getrxsubchans(struct CHIPSTATE *chip)
 {
        struct v4l2_subdev *sd = &chip->sd;
        int val, mode;
 
        val = chip_read(chip);
-       mode = V4L2_TUNER_MODE_MONO;
+       mode = V4L2_TUNER_SUB_MONO;
        if (val & TDA9840_DS_DUAL)
-               mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+               mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
        if (val & TDA9840_ST_STEREO)
-               mode |= V4L2_TUNER_MODE_STEREO;
+               mode = V4L2_TUNER_SUB_STEREO;
 
-       v4l2_dbg(1, debug, sd, "tda9840_getmode(): raw chip read: %d, return: %d\n",
+       v4l2_dbg(1, debug, sd,
+               "tda9840_getrxsubchans(): raw chip read: %d, return: %d\n",
                val, mode);
        return mode;
 }
 
-static void tda9840_setmode(struct CHIPSTATE *chip, int mode)
+static void tda9840_setaudmode(struct CHIPSTATE *chip, int mode)
 {
        int update = 1;
        int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e;
@@ -393,6 +409,9 @@ static void tda9840_setmode(struct CHIPSTATE *chip, int mode)
        case V4L2_TUNER_MODE_LANG2:
                t |= TDA9840_DUALB;
                break;
+       case V4L2_TUNER_MODE_LANG1_LANG2:
+               t |= TDA9840_DUALAB;
+               break;
        default:
                update = 0;
        }
@@ -477,6 +496,7 @@ static int tda9840_checkit(struct CHIPSTATE *chip)
 /* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */
 /* Common to TDA9855 and TDA9850: */
 #define TDA985x_SAP    3<<6 /* Selects SAP output, mute if not received */
+#define TDA985x_MONOSAP        2<<6 /* Selects Mono on left, SAP on right */
 #define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */
 #define TDA985x_MONO   0    /* Forces Mono output */
 #define TDA985x_LMU    1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */
@@ -513,18 +533,22 @@ static int tda9855_volume(int val) { return val/0x2e8+0x27; }
 static int tda9855_bass(int val)   { return val/0xccc+0x06; }
 static int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; }
 
-static int  tda985x_getmode(struct CHIPSTATE *chip)
+static int  tda985x_getrxsubchans(struct CHIPSTATE *chip)
 {
-       int mode;
+       int mode, val;
 
-       mode = ((TDA985x_STP | TDA985x_SAPP) &
-               chip_read(chip)) >> 4;
        /* Add mono mode regardless of SAP and stereo */
        /* Allows forced mono */
-       return mode | V4L2_TUNER_MODE_MONO;
+       mode = V4L2_TUNER_SUB_MONO;
+       val = chip_read(chip);
+       if (val & TDA985x_STP)
+               mode = V4L2_TUNER_SUB_STEREO;
+       if (val & TDA985x_SAPP)
+               mode |= V4L2_TUNER_SUB_SAP;
+       return mode;
 }
 
-static void tda985x_setmode(struct CHIPSTATE *chip, int mode)
+static void tda985x_setaudmode(struct CHIPSTATE *chip, int mode)
 {
        int update = 1;
        int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f;
@@ -534,11 +558,15 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode)
                c6 |= TDA985x_MONO;
                break;
        case V4L2_TUNER_MODE_STEREO:
+       case V4L2_TUNER_MODE_LANG1:
                c6 |= TDA985x_STEREO;
                break;
-       case V4L2_TUNER_MODE_LANG1:
+       case V4L2_TUNER_MODE_SAP:
                c6 |= TDA985x_SAP;
                break;
+       case V4L2_TUNER_MODE_LANG1_LANG2:
+               c6 |= TDA985x_MONOSAP;
+               break;
        default:
                update = 0;
        }
@@ -583,9 +611,10 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode)
 #define TDA9873_TR_MASK     (7 << 2)
 #define TDA9873_TR_MONO     4
 #define TDA9873_TR_STEREO   1 << 4
-#define TDA9873_TR_REVERSE  (1 << 3) & (1 << 2)
+#define TDA9873_TR_REVERSE  ((1 << 3) | (1 << 2))
 #define TDA9873_TR_DUALA    1 << 2
 #define TDA9873_TR_DUALB    1 << 3
+#define TDA9873_TR_DUALAB   0
 
 /* output level controls
  * B5:  output level switch (0 = reduced gain, 1 = normal gain)
@@ -653,46 +682,51 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode)
 #define TDA9873_MOUT_DUALA  0
 #define TDA9873_MOUT_DUALB  1 << 3
 #define TDA9873_MOUT_ST     1 << 4
-#define TDA9873_MOUT_EXTM   (1 << 4 ) & (1 << 3)
+#define TDA9873_MOUT_EXTM   ((1 << 4) | (1 << 3))
 #define TDA9873_MOUT_EXTL   1 << 5
-#define TDA9873_MOUT_EXTR   (1 << 5 ) & (1 << 3)
-#define TDA9873_MOUT_EXTLR  (1 << 5 ) & (1 << 4)
-#define TDA9873_MOUT_MUTE   (1 << 5 ) & (1 << 4) & (1 << 3)
+#define TDA9873_MOUT_EXTR   ((1 << 5) | (1 << 3))
+#define TDA9873_MOUT_EXTLR  ((1 << 5) | (1 << 4))
+#define TDA9873_MOUT_MUTE   ((1 << 5) | (1 << 4) | (1 << 3))
 
 /* Status bits: (chip read) */
 #define TDA9873_PONR        0 /* Power-on reset detected if = 1 */
 #define TDA9873_STEREO      2 /* Stereo sound is identified     */
 #define TDA9873_DUAL        4 /* Dual sound is identified       */
 
-static int tda9873_getmode(struct CHIPSTATE *chip)
+static int tda9873_getrxsubchans(struct CHIPSTATE *chip)
 {
        struct v4l2_subdev *sd = &chip->sd;
        int val,mode;
 
        val = chip_read(chip);
-       mode = V4L2_TUNER_MODE_MONO;
+       mode = V4L2_TUNER_SUB_MONO;
        if (val & TDA9873_STEREO)
-               mode |= V4L2_TUNER_MODE_STEREO;
+               mode = V4L2_TUNER_SUB_STEREO;
        if (val & TDA9873_DUAL)
-               mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
-       v4l2_dbg(1, debug, sd, "tda9873_getmode(): raw chip read: %d, return: %d\n",
+               mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+       v4l2_dbg(1, debug, sd,
+               "tda9873_getrxsubchans(): raw chip read: %d, return: %d\n",
                val, mode);
        return mode;
 }
 
-static void tda9873_setmode(struct CHIPSTATE *chip, int mode)
+static void tda9873_setaudmode(struct CHIPSTATE *chip, int mode)
 {
        struct v4l2_subdev *sd = &chip->sd;
        int sw_data  = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK;
        /*      int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */
 
        if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) {
-               v4l2_dbg(1, debug, sd, "tda9873_setmode(): external input\n");
+               v4l2_dbg(1, debug, sd,
+                        "tda9873_setaudmode(): external input\n");
                return;
        }
 
-       v4l2_dbg(1, debug, sd, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
-       v4l2_dbg(1, debug, sd, "tda9873_setmode(): sw_data  = %d\n", sw_data);
+       v4l2_dbg(1, debug, sd,
+                "tda9873_setaudmode(): chip->shadow.bytes[%d] = %d\n",
+                TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
+       v4l2_dbg(1, debug, sd, "tda9873_setaudmode(): sw_data  = %d\n",
+                sw_data);
 
        switch (mode) {
        case V4L2_TUNER_MODE_MONO:
@@ -707,13 +741,16 @@ static void tda9873_setmode(struct CHIPSTATE *chip, int mode)
        case V4L2_TUNER_MODE_LANG2:
                sw_data |= TDA9873_TR_DUALB;
                break;
+       case V4L2_TUNER_MODE_LANG1_LANG2:
+               sw_data |= TDA9873_TR_DUALAB;
+               break;
        default:
-               chip->mode = 0;
                return;
        }
 
        chip_write(chip, TDA9873_SW, sw_data);
-       v4l2_dbg(1, debug, sd, "tda9873_setmode(): req. mode %d; chip_write: %d\n",
+       v4l2_dbg(1, debug, sd,
+               "tda9873_setaudmode(): req. mode %d; chip_write: %d\n",
                mode, sw_data);
 }
 
@@ -859,13 +896,13 @@ static int tda9874a_setup(struct CHIPSTATE *chip)
        return 1;
 }
 
-static int tda9874a_getmode(struct CHIPSTATE *chip)
+static int tda9874a_getrxsubchans(struct CHIPSTATE *chip)
 {
        struct v4l2_subdev *sd = &chip->sd;
        int dsr,nsr,mode;
        int necr; /* just for debugging */
 
-       mode = V4L2_TUNER_MODE_MONO;
+       mode = V4L2_TUNER_SUB_MONO;
 
        if(-1 == (dsr = chip_read2(chip,TDA9874A_DSR)))
                return mode;
@@ -888,22 +925,23 @@ static int tda9874a_getmode(struct CHIPSTATE *chip)
                 * external 4052 multiplexer in audio_hook().
                 */
                if(nsr & 0x02) /* NSR.S/MB=1 */
-                       mode |= V4L2_TUNER_MODE_STEREO;
+                       mode = V4L2_TUNER_SUB_STEREO;
                if(nsr & 0x01) /* NSR.D/SB=1 */
-                       mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+                       mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
        } else {
                if(dsr & 0x02) /* DSR.IDSTE=1 */
-                       mode |= V4L2_TUNER_MODE_STEREO;
+                       mode = V4L2_TUNER_SUB_STEREO;
                if(dsr & 0x04) /* DSR.IDDUA=1 */
-                       mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+                       mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
        }
 
-       v4l2_dbg(1, debug, sd, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
+       v4l2_dbg(1, debug, sd,
+                "tda9874a_getrxsubchans(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
                 dsr, nsr, necr, mode);
        return mode;
 }
 
-static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
+static void tda9874a_setaudmode(struct CHIPSTATE *chip, int mode)
 {
        struct v4l2_subdev *sd = &chip->sd;
 
@@ -939,14 +977,18 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
                        aosr = 0xa0; /* auto-select, dual B/B */
                        mdacosr = (tda9874a_mode) ? 0x83:0x81;
                        break;
+               case V4L2_TUNER_MODE_LANG1_LANG2:
+                       aosr = 0x00; /* always route L to L and R to R */
+                       mdacosr = (tda9874a_mode) ? 0x82:0x80;
+                       break;
                default:
-                       chip->mode = 0;
                        return;
                }
                chip_write(chip, TDA9874A_AOSR, aosr);
                chip_write(chip, TDA9874A_MDACOSR, mdacosr);
 
-               v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
+               v4l2_dbg(1, debug, sd,
+                       "tda9874a_setaudmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
                        mode, aosr, mdacosr);
 
        } else { /* dic == 0x07 */
@@ -974,14 +1016,18 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
                        fmmr = 0x02; /* dual */
                        aosr = 0x20; /* dual B/B */
                        break;
+               case V4L2_TUNER_MODE_LANG1_LANG2:
+                       fmmr = 0x02; /* dual */
+                       aosr = 0x00; /* dual A/B */
+                       break;
                default:
-                       chip->mode = 0;
                        return;
                }
                chip_write(chip, TDA9874A_FMMR, fmmr);
                chip_write(chip, TDA9874A_AOSR, aosr);
 
-               v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
+               v4l2_dbg(1, debug, sd,
+                       "tda9874a_setaudmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
                        mode, fmmr, aosr);
        }
 }
@@ -1226,25 +1272,33 @@ static int tea6320_initialize(struct CHIPSTATE * chip)
 static int tda8425_shift10(int val) { return (val >> 10) | 0xc0; }
 static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; }
 
-static void tda8425_setmode(struct CHIPSTATE *chip, int mode)
+static void tda8425_setaudmode(struct CHIPSTATE *chip, int mode)
 {
        int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1;
 
-       if (mode & V4L2_TUNER_MODE_LANG1) {
+       switch (mode) {
+       case V4L2_TUNER_MODE_LANG1:
                s1 |= TDA8425_S1_ML_SOUND_A;
                s1 |= TDA8425_S1_STEREO_PSEUDO;
-
-       } else if (mode & V4L2_TUNER_MODE_LANG2) {
+               break;
+       case V4L2_TUNER_MODE_LANG2:
                s1 |= TDA8425_S1_ML_SOUND_B;
                s1 |= TDA8425_S1_STEREO_PSEUDO;
-
-       } else {
+               break;
+       case V4L2_TUNER_MODE_LANG1_LANG2:
                s1 |= TDA8425_S1_ML_STEREO;
-
-               if (mode & V4L2_TUNER_MODE_MONO)
-                       s1 |= TDA8425_S1_STEREO_MONO;
-               if (mode & V4L2_TUNER_MODE_STEREO)
-                       s1 |= TDA8425_S1_STEREO_SPATIAL;
+               s1 |= TDA8425_S1_STEREO_LINEAR;
+               break;
+       case V4L2_TUNER_MODE_MONO:
+               s1 |= TDA8425_S1_ML_STEREO;
+               s1 |= TDA8425_S1_STEREO_MONO;
+               break;
+       case V4L2_TUNER_MODE_STEREO:
+               s1 |= TDA8425_S1_ML_STEREO;
+               s1 |= TDA8425_S1_STEREO_SPATIAL;
+               break;
+       default:
+               return;
        }
        chip_write(chip,TDA8425_S1,s1);
 }
@@ -1297,18 +1351,20 @@ static void tda8425_setmode(struct CHIPSTATE *chip, int mode)
  * stereo  L  L
  * BIL     H  L
  */
-static int ta8874z_getmode(struct CHIPSTATE *chip)
+static int ta8874z_getrxsubchans(struct CHIPSTATE *chip)
 {
        int val, mode;
 
        val = chip_read(chip);
-       mode = V4L2_TUNER_MODE_MONO;
+       mode = V4L2_TUNER_SUB_MONO;
        if (val & TA8874Z_B1){
-               mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+               mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
        }else if (!(val & TA8874Z_B0)){
-               mode |= V4L2_TUNER_MODE_STEREO;
+               mode = V4L2_TUNER_SUB_STEREO;
        }
-       /* v4l_dbg(1, debug, chip->c, "ta8874z_getmode(): raw chip read: 0x%02x, return: 0x%02x\n", val, mode); */
+       /* v4l2_dbg(1, debug, &chip->sd,
+                "ta8874z_getrxsubchans(): raw chip read: 0x%02x, return: 0x%02x\n",
+                val, mode); */
        return mode;
 }
 
@@ -1316,14 +1372,15 @@ static audiocmd ta8874z_stereo = { 2, {0, TA8874Z_SEPARATION_DEFAULT}};
 static audiocmd ta8874z_mono = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}};
 static audiocmd ta8874z_main = {2, { 0, TA8874Z_SEPARATION_DEFAULT}};
 static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}};
+static audiocmd ta8874z_both = {2, { TA8874Z_MODE_MAIN | TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}};
 
-static void ta8874z_setmode(struct CHIPSTATE *chip, int mode)
+static void ta8874z_setaudmode(struct CHIPSTATE *chip, int mode)
 {
        struct v4l2_subdev *sd = &chip->sd;
        int update = 1;
        audiocmd *t = NULL;
 
-       v4l2_dbg(1, debug, sd, "ta8874z_setmode(): mode: 0x%02x\n", mode);
+       v4l2_dbg(1, debug, sd, "ta8874z_setaudmode(): mode: 0x%02x\n", mode);
 
        switch(mode){
        case V4L2_TUNER_MODE_MONO:
@@ -1338,6 +1395,9 @@ static void ta8874z_setmode(struct CHIPSTATE *chip, int mode)
        case V4L2_TUNER_MODE_LANG2:
                t = &ta8874z_sub;
                break;
+       case V4L2_TUNER_MODE_LANG1_LANG2:
+               t = &ta8874z_both;
+               break;
        default:
                update = 0;
        }
@@ -1394,8 +1454,8 @@ static struct CHIPDESC chiplist[] = {
 
                /* callbacks */
                .checkit    = tda9840_checkit,
-               .getmode    = tda9840_getmode,
-               .setmode    = tda9840_setmode,
+               .getrxsubchans = tda9840_getrxsubchans,
+               .setaudmode = tda9840_setaudmode,
 
                .init       = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN
                                /* ,TDA9840_SW, TDA9840_MONO */} }
@@ -1410,8 +1470,8 @@ static struct CHIPDESC chiplist[] = {
 
                /* callbacks */
                .checkit    = tda9873_checkit,
-               .getmode    = tda9873_getmode,
-               .setmode    = tda9873_setmode,
+               .getrxsubchans = tda9873_getrxsubchans,
+               .setaudmode = tda9873_setaudmode,
 
                .init       = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } },
                .inputreg   = TDA9873_SW,
@@ -1430,8 +1490,8 @@ static struct CHIPDESC chiplist[] = {
                /* callbacks */
                .initialize = tda9874a_initialize,
                .checkit    = tda9874a_checkit,
-               .getmode    = tda9874a_getmode,
-               .setmode    = tda9874a_setmode,
+               .getrxsubchans = tda9874a_getrxsubchans,
+               .setaudmode = tda9874a_setaudmode,
        },
        {
                .name       = "tda9875",
@@ -1460,8 +1520,8 @@ static struct CHIPDESC chiplist[] = {
                .addr_hi    = I2C_ADDR_TDA985x_H >> 1,
                .registers  = 11,
 
-               .getmode    = tda985x_getmode,
-               .setmode    = tda985x_setmode,
+               .getrxsubchans = tda985x_getrxsubchans,
+               .setaudmode = tda985x_setaudmode,
 
                .init       = { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } }
        },
@@ -1482,8 +1542,8 @@ static struct CHIPDESC chiplist[] = {
                .volfunc    = tda9855_volume,
                .bassfunc   = tda9855_bass,
                .treblefunc = tda9855_treble,
-               .getmode    = tda985x_getmode,
-               .setmode    = tda985x_setmode,
+               .getrxsubchans = tda985x_getrxsubchans,
+               .setaudmode = tda985x_setaudmode,
 
                .init       = { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2,
                                    TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT,
@@ -1564,7 +1624,7 @@ static struct CHIPDESC chiplist[] = {
                .volfunc    = tda8425_shift10,
                .bassfunc   = tda8425_shift12,
                .treblefunc = tda8425_shift12,
-               .setmode    = tda8425_setmode,
+               .setaudmode = tda8425_setaudmode,
 
                .inputreg   = TDA8425_S1,
                .inputmap   = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 },
@@ -1593,11 +1653,10 @@ static struct CHIPDESC chiplist[] = {
                .addr_lo    = I2C_ADDR_TDA9840 >> 1,
                .addr_hi    = I2C_ADDR_TDA9840 >> 1,
                .registers  = 2,
-               .flags      = CHIP_NEED_CHECKMODE,
 
                /* callbacks */
-               .getmode    = ta8874z_getmode,
-               .setmode    = ta8874z_setmode,
+               .getrxsubchans = ta8874z_getrxsubchans,
+               .setaudmode = ta8874z_setaudmode,
 
                .init       = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}},
        },
@@ -1736,7 +1795,6 @@ static int tvaudio_s_radio(struct v4l2_subdev *sd)
        struct CHIPSTATE *chip = to_state(sd);
 
        chip->radio = 1;
-       chip->watch_stereo = 0;
        /* del_timer(&chip->wt); */
        return 0;
 }
@@ -1793,9 +1851,8 @@ static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
 {
        struct CHIPSTATE *chip = to_state(sd);
        struct CHIPDESC *desc = chip->desc;
-       int mode = 0;
 
-       if (!desc->setmode)
+       if (!desc->setaudmode)
                return 0;
        if (chip->radio)
                return 0;
@@ -1805,22 +1862,18 @@ static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
        case V4L2_TUNER_MODE_STEREO:
        case V4L2_TUNER_MODE_LANG1:
        case V4L2_TUNER_MODE_LANG2:
-               mode = vt->audmode;
-               break;
        case V4L2_TUNER_MODE_LANG1_LANG2:
-               mode = V4L2_TUNER_MODE_STEREO;
                break;
        default:
                return -EINVAL;
        }
        chip->audmode = vt->audmode;
 
-       if (mode) {
-               chip->watch_stereo = 0;
-               /* del_timer(&chip->wt); */
-               chip->mode = mode;
-               desc->setmode(chip, mode);
-       }
+       if (chip->thread)
+               wake_up_process(chip->thread);
+       else
+               desc->setaudmode(chip, vt->audmode);
+
        return 0;
 }
 
@@ -1828,30 +1881,17 @@ static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
 {
        struct CHIPSTATE *chip = to_state(sd);
        struct CHIPDESC *desc = chip->desc;
-       int mode = V4L2_TUNER_MODE_MONO;
 
-       if (!desc->getmode)
+       if (!desc->getrxsubchans)
                return 0;
        if (chip->radio)
                return 0;
 
        vt->audmode = chip->audmode;
-       vt->rxsubchans = 0;
+       vt->rxsubchans = desc->getrxsubchans(chip);
        vt->capability = V4L2_TUNER_CAP_STEREO |
                V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
 
-       mode = desc->getmode(chip);
-
-       if (mode & V4L2_TUNER_MODE_MONO)
-               vt->rxsubchans |= V4L2_TUNER_SUB_MONO;
-       if (mode & V4L2_TUNER_MODE_STEREO)
-               vt->rxsubchans |= V4L2_TUNER_SUB_STEREO;
-       /* Note: for SAP it should be mono/lang2 or stereo/lang2.
-          When this module is converted fully to v4l2, then this
-          should change for those chips that can detect SAP. */
-       if (mode & V4L2_TUNER_MODE_LANG1)
-               vt->rxsubchans = V4L2_TUNER_SUB_LANG1 |
-                       V4L2_TUNER_SUB_LANG2;
        return 0;
 }
 
@@ -1868,9 +1908,7 @@ static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fr
        struct CHIPSTATE *chip = to_state(sd);
        struct CHIPDESC *desc = chip->desc;
 
-       chip->mode = 0; /* automatic */
-
-       /* For chips that provide getmode and setmode, and doesn't
+       /* For chips that provide getrxsubchans and setaudmode, and doesn't
           automatically follows the stereo carrier, a kthread is
           created to set the audio standard. In this case, when then
           the video channel is changed, tvaudio starts on MONO mode.
@@ -1879,9 +1917,8 @@ static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fr
           audio carrier.
         */
        if (chip->thread) {
-               desc->setmode(chip, V4L2_TUNER_MODE_MONO);
-               if (chip->prevmode != V4L2_TUNER_MODE_MONO)
-                       chip->prevmode = -1; /* reset previous mode */
+               desc->setaudmode(chip, V4L2_TUNER_MODE_MONO);
+               chip->prevmode = -1; /* reset previous mode */
                mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
        }
        return 0;
@@ -2023,7 +2060,7 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *
        chip->thread = NULL;
        init_timer(&chip->wt);
        if (desc->flags & CHIP_NEED_CHECKMODE) {
-               if (!desc->getmode || !desc->setmode) {
+               if (!desc->getrxsubchans || !desc->setaudmode) {
                        /* This shouldn't be happen. Warn user, but keep working
                           without kthread
                         */
index b7867427e5c4a03673789ed80b9ac67643cbfa7e..a751b6c146fdb523a6a8f3c00cdd9edf84f1f716 100644 (file)
@@ -61,13 +61,20 @@ static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr)
        int rc;
 
        buffer[0] = addr;
-       if (1 != (rc = i2c_master_send(c, buffer, 1)))
-               v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
+
+       rc = i2c_master_send(c, buffer, 1);
+       if (rc < 0) {
+               v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
+               return rc;
+       }
 
        msleep(10);
 
-       if (1 != (rc = i2c_master_recv(c, buffer, 1)))
-               v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
+       rc = i2c_master_recv(c, buffer, 1);
+       if (rc < 0) {
+               v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
+               return rc;
+       }
 
        v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]);
 
@@ -250,7 +257,7 @@ static inline void tvp5150_selmux(struct v4l2_subdev *sd)
        int opmode = 0;
        struct tvp5150 *decoder = to_tvp5150(sd);
        int input = 0;
-       unsigned char val;
+       int val;
 
        if ((decoder->output & TVP5150_BLACK_SCREEN) || !decoder->enable)
                input = 8;
@@ -279,6 +286,11 @@ static inline void tvp5150_selmux(struct v4l2_subdev *sd)
         * For Composite and TV, it should be the reverse
         */
        val = tvp5150_read(sd, TVP5150_MISC_CTL);
+       if (val < 0) {
+               v4l2_err(sd, "%s: failed with error = %d\n", __func__, val);
+               return;
+       }
+
        if (decoder->input == TVP5150_SVIDEO)
                val = (val & ~0x40) | 0x10;
        else
@@ -676,6 +688,7 @@ static int tvp5150_get_vbi(struct v4l2_subdev *sd,
        v4l2_std_id std = decoder->norm;
        u8 reg;
        int pos, type = 0;
+       int i, ret = 0;
 
        if (std == V4L2_STD_ALL) {
                v4l2_err(sd, "VBI can't be configured without knowing number of lines\n");
@@ -690,13 +703,17 @@ static int tvp5150_get_vbi(struct v4l2_subdev *sd,
 
        reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
 
-       pos = tvp5150_read(sd, reg) & 0x0f;
-       if (pos < 0x0f)
-               type = regs[pos].type.vbi_type;
-
-       pos = tvp5150_read(sd, reg + 1) & 0x0f;
-       if (pos < 0x0f)
-               type |= regs[pos].type.vbi_type;
+       for (i = 0; i <= 1; i++) {
+               ret = tvp5150_read(sd, reg + i);
+               if (ret < 0) {
+                       v4l2_err(sd, "%s: failed with error = %d\n",
+                                __func__, ret);
+                       return 0;
+               }
+               pos = ret & 0x0f;
+               if (pos < 0x0f)
+                       type |= regs[pos].type.vbi_type;
+       }
 
        return type;
 }
@@ -1031,13 +1048,21 @@ static int tvp5150_g_chip_ident(struct v4l2_subdev *sd,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
 {
+       int res;
+
        struct i2c_client *client = v4l2_get_subdevdata(sd);
 
        if (!v4l2_chip_match_i2c_client(client, &reg->match))
                return -EINVAL;
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
-       reg->val = tvp5150_read(sd, reg->reg & 0xff);
+       res = tvp5150_read(sd, reg->reg & 0xff);
+       if (res < 0) {
+               v4l2_err(sd, "%s: failed with error = %d\n", __func__, res);
+               return res;
+       }
+
+       reg->val = res;
        reg->size = 1;
        return 0;
 }
@@ -1126,7 +1151,8 @@ static int tvp5150_probe(struct i2c_client *c,
 {
        struct tvp5150 *core;
        struct v4l2_subdev *sd;
-       u8 msb_id, lsb_id, msb_rom, lsb_rom;
+       int tvp5150_id[4];
+       int i, res;
 
        /* Check if the adapter supports the needed features */
        if (!i2c_check_functionality(c->adapter,
@@ -1139,26 +1165,37 @@ static int tvp5150_probe(struct i2c_client *c,
        }
        sd = &core->sd;
        v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
+
+       /* 
+        * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID,
+        * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER 
+        */
+       for (i = 0; i < 4; i++) {
+               res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i);
+               if (res < 0)
+                       goto free_core;
+               tvp5150_id[i] = res;
+       }
+
        v4l_info(c, "chip found @ 0x%02x (%s)\n",
                 c->addr << 1, c->adapter->name);
 
-       msb_id = tvp5150_read(sd, TVP5150_MSB_DEV_ID);
-       lsb_id = tvp5150_read(sd, TVP5150_LSB_DEV_ID);
-       msb_rom = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER);
-       lsb_rom = tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
-
-       if (msb_rom == 4 && lsb_rom == 0) { /* Is TVP5150AM1 */
-               v4l2_info(sd, "tvp%02x%02xam1 detected.\n", msb_id, lsb_id);
+       if (tvp5150_id[2] == 4 && tvp5150_id[3] == 0) { /* Is TVP5150AM1 */
+               v4l2_info(sd, "tvp%02x%02xam1 detected.\n",
+                         tvp5150_id[0], tvp5150_id[1]);
 
                /* ITU-T BT.656.4 timing */
                tvp5150_write(sd, TVP5150_REV_SELECT, 0);
        } else {
-               if (msb_rom == 3 || lsb_rom == 0x21) { /* Is TVP5150A */
-                       v4l2_info(sd, "tvp%02x%02xa detected.\n", msb_id, lsb_id);
+               /* Is TVP5150A */
+               if (tvp5150_id[2] == 3 || tvp5150_id[3] == 0x21) {
+                       v4l2_info(sd, "tvp%02x%02xa detected.\n",
+                                 tvp5150_id[2], tvp5150_id[3]);
                } else {
                        v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n",
-                                       msb_id, lsb_id);
-                       v4l2_info(sd, "*** Rom ver is %d.%d\n", msb_rom, lsb_rom);
+                                 tvp5150_id[2], tvp5150_id[3]);
+                       v4l2_info(sd, "*** Rom ver is %d.%d\n",
+                                 tvp5150_id[2], tvp5150_id[3]);
                }
        }
 
@@ -1177,11 +1214,9 @@ static int tvp5150_probe(struct i2c_client *c,
                        V4L2_CID_HUE, -128, 127, 1, 0);
        sd->ctrl_handler = &core->hdl;
        if (core->hdl.error) {
-               int err = core->hdl.error;
-
+               res = core->hdl.error;
                v4l2_ctrl_handler_free(&core->hdl);
-               kfree(core);
-               return err;
+               goto free_core;
        }
        v4l2_ctrl_handler_setup(&core->hdl);
 
@@ -1197,6 +1232,10 @@ static int tvp5150_probe(struct i2c_client *c,
        if (debug > 1)
                tvp5150_log_status(sd);
        return 0;
+
+free_core:
+       kfree(core);
+       return res;
 }
 
 static int tvp5150_remove(struct i2c_client *c)
index 8768efb8508ad6125e8ed534d42732d8f4e664a5..9f53eacb66e3cd89a75f14ec08b0ced3ea752385 100644 (file)
@@ -699,11 +699,9 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd,
        struct tw9910_priv *priv = to_tw9910(client);
 
        if (!priv->scale) {
-               int ret;
-               u32 width = 640, height = 480;
-               ret = tw9910_set_frame(sd, &width, &height);
-               if (ret < 0)
-                       return ret;
+               priv->scale = tw9910_select_norm(priv->norm, 640, 480);
+               if (!priv->scale)
+                       return -EINVAL;
        }
 
        mf->width       = priv->scale->width;
index 6c197da531b239b7dcc9c38058842c1b9af93c1c..541c9f1e4c6a0dc0667597d527b2a0763ecdc668 100644 (file)
@@ -10,6 +10,7 @@ config USB_VIDEO_CLASS
 config USB_VIDEO_CLASS_INPUT_EVDEV
        bool "UVC input events device support"
        default y
+       depends on USB_VIDEO_CLASS
        depends on USB_VIDEO_CLASS=INPUT || INPUT=y
        ---help---
          This option makes USB Video Class devices register an input device
index af26bbe6f76ecba9726dc01983bda49f36b22c9a..f7061a5ef1d2a1c424c78f486d4348fc96b9b8ff 100644 (file)
@@ -2083,7 +2083,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
        /* Walk the entities list and instantiate controls */
        list_for_each_entry(entity, &dev->entities, list) {
                struct uvc_control *ctrl;
-               unsigned int bControlSize = 0, ncontrols = 0;
+               unsigned int bControlSize = 0, ncontrols;
                __u8 *bmControls = NULL;
 
                if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
@@ -2101,8 +2101,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
                uvc_ctrl_prune_entity(dev, entity);
 
                /* Count supported controls and allocate the controls array */
-               for (i = 0; i < bControlSize; ++i)
-                       ncontrols += hweight8(bmControls[i]);
+               ncontrols = memweight(bmControls, bControlSize);
                if (ncontrols == 0)
                        continue;
 
index 759bef8897e9d9d4645d837a451b2b2c96fee92d..f00db3060e0e36e005b306a53f393cc55071b101 100644 (file)
@@ -1051,7 +1051,7 @@ static long uvc_v4l2_ioctl(struct file *file,
 {
        if (uvc_trace_param & UVC_TRACE_IOCTL) {
                uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl(");
-               v4l_printk_ioctl(cmd);
+               v4l_printk_ioctl(NULL, cmd);
                printk(")\n");
        }
 
index b76b0ac0958f8d2d27a5a7193c623badfac5ee67..7ac4347ca09e72dc4bb2abb751779f3053a338c7 100644 (file)
@@ -1188,7 +1188,11 @@ static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream,
        u8 *mem;
        int len, ret;
 
-       if (urb->actual_length == 0)
+       /*
+        * Ignore ZLPs if they're not part of a frame, otherwise process them
+        * to trigger the end of payload detection.
+        */
+       if (urb->actual_length == 0 && stream->bulk.header_size == 0)
                return;
 
        mem = urb->transfer_buffer;
@@ -1594,7 +1598,7 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
                        psize = le16_to_cpu(ep->desc.wMaxPacketSize);
                        psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
                        if (psize >= bandwidth && psize <= best_psize) {
-                               altsetting = i;
+                               altsetting = alts->desc.bAlternateSetting;
                                best_psize = psize;
                                best_ep = ep;
                        }
index 5327ad3a63907a87f3da587812269b60edef5880..9ebd5c540d10feab78df929f39b8e64b1690f17f 100644 (file)
@@ -327,7 +327,7 @@ struct v4l2_buffer32 {
                compat_caddr_t  planes;
        } m;
        __u32                   length;
-       __u32                   input;
+       __u32                   reserved2;
        __u32                   reserved;
 };
 
@@ -387,8 +387,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
                get_user(kp->index, &up->index) ||
                get_user(kp->type, &up->type) ||
                get_user(kp->flags, &up->flags) ||
-               get_user(kp->memory, &up->memory) ||
-               get_user(kp->input, &up->input))
+               get_user(kp->memory, &up->memory))
                        return -EFAULT;
 
        if (V4L2_TYPE_IS_OUTPUT(kp->type))
@@ -472,8 +471,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
                put_user(kp->index, &up->index) ||
                put_user(kp->type, &up->type) ||
                put_user(kp->flags, &up->flags) ||
-               put_user(kp->memory, &up->memory) ||
-               put_user(kp->input, &up->input))
+               put_user(kp->memory, &up->memory))
                        return -EFAULT;
 
        if (put_user(kp->bytesused, &up->bytesused) ||
@@ -482,6 +480,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
                put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) ||
                copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) ||
                put_user(kp->sequence, &up->sequence) ||
+               put_user(kp->reserved2, &up->reserved2) ||
                put_user(kp->reserved, &up->reserved))
                        return -EFAULT;
 
@@ -1026,6 +1025,7 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
        case VIDIOC_ENUM_DV_TIMINGS:
        case VIDIOC_QUERY_DV_TIMINGS:
        case VIDIOC_DV_TIMINGS_CAP:
+       case VIDIOC_ENUM_FREQ_BANDS:
                ret = do_video_ioctl(file, cmd, arg);
                break;
 
index 9abd9abd4502f35e4adc31506d6bb66643290ffa..b6a2ee71e5c300cead8a4d77cb4e627c687a476e 100644 (file)
@@ -755,6 +755,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
        case V4L2_CID_HUE_AUTO:
        case V4L2_CID_CHROMA_AGC:
        case V4L2_CID_COLOR_KILLER:
+       case V4L2_CID_AUTOBRIGHTNESS:
        case V4L2_CID_MPEG_AUDIO_MUTE:
        case V4L2_CID_MPEG_VIDEO_MUTE:
        case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
@@ -2120,7 +2121,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
 
        /* First zero the helper field in the master control references */
        for (i = 0; i < cs->count; i++)
-               helpers[i].mref->helper = 0;
+               helpers[i].mref->helper = NULL;
        for (i = 0, h = helpers; i < cs->count; i++, h++) {
                struct v4l2_ctrl_ref *mref = h->mref;
 
index 0cbada18f6f57376d980d34345335279499fec03..07aeafca9eaabfd0f23117168945f0d1e603533a 100644 (file)
@@ -46,6 +46,29 @@ static ssize_t show_index(struct device *cd,
        return sprintf(buf, "%i\n", vdev->index);
 }
 
+static ssize_t show_debug(struct device *cd,
+                        struct device_attribute *attr, char *buf)
+{
+       struct video_device *vdev = to_video_device(cd);
+
+       return sprintf(buf, "%i\n", vdev->debug);
+}
+
+static ssize_t set_debug(struct device *cd, struct device_attribute *attr,
+                  const char *buf, size_t len)
+{
+       struct video_device *vdev = to_video_device(cd);
+       int res = 0;
+       u16 value;
+
+       res = kstrtou16(buf, 0, &value);
+       if (res)
+               return res;
+
+       vdev->debug = value;
+       return len;
+}
+
 static ssize_t show_name(struct device *cd,
                         struct device_attribute *attr, char *buf)
 {
@@ -56,6 +79,7 @@ static ssize_t show_name(struct device *cd,
 
 static struct device_attribute video_device_attrs[] = {
        __ATTR(name, S_IRUGO, show_name, NULL),
+       __ATTR(debug, 0644, show_debug, set_debug),
        __ATTR(index, S_IRUGO, show_index, NULL),
        __ATTR_NULL
 };
@@ -281,6 +305,9 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf,
                ret = vdev->fops->read(filp, buf, sz, off);
        if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
                mutex_unlock(vdev->lock);
+       if (vdev->debug)
+               printk(KERN_DEBUG "%s: read: %zd (%d)\n",
+                       video_device_node_name(vdev), sz, ret);
        return ret;
 }
 
@@ -299,6 +326,9 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf,
                ret = vdev->fops->write(filp, buf, sz, off);
        if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
                mutex_unlock(vdev->lock);
+       if (vdev->debug)
+               printk(KERN_DEBUG "%s: write: %zd (%d)\n",
+                       video_device_node_name(vdev), sz, ret);
        return ret;
 }
 
@@ -315,6 +345,9 @@ static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
                ret = vdev->fops->poll(filp, poll);
        if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
                mutex_unlock(vdev->lock);
+       if (vdev->debug)
+               printk(KERN_DEBUG "%s: poll: %08x\n",
+                       video_device_node_name(vdev), ret);
        return ret;
 }
 
@@ -324,20 +357,14 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        int ret = -ENODEV;
 
        if (vdev->fops->unlocked_ioctl) {
-               bool locked = false;
+               struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd);
 
-               if (vdev->lock) {
-                       /* always lock unless the cmd is marked as "don't use lock" */
-                       locked = !v4l2_is_known_ioctl(cmd) ||
-                                !test_bit(_IOC_NR(cmd), vdev->disable_locking);
-
-                       if (locked && mutex_lock_interruptible(vdev->lock))
-                               return -ERESTARTSYS;
-               }
+               if (lock && mutex_lock_interruptible(lock))
+                       return -ERESTARTSYS;
                if (video_is_registered(vdev))
                        ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
-               if (locked)
-                       mutex_unlock(vdev->lock);
+               if (lock)
+                       mutex_unlock(lock);
        } else if (vdev->fops->ioctl) {
                /* This code path is a replacement for the BKL. It is a major
                 * hack but it will have to do for those drivers that are not
@@ -385,12 +412,17 @@ static unsigned long v4l2_get_unmapped_area(struct file *filp,
                unsigned long flags)
 {
        struct video_device *vdev = video_devdata(filp);
+       int ret;
 
        if (!vdev->fops->get_unmapped_area)
                return -ENOSYS;
        if (!video_is_registered(vdev))
                return -ENODEV;
-       return vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
+       ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
+       if (vdev->debug)
+               printk(KERN_DEBUG "%s: get_unmapped_area (%d)\n",
+                       video_device_node_name(vdev), ret);
+       return ret;
 }
 #endif
 
@@ -408,6 +440,9 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
                ret = vdev->fops->mmap(filp, vm);
        if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
                mutex_unlock(vdev->lock);
+       if (vdev->debug)
+               printk(KERN_DEBUG "%s: mmap (%d)\n",
+                       video_device_node_name(vdev), ret);
        return ret;
 }
 
@@ -443,6 +478,9 @@ static int v4l2_open(struct inode *inode, struct file *filp)
        }
 
 err:
+       if (vdev->debug)
+               printk(KERN_DEBUG "%s: open (%d)\n",
+                       video_device_node_name(vdev), ret);
        /* decrease the refcount in case of an error */
        if (ret)
                video_put(vdev);
@@ -462,6 +500,9 @@ static int v4l2_release(struct inode *inode, struct file *filp)
                if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
                        mutex_unlock(vdev->lock);
        }
+       if (vdev->debug)
+               printk(KERN_DEBUG "%s: release\n",
+                       video_device_node_name(vdev));
        /* decrease the refcount unconditionally since the release()
           return value is ignored. */
        video_put(vdev);
@@ -656,7 +697,8 @@ static void determine_valid_ioctls(struct video_device *vdev)
        SET_VALID_IOCTL(ops, VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd);
        SET_VALID_IOCTL(ops, VIDIOC_DECODER_CMD, vidioc_decoder_cmd);
        SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd);
-       if (ops->vidioc_g_parm || vdev->vfl_type == VFL_TYPE_GRABBER)
+       if (ops->vidioc_g_parm || (vdev->vfl_type == VFL_TYPE_GRABBER &&
+                                       (ops->vidioc_g_std || vdev->tvnorms)))
                set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls);
        SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm);
        SET_VALID_IOCTL(ops, VIDIOC_G_TUNER, vidioc_g_tuner);
@@ -688,6 +730,8 @@ static void determine_valid_ioctls(struct video_device *vdev)
        SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event);
        SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs);
        SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
+       if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator)
+               set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls);
        bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls,
                        BASE_VIDIOC_PRIVATE);
 }
index d7fa8962d8b3129940514cb487d0d75a8931dbdd..c3b7b5f59b328a8beea170977261e4d650da88b7 100644 (file)
 #include <media/v4l2-event.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-
-#define dbgarg(cmd, fmt, arg...) \
-               do {                                                    \
-                   if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {            \
-                       printk(KERN_DEBUG "%s: ",  vfd->name);          \
-                       v4l_printk_ioctl(cmd);                          \
-                       printk(" " fmt,  ## arg);                       \
-                   }                                                   \
-               } while (0)
-
-#define dbgarg2(fmt, arg...) \
-               do {                                                    \
-                   if (vfd->debug & V4L2_DEBUG_IOCTL_ARG)              \
-                       printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);\
-               } while (0)
-
-#define dbgarg3(fmt, arg...) \
-               do {                                                    \
-                   if (vfd->debug & V4L2_DEBUG_IOCTL_ARG)              \
-                       printk(KERN_CONT "%s: " fmt, vfd->name, ## arg);\
-               } while (0)
+#include <media/videobuf2-core.h>
 
 /* Zero out the end of the struct pointed to by p.  Everything after, but
  * not including, the specified field is cleared. */
@@ -183,1968 +163,2004 @@ static const char *v4l2_memory_names[] = {
 /* ------------------------------------------------------------------ */
 /* debug help functions                                               */
 
-struct v4l2_ioctl_info {
-       unsigned int ioctl;
-       u16 flags;
-       const char * const name;
-};
+static void v4l_print_querycap(const void *arg, bool write_only)
+{
+       const struct v4l2_capability *p = arg;
 
-/* This control needs a priority check */
-#define INFO_FL_PRIO   (1 << 0)
-/* This control can be valid if the filehandle passes a control handler. */
-#define INFO_FL_CTRL   (1 << 1)
+       pr_cont("driver=%s, card=%s, bus=%s, version=0x%08x, "
+               "capabilities=0x%08x, device_caps=0x%08x\n",
+               p->driver, p->card, p->bus_info,
+               p->version, p->capabilities, p->device_caps);
+}
+
+static void v4l_print_enuminput(const void *arg, bool write_only)
+{
+       const struct v4l2_input *p = arg;
 
-#define IOCTL_INFO(_ioctl, _flags) [_IOC_NR(_ioctl)] = {       \
-       .ioctl = _ioctl,                                        \
-       .flags = _flags,                                        \
-       .name = #_ioctl,                                        \
+       pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, tuner=%u, "
+               "std=0x%08Lx, status=0x%x, capabilities=0x%x\n",
+               p->index, p->name, p->type, p->audioset, p->tuner,
+               (unsigned long long)p->std, p->status, p->capabilities);
 }
 
-static struct v4l2_ioctl_info v4l2_ioctls[] = {
-       IOCTL_INFO(VIDIOC_QUERYCAP, 0),
-       IOCTL_INFO(VIDIOC_ENUM_FMT, 0),
-       IOCTL_INFO(VIDIOC_G_FMT, 0),
-       IOCTL_INFO(VIDIOC_S_FMT, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_REQBUFS, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_QUERYBUF, 0),
-       IOCTL_INFO(VIDIOC_G_FBUF, 0),
-       IOCTL_INFO(VIDIOC_S_FBUF, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_OVERLAY, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_QBUF, 0),
-       IOCTL_INFO(VIDIOC_DQBUF, 0),
-       IOCTL_INFO(VIDIOC_STREAMON, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_STREAMOFF, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_G_PARM, 0),
-       IOCTL_INFO(VIDIOC_S_PARM, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_G_STD, 0),
-       IOCTL_INFO(VIDIOC_S_STD, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_ENUMSTD, 0),
-       IOCTL_INFO(VIDIOC_ENUMINPUT, 0),
-       IOCTL_INFO(VIDIOC_G_CTRL, INFO_FL_CTRL),
-       IOCTL_INFO(VIDIOC_S_CTRL, INFO_FL_PRIO | INFO_FL_CTRL),
-       IOCTL_INFO(VIDIOC_G_TUNER, 0),
-       IOCTL_INFO(VIDIOC_S_TUNER, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_G_AUDIO, 0),
-       IOCTL_INFO(VIDIOC_S_AUDIO, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_QUERYCTRL, INFO_FL_CTRL),
-       IOCTL_INFO(VIDIOC_QUERYMENU, INFO_FL_CTRL),
-       IOCTL_INFO(VIDIOC_G_INPUT, 0),
-       IOCTL_INFO(VIDIOC_S_INPUT, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_G_OUTPUT, 0),
-       IOCTL_INFO(VIDIOC_S_OUTPUT, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_ENUMOUTPUT, 0),
-       IOCTL_INFO(VIDIOC_G_AUDOUT, 0),
-       IOCTL_INFO(VIDIOC_S_AUDOUT, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_G_MODULATOR, 0),
-       IOCTL_INFO(VIDIOC_S_MODULATOR, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_G_FREQUENCY, 0),
-       IOCTL_INFO(VIDIOC_S_FREQUENCY, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_CROPCAP, 0),
-       IOCTL_INFO(VIDIOC_G_CROP, 0),
-       IOCTL_INFO(VIDIOC_S_CROP, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_G_SELECTION, 0),
-       IOCTL_INFO(VIDIOC_S_SELECTION, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_G_JPEGCOMP, 0),
-       IOCTL_INFO(VIDIOC_S_JPEGCOMP, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_QUERYSTD, 0),
-       IOCTL_INFO(VIDIOC_TRY_FMT, 0),
-       IOCTL_INFO(VIDIOC_ENUMAUDIO, 0),
-       IOCTL_INFO(VIDIOC_ENUMAUDOUT, 0),
-       IOCTL_INFO(VIDIOC_G_PRIORITY, 0),
-       IOCTL_INFO(VIDIOC_S_PRIORITY, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_G_SLICED_VBI_CAP, 0),
-       IOCTL_INFO(VIDIOC_LOG_STATUS, 0),
-       IOCTL_INFO(VIDIOC_G_EXT_CTRLS, INFO_FL_CTRL),
-       IOCTL_INFO(VIDIOC_S_EXT_CTRLS, INFO_FL_PRIO | INFO_FL_CTRL),
-       IOCTL_INFO(VIDIOC_TRY_EXT_CTRLS, 0),
-       IOCTL_INFO(VIDIOC_ENUM_FRAMESIZES, 0),
-       IOCTL_INFO(VIDIOC_ENUM_FRAMEINTERVALS, 0),
-       IOCTL_INFO(VIDIOC_G_ENC_INDEX, 0),
-       IOCTL_INFO(VIDIOC_ENCODER_CMD, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_TRY_ENCODER_CMD, 0),
-       IOCTL_INFO(VIDIOC_DECODER_CMD, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_TRY_DECODER_CMD, 0),
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-       IOCTL_INFO(VIDIOC_DBG_S_REGISTER, 0),
-       IOCTL_INFO(VIDIOC_DBG_G_REGISTER, 0),
-#endif
-       IOCTL_INFO(VIDIOC_DBG_G_CHIP_IDENT, 0),
-       IOCTL_INFO(VIDIOC_S_HW_FREQ_SEEK, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_ENUM_DV_PRESETS, 0),
-       IOCTL_INFO(VIDIOC_S_DV_PRESET, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_G_DV_PRESET, 0),
-       IOCTL_INFO(VIDIOC_QUERY_DV_PRESET, 0),
-       IOCTL_INFO(VIDIOC_S_DV_TIMINGS, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_G_DV_TIMINGS, 0),
-       IOCTL_INFO(VIDIOC_DQEVENT, 0),
-       IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT, 0),
-       IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT, 0),
-       IOCTL_INFO(VIDIOC_CREATE_BUFS, INFO_FL_PRIO),
-       IOCTL_INFO(VIDIOC_PREPARE_BUF, 0),
-       IOCTL_INFO(VIDIOC_ENUM_DV_TIMINGS, 0),
-       IOCTL_INFO(VIDIOC_QUERY_DV_TIMINGS, 0),
-       IOCTL_INFO(VIDIOC_DV_TIMINGS_CAP, 0),
-};
-#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
+static void v4l_print_enumoutput(const void *arg, bool write_only)
+{
+       const struct v4l2_output *p = arg;
 
-bool v4l2_is_known_ioctl(unsigned int cmd)
+       pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, "
+               "modulator=%u, std=0x%08Lx, capabilities=0x%x\n",
+               p->index, p->name, p->type, p->audioset, p->modulator,
+               (unsigned long long)p->std, p->capabilities);
+}
+
+static void v4l_print_audio(const void *arg, bool write_only)
 {
-       if (_IOC_NR(cmd) >= V4L2_IOCTLS)
-               return false;
-       return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd;
+       const struct v4l2_audio *p = arg;
+
+       if (write_only)
+               pr_cont("index=%u, mode=0x%x\n", p->index, p->mode);
+       else
+               pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
+                       p->index, p->name, p->capability, p->mode);
 }
 
-/* Common ioctl debug function. This function can be used by
-   external ioctl messages as well as internal V4L ioctl */
-void v4l_printk_ioctl(unsigned int cmd)
+static void v4l_print_audioout(const void *arg, bool write_only)
 {
-       char *dir, *type;
+       const struct v4l2_audioout *p = arg;
 
-       switch (_IOC_TYPE(cmd)) {
-       case 'd':
-               type = "v4l2_int";
+       if (write_only)
+               pr_cont("index=%u\n", p->index);
+       else
+               pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
+                       p->index, p->name, p->capability, p->mode);
+}
+
+static void v4l_print_fmtdesc(const void *arg, bool write_only)
+{
+       const struct v4l2_fmtdesc *p = arg;
+
+       pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, description='%s'\n",
+               p->index, prt_names(p->type, v4l2_type_names),
+               p->flags, (p->pixelformat & 0xff),
+               (p->pixelformat >>  8) & 0xff,
+               (p->pixelformat >> 16) & 0xff,
+               (p->pixelformat >> 24) & 0xff,
+               p->description);
+}
+
+static void v4l_print_format(const void *arg, bool write_only)
+{
+       const struct v4l2_format *p = arg;
+       const struct v4l2_pix_format *pix;
+       const struct v4l2_pix_format_mplane *mp;
+       const struct v4l2_vbi_format *vbi;
+       const struct v4l2_sliced_vbi_format *sliced;
+       const struct v4l2_window *win;
+       const struct v4l2_clip *clip;
+       unsigned i;
+
+       pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
+       switch (p->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               pix = &p->fmt.pix;
+               pr_cont(", width=%u, height=%u, "
+                       "pixelformat=%c%c%c%c, field=%s, "
+                       "bytesperline=%u sizeimage=%u, colorspace=%d\n",
+                       pix->width, pix->height,
+                       (pix->pixelformat & 0xff),
+                       (pix->pixelformat >>  8) & 0xff,
+                       (pix->pixelformat >> 16) & 0xff,
+                       (pix->pixelformat >> 24) & 0xff,
+                       prt_names(pix->field, v4l2_field_names),
+                       pix->bytesperline, pix->sizeimage,
+                       pix->colorspace);
                break;
-       case 'V':
-               if (_IOC_NR(cmd) >= V4L2_IOCTLS) {
-                       type = "v4l2";
-                       break;
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               mp = &p->fmt.pix_mp;
+               pr_cont(", width=%u, height=%u, "
+                       "format=%c%c%c%c, field=%s, "
+                       "colorspace=%d, num_planes=%u\n",
+                       mp->width, mp->height,
+                       (mp->pixelformat & 0xff),
+                       (mp->pixelformat >>  8) & 0xff,
+                       (mp->pixelformat >> 16) & 0xff,
+                       (mp->pixelformat >> 24) & 0xff,
+                       prt_names(mp->field, v4l2_field_names),
+                       mp->colorspace, mp->num_planes);
+               for (i = 0; i < mp->num_planes; i++)
+                       printk(KERN_DEBUG "plane %u: bytesperline=%u sizeimage=%u\n", i,
+                                       mp->plane_fmt[i].bytesperline,
+                                       mp->plane_fmt[i].sizeimage);
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+               win = &p->fmt.win;
+               pr_cont(", wxh=%dx%d, x,y=%d,%d, field=%s, "
+                       "chromakey=0x%08x, bitmap=%p, "
+                       "global_alpha=0x%02x\n",
+                       win->w.width, win->w.height,
+                       win->w.left, win->w.top,
+                       prt_names(win->field, v4l2_field_names),
+                       win->chromakey, win->bitmap, win->global_alpha);
+               clip = win->clips;
+               for (i = 0; i < win->clipcount; i++) {
+                       printk(KERN_DEBUG "clip %u: wxh=%dx%d, x,y=%d,%d\n",
+                                       i, clip->c.width, clip->c.height,
+                                       clip->c.left, clip->c.top);
+                       clip = clip->next;
                }
-               printk("%s", v4l2_ioctls[_IOC_NR(cmd)].name);
-               return;
-       default:
-               type = "unknown";
+               break;
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               vbi = &p->fmt.vbi;
+               pr_cont(", sampling_rate=%u, offset=%u, samples_per_line=%u, "
+                       "sample_format=%c%c%c%c, start=%u,%u, count=%u,%u\n",
+                       vbi->sampling_rate, vbi->offset,
+                       vbi->samples_per_line,
+                       (vbi->sample_format & 0xff),
+                       (vbi->sample_format >>  8) & 0xff,
+                       (vbi->sample_format >> 16) & 0xff,
+                       (vbi->sample_format >> 24) & 0xff,
+                       vbi->start[0], vbi->start[1],
+                       vbi->count[0], vbi->count[1]);
+               break;
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               sliced = &p->fmt.sliced;
+               pr_cont(", service_set=0x%08x, io_size=%d\n",
+                               sliced->service_set, sliced->io_size);
+               for (i = 0; i < 24; i++)
+                       printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i,
+                               sliced->service_lines[0][i],
+                               sliced->service_lines[1][i]);
+               break;
+       case V4L2_BUF_TYPE_PRIVATE:
+               pr_cont("\n");
+               break;
        }
+}
 
-       switch (_IOC_DIR(cmd)) {
-       case _IOC_NONE:              dir = "--"; break;
-       case _IOC_READ:              dir = "r-"; break;
-       case _IOC_WRITE:             dir = "-w"; break;
-       case _IOC_READ | _IOC_WRITE: dir = "rw"; break;
-       default:                     dir = "*ERR*"; break;
-       }
-       printk("%s ioctl '%c', dir=%s, #%d (0x%08x)",
-               type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd);
+static void v4l_print_framebuffer(const void *arg, bool write_only)
+{
+       const struct v4l2_framebuffer *p = arg;
+
+       pr_cont("capability=0x%x, flags=0x%x, base=0x%p, width=%u, "
+               "height=%u, pixelformat=%c%c%c%c, "
+               "bytesperline=%u sizeimage=%u, colorspace=%d\n",
+                       p->capability, p->flags, p->base,
+                       p->fmt.width, p->fmt.height,
+                       (p->fmt.pixelformat & 0xff),
+                       (p->fmt.pixelformat >>  8) & 0xff,
+                       (p->fmt.pixelformat >> 16) & 0xff,
+                       (p->fmt.pixelformat >> 24) & 0xff,
+                       p->fmt.bytesperline, p->fmt.sizeimage,
+                       p->fmt.colorspace);
+}
+
+static void v4l_print_buftype(const void *arg, bool write_only)
+{
+       pr_cont("type=%s\n", prt_names(*(u32 *)arg, v4l2_type_names));
+}
+
+static void v4l_print_modulator(const void *arg, bool write_only)
+{
+       const struct v4l2_modulator *p = arg;
+
+       if (write_only)
+               pr_cont("index=%u, txsubchans=0x%x", p->index, p->txsubchans);
+       else
+               pr_cont("index=%u, name=%s, capability=0x%x, "
+                       "rangelow=%u, rangehigh=%u, txsubchans=0x%x\n",
+                       p->index, p->name, p->capability,
+                       p->rangelow, p->rangehigh, p->txsubchans);
+}
+
+static void v4l_print_tuner(const void *arg, bool write_only)
+{
+       const struct v4l2_tuner *p = arg;
+
+       if (write_only)
+               pr_cont("index=%u, audmode=%u\n", p->index, p->audmode);
+       else
+               pr_cont("index=%u, name=%s, type=%u, capability=0x%x, "
+                       "rangelow=%u, rangehigh=%u, signal=%u, afc=%d, "
+                       "rxsubchans=0x%x, audmode=%u\n",
+                       p->index, p->name, p->type,
+                       p->capability, p->rangelow,
+                       p->rangehigh, p->signal, p->afc,
+                       p->rxsubchans, p->audmode);
+}
+
+static void v4l_print_frequency(const void *arg, bool write_only)
+{
+       const struct v4l2_frequency *p = arg;
+
+       pr_cont("tuner=%u, type=%u, frequency=%u\n",
+                               p->tuner, p->type, p->frequency);
+}
+
+static void v4l_print_standard(const void *arg, bool write_only)
+{
+       const struct v4l2_standard *p = arg;
+
+       pr_cont("index=%u, id=0x%Lx, name=%s, fps=%u/%u, "
+               "framelines=%u\n", p->index,
+               (unsigned long long)p->id, p->name,
+               p->frameperiod.numerator,
+               p->frameperiod.denominator,
+               p->framelines);
+}
+
+static void v4l_print_std(const void *arg, bool write_only)
+{
+       pr_cont("std=0x%08Lx\n", *(const long long unsigned *)arg);
+}
+
+static void v4l_print_hw_freq_seek(const void *arg, bool write_only)
+{
+       const struct v4l2_hw_freq_seek *p = arg;
+
+       pr_cont("tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u\n",
+               p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing);
 }
-EXPORT_SYMBOL(v4l_printk_ioctl);
 
-static void dbgbuf(unsigned int cmd, struct video_device *vfd,
-                                       struct v4l2_buffer *p)
+static void v4l_print_requestbuffers(const void *arg, bool write_only)
 {
-       struct v4l2_timecode *tc = &p->timecode;
-       struct v4l2_plane *plane;
+       const struct v4l2_requestbuffers *p = arg;
+
+       pr_cont("count=%d, type=%s, memory=%s\n",
+               p->count,
+               prt_names(p->type, v4l2_type_names),
+               prt_names(p->memory, v4l2_memory_names));
+}
+
+static void v4l_print_buffer(const void *arg, bool write_only)
+{
+       const struct v4l2_buffer *p = arg;
+       const struct v4l2_timecode *tc = &p->timecode;
+       const struct v4l2_plane *plane;
        int i;
 
-       dbgarg(cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, "
-               "flags=0x%08d, field=%0d, sequence=%d, memory=%s\n",
+       pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, "
+               "flags=0x%08x, field=%s, sequence=%d, memory=%s",
                        p->timestamp.tv_sec / 3600,
                        (int)(p->timestamp.tv_sec / 60) % 60,
                        (int)(p->timestamp.tv_sec % 60),
                        (long)p->timestamp.tv_usec,
                        p->index,
                        prt_names(p->type, v4l2_type_names),
-                       p->flags, p->field, p->sequence,
-                       prt_names(p->memory, v4l2_memory_names));
+                       p->flags, prt_names(p->field, v4l2_field_names),
+                       p->sequence, prt_names(p->memory, v4l2_memory_names));
 
        if (V4L2_TYPE_IS_MULTIPLANAR(p->type) && p->m.planes) {
+               pr_cont("\n");
                for (i = 0; i < p->length; ++i) {
                        plane = &p->m.planes[i];
-                       dbgarg2("plane %d: bytesused=%d, data_offset=0x%08x "
-                               "offset/userptr=0x%08lx, length=%d\n",
+                       printk(KERN_DEBUG
+                               "plane %d: bytesused=%d, data_offset=0x%08x "
+                               "offset/userptr=0x%lx, length=%d\n",
                                i, plane->bytesused, plane->data_offset,
                                plane->m.userptr, plane->length);
                }
        } else {
-               dbgarg2("bytesused=%d, offset/userptr=0x%08lx, length=%d\n",
+               pr_cont("bytesused=%d, offset/userptr=0x%lx, length=%d\n",
                        p->bytesused, p->m.userptr, p->length);
        }
 
-       dbgarg2("timecode=%02d:%02d:%02d type=%d, "
-               "flags=0x%08d, frames=%d, userbits=0x%08x\n",
+       printk(KERN_DEBUG "timecode=%02d:%02d:%02d type=%d, "
+               "flags=0x%08x, frames=%d, userbits=0x%08x\n",
                        tc->hours, tc->minutes, tc->seconds,
                        tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits);
 }
 
-static inline void dbgrect(struct video_device *vfd, char *s,
-                                                       struct v4l2_rect *r)
+static void v4l_print_create_buffers(const void *arg, bool write_only)
 {
-       dbgarg2("%sRect start at %dx%d, size=%dx%d\n", s, r->left, r->top,
-                                               r->width, r->height);
-};
+       const struct v4l2_create_buffers *p = arg;
+
+       pr_cont("index=%d, count=%d, memory=%s, ",
+                       p->index, p->count,
+                       prt_names(p->memory, v4l2_memory_names));
+       v4l_print_format(&p->format, write_only);
+}
 
-static void dbgtimings(struct video_device *vfd,
-                       const struct v4l2_dv_timings *p)
+static void v4l_print_streamparm(const void *arg, bool write_only)
 {
-       switch (p->type) {
-       case V4L2_DV_BT_656_1120:
-               dbgarg2("bt-656/1120:interlaced=%d,"
-                               " pixelclock=%lld,"
-                               " width=%d, height=%d, polarities=%x,"
-                               " hfrontporch=%d, hsync=%d,"
-                               " hbackporch=%d, vfrontporch=%d,"
-                               " vsync=%d, vbackporch=%d,"
-                               " il_vfrontporch=%d, il_vsync=%d,"
-                               " il_vbackporch=%d, standards=%x, flags=%x\n",
-                               p->bt.interlaced, p->bt.pixelclock,
-                               p->bt.width, p->bt.height,
-                               p->bt.polarities, p->bt.hfrontporch,
-                               p->bt.hsync, p->bt.hbackporch,
-                               p->bt.vfrontporch, p->bt.vsync,
-                               p->bt.vbackporch, p->bt.il_vfrontporch,
-                               p->bt.il_vsync, p->bt.il_vbackporch,
-                               p->bt.standards, p->bt.flags);
-               break;
-       default:
-               dbgarg2("Unknown type %d!\n", p->type);
-               break;
+       const struct v4l2_streamparm *p = arg;
+
+       pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
+
+       if (p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+           p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               const struct v4l2_captureparm *c = &p->parm.capture;
+
+               pr_cont(", capability=0x%x, capturemode=0x%x, timeperframe=%d/%d, "
+                       "extendedmode=%d, readbuffers=%d\n",
+                       c->capability, c->capturemode,
+                       c->timeperframe.numerator, c->timeperframe.denominator,
+                       c->extendedmode, c->readbuffers);
+       } else if (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+                  p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               const struct v4l2_outputparm *c = &p->parm.output;
+
+               pr_cont(", capability=0x%x, outputmode=0x%x, timeperframe=%d/%d, "
+                       "extendedmode=%d, writebuffers=%d\n",
+                       c->capability, c->outputmode,
+                       c->timeperframe.numerator, c->timeperframe.denominator,
+                       c->extendedmode, c->writebuffers);
        }
 }
 
-static inline void v4l_print_pix_fmt(struct video_device *vfd,
-                                               struct v4l2_pix_format *fmt)
+static void v4l_print_queryctrl(const void *arg, bool write_only)
 {
-       dbgarg2("width=%d, height=%d, format=%c%c%c%c, field=%s, "
-               "bytesperline=%d sizeimage=%d, colorspace=%d\n",
-               fmt->width, fmt->height,
-               (fmt->pixelformat & 0xff),
-               (fmt->pixelformat >>  8) & 0xff,
-               (fmt->pixelformat >> 16) & 0xff,
-               (fmt->pixelformat >> 24) & 0xff,
-               prt_names(fmt->field, v4l2_field_names),
-               fmt->bytesperline, fmt->sizeimage, fmt->colorspace);
-};
+       const struct v4l2_queryctrl *p = arg;
+
+       pr_cont("id=0x%x, type=%d, name=%s, min/max=%d/%d, "
+               "step=%d, default=%d, flags=0x%08x\n",
+                       p->id, p->type, p->name,
+                       p->minimum, p->maximum,
+                       p->step, p->default_value, p->flags);
+}
 
-static inline void v4l_print_pix_fmt_mplane(struct video_device *vfd,
-                                           struct v4l2_pix_format_mplane *fmt)
+static void v4l_print_querymenu(const void *arg, bool write_only)
 {
-       int i;
+       const struct v4l2_querymenu *p = arg;
+
+       pr_cont("id=0x%x, index=%d\n", p->id, p->index);
+}
 
-       dbgarg2("width=%d, height=%d, format=%c%c%c%c, field=%s, "
-               "colorspace=%d, num_planes=%d\n",
-               fmt->width, fmt->height,
-               (fmt->pixelformat & 0xff),
-               (fmt->pixelformat >>  8) & 0xff,
-               (fmt->pixelformat >> 16) & 0xff,
-               (fmt->pixelformat >> 24) & 0xff,
-               prt_names(fmt->field, v4l2_field_names),
-               fmt->colorspace, fmt->num_planes);
+static void v4l_print_control(const void *arg, bool write_only)
+{
+       const struct v4l2_control *p = arg;
 
-       for (i = 0; i < fmt->num_planes; ++i)
-               dbgarg2("plane %d: bytesperline=%d sizeimage=%d\n", i,
-                       fmt->plane_fmt[i].bytesperline,
-                       fmt->plane_fmt[i].sizeimage);
+       pr_cont("id=0x%x, value=%d\n", p->id, p->value);
 }
 
-static inline void v4l_print_ext_ctrls(unsigned int cmd,
-       struct video_device *vfd, struct v4l2_ext_controls *c, int show_vals)
+static void v4l_print_ext_controls(const void *arg, bool write_only)
 {
-       __u32 i;
+       const struct v4l2_ext_controls *p = arg;
+       int i;
 
-       if (!(vfd->debug & V4L2_DEBUG_IOCTL_ARG))
-               return;
-       dbgarg(cmd, "");
-       printk(KERN_CONT "class=0x%x", c->ctrl_class);
-       for (i = 0; i < c->count; i++) {
-               if (show_vals && !c->controls[i].size)
-                       printk(KERN_CONT " id/val=0x%x/0x%x",
-                               c->controls[i].id, c->controls[i].value);
+       pr_cont("class=0x%x, count=%d, error_idx=%d",
+                       p->ctrl_class, p->count, p->error_idx);
+       for (i = 0; i < p->count; i++) {
+               if (p->controls[i].size)
+                       pr_cont(", id/val=0x%x/0x%x",
+                               p->controls[i].id, p->controls[i].value);
                else
-                       printk(KERN_CONT " id=0x%x,size=%u",
-                               c->controls[i].id, c->controls[i].size);
+                       pr_cont(", id/size=0x%x/%u",
+                               p->controls[i].id, p->controls[i].size);
        }
-       printk(KERN_CONT "\n");
-};
+       pr_cont("\n");
+}
 
-static inline int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
+static void v4l_print_cropcap(const void *arg, bool write_only)
 {
-       __u32 i;
+       const struct v4l2_cropcap *p = arg;
+
+       pr_cont("type=%s, bounds wxh=%dx%d, x,y=%d,%d, "
+               "defrect wxh=%dx%d, x,y=%d,%d\n, "
+               "pixelaspect %d/%d\n",
+               prt_names(p->type, v4l2_type_names),
+               p->bounds.width, p->bounds.height,
+               p->bounds.left, p->bounds.top,
+               p->defrect.width, p->defrect.height,
+               p->defrect.left, p->defrect.top,
+               p->pixelaspect.numerator, p->pixelaspect.denominator);
+}
 
-       /* zero the reserved fields */
-       c->reserved[0] = c->reserved[1] = 0;
-       for (i = 0; i < c->count; i++)
-               c->controls[i].reserved2[0] = 0;
+static void v4l_print_crop(const void *arg, bool write_only)
+{
+       const struct v4l2_crop *p = arg;
 
-       /* V4L2_CID_PRIVATE_BASE cannot be used as control class
-          when using extended controls.
-          Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL
-          is it allowed for backwards compatibility.
-        */
-       if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE)
-               return 0;
-       /* Check that all controls are from the same control class. */
-       for (i = 0; i < c->count; i++) {
-               if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) {
-                       c->error_idx = i;
-                       return 0;
-               }
-       }
-       return 1;
+       pr_cont("type=%s, wxh=%dx%d, x,y=%d,%d\n",
+               prt_names(p->type, v4l2_type_names),
+               p->c.width, p->c.height,
+               p->c.left, p->c.top);
 }
 
-static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type)
+static void v4l_print_selection(const void *arg, bool write_only)
 {
-       if (ops == NULL)
-               return -EINVAL;
+       const struct v4l2_selection *p = arg;
 
-       switch (type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               if (ops->vidioc_g_fmt_vid_cap ||
-                               ops->vidioc_g_fmt_vid_cap_mplane)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-               if (ops->vidioc_g_fmt_vid_cap_mplane)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-               if (ops->vidioc_g_fmt_vid_overlay)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               if (ops->vidioc_g_fmt_vid_out ||
-                               ops->vidioc_g_fmt_vid_out_mplane)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-               if (ops->vidioc_g_fmt_vid_out_mplane)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-               if (ops->vidioc_g_fmt_vid_out_overlay)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               if (ops->vidioc_g_fmt_vbi_cap)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_VBI_OUTPUT:
-               if (ops->vidioc_g_fmt_vbi_out)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-               if (ops->vidioc_g_fmt_sliced_vbi_cap)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-               if (ops->vidioc_g_fmt_sliced_vbi_out)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_PRIVATE:
-               if (ops->vidioc_g_fmt_type_private)
-                       return 0;
-               break;
-       }
-       return -EINVAL;
+       pr_cont("type=%s, target=%d, flags=0x%x, wxh=%dx%d, x,y=%d,%d\n",
+               prt_names(p->type, v4l2_type_names),
+               p->target, p->flags,
+               p->r.width, p->r.height, p->r.left, p->r.top);
 }
 
-static long __video_do_ioctl(struct file *file,
-               unsigned int cmd, void *arg)
+static void v4l_print_jpegcompression(const void *arg, bool write_only)
 {
-       struct video_device *vfd = video_devdata(file);
-       const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
-       void *fh = file->private_data;
-       struct v4l2_fh *vfh = NULL;
-       int use_fh_prio = 0;
-       long ret = -ENOTTY;
+       const struct v4l2_jpegcompression *p = arg;
 
-       if (ops == NULL) {
-               printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n",
-                               vfd->name);
-               return ret;
-       }
+       pr_cont("quality=%d, APPn=%d, APP_len=%d, "
+               "COM_len=%d, jpeg_markers=0x%x\n",
+               p->quality, p->APPn, p->APP_len,
+               p->COM_len, p->jpeg_markers);
+}
 
-       if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
-               vfh = file->private_data;
-               use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
-       }
+static void v4l_print_enc_idx(const void *arg, bool write_only)
+{
+       const struct v4l2_enc_idx *p = arg;
 
-       if (v4l2_is_known_ioctl(cmd)) {
-               struct v4l2_ioctl_info *info = &v4l2_ioctls[_IOC_NR(cmd)];
+       pr_cont("entries=%d, entries_cap=%d\n",
+                       p->entries, p->entries_cap);
+}
 
-               if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
-                   !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
-                       return -ENOTTY;
+static void v4l_print_encoder_cmd(const void *arg, bool write_only)
+{
+       const struct v4l2_encoder_cmd *p = arg;
 
-               if (use_fh_prio && (info->flags & INFO_FL_PRIO)) {
-                       ret = v4l2_prio_check(vfd->prio, vfh->prio);
-                       if (ret)
-                               return ret;
-               }
-       }
+       pr_cont("cmd=%d, flags=0x%x\n",
+                       p->cmd, p->flags);
+}
 
-       if ((vfd->debug & V4L2_DEBUG_IOCTL) &&
-                               !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) {
-               v4l_print_ioctl(vfd->name, cmd);
-               printk(KERN_CONT "\n");
-       }
+static void v4l_print_decoder_cmd(const void *arg, bool write_only)
+{
+       const struct v4l2_decoder_cmd *p = arg;
 
-       switch (cmd) {
+       pr_cont("cmd=%d, flags=0x%x\n", p->cmd, p->flags);
 
-       /* --- capabilities ------------------------------------------ */
-       case VIDIOC_QUERYCAP:
-       {
-               struct v4l2_capability *cap = (struct v4l2_capability *)arg;
-
-               cap->version = LINUX_VERSION_CODE;
-               ret = ops->vidioc_querycap(file, fh, cap);
-               if (!ret)
-                       dbgarg(cmd, "driver=%s, card=%s, bus=%s, "
-                                       "version=0x%08x, "
-                                       "capabilities=0x%08x, "
-                                       "device_caps=0x%08x\n",
-                                       cap->driver, cap->card, cap->bus_info,
-                                       cap->version,
-                                       cap->capabilities,
-                                       cap->device_caps);
-               break;
-       }
+       if (p->cmd == V4L2_DEC_CMD_START)
+               pr_info("speed=%d, format=%u\n",
+                               p->start.speed, p->start.format);
+       else if (p->cmd == V4L2_DEC_CMD_STOP)
+               pr_info("pts=%llu\n", p->stop.pts);
+}
 
-       /* --- priority ------------------------------------------ */
-       case VIDIOC_G_PRIORITY:
-       {
-               enum v4l2_priority *p = arg;
+static void v4l_print_dbg_chip_ident(const void *arg, bool write_only)
+{
+       const struct v4l2_dbg_chip_ident *p = arg;
+
+       pr_cont("type=%u, ", p->match.type);
+       if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
+               pr_cont("name=%s, ", p->match.name);
+       else
+               pr_cont("addr=%u, ", p->match.addr);
+       pr_cont("chip_ident=%u, revision=0x%x\n",
+                       p->ident, p->revision);
+}
 
-               if (ops->vidioc_g_priority) {
-                       ret = ops->vidioc_g_priority(file, fh, p);
-               } else if (use_fh_prio) {
-                       *p = v4l2_prio_max(&vfd->v4l2_dev->prio);
-                       ret = 0;
-               }
-               if (!ret)
-                       dbgarg(cmd, "priority is %d\n", *p);
-               break;
-       }
-       case VIDIOC_S_PRIORITY:
-       {
-               enum v4l2_priority *p = arg;
+static void v4l_print_dbg_register(const void *arg, bool write_only)
+{
+       const struct v4l2_dbg_register *p = arg;
+
+       pr_cont("type=%u, ", p->match.type);
+       if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
+               pr_cont("name=%s, ", p->match.name);
+       else
+               pr_cont("addr=%u, ", p->match.addr);
+       pr_cont("reg=0x%llx, val=0x%llx\n",
+                       p->reg, p->val);
+}
 
-               dbgarg(cmd, "setting priority to %d\n", *p);
-               if (ops->vidioc_s_priority)
-                       ret = ops->vidioc_s_priority(file, fh, *p);
-               else
-                       ret = v4l2_prio_change(&vfd->v4l2_dev->prio,
-                                                       &vfh->prio, *p);
-               break;
-       }
+static void v4l_print_dv_enum_presets(const void *arg, bool write_only)
+{
+       const struct v4l2_dv_enum_preset *p = arg;
 
-       /* --- capture ioctls ---------------------------------------- */
-       case VIDIOC_ENUM_FMT:
-       {
-               struct v4l2_fmtdesc *f = arg;
+       pr_cont("index=%u, preset=%u, name=%s, width=%u, height=%u\n",
+                       p->index, p->preset, p->name, p->width, p->height);
+}
 
-               ret = -EINVAL;
-               switch (f->type) {
-               case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-                       if (likely(ops->vidioc_enum_fmt_vid_cap))
-                               ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f);
-                       break;
-               case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-                       if (likely(ops->vidioc_enum_fmt_vid_cap_mplane))
-                               ret = ops->vidioc_enum_fmt_vid_cap_mplane(file,
-                                                                       fh, f);
+static void v4l_print_dv_preset(const void *arg, bool write_only)
+{
+       const struct v4l2_dv_preset *p = arg;
+
+       pr_cont("preset=%u\n", p->preset);
+}
+
+static void v4l_print_dv_timings(const void *arg, bool write_only)
+{
+       const struct v4l2_dv_timings *p = arg;
+
+       switch (p->type) {
+       case V4L2_DV_BT_656_1120:
+               pr_cont("type=bt-656/1120, interlaced=%u, "
+                       "pixelclock=%llu, "
+                       "width=%u, height=%u, polarities=0x%x, "
+                       "hfrontporch=%u, hsync=%u, "
+                       "hbackporch=%u, vfrontporch=%u, "
+                       "vsync=%u, vbackporch=%u, "
+                       "il_vfrontporch=%u, il_vsync=%u, "
+                       "il_vbackporch=%u, standards=0x%x, flags=0x%x\n",
+                               p->bt.interlaced, p->bt.pixelclock,
+                               p->bt.width, p->bt.height,
+                               p->bt.polarities, p->bt.hfrontporch,
+                               p->bt.hsync, p->bt.hbackporch,
+                               p->bt.vfrontporch, p->bt.vsync,
+                               p->bt.vbackporch, p->bt.il_vfrontporch,
+                               p->bt.il_vsync, p->bt.il_vbackporch,
+                               p->bt.standards, p->bt.flags);
+               break;
+       default:
+               pr_cont("type=%d\n", p->type);
+               break;
+       }
+}
+
+static void v4l_print_enum_dv_timings(const void *arg, bool write_only)
+{
+       const struct v4l2_enum_dv_timings *p = arg;
+
+       pr_cont("index=%u, ", p->index);
+       v4l_print_dv_timings(&p->timings, write_only);
+}
+
+static void v4l_print_dv_timings_cap(const void *arg, bool write_only)
+{
+       const struct v4l2_dv_timings_cap *p = arg;
+
+       switch (p->type) {
+       case V4L2_DV_BT_656_1120:
+               pr_cont("type=bt-656/1120, width=%u-%u, height=%u-%u, "
+                       "pixelclock=%llu-%llu, standards=0x%x, capabilities=0x%x\n",
+                       p->bt.min_width, p->bt.max_width,
+                       p->bt.min_height, p->bt.max_height,
+                       p->bt.min_pixelclock, p->bt.max_pixelclock,
+                       p->bt.standards, p->bt.capabilities);
+               break;
+       default:
+               pr_cont("type=%u\n", p->type);
+               break;
+       }
+}
+
+static void v4l_print_frmsizeenum(const void *arg, bool write_only)
+{
+       const struct v4l2_frmsizeenum *p = arg;
+
+       pr_cont("index=%u, pixelformat=%c%c%c%c, type=%u",
+                       p->index,
+                       (p->pixel_format & 0xff),
+                       (p->pixel_format >>  8) & 0xff,
+                       (p->pixel_format >> 16) & 0xff,
+                       (p->pixel_format >> 24) & 0xff,
+                       p->type);
+       switch (p->type) {
+       case V4L2_FRMSIZE_TYPE_DISCRETE:
+               pr_cont(" wxh=%ux%u\n",
+                       p->discrete.width, p->discrete.height);
+               break;
+       case V4L2_FRMSIZE_TYPE_STEPWISE:
+               pr_cont(" min=%ux%u, max=%ux%u, step=%ux%u\n",
+                               p->stepwise.min_width,  p->stepwise.min_height,
+                               p->stepwise.step_width, p->stepwise.step_height,
+                               p->stepwise.max_width,  p->stepwise.max_height);
+               break;
+       case V4L2_FRMSIZE_TYPE_CONTINUOUS:
+               /* fall through */
+       default:
+               pr_cont("\n");
+               break;
+       }
+}
+
+static void v4l_print_frmivalenum(const void *arg, bool write_only)
+{
+       const struct v4l2_frmivalenum *p = arg;
+
+       pr_cont("index=%u, pixelformat=%c%c%c%c, wxh=%ux%u, type=%u",
+                       p->index,
+                       (p->pixel_format & 0xff),
+                       (p->pixel_format >>  8) & 0xff,
+                       (p->pixel_format >> 16) & 0xff,
+                       (p->pixel_format >> 24) & 0xff,
+                       p->width, p->height, p->type);
+       switch (p->type) {
+       case V4L2_FRMIVAL_TYPE_DISCRETE:
+               pr_cont(" fps=%d/%d\n",
+                               p->discrete.numerator,
+                               p->discrete.denominator);
+               break;
+       case V4L2_FRMIVAL_TYPE_STEPWISE:
+               pr_cont(" min=%d/%d, max=%d/%d, step=%d/%d\n",
+                               p->stepwise.min.numerator,
+                               p->stepwise.min.denominator,
+                               p->stepwise.max.numerator,
+                               p->stepwise.max.denominator,
+                               p->stepwise.step.numerator,
+                               p->stepwise.step.denominator);
+               break;
+       case V4L2_FRMIVAL_TYPE_CONTINUOUS:
+               /* fall through */
+       default:
+               pr_cont("\n");
+               break;
+       }
+}
+
+static void v4l_print_event(const void *arg, bool write_only)
+{
+       const struct v4l2_event *p = arg;
+       const struct v4l2_event_ctrl *c;
+
+       pr_cont("type=0x%x, pending=%u, sequence=%u, id=%u, "
+               "timestamp=%lu.%9.9lu\n",
+                       p->type, p->pending, p->sequence, p->id,
+                       p->timestamp.tv_sec, p->timestamp.tv_nsec);
+       switch (p->type) {
+       case V4L2_EVENT_VSYNC:
+               printk(KERN_DEBUG "field=%s\n",
+                       prt_names(p->u.vsync.field, v4l2_field_names));
+               break;
+       case V4L2_EVENT_CTRL:
+               c = &p->u.ctrl;
+               printk(KERN_DEBUG "changes=0x%x, type=%u, ",
+                       c->changes, c->type);
+               if (c->type == V4L2_CTRL_TYPE_INTEGER64)
+                       pr_cont("value64=%lld, ", c->value64);
+               else
+                       pr_cont("value=%d, ", c->value);
+               pr_cont("flags=0x%x, minimum=%d, maximum=%d, step=%d,"
+                               " default_value=%d\n",
+                       c->flags, c->minimum, c->maximum,
+                       c->step, c->default_value);
+               break;
+       case V4L2_EVENT_FRAME_SYNC:
+               pr_cont("frame_sequence=%u\n",
+                       p->u.frame_sync.frame_sequence);
+               break;
+       }
+}
+
+static void v4l_print_event_subscription(const void *arg, bool write_only)
+{
+       const struct v4l2_event_subscription *p = arg;
+
+       pr_cont("type=0x%x, id=0x%x, flags=0x%x\n",
+                       p->type, p->id, p->flags);
+}
+
+static void v4l_print_sliced_vbi_cap(const void *arg, bool write_only)
+{
+       const struct v4l2_sliced_vbi_cap *p = arg;
+       int i;
+
+       pr_cont("type=%s, service_set=0x%08x\n",
+                       prt_names(p->type, v4l2_type_names), p->service_set);
+       for (i = 0; i < 24; i++)
+               printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i,
+                               p->service_lines[0][i],
+                               p->service_lines[1][i]);
+}
+
+static void v4l_print_freq_band(const void *arg, bool write_only)
+{
+       const struct v4l2_frequency_band *p = arg;
+
+       pr_cont("tuner=%u, type=%u, index=%u, capability=0x%x, "
+                       "rangelow=%u, rangehigh=%u, modulation=0x%x\n",
+                       p->tuner, p->type, p->index,
+                       p->capability, p->rangelow,
+                       p->rangehigh, p->modulation);
+}
+
+static void v4l_print_u32(const void *arg, bool write_only)
+{
+       pr_cont("value=%u\n", *(const u32 *)arg);
+}
+
+static void v4l_print_newline(const void *arg, bool write_only)
+{
+       pr_cont("\n");
+}
+
+static void v4l_print_default(const void *arg, bool write_only)
+{
+       pr_cont("driver-specific ioctl\n");
+}
+
+static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
+{
+       __u32 i;
+
+       /* zero the reserved fields */
+       c->reserved[0] = c->reserved[1] = 0;
+       for (i = 0; i < c->count; i++)
+               c->controls[i].reserved2[0] = 0;
+
+       /* V4L2_CID_PRIVATE_BASE cannot be used as control class
+          when using extended controls.
+          Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL
+          is it allowed for backwards compatibility.
+        */
+       if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE)
+               return 0;
+       /* Check that all controls are from the same control class. */
+       for (i = 0; i < c->count; i++) {
+               if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) {
+                       c->error_idx = i;
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type)
+{
+       if (ops == NULL)
+               return -EINVAL;
+
+       switch (type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (ops->vidioc_g_fmt_vid_cap ||
+                               ops->vidioc_g_fmt_vid_cap_mplane)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               if (ops->vidioc_g_fmt_vid_cap_mplane)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               if (ops->vidioc_g_fmt_vid_overlay)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               if (ops->vidioc_g_fmt_vid_out ||
+                               ops->vidioc_g_fmt_vid_out_mplane)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               if (ops->vidioc_g_fmt_vid_out_mplane)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+               if (ops->vidioc_g_fmt_vid_out_overlay)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               if (ops->vidioc_g_fmt_vbi_cap)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               if (ops->vidioc_g_fmt_vbi_out)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+               if (ops->vidioc_g_fmt_sliced_vbi_cap)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               if (ops->vidioc_g_fmt_sliced_vbi_out)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_PRIVATE:
+               if (ops->vidioc_g_fmt_type_private)
+                       return 0;
+               break;
+       }
+       return -EINVAL;
+}
+
+static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_capability *cap = (struct v4l2_capability *)arg;
+
+       cap->version = LINUX_VERSION_CODE;
+       return ops->vidioc_querycap(file, fh, cap);
+}
+
+static int v4l_s_input(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       return ops->vidioc_s_input(file, fh, *(unsigned int *)arg);
+}
+
+static int v4l_s_output(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       return ops->vidioc_s_output(file, fh, *(unsigned int *)arg);
+}
+
+static int v4l_g_priority(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd;
+       u32 *p = arg;
+
+       if (ops->vidioc_g_priority)
+               return ops->vidioc_g_priority(file, fh, arg);
+       vfd = video_devdata(file);
+       *p = v4l2_prio_max(&vfd->v4l2_dev->prio);
+       return 0;
+}
+
+static int v4l_s_priority(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd;
+       struct v4l2_fh *vfh;
+       u32 *p = arg;
+
+       if (ops->vidioc_s_priority)
+               return ops->vidioc_s_priority(file, fh, *p);
+       vfd = video_devdata(file);
+       vfh = file->private_data;
+       return v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p);
+}
+
+static int v4l_enuminput(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_input *p = arg;
+
+       /*
+        * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
+        * CAP_STD here based on ioctl handler provided by the
+        * driver. If the driver doesn't support these
+        * for a specific input, it must override these flags.
+        */
+       if (ops->vidioc_s_std)
+               p->capabilities |= V4L2_IN_CAP_STD;
+       if (ops->vidioc_s_dv_preset)
+               p->capabilities |= V4L2_IN_CAP_PRESETS;
+       if (ops->vidioc_s_dv_timings)
+               p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS;
+
+       return ops->vidioc_enum_input(file, fh, p);
+}
+
+static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_output *p = arg;
+
+       /*
+        * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
+        * CAP_STD here based on ioctl handler provided by the
+        * driver. If the driver doesn't support these
+        * for a specific output, it must override these flags.
+        */
+       if (ops->vidioc_s_std)
+               p->capabilities |= V4L2_OUT_CAP_STD;
+       if (ops->vidioc_s_dv_preset)
+               p->capabilities |= V4L2_OUT_CAP_PRESETS;
+       if (ops->vidioc_s_dv_timings)
+               p->capabilities |= V4L2_OUT_CAP_CUSTOM_TIMINGS;
+
+       return ops->vidioc_enum_output(file, fh, p);
+}
+
+static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_fmtdesc *p = arg;
+
+       switch (p->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (unlikely(!ops->vidioc_enum_fmt_vid_cap))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-                       if (likely(ops->vidioc_enum_fmt_vid_overlay))
-                               ret = ops->vidioc_enum_fmt_vid_overlay(file,
-                                       fh, f);
+               return ops->vidioc_enum_fmt_vid_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               if (unlikely(!ops->vidioc_enum_fmt_vid_cap_mplane))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-                       if (likely(ops->vidioc_enum_fmt_vid_out))
-                               ret = ops->vidioc_enum_fmt_vid_out(file, fh, f);
+               return ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               if (unlikely(!ops->vidioc_enum_fmt_vid_overlay))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-                       if (likely(ops->vidioc_enum_fmt_vid_out_mplane))
-                               ret = ops->vidioc_enum_fmt_vid_out_mplane(file,
-                                                                       fh, f);
+               return ops->vidioc_enum_fmt_vid_overlay(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               if (unlikely(!ops->vidioc_enum_fmt_vid_out))
                        break;
-               case V4L2_BUF_TYPE_PRIVATE:
-                       if (likely(ops->vidioc_enum_fmt_type_private))
-                               ret = ops->vidioc_enum_fmt_type_private(file,
-                                                               fh, f);
+               return ops->vidioc_enum_fmt_vid_out(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               if (unlikely(!ops->vidioc_enum_fmt_vid_out_mplane))
                        break;
-               default:
+               return ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_PRIVATE:
+               if (unlikely(!ops->vidioc_enum_fmt_type_private))
                        break;
-               }
-               if (likely(!ret))
-                       dbgarg(cmd, "index=%d, type=%d, flags=%d, "
-                               "pixelformat=%c%c%c%c, description='%s'\n",
-                               f->index, f->type, f->flags,
-                               (f->pixelformat & 0xff),
-                               (f->pixelformat >>  8) & 0xff,
-                               (f->pixelformat >> 16) & 0xff,
-                               (f->pixelformat >> 24) & 0xff,
-                               f->description);
-               break;
+               return ops->vidioc_enum_fmt_type_private(file, fh, arg);
        }
-       case VIDIOC_G_FMT:
-       {
-               struct v4l2_format *f = (struct v4l2_format *)arg;
-
-               /* FIXME: Should be one dump per type */
-               dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names));
-
-               ret = -EINVAL;
-               switch (f->type) {
-               case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-                       if (ops->vidioc_g_fmt_vid_cap)
-                               ret = ops->vidioc_g_fmt_vid_cap(file, fh, f);
-                       if (!ret)
-                               v4l_print_pix_fmt(vfd, &f->fmt.pix);
+       return -EINVAL;
+}
+
+static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_format *p = arg;
+
+       switch (p->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (unlikely(!ops->vidioc_g_fmt_vid_cap))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-                       if (ops->vidioc_g_fmt_vid_cap_mplane)
-                               ret = ops->vidioc_g_fmt_vid_cap_mplane(file,
-                                                                       fh, f);
-                       if (!ret)
-                               v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+               return ops->vidioc_g_fmt_vid_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               if (unlikely(!ops->vidioc_g_fmt_vid_cap_mplane))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-                       if (likely(ops->vidioc_g_fmt_vid_overlay))
-                               ret = ops->vidioc_g_fmt_vid_overlay(file,
-                                                                   fh, f);
+               return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               if (unlikely(!ops->vidioc_g_fmt_vid_overlay))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-                       if (ops->vidioc_g_fmt_vid_out)
-                               ret = ops->vidioc_g_fmt_vid_out(file, fh, f);
-                       if (!ret)
-                               v4l_print_pix_fmt(vfd, &f->fmt.pix);
+               return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               if (unlikely(!ops->vidioc_g_fmt_vid_out))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-                       if (ops->vidioc_g_fmt_vid_out_mplane)
-                               ret = ops->vidioc_g_fmt_vid_out_mplane(file,
-                                                                       fh, f);
-                       if (!ret)
-                               v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+               return ops->vidioc_g_fmt_vid_out(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               if (unlikely(!ops->vidioc_g_fmt_vid_out_mplane))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-                       if (likely(ops->vidioc_g_fmt_vid_out_overlay))
-                               ret = ops->vidioc_g_fmt_vid_out_overlay(file,
-                                      fh, f);
+               return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+               if (unlikely(!ops->vidioc_g_fmt_vid_out_overlay))
                        break;
-               case V4L2_BUF_TYPE_VBI_CAPTURE:
-                       if (likely(ops->vidioc_g_fmt_vbi_cap))
-                               ret = ops->vidioc_g_fmt_vbi_cap(file, fh, f);
+               return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_g_fmt_vbi_cap))
                        break;
-               case V4L2_BUF_TYPE_VBI_OUTPUT:
-                       if (likely(ops->vidioc_g_fmt_vbi_out))
-                               ret = ops->vidioc_g_fmt_vbi_out(file, fh, f);
+               return ops->vidioc_g_fmt_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_g_fmt_vbi_out))
                        break;
-               case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-                       if (likely(ops->vidioc_g_fmt_sliced_vbi_cap))
-                               ret = ops->vidioc_g_fmt_sliced_vbi_cap(file,
-                                                                       fh, f);
+               return ops->vidioc_g_fmt_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_cap))
                        break;
-               case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-                       if (likely(ops->vidioc_g_fmt_sliced_vbi_out))
-                               ret = ops->vidioc_g_fmt_sliced_vbi_out(file,
-                                                                       fh, f);
+               return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_out))
                        break;
-               case V4L2_BUF_TYPE_PRIVATE:
-                       if (likely(ops->vidioc_g_fmt_type_private))
-                               ret = ops->vidioc_g_fmt_type_private(file,
-                                                               fh, f);
+               return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_PRIVATE:
+               if (unlikely(!ops->vidioc_g_fmt_type_private))
                        break;
-               }
-               break;
+               return ops->vidioc_g_fmt_type_private(file, fh, arg);
        }
-       case VIDIOC_S_FMT:
-       {
-               struct v4l2_format *f = (struct v4l2_format *)arg;
-
-               ret = -EINVAL;
+       return -EINVAL;
+}
 
-               /* FIXME: Should be one dump per type */
-               dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names));
+static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_format *p = arg;
 
-               switch (f->type) {
-               case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-                       CLEAR_AFTER_FIELD(f, fmt.pix);
-                       v4l_print_pix_fmt(vfd, &f->fmt.pix);
-                       if (ops->vidioc_s_fmt_vid_cap)
-                               ret = ops->vidioc_s_fmt_vid_cap(file, fh, f);
+       switch (p->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (unlikely(!ops->vidioc_s_fmt_vid_cap))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-                       CLEAR_AFTER_FIELD(f, fmt.pix_mp);
-                       v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
-                       if (ops->vidioc_s_fmt_vid_cap_mplane)
-                               ret = ops->vidioc_s_fmt_vid_cap_mplane(file,
-                                                                       fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.pix);
+               return ops->vidioc_s_fmt_vid_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-                       CLEAR_AFTER_FIELD(f, fmt.win);
-                       if (ops->vidioc_s_fmt_vid_overlay)
-                               ret = ops->vidioc_s_fmt_vid_overlay(file,
-                                                                   fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+               return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-                       CLEAR_AFTER_FIELD(f, fmt.pix);
-                       v4l_print_pix_fmt(vfd, &f->fmt.pix);
-                       if (ops->vidioc_s_fmt_vid_out)
-                               ret = ops->vidioc_s_fmt_vid_out(file, fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.win);
+               return ops->vidioc_s_fmt_vid_overlay(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               if (unlikely(!ops->vidioc_s_fmt_vid_out))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-                       CLEAR_AFTER_FIELD(f, fmt.pix_mp);
-                       v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
-                       if (ops->vidioc_s_fmt_vid_out_mplane)
-                               ret = ops->vidioc_s_fmt_vid_out_mplane(file,
-                                                                       fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.pix);
+               return ops->vidioc_s_fmt_vid_out(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-                       CLEAR_AFTER_FIELD(f, fmt.win);
-                       if (ops->vidioc_s_fmt_vid_out_overlay)
-                               ret = ops->vidioc_s_fmt_vid_out_overlay(file,
-                                       fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+               return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+               if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
                        break;
-               case V4L2_BUF_TYPE_VBI_CAPTURE:
-                       CLEAR_AFTER_FIELD(f, fmt.vbi);
-                       if (likely(ops->vidioc_s_fmt_vbi_cap))
-                               ret = ops->vidioc_s_fmt_vbi_cap(file, fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.win);
+               return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg);
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_s_fmt_vbi_cap))
                        break;
-               case V4L2_BUF_TYPE_VBI_OUTPUT:
-                       CLEAR_AFTER_FIELD(f, fmt.vbi);
-                       if (likely(ops->vidioc_s_fmt_vbi_out))
-                               ret = ops->vidioc_s_fmt_vbi_out(file, fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.vbi);
+               return ops->vidioc_s_fmt_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_s_fmt_vbi_out))
                        break;
-               case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-                       CLEAR_AFTER_FIELD(f, fmt.sliced);
-                       if (likely(ops->vidioc_s_fmt_sliced_vbi_cap))
-                               ret = ops->vidioc_s_fmt_sliced_vbi_cap(file,
-                                                                       fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.vbi);
+               return ops->vidioc_s_fmt_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap))
                        break;
-               case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-                       CLEAR_AFTER_FIELD(f, fmt.sliced);
-                       if (likely(ops->vidioc_s_fmt_sliced_vbi_out))
-                               ret = ops->vidioc_s_fmt_sliced_vbi_out(file,
-                                                                       fh, f);
-
+               CLEAR_AFTER_FIELD(p, fmt.sliced);
+               return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out))
                        break;
-               case V4L2_BUF_TYPE_PRIVATE:
-                       /* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */
-                       if (likely(ops->vidioc_s_fmt_type_private))
-                               ret = ops->vidioc_s_fmt_type_private(file,
-                                                               fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.sliced);
+               return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_PRIVATE:
+               if (unlikely(!ops->vidioc_s_fmt_type_private))
                        break;
-               }
-               break;
+               return ops->vidioc_s_fmt_type_private(file, fh, arg);
        }
-       case VIDIOC_TRY_FMT:
-       {
-               struct v4l2_format *f = (struct v4l2_format *)arg;
-
-               /* FIXME: Should be one dump per type */
-               dbgarg(cmd, "type=%s\n", prt_names(f->type,
-                                               v4l2_type_names));
-               ret = -EINVAL;
-               switch (f->type) {
-               case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-                       CLEAR_AFTER_FIELD(f, fmt.pix);
-                       if (ops->vidioc_try_fmt_vid_cap)
-                               ret = ops->vidioc_try_fmt_vid_cap(file, fh, f);
-                       if (!ret)
-                               v4l_print_pix_fmt(vfd, &f->fmt.pix);
-                       break;
-               case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-                       CLEAR_AFTER_FIELD(f, fmt.pix_mp);
-                       if (ops->vidioc_try_fmt_vid_cap_mplane)
-                               ret = ops->vidioc_try_fmt_vid_cap_mplane(file,
-                                                                        fh, f);
-                       if (!ret)
-                               v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
-                       break;
-               case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-                       CLEAR_AFTER_FIELD(f, fmt.win);
-                       if (likely(ops->vidioc_try_fmt_vid_overlay))
-                               ret = ops->vidioc_try_fmt_vid_overlay(file,
-                                       fh, f);
+       return -EINVAL;
+}
+
+static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_format *p = arg;
+
+       switch (p->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (unlikely(!ops->vidioc_try_fmt_vid_cap))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-                       CLEAR_AFTER_FIELD(f, fmt.pix);
-                       if (ops->vidioc_try_fmt_vid_out)
-                               ret = ops->vidioc_try_fmt_vid_out(file, fh, f);
-                       if (!ret)
-                               v4l_print_pix_fmt(vfd, &f->fmt.pix);
+               CLEAR_AFTER_FIELD(p, fmt.pix);
+               return ops->vidioc_try_fmt_vid_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-                       CLEAR_AFTER_FIELD(f, fmt.pix_mp);
-                       if (ops->vidioc_try_fmt_vid_out_mplane)
-                               ret = ops->vidioc_try_fmt_vid_out_mplane(file,
-                                                                        fh, f);
-                       if (!ret)
-                               v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+               CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+               return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-                       CLEAR_AFTER_FIELD(f, fmt.win);
-                       if (likely(ops->vidioc_try_fmt_vid_out_overlay))
-                               ret = ops->vidioc_try_fmt_vid_out_overlay(file,
-                                      fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.win);
+               return ops->vidioc_try_fmt_vid_overlay(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               if (unlikely(!ops->vidioc_try_fmt_vid_out))
                        break;
-               case V4L2_BUF_TYPE_VBI_CAPTURE:
-                       CLEAR_AFTER_FIELD(f, fmt.vbi);
-                       if (likely(ops->vidioc_try_fmt_vbi_cap))
-                               ret = ops->vidioc_try_fmt_vbi_cap(file, fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.pix);
+               return ops->vidioc_try_fmt_vid_out(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
                        break;
-               case V4L2_BUF_TYPE_VBI_OUTPUT:
-                       CLEAR_AFTER_FIELD(f, fmt.vbi);
-                       if (likely(ops->vidioc_try_fmt_vbi_out))
-                               ret = ops->vidioc_try_fmt_vbi_out(file, fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+               return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+               if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
                        break;
-               case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-                       CLEAR_AFTER_FIELD(f, fmt.sliced);
-                       if (likely(ops->vidioc_try_fmt_sliced_vbi_cap))
-                               ret = ops->vidioc_try_fmt_sliced_vbi_cap(file,
-                                                               fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.win);
+               return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg);
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_try_fmt_vbi_cap))
                        break;
-               case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-                       CLEAR_AFTER_FIELD(f, fmt.sliced);
-                       if (likely(ops->vidioc_try_fmt_sliced_vbi_out))
-                               ret = ops->vidioc_try_fmt_sliced_vbi_out(file,
-                                                               fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.vbi);
+               return ops->vidioc_try_fmt_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_try_fmt_vbi_out))
                        break;
-               case V4L2_BUF_TYPE_PRIVATE:
-                       /* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */
-                       if (likely(ops->vidioc_try_fmt_type_private))
-                               ret = ops->vidioc_try_fmt_type_private(file,
-                                                               fh, f);
+               CLEAR_AFTER_FIELD(p, fmt.vbi);
+               return ops->vidioc_try_fmt_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap))
                        break;
-               }
-               break;
-       }
-       /* FIXME: Those buf reqs could be handled here,
-          with some changes on videobuf to allow its header to be included at
-          videodev2.h or being merged at videodev2.
-        */
-       case VIDIOC_REQBUFS:
-       {
-               struct v4l2_requestbuffers *p = arg;
-
-               ret = check_fmt(ops, p->type);
-               if (ret)
+               CLEAR_AFTER_FIELD(p, fmt.sliced);
+               return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out))
                        break;
-
-               if (p->type < V4L2_BUF_TYPE_PRIVATE)
-                       CLEAR_AFTER_FIELD(p, memory);
-
-               ret = ops->vidioc_reqbufs(file, fh, p);
-               dbgarg(cmd, "count=%d, type=%s, memory=%s\n",
-                               p->count,
-                               prt_names(p->type, v4l2_type_names),
-                               prt_names(p->memory, v4l2_memory_names));
-               break;
-       }
-       case VIDIOC_QUERYBUF:
-       {
-               struct v4l2_buffer *p = arg;
-
-               ret = check_fmt(ops, p->type);
-               if (ret)
+               CLEAR_AFTER_FIELD(p, fmt.sliced);
+               return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_PRIVATE:
+               if (unlikely(!ops->vidioc_try_fmt_type_private))
                        break;
-
-               ret = ops->vidioc_querybuf(file, fh, p);
-               if (!ret)
-                       dbgbuf(cmd, vfd, p);
-               break;
+               return ops->vidioc_try_fmt_type_private(file, fh, arg);
        }
-       case VIDIOC_QBUF:
-       {
-               struct v4l2_buffer *p = arg;
+       return -EINVAL;
+}
 
-               ret = check_fmt(ops, p->type);
-               if (ret)
-                       break;
+static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       return ops->vidioc_streamon(file, fh, *(unsigned int *)arg);
+}
 
-               ret = ops->vidioc_qbuf(file, fh, p);
-               if (!ret)
-                       dbgbuf(cmd, vfd, p);
-               break;
-       }
-       case VIDIOC_DQBUF:
-       {
-               struct v4l2_buffer *p = arg;
+static int v4l_streamoff(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       return ops->vidioc_streamoff(file, fh, *(unsigned int *)arg);
+}
 
-               ret = check_fmt(ops, p->type);
-               if (ret)
-                       break;
+static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_tuner *p = arg;
+       int err;
 
-               ret = ops->vidioc_dqbuf(file, fh, p);
-               if (!ret)
-                       dbgbuf(cmd, vfd, p);
-               break;
-       }
-       case VIDIOC_OVERLAY:
-       {
-               int *i = arg;
+       p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+       err = ops->vidioc_g_tuner(file, fh, p);
+       if (!err)
+               p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
+       return err;
+}
 
-               dbgarg(cmd, "value=%d\n", *i);
-               ret = ops->vidioc_overlay(file, fh, *i);
-               break;
-       }
-       case VIDIOC_G_FBUF:
-       {
-               struct v4l2_framebuffer *p = arg;
-
-               ret = ops->vidioc_g_fbuf(file, fh, arg);
-               if (!ret) {
-                       dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n",
-                                       p->capability, p->flags,
-                                       (unsigned long)p->base);
-                       v4l_print_pix_fmt(vfd, &p->fmt);
-               }
-               break;
-       }
-       case VIDIOC_S_FBUF:
-       {
-               struct v4l2_framebuffer *p = arg;
-
-               dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n",
-                       p->capability, p->flags, (unsigned long)p->base);
-               v4l_print_pix_fmt(vfd, &p->fmt);
-               ret = ops->vidioc_s_fbuf(file, fh, arg);
-               break;
-       }
-       case VIDIOC_STREAMON:
-       {
-               enum v4l2_buf_type i = *(int *)arg;
+static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_tuner *p = arg;
 
-               dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
-               ret = ops->vidioc_streamon(file, fh, i);
-               break;
-       }
-       case VIDIOC_STREAMOFF:
-       {
-               enum v4l2_buf_type i = *(int *)arg;
+       p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+       return ops->vidioc_s_tuner(file, fh, p);
+}
 
-               dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
-               ret = ops->vidioc_streamoff(file, fh, i);
-               break;
-       }
-       /* ---------- tv norms ---------- */
-       case VIDIOC_ENUMSTD:
-       {
-               struct v4l2_standard *p = arg;
-               v4l2_std_id id = vfd->tvnorms, curr_id = 0;
-               unsigned int index = p->index, i, j = 0;
-               const char *descr = "";
-
-               if (id == 0)
-                       break;
-               ret = -EINVAL;
-
-               /* Return norm array in a canonical way */
-               for (i = 0; i <= index && id; i++) {
-                       /* last std value in the standards array is 0, so this
-                          while always ends there since (id & 0) == 0. */
-                       while ((id & standards[j].std) != standards[j].std)
-                               j++;
-                       curr_id = standards[j].std;
-                       descr = standards[j].descr;
-                       j++;
-                       if (curr_id == 0)
-                               break;
-                       if (curr_id != V4L2_STD_PAL &&
-                           curr_id != V4L2_STD_SECAM &&
-                           curr_id != V4L2_STD_NTSC)
-                               id &= ~curr_id;
-               }
-               if (i <= index)
-                       break;
+static int v4l_g_modulator(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_modulator *p = arg;
+       int err;
 
-               v4l2_video_std_construct(p, curr_id, descr);
+       err = ops->vidioc_g_modulator(file, fh, p);
+       if (!err)
+               p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
+       return err;
+}
 
-               dbgarg(cmd, "index=%d, id=0x%Lx, name=%s, fps=%d/%d, "
-                               "framelines=%d\n", p->index,
-                               (unsigned long long)p->id, p->name,
-                               p->frameperiod.numerator,
-                               p->frameperiod.denominator,
-                               p->framelines);
+static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_frequency *p = arg;
 
-               ret = 0;
-               break;
-       }
-       case VIDIOC_G_STD:
-       {
-               v4l2_std_id *id = arg;
-
-               /* Calls the specific handler */
-               if (ops->vidioc_g_std)
-                       ret = ops->vidioc_g_std(file, fh, id);
-               else if (vfd->current_norm) {
-                       ret = 0;
-                       *id = vfd->current_norm;
-               }
+       p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+       return ops->vidioc_g_frequency(file, fh, p);
+}
 
-               if (likely(!ret))
-                       dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id);
-               break;
-       }
-       case VIDIOC_S_STD:
-       {
-               v4l2_std_id *id = arg, norm;
+static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_frequency *p = arg;
+       enum v4l2_tuner_type type;
 
-               dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id);
+       type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+       if (p->type != type)
+               return -EINVAL;
+       return ops->vidioc_s_frequency(file, fh, p);
+}
 
-               ret = -EINVAL;
-               norm = (*id) & vfd->tvnorms;
-               if (vfd->tvnorms && !norm)      /* Check if std is supported */
+static int v4l_enumstd(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_standard *p = arg;
+       v4l2_std_id id = vfd->tvnorms, curr_id = 0;
+       unsigned int index = p->index, i, j = 0;
+       const char *descr = "";
+
+       /* Return norm array in a canonical way */
+       for (i = 0; i <= index && id; i++) {
+               /* last std value in the standards array is 0, so this
+                  while always ends there since (id & 0) == 0. */
+               while ((id & standards[j].std) != standards[j].std)
+                       j++;
+               curr_id = standards[j].std;
+               descr = standards[j].descr;
+               j++;
+               if (curr_id == 0)
                        break;
-
-               /* Calls the specific handler */
-               ret = ops->vidioc_s_std(file, fh, &norm);
-
-               /* Updates standard information */
-               if (ret >= 0)
-                       vfd->current_norm = norm;
-               break;
+               if (curr_id != V4L2_STD_PAL &&
+                               curr_id != V4L2_STD_SECAM &&
+                               curr_id != V4L2_STD_NTSC)
+                       id &= ~curr_id;
        }
-       case VIDIOC_QUERYSTD:
-       {
-               v4l2_std_id *p = arg;
-
-               /*
-                * If nothing detected, it should return all supported
-                * Drivers just need to mask the std argument, in order
-                * to remove the standards that don't apply from the mask.
-                * This means that tuners, audio and video decoders can join
-                * their efforts to improve the standards detection
-                */
-               *p = vfd->tvnorms;
-               ret = ops->vidioc_querystd(file, fh, arg);
-               if (!ret)
-                       dbgarg(cmd, "detected std=%08Lx\n",
-                                               (unsigned long long)*p);
-               break;
-       }
-       /* ------ input switching ---------- */
-       /* FIXME: Inputs can be handled inside videodev2 */
-       case VIDIOC_ENUMINPUT:
-       {
-               struct v4l2_input *p = arg;
+       if (i <= index)
+               return -EINVAL;
 
-               /*
-                * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
-                * CAP_STD here based on ioctl handler provided by the
-                * driver. If the driver doesn't support these
-                * for a specific input, it must override these flags.
-                */
-               if (ops->vidioc_s_std)
-                       p->capabilities |= V4L2_IN_CAP_STD;
-               if (ops->vidioc_s_dv_preset)
-                       p->capabilities |= V4L2_IN_CAP_PRESETS;
-               if (ops->vidioc_s_dv_timings)
-                       p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS;
-
-               ret = ops->vidioc_enum_input(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "index=%d, name=%s, type=%d, "
-                               "audioset=%d, "
-                               "tuner=%d, std=%08Lx, status=%d\n",
-                               p->index, p->name, p->type, p->audioset,
-                               p->tuner,
-                               (unsigned long long)p->std,
-                               p->status);
-               break;
-       }
-       case VIDIOC_G_INPUT:
-       {
-               unsigned int *i = arg;
+       v4l2_video_std_construct(p, curr_id, descr);
+       return 0;
+}
 
-               ret = ops->vidioc_g_input(file, fh, i);
-               if (!ret)
-                       dbgarg(cmd, "value=%d\n", *i);
-               break;
-       }
-       case VIDIOC_S_INPUT:
-       {
-               unsigned int *i = arg;
+static int v4l_g_std(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       v4l2_std_id *id = arg;
 
-               dbgarg(cmd, "value=%d\n", *i);
-               ret = ops->vidioc_s_input(file, fh, *i);
-               break;
+       /* Calls the specific handler */
+       if (ops->vidioc_g_std)
+               return ops->vidioc_g_std(file, fh, arg);
+       if (vfd->current_norm) {
+               *id = vfd->current_norm;
+               return 0;
        }
+       return -ENOTTY;
+}
 
-       /* ------ output switching ---------- */
-       case VIDIOC_ENUMOUTPUT:
-       {
-               struct v4l2_output *p = arg;
-
-               /*
-                * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
-                * CAP_STD here based on ioctl handler provided by the
-                * driver. If the driver doesn't support these
-                * for a specific output, it must override these flags.
-                */
-               if (ops->vidioc_s_std)
-                       p->capabilities |= V4L2_OUT_CAP_STD;
-               if (ops->vidioc_s_dv_preset)
-                       p->capabilities |= V4L2_OUT_CAP_PRESETS;
-               if (ops->vidioc_s_dv_timings)
-                       p->capabilities |= V4L2_OUT_CAP_CUSTOM_TIMINGS;
-
-               ret = ops->vidioc_enum_output(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "index=%d, name=%s, type=%d, "
-                               "audioset=0x%x, "
-                               "modulator=%d, std=0x%08Lx\n",
-                               p->index, p->name, p->type, p->audioset,
-                               p->modulator, (unsigned long long)p->std);
-               break;
-       }
-       case VIDIOC_G_OUTPUT:
-       {
-               unsigned int *i = arg;
+static int v4l_s_std(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       v4l2_std_id *id = arg, norm;
+       int ret;
 
-               ret = ops->vidioc_g_output(file, fh, i);
-               if (!ret)
-                       dbgarg(cmd, "value=%d\n", *i);
-               break;
-       }
-       case VIDIOC_S_OUTPUT:
-       {
-               unsigned int *i = arg;
+       norm = (*id) & vfd->tvnorms;
+       if (vfd->tvnorms && !norm)      /* Check if std is supported */
+               return -EINVAL;
 
-               dbgarg(cmd, "value=%d\n", *i);
-               ret = ops->vidioc_s_output(file, fh, *i);
-               break;
-       }
+       /* Calls the specific handler */
+       ret = ops->vidioc_s_std(file, fh, &norm);
 
-       /* --- controls ---------------------------------------------- */
-       case VIDIOC_QUERYCTRL:
-       {
-               struct v4l2_queryctrl *p = arg;
-
-               if (vfh && vfh->ctrl_handler)
-                       ret = v4l2_queryctrl(vfh->ctrl_handler, p);
-               else if (vfd->ctrl_handler)
-                       ret = v4l2_queryctrl(vfd->ctrl_handler, p);
-               else if (ops->vidioc_queryctrl)
-                       ret = ops->vidioc_queryctrl(file, fh, p);
-               else
-                       break;
-               if (!ret)
-                       dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, "
-                                       "step=%d, default=%d, flags=0x%08x\n",
-                                       p->id, p->type, p->name,
-                                       p->minimum, p->maximum,
-                                       p->step, p->default_value, p->flags);
-               else
-                       dbgarg(cmd, "id=0x%x\n", p->id);
-               break;
-       }
-       case VIDIOC_G_CTRL:
-       {
-               struct v4l2_control *p = arg;
-
-               if (vfh && vfh->ctrl_handler)
-                       ret = v4l2_g_ctrl(vfh->ctrl_handler, p);
-               else if (vfd->ctrl_handler)
-                       ret = v4l2_g_ctrl(vfd->ctrl_handler, p);
-               else if (ops->vidioc_g_ctrl)
-                       ret = ops->vidioc_g_ctrl(file, fh, p);
-               else if (ops->vidioc_g_ext_ctrls) {
-                       struct v4l2_ext_controls ctrls;
-                       struct v4l2_ext_control ctrl;
-
-                       ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
-                       ctrls.count = 1;
-                       ctrls.controls = &ctrl;
-                       ctrl.id = p->id;
-                       ctrl.value = p->value;
-                       if (check_ext_ctrls(&ctrls, 1)) {
-                               ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls);
-                               if (ret == 0)
-                                       p->value = ctrl.value;
-                       }
-               } else
-                       break;
-               if (!ret)
-                       dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
-               else
-                       dbgarg(cmd, "id=0x%x\n", p->id);
-               break;
-       }
-       case VIDIOC_S_CTRL:
-       {
-               struct v4l2_control *p = arg;
-               struct v4l2_ext_controls ctrls;
-               struct v4l2_ext_control ctrl;
-
-               if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
-                       !ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
-                       break;
+       /* Updates standard information */
+       if (ret >= 0)
+               vfd->current_norm = norm;
+       return ret;
+}
 
-               dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
+static int v4l_querystd(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       v4l2_std_id *p = arg;
+
+       /*
+        * If nothing detected, it should return all supported
+        * standard.
+        * Drivers just need to mask the std argument, in order
+        * to remove the standards that don't apply from the mask.
+        * This means that tuners, audio and video decoders can join
+        * their efforts to improve the standards detection.
+        */
+       *p = vfd->tvnorms;
+       return ops->vidioc_querystd(file, fh, arg);
+}
 
-               if (vfh && vfh->ctrl_handler) {
-                       ret = v4l2_s_ctrl(vfh, vfh->ctrl_handler, p);
-                       break;
-               }
-               if (vfd->ctrl_handler) {
-                       ret = v4l2_s_ctrl(NULL, vfd->ctrl_handler, p);
-                       break;
-               }
-               if (ops->vidioc_s_ctrl) {
-                       ret = ops->vidioc_s_ctrl(file, fh, p);
-                       break;
-               }
-               if (!ops->vidioc_s_ext_ctrls)
-                       break;
+static int v4l_s_hw_freq_seek(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_hw_freq_seek *p = arg;
+       enum v4l2_tuner_type type;
 
-               ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
-               ctrls.count = 1;
-               ctrls.controls = &ctrl;
-               ctrl.id = p->id;
-               ctrl.value = p->value;
-               if (check_ext_ctrls(&ctrls, 1))
-                       ret = ops->vidioc_s_ext_ctrls(file, fh, &ctrls);
-               else
-                       ret = -EINVAL;
-               break;
-       }
-       case VIDIOC_G_EXT_CTRLS:
-       {
-               struct v4l2_ext_controls *p = arg;
-
-               p->error_idx = p->count;
-               if (vfh && vfh->ctrl_handler)
-                       ret = v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
-               else if (vfd->ctrl_handler)
-                       ret = v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
-               else if (ops->vidioc_g_ext_ctrls)
-                       ret = check_ext_ctrls(p, 0) ?
-                               ops->vidioc_g_ext_ctrls(file, fh, p) :
-                               -EINVAL;
-               else
-                       break;
-               v4l_print_ext_ctrls(cmd, vfd, p, !ret);
-               break;
-       }
-       case VIDIOC_S_EXT_CTRLS:
-       {
-               struct v4l2_ext_controls *p = arg;
+       type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+               V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+       if (p->type != type)
+               return -EINVAL;
+       return ops->vidioc_s_hw_freq_seek(file, fh, p);
+}
 
-               p->error_idx = p->count;
-               if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
-                               !ops->vidioc_s_ext_ctrls)
-                       break;
-               v4l_print_ext_ctrls(cmd, vfd, p, 1);
-               if (vfh && vfh->ctrl_handler)
-                       ret = v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
-               else if (vfd->ctrl_handler)
-                       ret = v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p);
-               else if (check_ext_ctrls(p, 0))
-                       ret = ops->vidioc_s_ext_ctrls(file, fh, p);
-               else
-                       ret = -EINVAL;
-               break;
-       }
-       case VIDIOC_TRY_EXT_CTRLS:
-       {
-               struct v4l2_ext_controls *p = arg;
+static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_requestbuffers *p = arg;
+       int ret = check_fmt(ops, p->type);
 
-               p->error_idx = p->count;
-               if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
-                               !ops->vidioc_try_ext_ctrls)
-                       break;
-               v4l_print_ext_ctrls(cmd, vfd, p, 1);
-               if (vfh && vfh->ctrl_handler)
-                       ret = v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
-               else if (vfd->ctrl_handler)
-                       ret = v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
-               else if (check_ext_ctrls(p, 0))
-                       ret = ops->vidioc_try_ext_ctrls(file, fh, p);
-               else
-                       ret = -EINVAL;
-               break;
-       }
-       case VIDIOC_QUERYMENU:
-       {
-               struct v4l2_querymenu *p = arg;
-
-               if (vfh && vfh->ctrl_handler)
-                       ret = v4l2_querymenu(vfh->ctrl_handler, p);
-               else if (vfd->ctrl_handler)
-                       ret = v4l2_querymenu(vfd->ctrl_handler, p);
-               else if (ops->vidioc_querymenu)
-                       ret = ops->vidioc_querymenu(file, fh, p);
-               else
-                       break;
-               if (!ret)
-                       dbgarg(cmd, "id=0x%x, index=%d, name=%s\n",
-                               p->id, p->index, p->name);
-               else
-                       dbgarg(cmd, "id=0x%x, index=%d\n",
-                               p->id, p->index);
-               break;
-       }
-       /* --- audio ---------------------------------------------- */
-       case VIDIOC_ENUMAUDIO:
-       {
-               struct v4l2_audio *p = arg;
-
-               ret = ops->vidioc_enumaudio(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
-                                       "mode=0x%x\n", p->index, p->name,
-                                       p->capability, p->mode);
-               else
-                       dbgarg(cmd, "index=%d\n", p->index);
-               break;
-       }
-       case VIDIOC_G_AUDIO:
-       {
-               struct v4l2_audio *p = arg;
-
-               ret = ops->vidioc_g_audio(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
-                                       "mode=0x%x\n", p->index,
-                                       p->name, p->capability, p->mode);
-               else
-                       dbgarg(cmd, "index=%d\n", p->index);
-               break;
-       }
-       case VIDIOC_S_AUDIO:
-       {
-               struct v4l2_audio *p = arg;
-
-               dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
-                                       "mode=0x%x\n", p->index, p->name,
-                                       p->capability, p->mode);
-               ret = ops->vidioc_s_audio(file, fh, p);
-               break;
-       }
-       case VIDIOC_ENUMAUDOUT:
-       {
-               struct v4l2_audioout *p = arg;
-
-               dbgarg(cmd, "Enum for index=%d\n", p->index);
-               ret = ops->vidioc_enumaudout(file, fh, p);
-               if (!ret)
-                       dbgarg2("index=%d, name=%s, capability=%d, "
-                                       "mode=%d\n", p->index, p->name,
-                                       p->capability, p->mode);
-               break;
-       }
-       case VIDIOC_G_AUDOUT:
-       {
-               struct v4l2_audioout *p = arg;
-
-               ret = ops->vidioc_g_audout(file, fh, p);
-               if (!ret)
-                       dbgarg2("index=%d, name=%s, capability=%d, "
-                                       "mode=%d\n", p->index, p->name,
-                                       p->capability, p->mode);
-               break;
-       }
-       case VIDIOC_S_AUDOUT:
-       {
-               struct v4l2_audioout *p = arg;
+       if (ret)
+               return ret;
 
-               dbgarg(cmd, "index=%d, name=%s, capability=%d, "
-                                       "mode=%d\n", p->index, p->name,
-                                       p->capability, p->mode);
+       if (p->type < V4L2_BUF_TYPE_PRIVATE)
+               CLEAR_AFTER_FIELD(p, memory);
 
-               ret = ops->vidioc_s_audout(file, fh, p);
-               break;
-       }
-       case VIDIOC_G_MODULATOR:
-       {
-               struct v4l2_modulator *p = arg;
-
-               ret = ops->vidioc_g_modulator(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "index=%d, name=%s, "
-                                       "capability=%d, rangelow=%d,"
-                                       " rangehigh=%d, txsubchans=%d\n",
-                                       p->index, p->name, p->capability,
-                                       p->rangelow, p->rangehigh,
-                                       p->txsubchans);
-               break;
-       }
-       case VIDIOC_S_MODULATOR:
-       {
-               struct v4l2_modulator *p = arg;
-
-               dbgarg(cmd, "index=%d, name=%s, capability=%d, "
-                               "rangelow=%d, rangehigh=%d, txsubchans=%d\n",
-                               p->index, p->name, p->capability, p->rangelow,
-                               p->rangehigh, p->txsubchans);
-                       ret = ops->vidioc_s_modulator(file, fh, p);
-               break;
-       }
-       case VIDIOC_G_CROP:
-       {
-               struct v4l2_crop *p = arg;
+       return ops->vidioc_reqbufs(file, fh, p);
+}
 
-               dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+static int v4l_querybuf(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_buffer *p = arg;
+       int ret = check_fmt(ops, p->type);
 
-               if (ops->vidioc_g_crop) {
-                       ret = ops->vidioc_g_crop(file, fh, p);
-               } else {
-                       /* simulate capture crop using selection api */
-                       struct v4l2_selection s = {
-                               .type = p->type,
-                       };
-
-                       /* crop means compose for output devices */
-                       if (V4L2_TYPE_IS_OUTPUT(p->type))
-                               s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
-                       else
-                               s.target = V4L2_SEL_TGT_CROP_ACTIVE;
-
-                       ret = ops->vidioc_g_selection(file, fh, &s);
-
-                       /* copying results to old structure on success */
-                       if (!ret)
-                               p->c = s.r;
-               }
+       return ret ? ret : ops->vidioc_querybuf(file, fh, p);
+}
 
-               if (!ret)
-                       dbgrect(vfd, "", &p->c);
-               break;
-       }
-       case VIDIOC_S_CROP:
-       {
-               struct v4l2_crop *p = arg;
+static int v4l_qbuf(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_buffer *p = arg;
+       int ret = check_fmt(ops, p->type);
 
-               dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
-               dbgrect(vfd, "", &p->c);
+       return ret ? ret : ops->vidioc_qbuf(file, fh, p);
+}
 
-               if (ops->vidioc_s_crop) {
-                       ret = ops->vidioc_s_crop(file, fh, p);
-               } else {
-                       /* simulate capture crop using selection api */
-                       struct v4l2_selection s = {
-                               .type = p->type,
-                               .r = p->c,
-                       };
-
-                       /* crop means compose for output devices */
-                       if (V4L2_TYPE_IS_OUTPUT(p->type))
-                               s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
-                       else
-                               s.target = V4L2_SEL_TGT_CROP_ACTIVE;
-
-                       ret = ops->vidioc_s_selection(file, fh, &s);
-               }
-               break;
-       }
-       case VIDIOC_G_SELECTION:
-       {
-               struct v4l2_selection *p = arg;
+static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_buffer *p = arg;
+       int ret = check_fmt(ops, p->type);
 
-               dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+       return ret ? ret : ops->vidioc_dqbuf(file, fh, p);
+}
 
-               ret = ops->vidioc_g_selection(file, fh, p);
-               if (!ret)
-                       dbgrect(vfd, "", &p->r);
-               break;
-       }
-       case VIDIOC_S_SELECTION:
-       {
-               struct v4l2_selection *p = arg;
+static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_create_buffers *create = arg;
+       int ret = check_fmt(ops, create->format.type);
 
+       return ret ? ret : ops->vidioc_create_bufs(file, fh, create);
+}
 
-               dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
-               dbgrect(vfd, "", &p->r);
+static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_buffer *b = arg;
+       int ret = check_fmt(ops, b->type);
 
-               ret = ops->vidioc_s_selection(file, fh, p);
-               break;
-       }
-       case VIDIOC_CROPCAP:
-       {
-               struct v4l2_cropcap *p = arg;
-
-               /*FIXME: Should also show v4l2_fract pixelaspect */
-               dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
-               if (ops->vidioc_cropcap) {
-                       ret = ops->vidioc_cropcap(file, fh, p);
-               } else {
-                       struct v4l2_selection s = { .type = p->type };
+       return ret ? ret : ops->vidioc_prepare_buf(file, fh, b);
+}
 
-                       /* obtaining bounds */
-                       if (V4L2_TYPE_IS_OUTPUT(p->type))
-                               s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
-                       else
-                               s.target = V4L2_SEL_TGT_CROP_BOUNDS;
+static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_streamparm *p = arg;
+       v4l2_std_id std;
+       int ret = check_fmt(ops, p->type);
 
-                       ret = ops->vidioc_g_selection(file, fh, &s);
-                       if (ret)
-                               break;
-                       p->bounds = s.r;
+       if (ret)
+               return ret;
+       if (ops->vidioc_g_parm)
+               return ops->vidioc_g_parm(file, fh, p);
+       std = vfd->current_norm;
+       if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+           p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+               return -EINVAL;
+       p->parm.capture.readbuffers = 2;
+       if (ops->vidioc_g_std)
+               ret = ops->vidioc_g_std(file, fh, &std);
+       if (ret == 0)
+               v4l2_video_std_frame_period(std,
+                           &p->parm.capture.timeperframe);
+       return ret;
+}
 
-                       /* obtaining defrect */
-                       if (V4L2_TYPE_IS_OUTPUT(p->type))
-                               s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT;
-                       else
-                               s.target = V4L2_SEL_TGT_CROP_DEFAULT;
+static int v4l_s_parm(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_streamparm *p = arg;
+       int ret = check_fmt(ops, p->type);
 
-                       ret = ops->vidioc_g_selection(file, fh, &s);
-                       if (ret)
-                               break;
-                       p->defrect = s.r;
+       return ret ? ret : ops->vidioc_s_parm(file, fh, p);
+}
 
-                       /* setting trivial pixelaspect */
-                       p->pixelaspect.numerator = 1;
-                       p->pixelaspect.denominator = 1;
-               }
+static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_queryctrl *p = arg;
+       struct v4l2_fh *vfh =
+               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+       if (vfh && vfh->ctrl_handler)
+               return v4l2_queryctrl(vfh->ctrl_handler, p);
+       if (vfd->ctrl_handler)
+               return v4l2_queryctrl(vfd->ctrl_handler, p);
+       if (ops->vidioc_queryctrl)
+               return ops->vidioc_queryctrl(file, fh, p);
+       return -ENOTTY;
+}
 
-               if (!ret) {
-                       dbgrect(vfd, "bounds ", &p->bounds);
-                       dbgrect(vfd, "defrect ", &p->defrect);
-               }
-               break;
-       }
-       case VIDIOC_G_JPEGCOMP:
-       {
-               struct v4l2_jpegcompression *p = arg;
-
-               ret = ops->vidioc_g_jpegcomp(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "quality=%d, APPn=%d, "
-                                       "APP_len=%d, COM_len=%d, "
-                                       "jpeg_markers=%d\n",
-                                       p->quality, p->APPn, p->APP_len,
-                                       p->COM_len, p->jpeg_markers);
-               break;
-       }
-       case VIDIOC_S_JPEGCOMP:
-       {
-               struct v4l2_jpegcompression *p = arg;
-
-               dbgarg(cmd, "quality=%d, APPn=%d, APP_len=%d, "
-                                       "COM_len=%d, jpeg_markers=%d\n",
-                                       p->quality, p->APPn, p->APP_len,
-                                       p->COM_len, p->jpeg_markers);
-               ret = ops->vidioc_s_jpegcomp(file, fh, p);
-               break;
-       }
-       case VIDIOC_G_ENC_INDEX:
-       {
-               struct v4l2_enc_idx *p = arg;
-
-               ret = ops->vidioc_g_enc_index(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "entries=%d, entries_cap=%d\n",
-                                       p->entries, p->entries_cap);
-               break;
-       }
-       case VIDIOC_ENCODER_CMD:
-       {
-               struct v4l2_encoder_cmd *p = arg;
+static int v4l_querymenu(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_querymenu *p = arg;
+       struct v4l2_fh *vfh =
+               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+       if (vfh && vfh->ctrl_handler)
+               return v4l2_querymenu(vfh->ctrl_handler, p);
+       if (vfd->ctrl_handler)
+               return v4l2_querymenu(vfd->ctrl_handler, p);
+       if (ops->vidioc_querymenu)
+               return ops->vidioc_querymenu(file, fh, p);
+       return -ENOTTY;
+}
 
-               ret = ops->vidioc_encoder_cmd(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
-               break;
+static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_control *p = arg;
+       struct v4l2_fh *vfh =
+               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+       struct v4l2_ext_controls ctrls;
+       struct v4l2_ext_control ctrl;
+
+       if (vfh && vfh->ctrl_handler)
+               return v4l2_g_ctrl(vfh->ctrl_handler, p);
+       if (vfd->ctrl_handler)
+               return v4l2_g_ctrl(vfd->ctrl_handler, p);
+       if (ops->vidioc_g_ctrl)
+               return ops->vidioc_g_ctrl(file, fh, p);
+       if (ops->vidioc_g_ext_ctrls == NULL)
+               return -ENOTTY;
+
+       ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+       ctrls.count = 1;
+       ctrls.controls = &ctrl;
+       ctrl.id = p->id;
+       ctrl.value = p->value;
+       if (check_ext_ctrls(&ctrls, 1)) {
+               int ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls);
+
+               if (ret == 0)
+                       p->value = ctrl.value;
+               return ret;
        }
-       case VIDIOC_TRY_ENCODER_CMD:
-       {
-               struct v4l2_encoder_cmd *p = arg;
+       return -EINVAL;
+}
 
-               ret = ops->vidioc_try_encoder_cmd(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
-               break;
-       }
-       case VIDIOC_DECODER_CMD:
-       {
-               struct v4l2_decoder_cmd *p = arg;
+static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_control *p = arg;
+       struct v4l2_fh *vfh =
+               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+       struct v4l2_ext_controls ctrls;
+       struct v4l2_ext_control ctrl;
+
+       if (vfh && vfh->ctrl_handler)
+               return v4l2_s_ctrl(vfh, vfh->ctrl_handler, p);
+       if (vfd->ctrl_handler)
+               return v4l2_s_ctrl(NULL, vfd->ctrl_handler, p);
+       if (ops->vidioc_s_ctrl)
+               return ops->vidioc_s_ctrl(file, fh, p);
+       if (ops->vidioc_s_ext_ctrls == NULL)
+               return -ENOTTY;
+
+       ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+       ctrls.count = 1;
+       ctrls.controls = &ctrl;
+       ctrl.id = p->id;
+       ctrl.value = p->value;
+       if (check_ext_ctrls(&ctrls, 1))
+               return ops->vidioc_s_ext_ctrls(file, fh, &ctrls);
+       return -EINVAL;
+}
 
-               ret = ops->vidioc_decoder_cmd(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
-               break;
-       }
-       case VIDIOC_TRY_DECODER_CMD:
-       {
-               struct v4l2_decoder_cmd *p = arg;
+static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_ext_controls *p = arg;
+       struct v4l2_fh *vfh =
+               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+       p->error_idx = p->count;
+       if (vfh && vfh->ctrl_handler)
+               return v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
+       if (vfd->ctrl_handler)
+               return v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
+       if (ops->vidioc_g_ext_ctrls == NULL)
+               return -ENOTTY;
+       return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) :
+                                       -EINVAL;
+}
 
-               ret = ops->vidioc_try_decoder_cmd(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
-               break;
-       }
-       case VIDIOC_G_PARM:
-       {
-               struct v4l2_streamparm *p = arg;
+static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_ext_controls *p = arg;
+       struct v4l2_fh *vfh =
+               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+       p->error_idx = p->count;
+       if (vfh && vfh->ctrl_handler)
+               return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
+       if (vfd->ctrl_handler)
+               return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p);
+       if (ops->vidioc_s_ext_ctrls == NULL)
+               return -ENOTTY;
+       return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) :
+                                       -EINVAL;
+}
 
-               if (ops->vidioc_g_parm) {
-                       ret = check_fmt(ops, p->type);
-                       if (ret)
-                               break;
-                       ret = ops->vidioc_g_parm(file, fh, p);
-               } else {
-                       v4l2_std_id std = vfd->current_norm;
+static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_ext_controls *p = arg;
+       struct v4l2_fh *vfh =
+               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+       p->error_idx = p->count;
+       if (vfh && vfh->ctrl_handler)
+               return v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
+       if (vfd->ctrl_handler)
+               return v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
+       if (ops->vidioc_try_ext_ctrls == NULL)
+               return -ENOTTY;
+       return check_ext_ctrls(p, 0) ? ops->vidioc_try_ext_ctrls(file, fh, p) :
+                                       -EINVAL;
+}
 
-                       ret = -EINVAL;
-                       if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                               break;
+static int v4l_g_crop(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_crop *p = arg;
+       struct v4l2_selection s = {
+               .type = p->type,
+       };
+       int ret;
+
+       if (ops->vidioc_g_crop)
+               return ops->vidioc_g_crop(file, fh, p);
+       /* simulate capture crop using selection api */
+
+       /* crop means compose for output devices */
+       if (V4L2_TYPE_IS_OUTPUT(p->type))
+               s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
+       else
+               s.target = V4L2_SEL_TGT_CROP_ACTIVE;
+
+       ret = ops->vidioc_g_selection(file, fh, &s);
+
+       /* copying results to old structure on success */
+       if (!ret)
+               p->c = s.r;
+       return ret;
+}
 
-                       ret = 0;
-                       p->parm.capture.readbuffers = 2;
-                       if (ops->vidioc_g_std)
-                               ret = ops->vidioc_g_std(file, fh, &std);
-                       if (ret == 0)
-                               v4l2_video_std_frame_period(std,
-                                                   &p->parm.capture.timeperframe);
-               }
+static int v4l_s_crop(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_crop *p = arg;
+       struct v4l2_selection s = {
+               .type = p->type,
+               .r = p->c,
+       };
+
+       if (ops->vidioc_s_crop)
+               return ops->vidioc_s_crop(file, fh, p);
+       /* simulate capture crop using selection api */
+
+       /* crop means compose for output devices */
+       if (V4L2_TYPE_IS_OUTPUT(p->type))
+               s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
+       else
+               s.target = V4L2_SEL_TGT_CROP_ACTIVE;
+
+       return ops->vidioc_s_selection(file, fh, &s);
+}
 
-               dbgarg(cmd, "type=%d\n", p->type);
-               break;
-       }
-       case VIDIOC_S_PARM:
-       {
-               struct v4l2_streamparm *p = arg;
+static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_cropcap *p = arg;
+       struct v4l2_selection s = { .type = p->type };
+       int ret;
 
-               ret = check_fmt(ops, p->type);
-               if (ret)
-                       break;
+       if (ops->vidioc_cropcap)
+               return ops->vidioc_cropcap(file, fh, p);
 
-               dbgarg(cmd, "type=%d\n", p->type);
-               ret = ops->vidioc_s_parm(file, fh, p);
-               break;
-       }
-       case VIDIOC_G_TUNER:
-       {
-               struct v4l2_tuner *p = arg;
+       /* obtaining bounds */
+       if (V4L2_TYPE_IS_OUTPUT(p->type))
+               s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
+       else
+               s.target = V4L2_SEL_TGT_CROP_BOUNDS;
 
-               p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
-                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-               ret = ops->vidioc_g_tuner(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "index=%d, name=%s, type=%d, "
-                                       "capability=0x%x, rangelow=%d, "
-                                       "rangehigh=%d, signal=%d, afc=%d, "
-                                       "rxsubchans=0x%x, audmode=%d\n",
-                                       p->index, p->name, p->type,
-                                       p->capability, p->rangelow,
-                                       p->rangehigh, p->signal, p->afc,
-                                       p->rxsubchans, p->audmode);
-               break;
-       }
-       case VIDIOC_S_TUNER:
-       {
-               struct v4l2_tuner *p = arg;
+       ret = ops->vidioc_g_selection(file, fh, &s);
+       if (ret)
+               return ret;
+       p->bounds = s.r;
 
-               p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
-                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-               dbgarg(cmd, "index=%d, name=%s, type=%d, "
-                               "capability=0x%x, rangelow=%d, "
-                               "rangehigh=%d, signal=%d, afc=%d, "
-                               "rxsubchans=0x%x, audmode=%d\n",
-                               p->index, p->name, p->type,
-                               p->capability, p->rangelow,
-                               p->rangehigh, p->signal, p->afc,
-                               p->rxsubchans, p->audmode);
-               ret = ops->vidioc_s_tuner(file, fh, p);
-               break;
-       }
-       case VIDIOC_G_FREQUENCY:
-       {
-               struct v4l2_frequency *p = arg;
+       /* obtaining defrect */
+       if (V4L2_TYPE_IS_OUTPUT(p->type))
+               s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT;
+       else
+               s.target = V4L2_SEL_TGT_CROP_DEFAULT;
 
-               p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
-                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-               ret = ops->vidioc_g_frequency(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n",
-                                       p->tuner, p->type, p->frequency);
-               break;
-       }
-       case VIDIOC_S_FREQUENCY:
-       {
-               struct v4l2_frequency *p = arg;
-               enum v4l2_tuner_type type;
+       ret = ops->vidioc_g_selection(file, fh, &s);
+       if (ret)
+               return ret;
+       p->defrect = s.r;
 
-               type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
-                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-               dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n",
-                               p->tuner, p->type, p->frequency);
-               if (p->type != type)
-                       ret = -EINVAL;
-               else
-                       ret = ops->vidioc_s_frequency(file, fh, p);
-               break;
-       }
-       case VIDIOC_G_SLICED_VBI_CAP:
-       {
-               struct v4l2_sliced_vbi_cap *p = arg;
+       /* setting trivial pixelaspect */
+       p->pixelaspect.numerator = 1;
+       p->pixelaspect.denominator = 1;
+       return 0;
+}
 
-               /* Clear up to type, everything after type is zerod already */
-               memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type));
+static int v4l_log_status(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       int ret;
+
+       if (vfd->v4l2_dev)
+               pr_info("%s: =================  START STATUS  =================\n",
+                       vfd->v4l2_dev->name);
+       ret = ops->vidioc_log_status(file, fh);
+       if (vfd->v4l2_dev)
+               pr_info("%s: ==================  END STATUS  ==================\n",
+                       vfd->v4l2_dev->name);
+       return ret;
+}
 
-               dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
-               ret = ops->vidioc_g_sliced_vbi_cap(file, fh, p);
-               if (!ret)
-                       dbgarg2("service_set=%d\n", p->service_set);
-               break;
-       }
-       case VIDIOC_LOG_STATUS:
-       {
-               if (vfd->v4l2_dev)
-                       pr_info("%s: =================  START STATUS  =================\n",
-                               vfd->v4l2_dev->name);
-               ret = ops->vidioc_log_status(file, fh);
-               if (vfd->v4l2_dev)
-                       pr_info("%s: ==================  END STATUS  ==================\n",
-                               vfd->v4l2_dev->name);
-               break;
-       }
-       case VIDIOC_DBG_G_REGISTER:
-       {
+static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
 #ifdef CONFIG_VIDEO_ADV_DEBUG
-               struct v4l2_dbg_register *p = arg;
+       struct v4l2_dbg_register *p = arg;
 
-               if (!capable(CAP_SYS_ADMIN))
-                       ret = -EPERM;
-               else
-                       ret = ops->vidioc_g_register(file, fh, p);
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       return ops->vidioc_g_register(file, fh, p);
+#else
+       return -ENOTTY;
 #endif
-               break;
-       }
-       case VIDIOC_DBG_S_REGISTER:
-       {
+}
+
+static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
 #ifdef CONFIG_VIDEO_ADV_DEBUG
-               struct v4l2_dbg_register *p = arg;
+       struct v4l2_dbg_register *p = arg;
 
-               if (!capable(CAP_SYS_ADMIN))
-                       ret = -EPERM;
-               else
-                       ret = ops->vidioc_s_register(file, fh, p);
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       return ops->vidioc_s_register(file, fh, p);
+#else
+       return -ENOTTY;
 #endif
-               break;
-       }
-       case VIDIOC_DBG_G_CHIP_IDENT:
-       {
-               struct v4l2_dbg_chip_ident *p = arg;
-
-               p->ident = V4L2_IDENT_NONE;
-               p->revision = 0;
-               ret = ops->vidioc_g_chip_ident(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision);
-               break;
-       }
-       case VIDIOC_S_HW_FREQ_SEEK:
-       {
-               struct v4l2_hw_freq_seek *p = arg;
-               enum v4l2_tuner_type type;
+}
 
-               type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
-                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-               dbgarg(cmd,
-                       "tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u\n",
-                       p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing);
-               if (p->type != type)
-                       ret = -EINVAL;
-               else
-                       ret = ops->vidioc_s_hw_freq_seek(file, fh, p);
-               break;
-       }
-       case VIDIOC_ENUM_FRAMESIZES:
-       {
-               struct v4l2_frmsizeenum *p = arg;
+static int v4l_dbg_g_chip_ident(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_dbg_chip_ident *p = arg;
 
-               ret = ops->vidioc_enum_framesizes(file, fh, p);
-               dbgarg(cmd,
-                       "index=%d, pixelformat=%c%c%c%c, type=%d ",
-                       p->index,
-                       (p->pixel_format & 0xff),
-                       (p->pixel_format >>  8) & 0xff,
-                       (p->pixel_format >> 16) & 0xff,
-                       (p->pixel_format >> 24) & 0xff,
-                       p->type);
-               switch (p->type) {
-               case V4L2_FRMSIZE_TYPE_DISCRETE:
-                       dbgarg3("width = %d, height=%d\n",
-                               p->discrete.width, p->discrete.height);
-                       break;
-               case V4L2_FRMSIZE_TYPE_STEPWISE:
-                       dbgarg3("min %dx%d, max %dx%d, step %dx%d\n",
-                               p->stepwise.min_width,  p->stepwise.min_height,
-                               p->stepwise.step_width, p->stepwise.step_height,
-                               p->stepwise.max_width,  p->stepwise.max_height);
-                       break;
-               case V4L2_FRMSIZE_TYPE_CONTINUOUS:
-                       dbgarg3("continuous\n");
-                       break;
-               default:
-                       dbgarg3("- Unknown type!\n");
-               }
+       p->ident = V4L2_IDENT_NONE;
+       p->revision = 0;
+       return ops->vidioc_g_chip_ident(file, fh, p);
+}
 
-               break;
-       }
-       case VIDIOC_ENUM_FRAMEINTERVALS:
-       {
-               struct v4l2_frmivalenum *p = arg;
-
-               ret = ops->vidioc_enum_frameintervals(file, fh, p);
-               dbgarg(cmd,
-                       "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ",
-                       p->index, p->pixel_format,
-                       p->width, p->height, p->type);
-               switch (p->type) {
-               case V4L2_FRMIVAL_TYPE_DISCRETE:
-                       dbgarg2("fps=%d/%d\n",
-                               p->discrete.numerator,
-                               p->discrete.denominator);
-                       break;
-               case V4L2_FRMIVAL_TYPE_STEPWISE:
-                       dbgarg2("min=%d/%d, max=%d/%d, step=%d/%d\n",
-                               p->stepwise.min.numerator,
-                               p->stepwise.min.denominator,
-                               p->stepwise.max.numerator,
-                               p->stepwise.max.denominator,
-                               p->stepwise.step.numerator,
-                               p->stepwise.step.denominator);
-                       break;
-               case V4L2_FRMIVAL_TYPE_CONTINUOUS:
-                       dbgarg2("continuous\n");
-                       break;
-               default:
-                       dbgarg2("- Unknown type!\n");
-               }
-               break;
-       }
-       case VIDIOC_ENUM_DV_PRESETS:
-       {
-               struct v4l2_dv_enum_preset *p = arg;
-
-               ret = ops->vidioc_enum_dv_presets(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd,
-                               "index=%d, preset=%d, name=%s, width=%d,"
-                               " height=%d ",
-                               p->index, p->preset, p->name, p->width,
-                               p->height);
-               break;
-       }
-       case VIDIOC_S_DV_PRESET:
-       {
-               struct v4l2_dv_preset *p = arg;
+static int v4l_dqevent(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK);
+}
 
-               dbgarg(cmd, "preset=%d\n", p->preset);
-               ret = ops->vidioc_s_dv_preset(file, fh, p);
-               break;
-       }
-       case VIDIOC_G_DV_PRESET:
-       {
-               struct v4l2_dv_preset *p = arg;
+static int v4l_subscribe_event(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       return ops->vidioc_subscribe_event(fh, arg);
+}
 
-               ret = ops->vidioc_g_dv_preset(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "preset=%d\n", p->preset);
-               break;
-       }
-       case VIDIOC_QUERY_DV_PRESET:
-       {
-               struct v4l2_dv_preset *p = arg;
+static int v4l_unsubscribe_event(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       return ops->vidioc_unsubscribe_event(fh, arg);
+}
 
-               ret = ops->vidioc_query_dv_preset(file, fh, p);
-               if (!ret)
-                       dbgarg(cmd, "preset=%d\n", p->preset);
-               break;
-       }
-       case VIDIOC_S_DV_TIMINGS:
-       {
-               struct v4l2_dv_timings *p = arg;
-
-               dbgtimings(vfd, p);
-               switch (p->type) {
-               case V4L2_DV_BT_656_1120:
-                       ret = ops->vidioc_s_dv_timings(file, fh, p);
-                       break;
-               default:
-                       ret = -EINVAL;
-                       break;
-               }
-               break;
-       }
-       case VIDIOC_G_DV_TIMINGS:
-       {
-               struct v4l2_dv_timings *p = arg;
+static int v4l_g_sliced_vbi_cap(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_sliced_vbi_cap *p = arg;
 
-               ret = ops->vidioc_g_dv_timings(file, fh, p);
-               if (!ret)
-                       dbgtimings(vfd, p);
-               break;
-       }
-       case VIDIOC_ENUM_DV_TIMINGS:
-       {
-               struct v4l2_enum_dv_timings *p = arg;
+       /* Clear up to type, everything after type is zeroed already */
+       memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type));
 
-               if (!ops->vidioc_enum_dv_timings)
-                       break;
+       return ops->vidioc_g_sliced_vbi_cap(file, fh, p);
+}
 
-               ret = ops->vidioc_enum_dv_timings(file, fh, p);
-               if (!ret) {
-                       dbgarg(cmd, "index=%d: ", p->index);
-                       dbgtimings(vfd, &p->timings);
-               }
-               break;
-       }
-       case VIDIOC_QUERY_DV_TIMINGS:
-       {
-               struct v4l2_dv_timings *p = arg;
+static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_frequency_band *p = arg;
+       enum v4l2_tuner_type type;
+       int err;
 
-               if (!ops->vidioc_query_dv_timings)
-                       break;
+       type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
 
-               ret = ops->vidioc_query_dv_timings(file, fh, p);
-               if (!ret)
-                       dbgtimings(vfd, p);
-               break;
+       if (type != p->type)
+               return -EINVAL;
+       if (ops->vidioc_enum_freq_bands)
+               return ops->vidioc_enum_freq_bands(file, fh, p);
+       if (ops->vidioc_g_tuner) {
+               struct v4l2_tuner t = {
+                       .index = p->tuner,
+                       .type = type,
+               };
+
+               err = ops->vidioc_g_tuner(file, fh, &t);
+               if (err)
+                       return err;
+               p->capability = t.capability | V4L2_TUNER_CAP_FREQ_BANDS;
+               p->rangelow = t.rangelow;
+               p->rangehigh = t.rangehigh;
+               p->modulation = (type == V4L2_TUNER_RADIO) ?
+                       V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
+               return 0;
        }
-       case VIDIOC_DV_TIMINGS_CAP:
-       {
-               struct v4l2_dv_timings_cap *p = arg;
+       if (ops->vidioc_g_modulator) {
+               struct v4l2_modulator m = {
+                       .index = p->tuner,
+               };
+
+               if (type != V4L2_TUNER_RADIO)
+                       return -EINVAL;
+               err = ops->vidioc_g_modulator(file, fh, &m);
+               if (err)
+                       return err;
+               p->capability = m.capability | V4L2_TUNER_CAP_FREQ_BANDS;
+               p->rangelow = m.rangelow;
+               p->rangehigh = m.rangehigh;
+               p->modulation = (type == V4L2_TUNER_RADIO) ?
+                       V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
+               return 0;
+       }
+       return -ENOTTY;
+}
 
-               if (!ops->vidioc_dv_timings_cap)
-                       break;
+struct v4l2_ioctl_info {
+       unsigned int ioctl;
+       u32 flags;
+       const char * const name;
+       union {
+               u32 offset;
+               int (*func)(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *p);
+       } u;
+       void (*debug)(const void *arg, bool write_only);
+};
 
-               ret = ops->vidioc_dv_timings_cap(file, fh, p);
-               if (ret)
-                       break;
-               switch (p->type) {
-               case V4L2_DV_BT_656_1120:
-                       dbgarg(cmd,
-                              "type=%d, width=%u-%u, height=%u-%u, "
-                              "pixelclock=%llu-%llu, standards=%x, capabilities=%x ",
-                              p->type,
-                              p->bt.min_width, p->bt.max_width,
-                              p->bt.min_height, p->bt.max_height,
-                              p->bt.min_pixelclock, p->bt.max_pixelclock,
-                              p->bt.standards, p->bt.capabilities);
-                       break;
-               default:
-                       dbgarg(cmd, "unknown type ");
-                       break;
-               }
-               break;
+/* This control needs a priority check */
+#define INFO_FL_PRIO   (1 << 0)
+/* This control can be valid if the filehandle passes a control handler. */
+#define INFO_FL_CTRL   (1 << 1)
+/* This is a standard ioctl, no need for special code */
+#define INFO_FL_STD    (1 << 2)
+/* This is ioctl has its own function */
+#define INFO_FL_FUNC   (1 << 3)
+/* Queuing ioctl */
+#define INFO_FL_QUEUE  (1 << 4)
+/* Zero struct from after the field to the end */
+#define INFO_FL_CLEAR(v4l2_struct, field)                      \
+       ((offsetof(struct v4l2_struct, field) +                 \
+         sizeof(((struct v4l2_struct *)0)->field)) << 16)
+#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16)
+
+#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags)                        \
+       [_IOC_NR(_ioctl)] = {                                           \
+               .ioctl = _ioctl,                                        \
+               .flags = _flags | INFO_FL_STD,                          \
+               .name = #_ioctl,                                        \
+               .u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc),   \
+               .debug = _debug,                                        \
+       }
+
+#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags)                  \
+       [_IOC_NR(_ioctl)] = {                                           \
+               .ioctl = _ioctl,                                        \
+               .flags = _flags | INFO_FL_FUNC,                         \
+               .name = #_ioctl,                                        \
+               .u.func = _func,                                        \
+               .debug = _debug,                                        \
        }
-       case VIDIOC_DQEVENT:
-       {
-               struct v4l2_event *ev = arg;
 
-               ret = v4l2_event_dequeue(fh, ev, file->f_flags & O_NONBLOCK);
-               if (ret < 0) {
-                       dbgarg(cmd, "no pending events?");
-                       break;
-               }
-               dbgarg(cmd,
-                      "pending=%d, type=0x%8.8x, sequence=%d, "
-                      "timestamp=%lu.%9.9lu ",
-                      ev->pending, ev->type, ev->sequence,
-                      ev->timestamp.tv_sec, ev->timestamp.tv_nsec);
-               break;
-       }
-       case VIDIOC_SUBSCRIBE_EVENT:
-       {
-               struct v4l2_event_subscription *sub = arg;
+static struct v4l2_ioctl_info v4l2_ioctls[] = {
+       IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
+       IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
+       IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)),
+       IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
+       IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
+       IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0),
+       IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_OVERLAY, vidioc_overlay, v4l_print_u32, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
+       IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
+       IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
+       IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
+       IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
+       IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_G_STD, v4l_g_std, v4l_print_std, 0),
+       IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)),
+       IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)),
+       IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)),
+       IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL),
+       IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)),
+       IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0),
+       IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)),
+       IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)),
+       IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0),
+       IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0),
+       IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)),
+       IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0),
+       IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)),
+       IOCTL_INFO_STD(VIDIOC_S_MODULATOR, vidioc_s_modulator, v4l_print_modulator, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)),
+       IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)),
+       IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)),
+       IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, 0),
+       IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0),
+       IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0),
+       IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0),
+       IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)),
+       IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)),
+       IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0),
+       IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)),
+       IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0),
+       IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
+       IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL),
+       IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
+       IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)),
+       IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)),
+       IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0),
+       IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
+       IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
+       IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0),
+       IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0),
+       IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0),
+       IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_IDENT, v4l_dbg_g_chip_ident, v4l_print_dbg_chip_ident, 0),
+       IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets, v4l_print_dv_enum_presets, 0),
+       IOCTL_INFO_STD(VIDIOC_S_DV_PRESET, vidioc_s_dv_preset, v4l_print_dv_preset, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_G_DV_PRESET, vidioc_g_dv_preset, v4l_print_dv_preset, 0),
+       IOCTL_INFO_STD(VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset, v4l_print_dv_preset, 0),
+       IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0),
+       IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0),
+       IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0),
+       IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0),
+       IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
+       IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
+       IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0),
+       IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0),
+       IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)),
+       IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
+};
+#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
 
-               ret = ops->vidioc_subscribe_event(fh, sub);
-               if (ret < 0) {
-                       dbgarg(cmd, "failed, ret=%ld", ret);
-                       break;
-               }
-               dbgarg(cmd, "type=0x%8.8x", sub->type);
-               break;
-       }
-       case VIDIOC_UNSUBSCRIBE_EVENT:
-       {
-               struct v4l2_event_subscription *sub = arg;
+bool v4l2_is_known_ioctl(unsigned int cmd)
+{
+       if (_IOC_NR(cmd) >= V4L2_IOCTLS)
+               return false;
+       return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd;
+}
+
+struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd)
+{
+       if (_IOC_NR(cmd) >= V4L2_IOCTLS)
+               return vdev->lock;
+       if (test_bit(_IOC_NR(cmd), vdev->disable_locking))
+               return NULL;
+       if (vdev->queue && vdev->queue->lock &&
+                       (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE))
+               return vdev->queue->lock;
+       return vdev->lock;
+}
+
+/* Common ioctl debug function. This function can be used by
+   external ioctl messages as well as internal V4L ioctl */
+void v4l_printk_ioctl(const char *prefix, unsigned int cmd)
+{
+       const char *dir, *type;
 
-               ret = ops->vidioc_unsubscribe_event(fh, sub);
-               if (ret < 0) {
-                       dbgarg(cmd, "failed, ret=%ld", ret);
+       if (prefix)
+               printk(KERN_DEBUG "%s: ", prefix);
+
+       switch (_IOC_TYPE(cmd)) {
+       case 'd':
+               type = "v4l2_int";
+               break;
+       case 'V':
+               if (_IOC_NR(cmd) >= V4L2_IOCTLS) {
+                       type = "v4l2";
                        break;
                }
-               dbgarg(cmd, "type=0x%8.8x", sub->type);
+               pr_cont("%s", v4l2_ioctls[_IOC_NR(cmd)].name);
+               return;
+       default:
+               type = "unknown";
                break;
        }
-       case VIDIOC_CREATE_BUFS:
-       {
-               struct v4l2_create_buffers *create = arg;
 
-               ret = check_fmt(ops, create->format.type);
-               if (ret)
-                       break;
+       switch (_IOC_DIR(cmd)) {
+       case _IOC_NONE:              dir = "--"; break;
+       case _IOC_READ:              dir = "r-"; break;
+       case _IOC_WRITE:             dir = "-w"; break;
+       case _IOC_READ | _IOC_WRITE: dir = "rw"; break;
+       default:                     dir = "*ERR*"; break;
+       }
+       pr_cont("%s ioctl '%c', dir=%s, #%d (0x%08x)",
+               type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd);
+}
+EXPORT_SYMBOL(v4l_printk_ioctl);
 
-               ret = ops->vidioc_create_bufs(file, fh, create);
+static long __video_do_ioctl(struct file *file,
+               unsigned int cmd, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
+       bool write_only = false;
+       struct v4l2_ioctl_info default_info;
+       const struct v4l2_ioctl_info *info;
+       void *fh = file->private_data;
+       struct v4l2_fh *vfh = NULL;
+       int use_fh_prio = 0;
+       int debug = vfd->debug;
+       long ret = -ENOTTY;
 
-               dbgarg(cmd, "count=%d @ %d\n", create->count, create->index);
-               break;
+       if (ops == NULL) {
+               pr_warn("%s: has no ioctl_ops.\n",
+                               video_device_node_name(vfd));
+               return ret;
        }
-       case VIDIOC_PREPARE_BUF:
-       {
-               struct v4l2_buffer *b = arg;
 
-               ret = check_fmt(ops, b->type);
-               if (ret)
-                       break;
+       if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
+               vfh = file->private_data;
+               use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+       }
 
-               ret = ops->vidioc_prepare_buf(file, fh, b);
+       if (v4l2_is_known_ioctl(cmd)) {
+               info = &v4l2_ioctls[_IOC_NR(cmd)];
 
-               dbgarg(cmd, "index=%d", b->index);
-               break;
-       }
-       default:
-               if (!ops->vidioc_default)
-                       break;
-               ret = ops->vidioc_default(file, fh, use_fh_prio ?
-                               v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
-                               cmd, arg);
-               break;
-       } /* switch */
+               if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
+                   !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
+                       goto done;
 
-       if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {
-               if (ret < 0) {
-                       v4l_print_ioctl(vfd->name, cmd);
-                       printk(KERN_CONT " error %ld\n", ret);
+               if (use_fh_prio && (info->flags & INFO_FL_PRIO)) {
+                       ret = v4l2_prio_check(vfd->prio, vfh->prio);
+                       if (ret)
+                               goto done;
+               }
+       } else {
+               default_info.ioctl = cmd;
+               default_info.flags = 0;
+               default_info.debug = v4l_print_default;
+               info = &default_info;
+       }
+
+       write_only = _IOC_DIR(cmd) == _IOC_WRITE;
+       if (write_only && debug > V4L2_DEBUG_IOCTL) {
+               v4l_printk_ioctl(video_device_node_name(vfd), cmd);
+               pr_cont(": ");
+               info->debug(arg, write_only);
+       }
+       if (info->flags & INFO_FL_STD) {
+               typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
+               const void *p = vfd->ioctl_ops;
+               const vidioc_op *vidioc = p + info->u.offset;
+
+               ret = (*vidioc)(file, fh, arg);
+       } else if (info->flags & INFO_FL_FUNC) {
+               ret = info->u.func(ops, file, fh, arg);
+       } else if (!ops->vidioc_default) {
+               ret = -ENOTTY;
+       } else {
+               ret = ops->vidioc_default(file, fh,
+                       use_fh_prio ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
+                       cmd, arg);
+       }
+
+done:
+       if (debug) {
+               if (write_only && debug > V4L2_DEBUG_IOCTL) {
+                       if (ret < 0)
+                               printk(KERN_DEBUG "%s: error %ld\n",
+                                       video_device_node_name(vfd), ret);
+                       return ret;
+               }
+               v4l_printk_ioctl(video_device_node_name(vfd), cmd);
+               if (ret < 0)
+                       pr_cont(": error %ld\n", ret);
+               else if (debug == V4L2_DEBUG_IOCTL)
+                       pr_cont("\n");
+               else if (_IOC_DIR(cmd) == _IOC_NONE)
+                       info->debug(arg, write_only);
+               else {
+                       pr_cont(": ");
+                       info->debug(arg, write_only);
                }
        }
 
        return ret;
 }
 
-/* In some cases, only a few fields are used as input, i.e. when the app sets
- * "index" and then the driver fills in the rest of the structure for the thing
- * with that index.  We only need to copy up the first non-input field.  */
-static unsigned long cmd_input_size(unsigned int cmd)
-{
-       /* Size of structure up to and including 'field' */
-#define CMDINSIZE(cmd, type, field)                            \
-       case VIDIOC_##cmd:                                      \
-               return offsetof(struct v4l2_##type, field) +    \
-                       sizeof(((struct v4l2_##type *)0)->field);
-
-       switch (cmd) {
-               CMDINSIZE(ENUM_FMT,             fmtdesc,        type);
-               CMDINSIZE(G_FMT,                format,         type);
-               CMDINSIZE(QUERYBUF,             buffer,         length);
-               CMDINSIZE(G_PARM,               streamparm,     type);
-               CMDINSIZE(ENUMSTD,              standard,       index);
-               CMDINSIZE(ENUMINPUT,            input,          index);
-               CMDINSIZE(G_CTRL,               control,        id);
-               CMDINSIZE(G_TUNER,              tuner,          index);
-               CMDINSIZE(QUERYCTRL,            queryctrl,      id);
-               CMDINSIZE(QUERYMENU,            querymenu,      index);
-               CMDINSIZE(ENUMOUTPUT,           output,         index);
-               CMDINSIZE(G_MODULATOR,          modulator,      index);
-               CMDINSIZE(G_FREQUENCY,          frequency,      tuner);
-               CMDINSIZE(CROPCAP,              cropcap,        type);
-               CMDINSIZE(G_CROP,               crop,           type);
-               CMDINSIZE(ENUMAUDIO,            audio,          index);
-               CMDINSIZE(ENUMAUDOUT,           audioout,       index);
-               CMDINSIZE(ENCODER_CMD,          encoder_cmd,    flags);
-               CMDINSIZE(TRY_ENCODER_CMD,      encoder_cmd,    flags);
-               CMDINSIZE(G_SLICED_VBI_CAP,     sliced_vbi_cap, type);
-               CMDINSIZE(ENUM_FRAMESIZES,      frmsizeenum,    pixel_format);
-               CMDINSIZE(ENUM_FRAMEINTERVALS,  frmivalenum,    height);
-       default:
-               return _IOC_SIZE(cmd);
-       }
-}
-
 static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
                            void * __user *user_ptr, void ***kernel_ptr)
 {
@@ -2219,7 +2235,20 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
 
                err = -EFAULT;
                if (_IOC_DIR(cmd) & _IOC_WRITE) {
-                       unsigned long n = cmd_input_size(cmd);
+                       unsigned int n = _IOC_SIZE(cmd);
+
+                       /*
+                        * In some cases, only a few fields are used as input,
+                        * i.e. when the app sets "index" and then the driver
+                        * fills in the rest of the structure for the thing
+                        * with that index.  We only need to copy up the first
+                        * non-input field.
+                        */
+                       if (v4l2_is_known_ioctl(cmd)) {
+                               u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags;
+                               if (flags & INFO_FL_CLEAR_MASK)
+                                       n = (flags & INFO_FL_CLEAR_MASK) >> 16;
+                       }
 
                        if (copy_from_user(parg, (void __user *)arg, n))
                                goto out;
index 975d0fa938c6fa81fd9a6bcacafecb69386c31a5..97b48318aee1826d3ad3bf5225efb93d053b02cc 100644 (file)
@@ -19,6 +19,9 @@
 
 #include <media/videobuf2-core.h>
 #include <media/v4l2-mem2mem.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
 
 MODULE_DESCRIPTION("Mem to mem device framework for videobuf");
 MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
@@ -407,11 +410,24 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff);
 unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
                           struct poll_table_struct *wait)
 {
+       struct video_device *vfd = video_devdata(file);
+       unsigned long req_events = poll_requested_events(wait);
        struct vb2_queue *src_q, *dst_q;
        struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
        unsigned int rc = 0;
        unsigned long flags;
 
+       if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
+               struct v4l2_fh *fh = file->private_data;
+
+               if (v4l2_event_pending(fh))
+                       rc = POLLPRI;
+               else if (req_events & POLLPRI)
+                       poll_wait(file, &fh->wait, wait);
+               if (!(req_events & (POLLOUT | POLLWRNORM | POLLIN | POLLRDNORM)))
+                       return rc;
+       }
+
        src_q = v4l2_m2m_get_src_vq(m2m_ctx);
        dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
 
@@ -422,7 +438,7 @@ unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
         */
        if ((!src_q->streaming || list_empty(&src_q->queued_list))
                && (!dst_q->streaming || list_empty(&dst_q->queued_list))) {
-               rc = POLLERR;
+               rc |= POLLERR;
                goto end;
        }
 
index db6e859b93d4832a6420ca6d3d111d24b13164df..9182f81deb5b0177ede5b1d3280c07cc02e82188 100644 (file)
@@ -245,7 +245,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                memset(&sel, 0, sizeof(sel));
                sel.which = crop->which;
                sel.pad = crop->pad;
-               sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL;
+               sel.target = V4L2_SEL_TGT_CROP;
 
                rval = v4l2_subdev_call(
                        sd, pad, get_selection, subdev_fh, &sel);
@@ -274,7 +274,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                memset(&sel, 0, sizeof(sel));
                sel.which = crop->which;
                sel.pad = crop->pad;
-               sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL;
+               sel.target = V4L2_SEL_TGT_CROP;
                sel.r = crop->rect;
 
                rval = v4l2_subdev_call(
index 308e150a39bca34b439cc99a413ad7c43f1c3a18..eb404c2ce27043489a80ebfe1cbff114f55438ae 100644 (file)
@@ -963,7 +963,7 @@ static int viacam_do_try_fmt(struct via_camera *cam,
 
        upix->pixelformat = f->pixelformat;
        viacam_fmt_pre(upix, spix);
-       v4l2_fill_mbus_format(&mbus_fmt, upix, f->mbus_code);
+       v4l2_fill_mbus_format(&mbus_fmt, spix, f->mbus_code);
        ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt);
        v4l2_fill_pix_format(spix, &mbus_fmt);
        viacam_fmt_post(upix, spix);
index ffdf59cfe4054f18d258ec4d682fddcfc7dc1aa7..bf7a326b1cdc06f8346981f246a74be572894fe4 100644 (file)
@@ -359,11 +359,6 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
                break;
        }
 
-       if (vb->input != UNSET) {
-               b->flags |= V4L2_BUF_FLAG_INPUT;
-               b->input  = vb->input;
-       }
-
        b->field     = vb->field;
        b->timestamp = vb->ts;
        b->bytesused = vb->size;
@@ -402,7 +397,6 @@ int __videobuf_mmap_setup(struct videobuf_queue *q,
                        break;
 
                q->bufs[i]->i      = i;
-               q->bufs[i]->input  = UNSET;
                q->bufs[i]->memory = memory;
                q->bufs[i]->bsize  = bsize;
                switch (memory) {
@@ -566,16 +560,6 @@ int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b)
                goto done;
        }
 
-       if (b->flags & V4L2_BUF_FLAG_INPUT) {
-               if (b->input >= q->inputs) {
-                       dprintk(1, "qbuf: wrong input.\n");
-                       goto done;
-               }
-               buf->input = b->input;
-       } else {
-               buf->input = UNSET;
-       }
-
        switch (b->memory) {
        case V4L2_MEMORY_MMAP:
                if (0 == buf->baddr) {
index b6b5cc1a43cb9261023f52fd7c78fdb77e49066d..3a43ba0959bf15a1a23f7152c03efa6c1449bc78 100644 (file)
@@ -40,7 +40,7 @@ struct videobuf_dma_contig_memory {
 
 static int __videobuf_dc_alloc(struct device *dev,
                               struct videobuf_dma_contig_memory *mem,
-                              unsigned long size, unsigned long flags)
+                              unsigned long size, gfp_t flags)
 {
        mem->size = size;
        if (mem->cached) {
@@ -56,7 +56,7 @@ static int __videobuf_dc_alloc(struct device *dev,
                                dev_err(dev, "dma_map_single failed\n");
 
                                free_pages_exact(mem->vaddr, mem->size);
-                               mem->vaddr = 0;
+                               mem->vaddr = NULL;
                                return err;
                        }
                }
@@ -359,32 +359,43 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
        size = vma->vm_end - vma->vm_start;
        size = (size < mem->size) ? size : mem->size;
 
-       if (!mem->cached)
+       if (!mem->cached) {
                vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-
-       pos = (unsigned long)mem->vaddr;
-
-       while (size > 0) {
-               page = virt_to_page((void *)pos);
-               if (NULL == page) {
-                       dev_err(q->dev, "mmap: virt_to_page failed\n");
-                       __videobuf_dc_free(q->dev, mem);
-                       goto error;
-               }
-               retval = vm_insert_page(vma, start, page);
+               retval = remap_pfn_range(vma, vma->vm_start,
+                        mem->dma_handle >> PAGE_SHIFT,
+                                size, vma->vm_page_prot);
                if (retval) {
-                       dev_err(q->dev, "mmap: insert failed with error %d\n",
-                               retval);
-                       __videobuf_dc_free(q->dev, mem);
+                       dev_err(q->dev, "mmap: remap failed with error %d. ",
+                                                               retval);
+                       dma_free_coherent(q->dev, mem->size,
+                                       mem->vaddr, mem->dma_handle);
                        goto error;
                }
-               start += PAGE_SIZE;
-               pos += PAGE_SIZE;
+       } else {
+               pos = (unsigned long)mem->vaddr;
 
-               if (size > PAGE_SIZE)
-                       size -= PAGE_SIZE;
-               else
-                       size = 0;
+               while (size > 0) {
+                       page = virt_to_page((void *)pos);
+                       if (NULL == page) {
+                               dev_err(q->dev, "mmap: virt_to_page failed\n");
+                               __videobuf_dc_free(q->dev, mem);
+                               goto error;
+                       }
+                       retval = vm_insert_page(vma, start, page);
+                       if (retval) {
+                               dev_err(q->dev, "mmap: insert failed with error %d\n",
+                                       retval);
+                               __videobuf_dc_free(q->dev, mem);
+                               goto error;
+                       }
+                       start += PAGE_SIZE;
+                       pos += PAGE_SIZE;
+
+                       if (size > PAGE_SIZE)
+                               size -= PAGE_SIZE;
+                       else
+                               size = 0;
+               }
        }
 
        vma->vm_ops = &videobuf_vm_ops;
index 9d4e9edbd2e7a661b5e07a35637bf5ad28b65c14..268c7dd4f8231ebc2f3ca033bfea4bbc689f07f7 100644 (file)
@@ -336,9 +336,9 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
        struct vb2_queue *q = vb->vb2_queue;
        int ret;
 
-       /* Copy back data such as timestamp, flags, input, etc. */
+       /* Copy back data such as timestamp, flags, etc. */
        memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
-       b->input = vb->v4l2_buf.input;
+       b->reserved2 = vb->v4l2_buf.reserved2;
        b->reserved = vb->v4l2_buf.reserved;
 
        if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) {
@@ -454,7 +454,50 @@ static int __verify_mmap_ops(struct vb2_queue *q)
 }
 
 /**
- * vb2_reqbufs() - Initiate streaming
+ * __verify_memory_type() - Check whether the memory type and buffer type
+ * passed to a buffer operation are compatible with the queue.
+ */
+static int __verify_memory_type(struct vb2_queue *q,
+               enum v4l2_memory memory, enum v4l2_buf_type type)
+{
+       if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR) {
+               dprintk(1, "reqbufs: unsupported memory type\n");
+               return -EINVAL;
+       }
+
+       if (type != q->type) {
+               dprintk(1, "reqbufs: requested type is incorrect\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Make sure all the required memory ops for given memory type
+        * are available.
+        */
+       if (memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
+               dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
+               return -EINVAL;
+       }
+
+       if (memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
+               dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Place the busy tests at the end: -EBUSY can be ignored when
+        * create_bufs is called with count == 0, but count == 0 should still
+        * do the memory and type validation.
+        */
+       if (q->fileio) {
+               dprintk(1, "reqbufs: file io in progress\n");
+               return -EBUSY;
+       }
+       return 0;
+}
+
+/**
+ * __reqbufs() - Initiate streaming
  * @q:         videobuf2 queue
  * @req:       struct passed from userspace to vidioc_reqbufs handler in driver
  *
@@ -476,46 +519,16 @@ static int __verify_mmap_ops(struct vb2_queue *q)
  * The return values from this function are intended to be directly returned
  * from vidioc_reqbufs handler in driver.
  */
-int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
+static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
 {
        unsigned int num_buffers, allocated_buffers, num_planes = 0;
-       int ret = 0;
-
-       if (q->fileio) {
-               dprintk(1, "reqbufs: file io in progress\n");
-               return -EBUSY;
-       }
-
-       if (req->memory != V4L2_MEMORY_MMAP
-                       && req->memory != V4L2_MEMORY_USERPTR) {
-               dprintk(1, "reqbufs: unsupported memory type\n");
-               return -EINVAL;
-       }
-
-       if (req->type != q->type) {
-               dprintk(1, "reqbufs: requested type is incorrect\n");
-               return -EINVAL;
-       }
+       int ret;
 
        if (q->streaming) {
                dprintk(1, "reqbufs: streaming active\n");
                return -EBUSY;
        }
 
-       /*
-        * Make sure all the required memory ops for given memory type
-        * are available.
-        */
-       if (req->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
-               dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
-               return -EINVAL;
-       }
-
-       if (req->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
-               dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
-               return -EINVAL;
-       }
-
        if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) {
                /*
                 * We already have buffers allocated, so first check if they
@@ -595,10 +608,23 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
 
        return 0;
 }
+
+/**
+ * vb2_reqbufs() - Wrapper for __reqbufs() that also verifies the memory and
+ * type values.
+ * @q:         videobuf2 queue
+ * @req:       struct passed from userspace to vidioc_reqbufs handler in driver
+ */
+int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
+{
+       int ret = __verify_memory_type(q, req->memory, req->type);
+
+       return ret ? ret : __reqbufs(q, req);
+}
 EXPORT_SYMBOL_GPL(vb2_reqbufs);
 
 /**
- * vb2_create_bufs() - Allocate buffers and any required auxiliary structs
+ * __create_bufs() - Allocate buffers and any required auxiliary structs
  * @q:         videobuf2 queue
  * @create:    creation parameters, passed from userspace to vidioc_create_bufs
  *             handler in driver
@@ -612,40 +638,10 @@ EXPORT_SYMBOL_GPL(vb2_reqbufs);
  * The return values from this function are intended to be directly returned
  * from vidioc_create_bufs handler in driver.
  */
-int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
+static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
 {
        unsigned int num_planes = 0, num_buffers, allocated_buffers;
-       int ret = 0;
-
-       if (q->fileio) {
-               dprintk(1, "%s(): file io in progress\n", __func__);
-               return -EBUSY;
-       }
-
-       if (create->memory != V4L2_MEMORY_MMAP
-                       && create->memory != V4L2_MEMORY_USERPTR) {
-               dprintk(1, "%s(): unsupported memory type\n", __func__);
-               return -EINVAL;
-       }
-
-       if (create->format.type != q->type) {
-               dprintk(1, "%s(): requested type is incorrect\n", __func__);
-               return -EINVAL;
-       }
-
-       /*
-        * Make sure all the required memory ops for given memory type
-        * are available.
-        */
-       if (create->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
-               dprintk(1, "%s(): MMAP for current setup unsupported\n", __func__);
-               return -EINVAL;
-       }
-
-       if (create->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
-               dprintk(1, "%s(): USERPTR for current setup unsupported\n", __func__);
-               return -EINVAL;
-       }
+       int ret;
 
        if (q->num_buffers == VIDEO_MAX_FRAME) {
                dprintk(1, "%s(): maximum number of buffers already allocated\n",
@@ -653,8 +649,6 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
                return -ENOBUFS;
        }
 
-       create->index = q->num_buffers;
-
        if (!q->num_buffers) {
                memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
                memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
@@ -675,9 +669,9 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
        /* Finally, allocate buffers and video memory */
        ret = __vb2_queue_alloc(q, create->memory, num_buffers,
                                num_planes);
-       if (ret < 0) {
-               dprintk(1, "Memory allocation failed with error: %d\n", ret);
-               return ret;
+       if (ret == 0) {
+               dprintk(1, "Memory allocation failed\n");
+               return -ENOMEM;
        }
 
        allocated_buffers = ret;
@@ -708,7 +702,7 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
 
        if (ret < 0) {
                __vb2_queue_free(q, allocated_buffers);
-               return ret;
+               return -ENOMEM;
        }
 
        /*
@@ -719,6 +713,23 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
 
        return 0;
 }
+
+/**
+ * vb2_create_bufs() - Wrapper for __create_bufs() that also verifies the
+ * memory and type values.
+ * @q:         videobuf2 queue
+ * @create:    creation parameters, passed from userspace to vidioc_create_bufs
+ *             handler in driver
+ */
+int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
+{
+       int ret = __verify_memory_type(q, create->memory, create->format.type);
+
+       create->index = q->num_buffers;
+       if (create->count == 0)
+               return ret != -EBUSY ? ret : 0;
+       return ret ? ret : __create_bufs(q, create);
+}
 EXPORT_SYMBOL_GPL(vb2_create_bufs);
 
 /**
@@ -860,7 +871,6 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b,
 
        vb->v4l2_buf.field = b->field;
        vb->v4l2_buf.timestamp = b->timestamp;
-       vb->v4l2_buf.input = b->input;
        vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS;
 
        return 0;
@@ -2115,6 +2125,263 @@ size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
 }
 EXPORT_SYMBOL_GPL(vb2_write);
 
+
+/*
+ * The following functions are not part of the vb2 core API, but are helper
+ * functions that plug into struct v4l2_ioctl_ops, struct v4l2_file_operations
+ * and struct vb2_ops.
+ * They contain boilerplate code that most if not all drivers have to do
+ * and so they simplify the driver code.
+ */
+
+/* The queue is busy if there is a owner and you are not that owner. */
+static inline bool vb2_queue_is_busy(struct video_device *vdev, struct file *file)
+{
+       return vdev->queue->owner && vdev->queue->owner != file->private_data;
+}
+
+/* vb2 ioctl helpers */
+
+int vb2_ioctl_reqbufs(struct file *file, void *priv,
+                         struct v4l2_requestbuffers *p)
+{
+       struct video_device *vdev = video_devdata(file);
+       int res = __verify_memory_type(vdev->queue, p->memory, p->type);
+
+       if (res)
+               return res;
+       if (vb2_queue_is_busy(vdev, file))
+               return -EBUSY;
+       res = __reqbufs(vdev->queue, p);
+       /* If count == 0, then the owner has released all buffers and he
+          is no longer owner of the queue. Otherwise we have a new owner. */
+       if (res == 0)
+               vdev->queue->owner = p->count ? file->private_data : NULL;
+       return res;
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs);
+
+int vb2_ioctl_create_bufs(struct file *file, void *priv,
+                         struct v4l2_create_buffers *p)
+{
+       struct video_device *vdev = video_devdata(file);
+       int res = __verify_memory_type(vdev->queue, p->memory, p->format.type);
+
+       p->index = vdev->queue->num_buffers;
+       /* If count == 0, then just check if memory and type are valid.
+          Any -EBUSY result from __verify_memory_type can be mapped to 0. */
+       if (p->count == 0)
+               return res != -EBUSY ? res : 0;
+       if (res)
+               return res;
+       if (vb2_queue_is_busy(vdev, file))
+               return -EBUSY;
+       res = __create_bufs(vdev->queue, p);
+       if (res == 0)
+               vdev->queue->owner = file->private_data;
+       return res;
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_create_bufs);
+
+int vb2_ioctl_prepare_buf(struct file *file, void *priv,
+                         struct v4l2_buffer *p)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vb2_queue_is_busy(vdev, file))
+               return -EBUSY;
+       return vb2_prepare_buf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf);
+
+int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       /* No need to call vb2_queue_is_busy(), anyone can query buffers. */
+       return vb2_querybuf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_querybuf);
+
+int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vb2_queue_is_busy(vdev, file))
+               return -EBUSY;
+       return vb2_qbuf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf);
+
+int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vb2_queue_is_busy(vdev, file))
+               return -EBUSY;
+       return vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_dqbuf);
+
+int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vb2_queue_is_busy(vdev, file))
+               return -EBUSY;
+       return vb2_streamon(vdev->queue, i);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_streamon);
+
+int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vb2_queue_is_busy(vdev, file))
+               return -EBUSY;
+       return vb2_streamoff(vdev->queue, i);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff);
+
+/* v4l2_file_operations helpers */
+
+int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       return vb2_mmap(vdev->queue, vma);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_mmap);
+
+int vb2_fop_release(struct file *file)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (file->private_data == vdev->queue->owner) {
+               vb2_queue_release(vdev->queue);
+               vdev->queue->owner = NULL;
+       }
+       return v4l2_fh_release(file);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_release);
+
+ssize_t vb2_fop_write(struct file *file, char __user *buf,
+               size_t count, loff_t *ppos)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
+       bool must_lock = !test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && lock;
+       int err = -EBUSY;
+
+       if (must_lock && mutex_lock_interruptible(lock))
+               return -ERESTARTSYS;
+       if (vb2_queue_is_busy(vdev, file))
+               goto exit;
+       err = vb2_write(vdev->queue, buf, count, ppos,
+                      file->f_flags & O_NONBLOCK);
+       if (err >= 0)
+               vdev->queue->owner = file->private_data;
+exit:
+       if (must_lock)
+               mutex_unlock(lock);
+       return err;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_write);
+
+ssize_t vb2_fop_read(struct file *file, char __user *buf,
+               size_t count, loff_t *ppos)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
+       bool must_lock = !test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && vdev->lock;
+       int err = -EBUSY;
+
+       if (must_lock && mutex_lock_interruptible(lock))
+               return -ERESTARTSYS;
+       if (vb2_queue_is_busy(vdev, file))
+               goto exit;
+       err = vb2_read(vdev->queue, buf, count, ppos,
+                      file->f_flags & O_NONBLOCK);
+       if (err >= 0)
+               vdev->queue->owner = file->private_data;
+exit:
+       if (must_lock)
+               mutex_unlock(lock);
+       return err;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_read);
+
+unsigned int vb2_fop_poll(struct file *file, poll_table *wait)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct vb2_queue *q = vdev->queue;
+       struct mutex *lock = q->lock ? q->lock : vdev->lock;
+       unsigned long req_events = poll_requested_events(wait);
+       unsigned res;
+       void *fileio;
+       /* Yuck. We really need to get rid of this flag asap. If it is
+          set, then the core took the serialization lock before calling
+          poll(). This is being phased out, but for now we have to handle
+          this case. */
+       bool locked = test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags);
+       bool must_lock = false;
+
+       /* Try to be smart: only lock if polling might start fileio,
+          otherwise locking will only introduce unwanted delays. */
+       if (q->num_buffers == 0 && q->fileio == NULL) {
+               if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) &&
+                               (req_events & (POLLIN | POLLRDNORM)))
+                       must_lock = true;
+               else if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) &&
+                               (req_events & (POLLOUT | POLLWRNORM)))
+                       must_lock = true;
+       }
+
+       /* If locking is needed, but this helper doesn't know how, then you
+          shouldn't be using this helper but you should write your own. */
+       WARN_ON(must_lock && !locked && !lock);
+
+       if (must_lock && !locked && lock && mutex_lock_interruptible(lock))
+               return POLLERR;
+
+       fileio = q->fileio;
+
+       res = vb2_poll(vdev->queue, file, wait);
+
+       /* If fileio was started, then we have a new queue owner. */
+       if (must_lock && !fileio && q->fileio)
+               q->owner = file->private_data;
+       if (must_lock && !locked && lock)
+               mutex_unlock(lock);
+       return res;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_poll);
+
+#ifndef CONFIG_MMU
+unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr,
+               unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area);
+#endif
+
+/* vb2_ops helpers. Only use if vq->lock is non-NULL. */
+
+void vb2_ops_wait_prepare(struct vb2_queue *vq)
+{
+       mutex_unlock(vq->lock);
+}
+EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare);
+
+void vb2_ops_wait_finish(struct vb2_queue *vq)
+{
+       mutex_lock(vq->lock);
+}
+EXPORT_SYMBOL_GPL(vb2_ops_wait_finish);
+
 MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
 MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski");
 MODULE_LICENSE("GPL");
index 08c10240e70fba4063f2abd8cab3904b1f935a4b..a05494b71b20a7247a8bfb27c05304dcf8ae3272 100644 (file)
@@ -188,6 +188,7 @@ struct vivi_dev {
        struct list_head           vivi_devlist;
        struct v4l2_device         v4l2_dev;
        struct v4l2_ctrl_handler   ctrl_handler;
+       struct video_device        vdev;
 
        /* controls */
        struct v4l2_ctrl           *brightness;
@@ -213,9 +214,6 @@ struct vivi_dev {
        spinlock_t                 slock;
        struct mutex               mutex;
 
-       /* various device info */
-       struct video_device        *vfd;
-
        struct vivi_dmaqueue       vidq;
 
        /* Several counters */
@@ -232,7 +230,6 @@ struct vivi_dev {
        struct vivi_fmt            *fmt;
        unsigned int               width, height;
        struct vb2_queue           vb_vidq;
-       enum v4l2_field            field;
        unsigned int               field_count;
 
        u8                         bars[9][3];
@@ -625,7 +622,7 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
 
        dev->mv_count += 2;
 
-       buf->vb.v4l2_buf.field = dev->field;
+       buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
        dev->field_count++;
        buf->vb.v4l2_buf.sequence = dev->field_count >> 1;
        do_gettimeofday(&ts);
@@ -769,7 +766,13 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
        struct vivi_dev *dev = vb2_get_drv_priv(vq);
        unsigned long size;
 
-       size = dev->width * dev->height * dev->pixelsize;
+       if (fmt)
+               size = fmt->fmt.pix.sizeimage;
+       else
+               size = dev->width * dev->height * dev->pixelsize;
+
+       if (size == 0)
+               return -EINVAL;
 
        if (0 == *nbuffers)
                *nbuffers = 32;
@@ -792,27 +795,6 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
        return 0;
 }
 
-static int buffer_init(struct vb2_buffer *vb)
-{
-       struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
-
-       BUG_ON(NULL == dev->fmt);
-
-       /*
-        * This callback is called once per buffer, after its allocation.
-        *
-        * Vivi does not allow changing format during streaming, but it is
-        * possible to do so when streaming is paused (i.e. in streamoff state).
-        * Buffers however are not freed when going into streamoff and so
-        * buffer size verification has to be done in buffer_prepare, on each
-        * qbuf.
-        * It would be best to move verification code here to buf_init and
-        * s_fmt though.
-        */
-
-       return 0;
-}
-
 static int buffer_prepare(struct vb2_buffer *vb)
 {
        struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
@@ -850,20 +832,6 @@ static int buffer_prepare(struct vb2_buffer *vb)
        return 0;
 }
 
-static int buffer_finish(struct vb2_buffer *vb)
-{
-       struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
-       dprintk(dev, 1, "%s\n", __func__);
-       return 0;
-}
-
-static void buffer_cleanup(struct vb2_buffer *vb)
-{
-       struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
-       dprintk(dev, 1, "%s\n", __func__);
-
-}
-
 static void buffer_queue(struct vb2_buffer *vb)
 {
        struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
@@ -909,10 +877,7 @@ static void vivi_unlock(struct vb2_queue *vq)
 
 static struct vb2_ops vivi_video_qops = {
        .queue_setup            = queue_setup,
-       .buf_init               = buffer_init,
        .buf_prepare            = buffer_prepare,
-       .buf_finish             = buffer_finish,
-       .buf_cleanup            = buffer_cleanup,
        .buf_queue              = buffer_queue,
        .start_streaming        = start_streaming,
        .stop_streaming         = stop_streaming,
@@ -959,7 +924,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
 
        f->fmt.pix.width        = dev->width;
        f->fmt.pix.height       = dev->height;
-       f->fmt.pix.field        = dev->field;
+       f->fmt.pix.field        = V4L2_FIELD_INTERLACED;
        f->fmt.pix.pixelformat  = dev->fmt->fourcc;
        f->fmt.pix.bytesperline =
                (f->fmt.pix.width * dev->fmt->depth) >> 3;
@@ -978,25 +943,16 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
 {
        struct vivi_dev *dev = video_drvdata(file);
        struct vivi_fmt *fmt;
-       enum v4l2_field field;
 
        fmt = get_format(f);
        if (!fmt) {
-               dprintk(dev, 1, "Fourcc format (0x%08x) invalid.\n",
+               dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",
                        f->fmt.pix.pixelformat);
-               return -EINVAL;
-       }
-
-       field = f->fmt.pix.field;
-
-       if (field == V4L2_FIELD_ANY) {
-               field = V4L2_FIELD_INTERLACED;
-       } else if (V4L2_FIELD_INTERLACED != field) {
-               dprintk(dev, 1, "Field type invalid.\n");
-               return -EINVAL;
+               f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+               fmt = get_format(f);
        }
 
-       f->fmt.pix.field = field;
+       f->fmt.pix.field = V4L2_FIELD_INTERLACED;
        v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,
                              &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
        f->fmt.pix.bytesperline =
@@ -1021,7 +977,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
        if (ret < 0)
                return ret;
 
-       if (vb2_is_streaming(q)) {
+       if (vb2_is_busy(q)) {
                dprintk(dev, 1, "%s device busy\n", __func__);
                return -EBUSY;
        }
@@ -1030,53 +986,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
        dev->pixelsize = dev->fmt->depth / 8;
        dev->width = f->fmt.pix.width;
        dev->height = f->fmt.pix.height;
-       dev->field = f->fmt.pix.field;
 
        return 0;
 }
 
-static int vidioc_reqbufs(struct file *file, void *priv,
-                         struct v4l2_requestbuffers *p)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-       return vb2_reqbufs(&dev->vb_vidq, p);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-       return vb2_querybuf(&dev->vb_vidq, p);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-       return vb2_qbuf(&dev->vb_vidq, p);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-       return vb2_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK);
-}
-
-static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-       return vb2_streamon(&dev->vb_vidq, i);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-       return vb2_streamoff(&dev->vb_vidq, i);
-}
-
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
-{
-       return 0;
-}
-
 /* only one input in this sample driver */
 static int vidioc_enum_input(struct file *file, void *priv,
                                struct v4l2_input *inp)
@@ -1085,7 +998,6 @@ static int vidioc_enum_input(struct file *file, void *priv,
                return -EINVAL;
 
        inp->type = V4L2_INPUT_TYPE_CAMERA;
-       inp->std = V4L2_STD_525_60;
        sprintf(inp->name, "Camera %u", inp->index);
        return 0;
 }
@@ -1145,58 +1057,6 @@ static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
        File operations for the device
    ------------------------------------------------------------------*/
 
-static ssize_t
-vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-       int err;
-
-       dprintk(dev, 1, "read called\n");
-       mutex_lock(&dev->mutex);
-       err = vb2_read(&dev->vb_vidq, data, count, ppos,
-                      file->f_flags & O_NONBLOCK);
-       mutex_unlock(&dev->mutex);
-       return err;
-}
-
-static unsigned int
-vivi_poll(struct file *file, struct poll_table_struct *wait)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-       struct vb2_queue *q = &dev->vb_vidq;
-
-       dprintk(dev, 1, "%s\n", __func__);
-       return vb2_poll(q, file, wait);
-}
-
-static int vivi_close(struct file *file)
-{
-       struct video_device  *vdev = video_devdata(file);
-       struct vivi_dev *dev = video_drvdata(file);
-
-       dprintk(dev, 1, "close called (dev=%s), file %p\n",
-               video_device_node_name(vdev), file);
-
-       if (v4l2_fh_is_singular_file(file))
-               vb2_queue_release(&dev->vb_vidq);
-       return v4l2_fh_release(file);
-}
-
-static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-       int ret;
-
-       dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
-
-       ret = vb2_mmap(&dev->vb_vidq, vma);
-       dprintk(dev, 1, "vma start=0x%08lx, size=%ld, ret=%d\n",
-               (unsigned long)vma->vm_start,
-               (unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
-               ret);
-       return ret;
-}
-
 static const struct v4l2_ctrl_ops vivi_ctrl_ops = {
        .g_volatile_ctrl = vivi_g_volatile_ctrl,
        .s_ctrl = vivi_s_ctrl,
@@ -1301,11 +1161,11 @@ static const struct v4l2_ctrl_config vivi_ctrl_int_menu = {
 static const struct v4l2_file_operations vivi_fops = {
        .owner          = THIS_MODULE,
        .open           = v4l2_fh_open,
-       .release        = vivi_close,
-       .read           = vivi_read,
-       .poll           = vivi_poll,
+       .release        = vb2_fop_release,
+       .read           = vb2_fop_read,
+       .poll           = vb2_fop_poll,
        .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
-       .mmap           = vivi_mmap,
+       .mmap           = vb2_fop_mmap,
 };
 
 static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
@@ -1314,16 +1174,17 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
        .vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
        .vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
        .vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
-       .vidioc_reqbufs       = vidioc_reqbufs,
-       .vidioc_querybuf      = vidioc_querybuf,
-       .vidioc_qbuf          = vidioc_qbuf,
-       .vidioc_dqbuf         = vidioc_dqbuf,
-       .vidioc_s_std         = vidioc_s_std,
+       .vidioc_reqbufs       = vb2_ioctl_reqbufs,
+       .vidioc_create_bufs   = vb2_ioctl_create_bufs,
+       .vidioc_prepare_buf   = vb2_ioctl_prepare_buf,
+       .vidioc_querybuf      = vb2_ioctl_querybuf,
+       .vidioc_qbuf          = vb2_ioctl_qbuf,
+       .vidioc_dqbuf         = vb2_ioctl_dqbuf,
        .vidioc_enum_input    = vidioc_enum_input,
        .vidioc_g_input       = vidioc_g_input,
        .vidioc_s_input       = vidioc_s_input,
-       .vidioc_streamon      = vidioc_streamon,
-       .vidioc_streamoff     = vidioc_streamoff,
+       .vidioc_streamon      = vb2_ioctl_streamon,
+       .vidioc_streamoff     = vb2_ioctl_streamoff,
        .vidioc_log_status    = v4l2_ctrl_log_status,
        .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
        .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
@@ -1333,10 +1194,7 @@ static struct video_device vivi_template = {
        .name           = "vivi",
        .fops           = &vivi_fops,
        .ioctl_ops      = &vivi_ioctl_ops,
-       .release        = video_device_release,
-
-       .tvnorms              = V4L2_STD_525_60,
-       .current_norm         = V4L2_STD_NTSC_M,
+       .release        = video_device_release_empty,
 };
 
 /* -----------------------------------------------------------------
@@ -1354,8 +1212,8 @@ static int vivi_release(void)
                dev = list_entry(list, struct vivi_dev, vivi_devlist);
 
                v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
-                       video_device_node_name(dev->vfd));
-               video_unregister_device(dev->vfd);
+                       video_device_node_name(&dev->vdev));
+               video_unregister_device(&dev->vdev);
                v4l2_device_unregister(&dev->v4l2_dev);
                v4l2_ctrl_handler_free(&dev->ctrl_handler);
                kfree(dev);
@@ -1440,14 +1298,11 @@ static int __init vivi_create_instance(int inst)
        INIT_LIST_HEAD(&dev->vidq.active);
        init_waitqueue_head(&dev->vidq.wq);
 
-       ret = -ENOMEM;
-       vfd = video_device_alloc();
-       if (!vfd)
-               goto unreg_dev;
-
+       vfd = &dev->vdev;
        *vfd = vivi_template;
        vfd->debug = debug;
        vfd->v4l2_dev = &dev->v4l2_dev;
+       vfd->queue = q;
        set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
 
        /*
@@ -1455,26 +1310,19 @@ static int __init vivi_create_instance(int inst)
         * all fops and v4l2 ioctls.
         */
        vfd->lock = &dev->mutex;
+       video_set_drvdata(vfd, dev);
 
        ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
        if (ret < 0)
-               goto rel_vdev;
-
-       video_set_drvdata(vfd, dev);
+               goto unreg_dev;
 
        /* Now that everything is fine, let's add it to device list */
        list_add_tail(&dev->vivi_devlist, &vivi_devlist);
 
-       if (video_nr != -1)
-               video_nr++;
-
-       dev->vfd = vfd;
        v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
                  video_device_node_name(vfd));
        return 0;
 
-rel_vdev:
-       video_device_release(vfd);
 unreg_dev:
        v4l2_ctrl_handler_free(hdl);
        v4l2_device_unregister(&dev->v4l2_dev);
index e44cb330bbc8a8464d7e2e05106b0da1975a42c7..9afab35878b417f0c0ef7a7b352b718655373d7b 100644 (file)
 #include <linux/highmem.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
 #include <media/videobuf-vmalloc.h>
 
 
@@ -120,11 +124,6 @@ static struct usb_device_id device_table[] = {
 
 MODULE_DEVICE_TABLE(usb, device_table);
 
-struct zr364xx_mode {
-       u32 color;      /* output video color format */
-       u32 brightness; /* brightness */
-};
-
 /* frame structure */
 struct zr364xx_framei {
        unsigned long ulState;  /* ulState:ZR364XX_READ_IDLE,
@@ -173,7 +172,10 @@ static const struct zr364xx_fmt formats[] = {
 struct zr364xx_camera {
        struct usb_device *udev;        /* save off the usb device pointer */
        struct usb_interface *interface;/* the interface for this device */
-       struct video_device *vdev;      /* v4l video device */
+       struct v4l2_device v4l2_dev;
+       struct v4l2_ctrl_handler ctrl_handler;
+       struct video_device vdev;       /* v4l video device */
+       struct v4l2_fh *owner;          /* owns the streaming */
        int nb;
        struct zr364xx_bufferi          buffer;
        int skip;
@@ -181,12 +183,9 @@ struct zr364xx_camera {
        int height;
        int method;
        struct mutex lock;
-       struct mutex open_lock;
-       int users;
 
        spinlock_t              slock;
        struct zr364xx_dmaqueue vidq;
-       int                     resources;
        int                     last_frame;
        int                     cur_frame;
        unsigned long           frame_count;
@@ -197,8 +196,7 @@ struct zr364xx_camera {
 
        const struct zr364xx_fmt *fmt;
        struct videobuf_queue   vb_vidq;
-       enum v4l2_buf_type      type;
-       struct zr364xx_mode     mode;
+       bool was_streaming;
 };
 
 /* buffer for one video frame */
@@ -230,11 +228,6 @@ static int send_control_msg(struct usb_device *udev, u8 request, u16 value,
                                 transfer_buffer, size, CTRL_TIMEOUT);
 
        kfree(transfer_buffer);
-
-       if (status < 0)
-               dev_err(&udev->dev,
-                       "Failed sending control message, error %d.\n", status);
-
        return status;
 }
 
@@ -468,6 +461,7 @@ static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count,
                            loff_t * ppos)
 {
        struct zr364xx_camera *cam = video_drvdata(file);
+       int err = 0;
 
        _DBG("%s\n", __func__);
 
@@ -477,17 +471,21 @@ static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count,
        if (!count)
                return -EINVAL;
 
-       if (cam->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
-           zr364xx_vidioc_streamon(file, cam, cam->type) == 0) {
-               DBG("%s: reading %d bytes at pos %d.\n", __func__, (int) count,
-                   (int) *ppos);
+       if (mutex_lock_interruptible(&cam->lock))
+               return -ERESTARTSYS;
+
+       err = zr364xx_vidioc_streamon(file, file->private_data,
+                               V4L2_BUF_TYPE_VIDEO_CAPTURE);
+       if (err == 0) {
+               DBG("%s: reading %d bytes at pos %d.\n", __func__,
+                               (int) count, (int) *ppos);
 
                /* NoMan Sux ! */
-               return videobuf_read_one(&cam->vb_vidq, buf, count, ppos,
+               err = videobuf_read_one(&cam->vb_vidq, buf, count, ppos,
                                        file->f_flags & O_NONBLOCK);
        }
-
-       return 0;
+       mutex_unlock(&cam->lock);
+       return err;
 }
 
 /* video buffer vmalloc implementation based partly on VIVI driver which is
@@ -702,35 +700,6 @@ static int zr364xx_read_video_callback(struct zr364xx_camera *cam,
        return 0;
 }
 
-static int res_get(struct zr364xx_camera *cam)
-{
-       /* is it free? */
-       mutex_lock(&cam->lock);
-       if (cam->resources) {
-               /* no, someone else uses it */
-               mutex_unlock(&cam->lock);
-               return 0;
-       }
-       /* it's free, grab it */
-       cam->resources = 1;
-       _DBG("res: get\n");
-       mutex_unlock(&cam->lock);
-       return 1;
-}
-
-static inline int res_check(struct zr364xx_camera *cam)
-{
-       return cam->resources;
-}
-
-static void res_free(struct zr364xx_camera *cam)
-{
-       mutex_lock(&cam->lock);
-       cam->resources = 0;
-       mutex_unlock(&cam->lock);
-       _DBG("res: put\n");
-}
-
 static int zr364xx_vidioc_querycap(struct file *file, void *priv,
                                   struct v4l2_capability *cap)
 {
@@ -740,9 +709,10 @@ static int zr364xx_vidioc_querycap(struct file *file, void *priv,
        strlcpy(cap->card, cam->udev->product, sizeof(cap->card));
        strlcpy(cap->bus_info, dev_name(&cam->udev->dev),
                sizeof(cap->bus_info));
-       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+       cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
                            V4L2_CAP_READWRITE |
                            V4L2_CAP_STREAMING;
+       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
 
        return 0;
 }
@@ -772,50 +742,18 @@ static int zr364xx_vidioc_s_input(struct file *file, void *priv,
        return 0;
 }
 
-static int zr364xx_vidioc_queryctrl(struct file *file, void *priv,
-                                   struct v4l2_queryctrl *c)
+static int zr364xx_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct zr364xx_camera *cam;
-
-       if (file == NULL)
-               return -ENODEV;
-       cam = video_drvdata(file);
-
-       switch (c->id) {
-       case V4L2_CID_BRIGHTNESS:
-               c->type = V4L2_CTRL_TYPE_INTEGER;
-               strcpy(c->name, "Brightness");
-               c->minimum = 0;
-               c->maximum = 127;
-               c->step = 1;
-               c->default_value = cam->mode.brightness;
-               c->flags = 0;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv,
-                                struct v4l2_control *c)
-{
-       struct zr364xx_camera *cam;
+       struct zr364xx_camera *cam =
+               container_of(ctrl->handler, struct zr364xx_camera, ctrl_handler);
        int temp;
 
-       if (file == NULL)
-               return -ENODEV;
-       cam = video_drvdata(file);
-
-       switch (c->id) {
+       switch (ctrl->id) {
        case V4L2_CID_BRIGHTNESS:
-               cam->mode.brightness = c->value;
                /* hardware brightness */
-               mutex_lock(&cam->lock);
                send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0);
-               temp = (0x60 << 8) + 127 - cam->mode.brightness;
+               temp = (0x60 << 8) + 127 - ctrl->val;
                send_control_msg(cam->udev, 1, temp, 0, NULL, 0);
-               mutex_unlock(&cam->lock);
                break;
        default:
                return -EINVAL;
@@ -824,25 +762,6 @@ static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv,
        return 0;
 }
 
-static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv,
-                                struct v4l2_control *c)
-{
-       struct zr364xx_camera *cam;
-
-       if (file == NULL)
-               return -ENODEV;
-       cam = video_drvdata(file);
-
-       switch (c->id) {
-       case V4L2_CID_BRIGHTNESS:
-               c->value = cam->mode.brightness;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
 static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file,
                                       void *priv, struct v4l2_fmtdesc *f)
 {
@@ -888,7 +807,7 @@ static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
        f->fmt.pix.field = V4L2_FIELD_NONE;
        f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
        f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
-       f->fmt.pix.colorspace = 0;
+       f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
        f->fmt.pix.priv = 0;
        DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__,
            decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name),
@@ -911,7 +830,7 @@ static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
        f->fmt.pix.height = cam->height;
        f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
        f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
-       f->fmt.pix.colorspace = 0;
+       f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
        f->fmt.pix.priv = 0;
        return 0;
 }
@@ -936,7 +855,7 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                goto out;
        }
 
-       if (res_check(cam)) {
+       if (cam->owner) {
                DBG("%s can't change format after started\n", __func__);
                ret = -EBUSY;
                goto out;
@@ -944,14 +863,13 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
 
        cam->width = f->fmt.pix.width;
        cam->height = f->fmt.pix.height;
-       dev_info(&cam->udev->dev, "%s: %dx%d mode selected\n", __func__,
+       DBG("%s: %dx%d mode selected\n", __func__,
                 cam->width, cam->height);
        f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
        f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
-       f->fmt.pix.colorspace = 0;
+       f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
        f->fmt.pix.priv = 0;
        cam->vb_vidq.field = f->fmt.pix.field;
-       cam->mode.color = V4L2_PIX_FMT_JPEG;
 
        if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120)
                mode = 1;
@@ -1015,10 +933,11 @@ out:
 static int zr364xx_vidioc_reqbufs(struct file *file, void *priv,
                          struct v4l2_requestbuffers *p)
 {
-       int rc;
        struct zr364xx_camera *cam = video_drvdata(file);
-       rc = videobuf_reqbufs(&cam->vb_vidq, p);
-       return rc;
+
+       if (cam->owner && cam->owner != priv)
+               return -EBUSY;
+       return videobuf_reqbufs(&cam->vb_vidq, p);
 }
 
 static int zr364xx_vidioc_querybuf(struct file *file,
@@ -1038,6 +957,8 @@ static int zr364xx_vidioc_qbuf(struct file *file,
        int rc;
        struct zr364xx_camera *cam = video_drvdata(file);
        _DBG("%s\n", __func__);
+       if (cam->owner && cam->owner != priv)
+               return -EBUSY;
        rc = videobuf_qbuf(&cam->vb_vidq, p);
        return rc;
 }
@@ -1049,6 +970,8 @@ static int zr364xx_vidioc_dqbuf(struct file *file,
        int rc;
        struct zr364xx_camera *cam = video_drvdata(file);
        _DBG("%s\n", __func__);
+       if (cam->owner && cam->owner != priv)
+               return -EBUSY;
        rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK);
        return rc;
 }
@@ -1197,29 +1120,23 @@ static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam)
        return 0;
 }
 
-static int zr364xx_vidioc_streamon(struct file *file, void *priv,
-                                  enum v4l2_buf_type type)
+static int zr364xx_prepare(struct zr364xx_camera *cam)
 {
-       struct zr364xx_camera *cam = video_drvdata(file);
-       int j;
        int res;
+       int i, j;
 
-       DBG("%s\n", __func__);
-
-       if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-               dev_err(&cam->udev->dev, "invalid fh type0\n");
-               return -EINVAL;
-       }
-       if (cam->type != type) {
-               dev_err(&cam->udev->dev, "invalid fh type1\n");
-               return -EINVAL;
-       }
-
-       if (!res_get(cam)) {
-               dev_err(&cam->udev->dev, "stream busy\n");
-               return -EBUSY;
+       for (i = 0; init[cam->method][i].size != -1; i++) {
+               res = send_control_msg(cam->udev, 1, init[cam->method][i].value,
+                                    0, init[cam->method][i].bytes,
+                                    init[cam->method][i].size);
+               if (res < 0) {
+                       dev_err(&cam->udev->dev,
+                               "error during open sequence: %d\n", i);
+                       return res;
+               }
        }
 
+       cam->skip = 2;
        cam->last_frame = -1;
        cam->cur_frame = 0;
        cam->frame_count = 0;
@@ -1227,11 +1144,31 @@ static int zr364xx_vidioc_streamon(struct file *file, void *priv,
                cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE;
                cam->buffer.frame[j].cur_size = 0;
        }
+       v4l2_ctrl_handler_setup(&cam->ctrl_handler);
+       return 0;
+}
+
+static int zr364xx_vidioc_streamon(struct file *file, void *priv,
+                                  enum v4l2_buf_type type)
+{
+       struct zr364xx_camera *cam = video_drvdata(file);
+       int res;
+
+       DBG("%s\n", __func__);
+
+       if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       if (cam->owner && cam->owner != priv)
+               return -EBUSY;
+
+       res = zr364xx_prepare(cam);
+       if (res)
+               return res;
        res = videobuf_streamon(&cam->vb_vidq);
        if (res == 0) {
                zr364xx_start_acquire(cam);
-       } else {
-               res_free(cam);
+               cam->owner = file->private_data;
        }
        return res;
 }
@@ -1239,67 +1176,32 @@ static int zr364xx_vidioc_streamon(struct file *file, void *priv,
 static int zr364xx_vidioc_streamoff(struct file *file, void *priv,
                                    enum v4l2_buf_type type)
 {
-       int res;
        struct zr364xx_camera *cam = video_drvdata(file);
 
        DBG("%s\n", __func__);
-       if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-               dev_err(&cam->udev->dev, "invalid fh type0\n");
+       if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
-       }
-       if (cam->type != type) {
-               dev_err(&cam->udev->dev, "invalid fh type1\n");
-               return -EINVAL;
-       }
+       if (cam->owner && cam->owner != priv)
+               return -EBUSY;
        zr364xx_stop_acquire(cam);
-       res = videobuf_streamoff(&cam->vb_vidq);
-       if (res < 0)
-               return res;
-       res_free(cam);
-       return 0;
+       return videobuf_streamoff(&cam->vb_vidq);
 }
 
 
 /* open the camera */
 static int zr364xx_open(struct file *file)
 {
-       struct video_device *vdev = video_devdata(file);
        struct zr364xx_camera *cam = video_drvdata(file);
-       struct usb_device *udev = cam->udev;
-       int i, err;
+       int err;
 
        DBG("%s\n", __func__);
 
-       mutex_lock(&cam->open_lock);
+       if (mutex_lock_interruptible(&cam->lock))
+               return -ERESTARTSYS;
 
-       if (cam->users) {
-               err = -EBUSY;
+       err = v4l2_fh_open(file);
+       if (err)
                goto out;
-       }
-
-       for (i = 0; init[cam->method][i].size != -1; i++) {
-               err =
-                   send_control_msg(udev, 1, init[cam->method][i].value,
-                                    0, init[cam->method][i].bytes,
-                                    init[cam->method][i].size);
-               if (err < 0) {
-                       dev_err(&cam->udev->dev,
-                               "error during open sequence: %d\n", i);
-                       goto out;
-               }
-       }
-
-       cam->skip = 2;
-       cam->users++;
-       file->private_data = vdev;
-       cam->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-       cam->fmt = formats;
-
-       videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops,
-                                   NULL, &cam->slock,
-                                   cam->type,
-                                   V4L2_FIELD_NONE,
-                                   sizeof(struct zr364xx_buffer), cam, NULL);
 
        /* Added some delay here, since opening/closing the camera quickly,
         * like Ekiga does during its startup, can crash the webcam
@@ -1308,29 +1210,20 @@ static int zr364xx_open(struct file *file)
        err = 0;
 
 out:
-       mutex_unlock(&cam->open_lock);
+       mutex_unlock(&cam->lock);
        DBG("%s: %d\n", __func__, err);
        return err;
 }
 
-static void zr364xx_destroy(struct zr364xx_camera *cam)
+static void zr364xx_release(struct v4l2_device *v4l2_dev)
 {
+       struct zr364xx_camera *cam =
+               container_of(v4l2_dev, struct zr364xx_camera, v4l2_dev);
        unsigned long i;
 
-       if (!cam) {
-               printk(KERN_ERR KBUILD_MODNAME ", %s: no device\n", __func__);
-               return;
-       }
-       mutex_lock(&cam->open_lock);
-       if (cam->vdev)
-               video_unregister_device(cam->vdev);
-       cam->vdev = NULL;
-
-       /* stops the read pipe if it is running */
-       if (cam->b_acquire)
-               zr364xx_stop_acquire(cam);
+       v4l2_device_unregister(&cam->v4l2_dev);
 
-       zr364xx_stop_readpipe(cam);
+       videobuf_mmap_free(&cam->vb_vidq);
 
        /* release sys buffers */
        for (i = 0; i < FRAMES; i++) {
@@ -1341,62 +1234,45 @@ static void zr364xx_destroy(struct zr364xx_camera *cam)
                cam->buffer.frame[i].lpvbits = NULL;
        }
 
+       v4l2_ctrl_handler_free(&cam->ctrl_handler);
        /* release transfer buffer */
        kfree(cam->pipe->transfer_buffer);
-       cam->pipe->transfer_buffer = NULL;
-       mutex_unlock(&cam->open_lock);
        kfree(cam);
-       cam = NULL;
 }
 
 /* release the camera */
-static int zr364xx_release(struct file *file)
+static int zr364xx_close(struct file *file)
 {
        struct zr364xx_camera *cam;
        struct usb_device *udev;
-       int i, err;
+       int i;
 
        DBG("%s\n", __func__);
        cam = video_drvdata(file);
 
-       if (!cam)
-               return -ENODEV;
-
-       mutex_lock(&cam->open_lock);
+       mutex_lock(&cam->lock);
        udev = cam->udev;
 
-       /* turn off stream */
-       if (res_check(cam)) {
+       if (file->private_data == cam->owner) {
+               /* turn off stream */
                if (cam->b_acquire)
                        zr364xx_stop_acquire(cam);
                videobuf_streamoff(&cam->vb_vidq);
-               res_free(cam);
-       }
-
-       cam->users--;
-       file->private_data = NULL;
 
-       for (i = 0; i < 2; i++) {
-               err =
-                   send_control_msg(udev, 1, init[cam->method][i].value,
-                                    0, init[cam->method][i].bytes,
-                                    init[cam->method][i].size);
-               if (err < 0) {
-                       dev_err(&udev->dev, "error during release sequence\n");
-                       goto out;
+               for (i = 0; i < 2; i++) {
+                       send_control_msg(udev, 1, init[cam->method][i].value,
+                                       0, init[cam->method][i].bytes,
+                                       init[cam->method][i].size);
                }
+               cam->owner = NULL;
        }
 
        /* Added some delay here, since opening/closing the camera quickly,
         * like Ekiga does during its startup, can crash the webcam
         */
        mdelay(100);
-       err = 0;
-
-out:
-       mutex_unlock(&cam->open_lock);
-
-       return err;
+       mutex_unlock(&cam->lock);
+       return v4l2_fh_release(file);
 }
 
 
@@ -1424,21 +1300,24 @@ static unsigned int zr364xx_poll(struct file *file,
 {
        struct zr364xx_camera *cam = video_drvdata(file);
        struct videobuf_queue *q = &cam->vb_vidq;
-       _DBG("%s\n", __func__);
+       unsigned res = v4l2_ctrl_poll(file, wait);
 
-       if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return POLLERR;
+       _DBG("%s\n", __func__);
 
-       return videobuf_poll_stream(file, q, wait);
+       return res | videobuf_poll_stream(file, q, wait);
 }
 
+static const struct v4l2_ctrl_ops zr364xx_ctrl_ops = {
+       .s_ctrl = zr364xx_s_ctrl,
+};
+
 static const struct v4l2_file_operations zr364xx_fops = {
        .owner = THIS_MODULE,
        .open = zr364xx_open,
-       .release = zr364xx_release,
+       .release = zr364xx_close,
        .read = zr364xx_read,
        .mmap = zr364xx_mmap,
-       .ioctl = video_ioctl2,
+       .unlocked_ioctl = video_ioctl2,
        .poll = zr364xx_poll,
 };
 
@@ -1453,20 +1332,20 @@ static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = {
        .vidioc_s_input         = zr364xx_vidioc_s_input,
        .vidioc_streamon        = zr364xx_vidioc_streamon,
        .vidioc_streamoff       = zr364xx_vidioc_streamoff,
-       .vidioc_queryctrl       = zr364xx_vidioc_queryctrl,
-       .vidioc_g_ctrl          = zr364xx_vidioc_g_ctrl,
-       .vidioc_s_ctrl          = zr364xx_vidioc_s_ctrl,
        .vidioc_reqbufs         = zr364xx_vidioc_reqbufs,
        .vidioc_querybuf        = zr364xx_vidioc_querybuf,
        .vidioc_qbuf            = zr364xx_vidioc_qbuf,
        .vidioc_dqbuf           = zr364xx_vidioc_dqbuf,
+       .vidioc_log_status      = v4l2_ctrl_log_status,
+       .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
 };
 
 static struct video_device zr364xx_template = {
        .name = DRIVER_DESC,
        .fops = &zr364xx_fops,
        .ioctl_ops = &zr364xx_ioctl_ops,
-       .release = video_device_release,
+       .release = video_device_release_empty,
 };
 
 
@@ -1540,6 +1419,7 @@ static int zr364xx_probe(struct usb_interface *intf,
        struct zr364xx_camera *cam = NULL;
        struct usb_host_interface *iface_desc;
        struct usb_endpoint_descriptor *endpoint;
+       struct v4l2_ctrl_handler *hdl;
        int err;
        int i;
 
@@ -1555,21 +1435,34 @@ static int zr364xx_probe(struct usb_interface *intf,
                dev_err(&udev->dev, "cam: out of memory !\n");
                return -ENOMEM;
        }
-       /* save the init method used by this camera */
-       cam->method = id->driver_info;
 
-       cam->vdev = video_device_alloc();
-       if (cam->vdev == NULL) {
-               dev_err(&udev->dev, "cam->vdev: out of memory !\n");
+       cam->v4l2_dev.release = zr364xx_release;
+       err = v4l2_device_register(&intf->dev, &cam->v4l2_dev);
+       if (err < 0) {
+               dev_err(&udev->dev, "couldn't register v4l2_device\n");
                kfree(cam);
-               cam = NULL;
-               return -ENOMEM;
+               return err;
        }
-       memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template));
-       cam->vdev->parent = &intf->dev;
-       video_set_drvdata(cam->vdev, cam);
+       hdl = &cam->ctrl_handler;
+       v4l2_ctrl_handler_init(hdl, 1);
+       v4l2_ctrl_new_std(hdl, &zr364xx_ctrl_ops,
+                         V4L2_CID_BRIGHTNESS, 0, 127, 1, 64);
+       if (hdl->error) {
+               err = hdl->error;
+               dev_err(&udev->dev, "couldn't register control\n");
+               goto fail;
+       }
+       /* save the init method used by this camera */
+       cam->method = id->driver_info;
+       mutex_init(&cam->lock);
+       cam->vdev = zr364xx_template;
+       cam->vdev.lock = &cam->lock;
+       cam->vdev.v4l2_dev = &cam->v4l2_dev;
+       cam->vdev.ctrl_handler = &cam->ctrl_handler;
+       set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags);
+       video_set_drvdata(&cam->vdev, cam);
        if (debug)
-               cam->vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
+               cam->vdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
 
        cam->udev = udev;
 
@@ -1615,11 +1508,7 @@ static int zr364xx_probe(struct usb_interface *intf,
        header2[439] = cam->width / 256;
        header2[440] = cam->width % 256;
 
-       cam->users = 0;
        cam->nb = 0;
-       cam->mode.brightness = 64;
-       mutex_init(&cam->lock);
-       mutex_init(&cam->open_lock);
 
        DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf);
 
@@ -1635,52 +1524,100 @@ static int zr364xx_probe(struct usb_interface *intf,
        }
 
        if (!cam->read_endpoint) {
+               err = -ENOMEM;
                dev_err(&intf->dev, "Could not find bulk-in endpoint\n");
-               video_device_release(cam->vdev);
-               kfree(cam);
-               cam = NULL;
-               return -ENOMEM;
+               goto fail;
        }
 
        /* v4l */
        INIT_LIST_HEAD(&cam->vidq.active);
        cam->vidq.cam = cam;
-       err = video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1);
-       if (err) {
-               dev_err(&udev->dev, "video_register_device failed\n");
-               video_device_release(cam->vdev);
-               kfree(cam);
-               cam = NULL;
-               return err;
-       }
 
        usb_set_intfdata(intf, cam);
 
        /* load zr364xx board specific */
        err = zr364xx_board_init(cam);
-       if (err) {
-               spin_lock_init(&cam->slock);
-               return err;
-       }
+       if (!err)
+               err = v4l2_ctrl_handler_setup(hdl);
+       if (err)
+               goto fail;
 
        spin_lock_init(&cam->slock);
 
+       cam->fmt = formats;
+
+       videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops,
+                                   NULL, &cam->slock,
+                                   V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                                   V4L2_FIELD_NONE,
+                                   sizeof(struct zr364xx_buffer), cam, &cam->lock);
+
+       err = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
+       if (err) {
+               dev_err(&udev->dev, "video_register_device failed\n");
+               goto fail;
+       }
+
        dev_info(&udev->dev, DRIVER_DESC " controlling device %s\n",
-                video_device_node_name(cam->vdev));
+                video_device_node_name(&cam->vdev));
        return 0;
+
+fail:
+       v4l2_ctrl_handler_free(hdl);
+       v4l2_device_unregister(&cam->v4l2_dev);
+       kfree(cam);
+       return err;
 }
 
 
 static void zr364xx_disconnect(struct usb_interface *intf)
 {
        struct zr364xx_camera *cam = usb_get_intfdata(intf);
-       videobuf_mmap_free(&cam->vb_vidq);
+
+       mutex_lock(&cam->lock);
        usb_set_intfdata(intf, NULL);
        dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n");
-       zr364xx_destroy(cam);
+       video_unregister_device(&cam->vdev);
+       v4l2_device_disconnect(&cam->v4l2_dev);
+
+       /* stops the read pipe if it is running */
+       if (cam->b_acquire)
+               zr364xx_stop_acquire(cam);
+
+       zr364xx_stop_readpipe(cam);
+       mutex_unlock(&cam->lock);
+       v4l2_device_put(&cam->v4l2_dev);
 }
 
 
+#ifdef CONFIG_PM
+static int zr364xx_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct zr364xx_camera *cam = usb_get_intfdata(intf);
+
+       cam->was_streaming = cam->b_acquire;
+       if (!cam->was_streaming)
+               return 0;
+       zr364xx_stop_acquire(cam);
+       zr364xx_stop_readpipe(cam);
+       return 0;
+}
+
+static int zr364xx_resume(struct usb_interface *intf)
+{
+       struct zr364xx_camera *cam = usb_get_intfdata(intf);
+       int res;
+
+       if (!cam->was_streaming)
+               return 0;
+
+       zr364xx_start_readpipe(cam);
+       res = zr364xx_prepare(cam);
+       if (!res)
+               zr364xx_start_acquire(cam);
+       return res;
+}
+#endif
 
 /**********************/
 /* Module integration */
@@ -1690,6 +1627,11 @@ static struct usb_driver zr364xx_driver = {
        .name = "zr364xx",
        .probe = zr364xx_probe,
        .disconnect = zr364xx_disconnect,
+#ifdef CONFIG_PM
+       .suspend = zr364xx_suspend,
+       .resume = zr364xx_resume,
+       .reset_resume = zr364xx_resume,
+#endif
        .id_table = device_table
 };
 
index 098de2b35784a44cc4d5b9baeb08898a6a5ed805..9a49c243a6ac59c78c98cd9de021429d44aa8908 100644 (file)
@@ -188,6 +188,13 @@ static int i2o_cfg_parms(unsigned long arg, unsigned int type)
        if (!dev)
                return -ENXIO;
 
+       /*
+        * Stop users being able to try and allocate arbitary amounts
+        * of DMA space. 64K is way more than sufficient for this.
+        */
+       if (kcmd.oplen > 65536)
+               return -EMSGSIZE;
+
        ops = memdup_user(kcmd.opbuf, kcmd.oplen);
        if (IS_ERR(ops))
                return PTR_ERR(ops);
index 506c36f6e1db181d74ecf24809b82ef6c2abcb61..8001aa6bfb4809caf35c25ee758330dc3d08751a 100644 (file)
@@ -255,9 +255,8 @@ static char *scsi_devices[] = {
        "Array Controller Device"
 };
 
-static char *chtostr(u8 * chars, int n)
+static char *chtostr(char *tmp, u8 *chars, int n)
 {
-       char tmp[256];
        tmp[0] = 0;
        return strncat(tmp, (char *)chars, n);
 }
@@ -791,6 +790,7 @@ static int i2o_seq_show_ddm_table(struct seq_file *seq, void *v)
        } *result;
 
        i2o_exec_execute_ddm_table ddm_table;
+       char tmp[28 + 1];
 
        result = kmalloc(sizeof(*result), GFP_KERNEL);
        if (!result)
@@ -826,7 +826,7 @@ static int i2o_seq_show_ddm_table(struct seq_file *seq, void *v)
                seq_printf(seq, "%-#7x", ddm_table.i2o_vendor_id);
                seq_printf(seq, "%-#8x", ddm_table.module_id);
                seq_printf(seq, "%-29s",
-                          chtostr(ddm_table.module_name_version, 28));
+                          chtostr(tmp, ddm_table.module_name_version, 28));
                seq_printf(seq, "%9d  ", ddm_table.data_size);
                seq_printf(seq, "%8d", ddm_table.code_size);
 
@@ -893,6 +893,7 @@ static int i2o_seq_show_drivers_stored(struct seq_file *seq, void *v)
 
        i2o_driver_result_table *result;
        i2o_driver_store_table *dst;
+       char tmp[28 + 1];
 
        result = kmalloc(sizeof(i2o_driver_result_table), GFP_KERNEL);
        if (result == NULL)
@@ -927,8 +928,9 @@ static int i2o_seq_show_drivers_stored(struct seq_file *seq, void *v)
 
                seq_printf(seq, "%-#7x", dst->i2o_vendor_id);
                seq_printf(seq, "%-#8x", dst->module_id);
-               seq_printf(seq, "%-29s", chtostr(dst->module_name_version, 28));
-               seq_printf(seq, "%-9s", chtostr(dst->date, 8));
+               seq_printf(seq, "%-29s",
+                          chtostr(tmp, dst->module_name_version, 28));
+               seq_printf(seq, "%-9s", chtostr(tmp, dst->date, 8));
                seq_printf(seq, "%8d ", dst->module_size);
                seq_printf(seq, "%8d ", dst->mpb_size);
                seq_printf(seq, "0x%04x", dst->module_flags);
@@ -1248,6 +1250,7 @@ static int i2o_seq_show_dev_identity(struct seq_file *seq, void *v)
        // == (allow) 512d bytes (max)
        static u16 *work16 = (u16 *) work32;
        int token;
+       char tmp[16 + 1];
 
        token = i2o_parm_field_get(d, 0xF100, -1, &work32, sizeof(work32));
 
@@ -1260,13 +1263,13 @@ static int i2o_seq_show_dev_identity(struct seq_file *seq, void *v)
        seq_printf(seq, "Owner TID     : %0#5x\n", work16[2]);
        seq_printf(seq, "Parent TID    : %0#5x\n", work16[3]);
        seq_printf(seq, "Vendor info   : %s\n",
-                  chtostr((u8 *) (work32 + 2), 16));
+                  chtostr(tmp, (u8 *) (work32 + 2), 16));
        seq_printf(seq, "Product info  : %s\n",
-                  chtostr((u8 *) (work32 + 6), 16));
+                  chtostr(tmp, (u8 *) (work32 + 6), 16));
        seq_printf(seq, "Description   : %s\n",
-                  chtostr((u8 *) (work32 + 10), 16));
+                  chtostr(tmp, (u8 *) (work32 + 10), 16));
        seq_printf(seq, "Product rev.  : %s\n",
-                  chtostr((u8 *) (work32 + 14), 8));
+                  chtostr(tmp, (u8 *) (work32 + 14), 8));
 
        seq_printf(seq, "Serial number : ");
        print_serial_number(seq, (u8 *) (work32 + 16),
@@ -1303,6 +1306,8 @@ static int i2o_seq_show_ddm_identity(struct seq_file *seq, void *v)
                u8 pad[256];    // allow up to 256 byte (max) serial number
        } result;
 
+       char tmp[24 + 1];
+
        token = i2o_parm_field_get(d, 0xF101, -1, &result, sizeof(result));
 
        if (token < 0) {
@@ -1312,9 +1317,9 @@ static int i2o_seq_show_ddm_identity(struct seq_file *seq, void *v)
 
        seq_printf(seq, "Registering DDM TID : 0x%03x\n", result.ddm_tid);
        seq_printf(seq, "Module name         : %s\n",
-                  chtostr(result.module_name, 24));
+                  chtostr(tmp, result.module_name, 24));
        seq_printf(seq, "Module revision     : %s\n",
-                  chtostr(result.module_rev, 8));
+                  chtostr(tmp, result.module_rev, 8));
 
        seq_printf(seq, "Serial number       : ");
        print_serial_number(seq, result.serial_number, sizeof(result) - 36);
@@ -1338,6 +1343,8 @@ static int i2o_seq_show_uinfo(struct seq_file *seq, void *v)
                u8 instance_number[4];
        } result;
 
+       char tmp[64 + 1];
+
        token = i2o_parm_field_get(d, 0xF102, -1, &result, sizeof(result));
 
        if (token < 0) {
@@ -1346,13 +1353,13 @@ static int i2o_seq_show_uinfo(struct seq_file *seq, void *v)
        }
 
        seq_printf(seq, "Device name     : %s\n",
-                  chtostr(result.device_name, 64));
+                  chtostr(tmp, result.device_name, 64));
        seq_printf(seq, "Service name    : %s\n",
-                  chtostr(result.service_name, 64));
+                  chtostr(tmp, result.service_name, 64));
        seq_printf(seq, "Physical name   : %s\n",
-                  chtostr(result.physical_location, 64));
+                  chtostr(tmp, result.physical_location, 64));
        seq_printf(seq, "Instance number : %s\n",
-                  chtostr(result.instance_number, 4));
+                  chtostr(tmp, result.instance_number, 4));
 
        return 0;
 }
diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c
new file mode 100644 (file)
index 0000000..b67a301
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * Base driver for Marvell 88PM800
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ * Joseph(Yossi) Hanin <yhanin@marvell.com>
+ * Qiao Zhou <zhouqiao@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/slab.h>
+
+#define PM800_CHIP_ID                  (0x00)
+
+/* Interrupt Registers */
+#define PM800_INT_STATUS1              (0x05)
+#define PM800_ONKEY_INT_STS1           (1 << 0)
+#define PM800_EXTON_INT_STS1           (1 << 1)
+#define PM800_CHG_INT_STS1                     (1 << 2)
+#define PM800_BAT_INT_STS1                     (1 << 3)
+#define PM800_RTC_INT_STS1                     (1 << 4)
+#define PM800_CLASSD_OC_INT_STS1       (1 << 5)
+
+#define PM800_INT_STATUS2              (0x06)
+#define PM800_VBAT_INT_STS2            (1 << 0)
+#define PM800_VSYS_INT_STS2            (1 << 1)
+#define PM800_VCHG_INT_STS2            (1 << 2)
+#define PM800_TINT_INT_STS2            (1 << 3)
+#define PM800_GPADC0_INT_STS2  (1 << 4)
+#define PM800_TBAT_INT_STS2            (1 << 5)
+#define PM800_GPADC2_INT_STS2  (1 << 6)
+#define PM800_GPADC3_INT_STS2  (1 << 7)
+
+#define PM800_INT_STATUS3              (0x07)
+
+#define PM800_INT_STATUS4              (0x08)
+#define PM800_GPIO0_INT_STS4           (1 << 0)
+#define PM800_GPIO1_INT_STS4           (1 << 1)
+#define PM800_GPIO2_INT_STS4           (1 << 2)
+#define PM800_GPIO3_INT_STS4           (1 << 3)
+#define PM800_GPIO4_INT_STS4           (1 << 4)
+
+#define PM800_INT_ENA_1                (0x09)
+#define PM800_ONKEY_INT_ENA1           (1 << 0)
+#define PM800_EXTON_INT_ENA1           (1 << 1)
+#define PM800_CHG_INT_ENA1                     (1 << 2)
+#define PM800_BAT_INT_ENA1                     (1 << 3)
+#define PM800_RTC_INT_ENA1                     (1 << 4)
+#define PM800_CLASSD_OC_INT_ENA1       (1 << 5)
+
+#define PM800_INT_ENA_2                (0x0A)
+#define PM800_VBAT_INT_ENA2            (1 << 0)
+#define PM800_VSYS_INT_ENA2            (1 << 1)
+#define PM800_VCHG_INT_ENA2            (1 << 2)
+#define PM800_TINT_INT_ENA2            (1 << 3)
+
+#define PM800_INT_ENA_3                (0x0B)
+#define PM800_GPADC0_INT_ENA3          (1 << 0)
+#define PM800_GPADC1_INT_ENA3          (1 << 1)
+#define PM800_GPADC2_INT_ENA3          (1 << 2)
+#define PM800_GPADC3_INT_ENA3          (1 << 3)
+#define PM800_GPADC4_INT_ENA3          (1 << 4)
+
+#define PM800_INT_ENA_4                (0x0C)
+#define PM800_GPIO0_INT_ENA4           (1 << 0)
+#define PM800_GPIO1_INT_ENA4           (1 << 1)
+#define PM800_GPIO2_INT_ENA4           (1 << 2)
+#define PM800_GPIO3_INT_ENA4           (1 << 3)
+#define PM800_GPIO4_INT_ENA4           (1 << 4)
+
+/* number of INT_ENA & INT_STATUS regs */
+#define PM800_INT_REG_NUM                      (4)
+
+/* Interrupt Number in 88PM800 */
+enum {
+       PM800_IRQ_ONKEY,        /*EN1b0 *//*0 */
+       PM800_IRQ_EXTON,        /*EN1b1 */
+       PM800_IRQ_CHG,          /*EN1b2 */
+       PM800_IRQ_BAT,          /*EN1b3 */
+       PM800_IRQ_RTC,          /*EN1b4 */
+       PM800_IRQ_CLASSD,       /*EN1b5 *//*5 */
+       PM800_IRQ_VBAT,         /*EN2b0 */
+       PM800_IRQ_VSYS,         /*EN2b1 */
+       PM800_IRQ_VCHG,         /*EN2b2 */
+       PM800_IRQ_TINT,         /*EN2b3 */
+       PM800_IRQ_GPADC0,       /*EN3b0 *//*10 */
+       PM800_IRQ_GPADC1,       /*EN3b1 */
+       PM800_IRQ_GPADC2,       /*EN3b2 */
+       PM800_IRQ_GPADC3,       /*EN3b3 */
+       PM800_IRQ_GPADC4,       /*EN3b4 */
+       PM800_IRQ_GPIO0,        /*EN4b0 *//*15 */
+       PM800_IRQ_GPIO1,        /*EN4b1 */
+       PM800_IRQ_GPIO2,        /*EN4b2 */
+       PM800_IRQ_GPIO3,        /*EN4b3 */
+       PM800_IRQ_GPIO4,        /*EN4b4 *//*19 */
+       PM800_MAX_IRQ,
+};
+
+enum {
+       /* Procida */
+       PM800_CHIP_A0  = 0x60,
+       PM800_CHIP_A1  = 0x61,
+       PM800_CHIP_B0  = 0x62,
+       PM800_CHIP_C0  = 0x63,
+       PM800_CHIP_END = PM800_CHIP_C0,
+
+       /* Make sure to update this to the last stepping */
+       PM8XXX_CHIP_END = PM800_CHIP_END
+};
+
+static const struct i2c_device_id pm80x_id_table[] = {
+       {"88PM800", CHIP_PM800},
+       {} /* NULL terminated */
+};
+MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
+
+static struct resource rtc_resources[] = {
+       {
+        .name = "88pm80x-rtc",
+        .start = PM800_IRQ_RTC,
+        .end = PM800_IRQ_RTC,
+        .flags = IORESOURCE_IRQ,
+        },
+};
+
+static struct mfd_cell rtc_devs[] = {
+       {
+        .name = "88pm80x-rtc",
+        .num_resources = ARRAY_SIZE(rtc_resources),
+        .resources = &rtc_resources[0],
+        .id = -1,
+        },
+};
+
+static struct resource onkey_resources[] = {
+       {
+        .name = "88pm80x-onkey",
+        .start = PM800_IRQ_ONKEY,
+        .end = PM800_IRQ_ONKEY,
+        .flags = IORESOURCE_IRQ,
+        },
+};
+
+static struct mfd_cell onkey_devs[] = {
+       {
+        .name = "88pm80x-onkey",
+        .num_resources = 1,
+        .resources = &onkey_resources[0],
+        .id = -1,
+        },
+};
+
+static const struct regmap_irq pm800_irqs[] = {
+       /* INT0 */
+       [PM800_IRQ_ONKEY] = {
+               .mask = PM800_ONKEY_INT_ENA1,
+       },
+       [PM800_IRQ_EXTON] = {
+               .mask = PM800_EXTON_INT_ENA1,
+       },
+       [PM800_IRQ_CHG] = {
+               .mask = PM800_CHG_INT_ENA1,
+       },
+       [PM800_IRQ_BAT] = {
+               .mask = PM800_BAT_INT_ENA1,
+       },
+       [PM800_IRQ_RTC] = {
+               .mask = PM800_RTC_INT_ENA1,
+       },
+       [PM800_IRQ_CLASSD] = {
+               .mask = PM800_CLASSD_OC_INT_ENA1,
+       },
+       /* INT1 */
+       [PM800_IRQ_VBAT] = {
+               .reg_offset = 1,
+               .mask = PM800_VBAT_INT_ENA2,
+       },
+       [PM800_IRQ_VSYS] = {
+               .reg_offset = 1,
+               .mask = PM800_VSYS_INT_ENA2,
+       },
+       [PM800_IRQ_VCHG] = {
+               .reg_offset = 1,
+               .mask = PM800_VCHG_INT_ENA2,
+       },
+       [PM800_IRQ_TINT] = {
+               .reg_offset = 1,
+               .mask = PM800_TINT_INT_ENA2,
+       },
+       /* INT2 */
+       [PM800_IRQ_GPADC0] = {
+               .reg_offset = 2,
+               .mask = PM800_GPADC0_INT_ENA3,
+       },
+       [PM800_IRQ_GPADC1] = {
+               .reg_offset = 2,
+               .mask = PM800_GPADC1_INT_ENA3,
+       },
+       [PM800_IRQ_GPADC2] = {
+               .reg_offset = 2,
+               .mask = PM800_GPADC2_INT_ENA3,
+       },
+       [PM800_IRQ_GPADC3] = {
+               .reg_offset = 2,
+               .mask = PM800_GPADC3_INT_ENA3,
+       },
+       [PM800_IRQ_GPADC4] = {
+               .reg_offset = 2,
+               .mask = PM800_GPADC4_INT_ENA3,
+       },
+       /* INT3 */
+       [PM800_IRQ_GPIO0] = {
+               .reg_offset = 3,
+               .mask = PM800_GPIO0_INT_ENA4,
+       },
+       [PM800_IRQ_GPIO1] = {
+               .reg_offset = 3,
+               .mask = PM800_GPIO1_INT_ENA4,
+       },
+       [PM800_IRQ_GPIO2] = {
+               .reg_offset = 3,
+               .mask = PM800_GPIO2_INT_ENA4,
+       },
+       [PM800_IRQ_GPIO3] = {
+               .reg_offset = 3,
+               .mask = PM800_GPIO3_INT_ENA4,
+       },
+       [PM800_IRQ_GPIO4] = {
+               .reg_offset = 3,
+               .mask = PM800_GPIO4_INT_ENA4,
+       },
+};
+
+static int __devinit device_gpadc_init(struct pm80x_chip *chip,
+                                      struct pm80x_platform_data *pdata)
+{
+       struct pm80x_subchip *subchip = chip->subchip;
+       struct regmap *map = subchip->regmap_gpadc;
+       int data = 0, mask = 0, ret = 0;
+
+       if (!map) {
+               dev_warn(chip->dev,
+                        "Warning: gpadc regmap is not available!\n");
+               return -EINVAL;
+       }
+       /*
+        * initialize GPADC without activating it turn on GPADC
+        * measurments
+        */
+       ret = regmap_update_bits(map,
+                                PM800_GPADC_MISC_CONFIG2,
+                                PM800_GPADC_MISC_GPFSM_EN,
+                                PM800_GPADC_MISC_GPFSM_EN);
+       if (ret < 0)
+               goto out;
+       /*
+        * This function configures the ADC as requires for
+        * CP implementation.CP does not "own" the ADC configuration
+        * registers and relies on AP.
+        * Reason: enable automatic ADC measurements needed
+        * for CP to get VBAT and RF temperature readings.
+        */
+       ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN1,
+                                PM800_MEAS_EN1_VBAT, PM800_MEAS_EN1_VBAT);
+       if (ret < 0)
+               goto out;
+       ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN2,
+                                (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN),
+                                (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN));
+       if (ret < 0)
+               goto out;
+
+       /*
+        * the defult of PM800 is GPADC operates at 100Ks/s rate
+        * and Number of GPADC slots with active current bias prior
+        * to GPADC sampling = 1 slot for all GPADCs set for
+        * Temprature mesurmants
+        */
+       mask = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 |
+               PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3);
+
+       if (pdata && (pdata->batt_det == 0))
+               data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 |
+                       PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3);
+       else
+               data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN2 |
+                       PM800_GPADC_GP_BIAS_EN3);
+
+       ret = regmap_update_bits(map, PM800_GP_BIAS_ENA1, mask, data);
+       if (ret < 0)
+               goto out;
+
+       dev_info(chip->dev, "pm800 device_gpadc_init: Done\n");
+       return 0;
+
+out:
+       dev_info(chip->dev, "pm800 device_gpadc_init: Failed!\n");
+       return ret;
+}
+
+static int __devinit device_irq_init_800(struct pm80x_chip *chip)
+{
+       struct regmap *map = chip->regmap;
+       unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+       int data, mask, ret = -EINVAL;
+
+       if (!map || !chip->irq) {
+               dev_err(chip->dev, "incorrect parameters\n");
+               return -EINVAL;
+       }
+
+       /*
+        * irq_mode defines the way of clearing interrupt. it's read-clear by
+        * default.
+        */
+       mask =
+           PM800_WAKEUP2_INV_INT | PM800_WAKEUP2_INT_CLEAR |
+           PM800_WAKEUP2_INT_MASK;
+
+       data = PM800_WAKEUP2_INT_CLEAR;
+       ret = regmap_update_bits(map, PM800_WAKEUP2, mask, data);
+
+       if (ret < 0)
+               goto out;
+
+       ret =
+           regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1,
+                               chip->regmap_irq_chip, &chip->irq_data);
+
+out:
+       return ret;
+}
+
+static void device_irq_exit_800(struct pm80x_chip *chip)
+{
+       regmap_del_irq_chip(chip->irq, chip->irq_data);
+}
+
+static struct regmap_irq_chip pm800_irq_chip = {
+       .name = "88pm800",
+       .irqs = pm800_irqs,
+       .num_irqs = ARRAY_SIZE(pm800_irqs),
+
+       .num_regs = 4,
+       .status_base = PM800_INT_STATUS1,
+       .mask_base = PM800_INT_ENA_1,
+       .ack_base = PM800_INT_STATUS1,
+};
+
+static int pm800_pages_init(struct pm80x_chip *chip)
+{
+       struct pm80x_subchip *subchip;
+       struct i2c_client *client = chip->client;
+
+       subchip = chip->subchip;
+       /* PM800 block power: i2c addr 0x31 */
+       if (subchip->power_page_addr) {
+               subchip->power_page =
+                   i2c_new_dummy(client->adapter, subchip->power_page_addr);
+               subchip->regmap_power =
+                   devm_regmap_init_i2c(subchip->power_page,
+                                        &pm80x_regmap_config);
+               i2c_set_clientdata(subchip->power_page, chip);
+       } else
+               dev_info(chip->dev,
+                        "PM800 block power 0x31: No power_page_addr\n");
+
+       /* PM800 block GPADC: i2c addr 0x32 */
+       if (subchip->gpadc_page_addr) {
+               subchip->gpadc_page = i2c_new_dummy(client->adapter,
+                                                   subchip->gpadc_page_addr);
+               subchip->regmap_gpadc =
+                   devm_regmap_init_i2c(subchip->gpadc_page,
+                                        &pm80x_regmap_config);
+               i2c_set_clientdata(subchip->gpadc_page, chip);
+       } else
+               dev_info(chip->dev,
+                        "PM800 block GPADC 0x32: No gpadc_page_addr\n");
+
+       return 0;
+}
+
+static void pm800_pages_exit(struct pm80x_chip *chip)
+{
+       struct pm80x_subchip *subchip;
+
+       regmap_exit(chip->regmap);
+       i2c_unregister_device(chip->client);
+
+       subchip = chip->subchip;
+       if (subchip->power_page) {
+               regmap_exit(subchip->regmap_power);
+               i2c_unregister_device(subchip->power_page);
+       }
+       if (subchip->gpadc_page) {
+               regmap_exit(subchip->regmap_gpadc);
+               i2c_unregister_device(subchip->gpadc_page);
+       }
+}
+
+static int __devinit device_800_init(struct pm80x_chip *chip,
+                                    struct pm80x_platform_data *pdata)
+{
+       int ret, pmic_id;
+       unsigned int val;
+
+       ret = regmap_read(chip->regmap, PM800_CHIP_ID, &val);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
+               goto out;
+       }
+
+       pmic_id = val & PM80X_VERSION_MASK;
+
+       if ((pmic_id >= PM800_CHIP_A0) && (pmic_id <= PM800_CHIP_END)) {
+               chip->version = val;
+               dev_info(chip->dev,
+                        "88PM80x:Marvell 88PM800 (ID:0x%x) detected\n", val);
+       } else {
+               dev_err(chip->dev,
+                       "Failed to detect Marvell 88PM800:ChipID[0x%x]\n", val);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /*
+        * alarm wake up bit will be clear in device_irq_init(),
+        * read before that
+        */
+       ret = regmap_read(chip->regmap, PM800_RTC_CONTROL, &val);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to read RTC register: %d\n", ret);
+               goto out;
+       }
+       if (val & PM800_ALARM_WAKEUP) {
+               if (pdata && pdata->rtc)
+                       pdata->rtc->rtc_wakeup = 1;
+       }
+
+       ret = device_gpadc_init(chip, pdata);
+       if (ret < 0) {
+               dev_err(chip->dev, "[%s]Failed to init gpadc\n", __func__);
+               goto out;
+       }
+
+       chip->regmap_irq_chip = &pm800_irq_chip;
+
+       ret = device_irq_init_800(chip);
+       if (ret < 0) {
+               dev_err(chip->dev, "[%s]Failed to init pm800 irq\n", __func__);
+               goto out;
+       }
+
+       ret =
+           mfd_add_devices(chip->dev, 0, &onkey_devs[0],
+                           ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to add onkey subdev\n");
+               goto out_dev;
+       } else
+               dev_info(chip->dev, "[%s]:Added mfd onkey_devs\n", __func__);
+
+       if (pdata && pdata->rtc) {
+               rtc_devs[0].platform_data = pdata->rtc;
+               rtc_devs[0].pdata_size = sizeof(struct pm80x_rtc_pdata);
+               ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
+                                     ARRAY_SIZE(rtc_devs), NULL, 0);
+               if (ret < 0) {
+                       dev_err(chip->dev, "Failed to add rtc subdev\n");
+                       goto out_dev;
+               } else
+                       dev_info(chip->dev,
+                                "[%s]:Added mfd rtc_devs\n", __func__);
+       }
+
+       return 0;
+out_dev:
+       mfd_remove_devices(chip->dev);
+       device_irq_exit_800(chip);
+out:
+       return ret;
+}
+
+static int __devinit pm800_probe(struct i2c_client *client,
+                                const struct i2c_device_id *id)
+{
+       int ret = 0;
+       struct pm80x_chip *chip;
+       struct pm80x_platform_data *pdata = client->dev.platform_data;
+       struct pm80x_subchip *subchip;
+
+       ret = pm80x_init(client, id);
+       if (ret) {
+               dev_err(&client->dev, "pm800_init fail\n");
+               goto out_init;
+       }
+
+       chip = i2c_get_clientdata(client);
+
+       /* init subchip for PM800 */
+       subchip =
+           devm_kzalloc(&client->dev, sizeof(struct pm80x_subchip),
+                        GFP_KERNEL);
+       if (!subchip) {
+               ret = -ENOMEM;
+               goto err_subchip_alloc;
+       }
+
+       subchip->power_page_addr = pdata->power_page_addr;
+       subchip->gpadc_page_addr = pdata->gpadc_page_addr;
+       chip->subchip = subchip;
+
+       ret = device_800_init(chip, pdata);
+       if (ret) {
+               dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id);
+               goto err_800_init;
+       }
+
+       ret = pm800_pages_init(chip);
+       if (ret) {
+               dev_err(&client->dev, "pm800_pages_init failed!\n");
+               goto err_page_init;
+       }
+
+       if (pdata->plat_config)
+               pdata->plat_config(chip, pdata);
+
+err_page_init:
+       mfd_remove_devices(chip->dev);
+       device_irq_exit_800(chip);
+err_800_init:
+       devm_kfree(&client->dev, subchip);
+err_subchip_alloc:
+       pm80x_deinit(client);
+out_init:
+       return ret;
+}
+
+static int __devexit pm800_remove(struct i2c_client *client)
+{
+       struct pm80x_chip *chip = i2c_get_clientdata(client);
+
+       mfd_remove_devices(chip->dev);
+       device_irq_exit_800(chip);
+
+       pm800_pages_exit(chip);
+       devm_kfree(&client->dev, chip->subchip);
+
+       pm80x_deinit(client);
+
+       return 0;
+}
+
+static struct i2c_driver pm800_driver = {
+       .driver = {
+               .name = "88PM80X",
+               .owner = THIS_MODULE,
+               .pm = &pm80x_pm_ops,
+               },
+       .probe = pm800_probe,
+       .remove = __devexit_p(pm800_remove),
+       .id_table = pm80x_id_table,
+};
+
+static int __init pm800_i2c_init(void)
+{
+       return i2c_add_driver(&pm800_driver);
+}
+subsys_initcall(pm800_i2c_init);
+
+static void __exit pm800_i2c_exit(void)
+{
+       i2c_del_driver(&pm800_driver);
+}
+module_exit(pm800_i2c_exit);
+
+MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM800");
+MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c
new file mode 100644 (file)
index 0000000..6146583
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * Base driver for Marvell 88PM805
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ * Joseph(Yossi) Hanin <yhanin@marvell.com>
+ * Qiao Zhou <zhouqiao@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#define PM805_CHIP_ID                  (0x00)
+
+static const struct i2c_device_id pm80x_id_table[] = {
+       {"88PM805", CHIP_PM805},
+       {} /* NULL terminated */
+};
+MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
+
+/* Interrupt Number in 88PM805 */
+enum {
+       PM805_IRQ_LDO_OFF,      /*0 */
+       PM805_IRQ_SRC_DPLL_LOCK,        /*1 */
+       PM805_IRQ_CLIP_FAULT,
+       PM805_IRQ_MIC_CONFLICT,
+       PM805_IRQ_HP2_SHRT,
+       PM805_IRQ_HP1_SHRT,     /*5 */
+       PM805_IRQ_FINE_PLL_FAULT,
+       PM805_IRQ_RAW_PLL_FAULT,
+       PM805_IRQ_VOLP_BTN_DET,
+       PM805_IRQ_VOLM_BTN_DET,
+       PM805_IRQ_SHRT_BTN_DET, /*10 */
+       PM805_IRQ_MIC_DET,      /*11 */
+
+       PM805_MAX_IRQ,
+};
+
+static struct resource codec_resources[] = {
+       {
+        /* Headset microphone insertion or removal */
+        .name = "micin",
+        .start = PM805_IRQ_MIC_DET,
+        .end = PM805_IRQ_MIC_DET,
+        .flags = IORESOURCE_IRQ,
+        },
+       {
+        /* Audio short HP1 */
+        .name = "audio-short1",
+        .start = PM805_IRQ_HP1_SHRT,
+        .end = PM805_IRQ_HP1_SHRT,
+        .flags = IORESOURCE_IRQ,
+        },
+       {
+        /* Audio short HP2 */
+        .name = "audio-short2",
+        .start = PM805_IRQ_HP2_SHRT,
+        .end = PM805_IRQ_HP2_SHRT,
+        .flags = IORESOURCE_IRQ,
+        },
+};
+
+static struct mfd_cell codec_devs[] = {
+       {
+        .name = "88pm80x-codec",
+        .num_resources = ARRAY_SIZE(codec_resources),
+        .resources = &codec_resources[0],
+        .id = -1,
+        },
+};
+
+static struct regmap_irq pm805_irqs[] = {
+       /* INT0 */
+       [PM805_IRQ_LDO_OFF] = {
+               .mask = PM805_INT1_HP1_SHRT,
+       },
+       [PM805_IRQ_SRC_DPLL_LOCK] = {
+               .mask = PM805_INT1_HP2_SHRT,
+       },
+       [PM805_IRQ_CLIP_FAULT] = {
+               .mask = PM805_INT1_MIC_CONFLICT,
+       },
+       [PM805_IRQ_MIC_CONFLICT] = {
+               .mask = PM805_INT1_CLIP_FAULT,
+       },
+       [PM805_IRQ_HP2_SHRT] = {
+               .mask = PM805_INT1_LDO_OFF,
+       },
+       [PM805_IRQ_HP1_SHRT] = {
+               .mask = PM805_INT1_SRC_DPLL_LOCK,
+       },
+       /* INT1 */
+       [PM805_IRQ_FINE_PLL_FAULT] = {
+               .reg_offset = 1,
+               .mask = PM805_INT2_MIC_DET,
+       },
+       [PM805_IRQ_RAW_PLL_FAULT] = {
+               .reg_offset = 1,
+               .mask = PM805_INT2_SHRT_BTN_DET,
+       },
+       [PM805_IRQ_VOLP_BTN_DET] = {
+               .reg_offset = 1,
+               .mask = PM805_INT2_VOLM_BTN_DET,
+       },
+       [PM805_IRQ_VOLM_BTN_DET] = {
+               .reg_offset = 1,
+               .mask = PM805_INT2_VOLP_BTN_DET,
+       },
+       [PM805_IRQ_SHRT_BTN_DET] = {
+               .reg_offset = 1,
+               .mask = PM805_INT2_RAW_PLL_FAULT,
+       },
+       [PM805_IRQ_MIC_DET] = {
+               .reg_offset = 1,
+               .mask = PM805_INT2_FINE_PLL_FAULT,
+       },
+};
+
+static int __devinit device_irq_init_805(struct pm80x_chip *chip)
+{
+       struct regmap *map = chip->regmap;
+       unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+       int data, mask, ret = -EINVAL;
+
+       if (!map || !chip->irq) {
+               dev_err(chip->dev, "incorrect parameters\n");
+               return -EINVAL;
+       }
+
+       /*
+        * irq_mode defines the way of clearing interrupt. it's read-clear by
+        * default.
+        */
+       mask =
+           PM805_STATUS0_INT_CLEAR | PM805_STATUS0_INV_INT |
+           PM800_STATUS0_INT_MASK;
+
+       data = PM805_STATUS0_INT_CLEAR;
+       ret = regmap_update_bits(map, PM805_INT_STATUS0, mask, data);
+       /*
+        * PM805_INT_STATUS is under 32K clock domain, so need to
+        * add proper delay before the next I2C register access.
+        */
+       msleep(1);
+
+       if (ret < 0)
+               goto out;
+
+       ret =
+           regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1,
+                               chip->regmap_irq_chip, &chip->irq_data);
+
+out:
+       return ret;
+}
+
+static void device_irq_exit_805(struct pm80x_chip *chip)
+{
+       regmap_del_irq_chip(chip->irq, chip->irq_data);
+}
+
+static struct regmap_irq_chip pm805_irq_chip = {
+       .name = "88pm805",
+       .irqs = pm805_irqs,
+       .num_irqs = ARRAY_SIZE(pm805_irqs),
+
+       .num_regs = 2,
+       .status_base = PM805_INT_STATUS1,
+       .mask_base = PM805_INT_MASK1,
+       .ack_base = PM805_INT_STATUS1,
+};
+
+static int __devinit device_805_init(struct pm80x_chip *chip)
+{
+       int ret = 0;
+       unsigned int val;
+       struct regmap *map = chip->regmap;
+
+       if (!map) {
+               dev_err(chip->dev, "regmap is invalid\n");
+               return -EINVAL;
+       }
+
+       ret = regmap_read(map, PM805_CHIP_ID, &val);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
+               goto out_irq_init;
+       }
+       chip->version = val;
+
+       chip->regmap_irq_chip = &pm805_irq_chip;
+
+       ret = device_irq_init_805(chip);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to init pm805 irq!\n");
+               goto out_irq_init;
+       }
+
+       ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
+                             ARRAY_SIZE(codec_devs), &codec_resources[0], 0);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to add codec subdev\n");
+               goto out_codec;
+       } else
+               dev_info(chip->dev, "[%s]:Added mfd codec_devs\n", __func__);
+
+       return 0;
+
+out_codec:
+       device_irq_exit_805(chip);
+out_irq_init:
+       return ret;
+}
+
+static int __devinit pm805_probe(struct i2c_client *client,
+                                const struct i2c_device_id *id)
+{
+       int ret = 0;
+       struct pm80x_chip *chip;
+       struct pm80x_platform_data *pdata = client->dev.platform_data;
+
+       ret = pm80x_init(client, id);
+       if (ret) {
+               dev_err(&client->dev, "pm805_init fail!\n");
+               goto out_init;
+       }
+
+       chip = i2c_get_clientdata(client);
+
+       ret = device_805_init(chip);
+       if (ret) {
+               dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id);
+               goto err_805_init;
+       }
+
+       if (pdata->plat_config)
+               pdata->plat_config(chip, pdata);
+
+err_805_init:
+       pm80x_deinit(client);
+out_init:
+       return ret;
+}
+
+static int __devexit pm805_remove(struct i2c_client *client)
+{
+       struct pm80x_chip *chip = i2c_get_clientdata(client);
+
+       mfd_remove_devices(chip->dev);
+       device_irq_exit_805(chip);
+
+       pm80x_deinit(client);
+
+       return 0;
+}
+
+static struct i2c_driver pm805_driver = {
+       .driver = {
+               .name = "88PM80X",
+               .owner = THIS_MODULE,
+               .pm = &pm80x_pm_ops,
+               },
+       .probe = pm805_probe,
+       .remove = __devexit_p(pm805_remove),
+       .id_table = pm80x_id_table,
+};
+
+static int __init pm805_i2c_init(void)
+{
+       return i2c_add_driver(&pm805_driver);
+}
+subsys_initcall(pm805_i2c_init);
+
+static void __exit pm805_i2c_exit(void)
+{
+       i2c_del_driver(&pm805_driver);
+}
+module_exit(pm805_i2c_exit);
+
+MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM805");
+MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c
new file mode 100644 (file)
index 0000000..cd0bf52
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * I2C driver for Marvell 88PM80x
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ * Joseph(Yossi) Hanin <yhanin@marvell.com>
+ * Qiao Zhou <zhouqiao@marvell.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/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/err.h>
+
+/*
+ * workaround: some registers needed by pm805 are defined in pm800, so
+ * need to use this global variable to maintain the relation between
+ * pm800 and pm805. would remove it after HW chip fixes the issue.
+ */
+static struct pm80x_chip *g_pm80x_chip;
+
+const struct regmap_config pm80x_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+};
+EXPORT_SYMBOL_GPL(pm80x_regmap_config);
+
+int __devinit pm80x_init(struct i2c_client *client,
+                                const struct i2c_device_id *id)
+{
+       struct pm80x_chip *chip;
+       struct regmap *map;
+       int ret = 0;
+
+       chip =
+           devm_kzalloc(&client->dev, sizeof(struct pm80x_chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       map = devm_regmap_init_i2c(client, &pm80x_regmap_config);
+       if (IS_ERR(map)) {
+               ret = PTR_ERR(map);
+               dev_err(&client->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               goto err_regmap_init;
+       }
+
+       chip->id = id->driver_data;
+       if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) {
+               ret = -EINVAL;
+               goto err_chip_id;
+       }
+
+       chip->client = client;
+       chip->regmap = map;
+
+       chip->irq = client->irq;
+
+       chip->dev = &client->dev;
+       dev_set_drvdata(chip->dev, chip);
+       i2c_set_clientdata(chip->client, chip);
+
+       device_init_wakeup(&client->dev, 1);
+
+       /*
+        * workaround: set g_pm80x_chip to the first probed chip. if the
+        * second chip is probed, just point to the companion to each
+        * other so that pm805 can access those specific register. would
+        * remove it after HW chip fixes the issue.
+        */
+       if (!g_pm80x_chip)
+               g_pm80x_chip = chip;
+       else {
+               chip->companion = g_pm80x_chip->client;
+               g_pm80x_chip->companion = chip->client;
+       }
+
+       return 0;
+
+err_chip_id:
+       regmap_exit(map);
+err_regmap_init:
+       devm_kfree(&client->dev, chip);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pm80x_init);
+
+int pm80x_deinit(struct i2c_client *client)
+{
+       struct pm80x_chip *chip = i2c_get_clientdata(client);
+
+       /*
+        * workaround: clear the dependency between pm800 and pm805.
+        * would remove it after HW chip fixes the issue.
+        */
+       if (g_pm80x_chip->companion)
+               g_pm80x_chip->companion = NULL;
+       else
+               g_pm80x_chip = NULL;
+
+       regmap_exit(chip->regmap);
+       devm_kfree(&client->dev, chip);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pm80x_deinit);
+
+#ifdef CONFIG_PM_SLEEP
+static int pm80x_suspend(struct device *dev)
+{
+       struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+       struct pm80x_chip *chip = i2c_get_clientdata(client);
+
+       if (chip && chip->wu_flag)
+               if (device_may_wakeup(chip->dev))
+                       enable_irq_wake(chip->irq);
+
+       return 0;
+}
+
+static int pm80x_resume(struct device *dev)
+{
+       struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+       struct pm80x_chip *chip = i2c_get_clientdata(client);
+
+       if (chip && chip->wu_flag)
+               if (device_may_wakeup(chip->dev))
+                       disable_irq_wake(chip->irq);
+
+       return 0;
+}
+#endif
+
+SIMPLE_DEV_PM_OPS(pm80x_pm_ops, pm80x_suspend, pm80x_resume);
+EXPORT_SYMBOL_GPL(pm80x_pm_ops);
+
+MODULE_DESCRIPTION("I2C Driver for Marvell 88PM80x");
+MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
+MODULE_LICENSE("GPL");
index 87bd5ba38d5b649db355f252d326fe28116041c0..d09918cf1b1556a74edb622e0174d3ceffdf7ca6 100644 (file)
@@ -90,6 +90,10 @@ static struct resource charger_resources[] __devinitdata = {
        {PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage",    IORESOURCE_IRQ,},
 };
 
+static struct resource preg_resources[] __devinitdata = {
+       {PM8606_ID_PREG,  PM8606_ID_PREG,  "preg",   IORESOURCE_IO,},
+};
+
 static struct resource rtc_resources[] __devinitdata = {
        {PM8607_IRQ_RTC, PM8607_IRQ_RTC, "rtc", IORESOURCE_IRQ,},
 };
@@ -142,9 +146,19 @@ static struct mfd_cell codec_devs[] = {
        {"88pm860x-codec", -1,},
 };
 
+static struct regulator_consumer_supply preg_supply[] = {
+       REGULATOR_SUPPLY("preg", "charger-manager"),
+};
+
+static struct regulator_init_data preg_init_data = {
+       .num_consumer_supplies  = ARRAY_SIZE(preg_supply),
+       .consumer_supplies      = &preg_supply[0],
+};
+
 static struct mfd_cell power_devs[] = {
        {"88pm860x-battery", -1,},
        {"88pm860x-charger", -1,},
+       {"88pm860x-preg",    -1,},
 };
 
 static struct mfd_cell rtc_devs[] = {
@@ -768,6 +782,15 @@ static void __devinit device_power_init(struct pm860x_chip *chip,
                              &charger_resources[0], chip->irq_base);
        if (ret < 0)
                dev_err(chip->dev, "Failed to add charger subdev\n");
+
+       power_devs[2].platform_data = &preg_init_data;
+       power_devs[2].pdata_size = sizeof(struct regulator_init_data);
+       power_devs[2].num_resources = ARRAY_SIZE(preg_resources);
+       power_devs[2].resources = &preg_resources[0],
+       ret = mfd_add_devices(chip->dev, 0, &power_devs[2], 1,
+                             &preg_resources[0], chip->irq_base);
+       if (ret < 0)
+               dev_err(chip->dev, "Failed to add preg subdev\n");
 }
 
 static void __devinit device_onkey_init(struct pm860x_chip *chip,
index 92144ed1ad469d8257eab6636c4d7f8cc4c65180..d1facef28a60d77bc4f3eb8264e7fffeab1fc445 100644 (file)
@@ -7,6 +7,7 @@ menu "Multifunction device drivers"
 
 config MFD_CORE
        tristate
+       select IRQ_DOMAIN
        default n
 
 config MFD_88PM860X
@@ -20,6 +21,30 @@ config MFD_88PM860X
          select individual components like voltage regulators, RTC and
          battery-charger under the corresponding menus.
 
+config MFD_88PM800
+       tristate "Support Marvell 88PM800"
+       depends on I2C=y && GENERIC_HARDIRQS
+       select REGMAP_I2C
+       select REGMAP_IRQ
+       select MFD_CORE
+       help
+         This supports for Marvell 88PM800 Power Management IC.
+         This includes the I2C driver and the core APIs _only_, you have to
+         select individual components like voltage regulators, RTC and
+         battery-charger under the corresponding menus.
+
+config MFD_88PM805
+       tristate "Support Marvell 88PM805"
+       depends on I2C=y && GENERIC_HARDIRQS
+       select REGMAP_I2C
+       select REGMAP_IRQ
+       select MFD_CORE
+       help
+         This supports for Marvell 88PM805 Power Management IC. This includes
+         the I2C driver and the core APIs _only_, you have to select individual
+         components like codec device, headset/Mic device under the
+         corresponding menus.
+
 config MFD_SM501
        tristate "Support for Silicon Motion SM501"
         ---help---
@@ -173,8 +198,9 @@ config MFD_TPS65217
 
 config MFD_TPS6586X
        bool "TPS6586x Power Management chips"
-       depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
+       depends on I2C=y && GENERIC_HARDIRQS
        select MFD_CORE
+       select REGMAP_I2C
        depends on REGULATOR
        help
          If you say yes here you get support for the TPS6586X series of
@@ -276,6 +302,7 @@ config TWL6030_PWM
        tristate "TWL6030 PWM (Pulse Width Modulator) Support"
        depends on TWL4030_CORE
        select HAVE_PWM
+       depends on !PWM
        default n
        help
          Say yes here if you want support for TWL6030 PWM.
@@ -423,6 +450,19 @@ config PMIC_ADP5520
          individual components like LCD backlight, LEDs, GPIOs and Kepad
          under the corresponding menus.
 
+config MFD_MAX77686
+       bool "Maxim Semiconductor MAX77686 PMIC Support"
+       depends on I2C=y && GENERIC_HARDIRQS
+       select MFD_CORE
+       select REGMAP_I2C
+       select IRQ_DOMAIN
+       help
+         Say yes here to support for Maxim Semiconductor MAX77686.
+         This is a Power Management IC with RTC on chip.
+         This driver provides common support for accessing the device;
+         additional drivers must be enabled in order to use the functionality
+         of the device.
+
 config MFD_MAX77693
        bool "Maxim Semiconductor MAX77693 PMIC Support"
        depends on I2C=y && GENERIC_HARDIRQS
@@ -450,6 +490,7 @@ config MFD_MAX8997
        bool "Maxim Semiconductor MAX8997/8966 PMIC Support"
        depends on I2C=y && GENERIC_HARDIRQS
        select MFD_CORE
+       select IRQ_DOMAIN
        help
          Say yes here to support for Maxim Semiconductor MAX8997/8966.
          This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic,
@@ -469,17 +510,56 @@ config MFD_MAX8998
          additional drivers must be enabled in order to use the functionality
          of the device.
 
-config MFD_S5M_CORE
-       bool "SAMSUNG S5M Series Support"
+config MFD_SEC_CORE
+       bool "SAMSUNG Electronics PMIC Series Support"
        depends on I2C=y && GENERIC_HARDIRQS
        select MFD_CORE
        select REGMAP_I2C
+       select REGMAP_IRQ
        help
-        Support for the Samsung Electronics S5M MFD series.
+        Support for the Samsung Electronics MFD series.
         This driver provides common support for accessing the device,
         additional drivers must be enabled in order to use the functionality
         of the device
 
+config MFD_ARIZONA
+       select REGMAP
+       select REGMAP_IRQ
+       select MFD_CORE
+       bool
+
+config MFD_ARIZONA_I2C
+       tristate "Support Wolfson Microelectronics Arizona platform with I2C"
+       select MFD_ARIZONA
+       select MFD_CORE
+       select REGMAP_I2C
+       depends on I2C
+       help
+         Support for the Wolfson Microelectronics Arizona platform audio SoC
+         core functionality controlled via I2C.
+
+config MFD_ARIZONA_SPI
+       tristate "Support Wolfson Microelectronics Arizona platform with SPI"
+       select MFD_ARIZONA
+       select MFD_CORE
+       select REGMAP_SPI
+       depends on SPI_MASTER
+       help
+         Support for the Wolfson Microelectronics Arizona platform audio SoC
+         core functionality controlled via I2C.
+
+config MFD_WM5102
+       bool "Support Wolfson Microelectronics WM5102"
+       depends on MFD_ARIZONA
+       help
+         Support for Wolfson Microelectronics WM5102 low power audio SoC
+
+config MFD_WM5110
+       bool "Support Wolfson Microelectronics WM5110"
+       depends on MFD_ARIZONA
+       help
+         Support for Wolfson Microelectronics WM5110 low power audio SoC
+
 config MFD_WM8400
        bool "Support Wolfson Microelectronics WM8400"
        select MFD_CORE
@@ -697,6 +777,7 @@ config AB8500_CORE
        bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
        depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU
        select MFD_CORE
+       select IRQ_DOMAIN
        help
          Select this option to enable access to AB8500 power management
          chip. This connects to U8500 either on the SSP/SPI bus (deprecated
@@ -704,16 +785,6 @@ config AB8500_CORE
          the irq_chip parts for handling the Mixed Signal chip events.
          This chip embeds various other multimedia funtionalities as well.
 
-config AB8500_I2C_CORE
-       bool "AB8500 register access via PRCMU I2C"
-       depends on AB8500_CORE && MFD_DB8500_PRCMU
-       default y
-       help
-         This enables register access to the AB8500 chip via PRCMU I2C.
-         The AB8500 chip can be accessed via SPI or I2C. On DB8500 hardware
-         the I2C bus is connected to the Power Reset
-         and Mangagement Unit, PRCMU.
-
 config AB8500_DEBUG
        bool "Enable debug info via debugfs"
        depends on AB8500_CORE && DEBUG_FS
index 75f6ed68a4b9e259616795e41bba94eea0b755ea..79dd22d1dc3d16d68232047b843464e0b743b4b0 100644 (file)
@@ -4,6 +4,8 @@
 
 88pm860x-objs                  := 88pm860x-core.o 88pm860x-i2c.o
 obj-$(CONFIG_MFD_88PM860X)     += 88pm860x.o
+obj-$(CONFIG_MFD_88PM800)      += 88pm800.o 88pm80x.o
+obj-$(CONFIG_MFD_88PM805)      += 88pm805.o 88pm80x.o
 obj-$(CONFIG_MFD_SM501)                += sm501.o
 obj-$(CONFIG_MFD_ASIC3)                += asic3.o tmio_core.o
 
@@ -24,6 +26,16 @@ obj-$(CONFIG_MFD_T7L66XB)    += t7l66xb.o tmio_core.o
 obj-$(CONFIG_MFD_TC6387XB)     += tc6387xb.o tmio_core.o
 obj-$(CONFIG_MFD_TC6393XB)     += tc6393xb.o tmio_core.o
 
+obj-$(CONFIG_MFD_ARIZONA)      += arizona-core.o
+obj-$(CONFIG_MFD_ARIZONA)      += arizona-irq.o
+obj-$(CONFIG_MFD_ARIZONA_I2C)  += arizona-i2c.o
+obj-$(CONFIG_MFD_ARIZONA_SPI)  += arizona-spi.o
+ifneq ($(CONFIG_MFD_WM5102),n)
+obj-$(CONFIG_MFD_ARIZONA)      += wm5102-tables.o
+endif
+ifneq ($(CONFIG_MFD_WM5110),n)
+obj-$(CONFIG_MFD_ARIZONA)      += wm5110-tables.o
+endif
 obj-$(CONFIG_MFD_WM8400)       += wm8400-core.o
 wm831x-objs                    := wm831x-core.o wm831x-irq.o wm831x-otp.o
 wm831x-objs                    += wm831x-auxadc.o
@@ -78,6 +90,7 @@ obj-$(CONFIG_PMIC_DA9052)     += da9052-core.o
 obj-$(CONFIG_MFD_DA9052_SPI)   += da9052-spi.o
 obj-$(CONFIG_MFD_DA9052_I2C)   += da9052-i2c.o
 
+obj-$(CONFIG_MFD_MAX77686)     += max77686.o max77686-irq.o
 obj-$(CONFIG_MFD_MAX77693)     += max77693.o max77693-irq.o
 max8925-objs                   := max8925-core.o max8925-i2c.o
 obj-$(CONFIG_MFD_MAX8925)      += max8925.o
@@ -116,6 +129,6 @@ obj-$(CONFIG_MFD_AAT2870_CORE)      += aat2870-core.o
 obj-$(CONFIG_MFD_INTEL_MSIC)   += intel_msic.o
 obj-$(CONFIG_MFD_PALMAS)       += palmas.o
 obj-$(CONFIG_MFD_RC5T583)      += rc5t583.o rc5t583-irq.o
-obj-$(CONFIG_MFD_S5M_CORE)     += s5m-core.o s5m-irq.o
+obj-$(CONFIG_MFD_SEC_CORE)     += sec-core.o sec-irq.o
 obj-$(CONFIG_MFD_ANATOP)       += anatop-mfd.o
 obj-$(CONFIG_MFD_LM3533)       += lm3533-core.o lm3533-ctrlbank.o
index 1efad20fb1757523713359ff5af65f3d13ca4302..78fca2902c8da38fd07660e381e8ed55c0d78c2d 100644 (file)
@@ -409,8 +409,6 @@ static irqreturn_t ab3100_irq_handler(int irq, void *data)
        u32 fatevent;
        int err;
 
-       add_interrupt_randomness(irq);
-
        err = ab3100_get_register_page_interruptible(ab3100, AB3100_EVENTA1,
                                       event_regs, 3);
        if (err)
@@ -867,7 +865,7 @@ static int __devinit ab3100_probe(struct i2c_client *client,
        int err;
        int i;
 
-       ab3100 = kzalloc(sizeof(struct ab3100), GFP_KERNEL);
+       ab3100 = devm_kzalloc(&client->dev, sizeof(struct ab3100), GFP_KERNEL);
        if (!ab3100) {
                dev_err(&client->dev, "could not allocate AB3100 device\n");
                return -ENOMEM;
@@ -921,7 +919,7 @@ static int __devinit ab3100_probe(struct i2c_client *client,
 
        /* Attach a second dummy i2c_client to the test register address */
        ab3100->testreg_client = i2c_new_dummy(client->adapter,
-                                                    client->addr + 1);
+                                              client->addr + 1);
        if (!ab3100->testreg_client) {
                err = -ENOMEM;
                goto exit_no_testreg_client;
@@ -931,11 +929,9 @@ static int __devinit ab3100_probe(struct i2c_client *client,
        if (err)
                goto exit_no_setup;
 
-       err = request_threaded_irq(client->irq, NULL, ab3100_irq_handler,
-                               IRQF_ONESHOT, "ab3100-core", ab3100);
-       /* This real unpredictable IRQ is of course sampled for entropy */
-       rand_initialize_irq(client->irq);
-
+       err = devm_request_threaded_irq(&client->dev,
+                                       client->irq, NULL, ab3100_irq_handler,
+                                       IRQF_ONESHOT, "ab3100-core", ab3100);
        if (err)
                goto exit_no_irq;
 
@@ -962,7 +958,6 @@ static int __devinit ab3100_probe(struct i2c_client *client,
        i2c_unregister_device(ab3100->testreg_client);
  exit_no_testreg_client:
  exit_no_detect:
-       kfree(ab3100);
        return err;
 }
 
@@ -972,16 +967,8 @@ static int __devexit ab3100_remove(struct i2c_client *client)
 
        /* Unregister subdevices */
        mfd_remove_devices(&client->dev);
-
        ab3100_remove_debugfs();
        i2c_unregister_device(ab3100->testreg_client);
-
-       /*
-        * At this point, all subscribers should have unregistered
-        * their notifiers so deactivate IRQ
-        */
-       free_irq(client->irq, ab3100);
-       kfree(ab3100);
        return 0;
 }
 
index dac0e299860353f0a299a8b66c14c745e587a133..626b4ecaf64761fdd3cd43ac3f02986d6830e467 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/irq.h>
+#include <linux/irqdomain.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
@@ -140,7 +141,7 @@ static const char ab8500_version_str[][7] = {
        [AB8500_VERSION_AB8540] = "AB8540",
 };
 
-static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
+static int ab8500_prcmu_write(struct ab8500 *ab8500, u16 addr, u8 data)
 {
        int ret;
 
@@ -150,7 +151,7 @@ static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
        return ret;
 }
 
-static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
+static int ab8500_prcmu_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
        u8 data)
 {
        int ret;
@@ -162,7 +163,7 @@ static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
        return ret;
 }
 
-static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)
+static int ab8500_prcmu_read(struct ab8500 *ab8500, u16 addr)
 {
        int ret;
        u8 data;
@@ -361,7 +362,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)
 static void ab8500_irq_mask(struct irq_data *data)
 {
        struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
-       int offset = data->irq - ab8500->irq_base;
+       int offset = data->hwirq;
        int index = offset / 8;
        int mask = 1 << (offset % 8);
 
@@ -371,7 +372,7 @@ static void ab8500_irq_mask(struct irq_data *data)
 static void ab8500_irq_unmask(struct irq_data *data)
 {
        struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
-       int offset = data->irq - ab8500->irq_base;
+       int offset = data->hwirq;
        int index = offset / 8;
        int mask = 1 << (offset % 8);
 
@@ -510,38 +511,51 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
        return IRQ_HANDLED;
 }
 
-static int ab8500_irq_init(struct ab8500 *ab8500)
+/**
+ * ab8500_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
+ *
+ * @ab8500: ab8500_irq controller to operate on.
+ * @irq: index of the interrupt requested in the chip IRQs
+ *
+ * Useful for drivers to request their own IRQs.
+ */
+int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq)
 {
-       int base = ab8500->irq_base;
-       int irq;
-       int num_irqs;
+       if (!ab8500)
+               return -EINVAL;
 
-       if (is_ab9540(ab8500))
-               num_irqs = AB9540_NR_IRQS;
-       else if (is_ab8505(ab8500))
-               num_irqs = AB8505_NR_IRQS;
-       else
-               num_irqs = AB8500_NR_IRQS;
+       return irq_create_mapping(ab8500->domain, irq);
+}
+EXPORT_SYMBOL_GPL(ab8500_irq_get_virq);
+
+static int ab8500_irq_map(struct irq_domain *d, unsigned int virq,
+                               irq_hw_number_t hwirq)
+{
+       struct ab8500 *ab8500 = d->host_data;
 
-       for (irq = base; irq < base + num_irqs; irq++) {
-               irq_set_chip_data(irq, ab8500);
-               irq_set_chip_and_handler(irq, &ab8500_irq_chip,
-                                        handle_simple_irq);
-               irq_set_nested_thread(irq, 1);
+       if (!ab8500)
+               return -EINVAL;
+
+       irq_set_chip_data(virq, ab8500);
+       irq_set_chip_and_handler(virq, &ab8500_irq_chip,
+                               handle_simple_irq);
+       irq_set_nested_thread(virq, 1);
 #ifdef CONFIG_ARM
-               set_irq_flags(irq, IRQF_VALID);
+       set_irq_flags(virq, IRQF_VALID);
 #else
-               irq_set_noprobe(irq);
+       irq_set_noprobe(virq);
 #endif
-       }
 
        return 0;
 }
 
-static void ab8500_irq_remove(struct ab8500 *ab8500)
+static struct irq_domain_ops ab8500_irq_ops = {
+        .map    = ab8500_irq_map,
+        .xlate  = irq_domain_xlate_twocell,
+};
+
+static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np)
 {
-       int base = ab8500->irq_base;
-       int irq;
        int num_irqs;
 
        if (is_ab9540(ab8500))
@@ -551,13 +565,22 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)
        else
                num_irqs = AB8500_NR_IRQS;
 
-       for (irq = base; irq < base + num_irqs; irq++) {
-#ifdef CONFIG_ARM
-               set_irq_flags(irq, 0);
-#endif
-               irq_set_chip_and_handler(irq, NULL, NULL);
-               irq_set_chip_data(irq, NULL);
+       if (ab8500->irq_base) {
+               ab8500->domain = irq_domain_add_legacy(
+                       NULL, num_irqs, ab8500->irq_base,
+                       0, &ab8500_irq_ops, ab8500);
+       }
+       else {
+               ab8500->domain = irq_domain_add_linear(
+                       np, num_irqs, &ab8500_irq_ops, ab8500);
+       }
+
+       if (!ab8500->domain) {
+               dev_err(ab8500->dev, "Failed to create irqdomain\n");
+               return -ENOSYS;
        }
+
+       return 0;
 }
 
 int ab8500_suspend(struct ab8500 *ab8500)
@@ -947,54 +970,69 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
 #ifdef CONFIG_DEBUG_FS
        {
                .name = "ab8500-debug",
+               .of_compatible = "stericsson,ab8500-debug",
                .num_resources = ARRAY_SIZE(ab8500_debug_resources),
                .resources = ab8500_debug_resources,
        },
 #endif
        {
                .name = "ab8500-sysctrl",
+               .of_compatible = "stericsson,ab8500-sysctrl",
        },
        {
                .name = "ab8500-regulator",
+               .of_compatible = "stericsson,ab8500-regulator",
        },
        {
                .name = "ab8500-gpadc",
+               .of_compatible = "stericsson,ab8500-gpadc",
                .num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
                .resources = ab8500_gpadc_resources,
        },
        {
                .name = "ab8500-rtc",
+               .of_compatible = "stericsson,ab8500-rtc",
                .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
                .resources = ab8500_rtc_resources,
        },
        {
                .name = "ab8500-acc-det",
+               .of_compatible = "stericsson,ab8500-acc-det",
                .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
                .resources = ab8500_av_acc_detect_resources,
        },
        {
                .name = "ab8500-poweron-key",
+               .of_compatible = "stericsson,ab8500-poweron-key",
                .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
                .resources = ab8500_poweronkey_db_resources,
        },
        {
                .name = "ab8500-pwm",
+               .of_compatible = "stericsson,ab8500-pwm",
                .id = 1,
        },
        {
                .name = "ab8500-pwm",
+               .of_compatible = "stericsson,ab8500-pwm",
                .id = 2,
        },
        {
                .name = "ab8500-pwm",
+               .of_compatible = "stericsson,ab8500-pwm",
                .id = 3,
        },
-       { .name = "ab8500-leds", },
+       {
+               .name = "ab8500-leds",
+               .of_compatible = "stericsson,ab8500-leds",
+       },
        {
                .name = "ab8500-denc",
+               .of_compatible = "stericsson,ab8500-denc",
        },
        {
                .name = "ab8500-temp",
+               .of_compatible = "stericsson,ab8500-temp",
                .num_resources = ARRAY_SIZE(ab8500_temp_resources),
                .resources = ab8500_temp_resources,
        },
@@ -1026,11 +1064,13 @@ static struct mfd_cell __devinitdata ab8500_bm_devs[] = {
 static struct mfd_cell __devinitdata ab8500_devs[] = {
        {
                .name = "ab8500-gpio",
+               .of_compatible = "stericsson,ab8500-gpio",
                .num_resources = ARRAY_SIZE(ab8500_gpio_resources),
                .resources = ab8500_gpio_resources,
        },
        {
                .name = "ab8500-usb",
+               .of_compatible = "stericsson,ab8500-usb",
                .num_resources = ARRAY_SIZE(ab8500_usb_resources),
                .resources = ab8500_usb_resources,
        },
@@ -1207,16 +1247,17 @@ static struct attribute_group ab9540_attr_group = {
        .attrs  = ab9540_sysfs_entries,
 };
 
-static const struct of_device_id ab8500_match[] = {
-       {
-               .compatible = "stericsson,ab8500",
-               .data = (void *)AB8500_VERSION_AB8500,
-       },
-       {},
-};
-
 static int __devinit ab8500_probe(struct platform_device *pdev)
 {
+       static char *switch_off_status[] = {
+               "Swoff bit programming",
+               "Thermal protection activation",
+               "Vbat lower then BattOk falling threshold",
+               "Watchdog expired",
+               "Non presence of 32kHz clock",
+               "Battery level lower than power on reset threshold",
+               "Power on key 1 pressed longer than 10 seconds",
+               "DB8500 thermal shutdown"};
        struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev);
        const struct platform_device_id *platid = platform_get_device_id(pdev);
        enum ab8500_version version = AB8500_VERSION_UNDEFINED;
@@ -1233,14 +1274,6 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
 
        if (plat)
                ab8500->irq_base = plat->irq_base;
-       else if (np)
-               ret = of_property_read_u32(np, "stericsson,irq-base", &ab8500->irq_base);
-
-       if (!ab8500->irq_base) {
-               dev_info(&pdev->dev, "couldn't find irq-base\n");
-               ret = -EINVAL;
-               goto out_free_ab8500;
-       }
 
        ab8500->dev = &pdev->dev;
 
@@ -1252,9 +1285,9 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
 
        ab8500->irq = resource->start;
 
-       ab8500->read = ab8500_i2c_read;
-       ab8500->write = ab8500_i2c_write;
-       ab8500->write_masked = ab8500_i2c_write_masked;
+       ab8500->read = ab8500_prcmu_read;
+       ab8500->write = ab8500_prcmu_write;
+       ab8500->write_masked = ab8500_prcmu_write_masked;
 
        mutex_init(&ab8500->lock);
        mutex_init(&ab8500->irq_lock);
@@ -1264,9 +1297,6 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
 
        if (platid)
                version = platid->driver_data;
-       else if (np)
-               version = (unsigned int)
-                       of_match_device(ab8500_match, &pdev->dev)->data;
 
        if (version != AB8500_VERSION_UNDEFINED)
                ab8500->version = version;
@@ -1323,7 +1353,20 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
                AB8500_SWITCH_OFF_STATUS, &value);
        if (ret < 0)
                return ret;
-       dev_info(ab8500->dev, "switch off status: %#x", value);
+       dev_info(ab8500->dev, "switch off cause(s) (%#x): ", value);
+
+       if (value) {
+               for (i = 0; i < ARRAY_SIZE(switch_off_status); i++) {
+                       if (value & 1)
+                               printk(KERN_CONT " \"%s\"",
+                                      switch_off_status[i]);
+                       value = value >> 1;
+
+               }
+               printk(KERN_CONT "\n");
+       } else {
+               printk(KERN_CONT " None\n");
+       }
 
        if (plat && plat->init)
                plat->init(ab8500);
@@ -1352,53 +1395,50 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
        for (i = 0; i < ab8500->mask_size; i++)
                ab8500->mask[i] = ab8500->oldmask[i] = 0xff;
 
-       if (ab8500->irq_base) {
-               ret = ab8500_irq_init(ab8500);
-               if (ret)
-                       goto out_freeoldmask;
+       ret = ab8500_irq_init(ab8500, np);
+       if (ret)
+               goto out_freeoldmask;
 
-               /*  Activate this feature only in ab9540 */
-               /*  till tests are done on ab8500 1p2 or later*/
-               if (is_ab9540(ab8500))
-                       ret = request_threaded_irq(ab8500->irq, NULL,
+       /*  Activate this feature only in ab9540 */
+       /*  till tests are done on ab8500 1p2 or later*/
+       if (is_ab9540(ab8500)) {
+               ret = request_threaded_irq(ab8500->irq, NULL,
                                        ab8500_hierarchical_irq,
                                        IRQF_ONESHOT | IRQF_NO_SUSPEND,
                                        "ab8500", ab8500);
-               else
-                       ret = request_threaded_irq(ab8500->irq, NULL,
+       }
+       else {
+               ret = request_threaded_irq(ab8500->irq, NULL,
                                        ab8500_irq,
                                        IRQF_ONESHOT | IRQF_NO_SUSPEND,
                                        "ab8500", ab8500);
                if (ret)
-                       goto out_removeirq;
+                       goto out_freeoldmask;
        }
 
-       if (!np) {
-               ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
-                               ARRAY_SIZE(abx500_common_devs), NULL,
-                               ab8500->irq_base);
+       ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
+                       ARRAY_SIZE(abx500_common_devs), NULL,
+                       ab8500->irq_base);
+       if (ret)
+               goto out_freeirq;
 
-               if (ret)
-                       goto out_freeirq;
-
-               if (is_ab9540(ab8500))
-                       ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
-                                       ARRAY_SIZE(ab9540_devs), NULL,
-                                       ab8500->irq_base);
-               else
-                       ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
-                                       ARRAY_SIZE(ab8500_devs), NULL,
-                                       ab8500->irq_base);
-               if (ret)
-                       goto out_freeirq;
+       if (is_ab9540(ab8500))
+               ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
+                               ARRAY_SIZE(ab9540_devs), NULL,
+                               ab8500->irq_base);
+       else
+               ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
+                               ARRAY_SIZE(ab8500_devs), NULL,
+                               ab8500->irq_base);
+       if (ret)
+               goto out_freeirq;
 
-               if (is_ab9540(ab8500) || is_ab8505(ab8500))
-                       ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
-                                       ARRAY_SIZE(ab9540_ab8505_devs), NULL,
-                                       ab8500->irq_base);
-               if (ret)
-                       goto out_freeirq;
-       }
+       if (is_ab9540(ab8500) || is_ab8505(ab8500))
+               ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
+                               ARRAY_SIZE(ab9540_ab8505_devs), NULL,
+                               ab8500->irq_base);
+       if (ret)
+               goto out_freeirq;
 
        if (!no_bm) {
                /* Add battery management devices */
@@ -1417,15 +1457,11 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
                                        &ab8500_attr_group);
        if (ret)
                dev_err(ab8500->dev, "error creating sysfs entries\n");
-       else
-               return ret;
+
+       return ret;
 
 out_freeirq:
-       if (ab8500->irq_base)
-               free_irq(ab8500->irq, ab8500);
-out_removeirq:
-       if (ab8500->irq_base)
-               ab8500_irq_remove(ab8500);
+       free_irq(ab8500->irq, ab8500);
 out_freeoldmask:
        kfree(ab8500->oldmask);
 out_freemask:
@@ -1444,11 +1480,10 @@ static int __devexit ab8500_remove(struct platform_device *pdev)
                sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group);
        else
                sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);
+
        mfd_remove_devices(ab8500->dev);
-       if (ab8500->irq_base) {
-               free_irq(ab8500->irq, ab8500);
-               ab8500_irq_remove(ab8500);
-       }
+       free_irq(ab8500->irq, ab8500);
+
        kfree(ab8500->oldmask);
        kfree(ab8500->mask);
        kfree(ab8500);
@@ -1468,7 +1503,6 @@ static struct platform_driver ab8500_core_driver = {
        .driver = {
                .name = "ab8500-core",
                .owner = THIS_MODULE,
-               .of_match_table = ab8500_match,
        },
        .probe  = ab8500_probe,
        .remove = __devexit_p(ab8500_remove),
@@ -1484,7 +1518,7 @@ static void __exit ab8500_core_exit(void)
 {
        platform_driver_unregister(&ab8500_core_driver);
 }
-arch_initcall(ab8500_core_init);
+core_initcall(ab8500_core_init);
 module_exit(ab8500_core_exit);
 
 MODULE_AUTHOR("Mattias Wallin, Srinidhi Kasagar, Rabin Vincent");
index 50c4c89ab2202fba117a05087799bfaf9ddba9df..c4cb806978acd62603bc76a6780a2cad633fe4e9 100644 (file)
@@ -31,12 +31,12 @@ struct ab8500_reg_range {
 };
 
 /**
- * struct ab8500_i2c_ranges
+ * struct ab8500_prcmu_ranges
  * @num_ranges: the number of ranges in the list
  * @bankid: bank identifier
  * @range: the list of register ranges
  */
-struct ab8500_i2c_ranges {
+struct ab8500_prcmu_ranges {
        u8 num_ranges;
        u8 bankid;
        const struct ab8500_reg_range *range;
@@ -47,7 +47,7 @@ struct ab8500_i2c_ranges {
 
 #define AB8500_REV_REG 0x80
 
-static struct ab8500_i2c_ranges debug_ranges[AB8500_NUM_BANKS] = {
+static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
        [0x0] = {
                .num_ranges = 0,
                .range = 0,
@@ -608,16 +608,10 @@ static int __devexit ab8500_debug_remove(struct platform_device *plf)
        return 0;
 }
 
-static const struct of_device_id ab8500_debug_match[] = {
-        { .compatible = "stericsson,ab8500-debug", },
-        {}
-};
-
 static struct platform_driver ab8500_debug_driver = {
        .driver = {
                .name = "ab8500-debug",
                .owner = THIS_MODULE,
-               .of_match_table = ab8500_debug_match,
        },
        .probe  = ab8500_debug_probe,
        .remove = __devexit_p(ab8500_debug_remove)
index b86fd8e1ec3fbae7c4bf6770c1598a777d657d78..866f95960b4b69b34f2fd2e6347842f9b9b096b8 100644 (file)
@@ -599,7 +599,8 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
        /* Register interrupt  - SwAdcComplete */
        ret = request_threaded_irq(gpadc->irq, NULL,
                ab8500_bm_gpswadcconvend_handler,
-               IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc", gpadc);
+               IRQF_ONESHOT | IRQF_NO_SUSPEND | IRQF_SHARED,
+                               "ab8500-gpadc", gpadc);
        if (ret < 0) {
                dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n",
                        gpadc->irq);
@@ -648,18 +649,12 @@ static int __devexit ab8500_gpadc_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id ab8500_gpadc_match[] = {
-       { .compatible = "stericsson,ab8500-gpadc", },
-       {}
-};
-
 static struct platform_driver ab8500_gpadc_driver = {
        .probe = ab8500_gpadc_probe,
        .remove = __devexit_p(ab8500_gpadc_remove),
        .driver = {
                .name = "ab8500-gpadc",
                .owner = THIS_MODULE,
-               .of_match_table = ab8500_gpadc_match,
        },
 };
 
index 5a3e51ccf25863b94dccd26b25f4e7002b90ac97..c28d4eb1eff019517d166476c590652f2ac1b8b1 100644 (file)
@@ -61,16 +61,10 @@ static int __devexit ab8500_sysctrl_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id ab8500_sysctrl_match[] = {
-       { .compatible = "stericsson,ab8500-sysctrl", },
-       {}
-};
-
 static struct platform_driver ab8500_sysctrl_driver = {
        .driver = {
                .name = "ab8500-sysctrl",
                .owner = THIS_MODULE,
-               .of_match_table = ab8500_sysctrl_match,
        },
        .probe = ab8500_sysctrl_probe,
        .remove = __devexit_p(ab8500_sysctrl_remove),
index 8d816cce8322ebecdf8d40e29f0c0b0a3aedcbdb..ea8b9475731dd9fc48958f13eaa7aa0af262c573 100644 (file)
@@ -320,7 +320,7 @@ static int __devexit adp5520_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int adp5520_suspend(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
index 6da06341f6c909312afea387e875fe82cc1592b8..5576e07576decb0684abbc3d2c3d36d80784965f 100644 (file)
@@ -83,7 +83,7 @@ static int __devinit of_anatop_probe(struct platform_device *pdev)
        drvdata->ioreg = ioreg;
        spin_lock_init(&drvdata->reglock);
        platform_set_drvdata(pdev, drvdata);
-       of_platform_populate(np, of_anatop_match, NULL, dev);
+       of_platform_populate(np, NULL, NULL, dev);
 
        return 0;
 }
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
new file mode 100644 (file)
index 0000000..c7983e8
--- /dev/null
@@ -0,0 +1,566 @@
+/*
+ * Arizona core driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+
+static const char *wm5102_core_supplies[] = {
+       "AVDD",
+       "DBVDD1",
+};
+
+int arizona_clk32k_enable(struct arizona *arizona)
+{
+       int ret = 0;
+
+       mutex_lock(&arizona->clk_lock);
+
+       arizona->clk32k_ref++;
+
+       if (arizona->clk32k_ref == 1)
+               ret = regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
+                                        ARIZONA_CLK_32K_ENA,
+                                        ARIZONA_CLK_32K_ENA);
+
+       if (ret != 0)
+               arizona->clk32k_ref--;
+
+       mutex_unlock(&arizona->clk_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_clk32k_enable);
+
+int arizona_clk32k_disable(struct arizona *arizona)
+{
+       int ret = 0;
+
+       mutex_lock(&arizona->clk_lock);
+
+       BUG_ON(arizona->clk32k_ref <= 0);
+
+       arizona->clk32k_ref--;
+
+       if (arizona->clk32k_ref == 0)
+               regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
+                                  ARIZONA_CLK_32K_ENA, 0);
+
+       mutex_unlock(&arizona->clk_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_clk32k_disable);
+
+static irqreturn_t arizona_clkgen_err(int irq, void *data)
+{
+       struct arizona *arizona = data;
+
+       dev_err(arizona->dev, "CLKGEN error\n");
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t arizona_underclocked(int irq, void *data)
+{
+       struct arizona *arizona = data;
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_8,
+                         &val);
+       if (ret != 0) {
+               dev_err(arizona->dev, "Failed to read underclock status: %d\n",
+                       ret);
+               return IRQ_NONE;
+       }
+
+       if (val & ARIZONA_AIF3_UNDERCLOCKED_STS)
+               dev_err(arizona->dev, "AIF3 underclocked\n");
+       if (val & ARIZONA_AIF3_UNDERCLOCKED_STS)
+               dev_err(arizona->dev, "AIF3 underclocked\n");
+       if (val & ARIZONA_AIF2_UNDERCLOCKED_STS)
+               dev_err(arizona->dev, "AIF1 underclocked\n");
+       if (val & ARIZONA_ISRC2_UNDERCLOCKED_STS)
+               dev_err(arizona->dev, "ISRC2 underclocked\n");
+       if (val & ARIZONA_ISRC1_UNDERCLOCKED_STS)
+               dev_err(arizona->dev, "ISRC1 underclocked\n");
+       if (val & ARIZONA_FX_UNDERCLOCKED_STS)
+               dev_err(arizona->dev, "FX underclocked\n");
+       if (val & ARIZONA_ASRC_UNDERCLOCKED_STS)
+               dev_err(arizona->dev, "ASRC underclocked\n");
+       if (val & ARIZONA_DAC_UNDERCLOCKED_STS)
+               dev_err(arizona->dev, "DAC underclocked\n");
+       if (val & ARIZONA_ADC_UNDERCLOCKED_STS)
+               dev_err(arizona->dev, "ADC underclocked\n");
+       if (val & ARIZONA_MIXER_UNDERCLOCKED_STS)
+               dev_err(arizona->dev, "Mixer underclocked\n");
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t arizona_overclocked(int irq, void *data)
+{
+       struct arizona *arizona = data;
+       unsigned int val[2];
+       int ret;
+       
+       ret = regmap_bulk_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_6,
+                              &val[0], 2);
+       if (ret != 0) {
+               dev_err(arizona->dev, "Failed to read overclock status: %d\n",
+                       ret);
+               return IRQ_NONE;
+       }
+
+       if (val[0] & ARIZONA_PWM_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "PWM overclocked\n");
+       if (val[0] & ARIZONA_FX_CORE_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "FX core overclocked\n");
+       if (val[0] & ARIZONA_DAC_SYS_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "DAC SYS overclocked\n");
+       if (val[0] & ARIZONA_DAC_WARP_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "DAC WARP overclocked\n");
+       if (val[0] & ARIZONA_ADC_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "ADC overclocked\n");
+       if (val[0] & ARIZONA_MIXER_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "Mixer overclocked\n");
+       if (val[0] & ARIZONA_AIF3_SYNC_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "AIF3 overclocked\n");
+       if (val[0] & ARIZONA_AIF2_SYNC_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "AIF2 overclocked\n");
+       if (val[0] & ARIZONA_AIF1_SYNC_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "AIF1 overclocked\n");
+       if (val[0] & ARIZONA_PAD_CTRL_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "Pad control overclocked\n");
+
+       if (val[1] & ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "Slimbus subsystem overclocked\n");
+       if (val[1] & ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "Slimbus async overclocked\n");
+       if (val[1] & ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "Slimbus sync overclocked\n");
+       if (val[1] & ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "ASRC async system overclocked\n");
+       if (val[1] & ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "ASRC async WARP overclocked\n");
+       if (val[1] & ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "ASRC sync system overclocked\n");
+       if (val[1] & ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "ASRC sync WARP overclocked\n");
+       if (val[1] & ARIZONA_ADSP2_1_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "DSP1 overclocked\n");
+       if (val[1] & ARIZONA_ISRC2_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "ISRC2 overclocked\n");
+       if (val[1] & ARIZONA_ISRC1_OVERCLOCKED_STS)
+               dev_err(arizona->dev, "ISRC1 overclocked\n");
+
+       return IRQ_HANDLED;
+}
+
+static int arizona_wait_for_boot(struct arizona *arizona)
+{
+       unsigned int reg;
+       int ret, i;
+
+       /*
+        * We can't use an interrupt as we need to runtime resume to do so,
+        * we won't race with the interrupt handler as it'll be blocked on
+        * runtime resume.
+        */
+       for (i = 0; i < 5; i++) {
+               msleep(1);
+
+               ret = regmap_read(arizona->regmap,
+                                 ARIZONA_INTERRUPT_RAW_STATUS_5, &reg);
+               if (ret != 0) {
+                       dev_err(arizona->dev, "Failed to read boot state: %d\n",
+                               ret);
+                       continue;
+               }
+
+               if (reg & ARIZONA_BOOT_DONE_STS)
+                       break;
+       }
+
+       if (reg & ARIZONA_BOOT_DONE_STS) {
+               regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5,
+                            ARIZONA_BOOT_DONE_STS);
+       } else {
+               dev_err(arizona->dev, "Device boot timed out: %x\n", reg);
+               return -ETIMEDOUT;
+       }
+
+       pm_runtime_mark_last_busy(arizona->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int arizona_runtime_resume(struct device *dev)
+{
+       struct arizona *arizona = dev_get_drvdata(dev);
+       int ret;
+
+       dev_dbg(arizona->dev, "Leaving AoD mode\n");
+
+       ret = regulator_enable(arizona->dcvdd);
+       if (ret != 0) {
+               dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret);
+               return ret;
+       }
+
+       regcache_cache_only(arizona->regmap, false);
+
+       ret = arizona_wait_for_boot(arizona);
+       if (ret != 0) {
+               regulator_disable(arizona->dcvdd);
+               return ret;
+       }
+
+       regcache_sync(arizona->regmap);
+
+       return 0;
+}
+
+static int arizona_runtime_suspend(struct device *dev)
+{
+       struct arizona *arizona = dev_get_drvdata(dev);
+
+       dev_dbg(arizona->dev, "Entering AoD mode\n");
+
+       regulator_disable(arizona->dcvdd);
+       regcache_cache_only(arizona->regmap, true);
+       regcache_mark_dirty(arizona->regmap);
+
+       return 0;
+}
+#endif
+
+const struct dev_pm_ops arizona_pm_ops = {
+       SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
+                          arizona_runtime_resume,
+                          NULL)
+};
+EXPORT_SYMBOL_GPL(arizona_pm_ops);
+
+static struct mfd_cell early_devs[] = {
+       { .name = "arizona-ldo1" },
+};
+
+static struct mfd_cell wm5102_devs[] = {
+       { .name = "arizona-extcon" },
+       { .name = "arizona-gpio" },
+       { .name = "arizona-micsupp" },
+       { .name = "arizona-pwm" },
+       { .name = "wm5102-codec" },
+};
+
+static struct mfd_cell wm5110_devs[] = {
+       { .name = "arizona-extcon" },
+       { .name = "arizona-gpio" },
+       { .name = "arizona-micsupp" },
+       { .name = "arizona-pwm" },
+       { .name = "wm5110-codec" },
+};
+
+int __devinit arizona_dev_init(struct arizona *arizona)
+{
+       struct device *dev = arizona->dev;
+       const char *type_name;
+       unsigned int reg, val;
+       int ret, i;
+
+       dev_set_drvdata(arizona->dev, arizona);
+       mutex_init(&arizona->clk_lock);
+
+       if (dev_get_platdata(arizona->dev))
+               memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),
+                      sizeof(arizona->pdata));
+
+       regcache_cache_only(arizona->regmap, true);
+
+       switch (arizona->type) {
+       case WM5102:
+       case WM5110:
+               for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++)
+                       arizona->core_supplies[i].supply
+                               = wm5102_core_supplies[i];
+               arizona->num_core_supplies = ARRAY_SIZE(wm5102_core_supplies);
+               break;
+       default:
+               dev_err(arizona->dev, "Unknown device type %d\n",
+                       arizona->type);
+               return -EINVAL;
+       }
+
+       ret = mfd_add_devices(arizona->dev, -1, early_devs,
+                             ARRAY_SIZE(early_devs), NULL, 0);
+       if (ret != 0) {
+               dev_err(dev, "Failed to add early children: %d\n", ret);
+               return ret;
+       }
+
+       ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies,
+                                     arizona->core_supplies);
+       if (ret != 0) {
+               dev_err(dev, "Failed to request core supplies: %d\n",
+                       ret);
+               goto err_early;
+       }
+
+       arizona->dcvdd = devm_regulator_get(arizona->dev, "DCVDD");
+       if (IS_ERR(arizona->dcvdd)) {
+               ret = PTR_ERR(arizona->dcvdd);
+               dev_err(dev, "Failed to request DCVDD: %d\n", ret);
+               goto err_early;
+       }
+
+       ret = regulator_bulk_enable(arizona->num_core_supplies,
+                                   arizona->core_supplies);
+       if (ret != 0) {
+               dev_err(dev, "Failed to enable core supplies: %d\n",
+                       ret);
+               goto err_early;
+       }
+
+       ret = regulator_enable(arizona->dcvdd);
+       if (ret != 0) {
+               dev_err(dev, "Failed to enable DCVDD: %d\n", ret);
+               goto err_enable;
+       }
+
+       if (arizona->pdata.reset) {
+               /* Start out with /RESET low to put the chip into reset */
+               ret = gpio_request_one(arizona->pdata.reset,
+                                      GPIOF_DIR_OUT | GPIOF_INIT_LOW,
+                                      "arizona /RESET");
+               if (ret != 0) {
+                       dev_err(dev, "Failed to request /RESET: %d\n", ret);
+                       goto err_dcvdd;
+               }
+
+               gpio_set_value_cansleep(arizona->pdata.reset, 1);
+       }
+
+       regcache_cache_only(arizona->regmap, false);
+
+       ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
+       if (ret != 0) {
+               dev_err(dev, "Failed to read ID register: %d\n", ret);
+               goto err_reset;
+       }
+
+       ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION,
+                         &arizona->rev);
+       if (ret != 0) {
+               dev_err(dev, "Failed to read revision register: %d\n", ret);
+               goto err_reset;
+       }
+       arizona->rev &= ARIZONA_DEVICE_REVISION_MASK;
+
+       switch (reg) {
+#ifdef CONFIG_MFD_WM5102
+       case 0x5102:
+               type_name = "WM5102";
+               if (arizona->type != WM5102) {
+                       dev_err(arizona->dev, "WM5102 registered as %d\n",
+                               arizona->type);
+                       arizona->type = WM5102;
+               }
+               ret = wm5102_patch(arizona);
+               break;
+#endif
+#ifdef CONFIG_MFD_WM5110
+       case 0x5110:
+               type_name = "WM5110";
+               if (arizona->type != WM5110) {
+                       dev_err(arizona->dev, "WM5110 registered as %d\n",
+                               arizona->type);
+                       arizona->type = WM5110;
+               }
+               ret = wm5110_patch(arizona);
+               break;
+#endif
+       default:
+               dev_err(arizona->dev, "Unknown device ID %x\n", reg);
+               goto err_reset;
+       }
+
+       dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
+
+       if (ret != 0)
+               dev_err(arizona->dev, "Failed to apply patch: %d\n", ret);
+
+       /* If we have a /RESET GPIO we'll already be reset */
+       if (!arizona->pdata.reset) {
+               ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
+               if (ret != 0) {
+                       dev_err(dev, "Failed to reset device: %d\n", ret);
+                       goto err_reset;
+               }
+       }
+
+       ret = arizona_wait_for_boot(arizona);
+       if (ret != 0) {
+               dev_err(arizona->dev, "Device failed initial boot: %d\n", ret);
+               goto err_reset;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
+               if (!arizona->pdata.gpio_defaults[i])
+                       continue;
+
+               regmap_write(arizona->regmap, ARIZONA_GPIO1_CTRL + i,
+                            arizona->pdata.gpio_defaults[i]);
+       }
+
+       pm_runtime_set_autosuspend_delay(arizona->dev, 100);
+       pm_runtime_use_autosuspend(arizona->dev);
+       pm_runtime_enable(arizona->dev);
+
+       /* Chip default */
+       if (!arizona->pdata.clk32k_src)
+               arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2;
+
+       switch (arizona->pdata.clk32k_src) {
+       case ARIZONA_32KZ_MCLK1:
+       case ARIZONA_32KZ_MCLK2:
+               regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
+                                  ARIZONA_CLK_32K_SRC_MASK,
+                                  arizona->pdata.clk32k_src - 1);
+               break;
+       case ARIZONA_32KZ_NONE:
+               regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
+                                  ARIZONA_CLK_32K_SRC_MASK, 2);
+               break;
+       default:
+               dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n",
+                       arizona->pdata.clk32k_src);
+               ret = -EINVAL;
+               goto err_reset;
+       }
+
+       for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
+               /* Default for both is 0 so noop with defaults */
+               val = arizona->pdata.dmic_ref[i]
+                       << ARIZONA_IN1_DMIC_SUP_SHIFT;
+               val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT;
+
+               regmap_update_bits(arizona->regmap,
+                                  ARIZONA_IN1L_CONTROL + (i * 8),
+                                  ARIZONA_IN1_DMIC_SUP_MASK |
+                                  ARIZONA_IN1_MODE_MASK, val);
+       }
+
+       for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) {
+               /* Default is 0 so noop with defaults */
+               if (arizona->pdata.out_mono[i])
+                       val = ARIZONA_OUT1_MONO;
+               else
+                       val = 0;
+
+               regmap_update_bits(arizona->regmap,
+                                  ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
+                                  ARIZONA_OUT1_MONO, val);
+       }
+
+       for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) {
+               if (arizona->pdata.spk_mute[i])
+                       regmap_update_bits(arizona->regmap,
+                                          ARIZONA_PDM_SPK1_CTRL_1 + (i * 2),
+                                          ARIZONA_SPK1_MUTE_ENDIAN_MASK |
+                                          ARIZONA_SPK1_MUTE_SEQ1_MASK,
+                                          arizona->pdata.spk_mute[i]);
+
+               if (arizona->pdata.spk_fmt[i])
+                       regmap_update_bits(arizona->regmap,
+                                          ARIZONA_PDM_SPK1_CTRL_2 + (i * 2),
+                                          ARIZONA_SPK1_FMT_MASK,
+                                          arizona->pdata.spk_fmt[i]);
+       }
+
+       /* Set up for interrupts */
+       ret = arizona_irq_init(arizona);
+       if (ret != 0)
+               goto err_reset;
+
+       arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error",
+                           arizona_clkgen_err, arizona);
+       arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked",
+                           arizona_overclocked, arizona);
+       arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked",
+                           arizona_underclocked, arizona);
+
+       switch (arizona->type) {
+       case WM5102:
+               ret = mfd_add_devices(arizona->dev, -1, wm5102_devs,
+                                     ARRAY_SIZE(wm5102_devs), NULL, 0);
+               break;
+       case WM5110:
+               ret = mfd_add_devices(arizona->dev, -1, wm5110_devs,
+                                     ARRAY_SIZE(wm5102_devs), NULL, 0);
+               break;
+       }
+
+       if (ret != 0) {
+               dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret);
+               goto err_irq;
+       }
+
+#ifdef CONFIG_PM_RUNTIME
+       regulator_disable(arizona->dcvdd);
+#endif
+
+       return 0;
+
+err_irq:
+       arizona_irq_exit(arizona);
+err_reset:
+       if (arizona->pdata.reset) {
+               gpio_set_value_cansleep(arizona->pdata.reset, 1);
+               gpio_free(arizona->pdata.reset);
+       }
+err_dcvdd:
+       regulator_disable(arizona->dcvdd);
+err_enable:
+       regulator_bulk_disable(arizona->num_core_supplies,
+                              arizona->core_supplies);
+err_early:
+       mfd_remove_devices(dev);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_dev_init);
+
+int __devexit arizona_dev_exit(struct arizona *arizona)
+{
+       mfd_remove_devices(arizona->dev);
+       arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona);
+       arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona);
+       arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
+       pm_runtime_disable(arizona->dev);
+       arizona_irq_exit(arizona);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_dev_exit);
diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c
new file mode 100644 (file)
index 0000000..570c4b4
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Arizona-i2c.c  --  Arizona I2C bus interface
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/arizona/core.h>
+
+#include "arizona.h"
+
+static __devinit int arizona_i2c_probe(struct i2c_client *i2c,
+                                         const struct i2c_device_id *id)
+{
+       struct arizona *arizona;
+       const struct regmap_config *regmap_config;
+       int ret;
+
+       switch (id->driver_data) {
+#ifdef CONFIG_MFD_WM5102
+       case WM5102:
+               regmap_config = &wm5102_i2c_regmap;
+               break;
+#endif
+#ifdef CONFIG_MFD_WM5110
+       case WM5110:
+               regmap_config = &wm5110_i2c_regmap;
+               break;
+#endif
+       default:
+               dev_err(&i2c->dev, "Unknown device type %ld\n",
+                       id->driver_data);
+               return -EINVAL;
+       }
+
+       arizona = devm_kzalloc(&i2c->dev, sizeof(*arizona), GFP_KERNEL);
+       if (arizona == NULL)
+               return -ENOMEM;
+
+       arizona->regmap = devm_regmap_init_i2c(i2c, regmap_config);
+       if (IS_ERR(arizona->regmap)) {
+               ret = PTR_ERR(arizona->regmap);
+               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       arizona->type = id->driver_data;
+       arizona->dev = &i2c->dev;
+       arizona->irq = i2c->irq;
+
+       return arizona_dev_init(arizona);
+}
+
+static int __devexit arizona_i2c_remove(struct i2c_client *i2c)
+{
+       struct arizona *arizona = dev_get_drvdata(&i2c->dev);
+       arizona_dev_exit(arizona);
+       return 0;
+}
+
+static const struct i2c_device_id arizona_i2c_id[] = {
+       { "wm5102", WM5102 },
+       { "wm5110", WM5110 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, arizona_i2c_id);
+
+static struct i2c_driver arizona_i2c_driver = {
+       .driver = {
+               .name   = "arizona",
+               .owner  = THIS_MODULE,
+               .pm     = &arizona_pm_ops,
+       },
+       .probe          = arizona_i2c_probe,
+       .remove         = __devexit_p(arizona_i2c_remove),
+       .id_table       = arizona_i2c_id,
+};
+
+module_i2c_driver(arizona_i2c_driver);
+
+MODULE_DESCRIPTION("Arizona I2C bus interface");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c
new file mode 100644 (file)
index 0000000..98ac345
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Arizona interrupt support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+
+static int arizona_map_irq(struct arizona *arizona, int irq)
+{
+       int ret;
+
+       ret = regmap_irq_get_virq(arizona->aod_irq_chip, irq);
+       if (ret < 0)
+               ret = regmap_irq_get_virq(arizona->irq_chip, irq);
+
+       return ret;
+}
+
+int arizona_request_irq(struct arizona *arizona, int irq, char *name,
+                          irq_handler_t handler, void *data)
+{
+       irq = arizona_map_irq(arizona, irq);
+       if (irq < 0)
+               return irq;
+
+       return request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT,
+                                   name, data);
+}
+EXPORT_SYMBOL_GPL(arizona_request_irq);
+
+void arizona_free_irq(struct arizona *arizona, int irq, void *data)
+{
+       irq = arizona_map_irq(arizona, irq);
+       if (irq < 0)
+               return;
+
+       free_irq(irq, data);
+}
+EXPORT_SYMBOL_GPL(arizona_free_irq);
+
+int arizona_set_irq_wake(struct arizona *arizona, int irq, int on)
+{
+       irq = arizona_map_irq(arizona, irq);
+       if (irq < 0)
+               return irq;
+
+       return irq_set_irq_wake(irq, on);
+}
+EXPORT_SYMBOL_GPL(arizona_set_irq_wake);
+
+static irqreturn_t arizona_boot_done(int irq, void *data)
+{
+       struct arizona *arizona = data;
+
+       dev_dbg(arizona->dev, "Boot done\n");
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t arizona_ctrlif_err(int irq, void *data)
+{
+       struct arizona *arizona = data;
+
+       /*
+        * For pretty much all potential sources a register cache sync
+        * won't help, we've just got a software bug somewhere.
+        */
+       dev_err(arizona->dev, "Control interface error\n");
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t arizona_irq_thread(int irq, void *data)
+{
+       struct arizona *arizona = data;
+       int i, ret;
+
+       ret = pm_runtime_get_sync(arizona->dev);
+       if (ret < 0) {
+               dev_err(arizona->dev, "Failed to resume device: %d\n", ret);
+               return IRQ_NONE;
+       }
+
+       /* Check both domains */
+       for (i = 0; i < 2; i++)
+               handle_nested_irq(irq_find_mapping(arizona->virq, i));
+
+       pm_runtime_mark_last_busy(arizona->dev);
+       pm_runtime_put_autosuspend(arizona->dev);
+
+       return IRQ_HANDLED;
+}
+
+static void arizona_irq_enable(struct irq_data *data)
+{
+}
+
+static void arizona_irq_disable(struct irq_data *data)
+{
+}
+
+static struct irq_chip arizona_irq_chip = {
+       .name                   = "arizona",
+       .irq_disable            = arizona_irq_disable,
+       .irq_enable             = arizona_irq_enable,
+};
+
+static int arizona_irq_map(struct irq_domain *h, unsigned int virq,
+                             irq_hw_number_t hw)
+{
+       struct regmap_irq_chip_data *data = h->host_data;
+
+       irq_set_chip_data(virq, data);
+       irq_set_chip_and_handler(virq, &arizona_irq_chip, handle_edge_irq);
+       irq_set_nested_thread(virq, 1);
+
+       /* ARM needs us to explicitly flag the IRQ as valid
+        * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+       set_irq_flags(virq, IRQF_VALID);
+#else
+       irq_set_noprobe(virq);
+#endif
+
+       return 0;
+}
+
+static struct irq_domain_ops arizona_domain_ops = {
+       .map    = arizona_irq_map,
+       .xlate  = irq_domain_xlate_twocell,
+};
+
+int arizona_irq_init(struct arizona *arizona)
+{
+       int flags = IRQF_ONESHOT;
+       int ret, i;
+       const struct regmap_irq_chip *aod, *irq;
+
+       switch (arizona->type) {
+#ifdef CONFIG_MFD_WM5102
+       case WM5102:
+               aod = &wm5102_aod;
+               irq = &wm5102_irq;
+               break;
+#endif
+#ifdef CONFIG_MFD_WM5110
+       case WM5110:
+               aod = &wm5110_aod;
+               irq = &wm5110_irq;
+               break;
+#endif
+       default:
+               BUG_ON("Unknown Arizona class device" == NULL);
+               return -EINVAL;
+       }
+
+       if (arizona->pdata.irq_active_high) {
+               ret = regmap_update_bits(arizona->regmap, ARIZONA_IRQ_CTRL_1,
+                                        ARIZONA_IRQ_POL, 0);
+               if (ret != 0) {
+                       dev_err(arizona->dev, "Couldn't set IRQ polarity: %d\n",
+                               ret);
+                       goto err;
+               }
+
+               flags |= IRQF_TRIGGER_HIGH;
+       } else {
+               flags |= IRQF_TRIGGER_LOW;
+       }
+
+       /* Allocate a virtual IRQ domain to distribute to the regmap domains */
+       arizona->virq = irq_domain_add_linear(NULL, 2, &arizona_domain_ops,
+                                             arizona);
+       if (!arizona->virq) {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = regmap_add_irq_chip(arizona->regmap,
+                                 irq_create_mapping(arizona->virq, 0),
+                                 IRQF_ONESHOT, -1, aod,
+                                 &arizona->aod_irq_chip);
+       if (ret != 0) {
+               dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret);
+               goto err_domain;
+       }
+
+       ret = regmap_add_irq_chip(arizona->regmap,
+                                 irq_create_mapping(arizona->virq, 1),
+                                 IRQF_ONESHOT, -1, irq,
+                                 &arizona->irq_chip);
+       if (ret != 0) {
+               dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret);
+               goto err_aod;
+       }
+
+       /* Make sure the boot done IRQ is unmasked for resumes */
+       i = arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE);
+       ret = request_threaded_irq(i, NULL, arizona_boot_done, IRQF_ONESHOT,
+                                  "Boot done", arizona);
+       if (ret != 0) {
+               dev_err(arizona->dev, "Failed to request boot done %d: %d\n",
+                       arizona->irq, ret);
+               goto err_boot_done;
+       }
+
+       /* Handle control interface errors in the core */
+       i = arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR);
+       ret = request_threaded_irq(i, NULL, arizona_ctrlif_err, IRQF_ONESHOT,
+                                  "Control interface error", arizona);
+       if (ret != 0) {
+               dev_err(arizona->dev, "Failed to request boot done %d: %d\n",
+                       arizona->irq, ret);
+               goto err_ctrlif;
+       }
+
+       ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread,
+                                  flags, "arizona", arizona);
+
+       if (ret != 0) {
+               dev_err(arizona->dev, "Failed to request IRQ %d: %d\n",
+                       arizona->irq, ret);
+               goto err_main_irq;
+       }
+
+       return 0;
+
+err_main_irq:
+       free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), arizona);
+err_ctrlif:
+       free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona);
+err_boot_done:
+       regmap_del_irq_chip(irq_create_mapping(arizona->virq, 1),
+                           arizona->irq_chip);
+err_aod:
+       regmap_del_irq_chip(irq_create_mapping(arizona->virq, 0),
+                           arizona->aod_irq_chip);
+err_domain:
+err:
+       return ret;
+}
+
+int arizona_irq_exit(struct arizona *arizona)
+{
+       free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), arizona);
+       free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona);
+       regmap_del_irq_chip(irq_create_mapping(arizona->virq, 1),
+                           arizona->irq_chip);
+       regmap_del_irq_chip(irq_create_mapping(arizona->virq, 0),
+                           arizona->aod_irq_chip);
+       free_irq(arizona->irq, arizona);
+
+       return 0;
+}
diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c
new file mode 100644 (file)
index 0000000..df2e5a8
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * arizona-spi.c  --  Arizona SPI bus interface
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#include <linux/mfd/arizona/core.h>
+
+#include "arizona.h"
+
+static int __devinit arizona_spi_probe(struct spi_device *spi)
+{
+       const struct spi_device_id *id = spi_get_device_id(spi);
+       struct arizona *arizona;
+       const struct regmap_config *regmap_config;
+       int ret;
+
+       switch (id->driver_data) {
+#ifdef CONFIG_MFD_WM5102
+       case WM5102:
+               regmap_config = &wm5102_spi_regmap;
+               break;
+#endif
+#ifdef CONFIG_MFD_WM5110
+       case WM5110:
+               regmap_config = &wm5110_spi_regmap;
+               break;
+#endif
+       default:
+               dev_err(&spi->dev, "Unknown device type %ld\n",
+                       id->driver_data);
+               return -EINVAL;
+       }
+
+       arizona = devm_kzalloc(&spi->dev, sizeof(*arizona), GFP_KERNEL);
+       if (arizona == NULL)
+               return -ENOMEM;
+
+       arizona->regmap = devm_regmap_init_spi(spi, regmap_config);
+       if (IS_ERR(arizona->regmap)) {
+               ret = PTR_ERR(arizona->regmap);
+               dev_err(&spi->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       arizona->type = id->driver_data;
+       arizona->dev = &spi->dev;
+       arizona->irq = spi->irq;
+
+       return arizona_dev_init(arizona);
+}
+
+static int __devexit arizona_spi_remove(struct spi_device *spi)
+{
+       struct arizona *arizona = dev_get_drvdata(&spi->dev);
+       arizona_dev_exit(arizona);
+       return 0;
+}
+
+static const struct spi_device_id arizona_spi_ids[] = {
+       { "wm5102", WM5102 },
+       { "wm5110", WM5110 },
+       { },
+};
+MODULE_DEVICE_TABLE(spi, arizona_spi_ids);
+
+static struct spi_driver arizona_spi_driver = {
+       .driver = {
+               .name   = "arizona",
+               .owner  = THIS_MODULE,
+               .pm     = &arizona_pm_ops,
+       },
+       .probe          = arizona_spi_probe,
+       .remove         = __devexit_p(arizona_spi_remove),
+       .id_table       = arizona_spi_ids,
+};
+
+module_spi_driver(arizona_spi_driver);
+
+MODULE_DESCRIPTION("Arizona SPI bus interface");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/arizona.h b/drivers/mfd/arizona.h
new file mode 100644 (file)
index 0000000..9798ae5
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * wm5102.h  --  WM5102 MFD internals
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM5102_H
+#define _WM5102_H
+
+#include <linux/regmap.h>
+#include <linux/pm.h>
+
+struct wm_arizona;
+
+extern const struct regmap_config wm5102_i2c_regmap;
+extern const struct regmap_config wm5102_spi_regmap;
+
+extern const struct regmap_config wm5110_i2c_regmap;
+extern const struct regmap_config wm5110_spi_regmap;
+
+extern const struct dev_pm_ops arizona_pm_ops;
+
+extern const struct regmap_irq_chip wm5102_aod;
+extern const struct regmap_irq_chip wm5102_irq;
+
+extern const struct regmap_irq_chip wm5110_aod;
+extern const struct regmap_irq_chip wm5110_irq;
+
+int arizona_dev_init(struct arizona *arizona);
+int arizona_dev_exit(struct arizona *arizona);
+int arizona_irq_init(struct arizona *arizona);
+int arizona_irq_exit(struct arizona *arizona);
+
+#endif
index 1f1313c905736f352d09c8f8d764be2464467cc5..2544910e1fd604f5f6184009a1a208fa5f838cf2 100644 (file)
@@ -772,7 +772,6 @@ EXPORT_SYMBOL_GPL(da9052_regmap_config);
 int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
 {
        struct da9052_pdata *pdata = da9052->dev->platform_data;
-       struct irq_desc *desc;
        int ret;
 
        mutex_init(&da9052->auxadc_lock);
index 50e83dc5dc49b7520dfab72c52c80bb02e485f4c..7040a0081130c93ce6b73145355abec0a8c571b8 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/uaccess.h>
 #include <linux/mfd/core.h>
 #include <linux/mfd/dbx500-prcmu.h>
+#include <linux/mfd/abx500/ab8500.h>
 #include <linux/regulator/db8500-prcmu.h>
 #include <linux/regulator/machine.h>
 #include <asm/hardware/gic.h>
@@ -2269,10 +2270,10 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
 /**
  * prcmu_ac_wake_req - should be called whenever ARM wants to wakeup Modem
  */
-void prcmu_ac_wake_req(void)
+int prcmu_ac_wake_req(void)
 {
        u32 val;
-       u32 status;
+       int ret = 0;
 
        mutex_lock(&mb0_transfer.ac_wake_lock);
 
@@ -2282,39 +2283,32 @@ void prcmu_ac_wake_req(void)
 
        atomic_set(&ac_wake_req_state, 1);
 
-retry:
-       writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), PRCM_HOSTACCESS_REQ);
+       /*
+        * Force Modem Wake-up before hostaccess_req ping-pong.
+        * It prevents Modem to enter in Sleep while acking the hostaccess
+        * request. The 31us delay has been calculated by HWI.
+        */
+       val |= PRCM_HOSTACCESS_REQ_WAKE_REQ;
+       writel(val, PRCM_HOSTACCESS_REQ);
+
+       udelay(31);
+
+       val |= PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ;
+       writel(val, PRCM_HOSTACCESS_REQ);
 
        if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
                        msecs_to_jiffies(5000))) {
+#if defined(CONFIG_DBX500_PRCMU_DEBUG)
+               db8500_prcmu_debug_dump(__func__, true, true);
+#endif
                pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
                        __func__);
-               goto unlock_and_return;
-       }
-
-       /*
-        * The modem can generate an AC_WAKE_ACK, and then still go to sleep.
-        * As a workaround, we wait, and then check that the modem is indeed
-        * awake (in terms of the value of the PRCM_MOD_AWAKE_STATUS
-        * register, which may not be the whole truth).
-        */
-       udelay(400);
-       status = (readl(PRCM_MOD_AWAKE_STATUS) & BITS(0, 2));
-       if (status != (PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE |
-                       PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE)) {
-               pr_err("prcmu: %s received ack, but modem not awake (0x%X).\n",
-                       __func__, status);
-               udelay(1200);
-               writel(val, PRCM_HOSTACCESS_REQ);
-               if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
-                               msecs_to_jiffies(5000)))
-                       goto retry;
-               pr_crit("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n",
-                       __func__);
+               ret = -EFAULT;
        }
 
 unlock_and_return:
        mutex_unlock(&mb0_transfer.ac_wake_lock);
+       return ret;
 }
 
 /**
@@ -2945,14 +2939,31 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
        },
 };
 
+static struct resource ab8500_resources[] = {
+       [0] = {
+               .start  = IRQ_DB8500_AB8500,
+               .end    = IRQ_DB8500_AB8500,
+               .flags  = IORESOURCE_IRQ
+       }
+};
+
 static struct mfd_cell db8500_prcmu_devs[] = {
        {
                .name = "db8500-prcmu-regulators",
+               .of_compatible = "stericsson,db8500-prcmu-regulator",
                .platform_data = &db8500_regulators,
                .pdata_size = sizeof(db8500_regulators),
        },
        {
                .name = "cpufreq-u8500",
+               .of_compatible = "stericsson,cpufreq-u8500",
+       },
+       {
+               .name = "ab8500-core",
+               .of_compatible = "stericsson,ab8500",
+               .num_resources = ARRAY_SIZE(ab8500_resources),
+               .resources = ab8500_resources,
+               .id = AB8500_VERSION_AB8500,
        },
 };
 
@@ -2962,8 +2973,9 @@ static struct mfd_cell db8500_prcmu_devs[] = {
  */
 static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
 {
+       struct ab8500_platform_data *ab8500_platdata = pdev->dev.platform_data;
        struct device_node *np = pdev->dev.of_node;
-       int irq = 0, err = 0;
+       int irq = 0, err = 0, i;
 
        if (ux500_is_svp())
                return -ENODEV;
@@ -2987,16 +2999,21 @@ static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
                goto no_irq_return;
        }
 
+       for (i = 0; i < ARRAY_SIZE(db8500_prcmu_devs); i++) {
+               if (!strcmp(db8500_prcmu_devs[i].name, "ab8500-core")) {
+                       db8500_prcmu_devs[i].platform_data = ab8500_platdata;
+                       db8500_prcmu_devs[i].pdata_size = sizeof(struct ab8500_platform_data);
+               }
+       }
+
        if (cpu_is_u8500v20_or_later())
                prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
 
-       if (!np) {
-               err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
-                               ARRAY_SIZE(db8500_prcmu_devs), NULL, 0);
-               if (err) {
-                       pr_err("prcmu: Failed to add subdevices\n");
-                       return err;
-               }
+       err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
+                       ARRAY_SIZE(db8500_prcmu_devs), NULL, 0);
+       if (err) {
+               pr_err("prcmu: Failed to add subdevices\n");
+               return err;
        }
 
        pr_info("DB8500 PRCMU initialized\n");
@@ -3004,11 +3021,16 @@ static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
 no_irq_return:
        return err;
 }
+static const struct of_device_id db8500_prcmu_match[] = {
+       { .compatible = "stericsson,db8500-prcmu"},
+       { },
+};
 
 static struct platform_driver db8500_prcmu_driver = {
        .driver = {
                .name = "db8500-prcmu",
                .owner = THIS_MODULE,
+               .of_match_table = db8500_prcmu_match,
        },
        .probe = db8500_prcmu_probe,
 };
@@ -3018,7 +3040,7 @@ static int __init db8500_prcmu_init(void)
        return platform_driver_register(&db8500_prcmu_driver);
 }
 
-arch_initcall(db8500_prcmu_init);
+core_initcall(db8500_prcmu_init);
 
 MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com>");
 MODULE_DESCRIPTION("DB8500 PRCM Unit driver");
index 3a0bf91d7780894a552f0f83d0cd9defb4637c63..23108a6e316782612eafda90cb2944b37a644e0e 100644 (file)
 
 #define PRCM_HOSTACCESS_REQ    (_PRCMU_BASE + 0x334)
 #define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1
+#define PRCM_HOSTACCESS_REQ_WAKE_REQ   BIT(16)
 #define ARM_WAKEUP_MODEM       0x1
 
 #define PRCM_ARM_IT1_CLR       (_PRCMU_BASE + 0x48C)
diff --git a/drivers/mfd/max77686-irq.c b/drivers/mfd/max77686-irq.c
new file mode 100644 (file)
index 0000000..cdc3280
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * max77686-irq.c - Interrupt controller support for MAX77686
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ * Chiwoong Byun <woong.byun@samsung.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
+ *
+ * This driver is based on max8997-irq.c
+ */
+
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/irqdomain.h>
+#include <linux/regmap.h>
+
+enum {
+       MAX77686_DEBUG_IRQ_INFO = 1 << 0,
+       MAX77686_DEBUG_IRQ_MASK = 1 << 1,
+       MAX77686_DEBUG_IRQ_INT = 1 << 2,
+};
+
+static int debug_mask = 0;
+module_param(debug_mask, int, 0);
+MODULE_PARM_DESC(debug_mask, "Set debug_mask : 0x0=off 0x1=IRQ_INFO  0x2=IRQ_MASK 0x4=IRQ_INI)");
+
+static const u8 max77686_mask_reg[] = {
+       [PMIC_INT1] = MAX77686_REG_INT1MSK,
+       [PMIC_INT2] = MAX77686_REG_INT2MSK,
+       [RTC_INT] = MAX77686_RTC_INTM,
+};
+
+static struct regmap *max77686_get_regmap(struct max77686_dev *max77686,
+                               enum max77686_irq_source src)
+{
+       switch (src) {
+       case PMIC_INT1 ... PMIC_INT2:
+               return max77686->regmap;
+       case RTC_INT:
+               return max77686->rtc_regmap;
+       default:
+               return ERR_PTR(-EINVAL);
+       }
+}
+
+struct max77686_irq_data {
+       int mask;
+       enum max77686_irq_source group;
+};
+
+#define DECLARE_IRQ(idx, _group, _mask)                \
+       [(idx)] = { .group = (_group), .mask = (_mask) }
+static const struct max77686_irq_data max77686_irqs[] = {
+       DECLARE_IRQ(MAX77686_PMICIRQ_PWRONF,    PMIC_INT1, 1 << 0),
+       DECLARE_IRQ(MAX77686_PMICIRQ_PWRONR,    PMIC_INT1, 1 << 1),
+       DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBF,   PMIC_INT1, 1 << 2),
+       DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBR,   PMIC_INT1, 1 << 3),
+       DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBF,    PMIC_INT1, 1 << 4),
+       DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBR,    PMIC_INT1, 1 << 5),
+       DECLARE_IRQ(MAX77686_PMICIRQ_ONKEY1S,   PMIC_INT1, 1 << 6),
+       DECLARE_IRQ(MAX77686_PMICIRQ_MRSTB,             PMIC_INT1, 1 << 7),
+       DECLARE_IRQ(MAX77686_PMICIRQ_140C,              PMIC_INT2, 1 << 0),
+       DECLARE_IRQ(MAX77686_PMICIRQ_120C,              PMIC_INT2, 1 << 1),
+       DECLARE_IRQ(MAX77686_RTCIRQ_RTC60S,             RTC_INT, 1 << 0),
+       DECLARE_IRQ(MAX77686_RTCIRQ_RTCA1,              RTC_INT, 1 << 1),
+       DECLARE_IRQ(MAX77686_RTCIRQ_RTCA2,              RTC_INT, 1 << 2),
+       DECLARE_IRQ(MAX77686_RTCIRQ_SMPL,               RTC_INT, 1 << 3),
+       DECLARE_IRQ(MAX77686_RTCIRQ_RTC1S,              RTC_INT, 1 << 4),
+       DECLARE_IRQ(MAX77686_RTCIRQ_WTSR,               RTC_INT, 1 << 5),
+};
+
+static void max77686_irq_lock(struct irq_data *data)
+{
+       struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+
+       if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+               pr_info("%s\n", __func__);
+
+       mutex_lock(&max77686->irqlock);
+}
+
+static void max77686_irq_sync_unlock(struct irq_data *data)
+{
+       struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+       int i;
+
+       for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
+               u8 mask_reg = max77686_mask_reg[i];
+               struct regmap *map = max77686_get_regmap(max77686, i);
+
+               if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+                       pr_debug("%s: mask_reg[%d]=0x%x, cur=0x%x\n",
+                       __func__, i, mask_reg, max77686->irq_masks_cur[i]);
+
+               if (mask_reg == MAX77686_REG_INVALID ||
+                               IS_ERR_OR_NULL(map))
+                       continue;
+
+               max77686->irq_masks_cache[i] = max77686->irq_masks_cur[i];
+
+               regmap_write(map, max77686_mask_reg[i],
+                               max77686->irq_masks_cur[i]);
+       }
+
+       mutex_unlock(&max77686->irqlock);
+}
+
+static const inline struct max77686_irq_data *to_max77686_irq(int irq)
+{
+       struct irq_data *data = irq_get_irq_data(irq);
+       return &max77686_irqs[data->hwirq];
+}
+
+static void max77686_irq_mask(struct irq_data *data)
+{
+       struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+       const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq);
+
+       max77686->irq_masks_cur[irq_data->group] |= irq_data->mask;
+
+       if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+               pr_info("%s: group=%d, cur=0x%x\n",
+                       __func__, irq_data->group,
+                       max77686->irq_masks_cur[irq_data->group]);
+}
+
+static void max77686_irq_unmask(struct irq_data *data)
+{
+       struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+       const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq);
+
+       max77686->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
+
+       if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+               pr_info("%s: group=%d, cur=0x%x\n",
+                       __func__, irq_data->group,
+                       max77686->irq_masks_cur[irq_data->group]);
+}
+
+static struct irq_chip max77686_irq_chip = {
+       .name                   = "max77686",
+       .irq_bus_lock           = max77686_irq_lock,
+       .irq_bus_sync_unlock    = max77686_irq_sync_unlock,
+       .irq_mask               = max77686_irq_mask,
+       .irq_unmask             = max77686_irq_unmask,
+};
+
+static irqreturn_t max77686_irq_thread(int irq, void *data)
+{
+       struct max77686_dev *max77686 = data;
+       unsigned int irq_reg[MAX77686_IRQ_GROUP_NR] = {};
+       unsigned int irq_src;
+       int ret;
+       int i, cur_irq;
+
+       ret = regmap_read(max77686->regmap,  MAX77686_REG_INTSRC, &irq_src);
+       if (ret < 0) {
+               dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
+                               ret);
+               return IRQ_NONE;
+       }
+
+       if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+               pr_info("%s: irq_src=0x%x\n", __func__, irq_src);
+
+       if (irq_src == MAX77686_IRQSRC_PMIC) {
+               ret = regmap_bulk_read(max77686->regmap,
+                                        MAX77686_REG_INT1, irq_reg, 2);
+               if (ret < 0) {
+                       dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
+                                       ret);
+                       return IRQ_NONE;
+               }
+
+               if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+                       pr_info("%s: int1=0x%x, int2=0x%x\n", __func__,
+                                irq_reg[PMIC_INT1], irq_reg[PMIC_INT2]);
+       }
+
+       if (irq_src & MAX77686_IRQSRC_RTC) {
+               ret = regmap_read(max77686->rtc_regmap,
+                                       MAX77686_RTC_INT, &irq_reg[RTC_INT]);
+               if (ret < 0) {
+                       dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
+                                       ret);
+                       return IRQ_NONE;
+               }
+
+               if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+                       pr_info("%s: rtc int=0x%x\n", __func__,
+                                                        irq_reg[RTC_INT]);
+
+       }
+
+       for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++)
+               irq_reg[i] &= ~max77686->irq_masks_cur[i];
+
+       for (i = 0; i < MAX77686_IRQ_NR; i++) {
+               if (irq_reg[max77686_irqs[i].group] & max77686_irqs[i].mask) {
+                       cur_irq = irq_find_mapping(max77686->irq_domain, i);
+                       if (cur_irq)
+                               handle_nested_irq(cur_irq);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int max77686_irq_domain_map(struct irq_domain *d, unsigned int irq,
+                                       irq_hw_number_t hw)
+{
+       struct max77686_dev *max77686 = d->host_data;
+
+       irq_set_chip_data(irq, max77686);
+       irq_set_chip_and_handler(irq, &max77686_irq_chip, handle_edge_irq);
+       irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+       set_irq_flags(irq, IRQF_VALID);
+#else
+       irq_set_noprobe(irq);
+#endif
+       return 0;
+}
+
+static struct irq_domain_ops max77686_irq_domain_ops = {
+       .map = max77686_irq_domain_map,
+};
+
+int max77686_irq_init(struct max77686_dev *max77686)
+{
+       struct irq_domain *domain;
+       int i;
+       int ret;
+       int val;
+       struct regmap *map;
+
+       mutex_init(&max77686->irqlock);
+
+       if (max77686->irq_gpio && !max77686->irq) {
+               max77686->irq = gpio_to_irq(max77686->irq_gpio);
+
+               if (debug_mask & MAX77686_DEBUG_IRQ_INT) {
+                       ret = gpio_request(max77686->irq_gpio, "pmic_irq");
+                       if (ret < 0) {
+                               dev_err(max77686->dev,
+                                       "Failed to request gpio %d with ret:"
+                                       "%d\n", max77686->irq_gpio, ret);
+                               return IRQ_NONE;
+                       }
+
+                       gpio_direction_input(max77686->irq_gpio);
+                       val = gpio_get_value(max77686->irq_gpio);
+                       gpio_free(max77686->irq_gpio);
+                       pr_info("%s: gpio_irq=%x\n", __func__, val);
+               }
+       }
+
+       if (!max77686->irq) {
+               dev_err(max77686->dev, "irq is not specified\n");
+               return -ENODEV;
+       }
+
+       /* Mask individual interrupt sources */
+       for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
+               max77686->irq_masks_cur[i] = 0xff;
+               max77686->irq_masks_cache[i] = 0xff;
+               map = max77686_get_regmap(max77686, i);
+
+               if (IS_ERR_OR_NULL(map))
+                       continue;
+               if (max77686_mask_reg[i] == MAX77686_REG_INVALID)
+                       continue;
+
+               regmap_write(map, max77686_mask_reg[i], 0xff);
+       }
+       domain = irq_domain_add_linear(NULL, MAX77686_IRQ_NR,
+                                       &max77686_irq_domain_ops, max77686);
+       if (!domain) {
+               dev_err(max77686->dev, "could not create irq domain\n");
+               return -ENODEV;
+       }
+       max77686->irq_domain = domain;
+
+       ret = request_threaded_irq(max77686->irq, NULL, max77686_irq_thread,
+                                  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                  "max77686-irq", max77686);
+
+       if (ret)
+               dev_err(max77686->dev, "Failed to request IRQ %d: %d\n",
+                       max77686->irq, ret);
+
+
+       if (debug_mask & MAX77686_DEBUG_IRQ_INFO)
+               pr_info("%s-\n", __func__);
+
+       return 0;
+}
+
+void max77686_irq_exit(struct max77686_dev *max77686)
+{
+       if (max77686->irq)
+               free_irq(max77686->irq, max77686);
+}
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
new file mode 100644 (file)
index 0000000..c03e12b
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * max77686.c - mfd core driver for the Maxim 77686
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Chiwoong Byun <woong.byun@smasung.com>
+ * Jonghwa Lee <jonghwa3.lee@samsung.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
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/err.h>
+
+#define I2C_ADDR_RTC   (0x0C >> 1)
+
+static struct mfd_cell max77686_devs[] = {
+       { .name = "max77686-pmic", },
+       { .name = "max77686-rtc", },
+};
+
+static struct regmap_config max77686_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+};
+
+#ifdef CONFIG_OF
+static struct of_device_id __devinitdata max77686_pmic_dt_match[] = {
+       {.compatible = "maxim,max77686",        .data = 0},
+       {},
+};
+
+static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(struct device
+                                                                 *dev)
+{
+       struct max77686_platform_data *pd;
+
+       pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+       if (!pd) {
+               dev_err(dev, "could not allocate memory for pdata\n");
+               return NULL;
+       }
+
+       dev->platform_data = pd;
+       return pd;
+}
+#else
+static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(struct device
+                                                                 *dev)
+{
+       return 0;
+}
+#endif
+
+static int max77686_i2c_probe(struct i2c_client *i2c,
+                             const struct i2c_device_id *id)
+{
+       struct max77686_dev *max77686 = NULL;
+       struct max77686_platform_data *pdata = i2c->dev.platform_data;
+       unsigned int data;
+       int ret = 0;
+
+       if (i2c->dev.of_node)
+               pdata = max77686_i2c_parse_dt_pdata(&i2c->dev);
+
+       if (!pdata) {
+               ret = -EIO;
+               dev_err(&i2c->dev, "No platform data found.\n");
+               goto err;
+       }
+
+       max77686 = kzalloc(sizeof(struct max77686_dev), GFP_KERNEL);
+       if (max77686 == NULL)
+               return -ENOMEM;
+
+       max77686->regmap = regmap_init_i2c(i2c, &max77686_regmap_config);
+       if (IS_ERR(max77686->regmap)) {
+               ret = PTR_ERR(max77686->regmap);
+               dev_err(max77686->dev, "Failed to allocate register map: %d\n",
+                               ret);
+               kfree(max77686);
+               return ret;
+       }
+
+       i2c_set_clientdata(i2c, max77686);
+       max77686->dev = &i2c->dev;
+       max77686->i2c = i2c;
+       max77686->type = id->driver_data;
+
+       max77686->wakeup = pdata->wakeup;
+       max77686->irq_gpio = pdata->irq_gpio;
+       max77686->irq = i2c->irq;
+
+       if (regmap_read(max77686->regmap,
+                        MAX77686_REG_DEVICE_ID, &data) < 0) {
+               dev_err(max77686->dev,
+                       "device not found on this channel (this is not an error)\n");
+               ret = -ENODEV;
+               goto err;
+       } else
+               dev_info(max77686->dev, "device found\n");
+
+       max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
+       i2c_set_clientdata(max77686->rtc, max77686);
+
+       max77686_irq_init(max77686);
+
+       ret = mfd_add_devices(max77686->dev, -1, max77686_devs,
+                             ARRAY_SIZE(max77686_devs), NULL, 0);
+
+       if (ret < 0)
+               goto err_mfd;
+
+       return ret;
+
+err_mfd:
+       mfd_remove_devices(max77686->dev);
+       i2c_unregister_device(max77686->rtc);
+err:
+       kfree(max77686);
+       return ret;
+}
+
+static int max77686_i2c_remove(struct i2c_client *i2c)
+{
+       struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+       mfd_remove_devices(max77686->dev);
+       i2c_unregister_device(max77686->rtc);
+       kfree(max77686);
+
+       return 0;
+}
+
+static const struct i2c_device_id max77686_i2c_id[] = {
+       { "max77686", TYPE_MAX77686 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max77686_i2c_id);
+
+static struct i2c_driver max77686_i2c_driver = {
+       .driver = {
+                  .name = "max77686",
+                  .owner = THIS_MODULE,
+                  .of_match_table = of_match_ptr(max77686_pmic_dt_match),
+       },
+       .probe = max77686_i2c_probe,
+       .remove = max77686_i2c_remove,
+       .id_table = max77686_i2c_id,
+};
+
+static int __init max77686_i2c_init(void)
+{
+       return i2c_add_driver(&max77686_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(max77686_i2c_init);
+
+static void __exit max77686_i2c_exit(void)
+{
+       i2c_del_driver(&max77686_i2c_driver);
+}
+module_exit(max77686_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 77686 multi-function core driver");
+MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>");
+MODULE_LICENSE("GPL");
index e9e4278722f3af307fdfb53b5413c2d5dc61daf1..a1811cb50ec75fc7c1dffd02aca98f132d7e4810 100644 (file)
@@ -138,8 +138,6 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
 
        max77693->wakeup = pdata->wakeup;
 
-       mutex_init(&max77693->iolock);
-
        if (max77693_read_reg(max77693->regmap,
                                MAX77693_PMIC_REG_PMIC_ID2, &reg_data) < 0) {
                dev_err(max77693->dev, "device not found on this channel\n");
@@ -156,7 +154,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
 
        ret = max77693_irq_init(max77693);
        if (ret < 0)
-               goto err_mfd;
+               goto err_irq;
 
        pm_runtime_set_active(max77693->dev);
 
@@ -170,11 +168,11 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
        return ret;
 
 err_mfd:
+       max77693_irq_exit(max77693);
+err_irq:
        i2c_unregister_device(max77693->muic);
        i2c_unregister_device(max77693->haptic);
 err_regmap:
-       kfree(max77693);
-
        return ret;
 }
 
@@ -183,6 +181,7 @@ static int max77693_i2c_remove(struct i2c_client *i2c)
        struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
 
        mfd_remove_devices(max77693->dev);
+       max77693_irq_exit(max77693);
        i2c_unregister_device(max77693->muic);
        i2c_unregister_device(max77693->haptic);
 
@@ -215,7 +214,7 @@ static int max77693_resume(struct device *dev)
        return max77693_irq_resume(max77693);
 }
 
-const struct dev_pm_ops max77693_pm = {
+static const struct dev_pm_ops max77693_pm = {
        .suspend = max77693_suspend,
        .resume = max77693_resume,
 };
index ca881efedf75a43f7543e19d506702ab1518e0ec..825a7f06d9ba5ade6281810bec19c209187561b2 100644 (file)
@@ -75,9 +75,9 @@ static struct mfd_cell power_devs[] = {
 static struct resource rtc_resources[] = {
        {
                .name   = "max8925-rtc",
-               .start  = MAX8925_RTC_IRQ,
-               .end    = MAX8925_RTC_IRQ_MASK,
-               .flags  = IORESOURCE_IO,
+               .start  = MAX8925_IRQ_RTC_ALARM0,
+               .end    = MAX8925_IRQ_RTC_ALARM0,
+               .flags  = IORESOURCE_IRQ,
        },
 };
 
@@ -598,7 +598,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
 
        ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
                              ARRAY_SIZE(rtc_devs),
-                             &rtc_resources[0], 0);
+                             &rtc_resources[0], chip->irq_base);
        if (ret < 0) {
                dev_err(chip->dev, "Failed to add rtc subdev\n");
                goto out;
index 09274cf7c33bd0bd7d21ca2ee4cd12f78330bcfb..43fa61413e935d5c5ce4a46016df34895ecb2b1d 100644 (file)
@@ -142,7 +142,8 @@ static void max8997_irq_sync_unlock(struct irq_data *data)
 static const inline struct max8997_irq_data *
 irq_to_max8997_irq(struct max8997_dev *max8997, int irq)
 {
-       return &max8997_irqs[irq - max8997->irq_base];
+       struct irq_data *data = irq_get_irq_data(irq);
+       return &max8997_irqs[data->hwirq];
 }
 
 static void max8997_irq_mask(struct irq_data *data)
@@ -182,7 +183,7 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
        u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {};
        u8 irq_src;
        int ret;
-       int i;
+       int i, cur_irq;
 
        ret = max8997_read_reg(max8997->i2c, MAX8997_REG_INTSRC, &irq_src);
        if (ret < 0) {
@@ -269,8 +270,11 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
 
        /* Report */
        for (i = 0; i < MAX8997_IRQ_NR; i++) {
-               if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask)
-                       handle_nested_irq(max8997->irq_base + i);
+               if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) {
+                       cur_irq = irq_find_mapping(max8997->irq_domain, i);
+                       if (cur_irq)
+                               handle_nested_irq(cur_irq);
+               }
        }
 
        return IRQ_HANDLED;
@@ -278,26 +282,40 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
 
 int max8997_irq_resume(struct max8997_dev *max8997)
 {
-       if (max8997->irq && max8997->irq_base)
-               max8997_irq_thread(max8997->irq_base, max8997);
+       if (max8997->irq && max8997->irq_domain)
+               max8997_irq_thread(0, max8997);
+       return 0;
+}
+
+static int max8997_irq_domain_map(struct irq_domain *d, unsigned int irq,
+                                       irq_hw_number_t hw)
+{
+       struct max8997_dev *max8997 = d->host_data;
+
+       irq_set_chip_data(irq, max8997);
+       irq_set_chip_and_handler(irq, &max8997_irq_chip, handle_edge_irq);
+       irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+       set_irq_flags(irq, IRQF_VALID);
+#else
+       irq_set_noprobe(irq);
+#endif
        return 0;
 }
 
+static struct irq_domain_ops max8997_irq_domain_ops = {
+       .map = max8997_irq_domain_map,
+};
+
 int max8997_irq_init(struct max8997_dev *max8997)
 {
+       struct irq_domain *domain;
        int i;
-       int cur_irq;
        int ret;
        u8 val;
 
        if (!max8997->irq) {
                dev_warn(max8997->dev, "No interrupt specified.\n");
-               max8997->irq_base = 0;
-               return 0;
-       }
-
-       if (!max8997->irq_base) {
-               dev_err(max8997->dev, "No interrupt base specified.\n");
                return 0;
        }
 
@@ -327,19 +345,13 @@ int max8997_irq_init(struct max8997_dev *max8997)
                                        true : false;
        }
 
-       /* Register with genirq */
-       for (i = 0; i < MAX8997_IRQ_NR; i++) {
-               cur_irq = i + max8997->irq_base;
-               irq_set_chip_data(cur_irq, max8997);
-               irq_set_chip_and_handler(cur_irq, &max8997_irq_chip,
-                               handle_edge_irq);
-               irq_set_nested_thread(cur_irq, 1);
-#ifdef CONFIG_ARM
-               set_irq_flags(cur_irq, IRQF_VALID);
-#else
-               irq_set_noprobe(cur_irq);
-#endif
+       domain = irq_domain_add_linear(NULL, MAX8997_IRQ_NR,
+                                       &max8997_irq_domain_ops, max8997);
+       if (!domain) {
+               dev_err(max8997->dev, "could not create irq domain\n");
+               return -ENODEV;
        }
+       max8997->irq_domain = domain;
 
        ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread,
                        IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
index cb83a7ab53e73b9825c4eb527211ab49ccd33a7e..10b629c245b6770d304dde9bf978891c2a5ac800 100644 (file)
@@ -143,7 +143,6 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
        if (!pdata)
                goto err;
 
-       max8997->irq_base = pdata->irq_base;
        max8997->ono = pdata->ono;
 
        mutex_init(&max8997->iolock);
@@ -206,7 +205,7 @@ static const struct i2c_device_id max8997_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, max8998_i2c_id);
 
-u8 max8997_dumpaddr_pmic[] = {
+static u8 max8997_dumpaddr_pmic[] = {
        MAX8997_REG_INT1MSK,
        MAX8997_REG_INT2MSK,
        MAX8997_REG_INT3MSK,
@@ -331,7 +330,7 @@ u8 max8997_dumpaddr_pmic[] = {
        MAX8997_REG_DVSOKTIMER5,
 };
 
-u8 max8997_dumpaddr_muic[] = {
+static u8 max8997_dumpaddr_muic[] = {
        MAX8997_MUIC_REG_INTMASK1,
        MAX8997_MUIC_REG_INTMASK2,
        MAX8997_MUIC_REG_INTMASK3,
@@ -341,7 +340,7 @@ u8 max8997_dumpaddr_muic[] = {
        MAX8997_MUIC_REG_CONTROL3,
 };
 
-u8 max8997_dumpaddr_haptic[] = {
+static u8 max8997_dumpaddr_haptic[] = {
        MAX8997_HAPTIC_REG_CONF1,
        MAX8997_HAPTIC_REG_CONF2,
        MAX8997_HAPTIC_REG_DRVCONF,
@@ -423,7 +422,7 @@ static int max8997_resume(struct device *dev)
        return max8997_irq_resume(max8997);
 }
 
-const struct dev_pm_ops max8997_pm = {
+static const struct dev_pm_ops max8997_pm = {
        .suspend = max8997_suspend,
        .resume = max8997_resume,
        .freeze = max8997_freeze,
index f0ea3b8b3e4ad979d5e1d5fe53bde83a7392dfd2..b801dc72f041a125fcf9a52e25e6d594ee052d92 100644 (file)
@@ -723,10 +723,6 @@ void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx)
        free_irq(mc13xxx->irq, mc13xxx);
 
        mfd_remove_devices(mc13xxx->dev);
-
-       regmap_exit(mc13xxx->regmap);
-
-       kfree(mc13xxx);
 }
 EXPORT_SYMBOL_GPL(mc13xxx_common_cleanup);
 
index d22501dad6a688c11512f27ca2b0477188b57cf9..9d18dde3cd2aafd690ad85246ad0f78a70014a08 100644 (file)
@@ -53,17 +53,11 @@ static struct regmap_config mc13xxx_regmap_i2c_config = {
 static int mc13xxx_i2c_probe(struct i2c_client *client,
                const struct i2c_device_id *id)
 {
-       const struct of_device_id *of_id;
-       struct i2c_driver *idrv = to_i2c_driver(client->dev.driver);
        struct mc13xxx *mc13xxx;
        struct mc13xxx_platform_data *pdata = dev_get_platdata(&client->dev);
        int ret;
 
-       of_id = of_match_device(mc13xxx_dt_ids, &client->dev);
-       if (of_id)
-               idrv->id_table = (const struct i2c_device_id*) of_id->data;
-
-       mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
+       mc13xxx = devm_kzalloc(&client->dev, sizeof(*mc13xxx), GFP_KERNEL);
        if (!mc13xxx)
                return -ENOMEM;
 
@@ -72,13 +66,13 @@ static int mc13xxx_i2c_probe(struct i2c_client *client,
        mc13xxx->dev = &client->dev;
        mutex_init(&mc13xxx->lock);
 
-       mc13xxx->regmap = regmap_init_i2c(client, &mc13xxx_regmap_i2c_config);
+       mc13xxx->regmap = devm_regmap_init_i2c(client,
+                                              &mc13xxx_regmap_i2c_config);
        if (IS_ERR(mc13xxx->regmap)) {
                ret = PTR_ERR(mc13xxx->regmap);
                dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n",
                                ret);
                dev_set_drvdata(&client->dev, NULL);
-               kfree(mc13xxx);
                return ret;
        }
 
index 03df422feb763ab075ac5fa741ef09b71c066d1d..0bdb43a0aff0756513a06d2184bc16db41fdc88c 100644 (file)
@@ -119,17 +119,11 @@ static struct regmap_bus regmap_mc13xxx_bus = {
 
 static int mc13xxx_spi_probe(struct spi_device *spi)
 {
-       const struct of_device_id *of_id;
-       struct spi_driver *sdrv = to_spi_driver(spi->dev.driver);
        struct mc13xxx *mc13xxx;
        struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
        int ret;
 
-       of_id = of_match_device(mc13xxx_dt_ids, &spi->dev);
-       if (of_id)
-               sdrv->id_table = &mc13xxx_device_id[(enum mc13xxx_id) of_id->data];
-
-       mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
+       mc13xxx = devm_kzalloc(&spi->dev, sizeof(*mc13xxx), GFP_KERNEL);
        if (!mc13xxx)
                return -ENOMEM;
 
@@ -139,15 +133,14 @@ static int mc13xxx_spi_probe(struct spi_device *spi)
        mc13xxx->dev = &spi->dev;
        mutex_init(&mc13xxx->lock);
 
-       mc13xxx->regmap = regmap_init(&spi->dev, &regmap_mc13xxx_bus, &spi->dev,
-                                       &mc13xxx_regmap_spi_config);
-
+       mc13xxx->regmap = devm_regmap_init(&spi->dev, &regmap_mc13xxx_bus,
+                                          &spi->dev,
+                                          &mc13xxx_regmap_spi_config);
        if (IS_ERR(mc13xxx->regmap)) {
                ret = PTR_ERR(mc13xxx->regmap);
                dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n",
                                ret);
                dev_set_drvdata(&spi->dev, NULL);
-               kfree(mc13xxx);
                return ret;
        }
 
index ffc3d48676ae67c5c838700d212310025ec8f2da..0c3a01cde2f7615960fb2c9cc20ba7489bf00fbd 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
 
 int mfd_cell_enable(struct platform_device *pdev)
 {
@@ -76,6 +78,8 @@ static int mfd_add_device(struct device *parent, int id,
 {
        struct resource *res;
        struct platform_device *pdev;
+       struct device_node *np = NULL;
+       struct irq_domain *domain = NULL;
        int ret = -ENOMEM;
        int r;
 
@@ -89,6 +93,16 @@ static int mfd_add_device(struct device *parent, int id,
 
        pdev->dev.parent = parent;
 
+       if (parent->of_node && cell->of_compatible) {
+               for_each_child_of_node(parent->of_node, np) {
+                       if (of_device_is_compatible(np, cell->of_compatible)) {
+                               pdev->dev.of_node = np;
+                               domain = irq_find_host(parent->of_node);
+                               break;
+                       }
+               }
+       }
+
        if (cell->pdata_size) {
                ret = platform_device_add_data(pdev,
                                        cell->platform_data, cell->pdata_size);
@@ -112,10 +126,18 @@ static int mfd_add_device(struct device *parent, int id,
                        res[r].end = mem_base->start +
                                cell->resources[r].end;
                } else if (cell->resources[r].flags & IORESOURCE_IRQ) {
-                       res[r].start = irq_base +
-                               cell->resources[r].start;
-                       res[r].end   = irq_base +
-                               cell->resources[r].end;
+                       if (domain) {
+                               /* Unable to create mappings for IRQ ranges. */
+                               WARN_ON(cell->resources[r].start !=
+                                       cell->resources[r].end);
+                               res[r].start = res[r].end = irq_create_mapping(
+                                       domain, cell->resources[r].start);
+                       } else {
+                               res[r].start = irq_base +
+                                       cell->resources[r].start;
+                               res[r].end   = irq_base +
+                                       cell->resources[r].end;
+                       }
                } else {
                        res[r].parent = cell->resources[r].parent;
                        res[r].start = cell->resources[r].start;
index 29c122bf28ea723e36cafccda86eb09319d595ac..45ce1fb5a54998dee46e4f523c3428e172cba2a4 100644 (file)
@@ -253,8 +253,13 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
                }
 
                pdev->dev.parent = pcf->dev;
-               platform_device_add_data(pdev, &pdata->reg_init_data[i],
-                                       sizeof(pdata->reg_init_data[i]));
+               if (platform_device_add_data(pdev, &pdata->reg_init_data[i],
+                                       sizeof(pdata->reg_init_data[i])) < 0) {
+                       platform_device_put(pdev);
+                       dev_err(pcf->dev, "Out of memory for regulator parameters %d\n",
+                                                                       i);
+                       continue;
+               }
                pcf->regulator_pdev[i] = pdev;
 
                platform_device_add(pdev);
diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c
deleted file mode 100644 (file)
index dd17030..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * s5m87xx.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd
- *              http://www.samsung.com
- *
- *  This program is free software; you can redistribute  it and/or modify it
- *  under  the terms of  the GNU General  Public License as published by the
- *  Free Software Foundation;  either version 2 of the  License, or (at your
- *  option) any later version.
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/pm_runtime.h>
-#include <linux/mutex.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/s5m87xx/s5m-core.h>
-#include <linux/mfd/s5m87xx/s5m-pmic.h>
-#include <linux/mfd/s5m87xx/s5m-rtc.h>
-#include <linux/regmap.h>
-
-static struct mfd_cell s5m8751_devs[] = {
-       {
-               .name = "s5m8751-pmic",
-       }, {
-               .name = "s5m-charger",
-       }, {
-               .name = "s5m8751-codec",
-       },
-};
-
-static struct mfd_cell s5m8763_devs[] = {
-       {
-               .name = "s5m8763-pmic",
-       }, {
-               .name = "s5m-rtc",
-       }, {
-               .name = "s5m-charger",
-       },
-};
-
-static struct mfd_cell s5m8767_devs[] = {
-       {
-               .name = "s5m8767-pmic",
-       }, {
-               .name = "s5m-rtc",
-       },
-};
-
-int s5m_reg_read(struct s5m87xx_dev *s5m87xx, u8 reg, void *dest)
-{
-       return regmap_read(s5m87xx->regmap, reg, dest);
-}
-EXPORT_SYMBOL_GPL(s5m_reg_read);
-
-int s5m_bulk_read(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf)
-{
-       return regmap_bulk_read(s5m87xx->regmap, reg, buf, count);
-}
-EXPORT_SYMBOL_GPL(s5m_bulk_read);
-
-int s5m_reg_write(struct s5m87xx_dev *s5m87xx, u8 reg, u8 value)
-{
-       return regmap_write(s5m87xx->regmap, reg, value);
-}
-EXPORT_SYMBOL_GPL(s5m_reg_write);
-
-int s5m_bulk_write(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf)
-{
-       return regmap_raw_write(s5m87xx->regmap, reg, buf, count);
-}
-EXPORT_SYMBOL_GPL(s5m_bulk_write);
-
-int s5m_reg_update(struct s5m87xx_dev *s5m87xx, u8 reg, u8 val, u8 mask)
-{
-       return regmap_update_bits(s5m87xx->regmap, reg, mask, val);
-}
-EXPORT_SYMBOL_GPL(s5m_reg_update);
-
-static struct regmap_config s5m_regmap_config = {
-       .reg_bits = 8,
-       .val_bits = 8,
-};
-
-static int s5m87xx_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
-{
-       struct s5m_platform_data *pdata = i2c->dev.platform_data;
-       struct s5m87xx_dev *s5m87xx;
-       int ret;
-
-       s5m87xx = devm_kzalloc(&i2c->dev, sizeof(struct s5m87xx_dev),
-                               GFP_KERNEL);
-       if (s5m87xx == NULL)
-               return -ENOMEM;
-
-       i2c_set_clientdata(i2c, s5m87xx);
-       s5m87xx->dev = &i2c->dev;
-       s5m87xx->i2c = i2c;
-       s5m87xx->irq = i2c->irq;
-       s5m87xx->type = id->driver_data;
-
-       if (pdata) {
-               s5m87xx->device_type = pdata->device_type;
-               s5m87xx->ono = pdata->ono;
-               s5m87xx->irq_base = pdata->irq_base;
-               s5m87xx->wakeup = pdata->wakeup;
-       }
-
-       s5m87xx->regmap = devm_regmap_init_i2c(i2c, &s5m_regmap_config);
-       if (IS_ERR(s5m87xx->regmap)) {
-               ret = PTR_ERR(s5m87xx->regmap);
-               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
-                       ret);
-               return ret;
-       }
-
-       s5m87xx->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
-       i2c_set_clientdata(s5m87xx->rtc, s5m87xx);
-
-       if (pdata && pdata->cfg_pmic_irq)
-               pdata->cfg_pmic_irq();
-
-       s5m_irq_init(s5m87xx);
-
-       pm_runtime_set_active(s5m87xx->dev);
-
-       switch (s5m87xx->device_type) {
-       case S5M8751X:
-               ret = mfd_add_devices(s5m87xx->dev, -1, s5m8751_devs,
-                                       ARRAY_SIZE(s5m8751_devs), NULL, 0);
-               break;
-       case S5M8763X:
-               ret = mfd_add_devices(s5m87xx->dev, -1, s5m8763_devs,
-                                       ARRAY_SIZE(s5m8763_devs), NULL, 0);
-               break;
-       case S5M8767X:
-               ret = mfd_add_devices(s5m87xx->dev, -1, s5m8767_devs,
-                                       ARRAY_SIZE(s5m8767_devs), NULL, 0);
-               break;
-       default:
-               /* If this happens the probe function is problem */
-               BUG();
-       }
-
-       if (ret < 0)
-               goto err;
-
-       return ret;
-
-err:
-       mfd_remove_devices(s5m87xx->dev);
-       s5m_irq_exit(s5m87xx);
-       i2c_unregister_device(s5m87xx->rtc);
-       return ret;
-}
-
-static int s5m87xx_i2c_remove(struct i2c_client *i2c)
-{
-       struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
-
-       mfd_remove_devices(s5m87xx->dev);
-       s5m_irq_exit(s5m87xx);
-       i2c_unregister_device(s5m87xx->rtc);
-       return 0;
-}
-
-static const struct i2c_device_id s5m87xx_i2c_id[] = {
-       { "s5m87xx", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, s5m87xx_i2c_id);
-
-static struct i2c_driver s5m87xx_i2c_driver = {
-       .driver = {
-                  .name = "s5m87xx",
-                  .owner = THIS_MODULE,
-       },
-       .probe = s5m87xx_i2c_probe,
-       .remove = s5m87xx_i2c_remove,
-       .id_table = s5m87xx_i2c_id,
-};
-
-static int __init s5m87xx_i2c_init(void)
-{
-       return i2c_add_driver(&s5m87xx_i2c_driver);
-}
-
-subsys_initcall(s5m87xx_i2c_init);
-
-static void __exit s5m87xx_i2c_exit(void)
-{
-       i2c_del_driver(&s5m87xx_i2c_driver);
-}
-module_exit(s5m87xx_i2c_exit);
-
-MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
-MODULE_DESCRIPTION("Core support for the S5M MFD");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/s5m-irq.c b/drivers/mfd/s5m-irq.c
deleted file mode 100644 (file)
index 0236676..0000000
+++ /dev/null
@@ -1,495 +0,0 @@
-/*
- * s5m-irq.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd
- *              http://www.samsung.com
- *
- *  This program is free software; you can redistribute  it and/or modify it
- *  under  the terms of  the GNU General  Public License as published by the
- *  Free Software Foundation;  either version 2 of the  License, or (at your
- *  option) any later version.
- *
- */
-
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/mfd/s5m87xx/s5m-core.h>
-
-struct s5m_irq_data {
-       int reg;
-       int mask;
-};
-
-static struct s5m_irq_data s5m8767_irqs[] = {
-       [S5M8767_IRQ_PWRR] = {
-               .reg = 1,
-               .mask = S5M8767_IRQ_PWRR_MASK,
-       },
-       [S5M8767_IRQ_PWRF] = {
-               .reg = 1,
-               .mask = S5M8767_IRQ_PWRF_MASK,
-       },
-       [S5M8767_IRQ_PWR1S] = {
-               .reg = 1,
-               .mask = S5M8767_IRQ_PWR1S_MASK,
-       },
-       [S5M8767_IRQ_JIGR] = {
-               .reg = 1,
-               .mask = S5M8767_IRQ_JIGR_MASK,
-       },
-       [S5M8767_IRQ_JIGF] = {
-               .reg = 1,
-               .mask = S5M8767_IRQ_JIGF_MASK,
-       },
-       [S5M8767_IRQ_LOWBAT2] = {
-               .reg = 1,
-               .mask = S5M8767_IRQ_LOWBAT2_MASK,
-       },
-       [S5M8767_IRQ_LOWBAT1] = {
-               .reg = 1,
-               .mask = S5M8767_IRQ_LOWBAT1_MASK,
-       },
-       [S5M8767_IRQ_MRB] = {
-               .reg = 2,
-               .mask = S5M8767_IRQ_MRB_MASK,
-       },
-       [S5M8767_IRQ_DVSOK2] = {
-               .reg = 2,
-               .mask = S5M8767_IRQ_DVSOK2_MASK,
-       },
-       [S5M8767_IRQ_DVSOK3] = {
-               .reg = 2,
-               .mask = S5M8767_IRQ_DVSOK3_MASK,
-       },
-       [S5M8767_IRQ_DVSOK4] = {
-               .reg = 2,
-               .mask = S5M8767_IRQ_DVSOK4_MASK,
-       },
-       [S5M8767_IRQ_RTC60S] = {
-               .reg = 3,
-               .mask = S5M8767_IRQ_RTC60S_MASK,
-       },
-       [S5M8767_IRQ_RTCA1] = {
-               .reg = 3,
-               .mask = S5M8767_IRQ_RTCA1_MASK,
-       },
-       [S5M8767_IRQ_RTCA2] = {
-               .reg = 3,
-               .mask = S5M8767_IRQ_RTCA2_MASK,
-       },
-       [S5M8767_IRQ_SMPL] = {
-               .reg = 3,
-               .mask = S5M8767_IRQ_SMPL_MASK,
-       },
-       [S5M8767_IRQ_RTC1S] = {
-               .reg = 3,
-               .mask = S5M8767_IRQ_RTC1S_MASK,
-       },
-       [S5M8767_IRQ_WTSR] = {
-               .reg = 3,
-               .mask = S5M8767_IRQ_WTSR_MASK,
-       },
-};
-
-static struct s5m_irq_data s5m8763_irqs[] = {
-       [S5M8763_IRQ_DCINF] = {
-               .reg = 1,
-               .mask = S5M8763_IRQ_DCINF_MASK,
-       },
-       [S5M8763_IRQ_DCINR] = {
-               .reg = 1,
-               .mask = S5M8763_IRQ_DCINR_MASK,
-       },
-       [S5M8763_IRQ_JIGF] = {
-               .reg = 1,
-               .mask = S5M8763_IRQ_JIGF_MASK,
-       },
-       [S5M8763_IRQ_JIGR] = {
-               .reg = 1,
-               .mask = S5M8763_IRQ_JIGR_MASK,
-       },
-       [S5M8763_IRQ_PWRONF] = {
-               .reg = 1,
-               .mask = S5M8763_IRQ_PWRONF_MASK,
-       },
-       [S5M8763_IRQ_PWRONR] = {
-               .reg = 1,
-               .mask = S5M8763_IRQ_PWRONR_MASK,
-       },
-       [S5M8763_IRQ_WTSREVNT] = {
-               .reg = 2,
-               .mask = S5M8763_IRQ_WTSREVNT_MASK,
-       },
-       [S5M8763_IRQ_SMPLEVNT] = {
-               .reg = 2,
-               .mask = S5M8763_IRQ_SMPLEVNT_MASK,
-       },
-       [S5M8763_IRQ_ALARM1] = {
-               .reg = 2,
-               .mask = S5M8763_IRQ_ALARM1_MASK,
-       },
-       [S5M8763_IRQ_ALARM0] = {
-               .reg = 2,
-               .mask = S5M8763_IRQ_ALARM0_MASK,
-       },
-       [S5M8763_IRQ_ONKEY1S] = {
-               .reg = 3,
-               .mask = S5M8763_IRQ_ONKEY1S_MASK,
-       },
-       [S5M8763_IRQ_TOPOFFR] = {
-               .reg = 3,
-               .mask = S5M8763_IRQ_TOPOFFR_MASK,
-       },
-       [S5M8763_IRQ_DCINOVPR] = {
-               .reg = 3,
-               .mask = S5M8763_IRQ_DCINOVPR_MASK,
-       },
-       [S5M8763_IRQ_CHGRSTF] = {
-               .reg = 3,
-               .mask = S5M8763_IRQ_CHGRSTF_MASK,
-       },
-       [S5M8763_IRQ_DONER] = {
-               .reg = 3,
-               .mask = S5M8763_IRQ_DONER_MASK,
-       },
-       [S5M8763_IRQ_CHGFAULT] = {
-               .reg = 3,
-               .mask = S5M8763_IRQ_CHGFAULT_MASK,
-       },
-       [S5M8763_IRQ_LOBAT1] = {
-               .reg = 4,
-               .mask = S5M8763_IRQ_LOBAT1_MASK,
-       },
-       [S5M8763_IRQ_LOBAT2] = {
-               .reg = 4,
-               .mask = S5M8763_IRQ_LOBAT2_MASK,
-       },
-};
-
-static inline struct s5m_irq_data *
-irq_to_s5m8767_irq(struct s5m87xx_dev *s5m87xx, int irq)
-{
-       return &s5m8767_irqs[irq - s5m87xx->irq_base];
-}
-
-static void s5m8767_irq_lock(struct irq_data *data)
-{
-       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
-
-       mutex_lock(&s5m87xx->irqlock);
-}
-
-static void s5m8767_irq_sync_unlock(struct irq_data *data)
-{
-       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) {
-               if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) {
-                       s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i];
-                       s5m_reg_write(s5m87xx, S5M8767_REG_INT1M + i,
-                                       s5m87xx->irq_masks_cur[i]);
-               }
-       }
-
-       mutex_unlock(&s5m87xx->irqlock);
-}
-
-static void s5m8767_irq_unmask(struct irq_data *data)
-{
-       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
-       struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx,
-                                                              data->irq);
-
-       s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
-}
-
-static void s5m8767_irq_mask(struct irq_data *data)
-{
-       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
-       struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx,
-                                                              data->irq);
-
-       s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
-}
-
-static struct irq_chip s5m8767_irq_chip = {
-       .name = "s5m8767",
-       .irq_bus_lock = s5m8767_irq_lock,
-       .irq_bus_sync_unlock = s5m8767_irq_sync_unlock,
-       .irq_mask = s5m8767_irq_mask,
-       .irq_unmask = s5m8767_irq_unmask,
-};
-
-static inline struct s5m_irq_data *
-irq_to_s5m8763_irq(struct s5m87xx_dev *s5m87xx, int irq)
-{
-       return &s5m8763_irqs[irq - s5m87xx->irq_base];
-}
-
-static void s5m8763_irq_lock(struct irq_data *data)
-{
-       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
-
-       mutex_lock(&s5m87xx->irqlock);
-}
-
-static void s5m8763_irq_sync_unlock(struct irq_data *data)
-{
-       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) {
-               if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) {
-                       s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i];
-                       s5m_reg_write(s5m87xx, S5M8763_REG_IRQM1 + i,
-                                       s5m87xx->irq_masks_cur[i]);
-               }
-       }
-
-       mutex_unlock(&s5m87xx->irqlock);
-}
-
-static void s5m8763_irq_unmask(struct irq_data *data)
-{
-       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
-       struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx,
-                                                              data->irq);
-
-       s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
-}
-
-static void s5m8763_irq_mask(struct irq_data *data)
-{
-       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
-       struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx,
-                                                              data->irq);
-
-       s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
-}
-
-static struct irq_chip s5m8763_irq_chip = {
-       .name = "s5m8763",
-       .irq_bus_lock = s5m8763_irq_lock,
-       .irq_bus_sync_unlock = s5m8763_irq_sync_unlock,
-       .irq_mask = s5m8763_irq_mask,
-       .irq_unmask = s5m8763_irq_unmask,
-};
-
-
-static irqreturn_t s5m8767_irq_thread(int irq, void *data)
-{
-       struct s5m87xx_dev *s5m87xx = data;
-       u8 irq_reg[NUM_IRQ_REGS-1];
-       int ret;
-       int i;
-
-
-       ret = s5m_bulk_read(s5m87xx, S5M8767_REG_INT1,
-                               NUM_IRQ_REGS - 1, irq_reg);
-       if (ret < 0) {
-               dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n",
-                               ret);
-               return IRQ_NONE;
-       }
-
-       for (i = 0; i < NUM_IRQ_REGS - 1; i++)
-               irq_reg[i] &= ~s5m87xx->irq_masks_cur[i];
-
-       for (i = 0; i < S5M8767_IRQ_NR; i++) {
-               if (irq_reg[s5m8767_irqs[i].reg - 1] & s5m8767_irqs[i].mask)
-                       handle_nested_irq(s5m87xx->irq_base + i);
-       }
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t s5m8763_irq_thread(int irq, void *data)
-{
-       struct s5m87xx_dev *s5m87xx = data;
-       u8 irq_reg[NUM_IRQ_REGS];
-       int ret;
-       int i;
-
-       ret = s5m_bulk_read(s5m87xx, S5M8763_REG_IRQ1,
-                               NUM_IRQ_REGS, irq_reg);
-       if (ret < 0) {
-               dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n",
-                               ret);
-               return IRQ_NONE;
-       }
-
-       for (i = 0; i < NUM_IRQ_REGS; i++)
-               irq_reg[i] &= ~s5m87xx->irq_masks_cur[i];
-
-       for (i = 0; i < S5M8763_IRQ_NR; i++) {
-               if (irq_reg[s5m8763_irqs[i].reg - 1] & s5m8763_irqs[i].mask)
-                       handle_nested_irq(s5m87xx->irq_base + i);
-       }
-
-       return IRQ_HANDLED;
-}
-
-int s5m_irq_resume(struct s5m87xx_dev *s5m87xx)
-{
-       if (s5m87xx->irq && s5m87xx->irq_base){
-               switch (s5m87xx->device_type) {
-               case S5M8763X:
-                       s5m8763_irq_thread(s5m87xx->irq_base, s5m87xx);
-                       break;
-               case S5M8767X:
-                       s5m8767_irq_thread(s5m87xx->irq_base, s5m87xx);
-                       break;
-               default:
-                       dev_err(s5m87xx->dev,
-                               "Unknown device type %d\n",
-                               s5m87xx->device_type);
-                       return -EINVAL;
-
-               }
-       }
-       return 0;
-}
-
-int s5m_irq_init(struct s5m87xx_dev *s5m87xx)
-{
-       int i;
-       int cur_irq;
-       int ret = 0;
-       int type = s5m87xx->device_type;
-
-       if (!s5m87xx->irq) {
-               dev_warn(s5m87xx->dev,
-                        "No interrupt specified, no interrupts\n");
-               s5m87xx->irq_base = 0;
-               return 0;
-       }
-
-       if (!s5m87xx->irq_base) {
-               dev_err(s5m87xx->dev,
-                       "No interrupt base specified, no interrupts\n");
-               return 0;
-       }
-
-       mutex_init(&s5m87xx->irqlock);
-
-       switch (type) {
-       case S5M8763X:
-               for (i = 0; i < NUM_IRQ_REGS; i++) {
-                       s5m87xx->irq_masks_cur[i] = 0xff;
-                       s5m87xx->irq_masks_cache[i] = 0xff;
-                       s5m_reg_write(s5m87xx, S5M8763_REG_IRQM1 + i,
-                                               0xff);
-               }
-
-               s5m_reg_write(s5m87xx, S5M8763_REG_STATUSM1, 0xff);
-               s5m_reg_write(s5m87xx, S5M8763_REG_STATUSM2, 0xff);
-
-               for (i = 0; i < S5M8763_IRQ_NR; i++) {
-                       cur_irq = i + s5m87xx->irq_base;
-                       irq_set_chip_data(cur_irq, s5m87xx);
-                       irq_set_chip_and_handler(cur_irq, &s5m8763_irq_chip,
-                                                handle_edge_irq);
-                       irq_set_nested_thread(cur_irq, 1);
-#ifdef CONFIG_ARM
-                       set_irq_flags(cur_irq, IRQF_VALID);
-#else
-                       irq_set_noprobe(cur_irq);
-#endif
-               }
-
-               ret = request_threaded_irq(s5m87xx->irq, NULL,
-                                       s5m8763_irq_thread,
-                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-                                       "s5m87xx-irq", s5m87xx);
-               if (ret) {
-                       dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
-                               s5m87xx->irq, ret);
-                       return ret;
-               }
-               break;
-       case S5M8767X:
-               for (i = 0; i < NUM_IRQ_REGS - 1; i++) {
-                       s5m87xx->irq_masks_cur[i] = 0xff;
-                       s5m87xx->irq_masks_cache[i] = 0xff;
-                       s5m_reg_write(s5m87xx, S5M8767_REG_INT1M + i,
-                                               0xff);
-               }
-               for (i = 0; i < S5M8767_IRQ_NR; i++) {
-                       cur_irq = i + s5m87xx->irq_base;
-                       irq_set_chip_data(cur_irq, s5m87xx);
-                       if (ret) {
-                               dev_err(s5m87xx->dev,
-                                       "Failed to irq_set_chip_data %d: %d\n",
-                                       s5m87xx->irq, ret);
-                               return ret;
-                       }
-
-                       irq_set_chip_and_handler(cur_irq, &s5m8767_irq_chip,
-                                                handle_edge_irq);
-                       irq_set_nested_thread(cur_irq, 1);
-#ifdef CONFIG_ARM
-                       set_irq_flags(cur_irq, IRQF_VALID);
-#else
-                       irq_set_noprobe(cur_irq);
-#endif
-               }
-
-               ret = request_threaded_irq(s5m87xx->irq, NULL,
-                                          s5m8767_irq_thread,
-                                          IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-                                          "s5m87xx-irq", s5m87xx);
-               if (ret) {
-                       dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
-                               s5m87xx->irq, ret);
-                       return ret;
-               }
-               break;
-       default:
-               dev_err(s5m87xx->dev,
-                       "Unknown device type %d\n", s5m87xx->device_type);
-               return -EINVAL;
-       }
-
-       if (!s5m87xx->ono)
-               return 0;
-
-       switch (type) {
-       case S5M8763X:
-               ret = request_threaded_irq(s5m87xx->ono, NULL,
-                                               s5m8763_irq_thread,
-                                               IRQF_TRIGGER_FALLING |
-                                               IRQF_TRIGGER_RISING |
-                                               IRQF_ONESHOT, "s5m87xx-ono",
-                                               s5m87xx);
-               break;
-       case S5M8767X:
-               ret = request_threaded_irq(s5m87xx->ono, NULL,
-                                       s5m8767_irq_thread,
-                                       IRQF_TRIGGER_FALLING |
-                                       IRQF_TRIGGER_RISING |
-                                       IRQF_ONESHOT, "s5m87xx-ono", s5m87xx);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       if (ret) {
-               dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
-                       s5m87xx->ono, ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-void s5m_irq_exit(struct s5m87xx_dev *s5m87xx)
-{
-       if (s5m87xx->ono)
-               free_irq(s5m87xx->ono, s5m87xx);
-
-       if (s5m87xx->irq)
-               free_irq(s5m87xx->irq, s5m87xx);
-}
diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c
new file mode 100644 (file)
index 0000000..2988efd
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * sec-core.c
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd
+ *              http://www.samsung.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/irq.h>
+#include <linux/mfd/samsung/rtc.h>
+#include <linux/regmap.h>
+
+static struct mfd_cell s5m8751_devs[] = {
+       {
+               .name = "s5m8751-pmic",
+       }, {
+               .name = "s5m-charger",
+       }, {
+               .name = "s5m8751-codec",
+       },
+};
+
+static struct mfd_cell s5m8763_devs[] = {
+       {
+               .name = "s5m8763-pmic",
+       }, {
+               .name = "s5m-rtc",
+       }, {
+               .name = "s5m-charger",
+       },
+};
+
+static struct mfd_cell s5m8767_devs[] = {
+       {
+               .name = "s5m8767-pmic",
+       }, {
+               .name = "s5m-rtc",
+       },
+};
+
+static struct mfd_cell s2mps11_devs[] = {
+       {
+               .name = "s2mps11-pmic",
+       },
+};
+
+int sec_reg_read(struct sec_pmic_dev *sec_pmic, u8 reg, void *dest)
+{
+       return regmap_read(sec_pmic->regmap, reg, dest);
+}
+EXPORT_SYMBOL_GPL(sec_reg_read);
+
+int sec_bulk_read(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf)
+{
+       return regmap_bulk_read(sec_pmic->regmap, reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(sec_bulk_read);
+
+int sec_reg_write(struct sec_pmic_dev *sec_pmic, u8 reg, u8 value)
+{
+       return regmap_write(sec_pmic->regmap, reg, value);
+}
+EXPORT_SYMBOL_GPL(sec_reg_write);
+
+int sec_bulk_write(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf)
+{
+       return regmap_raw_write(sec_pmic->regmap, reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(sec_bulk_write);
+
+int sec_reg_update(struct sec_pmic_dev *sec_pmic, u8 reg, u8 val, u8 mask)
+{
+       return regmap_update_bits(sec_pmic->regmap, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(sec_reg_update);
+
+static struct regmap_config sec_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+};
+
+static int sec_pmic_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct sec_platform_data *pdata = i2c->dev.platform_data;
+       struct sec_pmic_dev *sec_pmic;
+       int ret;
+
+       sec_pmic = devm_kzalloc(&i2c->dev, sizeof(struct sec_pmic_dev),
+                               GFP_KERNEL);
+       if (sec_pmic == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, sec_pmic);
+       sec_pmic->dev = &i2c->dev;
+       sec_pmic->i2c = i2c;
+       sec_pmic->irq = i2c->irq;
+       sec_pmic->type = id->driver_data;
+
+       if (pdata) {
+               sec_pmic->device_type = pdata->device_type;
+               sec_pmic->ono = pdata->ono;
+               sec_pmic->irq_base = pdata->irq_base;
+               sec_pmic->wakeup = pdata->wakeup;
+       }
+
+       sec_pmic->regmap = devm_regmap_init_i2c(i2c, &sec_regmap_config);
+       if (IS_ERR(sec_pmic->regmap)) {
+               ret = PTR_ERR(sec_pmic->regmap);
+               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       sec_pmic->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
+       i2c_set_clientdata(sec_pmic->rtc, sec_pmic);
+
+       if (pdata && pdata->cfg_pmic_irq)
+               pdata->cfg_pmic_irq();
+
+       sec_irq_init(sec_pmic);
+
+       pm_runtime_set_active(sec_pmic->dev);
+
+       switch (sec_pmic->device_type) {
+       case S5M8751X:
+               ret = mfd_add_devices(sec_pmic->dev, -1, s5m8751_devs,
+                                       ARRAY_SIZE(s5m8751_devs), NULL, 0);
+               break;
+       case S5M8763X:
+               ret = mfd_add_devices(sec_pmic->dev, -1, s5m8763_devs,
+                                       ARRAY_SIZE(s5m8763_devs), NULL, 0);
+               break;
+       case S5M8767X:
+               ret = mfd_add_devices(sec_pmic->dev, -1, s5m8767_devs,
+                                       ARRAY_SIZE(s5m8767_devs), NULL, 0);
+               break;
+       case S2MPS11X:
+               ret = mfd_add_devices(sec_pmic->dev, -1, s2mps11_devs,
+                                       ARRAY_SIZE(s2mps11_devs), NULL, 0);
+               break;
+       default:
+               /* If this happens the probe function is problem */
+               BUG();
+       }
+
+       if (ret < 0)
+               goto err;
+
+       return ret;
+
+err:
+       mfd_remove_devices(sec_pmic->dev);
+       sec_irq_exit(sec_pmic);
+       i2c_unregister_device(sec_pmic->rtc);
+       return ret;
+}
+
+static int sec_pmic_remove(struct i2c_client *i2c)
+{
+       struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
+
+       mfd_remove_devices(sec_pmic->dev);
+       sec_irq_exit(sec_pmic);
+       i2c_unregister_device(sec_pmic->rtc);
+       return 0;
+}
+
+static const struct i2c_device_id sec_pmic_id[] = {
+       { "sec_pmic", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, sec_pmic_id);
+
+static struct i2c_driver sec_pmic_driver = {
+       .driver = {
+                  .name = "sec_pmic",
+                  .owner = THIS_MODULE,
+       },
+       .probe = sec_pmic_probe,
+       .remove = sec_pmic_remove,
+       .id_table = sec_pmic_id,
+};
+
+static int __init sec_pmic_init(void)
+{
+       return i2c_add_driver(&sec_pmic_driver);
+}
+
+subsys_initcall(sec_pmic_init);
+
+static void __exit sec_pmic_exit(void)
+{
+       i2c_del_driver(&sec_pmic_driver);
+}
+module_exit(sec_pmic_exit);
+
+MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
+MODULE_DESCRIPTION("Core support for the S5M MFD");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c
new file mode 100644 (file)
index 0000000..c901fa5
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * sec-irq.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ *              http://www.samsung.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/irq.h>
+#include <linux/mfd/samsung/s2mps11.h>
+#include <linux/mfd/samsung/s5m8763.h>
+#include <linux/mfd/samsung/s5m8767.h>
+
+static struct regmap_irq s2mps11_irqs[] = {
+       [S2MPS11_IRQ_PWRONF] = {
+               .reg_offset = 1,
+               .mask = S2MPS11_IRQ_PWRONF_MASK,
+       },
+       [S2MPS11_IRQ_PWRONR] = {
+               .reg_offset = 1,
+               .mask = S2MPS11_IRQ_PWRONR_MASK,
+       },
+       [S2MPS11_IRQ_JIGONBF] = {
+               .reg_offset = 1,
+               .mask = S2MPS11_IRQ_JIGONBF_MASK,
+       },
+       [S2MPS11_IRQ_JIGONBR] = {
+               .reg_offset = 1,
+               .mask = S2MPS11_IRQ_JIGONBR_MASK,
+       },
+       [S2MPS11_IRQ_ACOKBF] = {
+               .reg_offset = 1,
+               .mask = S2MPS11_IRQ_ACOKBF_MASK,
+       },
+       [S2MPS11_IRQ_ACOKBR] = {
+               .reg_offset = 1,
+               .mask = S2MPS11_IRQ_ACOKBR_MASK,
+       },
+       [S2MPS11_IRQ_PWRON1S] = {
+               .reg_offset = 1,
+               .mask = S2MPS11_IRQ_PWRON1S_MASK,
+       },
+       [S2MPS11_IRQ_MRB] = {
+               .reg_offset = 1,
+               .mask = S2MPS11_IRQ_MRB_MASK,
+       },
+       [S2MPS11_IRQ_RTC60S] = {
+               .reg_offset = 2,
+               .mask = S2MPS11_IRQ_RTC60S_MASK,
+       },
+       [S2MPS11_IRQ_RTCA1] = {
+               .reg_offset = 2,
+               .mask = S2MPS11_IRQ_RTCA1_MASK,
+       },
+       [S2MPS11_IRQ_RTCA2] = {
+               .reg_offset = 2,
+               .mask = S2MPS11_IRQ_RTCA2_MASK,
+       },
+       [S2MPS11_IRQ_SMPL] = {
+               .reg_offset = 2,
+               .mask = S2MPS11_IRQ_SMPL_MASK,
+       },
+       [S2MPS11_IRQ_RTC1S] = {
+               .reg_offset = 2,
+               .mask = S2MPS11_IRQ_RTC1S_MASK,
+       },
+       [S2MPS11_IRQ_WTSR] = {
+               .reg_offset = 2,
+               .mask = S2MPS11_IRQ_WTSR_MASK,
+       },
+       [S2MPS11_IRQ_INT120C] = {
+               .reg_offset = 3,
+               .mask = S2MPS11_IRQ_INT120C_MASK,
+       },
+       [S2MPS11_IRQ_INT140C] = {
+               .reg_offset = 3,
+               .mask = S2MPS11_IRQ_INT140C_MASK,
+       },
+};
+
+
+static struct regmap_irq s5m8767_irqs[] = {
+       [S5M8767_IRQ_PWRR] = {
+               .reg_offset = 1,
+               .mask = S5M8767_IRQ_PWRR_MASK,
+       },
+       [S5M8767_IRQ_PWRF] = {
+               .reg_offset = 1,
+               .mask = S5M8767_IRQ_PWRF_MASK,
+       },
+       [S5M8767_IRQ_PWR1S] = {
+               .reg_offset = 1,
+               .mask = S5M8767_IRQ_PWR1S_MASK,
+       },
+       [S5M8767_IRQ_JIGR] = {
+               .reg_offset = 1,
+               .mask = S5M8767_IRQ_JIGR_MASK,
+       },
+       [S5M8767_IRQ_JIGF] = {
+               .reg_offset = 1,
+               .mask = S5M8767_IRQ_JIGF_MASK,
+       },
+       [S5M8767_IRQ_LOWBAT2] = {
+               .reg_offset = 1,
+               .mask = S5M8767_IRQ_LOWBAT2_MASK,
+       },
+       [S5M8767_IRQ_LOWBAT1] = {
+               .reg_offset = 1,
+               .mask = S5M8767_IRQ_LOWBAT1_MASK,
+       },
+       [S5M8767_IRQ_MRB] = {
+               .reg_offset = 2,
+               .mask = S5M8767_IRQ_MRB_MASK,
+       },
+       [S5M8767_IRQ_DVSOK2] = {
+               .reg_offset = 2,
+               .mask = S5M8767_IRQ_DVSOK2_MASK,
+       },
+       [S5M8767_IRQ_DVSOK3] = {
+               .reg_offset = 2,
+               .mask = S5M8767_IRQ_DVSOK3_MASK,
+       },
+       [S5M8767_IRQ_DVSOK4] = {
+               .reg_offset = 2,
+               .mask = S5M8767_IRQ_DVSOK4_MASK,
+       },
+       [S5M8767_IRQ_RTC60S] = {
+               .reg_offset = 3,
+               .mask = S5M8767_IRQ_RTC60S_MASK,
+       },
+       [S5M8767_IRQ_RTCA1] = {
+               .reg_offset = 3,
+               .mask = S5M8767_IRQ_RTCA1_MASK,
+       },
+       [S5M8767_IRQ_RTCA2] = {
+               .reg_offset = 3,
+               .mask = S5M8767_IRQ_RTCA2_MASK,
+       },
+       [S5M8767_IRQ_SMPL] = {
+               .reg_offset = 3,
+               .mask = S5M8767_IRQ_SMPL_MASK,
+       },
+       [S5M8767_IRQ_RTC1S] = {
+               .reg_offset = 3,
+               .mask = S5M8767_IRQ_RTC1S_MASK,
+       },
+       [S5M8767_IRQ_WTSR] = {
+               .reg_offset = 3,
+               .mask = S5M8767_IRQ_WTSR_MASK,
+       },
+};
+
+static struct regmap_irq s5m8763_irqs[] = {
+       [S5M8763_IRQ_DCINF] = {
+               .reg_offset = 1,
+               .mask = S5M8763_IRQ_DCINF_MASK,
+       },
+       [S5M8763_IRQ_DCINR] = {
+               .reg_offset = 1,
+               .mask = S5M8763_IRQ_DCINR_MASK,
+       },
+       [S5M8763_IRQ_JIGF] = {
+               .reg_offset = 1,
+               .mask = S5M8763_IRQ_JIGF_MASK,
+       },
+       [S5M8763_IRQ_JIGR] = {
+               .reg_offset = 1,
+               .mask = S5M8763_IRQ_JIGR_MASK,
+       },
+       [S5M8763_IRQ_PWRONF] = {
+               .reg_offset = 1,
+               .mask = S5M8763_IRQ_PWRONF_MASK,
+       },
+       [S5M8763_IRQ_PWRONR] = {
+               .reg_offset = 1,
+               .mask = S5M8763_IRQ_PWRONR_MASK,
+       },
+       [S5M8763_IRQ_WTSREVNT] = {
+               .reg_offset = 2,
+               .mask = S5M8763_IRQ_WTSREVNT_MASK,
+       },
+       [S5M8763_IRQ_SMPLEVNT] = {
+               .reg_offset = 2,
+               .mask = S5M8763_IRQ_SMPLEVNT_MASK,
+       },
+       [S5M8763_IRQ_ALARM1] = {
+               .reg_offset = 2,
+               .mask = S5M8763_IRQ_ALARM1_MASK,
+       },
+       [S5M8763_IRQ_ALARM0] = {
+               .reg_offset = 2,
+               .mask = S5M8763_IRQ_ALARM0_MASK,
+       },
+       [S5M8763_IRQ_ONKEY1S] = {
+               .reg_offset = 3,
+               .mask = S5M8763_IRQ_ONKEY1S_MASK,
+       },
+       [S5M8763_IRQ_TOPOFFR] = {
+               .reg_offset = 3,
+               .mask = S5M8763_IRQ_TOPOFFR_MASK,
+       },
+       [S5M8763_IRQ_DCINOVPR] = {
+               .reg_offset = 3,
+               .mask = S5M8763_IRQ_DCINOVPR_MASK,
+       },
+       [S5M8763_IRQ_CHGRSTF] = {
+               .reg_offset = 3,
+               .mask = S5M8763_IRQ_CHGRSTF_MASK,
+       },
+       [S5M8763_IRQ_DONER] = {
+               .reg_offset = 3,
+               .mask = S5M8763_IRQ_DONER_MASK,
+       },
+       [S5M8763_IRQ_CHGFAULT] = {
+               .reg_offset = 3,
+               .mask = S5M8763_IRQ_CHGFAULT_MASK,
+       },
+       [S5M8763_IRQ_LOBAT1] = {
+               .reg_offset = 4,
+               .mask = S5M8763_IRQ_LOBAT1_MASK,
+       },
+       [S5M8763_IRQ_LOBAT2] = {
+               .reg_offset = 4,
+               .mask = S5M8763_IRQ_LOBAT2_MASK,
+       },
+};
+
+static struct regmap_irq_chip s2mps11_irq_chip = {
+       .name = "s2mps11",
+       .irqs = s2mps11_irqs,
+       .num_irqs = ARRAY_SIZE(s2mps11_irqs),
+       .num_regs = 3,
+       .status_base = S2MPS11_REG_INT1,
+       .mask_base = S2MPS11_REG_INT1M,
+       .ack_base = S2MPS11_REG_INT1,
+};
+
+static struct regmap_irq_chip s5m8767_irq_chip = {
+       .name = "s5m8767",
+       .irqs = s5m8767_irqs,
+       .num_irqs = ARRAY_SIZE(s5m8767_irqs),
+       .num_regs = 3,
+       .status_base = S5M8767_REG_INT1,
+       .mask_base = S5M8767_REG_INT1M,
+       .ack_base = S5M8767_REG_INT1,
+};
+
+static struct regmap_irq_chip s5m8763_irq_chip = {
+       .name = "s5m8763",
+       .irqs = s5m8763_irqs,
+       .num_irqs = ARRAY_SIZE(s5m8763_irqs),
+       .num_regs = 4,
+       .status_base = S5M8763_REG_IRQ1,
+       .mask_base = S5M8763_REG_IRQM1,
+       .ack_base = S5M8763_REG_IRQ1,
+};
+
+int sec_irq_init(struct sec_pmic_dev *sec_pmic)
+{
+       int ret = 0;
+       int type = sec_pmic->device_type;
+
+       if (!sec_pmic->irq) {
+               dev_warn(sec_pmic->dev,
+                        "No interrupt specified, no interrupts\n");
+               sec_pmic->irq_base = 0;
+               return 0;
+       }
+
+       switch (type) {
+       case S5M8763X:
+               ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq,
+                                 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                 sec_pmic->irq_base, &s5m8763_irq_chip,
+                                 &sec_pmic->irq_data);
+               break;
+       case S5M8767X:
+               ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq,
+                                 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                 sec_pmic->irq_base, &s5m8767_irq_chip,
+                                 &sec_pmic->irq_data);
+               break;
+       case S2MPS11X:
+               ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq,
+                                 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                 sec_pmic->irq_base, &s2mps11_irq_chip,
+                                 &sec_pmic->irq_data);
+               break;
+       default:
+               dev_err(sec_pmic->dev, "Unknown device type %d\n",
+                       sec_pmic->device_type);
+               return -EINVAL;
+       }
+
+       if (ret != 0) {
+               dev_err(sec_pmic->dev, "Failed to register IRQ chip: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+void sec_irq_exit(struct sec_pmic_dev *sec_pmic)
+{
+       regmap_del_irq_chip(sec_pmic->irq, sec_pmic->irq_data);
+}
index de979742c6fc1049cf49f858870034f3885eeb05..048bf0532a095014e03358b01af1f4cd58585b97 100644 (file)
@@ -357,7 +357,7 @@ static int __devexit tc3589x_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int tc3589x_suspend(struct device *dev)
 {
        struct tc3589x *tc3589x = dev_get_drvdata(dev);
@@ -385,11 +385,10 @@ static int tc3589x_resume(struct device *dev)
 
        return ret;
 }
-
-static const SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend,
-                                               tc3589x_resume);
 #endif
 
+static SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume);
+
 static const struct i2c_device_id tc3589x_id[] = {
        { "tc3589x", 24 },
        { }
@@ -399,9 +398,7 @@ MODULE_DEVICE_TABLE(i2c, tc3589x_id);
 static struct i2c_driver tc3589x_driver = {
        .driver.name    = "tc3589x",
        .driver.owner   = THIS_MODULE,
-#ifdef CONFIG_PM
        .driver.pm      = &tc3589x_dev_pm_ops,
-#endif
        .probe          = tc3589x_probe,
        .remove         = __devexit_p(tc3589x_remove),
        .id_table       = tc3589x_id,
index 0ba26fb12cf532f948fefa1e4df6599ce7db347b..a447f4ec11fb757ee38755aa44da467f83229d1f 100644 (file)
@@ -83,7 +83,7 @@ timberdale_xiic_platform_data = {
 
 static __devinitdata struct ocores_i2c_platform_data
 timberdale_ocores_platform_data = {
-       .regstep = 4,
+       .reg_shift = 2,
        .clock_khz = 62500,
        .devices = timberdale_i2c_board_info,
        .num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
index 93d5fdf020c7eaa90dbf4d1eee41de216873d78e..da2691f22e114390f3f40817473f24a12c302f8f 100644 (file)
@@ -563,8 +563,7 @@ static int tps65010_probe(struct i2c_client *client,
         */
        if (client->irq > 0) {
                status = request_irq(client->irq, tps65010_irq,
-                       IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
-                       DRIVER_NAME, tps);
+                                    IRQF_TRIGGER_FALLING, DRIVER_NAME, tps);
                if (status < 0) {
                        dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
                                        client->irq, status);
index 396b9d1b6bd68bc80ee0120693949461688b8b9f..80e24f4b47bffce67679b7e637627e9e7c769466 100644 (file)
@@ -71,10 +71,10 @@ static const struct tps65090_irq_data tps65090_irqs[] = {
 
 static struct mfd_cell tps65090s[] = {
        {
-               .name = "tps65910-pmic",
+               .name = "tps65090-pmic",
        },
        {
-               .name = "tps65910-regulator",
+               .name = "tps65090-regulator",
        },
 };
 
index c84b5506d5fbd06ee82d12b476abc6239ae4508f..353c34812120fc46e37140c0b6a04babdd5cb080 100644 (file)
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
-#include <linux/gpio.h>
+#include <linux/err.h>
 #include <linux/i2c.h>
+#include <linux/regmap.h>
 #include <linux/regulator/of_regulator.h>
 
 #include <linux/mfd/core.h>
 #include <linux/mfd/tps6586x.h>
 
-/* GPIO control registers */
-#define TPS6586X_GPIOSET1      0x5d
-#define TPS6586X_GPIOSET2      0x5e
-
 /* interrupt control registers */
 #define TPS6586X_INT_ACK1      0xb5
 #define TPS6586X_INT_ACK2      0xb6
@@ -48,6 +45,9 @@
 /* device id */
 #define TPS6586X_VERSIONCRC    0xcd
 
+/* Maximum register */
+#define TPS6586X_MAX_REGISTER  (TPS6586X_VERSIONCRC + 1)
+
 struct tps6586x_irq_data {
        u8      mask_reg;
        u8      mask_mask;
@@ -89,226 +89,96 @@ static const struct tps6586x_irq_data tps6586x_irqs[] = {
        [TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1),
 };
 
+static struct mfd_cell tps6586x_cell[] = {
+       {
+               .name = "tps6586x-gpio",
+       },
+       {
+               .name = "tps6586x-rtc",
+       },
+       {
+               .name = "tps6586x-onkey",
+       },
+};
+
 struct tps6586x {
-       struct mutex            lock;
        struct device           *dev;
        struct i2c_client       *client;
+       struct regmap           *regmap;
 
-       struct gpio_chip        gpio;
        struct irq_chip         irq_chip;
        struct mutex            irq_lock;
        int                     irq_base;
        u32                     irq_en;
-       u8                      mask_cache[5];
        u8                      mask_reg[5];
 };
 
-static inline int __tps6586x_read(struct i2c_client *client,
-                                 int reg, uint8_t *val)
-{
-       int ret;
-
-       ret = i2c_smbus_read_byte_data(client, reg);
-       if (ret < 0) {
-               dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
-               return ret;
-       }
-
-       *val = (uint8_t)ret;
-
-       return 0;
-}
-
-static inline int __tps6586x_reads(struct i2c_client *client, int reg,
-                                  int len, uint8_t *val)
-{
-       int ret;
-
-       ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);
-       if (ret < 0) {
-               dev_err(&client->dev, "failed reading from 0x%02x\n", reg);
-               return ret;
-       }
-
-       return 0;
-}
-
-static inline int __tps6586x_write(struct i2c_client *client,
-                                int reg, uint8_t val)
+static inline struct tps6586x *dev_to_tps6586x(struct device *dev)
 {
-       int ret;
-
-       ret = i2c_smbus_write_byte_data(client, reg, val);
-       if (ret < 0) {
-               dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
-                               val, reg);
-               return ret;
-       }
-
-       return 0;
-}
-
-static inline int __tps6586x_writes(struct i2c_client *client, int reg,
-                                 int len, uint8_t *val)
-{
-       int ret, i;
-
-       for (i = 0; i < len; i++) {
-               ret = __tps6586x_write(client, reg + i, *(val + i));
-               if (ret < 0)
-                       return ret;
-       }
-
-       return 0;
+       return i2c_get_clientdata(to_i2c_client(dev));
 }
 
 int tps6586x_write(struct device *dev, int reg, uint8_t val)
 {
-       return __tps6586x_write(to_i2c_client(dev), reg, val);
+       struct tps6586x *tps6586x = dev_to_tps6586x(dev);
+
+       return regmap_write(tps6586x->regmap, reg, val);
 }
 EXPORT_SYMBOL_GPL(tps6586x_write);
 
 int tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val)
 {
-       return __tps6586x_writes(to_i2c_client(dev), reg, len, val);
+       struct tps6586x *tps6586x = dev_to_tps6586x(dev);
+
+       return regmap_bulk_write(tps6586x->regmap, reg, val, len);
 }
 EXPORT_SYMBOL_GPL(tps6586x_writes);
 
 int tps6586x_read(struct device *dev, int reg, uint8_t *val)
 {
-       return __tps6586x_read(to_i2c_client(dev), reg, val);
+       struct tps6586x *tps6586x = dev_to_tps6586x(dev);
+       unsigned int rval;
+       int ret;
+
+       ret = regmap_read(tps6586x->regmap, reg, &rval);
+       if (!ret)
+               *val = rval;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(tps6586x_read);
 
 int tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val)
 {
-       return __tps6586x_reads(to_i2c_client(dev), reg, len, val);
+       struct tps6586x *tps6586x = dev_to_tps6586x(dev);
+
+       return regmap_bulk_read(tps6586x->regmap, reg, val, len);
 }
 EXPORT_SYMBOL_GPL(tps6586x_reads);
 
 int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
 {
-       struct tps6586x *tps6586x = dev_get_drvdata(dev);
-       uint8_t reg_val;
-       int ret = 0;
-
-       mutex_lock(&tps6586x->lock);
+       struct tps6586x *tps6586x = dev_to_tps6586x(dev);
 
-       ret = __tps6586x_read(to_i2c_client(dev), reg, &reg_val);
-       if (ret)
-               goto out;
-
-       if ((reg_val & bit_mask) != bit_mask) {
-               reg_val |= bit_mask;
-               ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
-       }
-out:
-       mutex_unlock(&tps6586x->lock);
-       return ret;
+       return regmap_update_bits(tps6586x->regmap, reg, bit_mask, bit_mask);
 }
 EXPORT_SYMBOL_GPL(tps6586x_set_bits);
 
 int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
 {
-       struct tps6586x *tps6586x = dev_get_drvdata(dev);
-       uint8_t reg_val;
-       int ret = 0;
-
-       mutex_lock(&tps6586x->lock);
+       struct tps6586x *tps6586x = dev_to_tps6586x(dev);
 
-       ret = __tps6586x_read(to_i2c_client(dev), reg, &reg_val);
-       if (ret)
-               goto out;
-
-       if (reg_val & bit_mask) {
-               reg_val &= ~bit_mask;
-               ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
-       }
-out:
-       mutex_unlock(&tps6586x->lock);
-       return ret;
+       return regmap_update_bits(tps6586x->regmap, reg, bit_mask, 0);
 }
 EXPORT_SYMBOL_GPL(tps6586x_clr_bits);
 
 int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
 {
-       struct tps6586x *tps6586x = dev_get_drvdata(dev);
-       uint8_t reg_val;
-       int ret = 0;
-
-       mutex_lock(&tps6586x->lock);
+       struct tps6586x *tps6586x = dev_to_tps6586x(dev);
 
-       ret = __tps6586x_read(tps6586x->client, reg, &reg_val);
-       if (ret)
-               goto out;
-
-       if ((reg_val & mask) != val) {
-               reg_val = (reg_val & ~mask) | val;
-               ret = __tps6586x_write(tps6586x->client, reg, reg_val);
-       }
-out:
-       mutex_unlock(&tps6586x->lock);
-       return ret;
+       return regmap_update_bits(tps6586x->regmap, reg, mask, val);
 }
 EXPORT_SYMBOL_GPL(tps6586x_update);
 
-static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset)
-{
-       struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
-       uint8_t val;
-       int ret;
-
-       ret = __tps6586x_read(tps6586x->client, TPS6586X_GPIOSET2, &val);
-       if (ret)
-               return ret;
-
-       return !!(val & (1 << offset));
-}
-
-
-static void tps6586x_gpio_set(struct gpio_chip *chip, unsigned offset,
-                             int value)
-{
-       struct tps6586x *tps6586x = container_of(chip, struct tps6586x, gpio);
-
-       tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET2,
-                       value << offset, 1 << offset);
-}
-
-static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
-                               int value)
-{
-       struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
-       uint8_t val, mask;
-
-       tps6586x_gpio_set(gc, offset, value);
-
-       val = 0x1 << (offset * 2);
-       mask = 0x3 << (offset * 2);
-
-       return tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET1, val, mask);
-}
-
-static int tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base)
-{
-       if (!gpio_base)
-               return 0;
-
-       tps6586x->gpio.owner            = THIS_MODULE;
-       tps6586x->gpio.label            = tps6586x->client->name;
-       tps6586x->gpio.dev              = tps6586x->dev;
-       tps6586x->gpio.base             = gpio_base;
-       tps6586x->gpio.ngpio            = 4;
-       tps6586x->gpio.can_sleep        = 1;
-
-       /* FIXME: add handling of GPIOs as dedicated inputs */
-       tps6586x->gpio.direction_output = tps6586x_gpio_output;
-       tps6586x->gpio.set              = tps6586x_gpio_set;
-       tps6586x->gpio.get              = tps6586x_gpio_get;
-
-       return gpiochip_add(&tps6586x->gpio);
-}
-
 static int __remove_subdev(struct device *dev, void *unused)
 {
        platform_device_unregister(to_platform_device(dev));
@@ -354,12 +224,11 @@ static void tps6586x_irq_sync_unlock(struct irq_data *data)
        int i;
 
        for (i = 0; i < ARRAY_SIZE(tps6586x->mask_reg); i++) {
-               if (tps6586x->mask_reg[i] != tps6586x->mask_cache[i]) {
-                       if (!WARN_ON(tps6586x_write(tps6586x->dev,
-                                                   TPS6586X_INT_MASK1 + i,
-                                                   tps6586x->mask_reg[i])))
-                               tps6586x->mask_cache[i] = tps6586x->mask_reg[i];
-               }
+               int ret;
+               ret = tps6586x_write(tps6586x->dev,
+                                           TPS6586X_INT_MASK1 + i,
+                                           tps6586x->mask_reg[i]);
+               WARN_ON(ret);
        }
 
        mutex_unlock(&tps6586x->irq_lock);
@@ -406,7 +275,6 @@ static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq,
 
        mutex_init(&tps6586x->irq_lock);
        for (i = 0; i < 5; i++) {
-               tps6586x->mask_cache[i] = 0xff;
                tps6586x->mask_reg[i] = 0xff;
                tps6586x_write(tps6586x->dev, TPS6586X_INT_MASK1 + i, 0xff);
        }
@@ -556,6 +424,23 @@ static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *clien
 }
 #endif
 
+static bool is_volatile_reg(struct device *dev, unsigned int reg)
+{
+       /* Cache all interrupt mask register */
+       if ((reg >= TPS6586X_INT_MASK1) && (reg <= TPS6586X_INT_MASK5))
+               return false;
+
+       return true;
+}
+
+static const struct regmap_config tps6586x_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = TPS6586X_MAX_REGISTER - 1,
+       .volatile_reg = is_volatile_reg,
+       .cache_type = REGCACHE_RBTREE,
+};
+
 static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
                                        const struct i2c_device_id *id)
 {
@@ -579,29 +464,39 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
 
        dev_info(&client->dev, "VERSIONCRC is %02x\n", ret);
 
-       tps6586x = kzalloc(sizeof(struct tps6586x), GFP_KERNEL);
-       if (tps6586x == NULL)
+       tps6586x = devm_kzalloc(&client->dev, sizeof(*tps6586x), GFP_KERNEL);
+       if (tps6586x == NULL) {
+               dev_err(&client->dev, "memory for tps6586x alloc failed\n");
                return -ENOMEM;
+       }
 
        tps6586x->client = client;
        tps6586x->dev = &client->dev;
        i2c_set_clientdata(client, tps6586x);
 
-       mutex_init(&tps6586x->lock);
+       tps6586x->regmap = devm_regmap_init_i2c(client,
+                                       &tps6586x_regmap_config);
+       if (IS_ERR(tps6586x->regmap)) {
+               ret = PTR_ERR(tps6586x->regmap);
+               dev_err(&client->dev, "regmap init failed: %d\n", ret);
+               return ret;
+       }
+
 
        if (client->irq) {
                ret = tps6586x_irq_init(tps6586x, client->irq,
                                        pdata->irq_base);
                if (ret) {
                        dev_err(&client->dev, "IRQ init failed: %d\n", ret);
-                       goto err_irq_init;
+                       return ret;
                }
        }
 
-       ret = tps6586x_gpio_init(tps6586x, pdata->gpio_base);
-       if (ret) {
-               dev_err(&client->dev, "GPIO registration failed: %d\n", ret);
-               goto err_gpio_init;
+       ret = mfd_add_devices(tps6586x->dev, -1,
+                       tps6586x_cell, ARRAY_SIZE(tps6586x_cell), NULL, 0);
+       if (ret < 0) {
+               dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret);
+               goto err_mfd_add;
        }
 
        ret = tps6586x_add_subdevs(tps6586x, pdata);
@@ -613,38 +508,21 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
        return 0;
 
 err_add_devs:
-       if (pdata->gpio_base) {
-               ret = gpiochip_remove(&tps6586x->gpio);
-               if (ret)
-                       dev_err(&client->dev, "Can't remove gpio chip: %d\n",
-                               ret);
-       }
-err_gpio_init:
+       mfd_remove_devices(tps6586x->dev);
+err_mfd_add:
        if (client->irq)
                free_irq(client->irq, tps6586x);
-err_irq_init:
-       kfree(tps6586x);
        return ret;
 }
 
 static int __devexit tps6586x_i2c_remove(struct i2c_client *client)
 {
        struct tps6586x *tps6586x = i2c_get_clientdata(client);
-       struct tps6586x_platform_data *pdata = client->dev.platform_data;
-       int ret;
 
+       tps6586x_remove_subdevs(tps6586x);
+       mfd_remove_devices(tps6586x->dev);
        if (client->irq)
                free_irq(client->irq, tps6586x);
-
-       if (pdata->gpio_base) {
-               ret = gpiochip_remove(&tps6586x->gpio);
-               if (ret)
-                       dev_err(&client->dev, "Can't remove gpio chip: %d\n",
-                               ret);
-       }
-
-       tps6586x_remove_subdevs(tps6586x);
-       kfree(tps6586x);
        return 0;
 }
 
index be9e07b77325a6afef8ed64253bc4a51e5cb4cc9..1c563792c777ba8f04c194a2a99c39159d8871f7 100644 (file)
@@ -68,6 +68,24 @@ static const struct regmap_config tps65910_regmap_config = {
        .cache_type = REGCACHE_RBTREE,
 };
 
+static int __devinit tps65910_ck32k_init(struct tps65910 *tps65910,
+                                       struct tps65910_board *pmic_pdata)
+{
+       int ret;
+
+       if (!pmic_pdata->en_ck32k_xtal)
+               return 0;
+
+       ret = tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL,
+                                               DEVCTRL_CK32K_CTRL_MASK);
+       if (ret < 0) {
+               dev_err(tps65910->dev, "clear ck32k_ctrl failed: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 static int __devinit tps65910_sleepinit(struct tps65910 *tps65910,
                struct tps65910_board *pmic_pdata)
 {
@@ -175,6 +193,9 @@ static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
        else if (*chip_id == TPS65911)
                dev_warn(&client->dev, "VMBCH2-Threshold not specified");
 
+       prop = of_property_read_bool(np, "ti,en-ck32k-xtal");
+       board_info->en_ck32k_xtal = prop;
+
        board_info->irq = client->irq;
        board_info->irq_base = -1;
 
@@ -243,7 +264,7 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c,
        init_data->irq_base = pmic_plat_data->irq_base;
 
        tps65910_irq_init(tps65910, init_data->irq, init_data);
-
+       tps65910_ck32k_init(tps65910, pmic_plat_data);
        tps65910_sleepinit(tps65910, pmic_plat_data);
 
        return ret;
index 6fc90befa79e415bbd377e978191b958abaf6fb1..1c32afed28aad0ade45943e6b20a6b7c22531e93 100644 (file)
@@ -568,7 +568,6 @@ add_numbered_child(unsigned chip, const char *name, int num,
                goto err;
        }
 
-       device_init_wakeup(&pdev->dev, can_wakeup);
        pdev->dev.parent = &twl->client->dev;
 
        if (pdata) {
@@ -593,6 +592,8 @@ add_numbered_child(unsigned chip, const char *name, int num,
        }
 
        status = platform_device_add(pdev);
+       if (status == 0)
+               device_init_wakeup(&pdev->dev, can_wakeup);
 
 err:
        if (status < 0) {
@@ -716,8 +717,9 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
                static struct regulator_consumer_supply usb1v8 = {
                        .supply =       "usb1v8",
                };
-               static struct regulator_consumer_supply usb3v1 = {
-                       .supply =       "usb3v1",
+               static struct regulator_consumer_supply usb3v1[] = {
+                       { .supply =     "usb3v1" },
+                       { .supply =     "bci3v1" },
                };
 
        /* First add the regulators so that they can be used by transceiver */
@@ -745,7 +747,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
                                return PTR_ERR(child);
 
                        child = add_regulator_linked(TWL4030_REG_VUSB3V1,
-                                                     &usb_fixed, &usb3v1, 1,
+                                                     &usb_fixed, usb3v1, 2,
                                                      features);
                        if (IS_ERR(child))
                                return PTR_ERR(child);
@@ -766,7 +768,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
                if (twl_has_regulator() && child) {
                        usb1v5.dev_name = dev_name(child);
                        usb1v8.dev_name = dev_name(child);
-                       usb3v1.dev_name = dev_name(child);
+                       usb3v1[0].dev_name = dev_name(child);
                }
        }
        if (twl_has_usb() && pdata->usb && twl_class_is_6030()) {
index 4ded9e7aa246efdc0e38347fab044f46ef2dc7ef..b0fad0ffca560b0714a9574b42d4c87c89ea3e4d 100644 (file)
@@ -64,19 +64,15 @@ int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
        int ret;
        unsigned int val;
 
-       mutex_lock(&twl6040->io_mutex);
        /* Vibra control registers from cache */
        if (unlikely(reg == TWL6040_REG_VIBCTLL ||
                     reg == TWL6040_REG_VIBCTLR)) {
                val = twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)];
        } else {
                ret = regmap_read(twl6040->regmap, reg, &val);
-               if (ret < 0) {
-                       mutex_unlock(&twl6040->io_mutex);
+               if (ret < 0)
                        return ret;
-               }
        }
-       mutex_unlock(&twl6040->io_mutex);
 
        return val;
 }
@@ -86,12 +82,10 @@ int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val)
 {
        int ret;
 
-       mutex_lock(&twl6040->io_mutex);
        ret = regmap_write(twl6040->regmap, reg, val);
        /* Cache the vibra control registers */
        if (reg == TWL6040_REG_VIBCTLL || reg == TWL6040_REG_VIBCTLR)
                twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)] = val;
-       mutex_unlock(&twl6040->io_mutex);
 
        return ret;
 }
@@ -99,23 +93,13 @@ EXPORT_SYMBOL(twl6040_reg_write);
 
 int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
 {
-       int ret;
-
-       mutex_lock(&twl6040->io_mutex);
-       ret = regmap_update_bits(twl6040->regmap, reg, mask, mask);
-       mutex_unlock(&twl6040->io_mutex);
-       return ret;
+       return regmap_update_bits(twl6040->regmap, reg, mask, mask);
 }
 EXPORT_SYMBOL(twl6040_set_bits);
 
 int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
 {
-       int ret;
-
-       mutex_lock(&twl6040->io_mutex);
-       ret = regmap_update_bits(twl6040->regmap, reg, mask, 0);
-       mutex_unlock(&twl6040->io_mutex);
-       return ret;
+       return regmap_update_bits(twl6040->regmap, reg, mask, 0);
 }
 EXPORT_SYMBOL(twl6040_clear_bits);
 
@@ -573,7 +557,6 @@ static int __devinit twl6040_probe(struct i2c_client *client,
        twl6040->irq = client->irq;
 
        mutex_init(&twl6040->mutex);
-       mutex_init(&twl6040->io_mutex);
        init_completion(&twl6040->ready);
 
        twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
@@ -696,6 +679,7 @@ static int __devexit twl6040_remove(struct i2c_client *client)
 
 static const struct i2c_device_id twl6040_i2c_id[] = {
        { "twl6040", 0, },
+       { "twl6041", 0, },
        { },
 };
 MODULE_DEVICE_TABLE(i2c, twl6040_i2c_id);
diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c
new file mode 100644 (file)
index 0000000..01b9255
--- /dev/null
@@ -0,0 +1,2399 @@
+/*
+ * wm5102-tables.c  --  WM5102 data tables
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+
+#define WM5102_NUM_AOD_ISR 2
+#define WM5102_NUM_ISR 5
+
+static const struct reg_default wm5102_reva_patch[] = {
+       { 0x80, 0x0003 },
+       { 0x221, 0x0090 },
+       { 0x211, 0x0014 },
+       { 0x212, 0x0000 },
+       { 0x214, 0x000C },
+       { 0x171, 0x0002 },
+       { 0x171, 0x0000 },
+       { 0x461, 0x8000 },
+       { 0x463, 0x50F0 },
+       { 0x465, 0x4820 },
+       { 0x467, 0x4040 },
+       { 0x469, 0x3940 },
+       { 0x46B, 0x3310 },
+       { 0x46D, 0x2D80 },
+       { 0x46F, 0x2890 },
+       { 0x471, 0x1990 },
+       { 0x473, 0x1450 },
+       { 0x475, 0x1020 },
+       { 0x477, 0x0CD0 },
+       { 0x479, 0x0A30 },
+       { 0x47B, 0x0810 },
+       { 0x47D, 0x0510 },
+       { 0x500, 0x000D },
+       { 0x507, 0x1820 },
+       { 0x508, 0x1820 },
+       { 0x540, 0x000D },
+       { 0x547, 0x1820 },
+       { 0x548, 0x1820 },
+       { 0x580, 0x000D },
+       { 0x587, 0x1820 },
+       { 0x588, 0x1820 },
+       { 0x101, 0x8140 },
+       { 0x3000, 0x2225 },
+       { 0x3001, 0x3a03 },
+       { 0x3002, 0x0225 },
+       { 0x3003, 0x0801 },
+       { 0x3004, 0x6249 },
+       { 0x3005, 0x0c04 },
+       { 0x3006, 0x0225 },
+       { 0x3007, 0x5901 },
+       { 0x3008, 0xe249 },
+       { 0x3009, 0x030d },
+       { 0x300a, 0x0249 },
+       { 0x300b, 0x2c01 },
+       { 0x300c, 0xe249 },
+       { 0x300d, 0x4342 },
+       { 0x300e, 0xe249 },
+       { 0x300f, 0x73c0 },
+       { 0x3010, 0x4249 },
+       { 0x3011, 0x0c00 },
+       { 0x3012, 0x0225 },
+       { 0x3013, 0x1f01 },
+       { 0x3014, 0x0225 },
+       { 0x3015, 0x1e01 },
+       { 0x3016, 0x0225 },
+       { 0x3017, 0xfa00 },
+       { 0x3018, 0x0000 },
+       { 0x3019, 0xf000 },
+       { 0x301a, 0x0000 },
+       { 0x301b, 0xf000 },
+       { 0x301c, 0x0000 },
+       { 0x301d, 0xf000 },
+       { 0x301e, 0x0000 },
+       { 0x301f, 0xf000 },
+       { 0x3020, 0x0000 },
+       { 0x3021, 0xf000 },
+       { 0x3022, 0x0000 },
+       { 0x3023, 0xf000 },
+       { 0x3024, 0x0000 },
+       { 0x3025, 0xf000 },
+       { 0x3026, 0x0000 },
+       { 0x3027, 0xf000 },
+       { 0x3028, 0x0000 },
+       { 0x3029, 0xf000 },
+       { 0x302a, 0x0000 },
+       { 0x302b, 0xf000 },
+       { 0x302c, 0x0000 },
+       { 0x302d, 0xf000 },
+       { 0x302e, 0x0000 },
+       { 0x302f, 0xf000 },
+       { 0x3030, 0x0225 },
+       { 0x3031, 0x1a01 },
+       { 0x3032, 0x0225 },
+       { 0x3033, 0x1e00 },
+       { 0x3034, 0x0225 },
+       { 0x3035, 0x1f00 },
+       { 0x3036, 0x6225 },
+       { 0x3037, 0xf800 },
+       { 0x3038, 0x0000 },
+       { 0x3039, 0xf000 },
+       { 0x303a, 0x0000 },
+       { 0x303b, 0xf000 },
+       { 0x303c, 0x0000 },
+       { 0x303d, 0xf000 },
+       { 0x303e, 0x0000 },
+       { 0x303f, 0xf000 },
+       { 0x3040, 0x2226 },
+       { 0x3041, 0x3a03 },
+       { 0x3042, 0x0226 },
+       { 0x3043, 0x0801 },
+       { 0x3044, 0x6249 },
+       { 0x3045, 0x0c06 },
+       { 0x3046, 0x0226 },
+       { 0x3047, 0x5901 },
+       { 0x3048, 0xe249 },
+       { 0x3049, 0x030d },
+       { 0x304a, 0x0249 },
+       { 0x304b, 0x2c01 },
+       { 0x304c, 0xe249 },
+       { 0x304d, 0x4342 },
+       { 0x304e, 0xe249 },
+       { 0x304f, 0x73c0 },
+       { 0x3050, 0x4249 },
+       { 0x3051, 0x0c00 },
+       { 0x3052, 0x0226 },
+       { 0x3053, 0x1f01 },
+       { 0x3054, 0x0226 },
+       { 0x3055, 0x1e01 },
+       { 0x3056, 0x0226 },
+       { 0x3057, 0xfa00 },
+       { 0x3058, 0x0000 },
+       { 0x3059, 0xf000 },
+       { 0x305a, 0x0000 },
+       { 0x305b, 0xf000 },
+       { 0x305c, 0x0000 },
+       { 0x305d, 0xf000 },
+       { 0x305e, 0x0000 },
+       { 0x305f, 0xf000 },
+       { 0x3060, 0x0000 },
+       { 0x3061, 0xf000 },
+       { 0x3062, 0x0000 },
+       { 0x3063, 0xf000 },
+       { 0x3064, 0x0000 },
+       { 0x3065, 0xf000 },
+       { 0x3066, 0x0000 },
+       { 0x3067, 0xf000 },
+       { 0x3068, 0x0000 },
+       { 0x3069, 0xf000 },
+       { 0x306a, 0x0000 },
+       { 0x306b, 0xf000 },
+       { 0x306c, 0x0000 },
+       { 0x306d, 0xf000 },
+       { 0x306e, 0x0000 },
+       { 0x306f, 0xf000 },
+       { 0x3070, 0x0226 },
+       { 0x3071, 0x1a01 },
+       { 0x3072, 0x0226 },
+       { 0x3073, 0x1e00 },
+       { 0x3074, 0x0226 },
+       { 0x3075, 0x1f00 },
+       { 0x3076, 0x6226 },
+       { 0x3077, 0xf800 },
+       { 0x3078, 0x0000 },
+       { 0x3079, 0xf000 },
+       { 0x307a, 0x0000 },
+       { 0x307b, 0xf000 },
+       { 0x307c, 0x0000 },
+       { 0x307d, 0xf000 },
+       { 0x307e, 0x0000 },
+       { 0x307f, 0xf000 },
+       { 0x3080, 0x2227 },
+       { 0x3081, 0x3a03 },
+       { 0x3082, 0x0227 },
+       { 0x3083, 0x0801 },
+       { 0x3084, 0x6255 },
+       { 0x3085, 0x0c04 },
+       { 0x3086, 0x0227 },
+       { 0x3087, 0x5901 },
+       { 0x3088, 0xe255 },
+       { 0x3089, 0x030d },
+       { 0x308a, 0x0255 },
+       { 0x308b, 0x2c01 },
+       { 0x308c, 0xe255 },
+       { 0x308d, 0x4342 },
+       { 0x308e, 0xe255 },
+       { 0x308f, 0x73c0 },
+       { 0x3090, 0x4255 },
+       { 0x3091, 0x0c00 },
+       { 0x3092, 0x0227 },
+       { 0x3093, 0x1f01 },
+       { 0x3094, 0x0227 },
+       { 0x3095, 0x1e01 },
+       { 0x3096, 0x0227 },
+       { 0x3097, 0xfa00 },
+       { 0x3098, 0x0000 },
+       { 0x3099, 0xf000 },
+       { 0x309a, 0x0000 },
+       { 0x309b, 0xf000 },
+       { 0x309c, 0x0000 },
+       { 0x309d, 0xf000 },
+       { 0x309e, 0x0000 },
+       { 0x309f, 0xf000 },
+       { 0x30a0, 0x0000 },
+       { 0x30a1, 0xf000 },
+       { 0x30a2, 0x0000 },
+       { 0x30a3, 0xf000 },
+       { 0x30a4, 0x0000 },
+       { 0x30a5, 0xf000 },
+       { 0x30a6, 0x0000 },
+       { 0x30a7, 0xf000 },
+       { 0x30a8, 0x0000 },
+       { 0x30a9, 0xf000 },
+       { 0x30aa, 0x0000 },
+       { 0x30ab, 0xf000 },
+       { 0x30ac, 0x0000 },
+       { 0x30ad, 0xf000 },
+       { 0x30ae, 0x0000 },
+       { 0x30af, 0xf000 },
+       { 0x30b0, 0x0227 },
+       { 0x30b1, 0x1a01 },
+       { 0x30b2, 0x0227 },
+       { 0x30b3, 0x1e00 },
+       { 0x30b4, 0x0227 },
+       { 0x30b5, 0x1f00 },
+       { 0x30b6, 0x6227 },
+       { 0x30b7, 0xf800 },
+       { 0x30b8, 0x0000 },
+       { 0x30b9, 0xf000 },
+       { 0x30ba, 0x0000 },
+       { 0x30bb, 0xf000 },
+       { 0x30bc, 0x0000 },
+       { 0x30bd, 0xf000 },
+       { 0x30be, 0x0000 },
+       { 0x30bf, 0xf000 },
+       { 0x30c0, 0x2228 },
+       { 0x30c1, 0x3a03 },
+       { 0x30c2, 0x0228 },
+       { 0x30c3, 0x0801 },
+       { 0x30c4, 0x6255 },
+       { 0x30c5, 0x0c06 },
+       { 0x30c6, 0x0228 },
+       { 0x30c7, 0x5901 },
+       { 0x30c8, 0xe255 },
+       { 0x30c9, 0x030d },
+       { 0x30ca, 0x0255 },
+       { 0x30cb, 0x2c01 },
+       { 0x30cc, 0xe255 },
+       { 0x30cd, 0x4342 },
+       { 0x30ce, 0xe255 },
+       { 0x30cf, 0x73c0 },
+       { 0x30d0, 0x4255 },
+       { 0x30d1, 0x0c00 },
+       { 0x30d2, 0x0228 },
+       { 0x30d3, 0x1f01 },
+       { 0x30d4, 0x0228 },
+       { 0x30d5, 0x1e01 },
+       { 0x30d6, 0x0228 },
+       { 0x30d7, 0xfa00 },
+       { 0x30d8, 0x0000 },
+       { 0x30d9, 0xf000 },
+       { 0x30da, 0x0000 },
+       { 0x30db, 0xf000 },
+       { 0x30dc, 0x0000 },
+       { 0x30dd, 0xf000 },
+       { 0x30de, 0x0000 },
+       { 0x30df, 0xf000 },
+       { 0x30e0, 0x0000 },
+       { 0x30e1, 0xf000 },
+       { 0x30e2, 0x0000 },
+       { 0x30e3, 0xf000 },
+       { 0x30e4, 0x0000 },
+       { 0x30e5, 0xf000 },
+       { 0x30e6, 0x0000 },
+       { 0x30e7, 0xf000 },
+       { 0x30e8, 0x0000 },
+       { 0x30e9, 0xf000 },
+       { 0x30ea, 0x0000 },
+       { 0x30eb, 0xf000 },
+       { 0x30ec, 0x0000 },
+       { 0x30ed, 0xf000 },
+       { 0x30ee, 0x0000 },
+       { 0x30ef, 0xf000 },
+       { 0x30f0, 0x0228 },
+       { 0x30f1, 0x1a01 },
+       { 0x30f2, 0x0228 },
+       { 0x30f3, 0x1e00 },
+       { 0x30f4, 0x0228 },
+       { 0x30f5, 0x1f00 },
+       { 0x30f6, 0x6228 },
+       { 0x30f7, 0xf800 },
+       { 0x30f8, 0x0000 },
+       { 0x30f9, 0xf000 },
+       { 0x30fa, 0x0000 },
+       { 0x30fb, 0xf000 },
+       { 0x30fc, 0x0000 },
+       { 0x30fd, 0xf000 },
+       { 0x30fe, 0x0000 },
+       { 0x30ff, 0xf000 },
+       { 0x3100, 0x222b },
+       { 0x3101, 0x3a03 },
+       { 0x3102, 0x222b },
+       { 0x3103, 0x5803 },
+       { 0x3104, 0xe26f },
+       { 0x3105, 0x030d },
+       { 0x3106, 0x626f },
+       { 0x3107, 0x2c01 },
+       { 0x3108, 0xe26f },
+       { 0x3109, 0x4342 },
+       { 0x310a, 0xe26f },
+       { 0x310b, 0x73c0 },
+       { 0x310c, 0x026f },
+       { 0x310d, 0x0c00 },
+       { 0x310e, 0x022b },
+       { 0x310f, 0x1f01 },
+       { 0x3110, 0x022b },
+       { 0x3111, 0x1e01 },
+       { 0x3112, 0x022b },
+       { 0x3113, 0xfa00 },
+       { 0x3114, 0x0000 },
+       { 0x3115, 0xf000 },
+       { 0x3116, 0x0000 },
+       { 0x3117, 0xf000 },
+       { 0x3118, 0x0000 },
+       { 0x3119, 0xf000 },
+       { 0x311a, 0x0000 },
+       { 0x311b, 0xf000 },
+       { 0x311c, 0x0000 },
+       { 0x311d, 0xf000 },
+       { 0x311e, 0x0000 },
+       { 0x311f, 0xf000 },
+       { 0x3120, 0x022b },
+       { 0x3121, 0x0a01 },
+       { 0x3122, 0x022b },
+       { 0x3123, 0x1e00 },
+       { 0x3124, 0x022b },
+       { 0x3125, 0x1f00 },
+       { 0x3126, 0x622b },
+       { 0x3127, 0xf800 },
+       { 0x3128, 0x0000 },
+       { 0x3129, 0xf000 },
+       { 0x312a, 0x0000 },
+       { 0x312b, 0xf000 },
+       { 0x312c, 0x0000 },
+       { 0x312d, 0xf000 },
+       { 0x312e, 0x0000 },
+       { 0x312f, 0xf000 },
+       { 0x3130, 0x0000 },
+       { 0x3131, 0xf000 },
+       { 0x3132, 0x0000 },
+       { 0x3133, 0xf000 },
+       { 0x3134, 0x0000 },
+       { 0x3135, 0xf000 },
+       { 0x3136, 0x0000 },
+       { 0x3137, 0xf000 },
+       { 0x3138, 0x0000 },
+       { 0x3139, 0xf000 },
+       { 0x313a, 0x0000 },
+       { 0x313b, 0xf000 },
+       { 0x313c, 0x0000 },
+       { 0x313d, 0xf000 },
+       { 0x313e, 0x0000 },
+       { 0x313f, 0xf000 },
+       { 0x3140, 0x0000 },
+       { 0x3141, 0xf000 },
+       { 0x3142, 0x0000 },
+       { 0x3143, 0xf000 },
+       { 0x3144, 0x0000 },
+       { 0x3145, 0xf000 },
+       { 0x3146, 0x0000 },
+       { 0x3147, 0xf000 },
+       { 0x3148, 0x0000 },
+       { 0x3149, 0xf000 },
+       { 0x314a, 0x0000 },
+       { 0x314b, 0xf000 },
+       { 0x314c, 0x0000 },
+       { 0x314d, 0xf000 },
+       { 0x314e, 0x0000 },
+       { 0x314f, 0xf000 },
+       { 0x3150, 0x0000 },
+       { 0x3151, 0xf000 },
+       { 0x3152, 0x0000 },
+       { 0x3153, 0xf000 },
+       { 0x3154, 0x0000 },
+       { 0x3155, 0xf000 },
+       { 0x3156, 0x0000 },
+       { 0x3157, 0xf000 },
+       { 0x3158, 0x0000 },
+       { 0x3159, 0xf000 },
+       { 0x315a, 0x0000 },
+       { 0x315b, 0xf000 },
+       { 0x315c, 0x0000 },
+       { 0x315d, 0xf000 },
+       { 0x315e, 0x0000 },
+       { 0x315f, 0xf000 },
+       { 0x3160, 0x0000 },
+       { 0x3161, 0xf000 },
+       { 0x3162, 0x0000 },
+       { 0x3163, 0xf000 },
+       { 0x3164, 0x0000 },
+       { 0x3165, 0xf000 },
+       { 0x3166, 0x0000 },
+       { 0x3167, 0xf000 },
+       { 0x3168, 0x0000 },
+       { 0x3169, 0xf000 },
+       { 0x316a, 0x0000 },
+       { 0x316b, 0xf000 },
+       { 0x316c, 0x0000 },
+       { 0x316d, 0xf000 },
+       { 0x316e, 0x0000 },
+       { 0x316f, 0xf000 },
+       { 0x3170, 0x0000 },
+       { 0x3171, 0xf000 },
+       { 0x3172, 0x0000 },
+       { 0x3173, 0xf000 },
+       { 0x3174, 0x0000 },
+       { 0x3175, 0xf000 },
+       { 0x3176, 0x0000 },
+       { 0x3177, 0xf000 },
+       { 0x3178, 0x0000 },
+       { 0x3179, 0xf000 },
+       { 0x317a, 0x0000 },
+       { 0x317b, 0xf000 },
+       { 0x317c, 0x0000 },
+       { 0x317d, 0xf000 },
+       { 0x317e, 0x0000 },
+       { 0x317f, 0xf000 },
+       { 0x3180, 0x2001 },
+       { 0x3181, 0xf101 },
+       { 0x3182, 0x0000 },
+       { 0x3183, 0xf000 },
+       { 0x3184, 0x0000 },
+       { 0x3185, 0xf000 },
+       { 0x3186, 0x0000 },
+       { 0x3187, 0xf000 },
+       { 0x3188, 0x0000 },
+       { 0x3189, 0xf000 },
+       { 0x318a, 0x0000 },
+       { 0x318b, 0xf000 },
+       { 0x318c, 0x0000 },
+       { 0x318d, 0xf000 },
+       { 0x318e, 0x0000 },
+       { 0x318f, 0xf000 },
+       { 0x3190, 0x0000 },
+       { 0x3191, 0xf000 },
+       { 0x3192, 0x0000 },
+       { 0x3193, 0xf000 },
+       { 0x3194, 0x0000 },
+       { 0x3195, 0xf000 },
+       { 0x3196, 0x0000 },
+       { 0x3197, 0xf000 },
+       { 0x3198, 0x0000 },
+       { 0x3199, 0xf000 },
+       { 0x319a, 0x0000 },
+       { 0x319b, 0xf000 },
+       { 0x319c, 0x0000 },
+       { 0x319d, 0xf000 },
+       { 0x319e, 0x0000 },
+       { 0x319f, 0xf000 },
+       { 0x31a0, 0x0000 },
+       { 0x31a1, 0xf000 },
+       { 0x31a2, 0x0000 },
+       { 0x31a3, 0xf000 },
+       { 0x31a4, 0x0000 },
+       { 0x31a5, 0xf000 },
+       { 0x31a6, 0x0000 },
+       { 0x31a7, 0xf000 },
+       { 0x31a8, 0x0000 },
+       { 0x31a9, 0xf000 },
+       { 0x31aa, 0x0000 },
+       { 0x31ab, 0xf000 },
+       { 0x31ac, 0x0000 },
+       { 0x31ad, 0xf000 },
+       { 0x31ae, 0x0000 },
+       { 0x31af, 0xf000 },
+       { 0x31b0, 0x0000 },
+       { 0x31b1, 0xf000 },
+       { 0x31b2, 0x0000 },
+       { 0x31b3, 0xf000 },
+       { 0x31b4, 0x0000 },
+       { 0x31b5, 0xf000 },
+       { 0x31b6, 0x0000 },
+       { 0x31b7, 0xf000 },
+       { 0x31b8, 0x0000 },
+       { 0x31b9, 0xf000 },
+       { 0x31ba, 0x0000 },
+       { 0x31bb, 0xf000 },
+       { 0x31bc, 0x0000 },
+       { 0x31bd, 0xf000 },
+       { 0x31be, 0x0000 },
+       { 0x31bf, 0xf000 },
+       { 0x31c0, 0x0000 },
+       { 0x31c1, 0xf000 },
+       { 0x31c2, 0x0000 },
+       { 0x31c3, 0xf000 },
+       { 0x31c4, 0x0000 },
+       { 0x31c5, 0xf000 },
+       { 0x31c6, 0x0000 },
+       { 0x31c7, 0xf000 },
+       { 0x31c8, 0x0000 },
+       { 0x31c9, 0xf000 },
+       { 0x31ca, 0x0000 },
+       { 0x31cb, 0xf000 },
+       { 0x31cc, 0x0000 },
+       { 0x31cd, 0xf000 },
+       { 0x31ce, 0x0000 },
+       { 0x31cf, 0xf000 },
+       { 0x31d0, 0x0000 },
+       { 0x31d1, 0xf000 },
+       { 0x31d2, 0x0000 },
+       { 0x31d3, 0xf000 },
+       { 0x31d4, 0x0000 },
+       { 0x31d5, 0xf000 },
+       { 0x31d6, 0x0000 },
+       { 0x31d7, 0xf000 },
+       { 0x31d8, 0x0000 },
+       { 0x31d9, 0xf000 },
+       { 0x31da, 0x0000 },
+       { 0x31db, 0xf000 },
+       { 0x31dc, 0x0000 },
+       { 0x31dd, 0xf000 },
+       { 0x31de, 0x0000 },
+       { 0x31df, 0xf000 },
+       { 0x31e0, 0x0000 },
+       { 0x31e1, 0xf000 },
+       { 0x31e2, 0x0000 },
+       { 0x31e3, 0xf000 },
+       { 0x31e4, 0x0000 },
+       { 0x31e5, 0xf000 },
+       { 0x31e6, 0x0000 },
+       { 0x31e7, 0xf000 },
+       { 0x31e8, 0x0000 },
+       { 0x31e9, 0xf000 },
+       { 0x31ea, 0x0000 },
+       { 0x31eb, 0xf000 },
+       { 0x31ec, 0x0000 },
+       { 0x31ed, 0xf000 },
+       { 0x31ee, 0x0000 },
+       { 0x31ef, 0xf000 },
+       { 0x31f0, 0x0000 },
+       { 0x31f1, 0xf000 },
+       { 0x31f2, 0x0000 },
+       { 0x31f3, 0xf000 },
+       { 0x31f4, 0x0000 },
+       { 0x31f5, 0xf000 },
+       { 0x31f6, 0x0000 },
+       { 0x31f7, 0xf000 },
+       { 0x31f8, 0x0000 },
+       { 0x31f9, 0xf000 },
+       { 0x31fa, 0x0000 },
+       { 0x31fb, 0xf000 },
+       { 0x31fc, 0x0000 },
+       { 0x31fd, 0xf000 },
+       { 0x31fe, 0x0000 },
+       { 0x31ff, 0xf000 },
+       { 0x024d, 0xff50 },
+       { 0x0252, 0xff50 },
+       { 0x0259, 0x0112 },
+       { 0x025e, 0x0112 },
+       { 0x101, 0x0304 },
+       { 0x80, 0x0000 },
+};
+
+/* We use a function so we can use ARRAY_SIZE() */
+int wm5102_patch(struct arizona *arizona)
+{
+       switch (arizona->rev) {
+       case 0:
+               return regmap_register_patch(arizona->regmap,
+                                            wm5102_reva_patch,
+                                            ARRAY_SIZE(wm5102_reva_patch));
+       default:
+               return 0;
+       }
+}
+
+static const struct regmap_irq wm5102_aod_irqs[ARIZONA_NUM_IRQ] = {
+       [ARIZONA_IRQ_GP5_FALL] = { .mask = ARIZONA_GP5_FALL_EINT1 },
+       [ARIZONA_IRQ_GP5_RISE] = { .mask = ARIZONA_GP5_RISE_EINT1 },
+       [ARIZONA_IRQ_JD_FALL] = { .mask = ARIZONA_JD1_FALL_EINT1 },
+       [ARIZONA_IRQ_JD_RISE] = { .mask = ARIZONA_JD1_RISE_EINT1 },
+};
+
+const struct regmap_irq_chip wm5102_aod = {
+       .name = "wm5102 AOD",
+       .status_base = ARIZONA_AOD_IRQ1,
+       .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1,
+       .ack_base = ARIZONA_AOD_IRQ1,
+       .wake_base = ARIZONA_WAKE_CONTROL,
+       .num_regs = 1,
+       .irqs = wm5102_aod_irqs,
+       .num_irqs = ARRAY_SIZE(wm5102_aod_irqs),
+};
+
+static const struct regmap_irq wm5102_irqs[ARIZONA_NUM_IRQ] = {
+       [ARIZONA_IRQ_GP4] = { .reg_offset = 0, .mask = ARIZONA_GP4_EINT1 },
+       [ARIZONA_IRQ_GP3] = { .reg_offset = 0, .mask = ARIZONA_GP3_EINT1 },
+       [ARIZONA_IRQ_GP2] = { .reg_offset = 0, .mask = ARIZONA_GP2_EINT1 },
+       [ARIZONA_IRQ_GP1] = { .reg_offset = 0, .mask = ARIZONA_GP1_EINT1 },
+
+       [ARIZONA_IRQ_DSP1_RAM_RDY] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP1_RAM_RDY_EINT1
+       },
+       [ARIZONA_IRQ_DSP_IRQ2] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP_IRQ2_EINT1
+       },
+       [ARIZONA_IRQ_DSP_IRQ1] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP_IRQ1_EINT1
+       },
+
+       [ARIZONA_IRQ_SPK_SHUTDOWN_WARN] = {
+               .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_WARN_EINT1
+       },
+       [ARIZONA_IRQ_SPK_SHUTDOWN] = {
+               .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_EINT1
+       },
+       [ARIZONA_IRQ_HPDET] = {
+               .reg_offset = 2, .mask = ARIZONA_HPDET_EINT1
+       },
+       [ARIZONA_IRQ_MICDET] = {
+               .reg_offset = 2, .mask = ARIZONA_MICDET_EINT1
+       },
+       [ARIZONA_IRQ_WSEQ_DONE] = {
+               .reg_offset = 2, .mask = ARIZONA_WSEQ_DONE_EINT1
+       },
+       [ARIZONA_IRQ_DRC2_SIG_DET] = {
+               .reg_offset = 2, .mask = ARIZONA_DRC2_SIG_DET_EINT1
+       },
+       [ARIZONA_IRQ_DRC1_SIG_DET] = {
+               .reg_offset = 2, .mask = ARIZONA_DRC1_SIG_DET_EINT1
+       },
+       [ARIZONA_IRQ_ASRC2_LOCK] = {
+               .reg_offset = 2, .mask = ARIZONA_ASRC2_LOCK_EINT1
+       },
+       [ARIZONA_IRQ_ASRC1_LOCK] = {
+               .reg_offset = 2, .mask = ARIZONA_ASRC1_LOCK_EINT1
+       },
+       [ARIZONA_IRQ_UNDERCLOCKED] = {
+               .reg_offset = 2, .mask = ARIZONA_UNDERCLOCKED_EINT1
+       },
+       [ARIZONA_IRQ_OVERCLOCKED] = {
+               .reg_offset = 2, .mask = ARIZONA_OVERCLOCKED_EINT1
+       },
+       [ARIZONA_IRQ_FLL2_LOCK] = {
+               .reg_offset = 2, .mask = ARIZONA_FLL2_LOCK_EINT1
+       },
+       [ARIZONA_IRQ_FLL1_LOCK] = {
+               .reg_offset = 2, .mask = ARIZONA_FLL1_LOCK_EINT1
+       },
+       [ARIZONA_IRQ_CLKGEN_ERR] = {
+               .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_EINT1
+       },
+       [ARIZONA_IRQ_CLKGEN_ERR_ASYNC] = {
+               .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_ASYNC_EINT1
+       },
+
+       [ARIZONA_IRQ_ASRC_CFG_ERR] = {
+               .reg_offset = 3, .mask = ARIZONA_ASRC_CFG_ERR_EINT1
+       },
+       [ARIZONA_IRQ_AIF3_ERR] = {
+               .reg_offset = 3, .mask = ARIZONA_AIF3_ERR_EINT1
+       },
+       [ARIZONA_IRQ_AIF2_ERR] = {
+               .reg_offset = 3, .mask = ARIZONA_AIF2_ERR_EINT1
+       },
+       [ARIZONA_IRQ_AIF1_ERR] = {
+               .reg_offset = 3, .mask = ARIZONA_AIF1_ERR_EINT1
+       },
+       [ARIZONA_IRQ_CTRLIF_ERR] = {
+               .reg_offset = 3, .mask = ARIZONA_CTRLIF_ERR_EINT1
+       },
+       [ARIZONA_IRQ_MIXER_DROPPED_SAMPLES] = {
+               .reg_offset = 3, .mask = ARIZONA_MIXER_DROPPED_SAMPLE_EINT1
+       },
+       [ARIZONA_IRQ_ASYNC_CLK_ENA_LOW] = {
+               .reg_offset = 3, .mask = ARIZONA_ASYNC_CLK_ENA_LOW_EINT1
+       },
+       [ARIZONA_IRQ_SYSCLK_ENA_LOW] = {
+               .reg_offset = 3, .mask = ARIZONA_SYSCLK_ENA_LOW_EINT1
+       },
+       [ARIZONA_IRQ_ISRC1_CFG_ERR] = {
+               .reg_offset = 3, .mask = ARIZONA_ISRC1_CFG_ERR_EINT1
+       },
+       [ARIZONA_IRQ_ISRC2_CFG_ERR] = {
+               .reg_offset = 3, .mask = ARIZONA_ISRC2_CFG_ERR_EINT1
+       },
+
+       [ARIZONA_IRQ_BOOT_DONE] = {
+               .reg_offset = 4, .mask = ARIZONA_BOOT_DONE_EINT1
+       },
+       [ARIZONA_IRQ_DCS_DAC_DONE] = {
+               .reg_offset = 4, .mask = ARIZONA_DCS_DAC_DONE_EINT1
+       },
+       [ARIZONA_IRQ_DCS_HP_DONE] = {
+               .reg_offset = 4, .mask = ARIZONA_DCS_HP_DONE_EINT1
+       },
+       [ARIZONA_IRQ_FLL2_CLOCK_OK] = {
+               .reg_offset = 4, .mask = ARIZONA_FLL2_CLOCK_OK_EINT1
+       },
+       [ARIZONA_IRQ_FLL1_CLOCK_OK] = {
+               .reg_offset = 4, .mask = ARIZONA_FLL1_CLOCK_OK_EINT1
+       },
+};
+
+const struct regmap_irq_chip wm5102_irq = {
+       .name = "wm5102 IRQ",
+       .status_base = ARIZONA_INTERRUPT_STATUS_1,
+       .mask_base = ARIZONA_INTERRUPT_STATUS_1_MASK,
+       .ack_base = ARIZONA_INTERRUPT_STATUS_1,
+       .num_regs = 5,
+       .irqs = wm5102_irqs,
+       .num_irqs = ARRAY_SIZE(wm5102_irqs),
+};
+
+static const struct reg_default wm5102_reg_default[] = {
+       { 0x00000008, 0x0019 },   /* R8     - Ctrl IF SPI CFG 1 */ 
+       { 0x00000009, 0x0001 },   /* R9     - Ctrl IF I2C1 CFG 1 */ 
+       { 0x0000000D, 0x0000 },   /* R13    - Ctrl IF Status 1 */ 
+       { 0x00000016, 0x0000 },   /* R22    - Write Sequencer Ctrl 0 */ 
+       { 0x00000017, 0x0000 },   /* R23    - Write Sequencer Ctrl 1 */ 
+       { 0x00000018, 0x0000 },   /* R24    - Write Sequencer Ctrl 2 */ 
+       { 0x0000001A, 0x0000 },   /* R26    - Write Sequencer PROM */ 
+       { 0x00000020, 0x0000 },   /* R32    - Tone Generator 1 */ 
+       { 0x00000021, 0x1000 },   /* R33    - Tone Generator 2 */ 
+       { 0x00000022, 0x0000 },   /* R34    - Tone Generator 3 */ 
+       { 0x00000023, 0x1000 },   /* R35    - Tone Generator 4 */ 
+       { 0x00000024, 0x0000 },   /* R36    - Tone Generator 5 */ 
+       { 0x00000030, 0x0000 },   /* R48    - PWM Drive 1 */ 
+       { 0x00000031, 0x0100 },   /* R49    - PWM Drive 2 */ 
+       { 0x00000032, 0x0100 },   /* R50    - PWM Drive 3 */ 
+       { 0x00000040, 0x0000 },   /* R64    - Wake control */ 
+       { 0x00000041, 0x0000 },   /* R65    - Sequence control */ 
+       { 0x00000061, 0x01FF },   /* R97    - Sample Rate Sequence Select 1 */ 
+       { 0x00000062, 0x01FF },   /* R98    - Sample Rate Sequence Select 2 */ 
+       { 0x00000063, 0x01FF },   /* R99    - Sample Rate Sequence Select 3 */ 
+       { 0x00000064, 0x01FF },   /* R100   - Sample Rate Sequence Select 4 */ 
+       { 0x00000068, 0x01FF },   /* R104   - Always On Triggers Sequence Select 1 */ 
+       { 0x00000069, 0x01FF },   /* R105   - Always On Triggers Sequence Select 2 */ 
+       { 0x0000006A, 0x01FF },   /* R106   - Always On Triggers Sequence Select 3 */ 
+       { 0x0000006B, 0x01FF },   /* R107   - Always On Triggers Sequence Select 4 */ 
+       { 0x0000006C, 0x01FF },   /* R108   - Always On Triggers Sequence Select 5 */ 
+       { 0x0000006D, 0x01FF },   /* R109   - Always On Triggers Sequence Select 6 */ 
+       { 0x00000070, 0x0000 },   /* R112   - Comfort Noise Generator */ 
+       { 0x00000090, 0x0000 },   /* R144   - Haptics Control 1 */ 
+       { 0x00000091, 0x7FFF },   /* R145   - Haptics Control 2 */ 
+       { 0x00000092, 0x0000 },   /* R146   - Haptics phase 1 intensity */ 
+       { 0x00000093, 0x0000 },   /* R147   - Haptics phase 1 duration */ 
+       { 0x00000094, 0x0000 },   /* R148   - Haptics phase 2 intensity */ 
+       { 0x00000095, 0x0000 },   /* R149   - Haptics phase 2 duration */ 
+       { 0x00000096, 0x0000 },   /* R150   - Haptics phase 3 intensity */ 
+       { 0x00000097, 0x0000 },   /* R151   - Haptics phase 3 duration */ 
+       { 0x00000100, 0x0001 },   /* R256   - Clock 32k 1 */ 
+       { 0x00000101, 0x0304 },   /* R257   - System Clock 1 */ 
+       { 0x00000102, 0x0011 },   /* R258   - Sample rate 1 */ 
+       { 0x00000103, 0x0011 },   /* R259   - Sample rate 2 */ 
+       { 0x00000104, 0x0011 },   /* R260   - Sample rate 3 */ 
+       { 0x00000112, 0x0305 },   /* R274   - Async clock 1 */ 
+       { 0x00000113, 0x0011 },   /* R275   - Async sample rate 1 */ 
+       { 0x00000149, 0x0000 },   /* R329   - Output system clock */ 
+       { 0x0000014A, 0x0000 },   /* R330   - Output async clock */ 
+       { 0x00000152, 0x0000 },   /* R338   - Rate Estimator 1 */ 
+       { 0x00000153, 0x0000 },   /* R339   - Rate Estimator 2 */ 
+       { 0x00000154, 0x0000 },   /* R340   - Rate Estimator 3 */ 
+       { 0x00000155, 0x0000 },   /* R341   - Rate Estimator 4 */ 
+       { 0x00000156, 0x0000 },   /* R342   - Rate Estimator 5 */ 
+       { 0x00000171, 0x0000 },   /* R369   - FLL1 Control 1 */ 
+       { 0x00000172, 0x0008 },   /* R370   - FLL1 Control 2 */ 
+       { 0x00000173, 0x0018 },   /* R371   - FLL1 Control 3 */ 
+       { 0x00000174, 0x007D },   /* R372   - FLL1 Control 4 */ 
+       { 0x00000175, 0x0004 },   /* R373   - FLL1 Control 5 */ 
+       { 0x00000176, 0x0000 },   /* R374   - FLL1 Control 6 */ 
+       { 0x00000177, 0x0181 },   /* R375   - FLL1 Loop Filter Test 1 */ 
+       { 0x00000181, 0x0000 },   /* R385   - FLL1 Synchroniser 1 */ 
+       { 0x00000182, 0x0000 },   /* R386   - FLL1 Synchroniser 2 */ 
+       { 0x00000183, 0x0000 },   /* R387   - FLL1 Synchroniser 3 */ 
+       { 0x00000184, 0x0000 },   /* R388   - FLL1 Synchroniser 4 */ 
+       { 0x00000185, 0x0000 },   /* R389   - FLL1 Synchroniser 5 */ 
+       { 0x00000186, 0x0000 },   /* R390   - FLL1 Synchroniser 6 */ 
+       { 0x00000189, 0x0000 },   /* R393   - FLL1 Spread Spectrum */ 
+       { 0x0000018A, 0x0004 },   /* R394   - FLL1 GPIO Clock */ 
+       { 0x00000191, 0x0000 },   /* R401   - FLL2 Control 1 */ 
+       { 0x00000192, 0x0008 },   /* R402   - FLL2 Control 2 */ 
+       { 0x00000193, 0x0018 },   /* R403   - FLL2 Control 3 */ 
+       { 0x00000194, 0x007D },   /* R404   - FLL2 Control 4 */ 
+       { 0x00000195, 0x0004 },   /* R405   - FLL2 Control 5 */ 
+       { 0x00000196, 0x0000 },   /* R406   - FLL2 Control 6 */ 
+       { 0x00000197, 0x0000 },   /* R407   - FLL2 Loop Filter Test 1 */ 
+       { 0x000001A1, 0x0000 },   /* R417   - FLL2 Synchroniser 1 */ 
+       { 0x000001A2, 0x0000 },   /* R418   - FLL2 Synchroniser 2 */ 
+       { 0x000001A3, 0x0000 },   /* R419   - FLL2 Synchroniser 3 */ 
+       { 0x000001A4, 0x0000 },   /* R420   - FLL2 Synchroniser 4 */ 
+       { 0x000001A5, 0x0000 },   /* R421   - FLL2 Synchroniser 5 */ 
+       { 0x000001A6, 0x0000 },   /* R422   - FLL2 Synchroniser 6 */ 
+       { 0x000001A9, 0x0000 },   /* R425   - FLL2 Spread Spectrum */ 
+       { 0x000001AA, 0x0004 },   /* R426   - FLL2 GPIO Clock */ 
+       { 0x00000200, 0x0006 },   /* R512   - Mic Charge Pump 1 */ 
+       { 0x00000210, 0x00D4 },   /* R528   - LDO1 Control 1 */ 
+       { 0x00000213, 0x0344 },   /* R531   - LDO2 Control 1 */ 
+       { 0x00000218, 0x01A6 },   /* R536   - Mic Bias Ctrl 1 */ 
+       { 0x00000219, 0x01A6 },   /* R537   - Mic Bias Ctrl 2 */ 
+       { 0x0000021A, 0x01A6 },   /* R538   - Mic Bias Ctrl 3 */ 
+       { 0x00000293, 0x0000 },   /* R659   - Accessory Detect Mode 1 */ 
+       { 0x0000029B, 0x0020 },   /* R667   - Headphone Detect 1 */ 
+       { 0x000002A3, 0x1102 },   /* R675   - Mic Detect 1 */ 
+       { 0x000002A4, 0x009F },   /* R676   - Mic Detect 2 */ 
+       { 0x000002A5, 0x0000 },   /* R677   - Mic Detect 3 */ 
+       { 0x000002C3, 0x0000 },   /* R707   - Mic noise mix control 1 */ 
+       { 0x000002CB, 0x0000 },   /* R715   - Isolation control */ 
+       { 0x000002D3, 0x0000 },   /* R723   - Jack detect analogue */ 
+       { 0x00000300, 0x0000 },   /* R768   - Input Enables */ 
+       { 0x00000308, 0x0000 },   /* R776   - Input Rate */ 
+       { 0x00000309, 0x0022 },   /* R777   - Input Volume Ramp */ 
+       { 0x00000310, 0x2080 },   /* R784   - IN1L Control */ 
+       { 0x00000311, 0x0180 },   /* R785   - ADC Digital Volume 1L */ 
+       { 0x00000312, 0x0000 },   /* R786   - DMIC1L Control */ 
+       { 0x00000314, 0x0080 },   /* R788   - IN1R Control */ 
+       { 0x00000315, 0x0180 },   /* R789   - ADC Digital Volume 1R */ 
+       { 0x00000316, 0x0000 },   /* R790   - DMIC1R Control */ 
+       { 0x00000318, 0x2080 },   /* R792   - IN2L Control */ 
+       { 0x00000319, 0x0180 },   /* R793   - ADC Digital Volume 2L */ 
+       { 0x0000031A, 0x0000 },   /* R794   - DMIC2L Control */ 
+       { 0x0000031C, 0x0080 },   /* R796   - IN2R Control */ 
+       { 0x0000031D, 0x0180 },   /* R797   - ADC Digital Volume 2R */ 
+       { 0x0000031E, 0x0000 },   /* R798   - DMIC2R Control */ 
+       { 0x00000320, 0x2080 },   /* R800   - IN3L Control */ 
+       { 0x00000321, 0x0180 },   /* R801   - ADC Digital Volume 3L */ 
+       { 0x00000322, 0x0000 },   /* R802   - DMIC3L Control */ 
+       { 0x00000324, 0x0080 },   /* R804   - IN3R Control */ 
+       { 0x00000325, 0x0180 },   /* R805   - ADC Digital Volume 3R */ 
+       { 0x00000326, 0x0000 },   /* R806   - DMIC3R Control */ 
+       { 0x00000400, 0x0000 },   /* R1024  - Output Enables 1 */ 
+       { 0x00000408, 0x0000 },   /* R1032  - Output Rate 1 */ 
+       { 0x00000409, 0x0022 },   /* R1033  - Output Volume Ramp */ 
+       { 0x00000410, 0x0080 },   /* R1040  - Output Path Config 1L */ 
+       { 0x00000411, 0x0180 },   /* R1041  - DAC Digital Volume 1L */ 
+       { 0x00000412, 0x0080 },   /* R1042  - DAC Volume Limit 1L */ 
+       { 0x00000413, 0x0001 },   /* R1043  - Noise Gate Select 1L */ 
+       { 0x00000414, 0x0080 },   /* R1044  - Output Path Config 1R */ 
+       { 0x00000415, 0x0180 },   /* R1045  - DAC Digital Volume 1R */ 
+       { 0x00000416, 0x0080 },   /* R1046  - DAC Volume Limit 1R */ 
+       { 0x00000417, 0x0002 },   /* R1047  - Noise Gate Select 1R */ 
+       { 0x00000418, 0x0080 },   /* R1048  - Output Path Config 2L */ 
+       { 0x00000419, 0x0180 },   /* R1049  - DAC Digital Volume 2L */ 
+       { 0x0000041A, 0x0080 },   /* R1050  - DAC Volume Limit 2L */ 
+       { 0x0000041B, 0x0004 },   /* R1051  - Noise Gate Select 2L */ 
+       { 0x0000041C, 0x0080 },   /* R1052  - Output Path Config 2R */ 
+       { 0x0000041D, 0x0180 },   /* R1053  - DAC Digital Volume 2R */ 
+       { 0x0000041E, 0x0080 },   /* R1054  - DAC Volume Limit 2R */ 
+       { 0x0000041F, 0x0008 },   /* R1055  - Noise Gate Select 2R */ 
+       { 0x00000420, 0x0080 },   /* R1056  - Output Path Config 3L */ 
+       { 0x00000421, 0x0180 },   /* R1057  - DAC Digital Volume 3L */ 
+       { 0x00000422, 0x0080 },   /* R1058  - DAC Volume Limit 3L */ 
+       { 0x00000423, 0x0010 },   /* R1059  - Noise Gate Select 3L */ 
+       { 0x00000424, 0x0080 },   /* R1060  - Output Path Config 3R */ 
+       { 0x00000425, 0x0180 },   /* R1061  - DAC Digital Volume 3R */ 
+       { 0x00000426, 0x0080 },   /* R1062  - DAC Volume Limit 3R */ 
+       { 0x00000428, 0x0000 },   /* R1064  - Output Path Config 4L */ 
+       { 0x00000429, 0x0180 },   /* R1065  - DAC Digital Volume 4L */ 
+       { 0x0000042A, 0x0080 },   /* R1066  - Out Volume 4L */ 
+       { 0x0000042B, 0x0040 },   /* R1067  - Noise Gate Select 4L */ 
+       { 0x0000042C, 0x0000 },   /* R1068  - Output Path Config 4R */ 
+       { 0x0000042D, 0x0180 },   /* R1069  - DAC Digital Volume 4R */ 
+       { 0x0000042E, 0x0080 },   /* R1070  - Out Volume 4R */ 
+       { 0x0000042F, 0x0080 },   /* R1071  - Noise Gate Select 4R */ 
+       { 0x00000430, 0x0000 },   /* R1072  - Output Path Config 5L */ 
+       { 0x00000431, 0x0180 },   /* R1073  - DAC Digital Volume 5L */ 
+       { 0x00000432, 0x0080 },   /* R1074  - DAC Volume Limit 5L */ 
+       { 0x00000433, 0x0100 },   /* R1075  - Noise Gate Select 5L */ 
+       { 0x00000434, 0x0000 },   /* R1076  - Output Path Config 5R */ 
+       { 0x00000435, 0x0180 },   /* R1077  - DAC Digital Volume 5R */ 
+       { 0x00000436, 0x0080 },   /* R1078  - DAC Volume Limit 5R */ 
+       { 0x00000437, 0x0200 },   /* R1079  - Noise Gate Select 5R */ 
+       { 0x00000450, 0x0000 },   /* R1104  - DAC AEC Control 1 */ 
+       { 0x00000458, 0x0001 },   /* R1112  - Noise Gate Control */ 
+       { 0x00000490, 0x0069 },   /* R1168  - PDM SPK1 CTRL 1 */ 
+       { 0x00000491, 0x0000 },   /* R1169  - PDM SPK1 CTRL 2 */ 
+       { 0x000004DC, 0x0000 },   /* R1244  - DAC comp 1 */ 
+       { 0x000004DD, 0x0000 },   /* R1245  - DAC comp 2 */ 
+       { 0x000004DE, 0x0000 },   /* R1246  - DAC comp 3 */ 
+       { 0x000004DF, 0x0000 },   /* R1247  - DAC comp 4 */ 
+       { 0x00000500, 0x000C },   /* R1280  - AIF1 BCLK Ctrl */ 
+       { 0x00000501, 0x0008 },   /* R1281  - AIF1 Tx Pin Ctrl */ 
+       { 0x00000502, 0x0000 },   /* R1282  - AIF1 Rx Pin Ctrl */ 
+       { 0x00000503, 0x0000 },   /* R1283  - AIF1 Rate Ctrl */ 
+       { 0x00000504, 0x0000 },   /* R1284  - AIF1 Format */ 
+       { 0x00000505, 0x0040 },   /* R1285  - AIF1 Tx BCLK Rate */ 
+       { 0x00000506, 0x0040 },   /* R1286  - AIF1 Rx BCLK Rate */ 
+       { 0x00000507, 0x1818 },   /* R1287  - AIF1 Frame Ctrl 1 */ 
+       { 0x00000508, 0x1818 },   /* R1288  - AIF1 Frame Ctrl 2 */ 
+       { 0x00000509, 0x0000 },   /* R1289  - AIF1 Frame Ctrl 3 */ 
+       { 0x0000050A, 0x0001 },   /* R1290  - AIF1 Frame Ctrl 4 */ 
+       { 0x0000050B, 0x0002 },   /* R1291  - AIF1 Frame Ctrl 5 */ 
+       { 0x0000050C, 0x0003 },   /* R1292  - AIF1 Frame Ctrl 6 */ 
+       { 0x0000050D, 0x0004 },   /* R1293  - AIF1 Frame Ctrl 7 */ 
+       { 0x0000050E, 0x0005 },   /* R1294  - AIF1 Frame Ctrl 8 */ 
+       { 0x0000050F, 0x0006 },   /* R1295  - AIF1 Frame Ctrl 9 */ 
+       { 0x00000510, 0x0007 },   /* R1296  - AIF1 Frame Ctrl 10 */ 
+       { 0x00000511, 0x0000 },   /* R1297  - AIF1 Frame Ctrl 11 */ 
+       { 0x00000512, 0x0001 },   /* R1298  - AIF1 Frame Ctrl 12 */ 
+       { 0x00000513, 0x0002 },   /* R1299  - AIF1 Frame Ctrl 13 */ 
+       { 0x00000514, 0x0003 },   /* R1300  - AIF1 Frame Ctrl 14 */ 
+       { 0x00000515, 0x0004 },   /* R1301  - AIF1 Frame Ctrl 15 */ 
+       { 0x00000516, 0x0005 },   /* R1302  - AIF1 Frame Ctrl 16 */ 
+       { 0x00000517, 0x0006 },   /* R1303  - AIF1 Frame Ctrl 17 */ 
+       { 0x00000518, 0x0007 },   /* R1304  - AIF1 Frame Ctrl 18 */ 
+       { 0x00000519, 0x0000 },   /* R1305  - AIF1 Tx Enables */ 
+       { 0x0000051A, 0x0000 },   /* R1306  - AIF1 Rx Enables */ 
+       { 0x0000051B, 0x0000 },   /* R1307  - AIF1 Force Write */ 
+       { 0x00000540, 0x000C },   /* R1344  - AIF2 BCLK Ctrl */ 
+       { 0x00000541, 0x0008 },   /* R1345  - AIF2 Tx Pin Ctrl */ 
+       { 0x00000542, 0x0000 },   /* R1346  - AIF2 Rx Pin Ctrl */ 
+       { 0x00000543, 0x0000 },   /* R1347  - AIF2 Rate Ctrl */ 
+       { 0x00000544, 0x0000 },   /* R1348  - AIF2 Format */ 
+       { 0x00000545, 0x0040 },   /* R1349  - AIF2 Tx BCLK Rate */ 
+       { 0x00000546, 0x0040 },   /* R1350  - AIF2 Rx BCLK Rate */ 
+       { 0x00000547, 0x1818 },   /* R1351  - AIF2 Frame Ctrl 1 */ 
+       { 0x00000548, 0x1818 },   /* R1352  - AIF2 Frame Ctrl 2 */ 
+       { 0x00000549, 0x0000 },   /* R1353  - AIF2 Frame Ctrl 3 */ 
+       { 0x0000054A, 0x0001 },   /* R1354  - AIF2 Frame Ctrl 4 */ 
+       { 0x00000551, 0x0000 },   /* R1361  - AIF2 Frame Ctrl 11 */ 
+       { 0x00000552, 0x0001 },   /* R1362  - AIF2 Frame Ctrl 12 */ 
+       { 0x00000559, 0x0000 },   /* R1369  - AIF2 Tx Enables */ 
+       { 0x0000055A, 0x0000 },   /* R1370  - AIF2 Rx Enables */ 
+       { 0x0000055B, 0x0000 },   /* R1371  - AIF2 Force Write */ 
+       { 0x00000580, 0x000C },   /* R1408  - AIF3 BCLK Ctrl */ 
+       { 0x00000581, 0x0008 },   /* R1409  - AIF3 Tx Pin Ctrl */ 
+       { 0x00000582, 0x0000 },   /* R1410  - AIF3 Rx Pin Ctrl */ 
+       { 0x00000583, 0x0000 },   /* R1411  - AIF3 Rate Ctrl */ 
+       { 0x00000584, 0x0000 },   /* R1412  - AIF3 Format */ 
+       { 0x00000585, 0x0040 },   /* R1413  - AIF3 Tx BCLK Rate */ 
+       { 0x00000586, 0x0040 },   /* R1414  - AIF3 Rx BCLK Rate */ 
+       { 0x00000587, 0x1818 },   /* R1415  - AIF3 Frame Ctrl 1 */ 
+       { 0x00000588, 0x1818 },   /* R1416  - AIF3 Frame Ctrl 2 */ 
+       { 0x00000589, 0x0000 },   /* R1417  - AIF3 Frame Ctrl 3 */ 
+       { 0x0000058A, 0x0001 },   /* R1418  - AIF3 Frame Ctrl 4 */ 
+       { 0x00000591, 0x0000 },   /* R1425  - AIF3 Frame Ctrl 11 */ 
+       { 0x00000592, 0x0001 },   /* R1426  - AIF3 Frame Ctrl 12 */ 
+       { 0x00000599, 0x0000 },   /* R1433  - AIF3 Tx Enables */ 
+       { 0x0000059A, 0x0000 },   /* R1434  - AIF3 Rx Enables */ 
+       { 0x0000059B, 0x0000 },   /* R1435  - AIF3 Force Write */ 
+       { 0x000005E3, 0x0004 },   /* R1507  - SLIMbus Framer Ref Gear */ 
+       { 0x000005E5, 0x0000 },   /* R1509  - SLIMbus Rates 1 */ 
+       { 0x000005E6, 0x0000 },   /* R1510  - SLIMbus Rates 2 */ 
+       { 0x000005E7, 0x0000 },   /* R1511  - SLIMbus Rates 3 */ 
+       { 0x000005E8, 0x0000 },   /* R1512  - SLIMbus Rates 4 */ 
+       { 0x000005E9, 0x0000 },   /* R1513  - SLIMbus Rates 5 */ 
+       { 0x000005EA, 0x0000 },   /* R1514  - SLIMbus Rates 6 */ 
+       { 0x000005EB, 0x0000 },   /* R1515  - SLIMbus Rates 7 */ 
+       { 0x000005EC, 0x0000 },   /* R1516  - SLIMbus Rates 8 */ 
+       { 0x000005F5, 0x0000 },   /* R1525  - SLIMbus RX Channel Enable */ 
+       { 0x000005F6, 0x0000 },   /* R1526  - SLIMbus TX Channel Enable */ 
+       { 0x00000640, 0x0000 },   /* R1600  - PWM1MIX Input 1 Source */ 
+       { 0x00000641, 0x0080 },   /* R1601  - PWM1MIX Input 1 Volume */ 
+       { 0x00000642, 0x0000 },   /* R1602  - PWM1MIX Input 2 Source */ 
+       { 0x00000643, 0x0080 },   /* R1603  - PWM1MIX Input 2 Volume */ 
+       { 0x00000644, 0x0000 },   /* R1604  - PWM1MIX Input 3 Source */ 
+       { 0x00000645, 0x0080 },   /* R1605  - PWM1MIX Input 3 Volume */ 
+       { 0x00000646, 0x0000 },   /* R1606  - PWM1MIX Input 4 Source */ 
+       { 0x00000647, 0x0080 },   /* R1607  - PWM1MIX Input 4 Volume */ 
+       { 0x00000648, 0x0000 },   /* R1608  - PWM2MIX Input 1 Source */ 
+       { 0x00000649, 0x0080 },   /* R1609  - PWM2MIX Input 1 Volume */ 
+       { 0x0000064A, 0x0000 },   /* R1610  - PWM2MIX Input 2 Source */ 
+       { 0x0000064B, 0x0080 },   /* R1611  - PWM2MIX Input 2 Volume */ 
+       { 0x0000064C, 0x0000 },   /* R1612  - PWM2MIX Input 3 Source */ 
+       { 0x0000064D, 0x0080 },   /* R1613  - PWM2MIX Input 3 Volume */ 
+       { 0x0000064E, 0x0000 },   /* R1614  - PWM2MIX Input 4 Source */ 
+       { 0x0000064F, 0x0080 },   /* R1615  - PWM2MIX Input 4 Volume */ 
+       { 0x00000660, 0x0000 },   /* R1632  - MICMIX Input 1 Source */ 
+       { 0x00000661, 0x0080 },   /* R1633  - MICMIX Input 1 Volume */ 
+       { 0x00000662, 0x0000 },   /* R1634  - MICMIX Input 2 Source */ 
+       { 0x00000663, 0x0080 },   /* R1635  - MICMIX Input 2 Volume */ 
+       { 0x00000664, 0x0000 },   /* R1636  - MICMIX Input 3 Source */ 
+       { 0x00000665, 0x0080 },   /* R1637  - MICMIX Input 3 Volume */ 
+       { 0x00000666, 0x0000 },   /* R1638  - MICMIX Input 4 Source */ 
+       { 0x00000667, 0x0080 },   /* R1639  - MICMIX Input 4 Volume */ 
+       { 0x00000668, 0x0000 },   /* R1640  - NOISEMIX Input 1 Source */ 
+       { 0x00000669, 0x0080 },   /* R1641  - NOISEMIX Input 1 Volume */ 
+       { 0x0000066A, 0x0000 },   /* R1642  - NOISEMIX Input 2 Source */ 
+       { 0x0000066B, 0x0080 },   /* R1643  - NOISEMIX Input 2 Volume */ 
+       { 0x0000066C, 0x0000 },   /* R1644  - NOISEMIX Input 3 Source */ 
+       { 0x0000066D, 0x0080 },   /* R1645  - NOISEMIX Input 3 Volume */ 
+       { 0x0000066E, 0x0000 },   /* R1646  - NOISEMIX Input 4 Source */ 
+       { 0x0000066F, 0x0080 },   /* R1647  - NOISEMIX Input 4 Volume */ 
+       { 0x00000680, 0x0000 },   /* R1664  - OUT1LMIX Input 1 Source */ 
+       { 0x00000681, 0x0080 },   /* R1665  - OUT1LMIX Input 1 Volume */ 
+       { 0x00000682, 0x0000 },   /* R1666  - OUT1LMIX Input 2 Source */ 
+       { 0x00000683, 0x0080 },   /* R1667  - OUT1LMIX Input 2 Volume */ 
+       { 0x00000684, 0x0000 },   /* R1668  - OUT1LMIX Input 3 Source */ 
+       { 0x00000685, 0x0080 },   /* R1669  - OUT1LMIX Input 3 Volume */ 
+       { 0x00000686, 0x0000 },   /* R1670  - OUT1LMIX Input 4 Source */ 
+       { 0x00000687, 0x0080 },   /* R1671  - OUT1LMIX Input 4 Volume */ 
+       { 0x00000688, 0x0000 },   /* R1672  - OUT1RMIX Input 1 Source */ 
+       { 0x00000689, 0x0080 },   /* R1673  - OUT1RMIX Input 1 Volume */ 
+       { 0x0000068A, 0x0000 },   /* R1674  - OUT1RMIX Input 2 Source */ 
+       { 0x0000068B, 0x0080 },   /* R1675  - OUT1RMIX Input 2 Volume */ 
+       { 0x0000068C, 0x0000 },   /* R1676  - OUT1RMIX Input 3 Source */ 
+       { 0x0000068D, 0x0080 },   /* R1677  - OUT1RMIX Input 3 Volume */ 
+       { 0x0000068E, 0x0000 },   /* R1678  - OUT1RMIX Input 4 Source */ 
+       { 0x0000068F, 0x0080 },   /* R1679  - OUT1RMIX Input 4 Volume */ 
+       { 0x00000690, 0x0000 },   /* R1680  - OUT2LMIX Input 1 Source */ 
+       { 0x00000691, 0x0080 },   /* R1681  - OUT2LMIX Input 1 Volume */ 
+       { 0x00000692, 0x0000 },   /* R1682  - OUT2LMIX Input 2 Source */ 
+       { 0x00000693, 0x0080 },   /* R1683  - OUT2LMIX Input 2 Volume */ 
+       { 0x00000694, 0x0000 },   /* R1684  - OUT2LMIX Input 3 Source */ 
+       { 0x00000695, 0x0080 },   /* R1685  - OUT2LMIX Input 3 Volume */ 
+       { 0x00000696, 0x0000 },   /* R1686  - OUT2LMIX Input 4 Source */ 
+       { 0x00000697, 0x0080 },   /* R1687  - OUT2LMIX Input 4 Volume */ 
+       { 0x00000698, 0x0000 },   /* R1688  - OUT2RMIX Input 1 Source */ 
+       { 0x00000699, 0x0080 },   /* R1689  - OUT2RMIX Input 1 Volume */ 
+       { 0x0000069A, 0x0000 },   /* R1690  - OUT2RMIX Input 2 Source */ 
+       { 0x0000069B, 0x0080 },   /* R1691  - OUT2RMIX Input 2 Volume */ 
+       { 0x0000069C, 0x0000 },   /* R1692  - OUT2RMIX Input 3 Source */ 
+       { 0x0000069D, 0x0080 },   /* R1693  - OUT2RMIX Input 3 Volume */ 
+       { 0x0000069E, 0x0000 },   /* R1694  - OUT2RMIX Input 4 Source */ 
+       { 0x0000069F, 0x0080 },   /* R1695  - OUT2RMIX Input 4 Volume */ 
+       { 0x000006A0, 0x0000 },   /* R1696  - OUT3LMIX Input 1 Source */ 
+       { 0x000006A1, 0x0080 },   /* R1697  - OUT3LMIX Input 1 Volume */ 
+       { 0x000006A2, 0x0000 },   /* R1698  - OUT3LMIX Input 2 Source */ 
+       { 0x000006A3, 0x0080 },   /* R1699  - OUT3LMIX Input 2 Volume */ 
+       { 0x000006A4, 0x0000 },   /* R1700  - OUT3LMIX Input 3 Source */ 
+       { 0x000006A5, 0x0080 },   /* R1701  - OUT3LMIX Input 3 Volume */ 
+       { 0x000006A6, 0x0000 },   /* R1702  - OUT3LMIX Input 4 Source */ 
+       { 0x000006A7, 0x0080 },   /* R1703  - OUT3LMIX Input 4 Volume */ 
+       { 0x000006B0, 0x0000 },   /* R1712  - OUT4LMIX Input 1 Source */ 
+       { 0x000006B1, 0x0080 },   /* R1713  - OUT4LMIX Input 1 Volume */ 
+       { 0x000006B2, 0x0000 },   /* R1714  - OUT4LMIX Input 2 Source */ 
+       { 0x000006B3, 0x0080 },   /* R1715  - OUT4LMIX Input 2 Volume */ 
+       { 0x000006B4, 0x0000 },   /* R1716  - OUT4LMIX Input 3 Source */ 
+       { 0x000006B5, 0x0080 },   /* R1717  - OUT4LMIX Input 3 Volume */ 
+       { 0x000006B6, 0x0000 },   /* R1718  - OUT4LMIX Input 4 Source */ 
+       { 0x000006B7, 0x0080 },   /* R1719  - OUT4LMIX Input 4 Volume */ 
+       { 0x000006B8, 0x0000 },   /* R1720  - OUT4RMIX Input 1 Source */ 
+       { 0x000006B9, 0x0080 },   /* R1721  - OUT4RMIX Input 1 Volume */ 
+       { 0x000006BA, 0x0000 },   /* R1722  - OUT4RMIX Input 2 Source */ 
+       { 0x000006BB, 0x0080 },   /* R1723  - OUT4RMIX Input 2 Volume */ 
+       { 0x000006BC, 0x0000 },   /* R1724  - OUT4RMIX Input 3 Source */ 
+       { 0x000006BD, 0x0080 },   /* R1725  - OUT4RMIX Input 3 Volume */ 
+       { 0x000006BE, 0x0000 },   /* R1726  - OUT4RMIX Input 4 Source */ 
+       { 0x000006BF, 0x0080 },   /* R1727  - OUT4RMIX Input 4 Volume */ 
+       { 0x000006C0, 0x0000 },   /* R1728  - OUT5LMIX Input 1 Source */ 
+       { 0x000006C1, 0x0080 },   /* R1729  - OUT5LMIX Input 1 Volume */ 
+       { 0x000006C2, 0x0000 },   /* R1730  - OUT5LMIX Input 2 Source */ 
+       { 0x000006C3, 0x0080 },   /* R1731  - OUT5LMIX Input 2 Volume */ 
+       { 0x000006C4, 0x0000 },   /* R1732  - OUT5LMIX Input 3 Source */ 
+       { 0x000006C5, 0x0080 },   /* R1733  - OUT5LMIX Input 3 Volume */ 
+       { 0x000006C6, 0x0000 },   /* R1734  - OUT5LMIX Input 4 Source */ 
+       { 0x000006C7, 0x0080 },   /* R1735  - OUT5LMIX Input 4 Volume */ 
+       { 0x000006C8, 0x0000 },   /* R1736  - OUT5RMIX Input 1 Source */ 
+       { 0x000006C9, 0x0080 },   /* R1737  - OUT5RMIX Input 1 Volume */ 
+       { 0x000006CA, 0x0000 },   /* R1738  - OUT5RMIX Input 2 Source */ 
+       { 0x000006CB, 0x0080 },   /* R1739  - OUT5RMIX Input 2 Volume */ 
+       { 0x000006CC, 0x0000 },   /* R1740  - OUT5RMIX Input 3 Source */ 
+       { 0x000006CD, 0x0080 },   /* R1741  - OUT5RMIX Input 3 Volume */ 
+       { 0x000006CE, 0x0000 },   /* R1742  - OUT5RMIX Input 4 Source */ 
+       { 0x000006CF, 0x0080 },   /* R1743  - OUT5RMIX Input 4 Volume */ 
+       { 0x00000700, 0x0000 },   /* R1792  - AIF1TX1MIX Input 1 Source */ 
+       { 0x00000701, 0x0080 },   /* R1793  - AIF1TX1MIX Input 1 Volume */ 
+       { 0x00000702, 0x0000 },   /* R1794  - AIF1TX1MIX Input 2 Source */ 
+       { 0x00000703, 0x0080 },   /* R1795  - AIF1TX1MIX Input 2 Volume */ 
+       { 0x00000704, 0x0000 },   /* R1796  - AIF1TX1MIX Input 3 Source */ 
+       { 0x00000705, 0x0080 },   /* R1797  - AIF1TX1MIX Input 3 Volume */ 
+       { 0x00000706, 0x0000 },   /* R1798  - AIF1TX1MIX Input 4 Source */ 
+       { 0x00000707, 0x0080 },   /* R1799  - AIF1TX1MIX Input 4 Volume */ 
+       { 0x00000708, 0x0000 },   /* R1800  - AIF1TX2MIX Input 1 Source */ 
+       { 0x00000709, 0x0080 },   /* R1801  - AIF1TX2MIX Input 1 Volume */ 
+       { 0x0000070A, 0x0000 },   /* R1802  - AIF1TX2MIX Input 2 Source */ 
+       { 0x0000070B, 0x0080 },   /* R1803  - AIF1TX2MIX Input 2 Volume */ 
+       { 0x0000070C, 0x0000 },   /* R1804  - AIF1TX2MIX Input 3 Source */ 
+       { 0x0000070D, 0x0080 },   /* R1805  - AIF1TX2MIX Input 3 Volume */ 
+       { 0x0000070E, 0x0000 },   /* R1806  - AIF1TX2MIX Input 4 Source */ 
+       { 0x0000070F, 0x0080 },   /* R1807  - AIF1TX2MIX Input 4 Volume */ 
+       { 0x00000710, 0x0000 },   /* R1808  - AIF1TX3MIX Input 1 Source */ 
+       { 0x00000711, 0x0080 },   /* R1809  - AIF1TX3MIX Input 1 Volume */ 
+       { 0x00000712, 0x0000 },   /* R1810  - AIF1TX3MIX Input 2 Source */ 
+       { 0x00000713, 0x0080 },   /* R1811  - AIF1TX3MIX Input 2 Volume */ 
+       { 0x00000714, 0x0000 },   /* R1812  - AIF1TX3MIX Input 3 Source */ 
+       { 0x00000715, 0x0080 },   /* R1813  - AIF1TX3MIX Input 3 Volume */ 
+       { 0x00000716, 0x0000 },   /* R1814  - AIF1TX3MIX Input 4 Source */ 
+       { 0x00000717, 0x0080 },   /* R1815  - AIF1TX3MIX Input 4 Volume */ 
+       { 0x00000718, 0x0000 },   /* R1816  - AIF1TX4MIX Input 1 Source */ 
+       { 0x00000719, 0x0080 },   /* R1817  - AIF1TX4MIX Input 1 Volume */ 
+       { 0x0000071A, 0x0000 },   /* R1818  - AIF1TX4MIX Input 2 Source */ 
+       { 0x0000071B, 0x0080 },   /* R1819  - AIF1TX4MIX Input 2 Volume */ 
+       { 0x0000071C, 0x0000 },   /* R1820  - AIF1TX4MIX Input 3 Source */ 
+       { 0x0000071D, 0x0080 },   /* R1821  - AIF1TX4MIX Input 3 Volume */ 
+       { 0x0000071E, 0x0000 },   /* R1822  - AIF1TX4MIX Input 4 Source */ 
+       { 0x0000071F, 0x0080 },   /* R1823  - AIF1TX4MIX Input 4 Volume */ 
+       { 0x00000720, 0x0000 },   /* R1824  - AIF1TX5MIX Input 1 Source */ 
+       { 0x00000721, 0x0080 },   /* R1825  - AIF1TX5MIX Input 1 Volume */ 
+       { 0x00000722, 0x0000 },   /* R1826  - AIF1TX5MIX Input 2 Source */ 
+       { 0x00000723, 0x0080 },   /* R1827  - AIF1TX5MIX Input 2 Volume */ 
+       { 0x00000724, 0x0000 },   /* R1828  - AIF1TX5MIX Input 3 Source */ 
+       { 0x00000725, 0x0080 },   /* R1829  - AIF1TX5MIX Input 3 Volume */ 
+       { 0x00000726, 0x0000 },   /* R1830  - AIF1TX5MIX Input 4 Source */ 
+       { 0x00000727, 0x0080 },   /* R1831  - AIF1TX5MIX Input 4 Volume */ 
+       { 0x00000728, 0x0000 },   /* R1832  - AIF1TX6MIX Input 1 Source */ 
+       { 0x00000729, 0x0080 },   /* R1833  - AIF1TX6MIX Input 1 Volume */ 
+       { 0x0000072A, 0x0000 },   /* R1834  - AIF1TX6MIX Input 2 Source */ 
+       { 0x0000072B, 0x0080 },   /* R1835  - AIF1TX6MIX Input 2 Volume */ 
+       { 0x0000072C, 0x0000 },   /* R1836  - AIF1TX6MIX Input 3 Source */ 
+       { 0x0000072D, 0x0080 },   /* R1837  - AIF1TX6MIX Input 3 Volume */ 
+       { 0x0000072E, 0x0000 },   /* R1838  - AIF1TX6MIX Input 4 Source */ 
+       { 0x0000072F, 0x0080 },   /* R1839  - AIF1TX6MIX Input 4 Volume */ 
+       { 0x00000730, 0x0000 },   /* R1840  - AIF1TX7MIX Input 1 Source */ 
+       { 0x00000731, 0x0080 },   /* R1841  - AIF1TX7MIX Input 1 Volume */ 
+       { 0x00000732, 0x0000 },   /* R1842  - AIF1TX7MIX Input 2 Source */ 
+       { 0x00000733, 0x0080 },   /* R1843  - AIF1TX7MIX Input 2 Volume */ 
+       { 0x00000734, 0x0000 },   /* R1844  - AIF1TX7MIX Input 3 Source */ 
+       { 0x00000735, 0x0080 },   /* R1845  - AIF1TX7MIX Input 3 Volume */ 
+       { 0x00000736, 0x0000 },   /* R1846  - AIF1TX7MIX Input 4 Source */ 
+       { 0x00000737, 0x0080 },   /* R1847  - AIF1TX7MIX Input 4 Volume */ 
+       { 0x00000738, 0x0000 },   /* R1848  - AIF1TX8MIX Input 1 Source */ 
+       { 0x00000739, 0x0080 },   /* R1849  - AIF1TX8MIX Input 1 Volume */ 
+       { 0x0000073A, 0x0000 },   /* R1850  - AIF1TX8MIX Input 2 Source */ 
+       { 0x0000073B, 0x0080 },   /* R1851  - AIF1TX8MIX Input 2 Volume */ 
+       { 0x0000073C, 0x0000 },   /* R1852  - AIF1TX8MIX Input 3 Source */ 
+       { 0x0000073D, 0x0080 },   /* R1853  - AIF1TX8MIX Input 3 Volume */ 
+       { 0x0000073E, 0x0000 },   /* R1854  - AIF1TX8MIX Input 4 Source */ 
+       { 0x0000073F, 0x0080 },   /* R1855  - AIF1TX8MIX Input 4 Volume */ 
+       { 0x00000740, 0x0000 },   /* R1856  - AIF2TX1MIX Input 1 Source */ 
+       { 0x00000741, 0x0080 },   /* R1857  - AIF2TX1MIX Input 1 Volume */ 
+       { 0x00000742, 0x0000 },   /* R1858  - AIF2TX1MIX Input 2 Source */ 
+       { 0x00000743, 0x0080 },   /* R1859  - AIF2TX1MIX Input 2 Volume */ 
+       { 0x00000744, 0x0000 },   /* R1860  - AIF2TX1MIX Input 3 Source */ 
+       { 0x00000745, 0x0080 },   /* R1861  - AIF2TX1MIX Input 3 Volume */ 
+       { 0x00000746, 0x0000 },   /* R1862  - AIF2TX1MIX Input 4 Source */ 
+       { 0x00000747, 0x0080 },   /* R1863  - AIF2TX1MIX Input 4 Volume */ 
+       { 0x00000748, 0x0000 },   /* R1864  - AIF2TX2MIX Input 1 Source */ 
+       { 0x00000749, 0x0080 },   /* R1865  - AIF2TX2MIX Input 1 Volume */ 
+       { 0x0000074A, 0x0000 },   /* R1866  - AIF2TX2MIX Input 2 Source */ 
+       { 0x0000074B, 0x0080 },   /* R1867  - AIF2TX2MIX Input 2 Volume */ 
+       { 0x0000074C, 0x0000 },   /* R1868  - AIF2TX2MIX Input 3 Source */ 
+       { 0x0000074D, 0x0080 },   /* R1869  - AIF2TX2MIX Input 3 Volume */ 
+       { 0x0000074E, 0x0000 },   /* R1870  - AIF2TX2MIX Input 4 Source */ 
+       { 0x0000074F, 0x0080 },   /* R1871  - AIF2TX2MIX Input 4 Volume */ 
+       { 0x00000780, 0x0000 },   /* R1920  - AIF3TX1MIX Input 1 Source */ 
+       { 0x00000781, 0x0080 },   /* R1921  - AIF3TX1MIX Input 1 Volume */ 
+       { 0x00000782, 0x0000 },   /* R1922  - AIF3TX1MIX Input 2 Source */ 
+       { 0x00000783, 0x0080 },   /* R1923  - AIF3TX1MIX Input 2 Volume */ 
+       { 0x00000784, 0x0000 },   /* R1924  - AIF3TX1MIX Input 3 Source */ 
+       { 0x00000785, 0x0080 },   /* R1925  - AIF3TX1MIX Input 3 Volume */ 
+       { 0x00000786, 0x0000 },   /* R1926  - AIF3TX1MIX Input 4 Source */ 
+       { 0x00000787, 0x0080 },   /* R1927  - AIF3TX1MIX Input 4 Volume */ 
+       { 0x00000788, 0x0000 },   /* R1928  - AIF3TX2MIX Input 1 Source */ 
+       { 0x00000789, 0x0080 },   /* R1929  - AIF3TX2MIX Input 1 Volume */ 
+       { 0x0000078A, 0x0000 },   /* R1930  - AIF3TX2MIX Input 2 Source */ 
+       { 0x0000078B, 0x0080 },   /* R1931  - AIF3TX2MIX Input 2 Volume */ 
+       { 0x0000078C, 0x0000 },   /* R1932  - AIF3TX2MIX Input 3 Source */ 
+       { 0x0000078D, 0x0080 },   /* R1933  - AIF3TX2MIX Input 3 Volume */ 
+       { 0x0000078E, 0x0000 },   /* R1934  - AIF3TX2MIX Input 4 Source */ 
+       { 0x0000078F, 0x0080 },   /* R1935  - AIF3TX2MIX Input 4 Volume */ 
+       { 0x000007C0, 0x0000 },   /* R1984  - SLIMTX1MIX Input 1 Source */ 
+       { 0x000007C1, 0x0080 },   /* R1985  - SLIMTX1MIX Input 1 Volume */ 
+       { 0x000007C2, 0x0000 },   /* R1986  - SLIMTX1MIX Input 2 Source */ 
+       { 0x000007C3, 0x0080 },   /* R1987  - SLIMTX1MIX Input 2 Volume */ 
+       { 0x000007C4, 0x0000 },   /* R1988  - SLIMTX1MIX Input 3 Source */ 
+       { 0x000007C5, 0x0080 },   /* R1989  - SLIMTX1MIX Input 3 Volume */ 
+       { 0x000007C6, 0x0000 },   /* R1990  - SLIMTX1MIX Input 4 Source */ 
+       { 0x000007C7, 0x0080 },   /* R1991  - SLIMTX1MIX Input 4 Volume */ 
+       { 0x000007C8, 0x0000 },   /* R1992  - SLIMTX2MIX Input 1 Source */ 
+       { 0x000007C9, 0x0080 },   /* R1993  - SLIMTX2MIX Input 1 Volume */ 
+       { 0x000007CA, 0x0000 },   /* R1994  - SLIMTX2MIX Input 2 Source */ 
+       { 0x000007CB, 0x0080 },   /* R1995  - SLIMTX2MIX Input 2 Volume */ 
+       { 0x000007CC, 0x0000 },   /* R1996  - SLIMTX2MIX Input 3 Source */ 
+       { 0x000007CD, 0x0080 },   /* R1997  - SLIMTX2MIX Input 3 Volume */ 
+       { 0x000007CE, 0x0000 },   /* R1998  - SLIMTX2MIX Input 4 Source */ 
+       { 0x000007CF, 0x0080 },   /* R1999  - SLIMTX2MIX Input 4 Volume */ 
+       { 0x000007D0, 0x0000 },   /* R2000  - SLIMTX3MIX Input 1 Source */ 
+       { 0x000007D1, 0x0080 },   /* R2001  - SLIMTX3MIX Input 1 Volume */ 
+       { 0x000007D2, 0x0000 },   /* R2002  - SLIMTX3MIX Input 2 Source */ 
+       { 0x000007D3, 0x0080 },   /* R2003  - SLIMTX3MIX Input 2 Volume */ 
+       { 0x000007D4, 0x0000 },   /* R2004  - SLIMTX3MIX Input 3 Source */ 
+       { 0x000007D5, 0x0080 },   /* R2005  - SLIMTX3MIX Input 3 Volume */ 
+       { 0x000007D6, 0x0000 },   /* R2006  - SLIMTX3MIX Input 4 Source */ 
+       { 0x000007D7, 0x0080 },   /* R2007  - SLIMTX3MIX Input 4 Volume */ 
+       { 0x000007D8, 0x0000 },   /* R2008  - SLIMTX4MIX Input 1 Source */ 
+       { 0x000007D9, 0x0080 },   /* R2009  - SLIMTX4MIX Input 1 Volume */ 
+       { 0x000007DA, 0x0000 },   /* R2010  - SLIMTX4MIX Input 2 Source */ 
+       { 0x000007DB, 0x0080 },   /* R2011  - SLIMTX4MIX Input 2 Volume */ 
+       { 0x000007DC, 0x0000 },   /* R2012  - SLIMTX4MIX Input 3 Source */ 
+       { 0x000007DD, 0x0080 },   /* R2013  - SLIMTX4MIX Input 3 Volume */ 
+       { 0x000007DE, 0x0000 },   /* R2014  - SLIMTX4MIX Input 4 Source */ 
+       { 0x000007DF, 0x0080 },   /* R2015  - SLIMTX4MIX Input 4 Volume */ 
+       { 0x000007E0, 0x0000 },   /* R2016  - SLIMTX5MIX Input 1 Source */ 
+       { 0x000007E1, 0x0080 },   /* R2017  - SLIMTX5MIX Input 1 Volume */ 
+       { 0x000007E2, 0x0000 },   /* R2018  - SLIMTX5MIX Input 2 Source */ 
+       { 0x000007E3, 0x0080 },   /* R2019  - SLIMTX5MIX Input 2 Volume */ 
+       { 0x000007E4, 0x0000 },   /* R2020  - SLIMTX5MIX Input 3 Source */ 
+       { 0x000007E5, 0x0080 },   /* R2021  - SLIMTX5MIX Input 3 Volume */ 
+       { 0x000007E6, 0x0000 },   /* R2022  - SLIMTX5MIX Input 4 Source */ 
+       { 0x000007E7, 0x0080 },   /* R2023  - SLIMTX5MIX Input 4 Volume */ 
+       { 0x000007E8, 0x0000 },   /* R2024  - SLIMTX6MIX Input 1 Source */ 
+       { 0x000007E9, 0x0080 },   /* R2025  - SLIMTX6MIX Input 1 Volume */ 
+       { 0x000007EA, 0x0000 },   /* R2026  - SLIMTX6MIX Input 2 Source */ 
+       { 0x000007EB, 0x0080 },   /* R2027  - SLIMTX6MIX Input 2 Volume */ 
+       { 0x000007EC, 0x0000 },   /* R2028  - SLIMTX6MIX Input 3 Source */ 
+       { 0x000007ED, 0x0080 },   /* R2029  - SLIMTX6MIX Input 3 Volume */ 
+       { 0x000007EE, 0x0000 },   /* R2030  - SLIMTX6MIX Input 4 Source */ 
+       { 0x000007EF, 0x0080 },   /* R2031  - SLIMTX6MIX Input 4 Volume */ 
+       { 0x000007F0, 0x0000 },   /* R2032  - SLIMTX7MIX Input 1 Source */ 
+       { 0x000007F1, 0x0080 },   /* R2033  - SLIMTX7MIX Input 1 Volume */ 
+       { 0x000007F2, 0x0000 },   /* R2034  - SLIMTX7MIX Input 2 Source */ 
+       { 0x000007F3, 0x0080 },   /* R2035  - SLIMTX7MIX Input 2 Volume */ 
+       { 0x000007F4, 0x0000 },   /* R2036  - SLIMTX7MIX Input 3 Source */ 
+       { 0x000007F5, 0x0080 },   /* R2037  - SLIMTX7MIX Input 3 Volume */ 
+       { 0x000007F6, 0x0000 },   /* R2038  - SLIMTX7MIX Input 4 Source */ 
+       { 0x000007F7, 0x0080 },   /* R2039  - SLIMTX7MIX Input 4 Volume */ 
+       { 0x000007F8, 0x0000 },   /* R2040  - SLIMTX8MIX Input 1 Source */ 
+       { 0x000007F9, 0x0080 },   /* R2041  - SLIMTX8MIX Input 1 Volume */ 
+       { 0x000007FA, 0x0000 },   /* R2042  - SLIMTX8MIX Input 2 Source */ 
+       { 0x000007FB, 0x0080 },   /* R2043  - SLIMTX8MIX Input 2 Volume */ 
+       { 0x000007FC, 0x0000 },   /* R2044  - SLIMTX8MIX Input 3 Source */ 
+       { 0x000007FD, 0x0080 },   /* R2045  - SLIMTX8MIX Input 3 Volume */ 
+       { 0x000007FE, 0x0000 },   /* R2046  - SLIMTX8MIX Input 4 Source */ 
+       { 0x000007FF, 0x0080 },   /* R2047  - SLIMTX8MIX Input 4 Volume */ 
+       { 0x00000880, 0x0000 },   /* R2176  - EQ1MIX Input 1 Source */ 
+       { 0x00000881, 0x0080 },   /* R2177  - EQ1MIX Input 1 Volume */ 
+       { 0x00000882, 0x0000 },   /* R2178  - EQ1MIX Input 2 Source */ 
+       { 0x00000883, 0x0080 },   /* R2179  - EQ1MIX Input 2 Volume */ 
+       { 0x00000884, 0x0000 },   /* R2180  - EQ1MIX Input 3 Source */ 
+       { 0x00000885, 0x0080 },   /* R2181  - EQ1MIX Input 3 Volume */ 
+       { 0x00000886, 0x0000 },   /* R2182  - EQ1MIX Input 4 Source */ 
+       { 0x00000887, 0x0080 },   /* R2183  - EQ1MIX Input 4 Volume */ 
+       { 0x00000888, 0x0000 },   /* R2184  - EQ2MIX Input 1 Source */ 
+       { 0x00000889, 0x0080 },   /* R2185  - EQ2MIX Input 1 Volume */ 
+       { 0x0000088A, 0x0000 },   /* R2186  - EQ2MIX Input 2 Source */ 
+       { 0x0000088B, 0x0080 },   /* R2187  - EQ2MIX Input 2 Volume */ 
+       { 0x0000088C, 0x0000 },   /* R2188  - EQ2MIX Input 3 Source */ 
+       { 0x0000088D, 0x0080 },   /* R2189  - EQ2MIX Input 3 Volume */ 
+       { 0x0000088E, 0x0000 },   /* R2190  - EQ2MIX Input 4 Source */ 
+       { 0x0000088F, 0x0080 },   /* R2191  - EQ2MIX Input 4 Volume */ 
+       { 0x00000890, 0x0000 },   /* R2192  - EQ3MIX Input 1 Source */ 
+       { 0x00000891, 0x0080 },   /* R2193  - EQ3MIX Input 1 Volume */ 
+       { 0x00000892, 0x0000 },   /* R2194  - EQ3MIX Input 2 Source */ 
+       { 0x00000893, 0x0080 },   /* R2195  - EQ3MIX Input 2 Volume */ 
+       { 0x00000894, 0x0000 },   /* R2196  - EQ3MIX Input 3 Source */ 
+       { 0x00000895, 0x0080 },   /* R2197  - EQ3MIX Input 3 Volume */ 
+       { 0x00000896, 0x0000 },   /* R2198  - EQ3MIX Input 4 Source */ 
+       { 0x00000897, 0x0080 },   /* R2199  - EQ3MIX Input 4 Volume */ 
+       { 0x00000898, 0x0000 },   /* R2200  - EQ4MIX Input 1 Source */ 
+       { 0x00000899, 0x0080 },   /* R2201  - EQ4MIX Input 1 Volume */ 
+       { 0x0000089A, 0x0000 },   /* R2202  - EQ4MIX Input 2 Source */ 
+       { 0x0000089B, 0x0080 },   /* R2203  - EQ4MIX Input 2 Volume */ 
+       { 0x0000089C, 0x0000 },   /* R2204  - EQ4MIX Input 3 Source */ 
+       { 0x0000089D, 0x0080 },   /* R2205  - EQ4MIX Input 3 Volume */ 
+       { 0x0000089E, 0x0000 },   /* R2206  - EQ4MIX Input 4 Source */ 
+       { 0x0000089F, 0x0080 },   /* R2207  - EQ4MIX Input 4 Volume */ 
+       { 0x000008C0, 0x0000 },   /* R2240  - DRC1LMIX Input 1 Source */ 
+       { 0x000008C1, 0x0080 },   /* R2241  - DRC1LMIX Input 1 Volume */ 
+       { 0x000008C2, 0x0000 },   /* R2242  - DRC1LMIX Input 2 Source */ 
+       { 0x000008C3, 0x0080 },   /* R2243  - DRC1LMIX Input 2 Volume */ 
+       { 0x000008C4, 0x0000 },   /* R2244  - DRC1LMIX Input 3 Source */ 
+       { 0x000008C5, 0x0080 },   /* R2245  - DRC1LMIX Input 3 Volume */ 
+       { 0x000008C6, 0x0000 },   /* R2246  - DRC1LMIX Input 4 Source */ 
+       { 0x000008C7, 0x0080 },   /* R2247  - DRC1LMIX Input 4 Volume */ 
+       { 0x000008C8, 0x0000 },   /* R2248  - DRC1RMIX Input 1 Source */ 
+       { 0x000008C9, 0x0080 },   /* R2249  - DRC1RMIX Input 1 Volume */ 
+       { 0x000008CA, 0x0000 },   /* R2250  - DRC1RMIX Input 2 Source */ 
+       { 0x000008CB, 0x0080 },   /* R2251  - DRC1RMIX Input 2 Volume */ 
+       { 0x000008CC, 0x0000 },   /* R2252  - DRC1RMIX Input 3 Source */ 
+       { 0x000008CD, 0x0080 },   /* R2253  - DRC1RMIX Input 3 Volume */ 
+       { 0x000008CE, 0x0000 },   /* R2254  - DRC1RMIX Input 4 Source */ 
+       { 0x000008CF, 0x0080 },   /* R2255  - DRC1RMIX Input 4 Volume */ 
+       { 0x000008D0, 0x0000 },   /* R2256  - DRC2LMIX Input 1 Source */ 
+       { 0x000008D1, 0x0080 },   /* R2257  - DRC2LMIX Input 1 Volume */ 
+       { 0x000008D2, 0x0000 },   /* R2258  - DRC2LMIX Input 2 Source */ 
+       { 0x000008D3, 0x0080 },   /* R2259  - DRC2LMIX Input 2 Volume */ 
+       { 0x000008D4, 0x0000 },   /* R2260  - DRC2LMIX Input 3 Source */ 
+       { 0x000008D5, 0x0080 },   /* R2261  - DRC2LMIX Input 3 Volume */ 
+       { 0x000008D6, 0x0000 },   /* R2262  - DRC2LMIX Input 4 Source */ 
+       { 0x000008D7, 0x0080 },   /* R2263  - DRC2LMIX Input 4 Volume */ 
+       { 0x000008D8, 0x0000 },   /* R2264  - DRC2RMIX Input 1 Source */ 
+       { 0x000008D9, 0x0080 },   /* R2265  - DRC2RMIX Input 1 Volume */ 
+       { 0x000008DA, 0x0000 },   /* R2266  - DRC2RMIX Input 2 Source */ 
+       { 0x000008DB, 0x0080 },   /* R2267  - DRC2RMIX Input 2 Volume */ 
+       { 0x000008DC, 0x0000 },   /* R2268  - DRC2RMIX Input 3 Source */ 
+       { 0x000008DD, 0x0080 },   /* R2269  - DRC2RMIX Input 3 Volume */ 
+       { 0x000008DE, 0x0000 },   /* R2270  - DRC2RMIX Input 4 Source */ 
+       { 0x000008DF, 0x0080 },   /* R2271  - DRC2RMIX Input 4 Volume */ 
+       { 0x00000900, 0x0000 },   /* R2304  - HPLP1MIX Input 1 Source */ 
+       { 0x00000901, 0x0080 },   /* R2305  - HPLP1MIX Input 1 Volume */ 
+       { 0x00000902, 0x0000 },   /* R2306  - HPLP1MIX Input 2 Source */ 
+       { 0x00000903, 0x0080 },   /* R2307  - HPLP1MIX Input 2 Volume */ 
+       { 0x00000904, 0x0000 },   /* R2308  - HPLP1MIX Input 3 Source */ 
+       { 0x00000905, 0x0080 },   /* R2309  - HPLP1MIX Input 3 Volume */ 
+       { 0x00000906, 0x0000 },   /* R2310  - HPLP1MIX Input 4 Source */ 
+       { 0x00000907, 0x0080 },   /* R2311  - HPLP1MIX Input 4 Volume */ 
+       { 0x00000908, 0x0000 },   /* R2312  - HPLP2MIX Input 1 Source */ 
+       { 0x00000909, 0x0080 },   /* R2313  - HPLP2MIX Input 1 Volume */ 
+       { 0x0000090A, 0x0000 },   /* R2314  - HPLP2MIX Input 2 Source */ 
+       { 0x0000090B, 0x0080 },   /* R2315  - HPLP2MIX Input 2 Volume */ 
+       { 0x0000090C, 0x0000 },   /* R2316  - HPLP2MIX Input 3 Source */ 
+       { 0x0000090D, 0x0080 },   /* R2317  - HPLP2MIX Input 3 Volume */ 
+       { 0x0000090E, 0x0000 },   /* R2318  - HPLP2MIX Input 4 Source */ 
+       { 0x0000090F, 0x0080 },   /* R2319  - HPLP2MIX Input 4 Volume */ 
+       { 0x00000910, 0x0000 },   /* R2320  - HPLP3MIX Input 1 Source */ 
+       { 0x00000911, 0x0080 },   /* R2321  - HPLP3MIX Input 1 Volume */ 
+       { 0x00000912, 0x0000 },   /* R2322  - HPLP3MIX Input 2 Source */ 
+       { 0x00000913, 0x0080 },   /* R2323  - HPLP3MIX Input 2 Volume */ 
+       { 0x00000914, 0x0000 },   /* R2324  - HPLP3MIX Input 3 Source */ 
+       { 0x00000915, 0x0080 },   /* R2325  - HPLP3MIX Input 3 Volume */ 
+       { 0x00000916, 0x0000 },   /* R2326  - HPLP3MIX Input 4 Source */ 
+       { 0x00000917, 0x0080 },   /* R2327  - HPLP3MIX Input 4 Volume */ 
+       { 0x00000918, 0x0000 },   /* R2328  - HPLP4MIX Input 1 Source */ 
+       { 0x00000919, 0x0080 },   /* R2329  - HPLP4MIX Input 1 Volume */ 
+       { 0x0000091A, 0x0000 },   /* R2330  - HPLP4MIX Input 2 Source */ 
+       { 0x0000091B, 0x0080 },   /* R2331  - HPLP4MIX Input 2 Volume */ 
+       { 0x0000091C, 0x0000 },   /* R2332  - HPLP4MIX Input 3 Source */ 
+       { 0x0000091D, 0x0080 },   /* R2333  - HPLP4MIX Input 3 Volume */ 
+       { 0x0000091E, 0x0000 },   /* R2334  - HPLP4MIX Input 4 Source */ 
+       { 0x0000091F, 0x0080 },   /* R2335  - HPLP4MIX Input 4 Volume */ 
+       { 0x00000940, 0x0000 },   /* R2368  - DSP1LMIX Input 1 Source */ 
+       { 0x00000941, 0x0080 },   /* R2369  - DSP1LMIX Input 1 Volume */ 
+       { 0x00000942, 0x0000 },   /* R2370  - DSP1LMIX Input 2 Source */ 
+       { 0x00000943, 0x0080 },   /* R2371  - DSP1LMIX Input 2 Volume */ 
+       { 0x00000944, 0x0000 },   /* R2372  - DSP1LMIX Input 3 Source */ 
+       { 0x00000945, 0x0080 },   /* R2373  - DSP1LMIX Input 3 Volume */ 
+       { 0x00000946, 0x0000 },   /* R2374  - DSP1LMIX Input 4 Source */ 
+       { 0x00000947, 0x0080 },   /* R2375  - DSP1LMIX Input 4 Volume */ 
+       { 0x00000948, 0x0000 },   /* R2376  - DSP1RMIX Input 1 Source */ 
+       { 0x00000949, 0x0080 },   /* R2377  - DSP1RMIX Input 1 Volume */ 
+       { 0x0000094A, 0x0000 },   /* R2378  - DSP1RMIX Input 2 Source */ 
+       { 0x0000094B, 0x0080 },   /* R2379  - DSP1RMIX Input 2 Volume */ 
+       { 0x0000094C, 0x0000 },   /* R2380  - DSP1RMIX Input 3 Source */ 
+       { 0x0000094D, 0x0080 },   /* R2381  - DSP1RMIX Input 3 Volume */ 
+       { 0x0000094E, 0x0000 },   /* R2382  - DSP1RMIX Input 4 Source */ 
+       { 0x0000094F, 0x0080 },   /* R2383  - DSP1RMIX Input 4 Volume */ 
+       { 0x00000950, 0x0000 },   /* R2384  - DSP1AUX1MIX Input 1 Source */ 
+       { 0x00000958, 0x0000 },   /* R2392  - DSP1AUX2MIX Input 1 Source */ 
+       { 0x00000960, 0x0000 },   /* R2400  - DSP1AUX3MIX Input 1 Source */ 
+       { 0x00000968, 0x0000 },   /* R2408  - DSP1AUX4MIX Input 1 Source */ 
+       { 0x00000970, 0x0000 },   /* R2416  - DSP1AUX5MIX Input 1 Source */ 
+       { 0x00000978, 0x0000 },   /* R2424  - DSP1AUX6MIX Input 1 Source */ 
+       { 0x00000A80, 0x0000 },   /* R2688  - ASRC1LMIX Input 1 Source */ 
+       { 0x00000A88, 0x0000 },   /* R2696  - ASRC1RMIX Input 1 Source */ 
+       { 0x00000A90, 0x0000 },   /* R2704  - ASRC2LMIX Input 1 Source */ 
+       { 0x00000A98, 0x0000 },   /* R2712  - ASRC2RMIX Input 1 Source */ 
+       { 0x00000B00, 0x0000 },   /* R2816  - ISRC1DEC1MIX Input 1 Source */ 
+       { 0x00000B08, 0x0000 },   /* R2824  - ISRC1DEC2MIX Input 1 Source */ 
+       { 0x00000B20, 0x0000 },   /* R2848  - ISRC1INT1MIX Input 1 Source */ 
+       { 0x00000B28, 0x0000 },   /* R2856  - ISRC1INT2MIX Input 1 Source */ 
+       { 0x00000B40, 0x0000 },   /* R2880  - ISRC2DEC1MIX Input 1 Source */ 
+       { 0x00000B48, 0x0000 },   /* R2888  - ISRC2DEC2MIX Input 1 Source */ 
+       { 0x00000B60, 0x0000 },   /* R2912  - ISRC2INT1MIX Input 1 Source */ 
+       { 0x00000B68, 0x0000 },   /* R2920  - ISRC2INT2MIX Input 1 Source */ 
+       { 0x00000C00, 0xA101 },   /* R3072  - GPIO1 CTRL */ 
+       { 0x00000C01, 0xA101 },   /* R3073  - GPIO2 CTRL */ 
+       { 0x00000C02, 0xA101 },   /* R3074  - GPIO3 CTRL */ 
+       { 0x00000C03, 0xA101 },   /* R3075  - GPIO4 CTRL */ 
+       { 0x00000C04, 0xA101 },   /* R3076  - GPIO5 CTRL */ 
+       { 0x00000C0F, 0x0400 },   /* R3087  - IRQ CTRL 1 */ 
+       { 0x00000C10, 0x1000 },   /* R3088  - GPIO Debounce Config */ 
+       { 0x00000C20, 0x8002 },   /* R3104  - Misc Pad Ctrl 1 */ 
+       { 0x00000C21, 0x8001 },   /* R3105  - Misc Pad Ctrl 2 */ 
+       { 0x00000C22, 0x0000 },   /* R3106  - Misc Pad Ctrl 3 */ 
+       { 0x00000C23, 0x0000 },   /* R3107  - Misc Pad Ctrl 4 */ 
+       { 0x00000C24, 0x0000 },   /* R3108  - Misc Pad Ctrl 5 */ 
+       { 0x00000C25, 0x0000 },   /* R3109  - Misc Pad Ctrl 6 */ 
+       { 0x00000D08, 0xFFFF },   /* R3336  - Interrupt Status 1 Mask */ 
+       { 0x00000D09, 0xFFFF },   /* R3337  - Interrupt Status 2 Mask */ 
+       { 0x00000D0A, 0xFFFF },   /* R3338  - Interrupt Status 3 Mask */ 
+       { 0x00000D0B, 0xFFFF },   /* R3339  - Interrupt Status 4 Mask */ 
+       { 0x00000D0C, 0xFEFF },   /* R3340  - Interrupt Status 5 Mask */ 
+       { 0x00000D0F, 0x0000 },   /* R3343  - Interrupt Control */ 
+       { 0x00000D18, 0xFFFF },   /* R3352  - IRQ2 Status 1 Mask */ 
+       { 0x00000D19, 0xFFFF },   /* R3353  - IRQ2 Status 2 Mask */ 
+       { 0x00000D1A, 0xFFFF },   /* R3354  - IRQ2 Status 3 Mask */ 
+       { 0x00000D1B, 0xFFFF },   /* R3355  - IRQ2 Status 4 Mask */ 
+       { 0x00000D1C, 0xFFFF },   /* R3356  - IRQ2 Status 5 Mask */ 
+       { 0x00000D1F, 0x0000 },   /* R3359  - IRQ2 Control */ 
+       { 0x00000D41, 0x0000 },   /* R3393  - ADSP2 IRQ0 */ 
+       { 0x00000D53, 0xFFFF },   /* R3411  - AOD IRQ Mask IRQ1 */ 
+       { 0x00000D54, 0xFFFF },   /* R3412  - AOD IRQ Mask IRQ2 */ 
+       { 0x00000D56, 0x0000 },   /* R3414  - Jack detect debounce */ 
+       { 0x00000E00, 0x0000 },   /* R3584  - FX_Ctrl1 */ 
+       { 0x00000E01, 0x0000 },   /* R3585  - FX_Ctrl2 */ 
+       { 0x00000E10, 0x6318 },   /* R3600  - EQ1_1 */ 
+       { 0x00000E11, 0x6300 },   /* R3601  - EQ1_2 */ 
+       { 0x00000E12, 0x0FC8 },   /* R3602  - EQ1_3 */ 
+       { 0x00000E13, 0x03FE },   /* R3603  - EQ1_4 */ 
+       { 0x00000E14, 0x00E0 },   /* R3604  - EQ1_5 */ 
+       { 0x00000E15, 0x1EC4 },   /* R3605  - EQ1_6 */ 
+       { 0x00000E16, 0xF136 },   /* R3606  - EQ1_7 */ 
+       { 0x00000E17, 0x0409 },   /* R3607  - EQ1_8 */ 
+       { 0x00000E18, 0x04CC },   /* R3608  - EQ1_9 */ 
+       { 0x00000E19, 0x1C9B },   /* R3609  - EQ1_10 */ 
+       { 0x00000E1A, 0xF337 },   /* R3610  - EQ1_11 */ 
+       { 0x00000E1B, 0x040B },   /* R3611  - EQ1_12 */ 
+       { 0x00000E1C, 0x0CBB },   /* R3612  - EQ1_13 */ 
+       { 0x00000E1D, 0x16F8 },   /* R3613  - EQ1_14 */ 
+       { 0x00000E1E, 0xF7D9 },   /* R3614  - EQ1_15 */ 
+       { 0x00000E1F, 0x040A },   /* R3615  - EQ1_16 */ 
+       { 0x00000E20, 0x1F14 },   /* R3616  - EQ1_17 */ 
+       { 0x00000E21, 0x058C },   /* R3617  - EQ1_18 */ 
+       { 0x00000E22, 0x0563 },   /* R3618  - EQ1_19 */ 
+       { 0x00000E23, 0x4000 },   /* R3619  - EQ1_20 */ 
+       { 0x00000E24, 0x0B75 },   /* R3620  - EQ1_21 */ 
+       { 0x00000E26, 0x6318 },   /* R3622  - EQ2_1 */ 
+       { 0x00000E27, 0x6300 },   /* R3623  - EQ2_2 */ 
+       { 0x00000E28, 0x0FC8 },   /* R3624  - EQ2_3 */ 
+       { 0x00000E29, 0x03FE },   /* R3625  - EQ2_4 */ 
+       { 0x00000E2A, 0x00E0 },   /* R3626  - EQ2_5 */ 
+       { 0x00000E2B, 0x1EC4 },   /* R3627  - EQ2_6 */ 
+       { 0x00000E2C, 0xF136 },   /* R3628  - EQ2_7 */ 
+       { 0x00000E2D, 0x0409 },   /* R3629  - EQ2_8 */ 
+       { 0x00000E2E, 0x04CC },   /* R3630  - EQ2_9 */ 
+       { 0x00000E2F, 0x1C9B },   /* R3631  - EQ2_10 */ 
+       { 0x00000E30, 0xF337 },   /* R3632  - EQ2_11 */ 
+       { 0x00000E31, 0x040B },   /* R3633  - EQ2_12 */ 
+       { 0x00000E32, 0x0CBB },   /* R3634  - EQ2_13 */ 
+       { 0x00000E33, 0x16F8 },   /* R3635  - EQ2_14 */ 
+       { 0x00000E34, 0xF7D9 },   /* R3636  - EQ2_15 */ 
+       { 0x00000E35, 0x040A },   /* R3637  - EQ2_16 */ 
+       { 0x00000E36, 0x1F14 },   /* R3638  - EQ2_17 */ 
+       { 0x00000E37, 0x058C },   /* R3639  - EQ2_18 */ 
+       { 0x00000E38, 0x0563 },   /* R3640  - EQ2_19 */ 
+       { 0x00000E39, 0x4000 },   /* R3641  - EQ2_20 */ 
+       { 0x00000E3A, 0x0B75 },   /* R3642  - EQ2_21 */ 
+       { 0x00000E3C, 0x6318 },   /* R3644  - EQ3_1 */ 
+       { 0x00000E3D, 0x6300 },   /* R3645  - EQ3_2 */ 
+       { 0x00000E3E, 0x0FC8 },   /* R3646  - EQ3_3 */ 
+       { 0x00000E3F, 0x03FE },   /* R3647  - EQ3_4 */ 
+       { 0x00000E40, 0x00E0 },   /* R3648  - EQ3_5 */ 
+       { 0x00000E41, 0x1EC4 },   /* R3649  - EQ3_6 */ 
+       { 0x00000E42, 0xF136 },   /* R3650  - EQ3_7 */ 
+       { 0x00000E43, 0x0409 },   /* R3651  - EQ3_8 */ 
+       { 0x00000E44, 0x04CC },   /* R3652  - EQ3_9 */ 
+       { 0x00000E45, 0x1C9B },   /* R3653  - EQ3_10 */ 
+       { 0x00000E46, 0xF337 },   /* R3654  - EQ3_11 */ 
+       { 0x00000E47, 0x040B },   /* R3655  - EQ3_12 */ 
+       { 0x00000E48, 0x0CBB },   /* R3656  - EQ3_13 */ 
+       { 0x00000E49, 0x16F8 },   /* R3657  - EQ3_14 */ 
+       { 0x00000E4A, 0xF7D9 },   /* R3658  - EQ3_15 */ 
+       { 0x00000E4B, 0x040A },   /* R3659  - EQ3_16 */ 
+       { 0x00000E4C, 0x1F14 },   /* R3660  - EQ3_17 */ 
+       { 0x00000E4D, 0x058C },   /* R3661  - EQ3_18 */ 
+       { 0x00000E4E, 0x0563 },   /* R3662  - EQ3_19 */ 
+       { 0x00000E4F, 0x4000 },   /* R3663  - EQ3_20 */ 
+       { 0x00000E50, 0x0B75 },   /* R3664  - EQ3_21 */ 
+       { 0x00000E52, 0x6318 },   /* R3666  - EQ4_1 */ 
+       { 0x00000E53, 0x6300 },   /* R3667  - EQ4_2 */ 
+       { 0x00000E54, 0x0FC8 },   /* R3668  - EQ4_3 */ 
+       { 0x00000E55, 0x03FE },   /* R3669  - EQ4_4 */ 
+       { 0x00000E56, 0x00E0 },   /* R3670  - EQ4_5 */ 
+       { 0x00000E57, 0x1EC4 },   /* R3671  - EQ4_6 */ 
+       { 0x00000E58, 0xF136 },   /* R3672  - EQ4_7 */ 
+       { 0x00000E59, 0x0409 },   /* R3673  - EQ4_8 */ 
+       { 0x00000E5A, 0x04CC },   /* R3674  - EQ4_9 */ 
+       { 0x00000E5B, 0x1C9B },   /* R3675  - EQ4_10 */ 
+       { 0x00000E5C, 0xF337 },   /* R3676  - EQ4_11 */ 
+       { 0x00000E5D, 0x040B },   /* R3677  - EQ4_12 */ 
+       { 0x00000E5E, 0x0CBB },   /* R3678  - EQ4_13 */ 
+       { 0x00000E5F, 0x16F8 },   /* R3679  - EQ4_14 */ 
+       { 0x00000E60, 0xF7D9 },   /* R3680  - EQ4_15 */ 
+       { 0x00000E61, 0x040A },   /* R3681  - EQ4_16 */ 
+       { 0x00000E62, 0x1F14 },   /* R3682  - EQ4_17 */ 
+       { 0x00000E63, 0x058C },   /* R3683  - EQ4_18 */ 
+       { 0x00000E64, 0x0563 },   /* R3684  - EQ4_19 */ 
+       { 0x00000E65, 0x4000 },   /* R3685  - EQ4_20 */ 
+       { 0x00000E66, 0x0B75 },   /* R3686  - EQ4_21 */ 
+       { 0x00000E80, 0x0018 },   /* R3712  - DRC1 ctrl1 */ 
+       { 0x00000E81, 0x0933 },   /* R3713  - DRC1 ctrl2 */ 
+       { 0x00000E82, 0x0018 },   /* R3714  - DRC1 ctrl3 */ 
+       { 0x00000E83, 0x0000 },   /* R3715  - DRC1 ctrl4 */ 
+       { 0x00000E84, 0x0000 },   /* R3716  - DRC1 ctrl5 */ 
+       { 0x00000E89, 0x0018 },   /* R3721  - DRC2 ctrl1 */ 
+       { 0x00000E8A, 0x0933 },   /* R3722  - DRC2 ctrl2 */ 
+       { 0x00000E8B, 0x0018 },   /* R3723  - DRC2 ctrl3 */ 
+       { 0x00000E8C, 0x0000 },   /* R3724  - DRC2 ctrl4 */ 
+       { 0x00000E8D, 0x0000 },   /* R3725  - DRC2 ctrl5 */ 
+       { 0x00000EC0, 0x0000 },   /* R3776  - HPLPF1_1 */ 
+       { 0x00000EC1, 0x0000 },   /* R3777  - HPLPF1_2 */ 
+       { 0x00000EC4, 0x0000 },   /* R3780  - HPLPF2_1 */ 
+       { 0x00000EC5, 0x0000 },   /* R3781  - HPLPF2_2 */ 
+       { 0x00000EC8, 0x0000 },   /* R3784  - HPLPF3_1 */ 
+       { 0x00000EC9, 0x0000 },   /* R3785  - HPLPF3_2 */ 
+       { 0x00000ECC, 0x0000 },   /* R3788  - HPLPF4_1 */ 
+       { 0x00000ECD, 0x0000 },   /* R3789  - HPLPF4_2 */ 
+       { 0x00000EE0, 0x0000 },   /* R3808  - ASRC_ENABLE */ 
+       { 0x00000EE2, 0x0000 },   /* R3810  - ASRC_RATE1 */ 
+       { 0x00000EE3, 0x4000 },   /* R3811  - ASRC_RATE2 */ 
+       { 0x00000EF0, 0x0000 },   /* R3824  - ISRC 1 CTRL 1 */ 
+       { 0x00000EF1, 0x0000 },   /* R3825  - ISRC 1 CTRL 2 */ 
+       { 0x00000EF2, 0x0000 },   /* R3826  - ISRC 1 CTRL 3 */ 
+       { 0x00000EF3, 0x0000 },   /* R3827  - ISRC 2 CTRL 1 */ 
+       { 0x00000EF4, 0x0000 },   /* R3828  - ISRC 2 CTRL 2 */ 
+       { 0x00000EF5, 0x0000 },   /* R3829  - ISRC 2 CTRL 3 */ 
+       { 0x00000EF6, 0x0000 },   /* R3830  - ISRC 3 CTRL 1 */ 
+       { 0x00000EF7, 0x0000 },   /* R3831  - ISRC 3 CTRL 2 */ 
+       { 0x00000EF8, 0x0000 },   /* R3832  - ISRC 3 CTRL 3 */ 
+       { 0x00001100, 0x0010 },   /* R4352  - DSP1 Control 1 */ 
+       { 0x00001101, 0x0000 },   /* R4353  - DSP1 Clocking 1 */ 
+};
+
+static bool wm5102_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case ARIZONA_SOFTWARE_RESET:
+       case ARIZONA_DEVICE_REVISION:
+       case ARIZONA_CTRL_IF_SPI_CFG_1:
+       case ARIZONA_CTRL_IF_I2C1_CFG_1:
+       case ARIZONA_CTRL_IF_STATUS_1:
+       case ARIZONA_WRITE_SEQUENCER_CTRL_0:
+       case ARIZONA_WRITE_SEQUENCER_CTRL_1:
+       case ARIZONA_WRITE_SEQUENCER_CTRL_2:
+       case ARIZONA_WRITE_SEQUENCER_PROM:
+       case ARIZONA_TONE_GENERATOR_1:
+       case ARIZONA_TONE_GENERATOR_2:
+       case ARIZONA_TONE_GENERATOR_3:
+       case ARIZONA_TONE_GENERATOR_4:
+       case ARIZONA_TONE_GENERATOR_5:
+       case ARIZONA_PWM_DRIVE_1:
+       case ARIZONA_PWM_DRIVE_2:
+       case ARIZONA_PWM_DRIVE_3:
+       case ARIZONA_WAKE_CONTROL:
+       case ARIZONA_SEQUENCE_CONTROL:
+       case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1:
+       case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2:
+       case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3:
+       case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4:
+       case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1:
+       case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2:
+       case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3:
+       case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4:
+       case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5:
+       case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6:
+       case ARIZONA_COMFORT_NOISE_GENERATOR:
+       case ARIZONA_HAPTICS_CONTROL_1:
+       case ARIZONA_HAPTICS_CONTROL_2:
+       case ARIZONA_HAPTICS_PHASE_1_INTENSITY:
+       case ARIZONA_HAPTICS_PHASE_1_DURATION:
+       case ARIZONA_HAPTICS_PHASE_2_INTENSITY:
+       case ARIZONA_HAPTICS_PHASE_2_DURATION:
+       case ARIZONA_HAPTICS_PHASE_3_INTENSITY:
+       case ARIZONA_HAPTICS_PHASE_3_DURATION:
+       case ARIZONA_HAPTICS_STATUS:
+       case ARIZONA_CLOCK_32K_1:
+       case ARIZONA_SYSTEM_CLOCK_1:
+       case ARIZONA_SAMPLE_RATE_1:
+       case ARIZONA_SAMPLE_RATE_2:
+       case ARIZONA_SAMPLE_RATE_3:
+       case ARIZONA_SAMPLE_RATE_1_STATUS:
+       case ARIZONA_SAMPLE_RATE_2_STATUS:
+       case ARIZONA_SAMPLE_RATE_3_STATUS:
+       case ARIZONA_ASYNC_CLOCK_1:
+       case ARIZONA_ASYNC_SAMPLE_RATE_1:
+       case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
+       case ARIZONA_OUTPUT_SYSTEM_CLOCK:
+       case ARIZONA_OUTPUT_ASYNC_CLOCK:
+       case ARIZONA_RATE_ESTIMATOR_1:
+       case ARIZONA_RATE_ESTIMATOR_2:
+       case ARIZONA_RATE_ESTIMATOR_3:
+       case ARIZONA_RATE_ESTIMATOR_4:
+       case ARIZONA_RATE_ESTIMATOR_5:
+       case ARIZONA_FLL1_CONTROL_1:
+       case ARIZONA_FLL1_CONTROL_2:
+       case ARIZONA_FLL1_CONTROL_3:
+       case ARIZONA_FLL1_CONTROL_4:
+       case ARIZONA_FLL1_CONTROL_5:
+       case ARIZONA_FLL1_CONTROL_6:
+       case ARIZONA_FLL1_LOOP_FILTER_TEST_1:
+       case ARIZONA_FLL1_SYNCHRONISER_1:
+       case ARIZONA_FLL1_SYNCHRONISER_2:
+       case ARIZONA_FLL1_SYNCHRONISER_3:
+       case ARIZONA_FLL1_SYNCHRONISER_4:
+       case ARIZONA_FLL1_SYNCHRONISER_5:
+       case ARIZONA_FLL1_SYNCHRONISER_6:
+       case ARIZONA_FLL1_SPREAD_SPECTRUM:
+       case ARIZONA_FLL1_GPIO_CLOCK:
+       case ARIZONA_FLL2_CONTROL_1:
+       case ARIZONA_FLL2_CONTROL_2:
+       case ARIZONA_FLL2_CONTROL_3:
+       case ARIZONA_FLL2_CONTROL_4:
+       case ARIZONA_FLL2_CONTROL_5:
+       case ARIZONA_FLL2_CONTROL_6:
+       case ARIZONA_FLL2_LOOP_FILTER_TEST_1:
+       case ARIZONA_FLL2_SYNCHRONISER_1:
+       case ARIZONA_FLL2_SYNCHRONISER_2:
+       case ARIZONA_FLL2_SYNCHRONISER_3:
+       case ARIZONA_FLL2_SYNCHRONISER_4:
+       case ARIZONA_FLL2_SYNCHRONISER_5:
+       case ARIZONA_FLL2_SYNCHRONISER_6:
+       case ARIZONA_FLL2_SPREAD_SPECTRUM:
+       case ARIZONA_FLL2_GPIO_CLOCK:
+       case ARIZONA_MIC_CHARGE_PUMP_1:
+       case ARIZONA_LDO1_CONTROL_1:
+       case ARIZONA_LDO2_CONTROL_1:
+       case ARIZONA_MIC_BIAS_CTRL_1:
+       case ARIZONA_MIC_BIAS_CTRL_2:
+       case ARIZONA_MIC_BIAS_CTRL_3:
+       case ARIZONA_ACCESSORY_DETECT_MODE_1:
+       case ARIZONA_HEADPHONE_DETECT_1:
+       case ARIZONA_HEADPHONE_DETECT_2:
+       case ARIZONA_MIC_DETECT_1:
+       case ARIZONA_MIC_DETECT_2:
+       case ARIZONA_MIC_DETECT_3:
+       case ARIZONA_MIC_NOISE_MIX_CONTROL_1:
+       case ARIZONA_ISOLATION_CONTROL:
+       case ARIZONA_JACK_DETECT_ANALOGUE:
+       case ARIZONA_INPUT_ENABLES:
+       case ARIZONA_INPUT_RATE:
+       case ARIZONA_INPUT_VOLUME_RAMP:
+       case ARIZONA_IN1L_CONTROL:
+       case ARIZONA_ADC_DIGITAL_VOLUME_1L:
+       case ARIZONA_DMIC1L_CONTROL:
+       case ARIZONA_IN1R_CONTROL:
+       case ARIZONA_ADC_DIGITAL_VOLUME_1R:
+       case ARIZONA_DMIC1R_CONTROL:
+       case ARIZONA_IN2L_CONTROL:
+       case ARIZONA_ADC_DIGITAL_VOLUME_2L:
+       case ARIZONA_DMIC2L_CONTROL:
+       case ARIZONA_IN2R_CONTROL:
+       case ARIZONA_ADC_DIGITAL_VOLUME_2R:
+       case ARIZONA_DMIC2R_CONTROL:
+       case ARIZONA_IN3L_CONTROL:
+       case ARIZONA_ADC_DIGITAL_VOLUME_3L:
+       case ARIZONA_DMIC3L_CONTROL:
+       case ARIZONA_IN3R_CONTROL:
+       case ARIZONA_ADC_DIGITAL_VOLUME_3R:
+       case ARIZONA_DMIC3R_CONTROL:
+       case ARIZONA_OUTPUT_ENABLES_1:
+       case ARIZONA_OUTPUT_STATUS_1:
+       case ARIZONA_OUTPUT_RATE_1:
+       case ARIZONA_OUTPUT_VOLUME_RAMP:
+       case ARIZONA_OUTPUT_PATH_CONFIG_1L:
+       case ARIZONA_DAC_DIGITAL_VOLUME_1L:
+       case ARIZONA_DAC_VOLUME_LIMIT_1L:
+       case ARIZONA_NOISE_GATE_SELECT_1L:
+       case ARIZONA_OUTPUT_PATH_CONFIG_1R:
+       case ARIZONA_DAC_DIGITAL_VOLUME_1R:
+       case ARIZONA_DAC_VOLUME_LIMIT_1R:
+       case ARIZONA_NOISE_GATE_SELECT_1R:
+       case ARIZONA_OUTPUT_PATH_CONFIG_2L:
+       case ARIZONA_DAC_DIGITAL_VOLUME_2L:
+       case ARIZONA_DAC_VOLUME_LIMIT_2L:
+       case ARIZONA_NOISE_GATE_SELECT_2L:
+       case ARIZONA_OUTPUT_PATH_CONFIG_2R:
+       case ARIZONA_DAC_DIGITAL_VOLUME_2R:
+       case ARIZONA_DAC_VOLUME_LIMIT_2R:
+       case ARIZONA_NOISE_GATE_SELECT_2R:
+       case ARIZONA_OUTPUT_PATH_CONFIG_3L:
+       case ARIZONA_DAC_DIGITAL_VOLUME_3L:
+       case ARIZONA_DAC_VOLUME_LIMIT_3L:
+       case ARIZONA_NOISE_GATE_SELECT_3L:
+       case ARIZONA_OUTPUT_PATH_CONFIG_3R:
+       case ARIZONA_DAC_DIGITAL_VOLUME_3R:
+       case ARIZONA_DAC_VOLUME_LIMIT_3R:
+       case ARIZONA_OUTPUT_PATH_CONFIG_4L:
+       case ARIZONA_DAC_DIGITAL_VOLUME_4L:
+       case ARIZONA_OUT_VOLUME_4L:
+       case ARIZONA_NOISE_GATE_SELECT_4L:
+       case ARIZONA_OUTPUT_PATH_CONFIG_4R:
+       case ARIZONA_DAC_DIGITAL_VOLUME_4R:
+       case ARIZONA_OUT_VOLUME_4R:
+       case ARIZONA_NOISE_GATE_SELECT_4R:
+       case ARIZONA_OUTPUT_PATH_CONFIG_5L:
+       case ARIZONA_DAC_DIGITAL_VOLUME_5L:
+       case ARIZONA_DAC_VOLUME_LIMIT_5L:
+       case ARIZONA_NOISE_GATE_SELECT_5L:
+       case ARIZONA_OUTPUT_PATH_CONFIG_5R:
+       case ARIZONA_DAC_DIGITAL_VOLUME_5R:
+       case ARIZONA_DAC_VOLUME_LIMIT_5R:
+       case ARIZONA_NOISE_GATE_SELECT_5R:
+       case ARIZONA_DAC_AEC_CONTROL_1:
+       case ARIZONA_NOISE_GATE_CONTROL:
+       case ARIZONA_PDM_SPK1_CTRL_1:
+       case ARIZONA_PDM_SPK1_CTRL_2:
+       case ARIZONA_DAC_COMP_1:
+       case ARIZONA_DAC_COMP_2:
+       case ARIZONA_DAC_COMP_3:
+       case ARIZONA_DAC_COMP_4:
+       case ARIZONA_AIF1_BCLK_CTRL:
+       case ARIZONA_AIF1_TX_PIN_CTRL:
+       case ARIZONA_AIF1_RX_PIN_CTRL:
+       case ARIZONA_AIF1_RATE_CTRL:
+       case ARIZONA_AIF1_FORMAT:
+       case ARIZONA_AIF1_TX_BCLK_RATE:
+       case ARIZONA_AIF1_RX_BCLK_RATE:
+       case ARIZONA_AIF1_FRAME_CTRL_1:
+       case ARIZONA_AIF1_FRAME_CTRL_2:
+       case ARIZONA_AIF1_FRAME_CTRL_3:
+       case ARIZONA_AIF1_FRAME_CTRL_4:
+       case ARIZONA_AIF1_FRAME_CTRL_5:
+       case ARIZONA_AIF1_FRAME_CTRL_6:
+       case ARIZONA_AIF1_FRAME_CTRL_7:
+       case ARIZONA_AIF1_FRAME_CTRL_8:
+       case ARIZONA_AIF1_FRAME_CTRL_9:
+       case ARIZONA_AIF1_FRAME_CTRL_10:
+       case ARIZONA_AIF1_FRAME_CTRL_11:
+       case ARIZONA_AIF1_FRAME_CTRL_12:
+       case ARIZONA_AIF1_FRAME_CTRL_13:
+       case ARIZONA_AIF1_FRAME_CTRL_14:
+       case ARIZONA_AIF1_FRAME_CTRL_15:
+       case ARIZONA_AIF1_FRAME_CTRL_16:
+       case ARIZONA_AIF1_FRAME_CTRL_17:
+       case ARIZONA_AIF1_FRAME_CTRL_18:
+       case ARIZONA_AIF1_TX_ENABLES:
+       case ARIZONA_AIF1_RX_ENABLES:
+       case ARIZONA_AIF1_FORCE_WRITE:
+       case ARIZONA_AIF2_BCLK_CTRL:
+       case ARIZONA_AIF2_TX_PIN_CTRL:
+       case ARIZONA_AIF2_RX_PIN_CTRL:
+       case ARIZONA_AIF2_RATE_CTRL:
+       case ARIZONA_AIF2_FORMAT:
+       case ARIZONA_AIF2_TX_BCLK_RATE:
+       case ARIZONA_AIF2_RX_BCLK_RATE:
+       case ARIZONA_AIF2_FRAME_CTRL_1:
+       case ARIZONA_AIF2_FRAME_CTRL_2:
+       case ARIZONA_AIF2_FRAME_CTRL_3:
+       case ARIZONA_AIF2_FRAME_CTRL_4:
+       case ARIZONA_AIF2_FRAME_CTRL_11:
+       case ARIZONA_AIF2_FRAME_CTRL_12:
+       case ARIZONA_AIF2_TX_ENABLES:
+       case ARIZONA_AIF2_RX_ENABLES:
+       case ARIZONA_AIF2_FORCE_WRITE:
+       case ARIZONA_AIF3_BCLK_CTRL:
+       case ARIZONA_AIF3_TX_PIN_CTRL:
+       case ARIZONA_AIF3_RX_PIN_CTRL:
+       case ARIZONA_AIF3_RATE_CTRL:
+       case ARIZONA_AIF3_FORMAT:
+       case ARIZONA_AIF3_TX_BCLK_RATE:
+       case ARIZONA_AIF3_RX_BCLK_RATE:
+       case ARIZONA_AIF3_FRAME_CTRL_1:
+       case ARIZONA_AIF3_FRAME_CTRL_2:
+       case ARIZONA_AIF3_FRAME_CTRL_3:
+       case ARIZONA_AIF3_FRAME_CTRL_4:
+       case ARIZONA_AIF3_FRAME_CTRL_11:
+       case ARIZONA_AIF3_FRAME_CTRL_12:
+       case ARIZONA_AIF3_TX_ENABLES:
+       case ARIZONA_AIF3_RX_ENABLES:
+       case ARIZONA_AIF3_FORCE_WRITE:
+       case ARIZONA_SLIMBUS_FRAMER_REF_GEAR:
+       case ARIZONA_SLIMBUS_RATES_1:
+       case ARIZONA_SLIMBUS_RATES_2:
+       case ARIZONA_SLIMBUS_RATES_3:
+       case ARIZONA_SLIMBUS_RATES_4:
+       case ARIZONA_SLIMBUS_RATES_5:
+       case ARIZONA_SLIMBUS_RATES_6:
+       case ARIZONA_SLIMBUS_RATES_7:
+       case ARIZONA_SLIMBUS_RATES_8:
+       case ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE:
+       case ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE:
+       case ARIZONA_SLIMBUS_RX_PORT_STATUS:
+       case ARIZONA_SLIMBUS_TX_PORT_STATUS:
+       case ARIZONA_PWM1MIX_INPUT_1_SOURCE:
+       case ARIZONA_PWM1MIX_INPUT_1_VOLUME:
+       case ARIZONA_PWM1MIX_INPUT_2_SOURCE:
+       case ARIZONA_PWM1MIX_INPUT_2_VOLUME:
+       case ARIZONA_PWM1MIX_INPUT_3_SOURCE:
+       case ARIZONA_PWM1MIX_INPUT_3_VOLUME:
+       case ARIZONA_PWM1MIX_INPUT_4_SOURCE:
+       case ARIZONA_PWM1MIX_INPUT_4_VOLUME:
+       case ARIZONA_PWM2MIX_INPUT_1_SOURCE:
+       case ARIZONA_PWM2MIX_INPUT_1_VOLUME:
+       case ARIZONA_PWM2MIX_INPUT_2_SOURCE:
+       case ARIZONA_PWM2MIX_INPUT_2_VOLUME:
+       case ARIZONA_PWM2MIX_INPUT_3_SOURCE:
+       case ARIZONA_PWM2MIX_INPUT_3_VOLUME:
+       case ARIZONA_PWM2MIX_INPUT_4_SOURCE:
+       case ARIZONA_PWM2MIX_INPUT_4_VOLUME:
+       case ARIZONA_MICMIX_INPUT_1_SOURCE:
+       case ARIZONA_MICMIX_INPUT_1_VOLUME:
+       case ARIZONA_MICMIX_INPUT_2_SOURCE:
+       case ARIZONA_MICMIX_INPUT_2_VOLUME:
+       case ARIZONA_MICMIX_INPUT_3_SOURCE:
+       case ARIZONA_MICMIX_INPUT_3_VOLUME:
+       case ARIZONA_MICMIX_INPUT_4_SOURCE:
+       case ARIZONA_MICMIX_INPUT_4_VOLUME:
+       case ARIZONA_NOISEMIX_INPUT_1_SOURCE:
+       case ARIZONA_NOISEMIX_INPUT_1_VOLUME:
+       case ARIZONA_NOISEMIX_INPUT_2_SOURCE:
+       case ARIZONA_NOISEMIX_INPUT_2_VOLUME:
+       case ARIZONA_NOISEMIX_INPUT_3_SOURCE:
+       case ARIZONA_NOISEMIX_INPUT_3_VOLUME:
+       case ARIZONA_NOISEMIX_INPUT_4_SOURCE:
+       case ARIZONA_NOISEMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT1LMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT1LMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT1LMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT1LMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT1LMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT1LMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT1LMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT1LMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT1RMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT1RMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT1RMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT1RMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT1RMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT1RMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT1RMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT1RMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT2LMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT2LMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT2LMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT2LMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT2LMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT2LMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT2LMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT2LMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT2RMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT2RMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT2RMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT2RMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT2RMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT2RMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT2RMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT2RMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT3LMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT3LMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT3LMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT3LMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT3LMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT3LMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT3LMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT3LMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT4LMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT4LMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT4LMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT4LMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT4LMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT4LMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT4LMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT4LMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT4RMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT4RMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT4RMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT4RMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT4RMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT4RMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT4RMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT4RMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT5LMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT5LMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT5LMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT5LMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT5LMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT5LMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT5LMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT5LMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT5RMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT5RMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT5RMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT5RMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT5RMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT5RMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT5RMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT5RMIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX1MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX1MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX1MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX1MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX1MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX1MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX1MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX2MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX2MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX2MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX2MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX2MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX2MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX2MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX3MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX3MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX3MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX3MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX3MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX3MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX3MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX4MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX4MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX4MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX4MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX4MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX4MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX4MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX5MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX5MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX5MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX5MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX5MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX5MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX5MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX6MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX6MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX6MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX6MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX6MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX6MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX6MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX7MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX7MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX7MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX7MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX7MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX7MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX7MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX8MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX8MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX8MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX8MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX8MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX8MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX8MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF2TX1MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF2TX1MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF2TX1MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF2TX1MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF2TX1MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF2TX1MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF2TX1MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF2TX2MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF2TX2MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF2TX2MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF2TX2MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF2TX2MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF2TX2MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF2TX2MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF3TX1MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF3TX1MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF3TX1MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF3TX1MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF3TX1MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF3TX1MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF3TX1MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF3TX2MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF3TX2MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF3TX2MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF3TX2MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF3TX2MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF3TX2MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF3TX2MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX1MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX1MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX1MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX1MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX1MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX1MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX1MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX2MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX2MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX2MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX2MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX2MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX2MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX2MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX3MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX3MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX3MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX3MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX3MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX3MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX3MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX4MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX4MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX4MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX4MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX4MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX4MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX4MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX5MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX5MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX5MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX5MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX5MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX5MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX5MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX6MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX6MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX6MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX6MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX6MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX6MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX6MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX7MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX7MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX7MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX7MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX7MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX7MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX7MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX8MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX8MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX8MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX8MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX8MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX8MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX8MIX_INPUT_4_VOLUME:
+       case ARIZONA_EQ1MIX_INPUT_1_SOURCE:
+       case ARIZONA_EQ1MIX_INPUT_1_VOLUME:
+       case ARIZONA_EQ1MIX_INPUT_2_SOURCE:
+       case ARIZONA_EQ1MIX_INPUT_2_VOLUME:
+       case ARIZONA_EQ1MIX_INPUT_3_SOURCE:
+       case ARIZONA_EQ1MIX_INPUT_3_VOLUME:
+       case ARIZONA_EQ1MIX_INPUT_4_SOURCE:
+       case ARIZONA_EQ1MIX_INPUT_4_VOLUME:
+       case ARIZONA_EQ2MIX_INPUT_1_SOURCE:
+       case ARIZONA_EQ2MIX_INPUT_1_VOLUME:
+       case ARIZONA_EQ2MIX_INPUT_2_SOURCE:
+       case ARIZONA_EQ2MIX_INPUT_2_VOLUME:
+       case ARIZONA_EQ2MIX_INPUT_3_SOURCE:
+       case ARIZONA_EQ2MIX_INPUT_3_VOLUME:
+       case ARIZONA_EQ2MIX_INPUT_4_SOURCE:
+       case ARIZONA_EQ2MIX_INPUT_4_VOLUME:
+       case ARIZONA_EQ3MIX_INPUT_1_SOURCE:
+       case ARIZONA_EQ3MIX_INPUT_1_VOLUME:
+       case ARIZONA_EQ3MIX_INPUT_2_SOURCE:
+       case ARIZONA_EQ3MIX_INPUT_2_VOLUME:
+       case ARIZONA_EQ3MIX_INPUT_3_SOURCE:
+       case ARIZONA_EQ3MIX_INPUT_3_VOLUME:
+       case ARIZONA_EQ3MIX_INPUT_4_SOURCE:
+       case ARIZONA_EQ3MIX_INPUT_4_VOLUME:
+       case ARIZONA_EQ4MIX_INPUT_1_SOURCE:
+       case ARIZONA_EQ4MIX_INPUT_1_VOLUME:
+       case ARIZONA_EQ4MIX_INPUT_2_SOURCE:
+       case ARIZONA_EQ4MIX_INPUT_2_VOLUME:
+       case ARIZONA_EQ4MIX_INPUT_3_SOURCE:
+       case ARIZONA_EQ4MIX_INPUT_3_VOLUME:
+       case ARIZONA_EQ4MIX_INPUT_4_SOURCE:
+       case ARIZONA_EQ4MIX_INPUT_4_VOLUME:
+       case ARIZONA_DRC1LMIX_INPUT_1_SOURCE:
+       case ARIZONA_DRC1LMIX_INPUT_1_VOLUME:
+       case ARIZONA_DRC1LMIX_INPUT_2_SOURCE:
+       case ARIZONA_DRC1LMIX_INPUT_2_VOLUME:
+       case ARIZONA_DRC1LMIX_INPUT_3_SOURCE:
+       case ARIZONA_DRC1LMIX_INPUT_3_VOLUME:
+       case ARIZONA_DRC1LMIX_INPUT_4_SOURCE:
+       case ARIZONA_DRC1LMIX_INPUT_4_VOLUME:
+       case ARIZONA_DRC1RMIX_INPUT_1_SOURCE:
+       case ARIZONA_DRC1RMIX_INPUT_1_VOLUME:
+       case ARIZONA_DRC1RMIX_INPUT_2_SOURCE:
+       case ARIZONA_DRC1RMIX_INPUT_2_VOLUME:
+       case ARIZONA_DRC1RMIX_INPUT_3_SOURCE:
+       case ARIZONA_DRC1RMIX_INPUT_3_VOLUME:
+       case ARIZONA_DRC1RMIX_INPUT_4_SOURCE:
+       case ARIZONA_DRC1RMIX_INPUT_4_VOLUME:
+       case ARIZONA_DRC2LMIX_INPUT_1_SOURCE:
+       case ARIZONA_DRC2LMIX_INPUT_1_VOLUME:
+       case ARIZONA_DRC2LMIX_INPUT_2_SOURCE:
+       case ARIZONA_DRC2LMIX_INPUT_2_VOLUME:
+       case ARIZONA_DRC2LMIX_INPUT_3_SOURCE:
+       case ARIZONA_DRC2LMIX_INPUT_3_VOLUME:
+       case ARIZONA_DRC2LMIX_INPUT_4_SOURCE:
+       case ARIZONA_DRC2LMIX_INPUT_4_VOLUME:
+       case ARIZONA_DRC2RMIX_INPUT_1_SOURCE:
+       case ARIZONA_DRC2RMIX_INPUT_1_VOLUME:
+       case ARIZONA_DRC2RMIX_INPUT_2_SOURCE:
+       case ARIZONA_DRC2RMIX_INPUT_2_VOLUME:
+       case ARIZONA_DRC2RMIX_INPUT_3_SOURCE:
+       case ARIZONA_DRC2RMIX_INPUT_3_VOLUME:
+       case ARIZONA_DRC2RMIX_INPUT_4_SOURCE:
+       case ARIZONA_DRC2RMIX_INPUT_4_VOLUME:
+       case ARIZONA_HPLP1MIX_INPUT_1_SOURCE:
+       case ARIZONA_HPLP1MIX_INPUT_1_VOLUME:
+       case ARIZONA_HPLP1MIX_INPUT_2_SOURCE:
+       case ARIZONA_HPLP1MIX_INPUT_2_VOLUME:
+       case ARIZONA_HPLP1MIX_INPUT_3_SOURCE:
+       case ARIZONA_HPLP1MIX_INPUT_3_VOLUME:
+       case ARIZONA_HPLP1MIX_INPUT_4_SOURCE:
+       case ARIZONA_HPLP1MIX_INPUT_4_VOLUME:
+       case ARIZONA_HPLP2MIX_INPUT_1_SOURCE:
+       case ARIZONA_HPLP2MIX_INPUT_1_VOLUME:
+       case ARIZONA_HPLP2MIX_INPUT_2_SOURCE:
+       case ARIZONA_HPLP2MIX_INPUT_2_VOLUME:
+       case ARIZONA_HPLP2MIX_INPUT_3_SOURCE:
+       case ARIZONA_HPLP2MIX_INPUT_3_VOLUME:
+       case ARIZONA_HPLP2MIX_INPUT_4_SOURCE:
+       case ARIZONA_HPLP2MIX_INPUT_4_VOLUME:
+       case ARIZONA_HPLP3MIX_INPUT_1_SOURCE:
+       case ARIZONA_HPLP3MIX_INPUT_1_VOLUME:
+       case ARIZONA_HPLP3MIX_INPUT_2_SOURCE:
+       case ARIZONA_HPLP3MIX_INPUT_2_VOLUME:
+       case ARIZONA_HPLP3MIX_INPUT_3_SOURCE:
+       case ARIZONA_HPLP3MIX_INPUT_3_VOLUME:
+       case ARIZONA_HPLP3MIX_INPUT_4_SOURCE:
+       case ARIZONA_HPLP3MIX_INPUT_4_VOLUME:
+       case ARIZONA_HPLP4MIX_INPUT_1_SOURCE:
+       case ARIZONA_HPLP4MIX_INPUT_1_VOLUME:
+       case ARIZONA_HPLP4MIX_INPUT_2_SOURCE:
+       case ARIZONA_HPLP4MIX_INPUT_2_VOLUME:
+       case ARIZONA_HPLP4MIX_INPUT_3_SOURCE:
+       case ARIZONA_HPLP4MIX_INPUT_3_VOLUME:
+       case ARIZONA_HPLP4MIX_INPUT_4_SOURCE:
+       case ARIZONA_HPLP4MIX_INPUT_4_VOLUME:
+       case ARIZONA_DSP1LMIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP1LMIX_INPUT_1_VOLUME:
+       case ARIZONA_DSP1LMIX_INPUT_2_SOURCE:
+       case ARIZONA_DSP1LMIX_INPUT_2_VOLUME:
+       case ARIZONA_DSP1LMIX_INPUT_3_SOURCE:
+       case ARIZONA_DSP1LMIX_INPUT_3_VOLUME:
+       case ARIZONA_DSP1LMIX_INPUT_4_SOURCE:
+       case ARIZONA_DSP1LMIX_INPUT_4_VOLUME:
+       case ARIZONA_DSP1RMIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP1RMIX_INPUT_1_VOLUME:
+       case ARIZONA_DSP1RMIX_INPUT_2_SOURCE:
+       case ARIZONA_DSP1RMIX_INPUT_2_VOLUME:
+       case ARIZONA_DSP1RMIX_INPUT_3_SOURCE:
+       case ARIZONA_DSP1RMIX_INPUT_3_VOLUME:
+       case ARIZONA_DSP1RMIX_INPUT_4_SOURCE:
+       case ARIZONA_DSP1RMIX_INPUT_4_VOLUME:
+       case ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP1AUX2MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP1AUX3MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP1AUX4MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP1AUX5MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP1AUX6MIX_INPUT_1_SOURCE:
+       case ARIZONA_ASRC1LMIX_INPUT_1_SOURCE:
+       case ARIZONA_ASRC1RMIX_INPUT_1_SOURCE:
+       case ARIZONA_ASRC2LMIX_INPUT_1_SOURCE:
+       case ARIZONA_ASRC2RMIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE:
+       case ARIZONA_GPIO1_CTRL:
+       case ARIZONA_GPIO2_CTRL:
+       case ARIZONA_GPIO3_CTRL:
+       case ARIZONA_GPIO4_CTRL:
+       case ARIZONA_GPIO5_CTRL:
+       case ARIZONA_IRQ_CTRL_1:
+       case ARIZONA_GPIO_DEBOUNCE_CONFIG:
+       case ARIZONA_MISC_PAD_CTRL_1:
+       case ARIZONA_MISC_PAD_CTRL_2:
+       case ARIZONA_MISC_PAD_CTRL_3:
+       case ARIZONA_MISC_PAD_CTRL_4:
+       case ARIZONA_MISC_PAD_CTRL_5:
+       case ARIZONA_MISC_PAD_CTRL_6:
+       case ARIZONA_INTERRUPT_STATUS_1:
+       case ARIZONA_INTERRUPT_STATUS_2:
+       case ARIZONA_INTERRUPT_STATUS_3:
+       case ARIZONA_INTERRUPT_STATUS_4:
+       case ARIZONA_INTERRUPT_STATUS_5:
+       case ARIZONA_INTERRUPT_STATUS_1_MASK:
+       case ARIZONA_INTERRUPT_STATUS_2_MASK:
+       case ARIZONA_INTERRUPT_STATUS_3_MASK:
+       case ARIZONA_INTERRUPT_STATUS_4_MASK:
+       case ARIZONA_INTERRUPT_STATUS_5_MASK:
+       case ARIZONA_INTERRUPT_CONTROL:
+       case ARIZONA_IRQ2_STATUS_1:
+       case ARIZONA_IRQ2_STATUS_2:
+       case ARIZONA_IRQ2_STATUS_3:
+       case ARIZONA_IRQ2_STATUS_4:
+       case ARIZONA_IRQ2_STATUS_5:
+       case ARIZONA_IRQ2_STATUS_1_MASK:
+       case ARIZONA_IRQ2_STATUS_2_MASK:
+       case ARIZONA_IRQ2_STATUS_3_MASK:
+       case ARIZONA_IRQ2_STATUS_4_MASK:
+       case ARIZONA_IRQ2_STATUS_5_MASK:
+       case ARIZONA_IRQ2_CONTROL:
+       case ARIZONA_INTERRUPT_RAW_STATUS_2:
+       case ARIZONA_INTERRUPT_RAW_STATUS_3:
+       case ARIZONA_INTERRUPT_RAW_STATUS_4:
+       case ARIZONA_INTERRUPT_RAW_STATUS_5:
+       case ARIZONA_INTERRUPT_RAW_STATUS_6:
+       case ARIZONA_INTERRUPT_RAW_STATUS_7:
+       case ARIZONA_INTERRUPT_RAW_STATUS_8:
+       case ARIZONA_IRQ_PIN_STATUS:
+       case ARIZONA_ADSP2_IRQ0:
+       case ARIZONA_AOD_WKUP_AND_TRIG:
+       case ARIZONA_AOD_IRQ1:
+       case ARIZONA_AOD_IRQ2:
+       case ARIZONA_AOD_IRQ_MASK_IRQ1:
+       case ARIZONA_AOD_IRQ_MASK_IRQ2:
+       case ARIZONA_AOD_IRQ_RAW_STATUS:
+       case ARIZONA_JACK_DETECT_DEBOUNCE:
+       case ARIZONA_FX_CTRL1:
+       case ARIZONA_FX_CTRL2:
+       case ARIZONA_EQ1_1:
+       case ARIZONA_EQ1_2:
+       case ARIZONA_EQ1_3:
+       case ARIZONA_EQ1_4:
+       case ARIZONA_EQ1_5:
+       case ARIZONA_EQ1_6:
+       case ARIZONA_EQ1_7:
+       case ARIZONA_EQ1_8:
+       case ARIZONA_EQ1_9:
+       case ARIZONA_EQ1_10:
+       case ARIZONA_EQ1_11:
+       case ARIZONA_EQ1_12:
+       case ARIZONA_EQ1_13:
+       case ARIZONA_EQ1_14:
+       case ARIZONA_EQ1_15:
+       case ARIZONA_EQ1_16:
+       case ARIZONA_EQ1_17:
+       case ARIZONA_EQ1_18:
+       case ARIZONA_EQ1_19:
+       case ARIZONA_EQ1_20:
+       case ARIZONA_EQ1_21:
+       case ARIZONA_EQ2_1:
+       case ARIZONA_EQ2_2:
+       case ARIZONA_EQ2_3:
+       case ARIZONA_EQ2_4:
+       case ARIZONA_EQ2_5:
+       case ARIZONA_EQ2_6:
+       case ARIZONA_EQ2_7:
+       case ARIZONA_EQ2_8:
+       case ARIZONA_EQ2_9:
+       case ARIZONA_EQ2_10:
+       case ARIZONA_EQ2_11:
+       case ARIZONA_EQ2_12:
+       case ARIZONA_EQ2_13:
+       case ARIZONA_EQ2_14:
+       case ARIZONA_EQ2_15:
+       case ARIZONA_EQ2_16:
+       case ARIZONA_EQ2_17:
+       case ARIZONA_EQ2_18:
+       case ARIZONA_EQ2_19:
+       case ARIZONA_EQ2_20:
+       case ARIZONA_EQ2_21:
+       case ARIZONA_EQ3_1:
+       case ARIZONA_EQ3_2:
+       case ARIZONA_EQ3_3:
+       case ARIZONA_EQ3_4:
+       case ARIZONA_EQ3_5:
+       case ARIZONA_EQ3_6:
+       case ARIZONA_EQ3_7:
+       case ARIZONA_EQ3_8:
+       case ARIZONA_EQ3_9:
+       case ARIZONA_EQ3_10:
+       case ARIZONA_EQ3_11:
+       case ARIZONA_EQ3_12:
+       case ARIZONA_EQ3_13:
+       case ARIZONA_EQ3_14:
+       case ARIZONA_EQ3_15:
+       case ARIZONA_EQ3_16:
+       case ARIZONA_EQ3_17:
+       case ARIZONA_EQ3_18:
+       case ARIZONA_EQ3_19:
+       case ARIZONA_EQ3_20:
+       case ARIZONA_EQ3_21:
+       case ARIZONA_EQ4_1:
+       case ARIZONA_EQ4_2:
+       case ARIZONA_EQ4_3:
+       case ARIZONA_EQ4_4:
+       case ARIZONA_EQ4_5:
+       case ARIZONA_EQ4_6:
+       case ARIZONA_EQ4_7:
+       case ARIZONA_EQ4_8:
+       case ARIZONA_EQ4_9:
+       case ARIZONA_EQ4_10:
+       case ARIZONA_EQ4_11:
+       case ARIZONA_EQ4_12:
+       case ARIZONA_EQ4_13:
+       case ARIZONA_EQ4_14:
+       case ARIZONA_EQ4_15:
+       case ARIZONA_EQ4_16:
+       case ARIZONA_EQ4_17:
+       case ARIZONA_EQ4_18:
+       case ARIZONA_EQ4_19:
+       case ARIZONA_EQ4_20:
+       case ARIZONA_EQ4_21:
+       case ARIZONA_DRC1_CTRL1:
+       case ARIZONA_DRC1_CTRL2:
+       case ARIZONA_DRC1_CTRL3:
+       case ARIZONA_DRC1_CTRL4:
+       case ARIZONA_DRC1_CTRL5:
+       case ARIZONA_DRC2_CTRL1:
+       case ARIZONA_DRC2_CTRL2:
+       case ARIZONA_DRC2_CTRL3:
+       case ARIZONA_DRC2_CTRL4:
+       case ARIZONA_DRC2_CTRL5:
+       case ARIZONA_HPLPF1_1:
+       case ARIZONA_HPLPF1_2:
+       case ARIZONA_HPLPF2_1:
+       case ARIZONA_HPLPF2_2:
+       case ARIZONA_HPLPF3_1:
+       case ARIZONA_HPLPF3_2:
+       case ARIZONA_HPLPF4_1:
+       case ARIZONA_HPLPF4_2:
+       case ARIZONA_ASRC_ENABLE:
+       case ARIZONA_ASRC_RATE1:
+       case ARIZONA_ASRC_RATE2:
+       case ARIZONA_ISRC_1_CTRL_1:
+       case ARIZONA_ISRC_1_CTRL_2:
+       case ARIZONA_ISRC_1_CTRL_3:
+       case ARIZONA_ISRC_2_CTRL_1:
+       case ARIZONA_ISRC_2_CTRL_2:
+       case ARIZONA_ISRC_2_CTRL_3:
+       case ARIZONA_ISRC_3_CTRL_1:
+       case ARIZONA_ISRC_3_CTRL_2:
+       case ARIZONA_ISRC_3_CTRL_3:
+       case ARIZONA_DSP1_CONTROL_1:
+       case ARIZONA_DSP1_CLOCKING_1:
+       case ARIZONA_DSP1_STATUS_1:
+       case ARIZONA_DSP1_STATUS_2:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool wm5102_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case ARIZONA_SOFTWARE_RESET:
+       case ARIZONA_DEVICE_REVISION:
+       case ARIZONA_OUTPUT_STATUS_1:
+       case ARIZONA_SAMPLE_RATE_1_STATUS:
+       case ARIZONA_SAMPLE_RATE_2_STATUS:
+       case ARIZONA_SAMPLE_RATE_3_STATUS:
+       case ARIZONA_HAPTICS_STATUS:
+       case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
+       case ARIZONA_FX_CTRL2:
+       case ARIZONA_INTERRUPT_STATUS_1:
+       case ARIZONA_INTERRUPT_STATUS_2:
+       case ARIZONA_INTERRUPT_STATUS_3:
+       case ARIZONA_INTERRUPT_STATUS_4:
+       case ARIZONA_INTERRUPT_STATUS_5:
+       case ARIZONA_IRQ2_STATUS_1:
+       case ARIZONA_IRQ2_STATUS_2:
+       case ARIZONA_IRQ2_STATUS_3:
+       case ARIZONA_IRQ2_STATUS_4:
+       case ARIZONA_IRQ2_STATUS_5:
+       case ARIZONA_INTERRUPT_RAW_STATUS_2:
+       case ARIZONA_INTERRUPT_RAW_STATUS_3:
+       case ARIZONA_INTERRUPT_RAW_STATUS_4:
+       case ARIZONA_INTERRUPT_RAW_STATUS_5:
+       case ARIZONA_INTERRUPT_RAW_STATUS_6:
+       case ARIZONA_INTERRUPT_RAW_STATUS_7:
+       case ARIZONA_INTERRUPT_RAW_STATUS_8:
+       case ARIZONA_IRQ_PIN_STATUS:
+       case ARIZONA_AOD_WKUP_AND_TRIG:
+       case ARIZONA_AOD_IRQ1:
+       case ARIZONA_AOD_IRQ2:
+       case ARIZONA_AOD_IRQ_RAW_STATUS:
+       case ARIZONA_DSP1_STATUS_1:
+       case ARIZONA_DSP1_STATUS_2:
+       case ARIZONA_HEADPHONE_DETECT_2:
+       case ARIZONA_MIC_DETECT_3:
+               return true;
+       default:
+               return false;
+       }
+}
+
+const struct regmap_config wm5102_spi_regmap = {
+       .reg_bits = 32,
+       .pad_bits = 16,
+       .val_bits = 16,
+
+       .max_register = ARIZONA_DSP1_STATUS_2,
+       .readable_reg = wm5102_readable_register,
+       .volatile_reg = wm5102_volatile_register,
+
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults = wm5102_reg_default,
+       .num_reg_defaults = ARRAY_SIZE(wm5102_reg_default),
+};
+EXPORT_SYMBOL_GPL(wm5102_spi_regmap);
+
+const struct regmap_config wm5102_i2c_regmap = {
+       .reg_bits = 32,
+       .val_bits = 16,
+
+       .max_register = ARIZONA_DSP1_STATUS_2,
+       .readable_reg = wm5102_readable_register,
+       .volatile_reg = wm5102_volatile_register,
+
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults = wm5102_reg_default,
+       .num_reg_defaults = ARRAY_SIZE(wm5102_reg_default),
+};
+EXPORT_SYMBOL_GPL(wm5102_i2c_regmap);
diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c
new file mode 100644 (file)
index 0000000..bd8782c
--- /dev/null
@@ -0,0 +1,2281 @@
+/*
+ * wm5110-tables.c  --  WM5110 data tables
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+
+#define WM5110_NUM_AOD_ISR 2
+#define WM5110_NUM_ISR 5
+
+static const struct reg_default wm5110_reva_patch[] = {
+       { 0x80, 0x3 },
+       { 0x44, 0x20 },
+       { 0x45, 0x40 },
+       { 0x46, 0x60 },
+       { 0x47, 0x80 },
+       { 0x48, 0xa0 },
+       { 0x51, 0x13 },
+       { 0x52, 0x33 },
+       { 0x53, 0x53 },
+       { 0x54, 0x73 },
+       { 0x55, 0x75 },
+       { 0x56, 0xb3 },
+       { 0x2ef, 0x124 },
+       { 0x2ef, 0x124 },
+       { 0x2f0, 0x124 },
+       { 0x2f0, 0x124 },
+       { 0x2f1, 0x124 },
+       { 0x2f1, 0x124 },
+       { 0x2f2, 0x124 },
+       { 0x2f2, 0x124 },
+       { 0x2f3, 0x124 },
+       { 0x2f3, 0x124 },
+       { 0x2f4, 0x124 },
+       { 0x2f4, 0x124 },
+       { 0x2eb, 0x60 },
+       { 0x2ec, 0x60 },
+       { 0x2ed, 0x60 },
+       { 0xc30, 0x3e3e },
+       { 0xc30, 0x3e3e },
+       { 0xc31, 0x3e },
+       { 0xc32, 0x3e3e },
+       { 0xc32, 0x3e3e },
+       { 0xc33, 0x3e3e },
+       { 0xc33, 0x3e3e },
+       { 0xc34, 0x3e3e },
+       { 0xc34, 0x3e3e },
+       { 0xc35, 0x3e3e },
+       { 0xc35, 0x3e3e },
+       { 0xc36, 0x3e3e },
+       { 0xc36, 0x3e3e },
+       { 0xc37, 0x3e3e },
+       { 0xc37, 0x3e3e },
+       { 0xc38, 0x3e3e },
+       { 0xc38, 0x3e3e },
+       { 0xc30, 0x3e3e },
+       { 0xc30, 0x3e3e },
+       { 0xc39, 0x3e3e },
+       { 0xc39, 0x3e3e },
+       { 0xc3a, 0x3e3e },
+       { 0xc3a, 0x3e3e },
+       { 0xc3b, 0x3e3e },
+       { 0xc3b, 0x3e3e },
+       { 0xc3c, 0x3e },
+       { 0x201, 0x18a5 },
+       { 0x201, 0x18a5 },
+       { 0x201, 0x18a5 },
+       { 0x202, 0x4100 },
+       { 0x460, 0xc00 },
+       { 0x461, 0x8000 },
+       { 0x462, 0xc01 },
+       { 0x463, 0x50f0 },
+       { 0x464, 0xc01 },
+       { 0x465, 0x4820 },
+       { 0x466, 0xc01 },
+       { 0x466, 0xc01 },
+       { 0x467, 0x4040 },
+       { 0x468, 0xc01 },
+       { 0x468, 0xc01 },
+       { 0x469, 0x3940 },
+       { 0x46a, 0xc01 },
+       { 0x46a, 0xc01 },
+       { 0x46a, 0xc01 },
+       { 0x46b, 0x3310 },
+       { 0x46c, 0x801 },
+       { 0x46c, 0x801 },
+       { 0x46d, 0x2d80 },
+       { 0x46e, 0x801 },
+       { 0x46e, 0x801 },
+       { 0x46f, 0x2890 },
+       { 0x470, 0x801 },
+       { 0x470, 0x801 },
+       { 0x471, 0x1990 },
+       { 0x472, 0x801 },
+       { 0x472, 0x801 },
+       { 0x473, 0x1450 },
+       { 0x474, 0x801 },
+       { 0x474, 0x801 },
+       { 0x474, 0x801 },
+       { 0x475, 0x1020 },
+       { 0x476, 0x801 },
+       { 0x476, 0x801 },
+       { 0x476, 0x801 },
+       { 0x477, 0xcd0 },
+       { 0x478, 0x806 },
+       { 0x478, 0x806 },
+       { 0x479, 0xa30 },
+       { 0x47a, 0x806 },
+       { 0x47a, 0x806 },
+       { 0x47b, 0x810 },
+       { 0x47c, 0x80e },
+       { 0x47c, 0x80e },
+       { 0x47d, 0x510 },
+       { 0x47e, 0x81f },
+       { 0x47e, 0x81f },
+       { 0x2DB, 0x0A00 },
+       { 0x2DD, 0x0023 },
+       { 0x2DF, 0x0102 },
+       { 0x80, 0x0 },
+       { 0xC20, 0x0002 },
+       { 0x209, 0x002A },
+};
+
+/* We use a function so we can use ARRAY_SIZE() */
+int wm5110_patch(struct arizona *arizona)
+{
+       switch (arizona->rev) {
+       case 0:
+       case 1:
+               return regmap_register_patch(arizona->regmap,
+                                            wm5110_reva_patch,
+                                            ARRAY_SIZE(wm5110_reva_patch));
+       default:
+               return 0;
+       }
+}
+EXPORT_SYMBOL_GPL(wm5110_patch);
+
+static const struct regmap_irq wm5110_aod_irqs[ARIZONA_NUM_IRQ] = {
+       [ARIZONA_IRQ_GP5_FALL] = { .mask = ARIZONA_GP5_FALL_EINT1 },
+       [ARIZONA_IRQ_GP5_RISE] = { .mask = ARIZONA_GP5_RISE_EINT1 },
+       [ARIZONA_IRQ_JD_FALL] = { .mask = ARIZONA_JD1_FALL_EINT1 },
+       [ARIZONA_IRQ_JD_RISE] = { .mask = ARIZONA_JD1_RISE_EINT1 },
+};
+
+const struct regmap_irq_chip wm5110_aod = {
+       .name = "wm5110 AOD",
+       .status_base = ARIZONA_AOD_IRQ1,
+       .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1,
+       .ack_base = ARIZONA_AOD_IRQ1,
+       .wake_base = ARIZONA_WAKE_CONTROL,
+       .num_regs = 1,
+       .irqs = wm5110_aod_irqs,
+       .num_irqs = ARRAY_SIZE(wm5110_aod_irqs),
+};
+EXPORT_SYMBOL_GPL(wm5110_aod);
+
+static const struct regmap_irq wm5110_irqs[ARIZONA_NUM_IRQ] = {
+       [ARIZONA_IRQ_GP4] = { .reg_offset = 0, .mask = ARIZONA_GP4_EINT1 },
+       [ARIZONA_IRQ_GP3] = { .reg_offset = 0, .mask = ARIZONA_GP3_EINT1 },
+       [ARIZONA_IRQ_GP2] = { .reg_offset = 0, .mask = ARIZONA_GP2_EINT1 },
+       [ARIZONA_IRQ_GP1] = { .reg_offset = 0, .mask = ARIZONA_GP1_EINT1 },
+
+       [ARIZONA_IRQ_DSP4_RAM_RDY] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP4_RAM_RDY_EINT1
+       },
+       [ARIZONA_IRQ_DSP3_RAM_RDY] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP3_RAM_RDY_EINT1
+       },
+       [ARIZONA_IRQ_DSP2_RAM_RDY] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP2_RAM_RDY_EINT1
+       },
+       [ARIZONA_IRQ_DSP1_RAM_RDY] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP1_RAM_RDY_EINT1
+       },
+       [ARIZONA_IRQ_DSP_IRQ8] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP_IRQ8_EINT1
+       },
+       [ARIZONA_IRQ_DSP_IRQ7] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP_IRQ7_EINT1
+       },
+       [ARIZONA_IRQ_DSP_IRQ6] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP_IRQ6_EINT1
+       },
+       [ARIZONA_IRQ_DSP_IRQ5] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP_IRQ5_EINT1
+       },
+       [ARIZONA_IRQ_DSP_IRQ4] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP_IRQ4_EINT1
+       },
+       [ARIZONA_IRQ_DSP_IRQ3] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP_IRQ3_EINT1
+       },
+       [ARIZONA_IRQ_DSP_IRQ2] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP_IRQ2_EINT1
+       },
+       [ARIZONA_IRQ_DSP_IRQ1] = {
+               .reg_offset = 1, .mask = ARIZONA_DSP_IRQ1_EINT1
+       },
+
+       [ARIZONA_IRQ_SPK_SHUTDOWN_WARN] = {
+               .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_WARN_EINT1
+       },
+       [ARIZONA_IRQ_SPK_SHUTDOWN] = {
+               .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_EINT1
+       },
+       [ARIZONA_IRQ_HPDET] = {
+               .reg_offset = 2, .mask = ARIZONA_HPDET_EINT1
+       },
+       [ARIZONA_IRQ_MICDET] = {
+               .reg_offset = 2, .mask = ARIZONA_MICDET_EINT1
+       },
+       [ARIZONA_IRQ_WSEQ_DONE] = {
+               .reg_offset = 2, .mask = ARIZONA_WSEQ_DONE_EINT1
+       },
+       [ARIZONA_IRQ_DRC2_SIG_DET] = {
+               .reg_offset = 2, .mask = ARIZONA_DRC2_SIG_DET_EINT1
+       },
+       [ARIZONA_IRQ_DRC1_SIG_DET] = {
+               .reg_offset = 2, .mask = ARIZONA_DRC1_SIG_DET_EINT1
+       },
+       [ARIZONA_IRQ_ASRC2_LOCK] = {
+               .reg_offset = 2, .mask = ARIZONA_ASRC2_LOCK_EINT1
+       },
+       [ARIZONA_IRQ_ASRC1_LOCK] = {
+               .reg_offset = 2, .mask = ARIZONA_ASRC1_LOCK_EINT1
+       },
+       [ARIZONA_IRQ_UNDERCLOCKED] = {
+               .reg_offset = 2, .mask = ARIZONA_UNDERCLOCKED_EINT1
+       },
+       [ARIZONA_IRQ_OVERCLOCKED] = {
+               .reg_offset = 2, .mask = ARIZONA_OVERCLOCKED_EINT1
+       },
+       [ARIZONA_IRQ_FLL2_LOCK] = {
+               .reg_offset = 2, .mask = ARIZONA_FLL2_LOCK_EINT1
+       },
+       [ARIZONA_IRQ_FLL1_LOCK] = {
+               .reg_offset = 2, .mask = ARIZONA_FLL1_LOCK_EINT1
+       },
+       [ARIZONA_IRQ_CLKGEN_ERR] = {
+               .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_EINT1
+       },
+       [ARIZONA_IRQ_CLKGEN_ERR_ASYNC] = {
+               .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_ASYNC_EINT1
+       },
+
+       [ARIZONA_IRQ_ASRC_CFG_ERR] = {
+               .reg_offset = 3, .mask = ARIZONA_ASRC_CFG_ERR_EINT1
+       },
+       [ARIZONA_IRQ_AIF3_ERR] = {
+               .reg_offset = 3, .mask = ARIZONA_AIF3_ERR_EINT1
+       },
+       [ARIZONA_IRQ_AIF2_ERR] = {
+               .reg_offset = 3, .mask = ARIZONA_AIF2_ERR_EINT1
+       },
+       [ARIZONA_IRQ_AIF1_ERR] = {
+               .reg_offset = 3, .mask = ARIZONA_AIF1_ERR_EINT1
+       },
+       [ARIZONA_IRQ_CTRLIF_ERR] = {
+               .reg_offset = 3, .mask = ARIZONA_CTRLIF_ERR_EINT1
+       },
+       [ARIZONA_IRQ_MIXER_DROPPED_SAMPLES] = {
+               .reg_offset = 3, .mask = ARIZONA_MIXER_DROPPED_SAMPLE_EINT1
+       },
+       [ARIZONA_IRQ_ASYNC_CLK_ENA_LOW] = {
+               .reg_offset = 3, .mask = ARIZONA_ASYNC_CLK_ENA_LOW_EINT1
+       },
+       [ARIZONA_IRQ_SYSCLK_ENA_LOW] = {
+               .reg_offset = 3, .mask = ARIZONA_SYSCLK_ENA_LOW_EINT1
+       },
+       [ARIZONA_IRQ_ISRC1_CFG_ERR] = {
+               .reg_offset = 3, .mask = ARIZONA_ISRC1_CFG_ERR_EINT1
+       },
+       [ARIZONA_IRQ_ISRC2_CFG_ERR] = {
+               .reg_offset = 3, .mask = ARIZONA_ISRC2_CFG_ERR_EINT1
+       },
+
+       [ARIZONA_IRQ_BOOT_DONE] = {
+               .reg_offset = 4, .mask = ARIZONA_BOOT_DONE_EINT1
+       },
+       [ARIZONA_IRQ_DCS_DAC_DONE] = {
+               .reg_offset = 4, .mask = ARIZONA_DCS_DAC_DONE_EINT1
+       },
+       [ARIZONA_IRQ_DCS_HP_DONE] = {
+               .reg_offset = 4, .mask = ARIZONA_DCS_HP_DONE_EINT1
+       },
+       [ARIZONA_IRQ_FLL2_CLOCK_OK] = {
+               .reg_offset = 4, .mask = ARIZONA_FLL2_CLOCK_OK_EINT1
+       },
+       [ARIZONA_IRQ_FLL1_CLOCK_OK] = {
+               .reg_offset = 4, .mask = ARIZONA_FLL1_CLOCK_OK_EINT1
+       },
+};
+
+const struct regmap_irq_chip wm5110_irq = {
+       .name = "wm5110 IRQ",
+       .status_base = ARIZONA_INTERRUPT_STATUS_1,
+       .mask_base = ARIZONA_INTERRUPT_STATUS_1_MASK,
+       .ack_base = ARIZONA_INTERRUPT_STATUS_1,
+       .num_regs = 5,
+       .irqs = wm5110_irqs,
+       .num_irqs = ARRAY_SIZE(wm5110_irqs),
+};
+EXPORT_SYMBOL_GPL(wm5110_irq);
+
+static const struct reg_default wm5110_reg_default[] = {
+       { 0x00000008, 0x0019 },    /* R8     - Ctrl IF SPI CFG 1 */
+       { 0x00000009, 0x0001 },    /* R9     - Ctrl IF I2C1 CFG 1 */
+       { 0x0000000A, 0x0001 },    /* R10    - Ctrl IF I2C2 CFG 1 */
+       { 0x0000000B, 0x0036 },    /* R11    - Ctrl IF I2C1 CFG 2 */
+       { 0x0000000C, 0x0036 },    /* R12    - Ctrl IF I2C2 CFG 2 */
+       { 0x00000016, 0x0000 },    /* R22    - Write Sequencer Ctrl 0 */
+       { 0x00000017, 0x0000 },    /* R23    - Write Sequencer Ctrl 1 */
+       { 0x00000018, 0x0000 },    /* R24    - Write Sequencer Ctrl 2 */
+       { 0x00000020, 0x0000 },    /* R32    - Tone Generator 1 */
+       { 0x00000021, 0x1000 },    /* R33    - Tone Generator 2 */
+       { 0x00000022, 0x0000 },    /* R34    - Tone Generator 3 */
+       { 0x00000023, 0x1000 },    /* R35    - Tone Generator 4 */
+       { 0x00000024, 0x0000 },    /* R36    - Tone Generator 5 */
+       { 0x00000030, 0x0000 },    /* R48    - PWM Drive 1 */
+       { 0x00000031, 0x0100 },    /* R49    - PWM Drive 2 */
+       { 0x00000032, 0x0100 },    /* R50    - PWM Drive 3 */
+       { 0x00000040, 0x0000 },    /* R64    - Wake control */
+       { 0x00000041, 0x0000 },    /* R65    - Sequence control */
+       { 0x00000061, 0x01FF },    /* R97    - Sample Rate Sequence Select 1 */
+       { 0x00000062, 0x01FF },    /* R98    - Sample Rate Sequence Select 2 */
+       { 0x00000063, 0x01FF },    /* R99    - Sample Rate Sequence Select 3 */
+       { 0x00000064, 0x01FF },    /* R100   - Sample Rate Sequence Select 4 */
+       { 0x00000068, 0x01FF },    /* R104   - Always On Triggers Sequence Select 1 */
+       { 0x00000069, 0x01FF },    /* R105   - Always On Triggers Sequence Select 2 */
+       { 0x0000006A, 0x01FF },    /* R106   - Always On Triggers Sequence Select 3 */
+       { 0x0000006B, 0x01FF },    /* R107   - Always On Triggers Sequence Select 4 */
+       { 0x00000070, 0x0000 },    /* R112   - Comfort Noise Generator */
+       { 0x00000090, 0x0000 },    /* R144   - Haptics Control 1 */
+       { 0x00000091, 0x7FFF },    /* R145   - Haptics Control 2 */
+       { 0x00000092, 0x0000 },    /* R146   - Haptics phase 1 intensity */
+       { 0x00000093, 0x0000 },    /* R147   - Haptics phase 1 duration */
+       { 0x00000094, 0x0000 },    /* R148   - Haptics phase 2 intensity */
+       { 0x00000095, 0x0000 },    /* R149   - Haptics phase 2 duration */
+       { 0x00000096, 0x0000 },    /* R150   - Haptics phase 3 intensity */
+       { 0x00000097, 0x0000 },    /* R151   - Haptics phase 3 duration */
+       { 0x00000100, 0x0001 },    /* R256   - Clock 32k 1 */
+       { 0x00000101, 0x0504 },    /* R257   - System Clock 1 */
+       { 0x00000102, 0x0011 },    /* R258   - Sample rate 1 */
+       { 0x00000103, 0x0011 },    /* R259   - Sample rate 2 */
+       { 0x00000104, 0x0011 },    /* R260   - Sample rate 3 */
+       { 0x00000112, 0x0305 },    /* R274   - Async clock 1 */
+       { 0x00000113, 0x0011 },    /* R275   - Async sample rate 1 */
+       { 0x00000149, 0x0000 },    /* R329   - Output system clock */
+       { 0x0000014A, 0x0000 },    /* R330   - Output async clock */
+       { 0x00000152, 0x0000 },    /* R338   - Rate Estimator 1 */
+       { 0x00000153, 0x0000 },    /* R339   - Rate Estimator 2 */
+       { 0x00000154, 0x0000 },    /* R340   - Rate Estimator 3 */
+       { 0x00000155, 0x0000 },    /* R341   - Rate Estimator 4 */
+       { 0x00000156, 0x0000 },    /* R342   - Rate Estimator 5 */
+       { 0x00000171, 0x0000 },    /* R369   - FLL1 Control 1 */
+       { 0x00000172, 0x0008 },    /* R370   - FLL1 Control 2 */
+       { 0x00000173, 0x0018 },    /* R371   - FLL1 Control 3 */
+       { 0x00000174, 0x007D },    /* R372   - FLL1 Control 4 */
+       { 0x00000175, 0x0006 },    /* R373   - FLL1 Control 5 */
+       { 0x00000176, 0x0000 },    /* R374   - FLL1 Control 6 */
+       { 0x00000177, 0x0281 },    /* R375   - FLL1 Loop Filter Test 1 */
+       { 0x00000178, 0x0000 },    /* R376   - FLL1 NCO Test 0 */
+       { 0x00000181, 0x0000 },    /* R385   - FLL1 Synchroniser 1 */
+       { 0x00000182, 0x0000 },    /* R386   - FLL1 Synchroniser 2 */
+       { 0x00000183, 0x0000 },    /* R387   - FLL1 Synchroniser 3 */
+       { 0x00000184, 0x0000 },    /* R388   - FLL1 Synchroniser 4 */
+       { 0x00000185, 0x0000 },    /* R389   - FLL1 Synchroniser 5 */
+       { 0x00000186, 0x0000 },    /* R390   - FLL1 Synchroniser 6 */
+       { 0x00000189, 0x0000 },    /* R393   - FLL1 Spread Spectrum */
+       { 0x0000018A, 0x0004 },    /* R394   - FLL1 GPIO Clock */
+       { 0x00000191, 0x0000 },    /* R401   - FLL2 Control 1 */
+       { 0x00000192, 0x0008 },    /* R402   - FLL2 Control 2 */
+       { 0x00000193, 0x0018 },    /* R403   - FLL2 Control 3 */
+       { 0x00000194, 0x007D },    /* R404   - FLL2 Control 4 */
+       { 0x00000195, 0x000C },    /* R405   - FLL2 Control 5 */
+       { 0x00000196, 0x0000 },    /* R406   - FLL2 Control 6 */
+       { 0x00000197, 0x0000 },    /* R407   - FLL2 Loop Filter Test 1 */
+       { 0x00000198, 0x0000 },    /* R408   - FLL2 NCO Test 0 */
+       { 0x000001A1, 0x0000 },    /* R417   - FLL2 Synchroniser 1 */
+       { 0x000001A2, 0x0000 },    /* R418   - FLL2 Synchroniser 2 */
+       { 0x000001A3, 0x0000 },    /* R419   - FLL2 Synchroniser 3 */
+       { 0x000001A4, 0x0000 },    /* R420   - FLL2 Synchroniser 4 */
+       { 0x000001A5, 0x0000 },    /* R421   - FLL2 Synchroniser 5 */
+       { 0x000001A6, 0x0000 },    /* R422   - FLL2 Synchroniser 6 */
+       { 0x000001A9, 0x0000 },    /* R425   - FLL2 Spread Spectrum */
+       { 0x000001AA, 0x0004 },    /* R426   - FLL2 GPIO Clock */
+       { 0x00000200, 0x0006 },    /* R512   - Mic Charge Pump 1 */
+       { 0x00000210, 0x0184 },    /* R528   - LDO1 Control 1 */
+       { 0x00000213, 0x0344 },    /* R531   - LDO2 Control 1 */
+       { 0x00000218, 0x01A6 },    /* R536   - Mic Bias Ctrl 1 */
+       { 0x00000219, 0x01A6 },    /* R537   - Mic Bias Ctrl 2 */
+       { 0x0000021A, 0x01A6 },    /* R538   - Mic Bias Ctrl 3 */
+       { 0x00000293, 0x0000 },    /* R659   - Accessory Detect Mode 1 */
+       { 0x0000029B, 0x0020 },    /* R667   - Headphone Detect 1 */
+       { 0x0000029C, 0x0000 },    /* R668   - Headphone Detect 2 */
+       { 0x000002A3, 0x1102 },    /* R675   - Mic Detect 1 */
+       { 0x000002A4, 0x009F },    /* R676   - Mic Detect 2 */
+       { 0x000002C3, 0x0000 },    /* R707   - Mic noise mix control 1 */
+       { 0x000002D3, 0x0000 },    /* R723   - Jack detect analogue */
+       { 0x00000300, 0x0000 },    /* R768   - Input Enables */
+       { 0x00000308, 0x0000 },    /* R776   - Input Rate */
+       { 0x00000309, 0x0022 },    /* R777   - Input Volume Ramp */
+       { 0x00000310, 0x2080 },    /* R784   - IN1L Control */
+       { 0x00000311, 0x0180 },    /* R785   - ADC Digital Volume 1L */
+       { 0x00000312, 0x0000 },    /* R786   - DMIC1L Control */
+       { 0x00000314, 0x0080 },    /* R788   - IN1R Control */
+       { 0x00000315, 0x0180 },    /* R789   - ADC Digital Volume 1R */
+       { 0x00000316, 0x0000 },    /* R790   - DMIC1R Control */
+       { 0x00000318, 0x2080 },    /* R792   - IN2L Control */
+       { 0x00000319, 0x0180 },    /* R793   - ADC Digital Volume 2L */
+       { 0x0000031A, 0x0000 },    /* R794   - DMIC2L Control */
+       { 0x0000031C, 0x0080 },    /* R796   - IN2R Control */
+       { 0x0000031D, 0x0180 },    /* R797   - ADC Digital Volume 2R */
+       { 0x0000031E, 0x0000 },    /* R798   - DMIC2R Control */
+       { 0x00000320, 0x2080 },    /* R800   - IN3L Control */
+       { 0x00000321, 0x0180 },    /* R801   - ADC Digital Volume 3L */
+       { 0x00000322, 0x0000 },    /* R802   - DMIC3L Control */
+       { 0x00000324, 0x0080 },    /* R804   - IN3R Control */
+       { 0x00000325, 0x0180 },    /* R805   - ADC Digital Volume 3R */
+       { 0x00000326, 0x0000 },    /* R806   - DMIC3R Control */
+       { 0x00000328, 0x2000 },    /* R808   - IN4L Control */
+       { 0x00000329, 0x0180 },    /* R809   - ADC Digital Volume 4L */
+       { 0x0000032A, 0x0000 },    /* R810   - DMIC4L Control */
+       { 0x0000032D, 0x0180 },    /* R813   - ADC Digital Volume 4R */
+       { 0x0000032E, 0x0000 },    /* R814   - DMIC4R Control */
+       { 0x00000400, 0x0000 },    /* R1024  - Output Enables 1 */
+       { 0x00000408, 0x0000 },    /* R1032  - Output Rate 1 */
+       { 0x00000409, 0x0022 },    /* R1033  - Output Volume Ramp */
+       { 0x00000410, 0x0080 },    /* R1040  - Output Path Config 1L */
+       { 0x00000411, 0x0180 },    /* R1041  - DAC Digital Volume 1L */
+       { 0x00000412, 0x0080 },    /* R1042  - DAC Volume Limit 1L */
+       { 0x00000413, 0x0001 },    /* R1043  - Noise Gate Select 1L */
+       { 0x00000414, 0x0080 },    /* R1044  - Output Path Config 1R */
+       { 0x00000415, 0x0180 },    /* R1045  - DAC Digital Volume 1R */
+       { 0x00000416, 0x0080 },    /* R1046  - DAC Volume Limit 1R */
+       { 0x00000417, 0x0002 },    /* R1047  - Noise Gate Select 1R */
+       { 0x00000418, 0x0080 },    /* R1048  - Output Path Config 2L */
+       { 0x00000419, 0x0180 },    /* R1049  - DAC Digital Volume 2L */
+       { 0x0000041A, 0x0080 },    /* R1050  - DAC Volume Limit 2L */
+       { 0x0000041B, 0x0004 },    /* R1051  - Noise Gate Select 2L */
+       { 0x0000041C, 0x0080 },    /* R1052  - Output Path Config 2R */
+       { 0x0000041D, 0x0180 },    /* R1053  - DAC Digital Volume 2R */
+       { 0x0000041E, 0x0080 },    /* R1054  - DAC Volume Limit 2R */
+       { 0x0000041F, 0x0008 },    /* R1055  - Noise Gate Select 2R */
+       { 0x00000420, 0x0080 },    /* R1056  - Output Path Config 3L */
+       { 0x00000421, 0x0180 },    /* R1057  - DAC Digital Volume 3L */
+       { 0x00000422, 0x0080 },    /* R1058  - DAC Volume Limit 3L */
+       { 0x00000423, 0x0010 },    /* R1059  - Noise Gate Select 3L */
+       { 0x00000424, 0x0080 },    /* R1060  - Output Path Config 3R */
+       { 0x00000425, 0x0180 },    /* R1061  - DAC Digital Volume 3R */
+       { 0x00000426, 0x0080 },    /* R1062  - DAC Volume Limit 3R */
+       { 0x00000427, 0x0020 },    /* R1063  - Noise Gate Select 3R */
+       { 0x00000428, 0x0000 },    /* R1064  - Output Path Config 4L */
+       { 0x00000429, 0x0180 },    /* R1065  - DAC Digital Volume 4L */
+       { 0x0000042A, 0x0080 },    /* R1066  - Out Volume 4L */
+       { 0x0000042B, 0x0040 },    /* R1067  - Noise Gate Select 4L */
+       { 0x0000042C, 0x0000 },    /* R1068  - Output Path Config 4R */
+       { 0x0000042D, 0x0180 },    /* R1069  - DAC Digital Volume 4R */
+       { 0x0000042E, 0x0080 },    /* R1070  - Out Volume 4R */
+       { 0x0000042F, 0x0080 },    /* R1071  - Noise Gate Select 4R */
+       { 0x00000430, 0x0000 },    /* R1072  - Output Path Config 5L */
+       { 0x00000431, 0x0180 },    /* R1073  - DAC Digital Volume 5L */
+       { 0x00000432, 0x0080 },    /* R1074  - DAC Volume Limit 5L */
+       { 0x00000433, 0x0100 },    /* R1075  - Noise Gate Select 5L */
+       { 0x00000434, 0x0000 },    /* R1076  - Output Path Config 5R */
+       { 0x00000435, 0x0180 },    /* R1077  - DAC Digital Volume 5R */
+       { 0x00000436, 0x0080 },    /* R1078  - DAC Volume Limit 5R */
+       { 0x00000437, 0x0200 },    /* R1079  - Noise Gate Select 5R */
+       { 0x00000438, 0x0000 },    /* R1080  - Output Path Config 6L */
+       { 0x00000439, 0x0180 },    /* R1081  - DAC Digital Volume 6L */
+       { 0x0000043A, 0x0080 },    /* R1082  - DAC Volume Limit 6L */
+       { 0x0000043B, 0x0400 },    /* R1083  - Noise Gate Select 6L */
+       { 0x0000043C, 0x0000 },    /* R1084  - Output Path Config 6R */
+       { 0x0000043D, 0x0180 },    /* R1085  - DAC Digital Volume 6R */
+       { 0x0000043E, 0x0080 },    /* R1086  - DAC Volume Limit 6R */
+       { 0x0000043F, 0x0800 },    /* R1087  - Noise Gate Select 6R */
+       { 0x00000450, 0x0000 },    /* R1104  - DAC AEC Control 1 */
+       { 0x00000458, 0x0001 },    /* R1112  - Noise Gate Control */
+       { 0x00000480, 0x0040 },    /* R1152  - Class W ANC Threshold 1 */
+       { 0x00000481, 0x0040 },    /* R1153  - Class W ANC Threshold 2 */
+       { 0x00000490, 0x0069 },    /* R1168  - PDM SPK1 CTRL 1 */
+       { 0x00000491, 0x0000 },    /* R1169  - PDM SPK1 CTRL 2 */
+       { 0x00000492, 0x0069 },    /* R1170  - PDM SPK2 CTRL 1 */
+       { 0x00000493, 0x0000 },    /* R1171  - PDM SPK2 CTRL 2 */
+       { 0x00000500, 0x000C },    /* R1280  - AIF1 BCLK Ctrl */
+       { 0x00000501, 0x0008 },    /* R1281  - AIF1 Tx Pin Ctrl */
+       { 0x00000502, 0x0000 },    /* R1282  - AIF1 Rx Pin Ctrl */
+       { 0x00000503, 0x0000 },    /* R1283  - AIF1 Rate Ctrl */
+       { 0x00000504, 0x0000 },    /* R1284  - AIF1 Format */
+       { 0x00000505, 0x0040 },    /* R1285  - AIF1 Tx BCLK Rate */
+       { 0x00000506, 0x0040 },    /* R1286  - AIF1 Rx BCLK Rate */
+       { 0x00000507, 0x1818 },    /* R1287  - AIF1 Frame Ctrl 1 */
+       { 0x00000508, 0x1818 },    /* R1288  - AIF1 Frame Ctrl 2 */
+       { 0x00000509, 0x0000 },    /* R1289  - AIF1 Frame Ctrl 3 */
+       { 0x0000050A, 0x0001 },    /* R1290  - AIF1 Frame Ctrl 4 */
+       { 0x0000050B, 0x0002 },    /* R1291  - AIF1 Frame Ctrl 5 */
+       { 0x0000050C, 0x0003 },    /* R1292  - AIF1 Frame Ctrl 6 */
+       { 0x0000050D, 0x0004 },    /* R1293  - AIF1 Frame Ctrl 7 */
+       { 0x0000050E, 0x0005 },    /* R1294  - AIF1 Frame Ctrl 8 */
+       { 0x0000050F, 0x0006 },    /* R1295  - AIF1 Frame Ctrl 9 */
+       { 0x00000510, 0x0007 },    /* R1296  - AIF1 Frame Ctrl 10 */
+       { 0x00000511, 0x0000 },    /* R1297  - AIF1 Frame Ctrl 11 */
+       { 0x00000512, 0x0001 },    /* R1298  - AIF1 Frame Ctrl 12 */
+       { 0x00000513, 0x0002 },    /* R1299  - AIF1 Frame Ctrl 13 */
+       { 0x00000514, 0x0003 },    /* R1300  - AIF1 Frame Ctrl 14 */
+       { 0x00000515, 0x0004 },    /* R1301  - AIF1 Frame Ctrl 15 */
+       { 0x00000516, 0x0005 },    /* R1302  - AIF1 Frame Ctrl 16 */
+       { 0x00000517, 0x0006 },    /* R1303  - AIF1 Frame Ctrl 17 */
+       { 0x00000518, 0x0007 },    /* R1304  - AIF1 Frame Ctrl 18 */
+       { 0x00000519, 0x0000 },    /* R1305  - AIF1 Tx Enables */
+       { 0x0000051A, 0x0000 },    /* R1306  - AIF1 Rx Enables */
+       { 0x00000540, 0x000C },    /* R1344  - AIF2 BCLK Ctrl */
+       { 0x00000541, 0x0008 },    /* R1345  - AIF2 Tx Pin Ctrl */
+       { 0x00000542, 0x0000 },    /* R1346  - AIF2 Rx Pin Ctrl */
+       { 0x00000543, 0x0000 },    /* R1347  - AIF2 Rate Ctrl */
+       { 0x00000544, 0x0000 },    /* R1348  - AIF2 Format */
+       { 0x00000545, 0x0040 },    /* R1349  - AIF2 Tx BCLK Rate */
+       { 0x00000546, 0x0040 },    /* R1350  - AIF2 Rx BCLK Rate */
+       { 0x00000547, 0x1818 },    /* R1351  - AIF2 Frame Ctrl 1 */
+       { 0x00000548, 0x1818 },    /* R1352  - AIF2 Frame Ctrl 2 */
+       { 0x00000549, 0x0000 },    /* R1353  - AIF2 Frame Ctrl 3 */
+       { 0x0000054A, 0x0001 },    /* R1354  - AIF2 Frame Ctrl 4 */
+       { 0x00000551, 0x0000 },    /* R1361  - AIF2 Frame Ctrl 11 */
+       { 0x00000552, 0x0001 },    /* R1362  - AIF2 Frame Ctrl 12 */
+       { 0x00000559, 0x0000 },    /* R1369  - AIF2 Tx Enables */
+       { 0x0000055A, 0x0000 },    /* R1370  - AIF2 Rx Enables */
+       { 0x00000580, 0x000C },    /* R1408  - AIF3 BCLK Ctrl */
+       { 0x00000581, 0x0008 },    /* R1409  - AIF3 Tx Pin Ctrl */
+       { 0x00000582, 0x0000 },    /* R1410  - AIF3 Rx Pin Ctrl */
+       { 0x00000583, 0x0000 },    /* R1411  - AIF3 Rate Ctrl */
+       { 0x00000584, 0x0000 },    /* R1412  - AIF3 Format */
+       { 0x00000585, 0x0040 },    /* R1413  - AIF3 Tx BCLK Rate */
+       { 0x00000586, 0x0040 },    /* R1414  - AIF3 Rx BCLK Rate */
+       { 0x00000587, 0x1818 },    /* R1415  - AIF3 Frame Ctrl 1 */
+       { 0x00000588, 0x1818 },    /* R1416  - AIF3 Frame Ctrl 2 */
+       { 0x00000589, 0x0000 },    /* R1417  - AIF3 Frame Ctrl 3 */
+       { 0x0000058A, 0x0001 },    /* R1418  - AIF3 Frame Ctrl 4 */
+       { 0x00000591, 0x0000 },    /* R1425  - AIF3 Frame Ctrl 11 */
+       { 0x00000592, 0x0001 },    /* R1426  - AIF3 Frame Ctrl 12 */
+       { 0x00000599, 0x0000 },    /* R1433  - AIF3 Tx Enables */
+       { 0x0000059A, 0x0000 },    /* R1434  - AIF3 Rx Enables */
+       { 0x000005E3, 0x0004 },    /* R1507  - SLIMbus Framer Ref Gear */
+       { 0x000005E5, 0x0000 },    /* R1509  - SLIMbus Rates 1 */
+       { 0x000005E6, 0x0000 },    /* R1510  - SLIMbus Rates 2 */
+       { 0x000005E7, 0x0000 },    /* R1511  - SLIMbus Rates 3 */
+       { 0x000005E8, 0x0000 },    /* R1512  - SLIMbus Rates 4 */
+       { 0x000005E9, 0x0000 },    /* R1513  - SLIMbus Rates 5 */
+       { 0x000005EA, 0x0000 },    /* R1514  - SLIMbus Rates 6 */
+       { 0x000005EB, 0x0000 },    /* R1515  - SLIMbus Rates 7 */
+       { 0x000005EC, 0x0000 },    /* R1516  - SLIMbus Rates 8 */
+       { 0x000005F5, 0x0000 },    /* R1525  - SLIMbus RX Channel Enable */
+       { 0x000005F6, 0x0000 },    /* R1526  - SLIMbus TX Channel Enable */
+       { 0x00000640, 0x0000 },    /* R1600  - PWM1MIX Input 1 Source */
+       { 0x00000641, 0x0080 },    /* R1601  - PWM1MIX Input 1 Volume */
+       { 0x00000642, 0x0000 },    /* R1602  - PWM1MIX Input 2 Source */
+       { 0x00000643, 0x0080 },    /* R1603  - PWM1MIX Input 2 Volume */
+       { 0x00000644, 0x0000 },    /* R1604  - PWM1MIX Input 3 Source */
+       { 0x00000645, 0x0080 },    /* R1605  - PWM1MIX Input 3 Volume */
+       { 0x00000646, 0x0000 },    /* R1606  - PWM1MIX Input 4 Source */
+       { 0x00000647, 0x0080 },    /* R1607  - PWM1MIX Input 4 Volume */
+       { 0x00000648, 0x0000 },    /* R1608  - PWM2MIX Input 1 Source */
+       { 0x00000649, 0x0080 },    /* R1609  - PWM2MIX Input 1 Volume */
+       { 0x0000064A, 0x0000 },    /* R1610  - PWM2MIX Input 2 Source */
+       { 0x0000064B, 0x0080 },    /* R1611  - PWM2MIX Input 2 Volume */
+       { 0x0000064C, 0x0000 },    /* R1612  - PWM2MIX Input 3 Source */
+       { 0x0000064D, 0x0080 },    /* R1613  - PWM2MIX Input 3 Volume */
+       { 0x0000064E, 0x0000 },    /* R1614  - PWM2MIX Input 4 Source */
+       { 0x0000064F, 0x0080 },    /* R1615  - PWM2MIX Input 4 Volume */
+       { 0x00000660, 0x0000 },    /* R1632  - MICMIX Input 1 Source */
+       { 0x00000661, 0x0080 },    /* R1633  - MICMIX Input 1 Volume */
+       { 0x00000662, 0x0000 },    /* R1634  - MICMIX Input 2 Source */
+       { 0x00000663, 0x0080 },    /* R1635  - MICMIX Input 2 Volume */
+       { 0x00000664, 0x0000 },    /* R1636  - MICMIX Input 3 Source */
+       { 0x00000665, 0x0080 },    /* R1637  - MICMIX Input 3 Volume */
+       { 0x00000666, 0x0000 },    /* R1638  - MICMIX Input 4 Source */
+       { 0x00000667, 0x0080 },    /* R1639  - MICMIX Input 4 Volume */
+       { 0x00000668, 0x0000 },    /* R1640  - NOISEMIX Input 1 Source */
+       { 0x00000669, 0x0080 },    /* R1641  - NOISEMIX Input 1 Volume */
+       { 0x0000066A, 0x0000 },    /* R1642  - NOISEMIX Input 2 Source */
+       { 0x0000066B, 0x0080 },    /* R1643  - NOISEMIX Input 2 Volume */
+       { 0x0000066C, 0x0000 },    /* R1644  - NOISEMIX Input 3 Source */
+       { 0x0000066D, 0x0080 },    /* R1645  - NOISEMIX Input 3 Volume */
+       { 0x0000066E, 0x0000 },    /* R1646  - NOISEMIX Input 4 Source */
+       { 0x0000066F, 0x0080 },    /* R1647  - NOISEMIX Input 4 Volume */
+       { 0x00000680, 0x0000 },    /* R1664  - OUT1LMIX Input 1 Source */
+       { 0x00000681, 0x0080 },    /* R1665  - OUT1LMIX Input 1 Volume */
+       { 0x00000682, 0x0000 },    /* R1666  - OUT1LMIX Input 2 Source */
+       { 0x00000683, 0x0080 },    /* R1667  - OUT1LMIX Input 2 Volume */
+       { 0x00000684, 0x0000 },    /* R1668  - OUT1LMIX Input 3 Source */
+       { 0x00000685, 0x0080 },    /* R1669  - OUT1LMIX Input 3 Volume */
+       { 0x00000686, 0x0000 },    /* R1670  - OUT1LMIX Input 4 Source */
+       { 0x00000687, 0x0080 },    /* R1671  - OUT1LMIX Input 4 Volume */
+       { 0x00000688, 0x0000 },    /* R1672  - OUT1RMIX Input 1 Source */
+       { 0x00000689, 0x0080 },    /* R1673  - OUT1RMIX Input 1 Volume */
+       { 0x0000068A, 0x0000 },    /* R1674  - OUT1RMIX Input 2 Source */
+       { 0x0000068B, 0x0080 },    /* R1675  - OUT1RMIX Input 2 Volume */
+       { 0x0000068C, 0x0000 },    /* R1676  - OUT1RMIX Input 3 Source */
+       { 0x0000068D, 0x0080 },    /* R1677  - OUT1RMIX Input 3 Volume */
+       { 0x0000068E, 0x0000 },    /* R1678  - OUT1RMIX Input 4 Source */
+       { 0x0000068F, 0x0080 },    /* R1679  - OUT1RMIX Input 4 Volume */
+       { 0x00000690, 0x0000 },    /* R1680  - OUT2LMIX Input 1 Source */
+       { 0x00000691, 0x0080 },    /* R1681  - OUT2LMIX Input 1 Volume */
+       { 0x00000692, 0x0000 },    /* R1682  - OUT2LMIX Input 2 Source */
+       { 0x00000693, 0x0080 },    /* R1683  - OUT2LMIX Input 2 Volume */
+       { 0x00000694, 0x0000 },    /* R1684  - OUT2LMIX Input 3 Source */
+       { 0x00000695, 0x0080 },    /* R1685  - OUT2LMIX Input 3 Volume */
+       { 0x00000696, 0x0000 },    /* R1686  - OUT2LMIX Input 4 Source */
+       { 0x00000697, 0x0080 },    /* R1687  - OUT2LMIX Input 4 Volume */
+       { 0x00000698, 0x0000 },    /* R1688  - OUT2RMIX Input 1 Source */
+       { 0x00000699, 0x0080 },    /* R1689  - OUT2RMIX Input 1 Volume */
+       { 0x0000069A, 0x0000 },    /* R1690  - OUT2RMIX Input 2 Source */
+       { 0x0000069B, 0x0080 },    /* R1691  - OUT2RMIX Input 2 Volume */
+       { 0x0000069C, 0x0000 },    /* R1692  - OUT2RMIX Input 3 Source */
+       { 0x0000069D, 0x0080 },    /* R1693  - OUT2RMIX Input 3 Volume */
+       { 0x0000069E, 0x0000 },    /* R1694  - OUT2RMIX Input 4 Source */
+       { 0x0000069F, 0x0080 },    /* R1695  - OUT2RMIX Input 4 Volume */
+       { 0x000006A0, 0x0000 },    /* R1696  - OUT3LMIX Input 1 Source */
+       { 0x000006A1, 0x0080 },    /* R1697  - OUT3LMIX Input 1 Volume */
+       { 0x000006A2, 0x0000 },    /* R1698  - OUT3LMIX Input 2 Source */
+       { 0x000006A3, 0x0080 },    /* R1699  - OUT3LMIX Input 2 Volume */
+       { 0x000006A4, 0x0000 },    /* R1700  - OUT3LMIX Input 3 Source */
+       { 0x000006A5, 0x0080 },    /* R1701  - OUT3LMIX Input 3 Volume */
+       { 0x000006A6, 0x0000 },    /* R1702  - OUT3LMIX Input 4 Source */
+       { 0x000006A7, 0x0080 },    /* R1703  - OUT3LMIX Input 4 Volume */
+       { 0x000006A8, 0x0000 },    /* R1704  - OUT3RMIX Input 1 Source */
+       { 0x000006A9, 0x0080 },    /* R1705  - OUT3RMIX Input 1 Volume */
+       { 0x000006AA, 0x0000 },    /* R1706  - OUT3RMIX Input 2 Source */
+       { 0x000006AB, 0x0080 },    /* R1707  - OUT3RMIX Input 2 Volume */
+       { 0x000006AC, 0x0000 },    /* R1708  - OUT3RMIX Input 3 Source */
+       { 0x000006AD, 0x0080 },    /* R1709  - OUT3RMIX Input 3 Volume */
+       { 0x000006AE, 0x0000 },    /* R1710  - OUT3RMIX Input 4 Source */
+       { 0x000006AF, 0x0080 },    /* R1711  - OUT3RMIX Input 4 Volume */
+       { 0x000006B0, 0x0000 },    /* R1712  - OUT4LMIX Input 1 Source */
+       { 0x000006B1, 0x0080 },    /* R1713  - OUT4LMIX Input 1 Volume */
+       { 0x000006B2, 0x0000 },    /* R1714  - OUT4LMIX Input 2 Source */
+       { 0x000006B3, 0x0080 },    /* R1715  - OUT4LMIX Input 2 Volume */
+       { 0x000006B4, 0x0000 },    /* R1716  - OUT4LMIX Input 3 Source */
+       { 0x000006B5, 0x0080 },    /* R1717  - OUT4LMIX Input 3 Volume */
+       { 0x000006B6, 0x0000 },    /* R1718  - OUT4LMIX Input 4 Source */
+       { 0x000006B7, 0x0080 },    /* R1719  - OUT4LMIX Input 4 Volume */
+       { 0x000006B8, 0x0000 },    /* R1720  - OUT4RMIX Input 1 Source */
+       { 0x000006B9, 0x0080 },    /* R1721  - OUT4RMIX Input 1 Volume */
+       { 0x000006BA, 0x0000 },    /* R1722  - OUT4RMIX Input 2 Source */
+       { 0x000006BB, 0x0080 },    /* R1723  - OUT4RMIX Input 2 Volume */
+       { 0x000006BC, 0x0000 },    /* R1724  - OUT4RMIX Input 3 Source */
+       { 0x000006BD, 0x0080 },    /* R1725  - OUT4RMIX Input 3 Volume */
+       { 0x000006BE, 0x0000 },    /* R1726  - OUT4RMIX Input 4 Source */
+       { 0x000006BF, 0x0080 },    /* R1727  - OUT4RMIX Input 4 Volume */
+       { 0x000006C0, 0x0000 },    /* R1728  - OUT5LMIX Input 1 Source */
+       { 0x000006C1, 0x0080 },    /* R1729  - OUT5LMIX Input 1 Volume */
+       { 0x000006C2, 0x0000 },    /* R1730  - OUT5LMIX Input 2 Source */
+       { 0x000006C3, 0x0080 },    /* R1731  - OUT5LMIX Input 2 Volume */
+       { 0x000006C4, 0x0000 },    /* R1732  - OUT5LMIX Input 3 Source */
+       { 0x000006C5, 0x0080 },    /* R1733  - OUT5LMIX Input 3 Volume */
+       { 0x000006C6, 0x0000 },    /* R1734  - OUT5LMIX Input 4 Source */
+       { 0x000006C7, 0x0080 },    /* R1735  - OUT5LMIX Input 4 Volume */
+       { 0x000006C8, 0x0000 },    /* R1736  - OUT5RMIX Input 1 Source */
+       { 0x000006C9, 0x0080 },    /* R1737  - OUT5RMIX Input 1 Volume */
+       { 0x000006CA, 0x0000 },    /* R1738  - OUT5RMIX Input 2 Source */
+       { 0x000006CB, 0x0080 },    /* R1739  - OUT5RMIX Input 2 Volume */
+       { 0x000006CC, 0x0000 },    /* R1740  - OUT5RMIX Input 3 Source */
+       { 0x000006CD, 0x0080 },    /* R1741  - OUT5RMIX Input 3 Volume */
+       { 0x000006CE, 0x0000 },    /* R1742  - OUT5RMIX Input 4 Source */
+       { 0x000006CF, 0x0080 },    /* R1743  - OUT5RMIX Input 4 Volume */
+       { 0x000006D0, 0x0000 },    /* R1744  - OUT6LMIX Input 1 Source */
+       { 0x000006D1, 0x0080 },    /* R1745  - OUT6LMIX Input 1 Volume */
+       { 0x000006D2, 0x0000 },    /* R1746  - OUT6LMIX Input 2 Source */
+       { 0x000006D3, 0x0080 },    /* R1747  - OUT6LMIX Input 2 Volume */
+       { 0x000006D4, 0x0000 },    /* R1748  - OUT6LMIX Input 3 Source */
+       { 0x000006D5, 0x0080 },    /* R1749  - OUT6LMIX Input 3 Volume */
+       { 0x000006D6, 0x0000 },    /* R1750  - OUT6LMIX Input 4 Source */
+       { 0x000006D7, 0x0080 },    /* R1751  - OUT6LMIX Input 4 Volume */
+       { 0x000006D8, 0x0000 },    /* R1752  - OUT6RMIX Input 1 Source */
+       { 0x000006D9, 0x0080 },    /* R1753  - OUT6RMIX Input 1 Volume */
+       { 0x000006DA, 0x0000 },    /* R1754  - OUT6RMIX Input 2 Source */
+       { 0x000006DB, 0x0080 },    /* R1755  - OUT6RMIX Input 2 Volume */
+       { 0x000006DC, 0x0000 },    /* R1756  - OUT6RMIX Input 3 Source */
+       { 0x000006DD, 0x0080 },    /* R1757  - OUT6RMIX Input 3 Volume */
+       { 0x000006DE, 0x0000 },    /* R1758  - OUT6RMIX Input 4 Source */
+       { 0x000006DF, 0x0080 },    /* R1759  - OUT6RMIX Input 4 Volume */
+       { 0x00000700, 0x0000 },    /* R1792  - AIF1TX1MIX Input 1 Source */
+       { 0x00000701, 0x0080 },    /* R1793  - AIF1TX1MIX Input 1 Volume */
+       { 0x00000702, 0x0000 },    /* R1794  - AIF1TX1MIX Input 2 Source */
+       { 0x00000703, 0x0080 },    /* R1795  - AIF1TX1MIX Input 2 Volume */
+       { 0x00000704, 0x0000 },    /* R1796  - AIF1TX1MIX Input 3 Source */
+       { 0x00000705, 0x0080 },    /* R1797  - AIF1TX1MIX Input 3 Volume */
+       { 0x00000706, 0x0000 },    /* R1798  - AIF1TX1MIX Input 4 Source */
+       { 0x00000707, 0x0080 },    /* R1799  - AIF1TX1MIX Input 4 Volume */
+       { 0x00000708, 0x0000 },    /* R1800  - AIF1TX2MIX Input 1 Source */
+       { 0x00000709, 0x0080 },    /* R1801  - AIF1TX2MIX Input 1 Volume */
+       { 0x0000070A, 0x0000 },    /* R1802  - AIF1TX2MIX Input 2 Source */
+       { 0x0000070B, 0x0080 },    /* R1803  - AIF1TX2MIX Input 2 Volume */
+       { 0x0000070C, 0x0000 },    /* R1804  - AIF1TX2MIX Input 3 Source */
+       { 0x0000070D, 0x0080 },    /* R1805  - AIF1TX2MIX Input 3 Volume */
+       { 0x0000070E, 0x0000 },    /* R1806  - AIF1TX2MIX Input 4 Source */
+       { 0x0000070F, 0x0080 },    /* R1807  - AIF1TX2MIX Input 4 Volume */
+       { 0x00000710, 0x0000 },    /* R1808  - AIF1TX3MIX Input 1 Source */
+       { 0x00000711, 0x0080 },    /* R1809  - AIF1TX3MIX Input 1 Volume */
+       { 0x00000712, 0x0000 },    /* R1810  - AIF1TX3MIX Input 2 Source */
+       { 0x00000713, 0x0080 },    /* R1811  - AIF1TX3MIX Input 2 Volume */
+       { 0x00000714, 0x0000 },    /* R1812  - AIF1TX3MIX Input 3 Source */
+       { 0x00000715, 0x0080 },    /* R1813  - AIF1TX3MIX Input 3 Volume */
+       { 0x00000716, 0x0000 },    /* R1814  - AIF1TX3MIX Input 4 Source */
+       { 0x00000717, 0x0080 },    /* R1815  - AIF1TX3MIX Input 4 Volume */
+       { 0x00000718, 0x0000 },    /* R1816  - AIF1TX4MIX Input 1 Source */
+       { 0x00000719, 0x0080 },    /* R1817  - AIF1TX4MIX Input 1 Volume */
+       { 0x0000071A, 0x0000 },    /* R1818  - AIF1TX4MIX Input 2 Source */
+       { 0x0000071B, 0x0080 },    /* R1819  - AIF1TX4MIX Input 2 Volume */
+       { 0x0000071C, 0x0000 },    /* R1820  - AIF1TX4MIX Input 3 Source */
+       { 0x0000071D, 0x0080 },    /* R1821  - AIF1TX4MIX Input 3 Volume */
+       { 0x0000071E, 0x0000 },    /* R1822  - AIF1TX4MIX Input 4 Source */
+       { 0x0000071F, 0x0080 },    /* R1823  - AIF1TX4MIX Input 4 Volume */
+       { 0x00000720, 0x0000 },    /* R1824  - AIF1TX5MIX Input 1 Source */
+       { 0x00000721, 0x0080 },    /* R1825  - AIF1TX5MIX Input 1 Volume */
+       { 0x00000722, 0x0000 },    /* R1826  - AIF1TX5MIX Input 2 Source */
+       { 0x00000723, 0x0080 },    /* R1827  - AIF1TX5MIX Input 2 Volume */
+       { 0x00000724, 0x0000 },    /* R1828  - AIF1TX5MIX Input 3 Source */
+       { 0x00000725, 0x0080 },    /* R1829  - AIF1TX5MIX Input 3 Volume */
+       { 0x00000726, 0x0000 },    /* R1830  - AIF1TX5MIX Input 4 Source */
+       { 0x00000727, 0x0080 },    /* R1831  - AIF1TX5MIX Input 4 Volume */
+       { 0x00000728, 0x0000 },    /* R1832  - AIF1TX6MIX Input 1 Source */
+       { 0x00000729, 0x0080 },    /* R1833  - AIF1TX6MIX Input 1 Volume */
+       { 0x0000072A, 0x0000 },    /* R1834  - AIF1TX6MIX Input 2 Source */
+       { 0x0000072B, 0x0080 },    /* R1835  - AIF1TX6MIX Input 2 Volume */
+       { 0x0000072C, 0x0000 },    /* R1836  - AIF1TX6MIX Input 3 Source */
+       { 0x0000072D, 0x0080 },    /* R1837  - AIF1TX6MIX Input 3 Volume */
+       { 0x0000072E, 0x0000 },    /* R1838  - AIF1TX6MIX Input 4 Source */
+       { 0x0000072F, 0x0080 },    /* R1839  - AIF1TX6MIX Input 4 Volume */
+       { 0x00000730, 0x0000 },    /* R1840  - AIF1TX7MIX Input 1 Source */
+       { 0x00000731, 0x0080 },    /* R1841  - AIF1TX7MIX Input 1 Volume */
+       { 0x00000732, 0x0000 },    /* R1842  - AIF1TX7MIX Input 2 Source */
+       { 0x00000733, 0x0080 },    /* R1843  - AIF1TX7MIX Input 2 Volume */
+       { 0x00000734, 0x0000 },    /* R1844  - AIF1TX7MIX Input 3 Source */
+       { 0x00000735, 0x0080 },    /* R1845  - AIF1TX7MIX Input 3 Volume */
+       { 0x00000736, 0x0000 },    /* R1846  - AIF1TX7MIX Input 4 Source */
+       { 0x00000737, 0x0080 },    /* R1847  - AIF1TX7MIX Input 4 Volume */
+       { 0x00000738, 0x0000 },    /* R1848  - AIF1TX8MIX Input 1 Source */
+       { 0x00000739, 0x0080 },    /* R1849  - AIF1TX8MIX Input 1 Volume */
+       { 0x0000073A, 0x0000 },    /* R1850  - AIF1TX8MIX Input 2 Source */
+       { 0x0000073B, 0x0080 },    /* R1851  - AIF1TX8MIX Input 2 Volume */
+       { 0x0000073C, 0x0000 },    /* R1852  - AIF1TX8MIX Input 3 Source */
+       { 0x0000073D, 0x0080 },    /* R1853  - AIF1TX8MIX Input 3 Volume */
+       { 0x0000073E, 0x0000 },    /* R1854  - AIF1TX8MIX Input 4 Source */
+       { 0x0000073F, 0x0080 },    /* R1855  - AIF1TX8MIX Input 4 Volume */
+       { 0x00000740, 0x0000 },    /* R1856  - AIF2TX1MIX Input 1 Source */
+       { 0x00000741, 0x0080 },    /* R1857  - AIF2TX1MIX Input 1 Volume */
+       { 0x00000742, 0x0000 },    /* R1858  - AIF2TX1MIX Input 2 Source */
+       { 0x00000743, 0x0080 },    /* R1859  - AIF2TX1MIX Input 2 Volume */
+       { 0x00000744, 0x0000 },    /* R1860  - AIF2TX1MIX Input 3 Source */
+       { 0x00000745, 0x0080 },    /* R1861  - AIF2TX1MIX Input 3 Volume */
+       { 0x00000746, 0x0000 },    /* R1862  - AIF2TX1MIX Input 4 Source */
+       { 0x00000747, 0x0080 },    /* R1863  - AIF2TX1MIX Input 4 Volume */
+       { 0x00000748, 0x0000 },    /* R1864  - AIF2TX2MIX Input 1 Source */
+       { 0x00000749, 0x0080 },    /* R1865  - AIF2TX2MIX Input 1 Volume */
+       { 0x0000074A, 0x0000 },    /* R1866  - AIF2TX2MIX Input 2 Source */
+       { 0x0000074B, 0x0080 },    /* R1867  - AIF2TX2MIX Input 2 Volume */
+       { 0x0000074C, 0x0000 },    /* R1868  - AIF2TX2MIX Input 3 Source */
+       { 0x0000074D, 0x0080 },    /* R1869  - AIF2TX2MIX Input 3 Volume */
+       { 0x0000074E, 0x0000 },    /* R1870  - AIF2TX2MIX Input 4 Source */
+       { 0x0000074F, 0x0080 },    /* R1871  - AIF2TX2MIX Input 4 Volume */
+       { 0x00000780, 0x0000 },    /* R1920  - AIF3TX1MIX Input 1 Source */
+       { 0x00000781, 0x0080 },    /* R1921  - AIF3TX1MIX Input 1 Volume */
+       { 0x00000782, 0x0000 },    /* R1922  - AIF3TX1MIX Input 2 Source */
+       { 0x00000783, 0x0080 },    /* R1923  - AIF3TX1MIX Input 2 Volume */
+       { 0x00000784, 0x0000 },    /* R1924  - AIF3TX1MIX Input 3 Source */
+       { 0x00000785, 0x0080 },    /* R1925  - AIF3TX1MIX Input 3 Volume */
+       { 0x00000786, 0x0000 },    /* R1926  - AIF3TX1MIX Input 4 Source */
+       { 0x00000787, 0x0080 },    /* R1927  - AIF3TX1MIX Input 4 Volume */
+       { 0x00000788, 0x0000 },    /* R1928  - AIF3TX2MIX Input 1 Source */
+       { 0x00000789, 0x0080 },    /* R1929  - AIF3TX2MIX Input 1 Volume */
+       { 0x0000078A, 0x0000 },    /* R1930  - AIF3TX2MIX Input 2 Source */
+       { 0x0000078B, 0x0080 },    /* R1931  - AIF3TX2MIX Input 2 Volume */
+       { 0x0000078C, 0x0000 },    /* R1932  - AIF3TX2MIX Input 3 Source */
+       { 0x0000078D, 0x0080 },    /* R1933  - AIF3TX2MIX Input 3 Volume */
+       { 0x0000078E, 0x0000 },    /* R1934  - AIF3TX2MIX Input 4 Source */
+       { 0x0000078F, 0x0080 },    /* R1935  - AIF3TX2MIX Input 4 Volume */
+       { 0x000007C0, 0x0000 },    /* R1984  - SLIMTX1MIX Input 1 Source */
+       { 0x000007C1, 0x0080 },    /* R1985  - SLIMTX1MIX Input 1 Volume */
+       { 0x000007C2, 0x0000 },    /* R1986  - SLIMTX1MIX Input 2 Source */
+       { 0x000007C3, 0x0080 },    /* R1987  - SLIMTX1MIX Input 2 Volume */
+       { 0x000007C4, 0x0000 },    /* R1988  - SLIMTX1MIX Input 3 Source */
+       { 0x000007C5, 0x0080 },    /* R1989  - SLIMTX1MIX Input 3 Volume */
+       { 0x000007C6, 0x0000 },    /* R1990  - SLIMTX1MIX Input 4 Source */
+       { 0x000007C7, 0x0080 },    /* R1991  - SLIMTX1MIX Input 4 Volume */
+       { 0x000007C8, 0x0000 },    /* R1992  - SLIMTX2MIX Input 1 Source */
+       { 0x000007C9, 0x0080 },    /* R1993  - SLIMTX2MIX Input 1 Volume */
+       { 0x000007CA, 0x0000 },    /* R1994  - SLIMTX2MIX Input 2 Source */
+       { 0x000007CB, 0x0080 },    /* R1995  - SLIMTX2MIX Input 2 Volume */
+       { 0x000007CC, 0x0000 },    /* R1996  - SLIMTX2MIX Input 3 Source */
+       { 0x000007CD, 0x0080 },    /* R1997  - SLIMTX2MIX Input 3 Volume */
+       { 0x000007CE, 0x0000 },    /* R1998  - SLIMTX2MIX Input 4 Source */
+       { 0x000007CF, 0x0080 },    /* R1999  - SLIMTX2MIX Input 4 Volume */
+       { 0x000007D0, 0x0000 },    /* R2000  - SLIMTX3MIX Input 1 Source */
+       { 0x000007D1, 0x0080 },    /* R2001  - SLIMTX3MIX Input 1 Volume */
+       { 0x000007D2, 0x0000 },    /* R2002  - SLIMTX3MIX Input 2 Source */
+       { 0x000007D3, 0x0080 },    /* R2003  - SLIMTX3MIX Input 2 Volume */
+       { 0x000007D4, 0x0000 },    /* R2004  - SLIMTX3MIX Input 3 Source */
+       { 0x000007D5, 0x0080 },    /* R2005  - SLIMTX3MIX Input 3 Volume */
+       { 0x000007D6, 0x0000 },    /* R2006  - SLIMTX3MIX Input 4 Source */
+       { 0x000007D7, 0x0080 },    /* R2007  - SLIMTX3MIX Input 4 Volume */
+       { 0x000007D8, 0x0000 },    /* R2008  - SLIMTX4MIX Input 1 Source */
+       { 0x000007D9, 0x0080 },    /* R2009  - SLIMTX4MIX Input 1 Volume */
+       { 0x000007DA, 0x0000 },    /* R2010  - SLIMTX4MIX Input 2 Source */
+       { 0x000007DB, 0x0080 },    /* R2011  - SLIMTX4MIX Input 2 Volume */
+       { 0x000007DC, 0x0000 },    /* R2012  - SLIMTX4MIX Input 3 Source */
+       { 0x000007DD, 0x0080 },    /* R2013  - SLIMTX4MIX Input 3 Volume */
+       { 0x000007DE, 0x0000 },    /* R2014  - SLIMTX4MIX Input 4 Source */
+       { 0x000007DF, 0x0080 },    /* R2015  - SLIMTX4MIX Input 4 Volume */
+       { 0x000007E0, 0x0000 },    /* R2016  - SLIMTX5MIX Input 1 Source */
+       { 0x000007E1, 0x0080 },    /* R2017  - SLIMTX5MIX Input 1 Volume */
+       { 0x000007E2, 0x0000 },    /* R2018  - SLIMTX5MIX Input 2 Source */
+       { 0x000007E3, 0x0080 },    /* R2019  - SLIMTX5MIX Input 2 Volume */
+       { 0x000007E4, 0x0000 },    /* R2020  - SLIMTX5MIX Input 3 Source */
+       { 0x000007E5, 0x0080 },    /* R2021  - SLIMTX5MIX Input 3 Volume */
+       { 0x000007E6, 0x0000 },    /* R2022  - SLIMTX5MIX Input 4 Source */
+       { 0x000007E7, 0x0080 },    /* R2023  - SLIMTX5MIX Input 4 Volume */
+       { 0x000007E8, 0x0000 },    /* R2024  - SLIMTX6MIX Input 1 Source */
+       { 0x000007E9, 0x0080 },    /* R2025  - SLIMTX6MIX Input 1 Volume */
+       { 0x000007EA, 0x0000 },    /* R2026  - SLIMTX6MIX Input 2 Source */
+       { 0x000007EB, 0x0080 },    /* R2027  - SLIMTX6MIX Input 2 Volume */
+       { 0x000007EC, 0x0000 },    /* R2028  - SLIMTX6MIX Input 3 Source */
+       { 0x000007ED, 0x0080 },    /* R2029  - SLIMTX6MIX Input 3 Volume */
+       { 0x000007EE, 0x0000 },    /* R2030  - SLIMTX6MIX Input 4 Source */
+       { 0x000007EF, 0x0080 },    /* R2031  - SLIMTX6MIX Input 4 Volume */
+       { 0x000007F0, 0x0000 },    /* R2032  - SLIMTX7MIX Input 1 Source */
+       { 0x000007F1, 0x0080 },    /* R2033  - SLIMTX7MIX Input 1 Volume */
+       { 0x000007F2, 0x0000 },    /* R2034  - SLIMTX7MIX Input 2 Source */
+       { 0x000007F3, 0x0080 },    /* R2035  - SLIMTX7MIX Input 2 Volume */
+       { 0x000007F4, 0x0000 },    /* R2036  - SLIMTX7MIX Input 3 Source */
+       { 0x000007F5, 0x0080 },    /* R2037  - SLIMTX7MIX Input 3 Volume */
+       { 0x000007F6, 0x0000 },    /* R2038  - SLIMTX7MIX Input 4 Source */
+       { 0x000007F7, 0x0080 },    /* R2039  - SLIMTX7MIX Input 4 Volume */
+       { 0x000007F8, 0x0000 },    /* R2040  - SLIMTX8MIX Input 1 Source */
+       { 0x000007F9, 0x0080 },    /* R2041  - SLIMTX8MIX Input 1 Volume */
+       { 0x000007FA, 0x0000 },    /* R2042  - SLIMTX8MIX Input 2 Source */
+       { 0x000007FB, 0x0080 },    /* R2043  - SLIMTX8MIX Input 2 Volume */
+       { 0x000007FC, 0x0000 },    /* R2044  - SLIMTX8MIX Input 3 Source */
+       { 0x000007FD, 0x0080 },    /* R2045  - SLIMTX8MIX Input 3 Volume */
+       { 0x000007FE, 0x0000 },    /* R2046  - SLIMTX8MIX Input 4 Source */
+       { 0x000007FF, 0x0080 },    /* R2047  - SLIMTX8MIX Input 4 Volume */
+       { 0x00000880, 0x0000 },    /* R2176  - EQ1MIX Input 1 Source */
+       { 0x00000881, 0x0080 },    /* R2177  - EQ1MIX Input 1 Volume */
+       { 0x00000882, 0x0000 },    /* R2178  - EQ1MIX Input 2 Source */
+       { 0x00000883, 0x0080 },    /* R2179  - EQ1MIX Input 2 Volume */
+       { 0x00000884, 0x0000 },    /* R2180  - EQ1MIX Input 3 Source */
+       { 0x00000885, 0x0080 },    /* R2181  - EQ1MIX Input 3 Volume */
+       { 0x00000886, 0x0000 },    /* R2182  - EQ1MIX Input 4 Source */
+       { 0x00000887, 0x0080 },    /* R2183  - EQ1MIX Input 4 Volume */
+       { 0x00000888, 0x0000 },    /* R2184  - EQ2MIX Input 1 Source */
+       { 0x00000889, 0x0080 },    /* R2185  - EQ2MIX Input 1 Volume */
+       { 0x0000088A, 0x0000 },    /* R2186  - EQ2MIX Input 2 Source */
+       { 0x0000088B, 0x0080 },    /* R2187  - EQ2MIX Input 2 Volume */
+       { 0x0000088C, 0x0000 },    /* R2188  - EQ2MIX Input 3 Source */
+       { 0x0000088D, 0x0080 },    /* R2189  - EQ2MIX Input 3 Volume */
+       { 0x0000088E, 0x0000 },    /* R2190  - EQ2MIX Input 4 Source */
+       { 0x0000088F, 0x0080 },    /* R2191  - EQ2MIX Input 4 Volume */
+       { 0x00000890, 0x0000 },    /* R2192  - EQ3MIX Input 1 Source */
+       { 0x00000891, 0x0080 },    /* R2193  - EQ3MIX Input 1 Volume */
+       { 0x00000892, 0x0000 },    /* R2194  - EQ3MIX Input 2 Source */
+       { 0x00000893, 0x0080 },    /* R2195  - EQ3MIX Input 2 Volume */
+       { 0x00000894, 0x0000 },    /* R2196  - EQ3MIX Input 3 Source */
+       { 0x00000895, 0x0080 },    /* R2197  - EQ3MIX Input 3 Volume */
+       { 0x00000896, 0x0000 },    /* R2198  - EQ3MIX Input 4 Source */
+       { 0x00000897, 0x0080 },    /* R2199  - EQ3MIX Input 4 Volume */
+       { 0x00000898, 0x0000 },    /* R2200  - EQ4MIX Input 1 Source */
+       { 0x00000899, 0x0080 },    /* R2201  - EQ4MIX Input 1 Volume */
+       { 0x0000089A, 0x0000 },    /* R2202  - EQ4MIX Input 2 Source */
+       { 0x0000089B, 0x0080 },    /* R2203  - EQ4MIX Input 2 Volume */
+       { 0x0000089C, 0x0000 },    /* R2204  - EQ4MIX Input 3 Source */
+       { 0x0000089D, 0x0080 },    /* R2205  - EQ4MIX Input 3 Volume */
+       { 0x0000089E, 0x0000 },    /* R2206  - EQ4MIX Input 4 Source */
+       { 0x0000089F, 0x0080 },    /* R2207  - EQ4MIX Input 4 Volume */
+       { 0x000008C0, 0x0000 },    /* R2240  - DRC1LMIX Input 1 Source */
+       { 0x000008C1, 0x0080 },    /* R2241  - DRC1LMIX Input 1 Volume */
+       { 0x000008C2, 0x0000 },    /* R2242  - DRC1LMIX Input 2 Source */
+       { 0x000008C3, 0x0080 },    /* R2243  - DRC1LMIX Input 2 Volume */
+       { 0x000008C4, 0x0000 },    /* R2244  - DRC1LMIX Input 3 Source */
+       { 0x000008C5, 0x0080 },    /* R2245  - DRC1LMIX Input 3 Volume */
+       { 0x000008C6, 0x0000 },    /* R2246  - DRC1LMIX Input 4 Source */
+       { 0x000008C7, 0x0080 },    /* R2247  - DRC1LMIX Input 4 Volume */
+       { 0x000008C8, 0x0000 },    /* R2248  - DRC1RMIX Input 1 Source */
+       { 0x000008C9, 0x0080 },    /* R2249  - DRC1RMIX Input 1 Volume */
+       { 0x000008CA, 0x0000 },    /* R2250  - DRC1RMIX Input 2 Source */
+       { 0x000008CB, 0x0080 },    /* R2251  - DRC1RMIX Input 2 Volume */
+       { 0x000008CC, 0x0000 },    /* R2252  - DRC1RMIX Input 3 Source */
+       { 0x000008CD, 0x0080 },    /* R2253  - DRC1RMIX Input 3 Volume */
+       { 0x000008CE, 0x0000 },    /* R2254  - DRC1RMIX Input 4 Source */
+       { 0x000008CF, 0x0080 },    /* R2255  - DRC1RMIX Input 4 Volume */
+       { 0x000008D0, 0x0000 },    /* R2256  - DRC2LMIX Input 1 Source */
+       { 0x000008D1, 0x0080 },    /* R2257  - DRC2LMIX Input 1 Volume */
+       { 0x000008D2, 0x0000 },    /* R2258  - DRC2LMIX Input 2 Source */
+       { 0x000008D3, 0x0080 },    /* R2259  - DRC2LMIX Input 2 Volume */
+       { 0x000008D4, 0x0000 },    /* R2260  - DRC2LMIX Input 3 Source */
+       { 0x000008D5, 0x0080 },    /* R2261  - DRC2LMIX Input 3 Volume */
+       { 0x000008D6, 0x0000 },    /* R2262  - DRC2LMIX Input 4 Source */
+       { 0x000008D7, 0x0080 },    /* R2263  - DRC2LMIX Input 4 Volume */
+       { 0x000008D8, 0x0000 },    /* R2264  - DRC2RMIX Input 1 Source */
+       { 0x000008D9, 0x0080 },    /* R2265  - DRC2RMIX Input 1 Volume */
+       { 0x000008DA, 0x0000 },    /* R2266  - DRC2RMIX Input 2 Source */
+       { 0x000008DB, 0x0080 },    /* R2267  - DRC2RMIX Input 2 Volume */
+       { 0x000008DC, 0x0000 },    /* R2268  - DRC2RMIX Input 3 Source */
+       { 0x000008DD, 0x0080 },    /* R2269  - DRC2RMIX Input 3 Volume */
+       { 0x000008DE, 0x0000 },    /* R2270  - DRC2RMIX Input 4 Source */
+       { 0x000008DF, 0x0080 },    /* R2271  - DRC2RMIX Input 4 Volume */
+       { 0x00000900, 0x0000 },    /* R2304  - HPLP1MIX Input 1 Source */
+       { 0x00000901, 0x0080 },    /* R2305  - HPLP1MIX Input 1 Volume */
+       { 0x00000902, 0x0000 },    /* R2306  - HPLP1MIX Input 2 Source */
+       { 0x00000903, 0x0080 },    /* R2307  - HPLP1MIX Input 2 Volume */
+       { 0x00000904, 0x0000 },    /* R2308  - HPLP1MIX Input 3 Source */
+       { 0x00000905, 0x0080 },    /* R2309  - HPLP1MIX Input 3 Volume */
+       { 0x00000906, 0x0000 },    /* R2310  - HPLP1MIX Input 4 Source */
+       { 0x00000907, 0x0080 },    /* R2311  - HPLP1MIX Input 4 Volume */
+       { 0x00000908, 0x0000 },    /* R2312  - HPLP2MIX Input 1 Source */
+       { 0x00000909, 0x0080 },    /* R2313  - HPLP2MIX Input 1 Volume */
+       { 0x0000090A, 0x0000 },    /* R2314  - HPLP2MIX Input 2 Source */
+       { 0x0000090B, 0x0080 },    /* R2315  - HPLP2MIX Input 2 Volume */
+       { 0x0000090C, 0x0000 },    /* R2316  - HPLP2MIX Input 3 Source */
+       { 0x0000090D, 0x0080 },    /* R2317  - HPLP2MIX Input 3 Volume */
+       { 0x0000090E, 0x0000 },    /* R2318  - HPLP2MIX Input 4 Source */
+       { 0x0000090F, 0x0080 },    /* R2319  - HPLP2MIX Input 4 Volume */
+       { 0x00000910, 0x0000 },    /* R2320  - HPLP3MIX Input 1 Source */
+       { 0x00000911, 0x0080 },    /* R2321  - HPLP3MIX Input 1 Volume */
+       { 0x00000912, 0x0000 },    /* R2322  - HPLP3MIX Input 2 Source */
+       { 0x00000913, 0x0080 },    /* R2323  - HPLP3MIX Input 2 Volume */
+       { 0x00000914, 0x0000 },    /* R2324  - HPLP3MIX Input 3 Source */
+       { 0x00000915, 0x0080 },    /* R2325  - HPLP3MIX Input 3 Volume */
+       { 0x00000916, 0x0000 },    /* R2326  - HPLP3MIX Input 4 Source */
+       { 0x00000917, 0x0080 },    /* R2327  - HPLP3MIX Input 4 Volume */
+       { 0x00000918, 0x0000 },    /* R2328  - HPLP4MIX Input 1 Source */
+       { 0x00000919, 0x0080 },    /* R2329  - HPLP4MIX Input 1 Volume */
+       { 0x0000091A, 0x0000 },    /* R2330  - HPLP4MIX Input 2 Source */
+       { 0x0000091B, 0x0080 },    /* R2331  - HPLP4MIX Input 2 Volume */
+       { 0x0000091C, 0x0000 },    /* R2332  - HPLP4MIX Input 3 Source */
+       { 0x0000091D, 0x0080 },    /* R2333  - HPLP4MIX Input 3 Volume */
+       { 0x0000091E, 0x0000 },    /* R2334  - HPLP4MIX Input 4 Source */
+       { 0x0000091F, 0x0080 },    /* R2335  - HPLP4MIX Input 4 Volume */
+       { 0x00000940, 0x0000 },    /* R2368  - DSP1LMIX Input 1 Source */
+       { 0x00000941, 0x0080 },    /* R2369  - DSP1LMIX Input 1 Volume */
+       { 0x00000942, 0x0000 },    /* R2370  - DSP1LMIX Input 2 Source */
+       { 0x00000943, 0x0080 },    /* R2371  - DSP1LMIX Input 2 Volume */
+       { 0x00000944, 0x0000 },    /* R2372  - DSP1LMIX Input 3 Source */
+       { 0x00000945, 0x0080 },    /* R2373  - DSP1LMIX Input 3 Volume */
+       { 0x00000946, 0x0000 },    /* R2374  - DSP1LMIX Input 4 Source */
+       { 0x00000947, 0x0080 },    /* R2375  - DSP1LMIX Input 4 Volume */
+       { 0x00000948, 0x0000 },    /* R2376  - DSP1RMIX Input 1 Source */
+       { 0x00000949, 0x0080 },    /* R2377  - DSP1RMIX Input 1 Volume */
+       { 0x0000094A, 0x0000 },    /* R2378  - DSP1RMIX Input 2 Source */
+       { 0x0000094B, 0x0080 },    /* R2379  - DSP1RMIX Input 2 Volume */
+       { 0x0000094C, 0x0000 },    /* R2380  - DSP1RMIX Input 3 Source */
+       { 0x0000094D, 0x0080 },    /* R2381  - DSP1RMIX Input 3 Volume */
+       { 0x0000094E, 0x0000 },    /* R2382  - DSP1RMIX Input 4 Source */
+       { 0x0000094F, 0x0080 },    /* R2383  - DSP1RMIX Input 4 Volume */
+       { 0x00000950, 0x0000 },    /* R2384  - DSP1AUX1MIX Input 1 Source */
+       { 0x00000958, 0x0000 },    /* R2392  - DSP1AUX2MIX Input 1 Source */
+       { 0x00000960, 0x0000 },    /* R2400  - DSP1AUX3MIX Input 1 Source */
+       { 0x00000968, 0x0000 },    /* R2408  - DSP1AUX4MIX Input 1 Source */
+       { 0x00000970, 0x0000 },    /* R2416  - DSP1AUX5MIX Input 1 Source */
+       { 0x00000978, 0x0000 },    /* R2424  - DSP1AUX6MIX Input 1 Source */
+       { 0x00000980, 0x0000 },    /* R2432  - DSP2LMIX Input 1 Source */
+       { 0x00000981, 0x0080 },    /* R2433  - DSP2LMIX Input 1 Volume */
+       { 0x00000982, 0x0000 },    /* R2434  - DSP2LMIX Input 2 Source */
+       { 0x00000983, 0x0080 },    /* R2435  - DSP2LMIX Input 2 Volume */
+       { 0x00000984, 0x0000 },    /* R2436  - DSP2LMIX Input 3 Source */
+       { 0x00000985, 0x0080 },    /* R2437  - DSP2LMIX Input 3 Volume */
+       { 0x00000986, 0x0000 },    /* R2438  - DSP2LMIX Input 4 Source */
+       { 0x00000987, 0x0080 },    /* R2439  - DSP2LMIX Input 4 Volume */
+       { 0x00000988, 0x0000 },    /* R2440  - DSP2RMIX Input 1 Source */
+       { 0x00000989, 0x0080 },    /* R2441  - DSP2RMIX Input 1 Volume */
+       { 0x0000098A, 0x0000 },    /* R2442  - DSP2RMIX Input 2 Source */
+       { 0x0000098B, 0x0080 },    /* R2443  - DSP2RMIX Input 2 Volume */
+       { 0x0000098C, 0x0000 },    /* R2444  - DSP2RMIX Input 3 Source */
+       { 0x0000098D, 0x0080 },    /* R2445  - DSP2RMIX Input 3 Volume */
+       { 0x0000098E, 0x0000 },    /* R2446  - DSP2RMIX Input 4 Source */
+       { 0x0000098F, 0x0080 },    /* R2447  - DSP2RMIX Input 4 Volume */
+       { 0x00000990, 0x0000 },    /* R2448  - DSP2AUX1MIX Input 1 Source */
+       { 0x00000998, 0x0000 },    /* R2456  - DSP2AUX2MIX Input 1 Source */
+       { 0x000009A0, 0x0000 },    /* R2464  - DSP2AUX3MIX Input 1 Source */
+       { 0x000009A8, 0x0000 },    /* R2472  - DSP2AUX4MIX Input 1 Source */
+       { 0x000009B0, 0x0000 },    /* R2480  - DSP2AUX5MIX Input 1 Source */
+       { 0x000009B8, 0x0000 },    /* R2488  - DSP2AUX6MIX Input 1 Source */
+       { 0x000009C0, 0x0000 },    /* R2496  - DSP3LMIX Input 1 Source */
+       { 0x000009C1, 0x0080 },    /* R2497  - DSP3LMIX Input 1 Volume */
+       { 0x000009C2, 0x0000 },    /* R2498  - DSP3LMIX Input 2 Source */
+       { 0x000009C3, 0x0080 },    /* R2499  - DSP3LMIX Input 2 Volume */
+       { 0x000009C4, 0x0000 },    /* R2500  - DSP3LMIX Input 3 Source */
+       { 0x000009C5, 0x0080 },    /* R2501  - DSP3LMIX Input 3 Volume */
+       { 0x000009C6, 0x0000 },    /* R2502  - DSP3LMIX Input 4 Source */
+       { 0x000009C7, 0x0080 },    /* R2503  - DSP3LMIX Input 4 Volume */
+       { 0x000009C8, 0x0000 },    /* R2504  - DSP3RMIX Input 1 Source */
+       { 0x000009C9, 0x0080 },    /* R2505  - DSP3RMIX Input 1 Volume */
+       { 0x000009CA, 0x0000 },    /* R2506  - DSP3RMIX Input 2 Source */
+       { 0x000009CB, 0x0080 },    /* R2507  - DSP3RMIX Input 2 Volume */
+       { 0x000009CC, 0x0000 },    /* R2508  - DSP3RMIX Input 3 Source */
+       { 0x000009CD, 0x0080 },    /* R2509  - DSP3RMIX Input 3 Volume */
+       { 0x000009CE, 0x0000 },    /* R2510  - DSP3RMIX Input 4 Source */
+       { 0x000009CF, 0x0080 },    /* R2511  - DSP3RMIX Input 4 Volume */
+       { 0x000009D0, 0x0000 },    /* R2512  - DSP3AUX1MIX Input 1 Source */
+       { 0x000009D8, 0x0000 },    /* R2520  - DSP3AUX2MIX Input 1 Source */
+       { 0x000009E0, 0x0000 },    /* R2528  - DSP3AUX3MIX Input 1 Source */
+       { 0x000009E8, 0x0000 },    /* R2536  - DSP3AUX4MIX Input 1 Source */
+       { 0x000009F0, 0x0000 },    /* R2544  - DSP3AUX5MIX Input 1 Source */
+       { 0x000009F8, 0x0000 },    /* R2552  - DSP3AUX6MIX Input 1 Source */
+       { 0x00000A00, 0x0000 },    /* R2560  - DSP4LMIX Input 1 Source */
+       { 0x00000A01, 0x0080 },    /* R2561  - DSP4LMIX Input 1 Volume */
+       { 0x00000A02, 0x0000 },    /* R2562  - DSP4LMIX Input 2 Source */
+       { 0x00000A03, 0x0080 },    /* R2563  - DSP4LMIX Input 2 Volume */
+       { 0x00000A04, 0x0000 },    /* R2564  - DSP4LMIX Input 3 Source */
+       { 0x00000A05, 0x0080 },    /* R2565  - DSP4LMIX Input 3 Volume */
+       { 0x00000A06, 0x0000 },    /* R2566  - DSP4LMIX Input 4 Source */
+       { 0x00000A07, 0x0080 },    /* R2567  - DSP4LMIX Input 4 Volume */
+       { 0x00000A08, 0x0000 },    /* R2568  - DSP4RMIX Input 1 Source */
+       { 0x00000A09, 0x0080 },    /* R2569  - DSP4RMIX Input 1 Volume */
+       { 0x00000A0A, 0x0000 },    /* R2570  - DSP4RMIX Input 2 Source */
+       { 0x00000A0B, 0x0080 },    /* R2571  - DSP4RMIX Input 2 Volume */
+       { 0x00000A0C, 0x0000 },    /* R2572  - DSP4RMIX Input 3 Source */
+       { 0x00000A0D, 0x0080 },    /* R2573  - DSP4RMIX Input 3 Volume */
+       { 0x00000A0E, 0x0000 },    /* R2574  - DSP4RMIX Input 4 Source */
+       { 0x00000A0F, 0x0080 },    /* R2575  - DSP4RMIX Input 4 Volume */
+       { 0x00000A10, 0x0000 },    /* R2576  - DSP4AUX1MIX Input 1 Source */
+       { 0x00000A18, 0x0000 },    /* R2584  - DSP4AUX2MIX Input 1 Source */
+       { 0x00000A20, 0x0000 },    /* R2592  - DSP4AUX3MIX Input 1 Source */
+       { 0x00000A28, 0x0000 },    /* R2600  - DSP4AUX4MIX Input 1 Source */
+       { 0x00000A30, 0x0000 },    /* R2608  - DSP4AUX5MIX Input 1 Source */
+       { 0x00000A38, 0x0000 },    /* R2616  - DSP4AUX6MIX Input 1 Source */
+       { 0x00000A80, 0x0000 },    /* R2688  - ASRC1LMIX Input 1 Source */
+       { 0x00000A88, 0x0000 },    /* R2696  - ASRC1RMIX Input 1 Source */
+       { 0x00000A90, 0x0000 },    /* R2704  - ASRC2LMIX Input 1 Source */
+       { 0x00000A98, 0x0000 },    /* R2712  - ASRC2RMIX Input 1 Source */
+       { 0x00000B00, 0x0000 },    /* R2816  - ISRC1DEC1MIX Input 1 Source */
+       { 0x00000B08, 0x0000 },    /* R2824  - ISRC1DEC2MIX Input 1 Source */
+       { 0x00000B10, 0x0000 },    /* R2832  - ISRC1DEC3MIX Input 1 Source */
+       { 0x00000B18, 0x0000 },    /* R2840  - ISRC1DEC4MIX Input 1 Source */
+       { 0x00000B20, 0x0000 },    /* R2848  - ISRC1INT1MIX Input 1 Source */
+       { 0x00000B28, 0x0000 },    /* R2856  - ISRC1INT2MIX Input 1 Source */
+       { 0x00000B30, 0x0000 },    /* R2864  - ISRC1INT3MIX Input 1 Source */
+       { 0x00000B38, 0x0000 },    /* R2872  - ISRC1INT4MIX Input 1 Source */
+       { 0x00000B40, 0x0000 },    /* R2880  - ISRC2DEC1MIX Input 1 Source */
+       { 0x00000B48, 0x0000 },    /* R2888  - ISRC2DEC2MIX Input 1 Source */
+       { 0x00000B50, 0x0000 },    /* R2896  - ISRC2DEC3MIX Input 1 Source */
+       { 0x00000B58, 0x0000 },    /* R2904  - ISRC2DEC4MIX Input 1 Source */
+       { 0x00000B60, 0x0000 },    /* R2912  - ISRC2INT1MIX Input 1 Source */
+       { 0x00000B68, 0x0000 },    /* R2920  - ISRC2INT2MIX Input 1 Source */
+       { 0x00000B70, 0x0000 },    /* R2928  - ISRC2INT3MIX Input 1 Source */
+       { 0x00000B78, 0x0000 },    /* R2936  - ISRC2INT4MIX Input 1 Source */
+       { 0x00000B80, 0x0000 },    /* R2944  - ISRC3DEC1MIX Input 1 Source */
+       { 0x00000B88, 0x0000 },    /* R2952  - ISRC3DEC2MIX Input 1 Source */
+       { 0x00000B90, 0x0000 },    /* R2960  - ISRC3DEC3MIX Input 1 Source */
+       { 0x00000B98, 0x0000 },    /* R2968  - ISRC3DEC4MIX Input 1 Source */
+       { 0x00000BA0, 0x0000 },    /* R2976  - ISRC3INT1MIX Input 1 Source */
+       { 0x00000BA8, 0x0000 },    /* R2984  - ISRC3INT2MIX Input 1 Source */
+       { 0x00000BB0, 0x0000 },    /* R2992  - ISRC3INT3MIX Input 1 Source */
+       { 0x00000BB8, 0x0000 },    /* R3000  - ISRC3INT4MIX Input 1 Source */
+       { 0x00000C00, 0xA101 },    /* R3072  - GPIO1 CTRL */
+       { 0x00000C01, 0xA101 },    /* R3073  - GPIO2 CTRL */
+       { 0x00000C02, 0xA101 },    /* R3074  - GPIO3 CTRL */
+       { 0x00000C03, 0xA101 },    /* R3075  - GPIO4 CTRL */
+       { 0x00000C04, 0xA101 },    /* R3076  - GPIO5 CTRL */
+       { 0x00000C0F, 0x0400 },    /* R3087  - IRQ CTRL 1 */
+       { 0x00000C10, 0x1000 },    /* R3088  - GPIO Debounce Config */
+       { 0x00000C20, 0x8002 },    /* R3104  - Misc Pad Ctrl 1 */
+       { 0x00000C21, 0x8001 },    /* R3105  - Misc Pad Ctrl 2 */
+       { 0x00000C22, 0x0000 },    /* R3106  - Misc Pad Ctrl 3 */
+       { 0x00000C23, 0x0000 },    /* R3107  - Misc Pad Ctrl 4 */
+       { 0x00000C24, 0x0000 },    /* R3108  - Misc Pad Ctrl 5 */
+       { 0x00000C25, 0x0000 },    /* R3109  - Misc Pad Ctrl 6 */
+       { 0x00000C30, 0x8282 },    /* R3120  - Misc Pad Ctrl 7 */
+       { 0x00000C31, 0x0082 },    /* R3121  - Misc Pad Ctrl 8 */
+       { 0x00000C32, 0x8282 },    /* R3122  - Misc Pad Ctrl 9 */
+       { 0x00000C33, 0x8282 },    /* R3123  - Misc Pad Ctrl 10 */
+       { 0x00000C34, 0x8282 },    /* R3124  - Misc Pad Ctrl 11 */
+       { 0x00000C35, 0x8282 },    /* R3125  - Misc Pad Ctrl 12 */
+       { 0x00000C36, 0x8282 },    /* R3126  - Misc Pad Ctrl 13 */
+       { 0x00000C37, 0x8282 },    /* R3127  - Misc Pad Ctrl 14 */
+       { 0x00000C38, 0x8282 },    /* R3128  - Misc Pad Ctrl 15 */
+       { 0x00000C39, 0x8282 },    /* R3129  - Misc Pad Ctrl 16 */
+       { 0x00000C3A, 0x8282 },    /* R3130  - Misc Pad Ctrl 17 */
+       { 0x00000C3B, 0x8282 },    /* R3131  - Misc Pad Ctrl 18 */
+       { 0x00000D08, 0xFFFF },    /* R3336  - Interrupt Status 1 Mask */
+       { 0x00000D09, 0xFFFF },    /* R3337  - Interrupt Status 2 Mask */
+       { 0x00000D0A, 0xFFFF },    /* R3338  - Interrupt Status 3 Mask */
+       { 0x00000D0B, 0xFFFF },    /* R3339  - Interrupt Status 4 Mask */
+       { 0x00000D0C, 0xFEFF },    /* R3340  - Interrupt Status 5 Mask */
+       { 0x00000D0F, 0x0000 },    /* R3343  - Interrupt Control */
+       { 0x00000D18, 0xFFFF },    /* R3352  - IRQ2 Status 1 Mask */
+       { 0x00000D19, 0xFFFF },    /* R3353  - IRQ2 Status 2 Mask */
+       { 0x00000D1A, 0xFFFF },    /* R3354  - IRQ2 Status 3 Mask */
+       { 0x00000D1B, 0xFFFF },    /* R3355  - IRQ2 Status 4 Mask */
+       { 0x00000D1C, 0xFFFF },    /* R3356  - IRQ2 Status 5 Mask */
+       { 0x00000D1F, 0x0000 },    /* R3359  - IRQ2 Control */
+       { 0x00000D50, 0x0000 },    /* R3408  - AOD wkup and trig */
+       { 0x00000D53, 0xFFFF },    /* R3411  - AOD IRQ Mask IRQ1 */
+       { 0x00000D54, 0xFFFF },    /* R3412  - AOD IRQ Mask IRQ2 */
+       { 0x00000D56, 0x0000 },    /* R3414  - Jack detect debounce */
+       { 0x00000E00, 0x0000 },    /* R3584  - FX_Ctrl1 */
+       { 0x00000E01, 0x0000 },    /* R3585  - FX_Ctrl2 */
+       { 0x00000E10, 0x6318 },    /* R3600  - EQ1_1 */
+       { 0x00000E11, 0x6300 },    /* R3601  - EQ1_2 */
+       { 0x00000E12, 0x0FC8 },    /* R3602  - EQ1_3 */
+       { 0x00000E13, 0x03FE },    /* R3603  - EQ1_4 */
+       { 0x00000E14, 0x00E0 },    /* R3604  - EQ1_5 */
+       { 0x00000E15, 0x1EC4 },    /* R3605  - EQ1_6 */
+       { 0x00000E16, 0xF136 },    /* R3606  - EQ1_7 */
+       { 0x00000E17, 0x0409 },    /* R3607  - EQ1_8 */
+       { 0x00000E18, 0x04CC },    /* R3608  - EQ1_9 */
+       { 0x00000E19, 0x1C9B },    /* R3609  - EQ1_10 */
+       { 0x00000E1A, 0xF337 },    /* R3610  - EQ1_11 */
+       { 0x00000E1B, 0x040B },    /* R3611  - EQ1_12 */
+       { 0x00000E1C, 0x0CBB },    /* R3612  - EQ1_13 */
+       { 0x00000E1D, 0x16F8 },    /* R3613  - EQ1_14 */
+       { 0x00000E1E, 0xF7D9 },    /* R3614  - EQ1_15 */
+       { 0x00000E1F, 0x040A },    /* R3615  - EQ1_16 */
+       { 0x00000E20, 0x1F14 },    /* R3616  - EQ1_17 */
+       { 0x00000E21, 0x058C },    /* R3617  - EQ1_18 */
+       { 0x00000E22, 0x0563 },    /* R3618  - EQ1_19 */
+       { 0x00000E23, 0x4000 },    /* R3619  - EQ1_20 */
+       { 0x00000E24, 0x0B75 },    /* R3620  - EQ1_21 */
+       { 0x00000E26, 0x6318 },    /* R3622  - EQ2_1 */
+       { 0x00000E27, 0x6300 },    /* R3623  - EQ2_2 */
+       { 0x00000E28, 0x0FC8 },    /* R3624  - EQ2_3 */
+       { 0x00000E29, 0x03FE },    /* R3625  - EQ2_4 */
+       { 0x00000E2A, 0x00E0 },    /* R3626  - EQ2_5 */
+       { 0x00000E2B, 0x1EC4 },    /* R3627  - EQ2_6 */
+       { 0x00000E2C, 0xF136 },    /* R3628  - EQ2_7 */
+       { 0x00000E2D, 0x0409 },    /* R3629  - EQ2_8 */
+       { 0x00000E2E, 0x04CC },    /* R3630  - EQ2_9 */
+       { 0x00000E2F, 0x1C9B },    /* R3631  - EQ2_10 */
+       { 0x00000E30, 0xF337 },    /* R3632  - EQ2_11 */
+       { 0x00000E31, 0x040B },    /* R3633  - EQ2_12 */
+       { 0x00000E32, 0x0CBB },    /* R3634  - EQ2_13 */
+       { 0x00000E33, 0x16F8 },    /* R3635  - EQ2_14 */
+       { 0x00000E34, 0xF7D9 },    /* R3636  - EQ2_15 */
+       { 0x00000E35, 0x040A },    /* R3637  - EQ2_16 */
+       { 0x00000E36, 0x1F14 },    /* R3638  - EQ2_17 */
+       { 0x00000E37, 0x058C },    /* R3639  - EQ2_18 */
+       { 0x00000E38, 0x0563 },    /* R3640  - EQ2_19 */
+       { 0x00000E39, 0x4000 },    /* R3641  - EQ2_20 */
+       { 0x00000E3A, 0x0B75 },    /* R3642  - EQ2_21 */
+       { 0x00000E3C, 0x6318 },    /* R3644  - EQ3_1 */
+       { 0x00000E3D, 0x6300 },    /* R3645  - EQ3_2 */
+       { 0x00000E3E, 0x0FC8 },    /* R3646  - EQ3_3 */
+       { 0x00000E3F, 0x03FE },    /* R3647  - EQ3_4 */
+       { 0x00000E40, 0x00E0 },    /* R3648  - EQ3_5 */
+       { 0x00000E41, 0x1EC4 },    /* R3649  - EQ3_6 */
+       { 0x00000E42, 0xF136 },    /* R3650  - EQ3_7 */
+       { 0x00000E43, 0x0409 },    /* R3651  - EQ3_8 */
+       { 0x00000E44, 0x04CC },    /* R3652  - EQ3_9 */
+       { 0x00000E45, 0x1C9B },    /* R3653  - EQ3_10 */
+       { 0x00000E46, 0xF337 },    /* R3654  - EQ3_11 */
+       { 0x00000E47, 0x040B },    /* R3655  - EQ3_12 */
+       { 0x00000E48, 0x0CBB },    /* R3656  - EQ3_13 */
+       { 0x00000E49, 0x16F8 },    /* R3657  - EQ3_14 */
+       { 0x00000E4A, 0xF7D9 },    /* R3658  - EQ3_15 */
+       { 0x00000E4B, 0x040A },    /* R3659  - EQ3_16 */
+       { 0x00000E4C, 0x1F14 },    /* R3660  - EQ3_17 */
+       { 0x00000E4D, 0x058C },    /* R3661  - EQ3_18 */
+       { 0x00000E4E, 0x0563 },    /* R3662  - EQ3_19 */
+       { 0x00000E4F, 0x4000 },    /* R3663  - EQ3_20 */
+       { 0x00000E50, 0x0B75 },    /* R3664  - EQ3_21 */
+       { 0x00000E52, 0x6318 },    /* R3666  - EQ4_1 */
+       { 0x00000E53, 0x6300 },    /* R3667  - EQ4_2 */
+       { 0x00000E54, 0x0FC8 },    /* R3668  - EQ4_3 */
+       { 0x00000E55, 0x03FE },    /* R3669  - EQ4_4 */
+       { 0x00000E56, 0x00E0 },    /* R3670  - EQ4_5 */
+       { 0x00000E57, 0x1EC4 },    /* R3671  - EQ4_6 */
+       { 0x00000E58, 0xF136 },    /* R3672  - EQ4_7 */
+       { 0x00000E59, 0x0409 },    /* R3673  - EQ4_8 */
+       { 0x00000E5A, 0x04CC },    /* R3674  - EQ4_9 */
+       { 0x00000E5B, 0x1C9B },    /* R3675  - EQ4_10 */
+       { 0x00000E5C, 0xF337 },    /* R3676  - EQ4_11 */
+       { 0x00000E5D, 0x040B },    /* R3677  - EQ4_12 */
+       { 0x00000E5E, 0x0CBB },    /* R3678  - EQ4_13 */
+       { 0x00000E5F, 0x16F8 },    /* R3679  - EQ4_14 */
+       { 0x00000E60, 0xF7D9 },    /* R3680  - EQ4_15 */
+       { 0x00000E61, 0x040A },    /* R3681  - EQ4_16 */
+       { 0x00000E62, 0x1F14 },    /* R3682  - EQ4_17 */
+       { 0x00000E63, 0x058C },    /* R3683  - EQ4_18 */
+       { 0x00000E64, 0x0563 },    /* R3684  - EQ4_19 */
+       { 0x00000E65, 0x4000 },    /* R3685  - EQ4_20 */
+       { 0x00000E66, 0x0B75 },    /* R3686  - EQ4_21 */
+       { 0x00000E80, 0x0018 },    /* R3712  - DRC1 ctrl1 */
+       { 0x00000E81, 0x0933 },    /* R3713  - DRC1 ctrl2 */
+       { 0x00000E82, 0x0018 },    /* R3714  - DRC1 ctrl3 */
+       { 0x00000E83, 0x0000 },    /* R3715  - DRC1 ctrl4 */
+       { 0x00000E84, 0x0000 },    /* R3716  - DRC1 ctrl5 */
+       { 0x00000E89, 0x0018 },    /* R3721  - DRC2 ctrl1 */
+       { 0x00000E8A, 0x0933 },    /* R3722  - DRC2 ctrl2 */
+       { 0x00000E8B, 0x0018 },    /* R3723  - DRC2 ctrl3 */
+       { 0x00000E8C, 0x0000 },    /* R3724  - DRC2 ctrl4 */
+       { 0x00000E8D, 0x0000 },    /* R3725  - DRC2 ctrl5 */
+       { 0x00000EC0, 0x0000 },    /* R3776  - HPLPF1_1 */
+       { 0x00000EC1, 0x0000 },    /* R3777  - HPLPF1_2 */
+       { 0x00000EC4, 0x0000 },    /* R3780  - HPLPF2_1 */
+       { 0x00000EC5, 0x0000 },    /* R3781  - HPLPF2_2 */
+       { 0x00000EC8, 0x0000 },    /* R3784  - HPLPF3_1 */
+       { 0x00000EC9, 0x0000 },    /* R3785  - HPLPF3_2 */
+       { 0x00000ECC, 0x0000 },    /* R3788  - HPLPF4_1 */
+       { 0x00000ECD, 0x0000 },    /* R3789  - HPLPF4_2 */
+       { 0x00000EE0, 0x0000 },    /* R3808  - ASRC_ENABLE */
+       { 0x00000EE2, 0x0000 },    /* R3810  - ASRC_RATE1 */
+       { 0x00000EF0, 0x0000 },    /* R3824  - ISRC 1 CTRL 1 */
+       { 0x00000EF1, 0x0000 },    /* R3825  - ISRC 1 CTRL 2 */
+       { 0x00000EF2, 0x0000 },    /* R3826  - ISRC 1 CTRL 3 */
+       { 0x00000EF3, 0x0000 },    /* R3827  - ISRC 2 CTRL 1 */
+       { 0x00000EF4, 0x0000 },    /* R3828  - ISRC 2 CTRL 2 */
+       { 0x00000EF5, 0x0000 },    /* R3829  - ISRC 2 CTRL 3 */
+       { 0x00000EF6, 0x0000 },    /* R3830  - ISRC 3 CTRL 1 */
+       { 0x00000EF7, 0x0000 },    /* R3831  - ISRC 3 CTRL 2 */
+       { 0x00000EF8, 0x0000 },    /* R3832  - ISRC 3 CTRL 3 */
+       { 0x00000F00, 0x0000 },    /* R3840  - Clock Control */
+       { 0x00000F01, 0x0000 },    /* R3841  - ANC_SRC */
+       { 0x00001100, 0x0010 },    /* R4352  - DSP1 Control 1 */
+       { 0x00001101, 0x0000 },    /* R4353  - DSP1 Clocking 1 */
+       { 0x00001200, 0x0010 },    /* R4608  - DSP2 Control 1 */
+       { 0x00001201, 0x0000 },    /* R4609  - DSP2 Clocking 1 */
+       { 0x00001300, 0x0010 },    /* R4864  - DSP3 Control 1 */
+       { 0x00001301, 0x0000 },    /* R4865  - DSP3 Clocking 1 */
+       { 0x00001400, 0x0010 },    /* R5120  - DSP4 Control 1 */
+       { 0x00001401, 0x0000 },    /* R5121  - DSP4 Clocking 1 */
+       { 0x00001404, 0x0000 },    /* R5124  - DSP4 Status 1 */
+};
+
+static bool wm5110_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case ARIZONA_SOFTWARE_RESET:
+       case ARIZONA_DEVICE_REVISION:
+       case ARIZONA_CTRL_IF_SPI_CFG_1:
+       case ARIZONA_CTRL_IF_I2C1_CFG_1:
+       case ARIZONA_CTRL_IF_I2C2_CFG_1:
+       case ARIZONA_CTRL_IF_I2C1_CFG_2:
+       case ARIZONA_CTRL_IF_I2C2_CFG_2:
+       case ARIZONA_WRITE_SEQUENCER_CTRL_0:
+       case ARIZONA_WRITE_SEQUENCER_CTRL_1:
+       case ARIZONA_WRITE_SEQUENCER_CTRL_2:
+       case ARIZONA_TONE_GENERATOR_1:
+       case ARIZONA_TONE_GENERATOR_2:
+       case ARIZONA_TONE_GENERATOR_3:
+       case ARIZONA_TONE_GENERATOR_4:
+       case ARIZONA_TONE_GENERATOR_5:
+       case ARIZONA_PWM_DRIVE_1:
+       case ARIZONA_PWM_DRIVE_2:
+       case ARIZONA_PWM_DRIVE_3:
+       case ARIZONA_WAKE_CONTROL:
+       case ARIZONA_SEQUENCE_CONTROL:
+       case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1:
+       case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2:
+       case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3:
+       case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4:
+       case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1:
+       case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2:
+       case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3:
+       case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4:
+       case ARIZONA_COMFORT_NOISE_GENERATOR:
+       case ARIZONA_HAPTICS_CONTROL_1:
+       case ARIZONA_HAPTICS_CONTROL_2:
+       case ARIZONA_HAPTICS_PHASE_1_INTENSITY:
+       case ARIZONA_HAPTICS_PHASE_1_DURATION:
+       case ARIZONA_HAPTICS_PHASE_2_INTENSITY:
+       case ARIZONA_HAPTICS_PHASE_2_DURATION:
+       case ARIZONA_HAPTICS_PHASE_3_INTENSITY:
+       case ARIZONA_HAPTICS_PHASE_3_DURATION:
+       case ARIZONA_HAPTICS_STATUS:
+       case ARIZONA_CLOCK_32K_1:
+       case ARIZONA_SYSTEM_CLOCK_1:
+       case ARIZONA_SAMPLE_RATE_1:
+       case ARIZONA_SAMPLE_RATE_2:
+       case ARIZONA_SAMPLE_RATE_3:
+       case ARIZONA_SAMPLE_RATE_1_STATUS:
+       case ARIZONA_SAMPLE_RATE_2_STATUS:
+       case ARIZONA_SAMPLE_RATE_3_STATUS:
+       case ARIZONA_ASYNC_CLOCK_1:
+       case ARIZONA_ASYNC_SAMPLE_RATE_1:
+       case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
+       case ARIZONA_OUTPUT_SYSTEM_CLOCK:
+       case ARIZONA_OUTPUT_ASYNC_CLOCK:
+       case ARIZONA_RATE_ESTIMATOR_1:
+       case ARIZONA_RATE_ESTIMATOR_2:
+       case ARIZONA_RATE_ESTIMATOR_3:
+       case ARIZONA_RATE_ESTIMATOR_4:
+       case ARIZONA_RATE_ESTIMATOR_5:
+       case ARIZONA_FLL1_CONTROL_1:
+       case ARIZONA_FLL1_CONTROL_2:
+       case ARIZONA_FLL1_CONTROL_3:
+       case ARIZONA_FLL1_CONTROL_4:
+       case ARIZONA_FLL1_CONTROL_5:
+       case ARIZONA_FLL1_CONTROL_6:
+       case ARIZONA_FLL1_LOOP_FILTER_TEST_1:
+       case ARIZONA_FLL1_NCO_TEST_0:
+       case ARIZONA_FLL1_SYNCHRONISER_1:
+       case ARIZONA_FLL1_SYNCHRONISER_2:
+       case ARIZONA_FLL1_SYNCHRONISER_3:
+       case ARIZONA_FLL1_SYNCHRONISER_4:
+       case ARIZONA_FLL1_SYNCHRONISER_5:
+       case ARIZONA_FLL1_SYNCHRONISER_6:
+       case ARIZONA_FLL1_SPREAD_SPECTRUM:
+       case ARIZONA_FLL1_GPIO_CLOCK:
+       case ARIZONA_FLL2_CONTROL_1:
+       case ARIZONA_FLL2_CONTROL_2:
+       case ARIZONA_FLL2_CONTROL_3:
+       case ARIZONA_FLL2_CONTROL_4:
+       case ARIZONA_FLL2_CONTROL_5:
+       case ARIZONA_FLL2_CONTROL_6:
+       case ARIZONA_FLL2_LOOP_FILTER_TEST_1:
+       case ARIZONA_FLL2_NCO_TEST_0:
+       case ARIZONA_FLL2_SYNCHRONISER_1:
+       case ARIZONA_FLL2_SYNCHRONISER_2:
+       case ARIZONA_FLL2_SYNCHRONISER_3:
+       case ARIZONA_FLL2_SYNCHRONISER_4:
+       case ARIZONA_FLL2_SYNCHRONISER_5:
+       case ARIZONA_FLL2_SYNCHRONISER_6:
+       case ARIZONA_FLL2_SPREAD_SPECTRUM:
+       case ARIZONA_FLL2_GPIO_CLOCK:
+       case ARIZONA_MIC_CHARGE_PUMP_1:
+       case ARIZONA_LDO1_CONTROL_1:
+       case ARIZONA_LDO2_CONTROL_1:
+       case ARIZONA_MIC_BIAS_CTRL_1:
+       case ARIZONA_MIC_BIAS_CTRL_2:
+       case ARIZONA_MIC_BIAS_CTRL_3:
+       case ARIZONA_ACCESSORY_DETECT_MODE_1:
+       case ARIZONA_HEADPHONE_DETECT_1:
+       case ARIZONA_HEADPHONE_DETECT_2:
+       case ARIZONA_MIC_DETECT_1:
+       case ARIZONA_MIC_DETECT_2:
+       case ARIZONA_MIC_DETECT_3:
+       case ARIZONA_MIC_NOISE_MIX_CONTROL_1:
+       case ARIZONA_JACK_DETECT_ANALOGUE:
+       case ARIZONA_INPUT_ENABLES:
+       case ARIZONA_INPUT_ENABLES_STATUS:
+       case ARIZONA_INPUT_RATE:
+       case ARIZONA_INPUT_VOLUME_RAMP:
+       case ARIZONA_IN1L_CONTROL:
+       case ARIZONA_ADC_DIGITAL_VOLUME_1L:
+       case ARIZONA_DMIC1L_CONTROL:
+       case ARIZONA_IN1R_CONTROL:
+       case ARIZONA_ADC_DIGITAL_VOLUME_1R:
+       case ARIZONA_DMIC1R_CONTROL:
+       case ARIZONA_IN2L_CONTROL:
+       case ARIZONA_ADC_DIGITAL_VOLUME_2L:
+       case ARIZONA_DMIC2L_CONTROL:
+       case ARIZONA_IN2R_CONTROL:
+       case ARIZONA_ADC_DIGITAL_VOLUME_2R:
+       case ARIZONA_DMIC2R_CONTROL:
+       case ARIZONA_IN3L_CONTROL:
+       case ARIZONA_ADC_DIGITAL_VOLUME_3L:
+       case ARIZONA_DMIC3L_CONTROL:
+       case ARIZONA_IN3R_CONTROL:
+       case ARIZONA_ADC_DIGITAL_VOLUME_3R:
+       case ARIZONA_DMIC3R_CONTROL:
+       case ARIZONA_IN4L_CONTROL:
+       case ARIZONA_ADC_DIGITAL_VOLUME_4L:
+       case ARIZONA_DMIC4L_CONTROL:
+       case ARIZONA_ADC_DIGITAL_VOLUME_4R:
+       case ARIZONA_DMIC4R_CONTROL:
+       case ARIZONA_OUTPUT_ENABLES_1:
+       case ARIZONA_OUTPUT_STATUS_1:
+       case ARIZONA_RAW_OUTPUT_STATUS_1:
+       case ARIZONA_OUTPUT_RATE_1:
+       case ARIZONA_OUTPUT_VOLUME_RAMP:
+       case ARIZONA_OUTPUT_PATH_CONFIG_1L:
+       case ARIZONA_DAC_DIGITAL_VOLUME_1L:
+       case ARIZONA_DAC_VOLUME_LIMIT_1L:
+       case ARIZONA_NOISE_GATE_SELECT_1L:
+       case ARIZONA_OUTPUT_PATH_CONFIG_1R:
+       case ARIZONA_DAC_DIGITAL_VOLUME_1R:
+       case ARIZONA_DAC_VOLUME_LIMIT_1R:
+       case ARIZONA_NOISE_GATE_SELECT_1R:
+       case ARIZONA_OUTPUT_PATH_CONFIG_2L:
+       case ARIZONA_DAC_DIGITAL_VOLUME_2L:
+       case ARIZONA_DAC_VOLUME_LIMIT_2L:
+       case ARIZONA_NOISE_GATE_SELECT_2L:
+       case ARIZONA_OUTPUT_PATH_CONFIG_2R:
+       case ARIZONA_DAC_DIGITAL_VOLUME_2R:
+       case ARIZONA_DAC_VOLUME_LIMIT_2R:
+       case ARIZONA_NOISE_GATE_SELECT_2R:
+       case ARIZONA_OUTPUT_PATH_CONFIG_3L:
+       case ARIZONA_DAC_DIGITAL_VOLUME_3L:
+       case ARIZONA_DAC_VOLUME_LIMIT_3L:
+       case ARIZONA_NOISE_GATE_SELECT_3L:
+       case ARIZONA_OUTPUT_PATH_CONFIG_3R:
+       case ARIZONA_DAC_DIGITAL_VOLUME_3R:
+       case ARIZONA_DAC_VOLUME_LIMIT_3R:
+       case ARIZONA_NOISE_GATE_SELECT_3R:
+       case ARIZONA_OUTPUT_PATH_CONFIG_4L:
+       case ARIZONA_DAC_DIGITAL_VOLUME_4L:
+       case ARIZONA_OUT_VOLUME_4L:
+       case ARIZONA_NOISE_GATE_SELECT_4L:
+       case ARIZONA_OUTPUT_PATH_CONFIG_4R:
+       case ARIZONA_DAC_DIGITAL_VOLUME_4R:
+       case ARIZONA_OUT_VOLUME_4R:
+       case ARIZONA_NOISE_GATE_SELECT_4R:
+       case ARIZONA_OUTPUT_PATH_CONFIG_5L:
+       case ARIZONA_DAC_DIGITAL_VOLUME_5L:
+       case ARIZONA_DAC_VOLUME_LIMIT_5L:
+       case ARIZONA_NOISE_GATE_SELECT_5L:
+       case ARIZONA_OUTPUT_PATH_CONFIG_5R:
+       case ARIZONA_DAC_DIGITAL_VOLUME_5R:
+       case ARIZONA_DAC_VOLUME_LIMIT_5R:
+       case ARIZONA_NOISE_GATE_SELECT_5R:
+       case ARIZONA_OUTPUT_PATH_CONFIG_6L:
+       case ARIZONA_DAC_DIGITAL_VOLUME_6L:
+       case ARIZONA_DAC_VOLUME_LIMIT_6L:
+       case ARIZONA_NOISE_GATE_SELECT_6L:
+       case ARIZONA_OUTPUT_PATH_CONFIG_6R:
+       case ARIZONA_DAC_DIGITAL_VOLUME_6R:
+       case ARIZONA_DAC_VOLUME_LIMIT_6R:
+       case ARIZONA_NOISE_GATE_SELECT_6R:
+       case ARIZONA_DAC_AEC_CONTROL_1:
+       case ARIZONA_NOISE_GATE_CONTROL:
+       case ARIZONA_PDM_SPK1_CTRL_1:
+       case ARIZONA_PDM_SPK1_CTRL_2:
+       case ARIZONA_PDM_SPK2_CTRL_1:
+       case ARIZONA_PDM_SPK2_CTRL_2:
+       case ARIZONA_AIF1_BCLK_CTRL:
+       case ARIZONA_AIF1_TX_PIN_CTRL:
+       case ARIZONA_AIF1_RX_PIN_CTRL:
+       case ARIZONA_AIF1_RATE_CTRL:
+       case ARIZONA_AIF1_FORMAT:
+       case ARIZONA_AIF1_TX_BCLK_RATE:
+       case ARIZONA_AIF1_RX_BCLK_RATE:
+       case ARIZONA_AIF1_FRAME_CTRL_1:
+       case ARIZONA_AIF1_FRAME_CTRL_2:
+       case ARIZONA_AIF1_FRAME_CTRL_3:
+       case ARIZONA_AIF1_FRAME_CTRL_4:
+       case ARIZONA_AIF1_FRAME_CTRL_5:
+       case ARIZONA_AIF1_FRAME_CTRL_6:
+       case ARIZONA_AIF1_FRAME_CTRL_7:
+       case ARIZONA_AIF1_FRAME_CTRL_8:
+       case ARIZONA_AIF1_FRAME_CTRL_9:
+       case ARIZONA_AIF1_FRAME_CTRL_10:
+       case ARIZONA_AIF1_FRAME_CTRL_11:
+       case ARIZONA_AIF1_FRAME_CTRL_12:
+       case ARIZONA_AIF1_FRAME_CTRL_13:
+       case ARIZONA_AIF1_FRAME_CTRL_14:
+       case ARIZONA_AIF1_FRAME_CTRL_15:
+       case ARIZONA_AIF1_FRAME_CTRL_16:
+       case ARIZONA_AIF1_FRAME_CTRL_17:
+       case ARIZONA_AIF1_FRAME_CTRL_18:
+       case ARIZONA_AIF1_TX_ENABLES:
+       case ARIZONA_AIF1_RX_ENABLES:
+       case ARIZONA_AIF2_BCLK_CTRL:
+       case ARIZONA_AIF2_TX_PIN_CTRL:
+       case ARIZONA_AIF2_RX_PIN_CTRL:
+       case ARIZONA_AIF2_RATE_CTRL:
+       case ARIZONA_AIF2_FORMAT:
+       case ARIZONA_AIF2_TX_BCLK_RATE:
+       case ARIZONA_AIF2_RX_BCLK_RATE:
+       case ARIZONA_AIF2_FRAME_CTRL_1:
+       case ARIZONA_AIF2_FRAME_CTRL_2:
+       case ARIZONA_AIF2_FRAME_CTRL_3:
+       case ARIZONA_AIF2_FRAME_CTRL_4:
+       case ARIZONA_AIF2_FRAME_CTRL_11:
+       case ARIZONA_AIF2_FRAME_CTRL_12:
+       case ARIZONA_AIF2_TX_ENABLES:
+       case ARIZONA_AIF2_RX_ENABLES:
+       case ARIZONA_AIF3_BCLK_CTRL:
+       case ARIZONA_AIF3_TX_PIN_CTRL:
+       case ARIZONA_AIF3_RX_PIN_CTRL:
+       case ARIZONA_AIF3_RATE_CTRL:
+       case ARIZONA_AIF3_FORMAT:
+       case ARIZONA_AIF3_TX_BCLK_RATE:
+       case ARIZONA_AIF3_RX_BCLK_RATE:
+       case ARIZONA_AIF3_FRAME_CTRL_1:
+       case ARIZONA_AIF3_FRAME_CTRL_2:
+       case ARIZONA_AIF3_FRAME_CTRL_3:
+       case ARIZONA_AIF3_FRAME_CTRL_4:
+       case ARIZONA_AIF3_FRAME_CTRL_11:
+       case ARIZONA_AIF3_FRAME_CTRL_12:
+       case ARIZONA_AIF3_TX_ENABLES:
+       case ARIZONA_AIF3_RX_ENABLES:
+       case ARIZONA_SLIMBUS_FRAMER_REF_GEAR:
+       case ARIZONA_SLIMBUS_RATES_1:
+       case ARIZONA_SLIMBUS_RATES_2:
+       case ARIZONA_SLIMBUS_RATES_3:
+       case ARIZONA_SLIMBUS_RATES_4:
+       case ARIZONA_SLIMBUS_RATES_5:
+       case ARIZONA_SLIMBUS_RATES_6:
+       case ARIZONA_SLIMBUS_RATES_7:
+       case ARIZONA_SLIMBUS_RATES_8:
+       case ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE:
+       case ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE:
+       case ARIZONA_SLIMBUS_RX_PORT_STATUS:
+       case ARIZONA_SLIMBUS_TX_PORT_STATUS:
+       case ARIZONA_PWM1MIX_INPUT_1_SOURCE:
+       case ARIZONA_PWM1MIX_INPUT_1_VOLUME:
+       case ARIZONA_PWM1MIX_INPUT_2_SOURCE:
+       case ARIZONA_PWM1MIX_INPUT_2_VOLUME:
+       case ARIZONA_PWM1MIX_INPUT_3_SOURCE:
+       case ARIZONA_PWM1MIX_INPUT_3_VOLUME:
+       case ARIZONA_PWM1MIX_INPUT_4_SOURCE:
+       case ARIZONA_PWM1MIX_INPUT_4_VOLUME:
+       case ARIZONA_PWM2MIX_INPUT_1_SOURCE:
+       case ARIZONA_PWM2MIX_INPUT_1_VOLUME:
+       case ARIZONA_PWM2MIX_INPUT_2_SOURCE:
+       case ARIZONA_PWM2MIX_INPUT_2_VOLUME:
+       case ARIZONA_PWM2MIX_INPUT_3_SOURCE:
+       case ARIZONA_PWM2MIX_INPUT_3_VOLUME:
+       case ARIZONA_PWM2MIX_INPUT_4_SOURCE:
+       case ARIZONA_PWM2MIX_INPUT_4_VOLUME:
+       case ARIZONA_MICMIX_INPUT_1_SOURCE:
+       case ARIZONA_MICMIX_INPUT_1_VOLUME:
+       case ARIZONA_MICMIX_INPUT_2_SOURCE:
+       case ARIZONA_MICMIX_INPUT_2_VOLUME:
+       case ARIZONA_MICMIX_INPUT_3_SOURCE:
+       case ARIZONA_MICMIX_INPUT_3_VOLUME:
+       case ARIZONA_MICMIX_INPUT_4_SOURCE:
+       case ARIZONA_MICMIX_INPUT_4_VOLUME:
+       case ARIZONA_NOISEMIX_INPUT_1_SOURCE:
+       case ARIZONA_NOISEMIX_INPUT_1_VOLUME:
+       case ARIZONA_NOISEMIX_INPUT_2_SOURCE:
+       case ARIZONA_NOISEMIX_INPUT_2_VOLUME:
+       case ARIZONA_NOISEMIX_INPUT_3_SOURCE:
+       case ARIZONA_NOISEMIX_INPUT_3_VOLUME:
+       case ARIZONA_NOISEMIX_INPUT_4_SOURCE:
+       case ARIZONA_NOISEMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT1LMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT1LMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT1LMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT1LMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT1LMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT1LMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT1LMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT1LMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT1RMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT1RMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT1RMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT1RMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT1RMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT1RMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT1RMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT1RMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT2LMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT2LMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT2LMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT2LMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT2LMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT2LMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT2LMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT2LMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT2RMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT2RMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT2RMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT2RMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT2RMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT2RMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT2RMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT2RMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT3LMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT3LMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT3LMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT3LMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT3LMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT3LMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT3LMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT3LMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT3RMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT3RMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT3RMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT3RMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT3RMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT3RMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT3RMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT3RMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT4LMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT4LMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT4LMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT4LMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT4LMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT4LMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT4LMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT4LMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT4RMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT4RMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT4RMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT4RMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT4RMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT4RMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT4RMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT4RMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT5LMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT5LMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT5LMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT5LMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT5LMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT5LMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT5LMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT5LMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT5RMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT5RMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT5RMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT5RMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT5RMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT5RMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT5RMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT5RMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT6LMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT6LMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT6LMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT6LMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT6LMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT6LMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT6LMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT6LMIX_INPUT_4_VOLUME:
+       case ARIZONA_OUT6RMIX_INPUT_1_SOURCE:
+       case ARIZONA_OUT6RMIX_INPUT_1_VOLUME:
+       case ARIZONA_OUT6RMIX_INPUT_2_SOURCE:
+       case ARIZONA_OUT6RMIX_INPUT_2_VOLUME:
+       case ARIZONA_OUT6RMIX_INPUT_3_SOURCE:
+       case ARIZONA_OUT6RMIX_INPUT_3_VOLUME:
+       case ARIZONA_OUT6RMIX_INPUT_4_SOURCE:
+       case ARIZONA_OUT6RMIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX1MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX1MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX1MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX1MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX1MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX1MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX1MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX2MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX2MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX2MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX2MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX2MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX2MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX2MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX3MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX3MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX3MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX3MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX3MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX3MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX3MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX4MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX4MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX4MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX4MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX4MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX4MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX4MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX5MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX5MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX5MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX5MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX5MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX5MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX5MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX6MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX6MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX6MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX6MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX6MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX6MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX6MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX7MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX7MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX7MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX7MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX7MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX7MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX7MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF1TX8MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF1TX8MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF1TX8MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF1TX8MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF1TX8MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF1TX8MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF1TX8MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF2TX1MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF2TX1MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF2TX1MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF2TX1MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF2TX1MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF2TX1MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF2TX1MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF2TX2MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF2TX2MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF2TX2MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF2TX2MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF2TX2MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF2TX2MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF2TX2MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF3TX1MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF3TX1MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF3TX1MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF3TX1MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF3TX1MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF3TX1MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF3TX1MIX_INPUT_4_VOLUME:
+       case ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE:
+       case ARIZONA_AIF3TX2MIX_INPUT_1_VOLUME:
+       case ARIZONA_AIF3TX2MIX_INPUT_2_SOURCE:
+       case ARIZONA_AIF3TX2MIX_INPUT_2_VOLUME:
+       case ARIZONA_AIF3TX2MIX_INPUT_3_SOURCE:
+       case ARIZONA_AIF3TX2MIX_INPUT_3_VOLUME:
+       case ARIZONA_AIF3TX2MIX_INPUT_4_SOURCE:
+       case ARIZONA_AIF3TX2MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX1MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX1MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX1MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX1MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX1MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX1MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX1MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX2MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX2MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX2MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX2MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX2MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX2MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX2MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX3MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX3MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX3MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX3MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX3MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX3MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX3MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX4MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX4MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX4MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX4MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX4MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX4MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX4MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX5MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX5MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX5MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX5MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX5MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX5MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX5MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX6MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX6MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX6MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX6MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX6MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX6MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX6MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX7MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX7MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX7MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX7MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX7MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX7MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX7MIX_INPUT_4_VOLUME:
+       case ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE:
+       case ARIZONA_SLIMTX8MIX_INPUT_1_VOLUME:
+       case ARIZONA_SLIMTX8MIX_INPUT_2_SOURCE:
+       case ARIZONA_SLIMTX8MIX_INPUT_2_VOLUME:
+       case ARIZONA_SLIMTX8MIX_INPUT_3_SOURCE:
+       case ARIZONA_SLIMTX8MIX_INPUT_3_VOLUME:
+       case ARIZONA_SLIMTX8MIX_INPUT_4_SOURCE:
+       case ARIZONA_SLIMTX8MIX_INPUT_4_VOLUME:
+       case ARIZONA_EQ1MIX_INPUT_1_SOURCE:
+       case ARIZONA_EQ1MIX_INPUT_1_VOLUME:
+       case ARIZONA_EQ1MIX_INPUT_2_SOURCE:
+       case ARIZONA_EQ1MIX_INPUT_2_VOLUME:
+       case ARIZONA_EQ1MIX_INPUT_3_SOURCE:
+       case ARIZONA_EQ1MIX_INPUT_3_VOLUME:
+       case ARIZONA_EQ1MIX_INPUT_4_SOURCE:
+       case ARIZONA_EQ1MIX_INPUT_4_VOLUME:
+       case ARIZONA_EQ2MIX_INPUT_1_SOURCE:
+       case ARIZONA_EQ2MIX_INPUT_1_VOLUME:
+       case ARIZONA_EQ2MIX_INPUT_2_SOURCE:
+       case ARIZONA_EQ2MIX_INPUT_2_VOLUME:
+       case ARIZONA_EQ2MIX_INPUT_3_SOURCE:
+       case ARIZONA_EQ2MIX_INPUT_3_VOLUME:
+       case ARIZONA_EQ2MIX_INPUT_4_SOURCE:
+       case ARIZONA_EQ2MIX_INPUT_4_VOLUME:
+       case ARIZONA_EQ3MIX_INPUT_1_SOURCE:
+       case ARIZONA_EQ3MIX_INPUT_1_VOLUME:
+       case ARIZONA_EQ3MIX_INPUT_2_SOURCE:
+       case ARIZONA_EQ3MIX_INPUT_2_VOLUME:
+       case ARIZONA_EQ3MIX_INPUT_3_SOURCE:
+       case ARIZONA_EQ3MIX_INPUT_3_VOLUME:
+       case ARIZONA_EQ3MIX_INPUT_4_SOURCE:
+       case ARIZONA_EQ3MIX_INPUT_4_VOLUME:
+       case ARIZONA_EQ4MIX_INPUT_1_SOURCE:
+       case ARIZONA_EQ4MIX_INPUT_1_VOLUME:
+       case ARIZONA_EQ4MIX_INPUT_2_SOURCE:
+       case ARIZONA_EQ4MIX_INPUT_2_VOLUME:
+       case ARIZONA_EQ4MIX_INPUT_3_SOURCE:
+       case ARIZONA_EQ4MIX_INPUT_3_VOLUME:
+       case ARIZONA_EQ4MIX_INPUT_4_SOURCE:
+       case ARIZONA_EQ4MIX_INPUT_4_VOLUME:
+       case ARIZONA_DRC1LMIX_INPUT_1_SOURCE:
+       case ARIZONA_DRC1LMIX_INPUT_1_VOLUME:
+       case ARIZONA_DRC1LMIX_INPUT_2_SOURCE:
+       case ARIZONA_DRC1LMIX_INPUT_2_VOLUME:
+       case ARIZONA_DRC1LMIX_INPUT_3_SOURCE:
+       case ARIZONA_DRC1LMIX_INPUT_3_VOLUME:
+       case ARIZONA_DRC1LMIX_INPUT_4_SOURCE:
+       case ARIZONA_DRC1LMIX_INPUT_4_VOLUME:
+       case ARIZONA_DRC1RMIX_INPUT_1_SOURCE:
+       case ARIZONA_DRC1RMIX_INPUT_1_VOLUME:
+       case ARIZONA_DRC1RMIX_INPUT_2_SOURCE:
+       case ARIZONA_DRC1RMIX_INPUT_2_VOLUME:
+       case ARIZONA_DRC1RMIX_INPUT_3_SOURCE:
+       case ARIZONA_DRC1RMIX_INPUT_3_VOLUME:
+       case ARIZONA_DRC1RMIX_INPUT_4_SOURCE:
+       case ARIZONA_DRC1RMIX_INPUT_4_VOLUME:
+       case ARIZONA_DRC2LMIX_INPUT_1_SOURCE:
+       case ARIZONA_DRC2LMIX_INPUT_1_VOLUME:
+       case ARIZONA_DRC2LMIX_INPUT_2_SOURCE:
+       case ARIZONA_DRC2LMIX_INPUT_2_VOLUME:
+       case ARIZONA_DRC2LMIX_INPUT_3_SOURCE:
+       case ARIZONA_DRC2LMIX_INPUT_3_VOLUME:
+       case ARIZONA_DRC2LMIX_INPUT_4_SOURCE:
+       case ARIZONA_DRC2LMIX_INPUT_4_VOLUME:
+       case ARIZONA_DRC2RMIX_INPUT_1_SOURCE:
+       case ARIZONA_DRC2RMIX_INPUT_1_VOLUME:
+       case ARIZONA_DRC2RMIX_INPUT_2_SOURCE:
+       case ARIZONA_DRC2RMIX_INPUT_2_VOLUME:
+       case ARIZONA_DRC2RMIX_INPUT_3_SOURCE:
+       case ARIZONA_DRC2RMIX_INPUT_3_VOLUME:
+       case ARIZONA_DRC2RMIX_INPUT_4_SOURCE:
+       case ARIZONA_DRC2RMIX_INPUT_4_VOLUME:
+       case ARIZONA_HPLP1MIX_INPUT_1_SOURCE:
+       case ARIZONA_HPLP1MIX_INPUT_1_VOLUME:
+       case ARIZONA_HPLP1MIX_INPUT_2_SOURCE:
+       case ARIZONA_HPLP1MIX_INPUT_2_VOLUME:
+       case ARIZONA_HPLP1MIX_INPUT_3_SOURCE:
+       case ARIZONA_HPLP1MIX_INPUT_3_VOLUME:
+       case ARIZONA_HPLP1MIX_INPUT_4_SOURCE:
+       case ARIZONA_HPLP1MIX_INPUT_4_VOLUME:
+       case ARIZONA_HPLP2MIX_INPUT_1_SOURCE:
+       case ARIZONA_HPLP2MIX_INPUT_1_VOLUME:
+       case ARIZONA_HPLP2MIX_INPUT_2_SOURCE:
+       case ARIZONA_HPLP2MIX_INPUT_2_VOLUME:
+       case ARIZONA_HPLP2MIX_INPUT_3_SOURCE:
+       case ARIZONA_HPLP2MIX_INPUT_3_VOLUME:
+       case ARIZONA_HPLP2MIX_INPUT_4_SOURCE:
+       case ARIZONA_HPLP2MIX_INPUT_4_VOLUME:
+       case ARIZONA_HPLP3MIX_INPUT_1_SOURCE:
+       case ARIZONA_HPLP3MIX_INPUT_1_VOLUME:
+       case ARIZONA_HPLP3MIX_INPUT_2_SOURCE:
+       case ARIZONA_HPLP3MIX_INPUT_2_VOLUME:
+       case ARIZONA_HPLP3MIX_INPUT_3_SOURCE:
+       case ARIZONA_HPLP3MIX_INPUT_3_VOLUME:
+       case ARIZONA_HPLP3MIX_INPUT_4_SOURCE:
+       case ARIZONA_HPLP3MIX_INPUT_4_VOLUME:
+       case ARIZONA_HPLP4MIX_INPUT_1_SOURCE:
+       case ARIZONA_HPLP4MIX_INPUT_1_VOLUME:
+       case ARIZONA_HPLP4MIX_INPUT_2_SOURCE:
+       case ARIZONA_HPLP4MIX_INPUT_2_VOLUME:
+       case ARIZONA_HPLP4MIX_INPUT_3_SOURCE:
+       case ARIZONA_HPLP4MIX_INPUT_3_VOLUME:
+       case ARIZONA_HPLP4MIX_INPUT_4_SOURCE:
+       case ARIZONA_HPLP4MIX_INPUT_4_VOLUME:
+       case ARIZONA_DSP1LMIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP1LMIX_INPUT_1_VOLUME:
+       case ARIZONA_DSP1LMIX_INPUT_2_SOURCE:
+       case ARIZONA_DSP1LMIX_INPUT_2_VOLUME:
+       case ARIZONA_DSP1LMIX_INPUT_3_SOURCE:
+       case ARIZONA_DSP1LMIX_INPUT_3_VOLUME:
+       case ARIZONA_DSP1LMIX_INPUT_4_SOURCE:
+       case ARIZONA_DSP1LMIX_INPUT_4_VOLUME:
+       case ARIZONA_DSP1RMIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP1RMIX_INPUT_1_VOLUME:
+       case ARIZONA_DSP1RMIX_INPUT_2_SOURCE:
+       case ARIZONA_DSP1RMIX_INPUT_2_VOLUME:
+       case ARIZONA_DSP1RMIX_INPUT_3_SOURCE:
+       case ARIZONA_DSP1RMIX_INPUT_3_VOLUME:
+       case ARIZONA_DSP1RMIX_INPUT_4_SOURCE:
+       case ARIZONA_DSP1RMIX_INPUT_4_VOLUME:
+       case ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP1AUX2MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP1AUX3MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP1AUX4MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP1AUX5MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP1AUX6MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP2LMIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP2LMIX_INPUT_1_VOLUME:
+       case ARIZONA_DSP2LMIX_INPUT_2_SOURCE:
+       case ARIZONA_DSP2LMIX_INPUT_2_VOLUME:
+       case ARIZONA_DSP2LMIX_INPUT_3_SOURCE:
+       case ARIZONA_DSP2LMIX_INPUT_3_VOLUME:
+       case ARIZONA_DSP2LMIX_INPUT_4_SOURCE:
+       case ARIZONA_DSP2LMIX_INPUT_4_VOLUME:
+       case ARIZONA_DSP2RMIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP2RMIX_INPUT_1_VOLUME:
+       case ARIZONA_DSP2RMIX_INPUT_2_SOURCE:
+       case ARIZONA_DSP2RMIX_INPUT_2_VOLUME:
+       case ARIZONA_DSP2RMIX_INPUT_3_SOURCE:
+       case ARIZONA_DSP2RMIX_INPUT_3_VOLUME:
+       case ARIZONA_DSP2RMIX_INPUT_4_SOURCE:
+       case ARIZONA_DSP2RMIX_INPUT_4_VOLUME:
+       case ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP2AUX2MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP2AUX3MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP2AUX4MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP2AUX5MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP2AUX6MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP3LMIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP3LMIX_INPUT_1_VOLUME:
+       case ARIZONA_DSP3LMIX_INPUT_2_SOURCE:
+       case ARIZONA_DSP3LMIX_INPUT_2_VOLUME:
+       case ARIZONA_DSP3LMIX_INPUT_3_SOURCE:
+       case ARIZONA_DSP3LMIX_INPUT_3_VOLUME:
+       case ARIZONA_DSP3LMIX_INPUT_4_SOURCE:
+       case ARIZONA_DSP3LMIX_INPUT_4_VOLUME:
+       case ARIZONA_DSP3RMIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP3RMIX_INPUT_1_VOLUME:
+       case ARIZONA_DSP3RMIX_INPUT_2_SOURCE:
+       case ARIZONA_DSP3RMIX_INPUT_2_VOLUME:
+       case ARIZONA_DSP3RMIX_INPUT_3_SOURCE:
+       case ARIZONA_DSP3RMIX_INPUT_3_VOLUME:
+       case ARIZONA_DSP3RMIX_INPUT_4_SOURCE:
+       case ARIZONA_DSP3RMIX_INPUT_4_VOLUME:
+       case ARIZONA_DSP3AUX1MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP3AUX2MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP3AUX3MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP3AUX4MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP3AUX5MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP3AUX6MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP4LMIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP4LMIX_INPUT_1_VOLUME:
+       case ARIZONA_DSP4LMIX_INPUT_2_SOURCE:
+       case ARIZONA_DSP4LMIX_INPUT_2_VOLUME:
+       case ARIZONA_DSP4LMIX_INPUT_3_SOURCE:
+       case ARIZONA_DSP4LMIX_INPUT_3_VOLUME:
+       case ARIZONA_DSP4LMIX_INPUT_4_SOURCE:
+       case ARIZONA_DSP4LMIX_INPUT_4_VOLUME:
+       case ARIZONA_DSP4RMIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP4RMIX_INPUT_1_VOLUME:
+       case ARIZONA_DSP4RMIX_INPUT_2_SOURCE:
+       case ARIZONA_DSP4RMIX_INPUT_2_VOLUME:
+       case ARIZONA_DSP4RMIX_INPUT_3_SOURCE:
+       case ARIZONA_DSP4RMIX_INPUT_3_VOLUME:
+       case ARIZONA_DSP4RMIX_INPUT_4_SOURCE:
+       case ARIZONA_DSP4RMIX_INPUT_4_VOLUME:
+       case ARIZONA_DSP4AUX1MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP4AUX2MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP4AUX3MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP4AUX4MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP4AUX5MIX_INPUT_1_SOURCE:
+       case ARIZONA_DSP4AUX6MIX_INPUT_1_SOURCE:
+       case ARIZONA_ASRC1LMIX_INPUT_1_SOURCE:
+       case ARIZONA_ASRC1RMIX_INPUT_1_SOURCE:
+       case ARIZONA_ASRC2LMIX_INPUT_1_SOURCE:
+       case ARIZONA_ASRC2RMIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE:
+       case ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE:
+       case ARIZONA_GPIO1_CTRL:
+       case ARIZONA_GPIO2_CTRL:
+       case ARIZONA_GPIO3_CTRL:
+       case ARIZONA_GPIO4_CTRL:
+       case ARIZONA_GPIO5_CTRL:
+       case ARIZONA_IRQ_CTRL_1:
+       case ARIZONA_GPIO_DEBOUNCE_CONFIG:
+       case ARIZONA_MISC_PAD_CTRL_1:
+       case ARIZONA_MISC_PAD_CTRL_2:
+       case ARIZONA_MISC_PAD_CTRL_3:
+       case ARIZONA_MISC_PAD_CTRL_4:
+       case ARIZONA_MISC_PAD_CTRL_5:
+       case ARIZONA_MISC_PAD_CTRL_6:
+       case ARIZONA_MISC_PAD_CTRL_7:
+       case ARIZONA_MISC_PAD_CTRL_8:
+       case ARIZONA_MISC_PAD_CTRL_9:
+       case ARIZONA_MISC_PAD_CTRL_10:
+       case ARIZONA_MISC_PAD_CTRL_11:
+       case ARIZONA_MISC_PAD_CTRL_12:
+       case ARIZONA_MISC_PAD_CTRL_13:
+       case ARIZONA_MISC_PAD_CTRL_14:
+       case ARIZONA_MISC_PAD_CTRL_15:
+       case ARIZONA_MISC_PAD_CTRL_16:
+       case ARIZONA_MISC_PAD_CTRL_17:
+       case ARIZONA_MISC_PAD_CTRL_18:
+       case ARIZONA_INTERRUPT_STATUS_1:
+       case ARIZONA_INTERRUPT_STATUS_2:
+       case ARIZONA_INTERRUPT_STATUS_3:
+       case ARIZONA_INTERRUPT_STATUS_4:
+       case ARIZONA_INTERRUPT_STATUS_5:
+       case ARIZONA_INTERRUPT_STATUS_1_MASK:
+       case ARIZONA_INTERRUPT_STATUS_2_MASK:
+       case ARIZONA_INTERRUPT_STATUS_3_MASK:
+       case ARIZONA_INTERRUPT_STATUS_4_MASK:
+       case ARIZONA_INTERRUPT_STATUS_5_MASK:
+       case ARIZONA_INTERRUPT_CONTROL:
+       case ARIZONA_IRQ2_STATUS_1:
+       case ARIZONA_IRQ2_STATUS_2:
+       case ARIZONA_IRQ2_STATUS_3:
+       case ARIZONA_IRQ2_STATUS_4:
+       case ARIZONA_IRQ2_STATUS_5:
+       case ARIZONA_IRQ2_STATUS_1_MASK:
+       case ARIZONA_IRQ2_STATUS_2_MASK:
+       case ARIZONA_IRQ2_STATUS_3_MASK:
+       case ARIZONA_IRQ2_STATUS_4_MASK:
+       case ARIZONA_IRQ2_STATUS_5_MASK:
+       case ARIZONA_IRQ2_CONTROL:
+       case ARIZONA_INTERRUPT_RAW_STATUS_2:
+       case ARIZONA_INTERRUPT_RAW_STATUS_3:
+       case ARIZONA_INTERRUPT_RAW_STATUS_4:
+       case ARIZONA_INTERRUPT_RAW_STATUS_5:
+       case ARIZONA_INTERRUPT_RAW_STATUS_6:
+       case ARIZONA_INTERRUPT_RAW_STATUS_7:
+       case ARIZONA_INTERRUPT_RAW_STATUS_8:
+       case ARIZONA_IRQ_PIN_STATUS:
+       case ARIZONA_AOD_WKUP_AND_TRIG:
+       case ARIZONA_AOD_IRQ1:
+       case ARIZONA_AOD_IRQ2:
+       case ARIZONA_AOD_IRQ_MASK_IRQ1:
+       case ARIZONA_AOD_IRQ_MASK_IRQ2:
+       case ARIZONA_AOD_IRQ_RAW_STATUS:
+       case ARIZONA_JACK_DETECT_DEBOUNCE:
+       case ARIZONA_FX_CTRL1:
+       case ARIZONA_FX_CTRL2:
+       case ARIZONA_EQ1_1:
+       case ARIZONA_EQ1_2:
+       case ARIZONA_EQ1_3:
+       case ARIZONA_EQ1_4:
+       case ARIZONA_EQ1_5:
+       case ARIZONA_EQ1_6:
+       case ARIZONA_EQ1_7:
+       case ARIZONA_EQ1_8:
+       case ARIZONA_EQ1_9:
+       case ARIZONA_EQ1_10:
+       case ARIZONA_EQ1_11:
+       case ARIZONA_EQ1_12:
+       case ARIZONA_EQ1_13:
+       case ARIZONA_EQ1_14:
+       case ARIZONA_EQ1_15:
+       case ARIZONA_EQ1_16:
+       case ARIZONA_EQ1_17:
+       case ARIZONA_EQ1_18:
+       case ARIZONA_EQ1_19:
+       case ARIZONA_EQ1_20:
+       case ARIZONA_EQ1_21:
+       case ARIZONA_EQ2_1:
+       case ARIZONA_EQ2_2:
+       case ARIZONA_EQ2_3:
+       case ARIZONA_EQ2_4:
+       case ARIZONA_EQ2_5:
+       case ARIZONA_EQ2_6:
+       case ARIZONA_EQ2_7:
+       case ARIZONA_EQ2_8:
+       case ARIZONA_EQ2_9:
+       case ARIZONA_EQ2_10:
+       case ARIZONA_EQ2_11:
+       case ARIZONA_EQ2_12:
+       case ARIZONA_EQ2_13:
+       case ARIZONA_EQ2_14:
+       case ARIZONA_EQ2_15:
+       case ARIZONA_EQ2_16:
+       case ARIZONA_EQ2_17:
+       case ARIZONA_EQ2_18:
+       case ARIZONA_EQ2_19:
+       case ARIZONA_EQ2_20:
+       case ARIZONA_EQ2_21:
+       case ARIZONA_EQ3_1:
+       case ARIZONA_EQ3_2:
+       case ARIZONA_EQ3_3:
+       case ARIZONA_EQ3_4:
+       case ARIZONA_EQ3_5:
+       case ARIZONA_EQ3_6:
+       case ARIZONA_EQ3_7:
+       case ARIZONA_EQ3_8:
+       case ARIZONA_EQ3_9:
+       case ARIZONA_EQ3_10:
+       case ARIZONA_EQ3_11:
+       case ARIZONA_EQ3_12:
+       case ARIZONA_EQ3_13:
+       case ARIZONA_EQ3_14:
+       case ARIZONA_EQ3_15:
+       case ARIZONA_EQ3_16:
+       case ARIZONA_EQ3_17:
+       case ARIZONA_EQ3_18:
+       case ARIZONA_EQ3_19:
+       case ARIZONA_EQ3_20:
+       case ARIZONA_EQ3_21:
+       case ARIZONA_EQ4_1:
+       case ARIZONA_EQ4_2:
+       case ARIZONA_EQ4_3:
+       case ARIZONA_EQ4_4:
+       case ARIZONA_EQ4_5:
+       case ARIZONA_EQ4_6:
+       case ARIZONA_EQ4_7:
+       case ARIZONA_EQ4_8:
+       case ARIZONA_EQ4_9:
+       case ARIZONA_EQ4_10:
+       case ARIZONA_EQ4_11:
+       case ARIZONA_EQ4_12:
+       case ARIZONA_EQ4_13:
+       case ARIZONA_EQ4_14:
+       case ARIZONA_EQ4_15:
+       case ARIZONA_EQ4_16:
+       case ARIZONA_EQ4_17:
+       case ARIZONA_EQ4_18:
+       case ARIZONA_EQ4_19:
+       case ARIZONA_EQ4_20:
+       case ARIZONA_EQ4_21:
+       case ARIZONA_DRC1_CTRL1:
+       case ARIZONA_DRC1_CTRL2:
+       case ARIZONA_DRC1_CTRL3:
+       case ARIZONA_DRC1_CTRL4:
+       case ARIZONA_DRC1_CTRL5:
+       case ARIZONA_DRC2_CTRL1:
+       case ARIZONA_DRC2_CTRL2:
+       case ARIZONA_DRC2_CTRL3:
+       case ARIZONA_DRC2_CTRL4:
+       case ARIZONA_DRC2_CTRL5:
+       case ARIZONA_HPLPF1_1:
+       case ARIZONA_HPLPF1_2:
+       case ARIZONA_HPLPF2_1:
+       case ARIZONA_HPLPF2_2:
+       case ARIZONA_HPLPF3_1:
+       case ARIZONA_HPLPF3_2:
+       case ARIZONA_HPLPF4_1:
+       case ARIZONA_HPLPF4_2:
+       case ARIZONA_ASRC_ENABLE:
+       case ARIZONA_ASRC_STATUS:
+       case ARIZONA_ASRC_RATE1:
+       case ARIZONA_ISRC_1_CTRL_1:
+       case ARIZONA_ISRC_1_CTRL_2:
+       case ARIZONA_ISRC_1_CTRL_3:
+       case ARIZONA_ISRC_2_CTRL_1:
+       case ARIZONA_ISRC_2_CTRL_2:
+       case ARIZONA_ISRC_2_CTRL_3:
+       case ARIZONA_ISRC_3_CTRL_1:
+       case ARIZONA_ISRC_3_CTRL_2:
+       case ARIZONA_ISRC_3_CTRL_3:
+       case ARIZONA_CLOCK_CONTROL:
+       case ARIZONA_ANC_SRC:
+       case ARIZONA_DSP_STATUS:
+       case ARIZONA_DSP1_CONTROL_1:
+       case ARIZONA_DSP1_CLOCKING_1:
+       case ARIZONA_DSP1_STATUS_1:
+       case ARIZONA_DSP1_STATUS_2:
+       case ARIZONA_DSP2_CONTROL_1:
+       case ARIZONA_DSP2_CLOCKING_1:
+       case ARIZONA_DSP2_STATUS_1:
+       case ARIZONA_DSP2_STATUS_2:
+       case ARIZONA_DSP3_CONTROL_1:
+       case ARIZONA_DSP3_CLOCKING_1:
+       case ARIZONA_DSP3_STATUS_1:
+       case ARIZONA_DSP3_STATUS_2:
+       case ARIZONA_DSP4_CONTROL_1:
+       case ARIZONA_DSP4_CLOCKING_1:
+       case ARIZONA_DSP4_STATUS_1:
+       case ARIZONA_DSP4_STATUS_2:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool wm5110_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case ARIZONA_SOFTWARE_RESET:
+       case ARIZONA_DEVICE_REVISION:
+       case ARIZONA_HAPTICS_STATUS:
+       case ARIZONA_SAMPLE_RATE_1_STATUS:
+       case ARIZONA_SAMPLE_RATE_2_STATUS:
+       case ARIZONA_SAMPLE_RATE_3_STATUS:
+       case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
+       case ARIZONA_MIC_DETECT_3:
+       case ARIZONA_HEADPHONE_DETECT_2:
+       case ARIZONA_INPUT_ENABLES_STATUS:
+       case ARIZONA_OUTPUT_STATUS_1:
+       case ARIZONA_RAW_OUTPUT_STATUS_1:
+       case ARIZONA_SLIMBUS_RX_PORT_STATUS:
+       case ARIZONA_SLIMBUS_TX_PORT_STATUS:
+       case ARIZONA_INTERRUPT_STATUS_1:
+       case ARIZONA_INTERRUPT_STATUS_2:
+       case ARIZONA_INTERRUPT_STATUS_3:
+       case ARIZONA_INTERRUPT_STATUS_4:
+       case ARIZONA_INTERRUPT_STATUS_5:
+       case ARIZONA_IRQ2_STATUS_1:
+       case ARIZONA_IRQ2_STATUS_2:
+       case ARIZONA_IRQ2_STATUS_3:
+       case ARIZONA_IRQ2_STATUS_4:
+       case ARIZONA_IRQ2_STATUS_5:
+       case ARIZONA_INTERRUPT_RAW_STATUS_2:
+       case ARIZONA_INTERRUPT_RAW_STATUS_3:
+       case ARIZONA_INTERRUPT_RAW_STATUS_4:
+       case ARIZONA_INTERRUPT_RAW_STATUS_5:
+       case ARIZONA_INTERRUPT_RAW_STATUS_6:
+       case ARIZONA_INTERRUPT_RAW_STATUS_7:
+       case ARIZONA_INTERRUPT_RAW_STATUS_8:
+       case ARIZONA_IRQ_PIN_STATUS:
+       case ARIZONA_AOD_IRQ1:
+       case ARIZONA_AOD_IRQ2:
+       case ARIZONA_ASRC_STATUS:
+       case ARIZONA_DSP_STATUS:
+       case ARIZONA_DSP1_CONTROL_1:
+       case ARIZONA_DSP1_CLOCKING_1:
+       case ARIZONA_DSP1_STATUS_1:
+       case ARIZONA_DSP1_STATUS_2:
+       case ARIZONA_DSP2_STATUS_1:
+       case ARIZONA_DSP2_STATUS_2:
+       case ARIZONA_DSP3_STATUS_1:
+       case ARIZONA_DSP3_STATUS_2:
+       case ARIZONA_DSP4_STATUS_1:
+       case ARIZONA_DSP4_STATUS_2:
+               return true;
+       default:
+               return false;
+       }
+}
+
+const struct regmap_config wm5110_spi_regmap = {
+       .reg_bits = 32,
+       .pad_bits = 16,
+       .val_bits = 16,
+
+       .max_register = ARIZONA_DSP1_STATUS_2,
+       .readable_reg = wm5110_readable_register,
+       .volatile_reg = wm5110_volatile_register,
+
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults = wm5110_reg_default,
+       .num_reg_defaults = ARRAY_SIZE(wm5110_reg_default),
+};
+EXPORT_SYMBOL_GPL(wm5110_spi_regmap);
+
+const struct regmap_config wm5110_i2c_regmap = {
+       .reg_bits = 32,
+       .val_bits = 16,
+
+       .max_register = ARIZONA_DSP1_STATUS_2,
+       .readable_reg = wm5110_readable_register,
+       .volatile_reg = wm5110_volatile_register,
+
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults = wm5110_reg_default,
+       .num_reg_defaults = ARRAY_SIZE(wm5110_reg_default),
+};
+EXPORT_SYMBOL_GPL(wm5110_i2c_regmap);
index f742745ff354ec2bc485d533805f02340a11d2fd..b90f3e06b6c957f6a73aa87e0eb9bbec2f38fa58 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/bcd.h>
 #include <linux/delay.h>
 #include <linux/mfd/core.h>
+#include <linux/random.h>
 
 #include <linux/mfd/wm831x/core.h>
 #include <linux/mfd/wm831x/otp.h>
@@ -66,6 +67,7 @@ static DEVICE_ATTR(unique_id, 0444, wm831x_unique_id_show, NULL);
 
 int wm831x_otp_init(struct wm831x *wm831x)
 {
+       char uuid[WM831X_UNIQUE_ID_LEN];
        int ret;
 
        ret = device_create_file(wm831x->dev, &dev_attr_unique_id);
@@ -73,6 +75,12 @@ int wm831x_otp_init(struct wm831x *wm831x)
                dev_err(wm831x->dev, "Unique ID attribute not created: %d\n",
                        ret);
 
+       ret = wm831x_unique_id_read(wm831x, uuid);
+       if (ret == 0)
+               add_device_randomness(uuid, sizeof(uuid));
+       else
+               dev_err(wm831x->dev, "Failed to read UUID: %d\n", ret);
+
        return ret;
 }
 
index 8a9b11ca076ac3c528e914b52dd78d958b714fae..7c1ae24605d936e51e0ae485c59dcbf9c2f5372b 100644 (file)
@@ -32,9 +32,6 @@
 #include <linux/mfd/wm8350/supply.h>
 #include <linux/mfd/wm8350/wdt.h>
 
-#define WM8350_UNLOCK_KEY              0x0013
-#define WM8350_LOCK_KEY                        0x0000
-
 #define WM8350_CLOCK_CONTROL_1         0x28
 #define WM8350_AIF_TEST                        0x74
 
 /*
  * WM8350 Device IO
  */
-static DEFINE_MUTEX(io_mutex);
 static DEFINE_MUTEX(reg_lock_mutex);
 
-/* Perform a physical read from the device.
- */
-static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs,
-                           u16 *dest)
-{
-       int i, ret;
-       int bytes = num_regs * 2;
-
-       dev_dbg(wm8350->dev, "volatile read\n");
-       ret = regmap_raw_read(wm8350->regmap, reg, dest, bytes);
-
-       for (i = reg; i < reg + num_regs; i++) {
-               /* Cache is CPU endian */
-               dest[i - reg] = be16_to_cpu(dest[i - reg]);
-
-               /* Mask out non-readable bits */
-               dest[i - reg] &= wm8350_reg_io_map[i].readable;
-       }
-
-       dump(num_regs, dest);
-
-       return ret;
-}
-
-static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest)
-{
-       int i;
-       int end = reg + num_regs;
-       int ret = 0;
-       int bytes = num_regs * 2;
-
-       if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
-               dev_err(wm8350->dev, "invalid reg %x\n",
-                       reg + num_regs - 1);
-               return -EINVAL;
-       }
-
-       dev_dbg(wm8350->dev,
-               "%s R%d(0x%2.2x) %d regs\n", __func__, reg, reg, num_regs);
-
-#if WM8350_BUS_DEBUG
-       /* we can _safely_ read any register, but warn if read not supported */
-       for (i = reg; i < end; i++) {
-               if (!wm8350_reg_io_map[i].readable)
-                       dev_warn(wm8350->dev,
-                               "reg R%d is not readable\n", i);
-       }
-#endif
-
-       /* if any volatile registers are required, then read back all */
-       for (i = reg; i < end; i++)
-               if (wm8350_reg_io_map[i].vol)
-                       return wm8350_phys_read(wm8350, reg, num_regs, dest);
-
-       /* no volatiles, then cache is good */
-       dev_dbg(wm8350->dev, "cache read\n");
-       memcpy(dest, &wm8350->reg_cache[reg], bytes);
-       dump(num_regs, dest);
-       return ret;
-}
-
-static inline int is_reg_locked(struct wm8350 *wm8350, u8 reg)
-{
-       if (reg == WM8350_SECURITY ||
-           wm8350->reg_cache[WM8350_SECURITY] == WM8350_UNLOCK_KEY)
-               return 0;
-
-       if ((reg >= WM8350_GPIO_FUNCTION_SELECT_1 &&
-            reg <= WM8350_GPIO_FUNCTION_SELECT_4) ||
-           (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 &&
-            reg <= WM8350_BATTERY_CHARGER_CONTROL_3))
-               return 1;
-       return 0;
-}
-
-static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src)
-{
-       int i;
-       int end = reg + num_regs;
-       int bytes = num_regs * 2;
-
-       if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
-               dev_err(wm8350->dev, "invalid reg %x\n",
-                       reg + num_regs - 1);
-               return -EINVAL;
-       }
-
-       /* it's generally not a good idea to write to RO or locked registers */
-       for (i = reg; i < end; i++) {
-               if (!wm8350_reg_io_map[i].writable) {
-                       dev_err(wm8350->dev,
-                               "attempted write to read only reg R%d\n", i);
-                       return -EINVAL;
-               }
-
-               if (is_reg_locked(wm8350, i)) {
-                       dev_err(wm8350->dev,
-                              "attempted write to locked reg R%d\n", i);
-                       return -EINVAL;
-               }
-
-               src[i - reg] &= wm8350_reg_io_map[i].writable;
-
-               wm8350->reg_cache[i] =
-                       (wm8350->reg_cache[i] & ~wm8350_reg_io_map[i].writable)
-                       | src[i - reg];
-
-               src[i - reg] = cpu_to_be16(src[i - reg]);
-       }
-
-       /* Actually write it out */
-       return regmap_raw_write(wm8350->regmap, reg, src, bytes);
-}
-
 /*
  * Safe read, modify, write methods
  */
 int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask)
 {
-       u16 data;
-       int err;
-
-       mutex_lock(&io_mutex);
-       err = wm8350_read(wm8350, reg, 1, &data);
-       if (err) {
-               dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
-               goto out;
-       }
-
-       data &= ~mask;
-       err = wm8350_write(wm8350, reg, 1, &data);
-       if (err)
-               dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
-out:
-       mutex_unlock(&io_mutex);
-       return err;
+       return regmap_update_bits(wm8350->regmap, reg, mask, 0);
 }
 EXPORT_SYMBOL_GPL(wm8350_clear_bits);
 
 int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask)
 {
-       u16 data;
-       int err;
-
-       mutex_lock(&io_mutex);
-       err = wm8350_read(wm8350, reg, 1, &data);
-       if (err) {
-               dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
-               goto out;
-       }
-
-       data |= mask;
-       err = wm8350_write(wm8350, reg, 1, &data);
-       if (err)
-               dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
-out:
-       mutex_unlock(&io_mutex);
-       return err;
+       return regmap_update_bits(wm8350->regmap, reg, mask, mask);
 }
 EXPORT_SYMBOL_GPL(wm8350_set_bits);
 
 u16 wm8350_reg_read(struct wm8350 *wm8350, int reg)
 {
-       u16 data;
+       unsigned int data;
        int err;
 
-       mutex_lock(&io_mutex);
-       err = wm8350_read(wm8350, reg, 1, &data);
+       err = regmap_read(wm8350->regmap, reg, &data);
        if (err)
                dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
 
-       mutex_unlock(&io_mutex);
        return data;
 }
 EXPORT_SYMBOL_GPL(wm8350_reg_read);
@@ -245,13 +93,11 @@ EXPORT_SYMBOL_GPL(wm8350_reg_read);
 int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val)
 {
        int ret;
-       u16 data = val;
 
-       mutex_lock(&io_mutex);
-       ret = wm8350_write(wm8350, reg, 1, &data);
+       ret = regmap_write(wm8350->regmap, reg, val);
+
        if (ret)
                dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
-       mutex_unlock(&io_mutex);
        return ret;
 }
 EXPORT_SYMBOL_GPL(wm8350_reg_write);
@@ -261,12 +107,11 @@ int wm8350_block_read(struct wm8350 *wm8350, int start_reg, int regs,
 {
        int err = 0;
 
-       mutex_lock(&io_mutex);
-       err = wm8350_read(wm8350, start_reg, regs, dest);
+       err = regmap_bulk_read(wm8350->regmap, start_reg, dest, regs);
        if (err)
                dev_err(wm8350->dev, "block read starting from R%d failed\n",
                        start_reg);
-       mutex_unlock(&io_mutex);
+
        return err;
 }
 EXPORT_SYMBOL_GPL(wm8350_block_read);
@@ -276,12 +121,11 @@ int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs,
 {
        int ret = 0;
 
-       mutex_lock(&io_mutex);
-       ret = wm8350_write(wm8350, start_reg, regs, src);
+       ret = regmap_bulk_write(wm8350->regmap, start_reg, src, regs);
        if (ret)
                dev_err(wm8350->dev, "block write starting at R%d failed\n",
                        start_reg);
-       mutex_unlock(&io_mutex);
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(wm8350_block_write);
@@ -295,15 +139,20 @@ EXPORT_SYMBOL_GPL(wm8350_block_write);
  */
 int wm8350_reg_lock(struct wm8350 *wm8350)
 {
-       u16 key = WM8350_LOCK_KEY;
        int ret;
 
+       mutex_lock(&reg_lock_mutex);
+
        ldbg(__func__);
-       mutex_lock(&io_mutex);
-       ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key);
+
+       ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_LOCK_KEY);
        if (ret)
                dev_err(wm8350->dev, "lock failed\n");
-       mutex_unlock(&io_mutex);
+
+       wm8350->unlocked = false;
+
+       mutex_unlock(&reg_lock_mutex);
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(wm8350_reg_lock);
@@ -319,15 +168,20 @@ EXPORT_SYMBOL_GPL(wm8350_reg_lock);
  */
 int wm8350_reg_unlock(struct wm8350 *wm8350)
 {
-       u16 key = WM8350_UNLOCK_KEY;
        int ret;
 
+       mutex_lock(&reg_lock_mutex);
+
        ldbg(__func__);
-       mutex_lock(&io_mutex);
-       ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key);
+
+       ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_UNLOCK_KEY);
        if (ret)
                dev_err(wm8350->dev, "unlock failed\n");
-       mutex_unlock(&io_mutex);
+
+       wm8350->unlocked = true;
+
+       mutex_unlock(&reg_lock_mutex);
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(wm8350_reg_unlock);
@@ -394,146 +248,6 @@ static irqreturn_t wm8350_auxadc_irq(int irq, void *irq_data)
        return IRQ_HANDLED;
 }
 
-/*
- * Cache is always host endian.
- */
-static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode)
-{
-       int i, ret = 0;
-       u16 value;
-       const u16 *reg_map;
-
-       switch (type) {
-       case 0:
-               switch (mode) {
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
-               case 0:
-                       reg_map = wm8350_mode0_defaults;
-                       break;
-#endif
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1
-               case 1:
-                       reg_map = wm8350_mode1_defaults;
-                       break;
-#endif
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2
-               case 2:
-                       reg_map = wm8350_mode2_defaults;
-                       break;
-#endif
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3
-               case 3:
-                       reg_map = wm8350_mode3_defaults;
-                       break;
-#endif
-               default:
-                       dev_err(wm8350->dev,
-                               "WM8350 configuration mode %d not supported\n",
-                               mode);
-                       return -EINVAL;
-               }
-               break;
-
-       case 1:
-               switch (mode) {
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0
-               case 0:
-                       reg_map = wm8351_mode0_defaults;
-                       break;
-#endif
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1
-               case 1:
-                       reg_map = wm8351_mode1_defaults;
-                       break;
-#endif
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2
-               case 2:
-                       reg_map = wm8351_mode2_defaults;
-                       break;
-#endif
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3
-               case 3:
-                       reg_map = wm8351_mode3_defaults;
-                       break;
-#endif
-               default:
-                       dev_err(wm8350->dev,
-                               "WM8351 configuration mode %d not supported\n",
-                               mode);
-                       return -EINVAL;
-               }
-               break;
-
-       case 2:
-               switch (mode) {
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0
-               case 0:
-                       reg_map = wm8352_mode0_defaults;
-                       break;
-#endif
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1
-               case 1:
-                       reg_map = wm8352_mode1_defaults;
-                       break;
-#endif
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2
-               case 2:
-                       reg_map = wm8352_mode2_defaults;
-                       break;
-#endif
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3
-               case 3:
-                       reg_map = wm8352_mode3_defaults;
-                       break;
-#endif
-               default:
-                       dev_err(wm8350->dev,
-                               "WM8352 configuration mode %d not supported\n",
-                               mode);
-                       return -EINVAL;
-               }
-               break;
-
-       default:
-               dev_err(wm8350->dev,
-                       "WM835x configuration mode %d not supported\n",
-                       mode);
-               return -EINVAL;
-       }
-
-       wm8350->reg_cache =
-               kmalloc(sizeof(u16) * (WM8350_MAX_REGISTER + 1), GFP_KERNEL);
-       if (wm8350->reg_cache == NULL)
-               return -ENOMEM;
-
-       /* Read the initial cache state back from the device - this is
-        * a PMIC so the device many not be in a virgin state and we
-        * can't rely on the silicon values.
-        */
-       ret = regmap_raw_read(wm8350->regmap, 0, wm8350->reg_cache,
-                             sizeof(u16) * (WM8350_MAX_REGISTER + 1));
-       if (ret < 0) {
-               dev_err(wm8350->dev,
-                       "failed to read initial cache values\n");
-               goto out;
-       }
-
-       /* Mask out uncacheable/unreadable bits and the audio. */
-       for (i = 0; i < WM8350_MAX_REGISTER; i++) {
-               if (wm8350_reg_io_map[i].readable &&
-                   (i < WM8350_CLOCK_CONTROL_1 || i > WM8350_AIF_TEST)) {
-                       value = be16_to_cpu(wm8350->reg_cache[i]);
-                       value &= wm8350_reg_io_map[i].readable;
-                       wm8350->reg_cache[i] = value;
-               } else
-                       wm8350->reg_cache[i] = reg_map[i];
-       }
-
-out:
-       kfree(wm8350->reg_cache);
-       return ret;
-}
-
 /*
  * Register a client device.  This is non-fatal since there is no need to
  * fail the entire device init due to a single platform device failing.
@@ -681,18 +395,12 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
                goto err;
        }
 
-       ret = wm8350_create_cache(wm8350, mask_rev, mode);
-       if (ret < 0) {
-               dev_err(wm8350->dev, "Failed to create register cache\n");
-               return ret;
-       }
-
        mutex_init(&wm8350->auxadc_mutex);
        init_completion(&wm8350->auxadc_done);
 
        ret = wm8350_irq_init(wm8350, irq, pdata);
        if (ret < 0)
-               goto err_free;
+               goto err;
 
        if (wm8350->irq_base) {
                ret = request_threaded_irq(wm8350->irq_base +
@@ -730,8 +438,6 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
 
 err_irq:
        wm8350_irq_exit(wm8350);
-err_free:
-       kfree(wm8350->reg_cache);
 err:
        return ret;
 }
@@ -758,8 +464,6 @@ void wm8350_device_exit(struct wm8350 *wm8350)
                free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350);
 
        wm8350_irq_exit(wm8350);
-
-       kfree(wm8350->reg_cache);
 }
 EXPORT_SYMBOL_GPL(wm8350_device_exit);
 
index a68aceb4e48c880fbf7b79e64459762241eeaa29..2e57101c8d3dabfc395f2bf764bddc23a411bbe5 100644 (file)
 #include <linux/regmap.h>
 #include <linux/slab.h>
 
-static const struct regmap_config wm8350_regmap = {
-       .reg_bits = 8,
-       .val_bits = 16,
-};
-
 static int wm8350_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
index 9fd01bf63c510eafab2c1efc987728576aa54f5e..624ff90501cde3c5fdf1e79f026db8cfa321eb37 100644 (file)
@@ -432,11 +432,9 @@ static void wm8350_irq_sync_unlock(struct irq_data *data)
        for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) {
                /* If there's been a change in the mask write it back
                 * to the hardware. */
-               if (wm8350->irq_masks[i] !=
-                   wm8350->reg_cache[WM8350_INT_STATUS_1_MASK + i])
-                       WARN_ON(wm8350_reg_write(wm8350,
-                                        WM8350_INT_STATUS_1_MASK + i,
-                                                wm8350->irq_masks[i]));
+               WARN_ON(regmap_update_bits(wm8350->regmap,
+                                          WM8350_INT_STATUS_1_MASK + i,
+                                          0xffff, wm8350->irq_masks[i]));
        }
 
        mutex_unlock(&wm8350->irq_lock);
index e965139e5cd5ba116369eedd05b72435621b6796..9efc64750fb68d8a323f2a7241d99cf47d781b0f 100644 (file)
 
 #include <linux/mfd/wm8350/core.h>
 
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8350_mode0_defaults[] = {
-       0x17FF,     /* R0   - Reset/ID */
-       0x1000,     /* R1   - ID */
-       0x0000,     /* R2 */
-       0x1002,     /* R3   - System Control 1 */
-       0x0004,     /* R4   - System Control 2 */
-       0x0000,     /* R5   - System Hibernate */
-       0x8A00,     /* R6   - Interface Control */
-       0x0000,     /* R7 */
-       0x8000,     /* R8   - Power mgmt (1) */
-       0x0000,     /* R9   - Power mgmt (2) */
-       0x0000,     /* R10  - Power mgmt (3) */
-       0x2000,     /* R11  - Power mgmt (4) */
-       0x0E00,     /* R12  - Power mgmt (5) */
-       0x0000,     /* R13  - Power mgmt (6) */
-       0x0000,     /* R14  - Power mgmt (7) */
-       0x0000,     /* R15 */
-       0x0000,     /* R16  - RTC Seconds/Minutes */
-       0x0100,     /* R17  - RTC Hours/Day */
-       0x0101,     /* R18  - RTC Date/Month */
-       0x1400,     /* R19  - RTC Year */
-       0x0000,     /* R20  - Alarm Seconds/Minutes */
-       0x0000,     /* R21  - Alarm Hours/Day */
-       0x0000,     /* R22  - Alarm Date/Month */
-       0x0320,     /* R23  - RTC Time Control */
-       0x0000,     /* R24  - System Interrupts */
-       0x0000,     /* R25  - Interrupt Status 1 */
-       0x0000,     /* R26  - Interrupt Status 2 */
-       0x0000,     /* R27  - Power Up Interrupt Status */
-       0x0000,     /* R28  - Under Voltage Interrupt status */
-       0x0000,     /* R29  - Over Current Interrupt status */
-       0x0000,     /* R30  - GPIO Interrupt Status */
-       0x0000,     /* R31  - Comparator Interrupt Status */
-       0x3FFF,     /* R32  - System Interrupts Mask */
-       0x0000,     /* R33  - Interrupt Status 1 Mask */
-       0x0000,     /* R34  - Interrupt Status 2 Mask */
-       0x0000,     /* R35  - Power Up Interrupt Status Mask */
-       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
-       0x0000,     /* R37  - Over Current Interrupt status Mask */
-       0x0000,     /* R38  - GPIO Interrupt Status Mask */
-       0x0000,     /* R39  - Comparator Interrupt Status Mask */
-       0x0040,     /* R40  - Clock Control 1 */
-       0x0000,     /* R41  - Clock Control 2 */
-       0x3B00,     /* R42  - FLL Control 1 */
-       0x7086,     /* R43  - FLL Control 2 */
-       0xC226,     /* R44  - FLL Control 3 */
-       0x0000,     /* R45  - FLL Control 4 */
-       0x0000,     /* R46 */
-       0x0000,     /* R47 */
-       0x0000,     /* R48  - DAC Control */
-       0x0000,     /* R49 */
-       0x00C0,     /* R50  - DAC Digital Volume L */
-       0x00C0,     /* R51  - DAC Digital Volume R */
-       0x0000,     /* R52 */
-       0x0040,     /* R53  - DAC LR Rate */
-       0x0000,     /* R54  - DAC Clock Control */
-       0x0000,     /* R55 */
-       0x0000,     /* R56 */
-       0x0000,     /* R57 */
-       0x4000,     /* R58  - DAC Mute */
-       0x0000,     /* R59  - DAC Mute Volume */
-       0x0000,     /* R60  - DAC Side */
-       0x0000,     /* R61 */
-       0x0000,     /* R62 */
-       0x0000,     /* R63 */
-       0x8000,     /* R64  - ADC Control */
-       0x0000,     /* R65 */
-       0x00C0,     /* R66  - ADC Digital Volume L */
-       0x00C0,     /* R67  - ADC Digital Volume R */
-       0x0000,     /* R68  - ADC Divider */
-       0x0000,     /* R69 */
-       0x0040,     /* R70  - ADC LR Rate */
-       0x0000,     /* R71 */
-       0x0303,     /* R72  - Input Control */
-       0x0000,     /* R73  - IN3 Input Control */
-       0x0000,     /* R74  - Mic Bias Control */
-       0x0000,     /* R75 */
-       0x0000,     /* R76  - Output Control */
-       0x0000,     /* R77  - Jack Detect */
-       0x0000,     /* R78  - Anti Pop Control */
-       0x0000,     /* R79 */
-       0x0040,     /* R80  - Left Input Volume */
-       0x0040,     /* R81  - Right Input Volume */
-       0x0000,     /* R82 */
-       0x0000,     /* R83 */
-       0x0000,     /* R84 */
-       0x0000,     /* R85 */
-       0x0000,     /* R86 */
-       0x0000,     /* R87 */
-       0x0800,     /* R88  - Left Mixer Control */
-       0x1000,     /* R89  - Right Mixer Control */
-       0x0000,     /* R90 */
-       0x0000,     /* R91 */
-       0x0000,     /* R92  - OUT3 Mixer Control */
-       0x0000,     /* R93  - OUT4 Mixer Control */
-       0x0000,     /* R94 */
-       0x0000,     /* R95 */
-       0x0000,     /* R96  - Output Left Mixer Volume */
-       0x0000,     /* R97  - Output Right Mixer Volume */
-       0x0000,     /* R98  - Input Mixer Volume L */
-       0x0000,     /* R99  - Input Mixer Volume R */
-       0x0000,     /* R100 - Input Mixer Volume */
-       0x0000,     /* R101 */
-       0x0000,     /* R102 */
-       0x0000,     /* R103 */
-       0x00E4,     /* R104 - LOUT1 Volume */
-       0x00E4,     /* R105 - ROUT1 Volume */
-       0x00E4,     /* R106 - LOUT2 Volume */
-       0x02E4,     /* R107 - ROUT2 Volume */
-       0x0000,     /* R108 */
-       0x0000,     /* R109 */
-       0x0000,     /* R110 */
-       0x0000,     /* R111 - BEEP Volume */
-       0x0A00,     /* R112 - AI Formating */
-       0x0000,     /* R113 - ADC DAC COMP */
-       0x0020,     /* R114 - AI ADC Control */
-       0x0020,     /* R115 - AI DAC Control */
-       0x0000,     /* R116 - AIF Test */
-       0x0000,     /* R117 */
-       0x0000,     /* R118 */
-       0x0000,     /* R119 */
-       0x0000,     /* R120 */
-       0x0000,     /* R121 */
-       0x0000,     /* R122 */
-       0x0000,     /* R123 */
-       0x0000,     /* R124 */
-       0x0000,     /* R125 */
-       0x0000,     /* R126 */
-       0x0000,     /* R127 */
-       0x1FFF,     /* R128 - GPIO Debounce */
-       0x0000,     /* R129 - GPIO Pin pull up Control */
-       0x03FC,     /* R130 - GPIO Pull down Control */
-       0x0000,     /* R131 - GPIO Interrupt Mode */
-       0x0000,     /* R132 */
-       0x0000,     /* R133 - GPIO Control */
-       0x0FFC,     /* R134 - GPIO Configuration (i/o) */
-       0x0FFC,     /* R135 - GPIO Pin Polarity / Type */
-       0x0000,     /* R136 */
-       0x0000,     /* R137 */
-       0x0000,     /* R138 */
-       0x0000,     /* R139 */
-       0x0013,     /* R140 - GPIO Function Select 1 */
-       0x0000,     /* R141 - GPIO Function Select 2 */
-       0x0000,     /* R142 - GPIO Function Select 3 */
-       0x0003,     /* R143 - GPIO Function Select 4 */
-       0x0000,     /* R144 - Digitiser Control (1) */
-       0x0002,     /* R145 - Digitiser Control (2) */
-       0x0000,     /* R146 */
-       0x0000,     /* R147 */
-       0x0000,     /* R148 */
-       0x0000,     /* R149 */
-       0x0000,     /* R150 */
-       0x0000,     /* R151 */
-       0x7000,     /* R152 - AUX1 Readback */
-       0x7000,     /* R153 - AUX2 Readback */
-       0x7000,     /* R154 - AUX3 Readback */
-       0x7000,     /* R155 - AUX4 Readback */
-       0x0000,     /* R156 - USB Voltage Readback */
-       0x0000,     /* R157 - LINE Voltage Readback */
-       0x0000,     /* R158 - BATT Voltage Readback */
-       0x0000,     /* R159 - Chip Temp Readback */
-       0x0000,     /* R160 */
-       0x0000,     /* R161 */
-       0x0000,     /* R162 */
-       0x0000,     /* R163 - Generic Comparator Control */
-       0x0000,     /* R164 - Generic comparator 1 */
-       0x0000,     /* R165 - Generic comparator 2 */
-       0x0000,     /* R166 - Generic comparator 3 */
-       0x0000,     /* R167 - Generic comparator 4 */
-       0xA00F,     /* R168 - Battery Charger Control 1 */
-       0x0B06,     /* R169 - Battery Charger Control 2 */
-       0x0000,     /* R170 - Battery Charger Control 3 */
-       0x0000,     /* R171 */
-       0x0000,     /* R172 - Current Sink Driver A */
-       0x0000,     /* R173 - CSA Flash control */
-       0x0000,     /* R174 - Current Sink Driver B */
-       0x0000,     /* R175 - CSB Flash control */
-       0x0000,     /* R176 - DCDC/LDO requested */
-       0x002D,     /* R177 - DCDC Active options */
-       0x0000,     /* R178 - DCDC Sleep options */
-       0x0025,     /* R179 - Power-check comparator */
-       0x000E,     /* R180 - DCDC1 Control */
-       0x0000,     /* R181 - DCDC1 Timeouts */
-       0x1006,     /* R182 - DCDC1 Low Power */
-       0x0018,     /* R183 - DCDC2 Control */
-       0x0000,     /* R184 - DCDC2 Timeouts */
-       0x0000,     /* R185 */
-       0x0000,     /* R186 - DCDC3 Control */
-       0x0000,     /* R187 - DCDC3 Timeouts */
-       0x0006,     /* R188 - DCDC3 Low Power */
-       0x0000,     /* R189 - DCDC4 Control */
-       0x0000,     /* R190 - DCDC4 Timeouts */
-       0x0006,     /* R191 - DCDC4 Low Power */
-       0x0008,     /* R192 - DCDC5 Control */
-       0x0000,     /* R193 - DCDC5 Timeouts */
-       0x0000,     /* R194 */
-       0x0000,     /* R195 - DCDC6 Control */
-       0x0000,     /* R196 - DCDC6 Timeouts */
-       0x0006,     /* R197 - DCDC6 Low Power */
-       0x0000,     /* R198 */
-       0x0003,     /* R199 - Limit Switch Control */
-       0x001C,     /* R200 - LDO1 Control */
-       0x0000,     /* R201 - LDO1 Timeouts */
-       0x001C,     /* R202 - LDO1 Low Power */
-       0x001B,     /* R203 - LDO2 Control */
-       0x0000,     /* R204 - LDO2 Timeouts */
-       0x001C,     /* R205 - LDO2 Low Power */
-       0x001B,     /* R206 - LDO3 Control */
-       0x0000,     /* R207 - LDO3 Timeouts */
-       0x001C,     /* R208 - LDO3 Low Power */
-       0x001B,     /* R209 - LDO4 Control */
-       0x0000,     /* R210 - LDO4 Timeouts */
-       0x001C,     /* R211 - LDO4 Low Power */
-       0x0000,     /* R212 */
-       0x0000,     /* R213 */
-       0x0000,     /* R214 */
-       0x0000,     /* R215 - VCC_FAULT Masks */
-       0x001F,     /* R216 - Main Bandgap Control */
-       0x0000,     /* R217 - OSC Control */
-       0x9000,     /* R218 - RTC Tick Control */
-       0x0000,     /* R219 */
-       0x4000,     /* R220 - RAM BIST 1 */
-       0x0000,     /* R221 */
-       0x0000,     /* R222 */
-       0x0000,     /* R223 */
-       0x0000,     /* R224 */
-       0x0000,     /* R225 - DCDC/LDO status */
-       0x0000,     /* R226 */
-       0x0000,     /* R227 */
-       0x0000,     /* R228 */
-       0x0000,     /* R229 */
-       0xE000,     /* R230 - GPIO Pin Status */
-       0x0000,     /* R231 */
-       0x0000,     /* R232 */
-       0x0000,     /* R233 */
-       0x0000,     /* R234 */
-       0x0000,     /* R235 */
-       0x0000,     /* R236 */
-       0x0000,     /* R237 */
-       0x0000,     /* R238 */
-       0x0000,     /* R239 */
-       0x0000,     /* R240 */
-       0x0000,     /* R241 */
-       0x0000,     /* R242 */
-       0x0000,     /* R243 */
-       0x0000,     /* R244 */
-       0x0000,     /* R245 */
-       0x0000,     /* R246 */
-       0x0000,     /* R247 */
-       0x0000,     /* R248 */
-       0x0000,     /* R249 */
-       0x0000,     /* R250 */
-       0x0000,     /* R251 */
-       0x0000,     /* R252 */
-       0x0000,     /* R253 */
-       0x0000,     /* R254 */
-       0x0000,     /* R255 */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8350_mode1_defaults[] = {
-       0x17FF,     /* R0   - Reset/ID */
-       0x1000,     /* R1   - ID */
-       0x0000,     /* R2 */
-       0x1002,     /* R3   - System Control 1 */
-       0x0014,     /* R4   - System Control 2 */
-       0x0000,     /* R5   - System Hibernate */
-       0x8A00,     /* R6   - Interface Control */
-       0x0000,     /* R7 */
-       0x8000,     /* R8   - Power mgmt (1) */
-       0x0000,     /* R9   - Power mgmt (2) */
-       0x0000,     /* R10  - Power mgmt (3) */
-       0x2000,     /* R11  - Power mgmt (4) */
-       0x0E00,     /* R12  - Power mgmt (5) */
-       0x0000,     /* R13  - Power mgmt (6) */
-       0x0000,     /* R14  - Power mgmt (7) */
-       0x0000,     /* R15 */
-       0x0000,     /* R16  - RTC Seconds/Minutes */
-       0x0100,     /* R17  - RTC Hours/Day */
-       0x0101,     /* R18  - RTC Date/Month */
-       0x1400,     /* R19  - RTC Year */
-       0x0000,     /* R20  - Alarm Seconds/Minutes */
-       0x0000,     /* R21  - Alarm Hours/Day */
-       0x0000,     /* R22  - Alarm Date/Month */
-       0x0320,     /* R23  - RTC Time Control */
-       0x0000,     /* R24  - System Interrupts */
-       0x0000,     /* R25  - Interrupt Status 1 */
-       0x0000,     /* R26  - Interrupt Status 2 */
-       0x0000,     /* R27  - Power Up Interrupt Status */
-       0x0000,     /* R28  - Under Voltage Interrupt status */
-       0x0000,     /* R29  - Over Current Interrupt status */
-       0x0000,     /* R30  - GPIO Interrupt Status */
-       0x0000,     /* R31  - Comparator Interrupt Status */
-       0x3FFF,     /* R32  - System Interrupts Mask */
-       0x0000,     /* R33  - Interrupt Status 1 Mask */
-       0x0000,     /* R34  - Interrupt Status 2 Mask */
-       0x0000,     /* R35  - Power Up Interrupt Status Mask */
-       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
-       0x0000,     /* R37  - Over Current Interrupt status Mask */
-       0x0000,     /* R38  - GPIO Interrupt Status Mask */
-       0x0000,     /* R39  - Comparator Interrupt Status Mask */
-       0x0040,     /* R40  - Clock Control 1 */
-       0x0000,     /* R41  - Clock Control 2 */
-       0x3B00,     /* R42  - FLL Control 1 */
-       0x7086,     /* R43  - FLL Control 2 */
-       0xC226,     /* R44  - FLL Control 3 */
-       0x0000,     /* R45  - FLL Control 4 */
-       0x0000,     /* R46 */
-       0x0000,     /* R47 */
-       0x0000,     /* R48  - DAC Control */
-       0x0000,     /* R49 */
-       0x00C0,     /* R50  - DAC Digital Volume L */
-       0x00C0,     /* R51  - DAC Digital Volume R */
-       0x0000,     /* R52 */
-       0x0040,     /* R53  - DAC LR Rate */
-       0x0000,     /* R54  - DAC Clock Control */
-       0x0000,     /* R55 */
-       0x0000,     /* R56 */
-       0x0000,     /* R57 */
-       0x4000,     /* R58  - DAC Mute */
-       0x0000,     /* R59  - DAC Mute Volume */
-       0x0000,     /* R60  - DAC Side */
-       0x0000,     /* R61 */
-       0x0000,     /* R62 */
-       0x0000,     /* R63 */
-       0x8000,     /* R64  - ADC Control */
-       0x0000,     /* R65 */
-       0x00C0,     /* R66  - ADC Digital Volume L */
-       0x00C0,     /* R67  - ADC Digital Volume R */
-       0x0000,     /* R68  - ADC Divider */
-       0x0000,     /* R69 */
-       0x0040,     /* R70  - ADC LR Rate */
-       0x0000,     /* R71 */
-       0x0303,     /* R72  - Input Control */
-       0x0000,     /* R73  - IN3 Input Control */
-       0x0000,     /* R74  - Mic Bias Control */
-       0x0000,     /* R75 */
-       0x0000,     /* R76  - Output Control */
-       0x0000,     /* R77  - Jack Detect */
-       0x0000,     /* R78  - Anti Pop Control */
-       0x0000,     /* R79 */
-       0x0040,     /* R80  - Left Input Volume */
-       0x0040,     /* R81  - Right Input Volume */
-       0x0000,     /* R82 */
-       0x0000,     /* R83 */
-       0x0000,     /* R84 */
-       0x0000,     /* R85 */
-       0x0000,     /* R86 */
-       0x0000,     /* R87 */
-       0x0800,     /* R88  - Left Mixer Control */
-       0x1000,     /* R89  - Right Mixer Control */
-       0x0000,     /* R90 */
-       0x0000,     /* R91 */
-       0x0000,     /* R92  - OUT3 Mixer Control */
-       0x0000,     /* R93  - OUT4 Mixer Control */
-       0x0000,     /* R94 */
-       0x0000,     /* R95 */
-       0x0000,     /* R96  - Output Left Mixer Volume */
-       0x0000,     /* R97  - Output Right Mixer Volume */
-       0x0000,     /* R98  - Input Mixer Volume L */
-       0x0000,     /* R99  - Input Mixer Volume R */
-       0x0000,     /* R100 - Input Mixer Volume */
-       0x0000,     /* R101 */
-       0x0000,     /* R102 */
-       0x0000,     /* R103 */
-       0x00E4,     /* R104 - LOUT1 Volume */
-       0x00E4,     /* R105 - ROUT1 Volume */
-       0x00E4,     /* R106 - LOUT2 Volume */
-       0x02E4,     /* R107 - ROUT2 Volume */
-       0x0000,     /* R108 */
-       0x0000,     /* R109 */
-       0x0000,     /* R110 */
-       0x0000,     /* R111 - BEEP Volume */
-       0x0A00,     /* R112 - AI Formating */
-       0x0000,     /* R113 - ADC DAC COMP */
-       0x0020,     /* R114 - AI ADC Control */
-       0x0020,     /* R115 - AI DAC Control */
-       0x0000,     /* R116 - AIF Test */
-       0x0000,     /* R117 */
-       0x0000,     /* R118 */
-       0x0000,     /* R119 */
-       0x0000,     /* R120 */
-       0x0000,     /* R121 */
-       0x0000,     /* R122 */
-       0x0000,     /* R123 */
-       0x0000,     /* R124 */
-       0x0000,     /* R125 */
-       0x0000,     /* R126 */
-       0x0000,     /* R127 */
-       0x1FFF,     /* R128 - GPIO Debounce */
-       0x0000,     /* R129 - GPIO Pin pull up Control */
-       0x03FC,     /* R130 - GPIO Pull down Control */
-       0x0000,     /* R131 - GPIO Interrupt Mode */
-       0x0000,     /* R132 */
-       0x0000,     /* R133 - GPIO Control */
-       0x00FB,     /* R134 - GPIO Configuration (i/o) */
-       0x04FE,     /* R135 - GPIO Pin Polarity / Type */
-       0x0000,     /* R136 */
-       0x0000,     /* R137 */
-       0x0000,     /* R138 */
-       0x0000,     /* R139 */
-       0x0312,     /* R140 - GPIO Function Select 1 */
-       0x1003,     /* R141 - GPIO Function Select 2 */
-       0x1331,     /* R142 - GPIO Function Select 3 */
-       0x0003,     /* R143 - GPIO Function Select 4 */
-       0x0000,     /* R144 - Digitiser Control (1) */
-       0x0002,     /* R145 - Digitiser Control (2) */
-       0x0000,     /* R146 */
-       0x0000,     /* R147 */
-       0x0000,     /* R148 */
-       0x0000,     /* R149 */
-       0x0000,     /* R150 */
-       0x0000,     /* R151 */
-       0x7000,     /* R152 - AUX1 Readback */
-       0x7000,     /* R153 - AUX2 Readback */
-       0x7000,     /* R154 - AUX3 Readback */
-       0x7000,     /* R155 - AUX4 Readback */
-       0x0000,     /* R156 - USB Voltage Readback */
-       0x0000,     /* R157 - LINE Voltage Readback */
-       0x0000,     /* R158 - BATT Voltage Readback */
-       0x0000,     /* R159 - Chip Temp Readback */
-       0x0000,     /* R160 */
-       0x0000,     /* R161 */
-       0x0000,     /* R162 */
-       0x0000,     /* R163 - Generic Comparator Control */
-       0x0000,     /* R164 - Generic comparator 1 */
-       0x0000,     /* R165 - Generic comparator 2 */
-       0x0000,     /* R166 - Generic comparator 3 */
-       0x0000,     /* R167 - Generic comparator 4 */
-       0xA00F,     /* R168 - Battery Charger Control 1 */
-       0x0B06,     /* R169 - Battery Charger Control 2 */
-       0x0000,     /* R170 - Battery Charger Control 3 */
-       0x0000,     /* R171 */
-       0x0000,     /* R172 - Current Sink Driver A */
-       0x0000,     /* R173 - CSA Flash control */
-       0x0000,     /* R174 - Current Sink Driver B */
-       0x0000,     /* R175 - CSB Flash control */
-       0x0000,     /* R176 - DCDC/LDO requested */
-       0x002D,     /* R177 - DCDC Active options */
-       0x0000,     /* R178 - DCDC Sleep options */
-       0x0025,     /* R179 - Power-check comparator */
-       0x0062,     /* R180 - DCDC1 Control */
-       0x0400,     /* R181 - DCDC1 Timeouts */
-       0x1006,     /* R182 - DCDC1 Low Power */
-       0x0018,     /* R183 - DCDC2 Control */
-       0x0000,     /* R184 - DCDC2 Timeouts */
-       0x0000,     /* R185 */
-       0x0026,     /* R186 - DCDC3 Control */
-       0x0400,     /* R187 - DCDC3 Timeouts */
-       0x0006,     /* R188 - DCDC3 Low Power */
-       0x0062,     /* R189 - DCDC4 Control */
-       0x0400,     /* R190 - DCDC4 Timeouts */
-       0x0006,     /* R191 - DCDC4 Low Power */
-       0x0008,     /* R192 - DCDC5 Control */
-       0x0000,     /* R193 - DCDC5 Timeouts */
-       0x0000,     /* R194 */
-       0x0026,     /* R195 - DCDC6 Control */
-       0x0800,     /* R196 - DCDC6 Timeouts */
-       0x0006,     /* R197 - DCDC6 Low Power */
-       0x0000,     /* R198 */
-       0x0003,     /* R199 - Limit Switch Control */
-       0x0006,     /* R200 - LDO1 Control */
-       0x0400,     /* R201 - LDO1 Timeouts */
-       0x001C,     /* R202 - LDO1 Low Power */
-       0x0006,     /* R203 - LDO2 Control */
-       0x0400,     /* R204 - LDO2 Timeouts */
-       0x001C,     /* R205 - LDO2 Low Power */
-       0x001B,     /* R206 - LDO3 Control */
-       0x0000,     /* R207 - LDO3 Timeouts */
-       0x001C,     /* R208 - LDO3 Low Power */
-       0x001B,     /* R209 - LDO4 Control */
-       0x0000,     /* R210 - LDO4 Timeouts */
-       0x001C,     /* R211 - LDO4 Low Power */
-       0x0000,     /* R212 */
-       0x0000,     /* R213 */
-       0x0000,     /* R214 */
-       0x0000,     /* R215 - VCC_FAULT Masks */
-       0x001F,     /* R216 - Main Bandgap Control */
-       0x0000,     /* R217 - OSC Control */
-       0x9000,     /* R218 - RTC Tick Control */
-       0x0000,     /* R219 */
-       0x4000,     /* R220 - RAM BIST 1 */
-       0x0000,     /* R221 */
-       0x0000,     /* R222 */
-       0x0000,     /* R223 */
-       0x0000,     /* R224 */
-       0x0000,     /* R225 - DCDC/LDO status */
-       0x0000,     /* R226 */
-       0x0000,     /* R227 */
-       0x0000,     /* R228 */
-       0x0000,     /* R229 */
-       0xE000,     /* R230 - GPIO Pin Status */
-       0x0000,     /* R231 */
-       0x0000,     /* R232 */
-       0x0000,     /* R233 */
-       0x0000,     /* R234 */
-       0x0000,     /* R235 */
-       0x0000,     /* R236 */
-       0x0000,     /* R237 */
-       0x0000,     /* R238 */
-       0x0000,     /* R239 */
-       0x0000,     /* R240 */
-       0x0000,     /* R241 */
-       0x0000,     /* R242 */
-       0x0000,     /* R243 */
-       0x0000,     /* R244 */
-       0x0000,     /* R245 */
-       0x0000,     /* R246 */
-       0x0000,     /* R247 */
-       0x0000,     /* R248 */
-       0x0000,     /* R249 */
-       0x0000,     /* R250 */
-       0x0000,     /* R251 */
-       0x0000,     /* R252 */
-       0x0000,     /* R253 */
-       0x0000,     /* R254 */
-       0x0000,     /* R255 */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8350_mode2_defaults[] = {
-       0x17FF,     /* R0   - Reset/ID */
-       0x1000,     /* R1   - ID */
-       0x0000,     /* R2 */
-       0x1002,     /* R3   - System Control 1 */
-       0x0014,     /* R4   - System Control 2 */
-       0x0000,     /* R5   - System Hibernate */
-       0x8A00,     /* R6   - Interface Control */
-       0x0000,     /* R7 */
-       0x8000,     /* R8   - Power mgmt (1) */
-       0x0000,     /* R9   - Power mgmt (2) */
-       0x0000,     /* R10  - Power mgmt (3) */
-       0x2000,     /* R11  - Power mgmt (4) */
-       0x0E00,     /* R12  - Power mgmt (5) */
-       0x0000,     /* R13  - Power mgmt (6) */
-       0x0000,     /* R14  - Power mgmt (7) */
-       0x0000,     /* R15 */
-       0x0000,     /* R16  - RTC Seconds/Minutes */
-       0x0100,     /* R17  - RTC Hours/Day */
-       0x0101,     /* R18  - RTC Date/Month */
-       0x1400,     /* R19  - RTC Year */
-       0x0000,     /* R20  - Alarm Seconds/Minutes */
-       0x0000,     /* R21  - Alarm Hours/Day */
-       0x0000,     /* R22  - Alarm Date/Month */
-       0x0320,     /* R23  - RTC Time Control */
-       0x0000,     /* R24  - System Interrupts */
-       0x0000,     /* R25  - Interrupt Status 1 */
-       0x0000,     /* R26  - Interrupt Status 2 */
-       0x0000,     /* R27  - Power Up Interrupt Status */
-       0x0000,     /* R28  - Under Voltage Interrupt status */
-       0x0000,     /* R29  - Over Current Interrupt status */
-       0x0000,     /* R30  - GPIO Interrupt Status */
-       0x0000,     /* R31  - Comparator Interrupt Status */
-       0x3FFF,     /* R32  - System Interrupts Mask */
-       0x0000,     /* R33  - Interrupt Status 1 Mask */
-       0x0000,     /* R34  - Interrupt Status 2 Mask */
-       0x0000,     /* R35  - Power Up Interrupt Status Mask */
-       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
-       0x0000,     /* R37  - Over Current Interrupt status Mask */
-       0x0000,     /* R38  - GPIO Interrupt Status Mask */
-       0x0000,     /* R39  - Comparator Interrupt Status Mask */
-       0x0040,     /* R40  - Clock Control 1 */
-       0x0000,     /* R41  - Clock Control 2 */
-       0x3B00,     /* R42  - FLL Control 1 */
-       0x7086,     /* R43  - FLL Control 2 */
-       0xC226,     /* R44  - FLL Control 3 */
-       0x0000,     /* R45  - FLL Control 4 */
-       0x0000,     /* R46 */
-       0x0000,     /* R47 */
-       0x0000,     /* R48  - DAC Control */
-       0x0000,     /* R49 */
-       0x00C0,     /* R50  - DAC Digital Volume L */
-       0x00C0,     /* R51  - DAC Digital Volume R */
-       0x0000,     /* R52 */
-       0x0040,     /* R53  - DAC LR Rate */
-       0x0000,     /* R54  - DAC Clock Control */
-       0x0000,     /* R55 */
-       0x0000,     /* R56 */
-       0x0000,     /* R57 */
-       0x4000,     /* R58  - DAC Mute */
-       0x0000,     /* R59  - DAC Mute Volume */
-       0x0000,     /* R60  - DAC Side */
-       0x0000,     /* R61 */
-       0x0000,     /* R62 */
-       0x0000,     /* R63 */
-       0x8000,     /* R64  - ADC Control */
-       0x0000,     /* R65 */
-       0x00C0,     /* R66  - ADC Digital Volume L */
-       0x00C0,     /* R67  - ADC Digital Volume R */
-       0x0000,     /* R68  - ADC Divider */
-       0x0000,     /* R69 */
-       0x0040,     /* R70  - ADC LR Rate */
-       0x0000,     /* R71 */
-       0x0303,     /* R72  - Input Control */
-       0x0000,     /* R73  - IN3 Input Control */
-       0x0000,     /* R74  - Mic Bias Control */
-       0x0000,     /* R75 */
-       0x0000,     /* R76  - Output Control */
-       0x0000,     /* R77  - Jack Detect */
-       0x0000,     /* R78  - Anti Pop Control */
-       0x0000,     /* R79 */
-       0x0040,     /* R80  - Left Input Volume */
-       0x0040,     /* R81  - Right Input Volume */
-       0x0000,     /* R82 */
-       0x0000,     /* R83 */
-       0x0000,     /* R84 */
-       0x0000,     /* R85 */
-       0x0000,     /* R86 */
-       0x0000,     /* R87 */
-       0x0800,     /* R88  - Left Mixer Control */
-       0x1000,     /* R89  - Right Mixer Control */
-       0x0000,     /* R90 */
-       0x0000,     /* R91 */
-       0x0000,     /* R92  - OUT3 Mixer Control */
-       0x0000,     /* R93  - OUT4 Mixer Control */
-       0x0000,     /* R94 */
-       0x0000,     /* R95 */
-       0x0000,     /* R96  - Output Left Mixer Volume */
-       0x0000,     /* R97  - Output Right Mixer Volume */
-       0x0000,     /* R98  - Input Mixer Volume L */
-       0x0000,     /* R99  - Input Mixer Volume R */
-       0x0000,     /* R100 - Input Mixer Volume */
-       0x0000,     /* R101 */
-       0x0000,     /* R102 */
-       0x0000,     /* R103 */
-       0x00E4,     /* R104 - LOUT1 Volume */
-       0x00E4,     /* R105 - ROUT1 Volume */
-       0x00E4,     /* R106 - LOUT2 Volume */
-       0x02E4,     /* R107 - ROUT2 Volume */
-       0x0000,     /* R108 */
-       0x0000,     /* R109 */
-       0x0000,     /* R110 */
-       0x0000,     /* R111 - BEEP Volume */
-       0x0A00,     /* R112 - AI Formating */
-       0x0000,     /* R113 - ADC DAC COMP */
-       0x0020,     /* R114 - AI ADC Control */
-       0x0020,     /* R115 - AI DAC Control */
-       0x0000,     /* R116 - AIF Test */
-       0x0000,     /* R117 */
-       0x0000,     /* R118 */
-       0x0000,     /* R119 */
-       0x0000,     /* R120 */
-       0x0000,     /* R121 */
-       0x0000,     /* R122 */
-       0x0000,     /* R123 */
-       0x0000,     /* R124 */
-       0x0000,     /* R125 */
-       0x0000,     /* R126 */
-       0x0000,     /* R127 */
-       0x1FFF,     /* R128 - GPIO Debounce */
-       0x0000,     /* R129 - GPIO Pin pull up Control */
-       0x03FC,     /* R130 - GPIO Pull down Control */
-       0x0000,     /* R131 - GPIO Interrupt Mode */
-       0x0000,     /* R132 */
-       0x0000,     /* R133 - GPIO Control */
-       0x08FB,     /* R134 - GPIO Configuration (i/o) */
-       0x0CFE,     /* R135 - GPIO Pin Polarity / Type */
-       0x0000,     /* R136 */
-       0x0000,     /* R137 */
-       0x0000,     /* R138 */
-       0x0000,     /* R139 */
-       0x0312,     /* R140 - GPIO Function Select 1 */
-       0x0003,     /* R141 - GPIO Function Select 2 */
-       0x2331,     /* R142 - GPIO Function Select 3 */
-       0x0003,     /* R143 - GPIO Function Select 4 */
-       0x0000,     /* R144 - Digitiser Control (1) */
-       0x0002,     /* R145 - Digitiser Control (2) */
-       0x0000,     /* R146 */
-       0x0000,     /* R147 */
-       0x0000,     /* R148 */
-       0x0000,     /* R149 */
-       0x0000,     /* R150 */
-       0x0000,     /* R151 */
-       0x7000,     /* R152 - AUX1 Readback */
-       0x7000,     /* R153 - AUX2 Readback */
-       0x7000,     /* R154 - AUX3 Readback */
-       0x7000,     /* R155 - AUX4 Readback */
-       0x0000,     /* R156 - USB Voltage Readback */
-       0x0000,     /* R157 - LINE Voltage Readback */
-       0x0000,     /* R158 - BATT Voltage Readback */
-       0x0000,     /* R159 - Chip Temp Readback */
-       0x0000,     /* R160 */
-       0x0000,     /* R161 */
-       0x0000,     /* R162 */
-       0x0000,     /* R163 - Generic Comparator Control */
-       0x0000,     /* R164 - Generic comparator 1 */
-       0x0000,     /* R165 - Generic comparator 2 */
-       0x0000,     /* R166 - Generic comparator 3 */
-       0x0000,     /* R167 - Generic comparator 4 */
-       0xA00F,     /* R168 - Battery Charger Control 1 */
-       0x0B06,     /* R169 - Battery Charger Control 2 */
-       0x0000,     /* R170 - Battery Charger Control 3 */
-       0x0000,     /* R171 */
-       0x0000,     /* R172 - Current Sink Driver A */
-       0x0000,     /* R173 - CSA Flash control */
-       0x0000,     /* R174 - Current Sink Driver B */
-       0x0000,     /* R175 - CSB Flash control */
-       0x0000,     /* R176 - DCDC/LDO requested */
-       0x002D,     /* R177 - DCDC Active options */
-       0x0000,     /* R178 - DCDC Sleep options */
-       0x0025,     /* R179 - Power-check comparator */
-       0x000E,     /* R180 - DCDC1 Control */
-       0x0400,     /* R181 - DCDC1 Timeouts */
-       0x1006,     /* R182 - DCDC1 Low Power */
-       0x0018,     /* R183 - DCDC2 Control */
-       0x0000,     /* R184 - DCDC2 Timeouts */
-       0x0000,     /* R185 */
-       0x002E,     /* R186 - DCDC3 Control */
-       0x0800,     /* R187 - DCDC3 Timeouts */
-       0x0006,     /* R188 - DCDC3 Low Power */
-       0x000E,     /* R189 - DCDC4 Control */
-       0x0800,     /* R190 - DCDC4 Timeouts */
-       0x0006,     /* R191 - DCDC4 Low Power */
-       0x0008,     /* R192 - DCDC5 Control */
-       0x0000,     /* R193 - DCDC5 Timeouts */
-       0x0000,     /* R194 */
-       0x0026,     /* R195 - DCDC6 Control */
-       0x0C00,     /* R196 - DCDC6 Timeouts */
-       0x0006,     /* R197 - DCDC6 Low Power */
-       0x0000,     /* R198 */
-       0x0003,     /* R199 - Limit Switch Control */
-       0x001A,     /* R200 - LDO1 Control */
-       0x0800,     /* R201 - LDO1 Timeouts */
-       0x001C,     /* R202 - LDO1 Low Power */
-       0x0010,     /* R203 - LDO2 Control */
-       0x0800,     /* R204 - LDO2 Timeouts */
-       0x001C,     /* R205 - LDO2 Low Power */
-       0x000A,     /* R206 - LDO3 Control */
-       0x0C00,     /* R207 - LDO3 Timeouts */
-       0x001C,     /* R208 - LDO3 Low Power */
-       0x001A,     /* R209 - LDO4 Control */
-       0x0800,     /* R210 - LDO4 Timeouts */
-       0x001C,     /* R211 - LDO4 Low Power */
-       0x0000,     /* R212 */
-       0x0000,     /* R213 */
-       0x0000,     /* R214 */
-       0x0000,     /* R215 - VCC_FAULT Masks */
-       0x001F,     /* R216 - Main Bandgap Control */
-       0x0000,     /* R217 - OSC Control */
-       0x9000,     /* R218 - RTC Tick Control */
-       0x0000,     /* R219 */
-       0x4000,     /* R220 - RAM BIST 1 */
-       0x0000,     /* R221 */
-       0x0000,     /* R222 */
-       0x0000,     /* R223 */
-       0x0000,     /* R224 */
-       0x0000,     /* R225 - DCDC/LDO status */
-       0x0000,     /* R226 */
-       0x0000,     /* R227 */
-       0x0000,     /* R228 */
-       0x0000,     /* R229 */
-       0xE000,     /* R230 - GPIO Pin Status */
-       0x0000,     /* R231 */
-       0x0000,     /* R232 */
-       0x0000,     /* R233 */
-       0x0000,     /* R234 */
-       0x0000,     /* R235 */
-       0x0000,     /* R236 */
-       0x0000,     /* R237 */
-       0x0000,     /* R238 */
-       0x0000,     /* R239 */
-       0x0000,     /* R240 */
-       0x0000,     /* R241 */
-       0x0000,     /* R242 */
-       0x0000,     /* R243 */
-       0x0000,     /* R244 */
-       0x0000,     /* R245 */
-       0x0000,     /* R246 */
-       0x0000,     /* R247 */
-       0x0000,     /* R248 */
-       0x0000,     /* R249 */
-       0x0000,     /* R250 */
-       0x0000,     /* R251 */
-       0x0000,     /* R252 */
-       0x0000,     /* R253 */
-       0x0000,     /* R254 */
-       0x0000,     /* R255 */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8350_mode3_defaults[] = {
-       0x17FF,     /* R0   - Reset/ID */
-       0x1000,     /* R1   - ID */
-       0x0000,     /* R2 */
-       0x1000,     /* R3   - System Control 1 */
-       0x0004,     /* R4   - System Control 2 */
-       0x0000,     /* R5   - System Hibernate */
-       0x8A00,     /* R6   - Interface Control */
-       0x0000,     /* R7 */
-       0x8000,     /* R8   - Power mgmt (1) */
-       0x0000,     /* R9   - Power mgmt (2) */
-       0x0000,     /* R10  - Power mgmt (3) */
-       0x2000,     /* R11  - Power mgmt (4) */
-       0x0E00,     /* R12  - Power mgmt (5) */
-       0x0000,     /* R13  - Power mgmt (6) */
-       0x0000,     /* R14  - Power mgmt (7) */
-       0x0000,     /* R15 */
-       0x0000,     /* R16  - RTC Seconds/Minutes */
-       0x0100,     /* R17  - RTC Hours/Day */
-       0x0101,     /* R18  - RTC Date/Month */
-       0x1400,     /* R19  - RTC Year */
-       0x0000,     /* R20  - Alarm Seconds/Minutes */
-       0x0000,     /* R21  - Alarm Hours/Day */
-       0x0000,     /* R22  - Alarm Date/Month */
-       0x0320,     /* R23  - RTC Time Control */
-       0x0000,     /* R24  - System Interrupts */
-       0x0000,     /* R25  - Interrupt Status 1 */
-       0x0000,     /* R26  - Interrupt Status 2 */
-       0x0000,     /* R27  - Power Up Interrupt Status */
-       0x0000,     /* R28  - Under Voltage Interrupt status */
-       0x0000,     /* R29  - Over Current Interrupt status */
-       0x0000,     /* R30  - GPIO Interrupt Status */
-       0x0000,     /* R31  - Comparator Interrupt Status */
-       0x3FFF,     /* R32  - System Interrupts Mask */
-       0x0000,     /* R33  - Interrupt Status 1 Mask */
-       0x0000,     /* R34  - Interrupt Status 2 Mask */
-       0x0000,     /* R35  - Power Up Interrupt Status Mask */
-       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
-       0x0000,     /* R37  - Over Current Interrupt status Mask */
-       0x0000,     /* R38  - GPIO Interrupt Status Mask */
-       0x0000,     /* R39  - Comparator Interrupt Status Mask */
-       0x0040,     /* R40  - Clock Control 1 */
-       0x0000,     /* R41  - Clock Control 2 */
-       0x3B00,     /* R42  - FLL Control 1 */
-       0x7086,     /* R43  - FLL Control 2 */
-       0xC226,     /* R44  - FLL Control 3 */
-       0x0000,     /* R45  - FLL Control 4 */
-       0x0000,     /* R46 */
-       0x0000,     /* R47 */
-       0x0000,     /* R48  - DAC Control */
-       0x0000,     /* R49 */
-       0x00C0,     /* R50  - DAC Digital Volume L */
-       0x00C0,     /* R51  - DAC Digital Volume R */
-       0x0000,     /* R52 */
-       0x0040,     /* R53  - DAC LR Rate */
-       0x0000,     /* R54  - DAC Clock Control */
-       0x0000,     /* R55 */
-       0x0000,     /* R56 */
-       0x0000,     /* R57 */
-       0x4000,     /* R58  - DAC Mute */
-       0x0000,     /* R59  - DAC Mute Volume */
-       0x0000,     /* R60  - DAC Side */
-       0x0000,     /* R61 */
-       0x0000,     /* R62 */
-       0x0000,     /* R63 */
-       0x8000,     /* R64  - ADC Control */
-       0x0000,     /* R65 */
-       0x00C0,     /* R66  - ADC Digital Volume L */
-       0x00C0,     /* R67  - ADC Digital Volume R */
-       0x0000,     /* R68  - ADC Divider */
-       0x0000,     /* R69 */
-       0x0040,     /* R70  - ADC LR Rate */
-       0x0000,     /* R71 */
-       0x0303,     /* R72  - Input Control */
-       0x0000,     /* R73  - IN3 Input Control */
-       0x0000,     /* R74  - Mic Bias Control */
-       0x0000,     /* R75 */
-       0x0000,     /* R76  - Output Control */
-       0x0000,     /* R77  - Jack Detect */
-       0x0000,     /* R78  - Anti Pop Control */
-       0x0000,     /* R79 */
-       0x0040,     /* R80  - Left Input Volume */
-       0x0040,     /* R81  - Right Input Volume */
-       0x0000,     /* R82 */
-       0x0000,     /* R83 */
-       0x0000,     /* R84 */
-       0x0000,     /* R85 */
-       0x0000,     /* R86 */
-       0x0000,     /* R87 */
-       0x0800,     /* R88  - Left Mixer Control */
-       0x1000,     /* R89  - Right Mixer Control */
-       0x0000,     /* R90 */
-       0x0000,     /* R91 */
-       0x0000,     /* R92  - OUT3 Mixer Control */
-       0x0000,     /* R93  - OUT4 Mixer Control */
-       0x0000,     /* R94 */
-       0x0000,     /* R95 */
-       0x0000,     /* R96  - Output Left Mixer Volume */
-       0x0000,     /* R97  - Output Right Mixer Volume */
-       0x0000,     /* R98  - Input Mixer Volume L */
-       0x0000,     /* R99  - Input Mixer Volume R */
-       0x0000,     /* R100 - Input Mixer Volume */
-       0x0000,     /* R101 */
-       0x0000,     /* R102 */
-       0x0000,     /* R103 */
-       0x00E4,     /* R104 - LOUT1 Volume */
-       0x00E4,     /* R105 - ROUT1 Volume */
-       0x00E4,     /* R106 - LOUT2 Volume */
-       0x02E4,     /* R107 - ROUT2 Volume */
-       0x0000,     /* R108 */
-       0x0000,     /* R109 */
-       0x0000,     /* R110 */
-       0x0000,     /* R111 - BEEP Volume */
-       0x0A00,     /* R112 - AI Formating */
-       0x0000,     /* R113 - ADC DAC COMP */
-       0x0020,     /* R114 - AI ADC Control */
-       0x0020,     /* R115 - AI DAC Control */
-       0x0000,     /* R116 - AIF Test */
-       0x0000,     /* R117 */
-       0x0000,     /* R118 */
-       0x0000,     /* R119 */
-       0x0000,     /* R120 */
-       0x0000,     /* R121 */
-       0x0000,     /* R122 */
-       0x0000,     /* R123 */
-       0x0000,     /* R124 */
-       0x0000,     /* R125 */
-       0x0000,     /* R126 */
-       0x0000,     /* R127 */
-       0x1FFF,     /* R128 - GPIO Debounce */
-       0x0000,     /* R129 - GPIO Pin pull up Control */
-       0x03FC,     /* R130 - GPIO Pull down Control */
-       0x0000,     /* R131 - GPIO Interrupt Mode */
-       0x0000,     /* R132 */
-       0x0000,     /* R133 - GPIO Control */
-       0x0A7B,     /* R134 - GPIO Configuration (i/o) */
-       0x06FE,     /* R135 - GPIO Pin Polarity / Type */
-       0x0000,     /* R136 */
-       0x0000,     /* R137 */
-       0x0000,     /* R138 */
-       0x0000,     /* R139 */
-       0x1312,     /* R140 - GPIO Function Select 1 */
-       0x1030,     /* R141 - GPIO Function Select 2 */
-       0x2231,     /* R142 - GPIO Function Select 3 */
-       0x0003,     /* R143 - GPIO Function Select 4 */
-       0x0000,     /* R144 - Digitiser Control (1) */
-       0x0002,     /* R145 - Digitiser Control (2) */
-       0x0000,     /* R146 */
-       0x0000,     /* R147 */
-       0x0000,     /* R148 */
-       0x0000,     /* R149 */
-       0x0000,     /* R150 */
-       0x0000,     /* R151 */
-       0x7000,     /* R152 - AUX1 Readback */
-       0x7000,     /* R153 - AUX2 Readback */
-       0x7000,     /* R154 - AUX3 Readback */
-       0x7000,     /* R155 - AUX4 Readback */
-       0x0000,     /* R156 - USB Voltage Readback */
-       0x0000,     /* R157 - LINE Voltage Readback */
-       0x0000,     /* R158 - BATT Voltage Readback */
-       0x0000,     /* R159 - Chip Temp Readback */
-       0x0000,     /* R160 */
-       0x0000,     /* R161 */
-       0x0000,     /* R162 */
-       0x0000,     /* R163 - Generic Comparator Control */
-       0x0000,     /* R164 - Generic comparator 1 */
-       0x0000,     /* R165 - Generic comparator 2 */
-       0x0000,     /* R166 - Generic comparator 3 */
-       0x0000,     /* R167 - Generic comparator 4 */
-       0xA00F,     /* R168 - Battery Charger Control 1 */
-       0x0B06,     /* R169 - Battery Charger Control 2 */
-       0x0000,     /* R170 - Battery Charger Control 3 */
-       0x0000,     /* R171 */
-       0x0000,     /* R172 - Current Sink Driver A */
-       0x0000,     /* R173 - CSA Flash control */
-       0x0000,     /* R174 - Current Sink Driver B */
-       0x0000,     /* R175 - CSB Flash control */
-       0x0000,     /* R176 - DCDC/LDO requested */
-       0x002D,     /* R177 - DCDC Active options */
-       0x0000,     /* R178 - DCDC Sleep options */
-       0x0025,     /* R179 - Power-check comparator */
-       0x000E,     /* R180 - DCDC1 Control */
-       0x0400,     /* R181 - DCDC1 Timeouts */
-       0x1006,     /* R182 - DCDC1 Low Power */
-       0x0018,     /* R183 - DCDC2 Control */
-       0x0000,     /* R184 - DCDC2 Timeouts */
-       0x0000,     /* R185 */
-       0x000E,     /* R186 - DCDC3 Control */
-       0x0400,     /* R187 - DCDC3 Timeouts */
-       0x0006,     /* R188 - DCDC3 Low Power */
-       0x0026,     /* R189 - DCDC4 Control */
-       0x0400,     /* R190 - DCDC4 Timeouts */
-       0x0006,     /* R191 - DCDC4 Low Power */
-       0x0008,     /* R192 - DCDC5 Control */
-       0x0000,     /* R193 - DCDC5 Timeouts */
-       0x0000,     /* R194 */
-       0x0026,     /* R195 - DCDC6 Control */
-       0x0400,     /* R196 - DCDC6 Timeouts */
-       0x0006,     /* R197 - DCDC6 Low Power */
-       0x0000,     /* R198 */
-       0x0003,     /* R199 - Limit Switch Control */
-       0x001C,     /* R200 - LDO1 Control */
-       0x0000,     /* R201 - LDO1 Timeouts */
-       0x001C,     /* R202 - LDO1 Low Power */
-       0x001C,     /* R203 - LDO2 Control */
-       0x0400,     /* R204 - LDO2 Timeouts */
-       0x001C,     /* R205 - LDO2 Low Power */
-       0x001C,     /* R206 - LDO3 Control */
-       0x0400,     /* R207 - LDO3 Timeouts */
-       0x001C,     /* R208 - LDO3 Low Power */
-       0x001F,     /* R209 - LDO4 Control */
-       0x0400,     /* R210 - LDO4 Timeouts */
-       0x001C,     /* R211 - LDO4 Low Power */
-       0x0000,     /* R212 */
-       0x0000,     /* R213 */
-       0x0000,     /* R214 */
-       0x0000,     /* R215 - VCC_FAULT Masks */
-       0x001F,     /* R216 - Main Bandgap Control */
-       0x0000,     /* R217 - OSC Control */
-       0x9000,     /* R218 - RTC Tick Control */
-       0x0000,     /* R219 */
-       0x4000,     /* R220 - RAM BIST 1 */
-       0x0000,     /* R221 */
-       0x0000,     /* R222 */
-       0x0000,     /* R223 */
-       0x0000,     /* R224 */
-       0x0000,     /* R225 - DCDC/LDO status */
-       0x0000,     /* R226 */
-       0x0000,     /* R227 */
-       0x0000,     /* R228 */
-       0x0000,     /* R229 */
-       0xE000,     /* R230 - GPIO Pin Status */
-       0x0000,     /* R231 */
-       0x0000,     /* R232 */
-       0x0000,     /* R233 */
-       0x0000,     /* R234 */
-       0x0000,     /* R235 */
-       0x0000,     /* R236 */
-       0x0000,     /* R237 */
-       0x0000,     /* R238 */
-       0x0000,     /* R239 */
-       0x0000,     /* R240 */
-       0x0000,     /* R241 */
-       0x0000,     /* R242 */
-       0x0000,     /* R243 */
-       0x0000,     /* R244 */
-       0x0000,     /* R245 */
-       0x0000,     /* R246 */
-       0x0000,     /* R247 */
-       0x0000,     /* R248 */
-       0x0000,     /* R249 */
-       0x0000,     /* R250 */
-       0x0000,     /* R251 */
-       0x0000,     /* R252 */
-       0x0000,     /* R253 */
-       0x0000,     /* R254 */
-       0x0000,     /* R255 */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8351_mode0_defaults[] = {
-       0x6143,     /* R0   - Reset/ID */
-       0x0000,     /* R1   - ID */
-       0x0001,     /* R2   - Revision */
-       0x1C02,     /* R3   - System Control 1 */
-       0x0004,     /* R4   - System Control 2 */
-       0x0000,     /* R5   - System Hibernate */
-       0x8A00,     /* R6   - Interface Control */
-       0x0000,     /* R7 */
-       0x8000,     /* R8   - Power mgmt (1) */
-       0x0000,     /* R9   - Power mgmt (2) */
-       0x0000,     /* R10  - Power mgmt (3) */
-       0x2000,     /* R11  - Power mgmt (4) */
-       0x0E00,     /* R12  - Power mgmt (5) */
-       0x0000,     /* R13  - Power mgmt (6) */
-       0x0000,     /* R14  - Power mgmt (7) */
-       0x0000,     /* R15 */
-       0x0000,     /* R16  - RTC Seconds/Minutes */
-       0x0100,     /* R17  - RTC Hours/Day */
-       0x0101,     /* R18  - RTC Date/Month */
-       0x1400,     /* R19  - RTC Year */
-       0x0000,     /* R20  - Alarm Seconds/Minutes */
-       0x0000,     /* R21  - Alarm Hours/Day */
-       0x0000,     /* R22  - Alarm Date/Month */
-       0x0320,     /* R23  - RTC Time Control */
-       0x0000,     /* R24  - System Interrupts */
-       0x0000,     /* R25  - Interrupt Status 1 */
-       0x0000,     /* R26  - Interrupt Status 2 */
-       0x0000,     /* R27 */
-       0x0000,     /* R28  - Under Voltage Interrupt status */
-       0x0000,     /* R29  - Over Current Interrupt status */
-       0x0000,     /* R30  - GPIO Interrupt Status */
-       0x0000,     /* R31  - Comparator Interrupt Status */
-       0x3FFF,     /* R32  - System Interrupts Mask */
-       0x0000,     /* R33  - Interrupt Status 1 Mask */
-       0x0000,     /* R34  - Interrupt Status 2 Mask */
-       0x0000,     /* R35 */
-       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
-       0x0000,     /* R37  - Over Current Interrupt status Mask */
-       0x0000,     /* R38  - GPIO Interrupt Status Mask */
-       0x0000,     /* R39  - Comparator Interrupt Status Mask */
-       0x0040,     /* R40  - Clock Control 1 */
-       0x0000,     /* R41  - Clock Control 2 */
-       0x3A00,     /* R42  - FLL Control 1 */
-       0x7086,     /* R43  - FLL Control 2 */
-       0xC226,     /* R44  - FLL Control 3 */
-       0x0000,     /* R45  - FLL Control 4 */
-       0x0000,     /* R46 */
-       0x0000,     /* R47 */
-       0x0000,     /* R48  - DAC Control */
-       0x0000,     /* R49 */
-       0x00C0,     /* R50  - DAC Digital Volume L */
-       0x00C0,     /* R51  - DAC Digital Volume R */
-       0x0000,     /* R52 */
-       0x0040,     /* R53  - DAC LR Rate */
-       0x0000,     /* R54  - DAC Clock Control */
-       0x0000,     /* R55 */
-       0x0000,     /* R56 */
-       0x0000,     /* R57 */
-       0x4000,     /* R58  - DAC Mute */
-       0x0000,     /* R59  - DAC Mute Volume */
-       0x0000,     /* R60  - DAC Side */
-       0x0000,     /* R61 */
-       0x0000,     /* R62 */
-       0x0000,     /* R63 */
-       0x8000,     /* R64  - ADC Control */
-       0x0000,     /* R65 */
-       0x00C0,     /* R66  - ADC Digital Volume L */
-       0x00C0,     /* R67  - ADC Digital Volume R */
-       0x0000,     /* R68  - ADC Divider */
-       0x0000,     /* R69 */
-       0x0040,     /* R70  - ADC LR Rate */
-       0x0000,     /* R71 */
-       0x0303,     /* R72  - Input Control */
-       0x0000,     /* R73  - IN3 Input Control */
-       0x0000,     /* R74  - Mic Bias Control */
-       0x0000,     /* R75 */
-       0x0000,     /* R76  - Output Control */
-       0x0000,     /* R77  - Jack Detect */
-       0x0000,     /* R78  - Anti Pop Control */
-       0x0000,     /* R79 */
-       0x0040,     /* R80  - Left Input Volume */
-       0x0040,     /* R81  - Right Input Volume */
-       0x0000,     /* R82 */
-       0x0000,     /* R83 */
-       0x0000,     /* R84 */
-       0x0000,     /* R85 */
-       0x0000,     /* R86 */
-       0x0000,     /* R87 */
-       0x0800,     /* R88  - Left Mixer Control */
-       0x1000,     /* R89  - Right Mixer Control */
-       0x0000,     /* R90 */
-       0x0000,     /* R91 */
-       0x0000,     /* R92  - OUT3 Mixer Control */
-       0x0000,     /* R93  - OUT4 Mixer Control */
-       0x0000,     /* R94 */
-       0x0000,     /* R95 */
-       0x0000,     /* R96  - Output Left Mixer Volume */
-       0x0000,     /* R97  - Output Right Mixer Volume */
-       0x0000,     /* R98  - Input Mixer Volume L */
-       0x0000,     /* R99  - Input Mixer Volume R */
-       0x0000,     /* R100 - Input Mixer Volume */
-       0x0000,     /* R101 */
-       0x0000,     /* R102 */
-       0x0000,     /* R103 */
-       0x00E4,     /* R104 - OUT1L Volume */
-       0x00E4,     /* R105 - OUT1R Volume */
-       0x00E4,     /* R106 - OUT2L Volume */
-       0x02E4,     /* R107 - OUT2R Volume */
-       0x0000,     /* R108 */
-       0x0000,     /* R109 */
-       0x0000,     /* R110 */
-       0x0000,     /* R111 - BEEP Volume */
-       0x0A00,     /* R112 - AI Formating */
-       0x0000,     /* R113 - ADC DAC COMP */
-       0x0020,     /* R114 - AI ADC Control */
-       0x0020,     /* R115 - AI DAC Control */
-       0x0000,     /* R116 */
-       0x0000,     /* R117 */
-       0x0000,     /* R118 */
-       0x0000,     /* R119 */
-       0x0000,     /* R120 */
-       0x0000,     /* R121 */
-       0x0000,     /* R122 */
-       0x0000,     /* R123 */
-       0x0000,     /* R124 */
-       0x0000,     /* R125 */
-       0x0000,     /* R126 */
-       0x0000,     /* R127 */
-       0x1FFF,     /* R128 - GPIO Debounce */
-       0x0000,     /* R129 - GPIO Pin pull up Control */
-       0x0000,     /* R130 - GPIO Pull down Control */
-       0x0000,     /* R131 - GPIO Interrupt Mode */
-       0x0000,     /* R132 */
-       0x0000,     /* R133 - GPIO Control */
-       0x0FFC,     /* R134 - GPIO Configuration (i/o) */
-       0x0FFC,     /* R135 - GPIO Pin Polarity / Type */
-       0x0000,     /* R136 */
-       0x0000,     /* R137 */
-       0x0000,     /* R138 */
-       0x0000,     /* R139 */
-       0x0013,     /* R140 - GPIO Function Select 1 */
-       0x0000,     /* R141 - GPIO Function Select 2 */
-       0x0000,     /* R142 - GPIO Function Select 3 */
-       0x0003,     /* R143 - GPIO Function Select 4 */
-       0x0000,     /* R144 - Digitiser Control (1) */
-       0x0002,     /* R145 - Digitiser Control (2) */
-       0x0000,     /* R146 */
-       0x0000,     /* R147 */
-       0x0000,     /* R148 */
-       0x0000,     /* R149 */
-       0x0000,     /* R150 */
-       0x0000,     /* R151 */
-       0x7000,     /* R152 - AUX1 Readback */
-       0x7000,     /* R153 - AUX2 Readback */
-       0x7000,     /* R154 - AUX3 Readback */
-       0x7000,     /* R155 - AUX4 Readback */
-       0x0000,     /* R156 - USB Voltage Readback */
-       0x0000,     /* R157 - LINE Voltage Readback */
-       0x0000,     /* R158 - BATT Voltage Readback */
-       0x0000,     /* R159 - Chip Temp Readback */
-       0x0000,     /* R160 */
-       0x0000,     /* R161 */
-       0x0000,     /* R162 */
-       0x0000,     /* R163 - Generic Comparator Control */
-       0x0000,     /* R164 - Generic comparator 1 */
-       0x0000,     /* R165 - Generic comparator 2 */
-       0x0000,     /* R166 - Generic comparator 3 */
-       0x0000,     /* R167 - Generic comparator 4 */
-       0xA00F,     /* R168 - Battery Charger Control 1 */
-       0x0B06,     /* R169 - Battery Charger Control 2 */
-       0x0000,     /* R170 - Battery Charger Control 3 */
-       0x0000,     /* R171 */
-       0x0000,     /* R172 - Current Sink Driver A */
-       0x0000,     /* R173 - CSA Flash control */
-       0x0000,     /* R174 */
-       0x0000,     /* R175 */
-       0x0000,     /* R176 - DCDC/LDO requested */
-       0x032D,     /* R177 - DCDC Active options */
-       0x0000,     /* R178 - DCDC Sleep options */
-       0x0025,     /* R179 - Power-check comparator */
-       0x000E,     /* R180 - DCDC1 Control */
-       0x0000,     /* R181 - DCDC1 Timeouts */
-       0x1006,     /* R182 - DCDC1 Low Power */
-       0x0018,     /* R183 - DCDC2 Control */
-       0x0000,     /* R184 - DCDC2 Timeouts */
-       0x0000,     /* R185 */
-       0x0000,     /* R186 - DCDC3 Control */
-       0x0000,     /* R187 - DCDC3 Timeouts */
-       0x0006,     /* R188 - DCDC3 Low Power */
-       0x0000,     /* R189 - DCDC4 Control */
-       0x0000,     /* R190 - DCDC4 Timeouts */
-       0x0006,     /* R191 - DCDC4 Low Power */
-       0x0008,     /* R192 */
-       0x0000,     /* R193 */
-       0x0000,     /* R194 */
-       0x0000,     /* R195 */
-       0x0000,     /* R196 */
-       0x0006,     /* R197 */
-       0x0000,     /* R198 */
-       0x0003,     /* R199 - Limit Switch Control */
-       0x001C,     /* R200 - LDO1 Control */
-       0x0000,     /* R201 - LDO1 Timeouts */
-       0x001C,     /* R202 - LDO1 Low Power */
-       0x001B,     /* R203 - LDO2 Control */
-       0x0000,     /* R204 - LDO2 Timeouts */
-       0x001C,     /* R205 - LDO2 Low Power */
-       0x001B,     /* R206 - LDO3 Control */
-       0x0000,     /* R207 - LDO3 Timeouts */
-       0x001C,     /* R208 - LDO3 Low Power */
-       0x001B,     /* R209 - LDO4 Control */
-       0x0000,     /* R210 - LDO4 Timeouts */
-       0x001C,     /* R211 - LDO4 Low Power */
-       0x0000,     /* R212 */
-       0x0000,     /* R213 */
-       0x0000,     /* R214 */
-       0x0000,     /* R215 - VCC_FAULT Masks */
-       0x001F,     /* R216 - Main Bandgap Control */
-       0x0000,     /* R217 - OSC Control */
-       0x9000,     /* R218 - RTC Tick Control */
-       0x0000,     /* R219 - Security1 */
-       0x4000,     /* R220 */
-       0x0000,     /* R221 */
-       0x0000,     /* R222 */
-       0x0000,     /* R223 */
-       0x0000,     /* R224 - Signal overrides */
-       0x0000,     /* R225 - DCDC/LDO status */
-       0x0000,     /* R226 - Charger Overides/status */
-       0x0000,     /* R227 - misc overrides */
-       0x0000,     /* R228 - Supply overrides/status 1 */
-       0x0000,     /* R229 - Supply overrides/status 2 */
-       0xE000,     /* R230 - GPIO Pin Status */
-       0x0000,     /* R231 - comparotor overrides */
-       0x0000,     /* R232 */
-       0x0000,     /* R233 - State Machine status */
-       0x1200,     /* R234 - FLL Test 1 */
-       0x0000,     /* R235 */
-       0x8000,     /* R236 */
-       0x0000,     /* R237 */
-       0x0000,     /* R238 */
-       0x0000,     /* R239 */
-       0x0003,     /* R240 */
-       0x0000,     /* R241 */
-       0x0000,     /* R242 */
-       0x0004,     /* R243 */
-       0x0300,     /* R244 */
-       0x0000,     /* R245 */
-       0x0200,     /* R246 */
-       0x0000,     /* R247 */
-       0x1000,     /* R248 - DCDC1 Test Controls */
-       0x1000,     /* R249 */
-       0x1000,     /* R250 - DCDC3 Test Controls */
-       0x1000,     /* R251 - DCDC4 Test Controls */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8351_mode1_defaults[] = {
-       0x6143,     /* R0   - Reset/ID */
-       0x0000,     /* R1   - ID */
-       0x0001,     /* R2   - Revision */
-       0x1C02,     /* R3   - System Control 1 */
-       0x0204,     /* R4   - System Control 2 */
-       0x0000,     /* R5   - System Hibernate */
-       0x8A00,     /* R6   - Interface Control */
-       0x0000,     /* R7 */
-       0x8000,     /* R8   - Power mgmt (1) */
-       0x0000,     /* R9   - Power mgmt (2) */
-       0x0000,     /* R10  - Power mgmt (3) */
-       0x2000,     /* R11  - Power mgmt (4) */
-       0x0E00,     /* R12  - Power mgmt (5) */
-       0x0000,     /* R13  - Power mgmt (6) */
-       0x0000,     /* R14  - Power mgmt (7) */
-       0x0000,     /* R15 */
-       0x0000,     /* R16  - RTC Seconds/Minutes */
-       0x0100,     /* R17  - RTC Hours/Day */
-       0x0101,     /* R18  - RTC Date/Month */
-       0x1400,     /* R19  - RTC Year */
-       0x0000,     /* R20  - Alarm Seconds/Minutes */
-       0x0000,     /* R21  - Alarm Hours/Day */
-       0x0000,     /* R22  - Alarm Date/Month */
-       0x0320,     /* R23  - RTC Time Control */
-       0x0000,     /* R24  - System Interrupts */
-       0x0000,     /* R25  - Interrupt Status 1 */
-       0x0000,     /* R26  - Interrupt Status 2 */
-       0x0000,     /* R27 */
-       0x0000,     /* R28  - Under Voltage Interrupt status */
-       0x0000,     /* R29  - Over Current Interrupt status */
-       0x0000,     /* R30  - GPIO Interrupt Status */
-       0x0000,     /* R31  - Comparator Interrupt Status */
-       0x3FFF,     /* R32  - System Interrupts Mask */
-       0x0000,     /* R33  - Interrupt Status 1 Mask */
-       0x0000,     /* R34  - Interrupt Status 2 Mask */
-       0x0000,     /* R35 */
-       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
-       0x0000,     /* R37  - Over Current Interrupt status Mask */
-       0x0000,     /* R38  - GPIO Interrupt Status Mask */
-       0x0000,     /* R39  - Comparator Interrupt Status Mask */
-       0x0040,     /* R40  - Clock Control 1 */
-       0x0000,     /* R41  - Clock Control 2 */
-       0x3A00,     /* R42  - FLL Control 1 */
-       0x7086,     /* R43  - FLL Control 2 */
-       0xC226,     /* R44  - FLL Control 3 */
-       0x0000,     /* R45  - FLL Control 4 */
-       0x0000,     /* R46 */
-       0x0000,     /* R47 */
-       0x0000,     /* R48  - DAC Control */
-       0x0000,     /* R49 */
-       0x00C0,     /* R50  - DAC Digital Volume L */
-       0x00C0,     /* R51  - DAC Digital Volume R */
-       0x0000,     /* R52 */
-       0x0040,     /* R53  - DAC LR Rate */
-       0x0000,     /* R54  - DAC Clock Control */
-       0x0000,     /* R55 */
-       0x0000,     /* R56 */
-       0x0000,     /* R57 */
-       0x4000,     /* R58  - DAC Mute */
-       0x0000,     /* R59  - DAC Mute Volume */
-       0x0000,     /* R60  - DAC Side */
-       0x0000,     /* R61 */
-       0x0000,     /* R62 */
-       0x0000,     /* R63 */
-       0x8000,     /* R64  - ADC Control */
-       0x0000,     /* R65 */
-       0x00C0,     /* R66  - ADC Digital Volume L */
-       0x00C0,     /* R67  - ADC Digital Volume R */
-       0x0000,     /* R68  - ADC Divider */
-       0x0000,     /* R69 */
-       0x0040,     /* R70  - ADC LR Rate */
-       0x0000,     /* R71 */
-       0x0303,     /* R72  - Input Control */
-       0x0000,     /* R73  - IN3 Input Control */
-       0x0000,     /* R74  - Mic Bias Control */
-       0x0000,     /* R75 */
-       0x0000,     /* R76  - Output Control */
-       0x0000,     /* R77  - Jack Detect */
-       0x0000,     /* R78  - Anti Pop Control */
-       0x0000,     /* R79 */
-       0x0040,     /* R80  - Left Input Volume */
-       0x0040,     /* R81  - Right Input Volume */
-       0x0000,     /* R82 */
-       0x0000,     /* R83 */
-       0x0000,     /* R84 */
-       0x0000,     /* R85 */
-       0x0000,     /* R86 */
-       0x0000,     /* R87 */
-       0x0800,     /* R88  - Left Mixer Control */
-       0x1000,     /* R89  - Right Mixer Control */
-       0x0000,     /* R90 */
-       0x0000,     /* R91 */
-       0x0000,     /* R92  - OUT3 Mixer Control */
-       0x0000,     /* R93  - OUT4 Mixer Control */
-       0x0000,     /* R94 */
-       0x0000,     /* R95 */
-       0x0000,     /* R96  - Output Left Mixer Volume */
-       0x0000,     /* R97  - Output Right Mixer Volume */
-       0x0000,     /* R98  - Input Mixer Volume L */
-       0x0000,     /* R99  - Input Mixer Volume R */
-       0x0000,     /* R100 - Input Mixer Volume */
-       0x0000,     /* R101 */
-       0x0000,     /* R102 */
-       0x0000,     /* R103 */
-       0x00E4,     /* R104 - OUT1L Volume */
-       0x00E4,     /* R105 - OUT1R Volume */
-       0x00E4,     /* R106 - OUT2L Volume */
-       0x02E4,     /* R107 - OUT2R Volume */
-       0x0000,     /* R108 */
-       0x0000,     /* R109 */
-       0x0000,     /* R110 */
-       0x0000,     /* R111 - BEEP Volume */
-       0x0A00,     /* R112 - AI Formating */
-       0x0000,     /* R113 - ADC DAC COMP */
-       0x0020,     /* R114 - AI ADC Control */
-       0x0020,     /* R115 - AI DAC Control */
-       0x0000,     /* R116 */
-       0x0000,     /* R117 */
-       0x0000,     /* R118 */
-       0x0000,     /* R119 */
-       0x0000,     /* R120 */
-       0x0000,     /* R121 */
-       0x0000,     /* R122 */
-       0x0000,     /* R123 */
-       0x0000,     /* R124 */
-       0x0000,     /* R125 */
-       0x0000,     /* R126 */
-       0x0000,     /* R127 */
-       0x1FFF,     /* R128 - GPIO Debounce */
-       0x0000,     /* R129 - GPIO Pin pull up Control */
-       0x0000,     /* R130 - GPIO Pull down Control */
-       0x0000,     /* R131 - GPIO Interrupt Mode */
-       0x0000,     /* R132 */
-       0x0000,     /* R133 - GPIO Control */
-       0x0CFB,     /* R134 - GPIO Configuration (i/o) */
-       0x0C1F,     /* R135 - GPIO Pin Polarity / Type */
-       0x0000,     /* R136 */
-       0x0000,     /* R137 */
-       0x0000,     /* R138 */
-       0x0000,     /* R139 */
-       0x0300,     /* R140 - GPIO Function Select 1 */
-       0x1110,     /* R141 - GPIO Function Select 2 */
-       0x0013,     /* R142 - GPIO Function Select 3 */
-       0x0003,     /* R143 - GPIO Function Select 4 */
-       0x0000,     /* R144 - Digitiser Control (1) */
-       0x0002,     /* R145 - Digitiser Control (2) */
-       0x0000,     /* R146 */
-       0x0000,     /* R147 */
-       0x0000,     /* R148 */
-       0x0000,     /* R149 */
-       0x0000,     /* R150 */
-       0x0000,     /* R151 */
-       0x7000,     /* R152 - AUX1 Readback */
-       0x7000,     /* R153 - AUX2 Readback */
-       0x7000,     /* R154 - AUX3 Readback */
-       0x7000,     /* R155 - AUX4 Readback */
-       0x0000,     /* R156 - USB Voltage Readback */
-       0x0000,     /* R157 - LINE Voltage Readback */
-       0x0000,     /* R158 - BATT Voltage Readback */
-       0x0000,     /* R159 - Chip Temp Readback */
-       0x0000,     /* R160 */
-       0x0000,     /* R161 */
-       0x0000,     /* R162 */
-       0x0000,     /* R163 - Generic Comparator Control */
-       0x0000,     /* R164 - Generic comparator 1 */
-       0x0000,     /* R165 - Generic comparator 2 */
-       0x0000,     /* R166 - Generic comparator 3 */
-       0x0000,     /* R167 - Generic comparator 4 */
-       0xA00F,     /* R168 - Battery Charger Control 1 */
-       0x0B06,     /* R169 - Battery Charger Control 2 */
-       0x0000,     /* R170 - Battery Charger Control 3 */
-       0x0000,     /* R171 */
-       0x0000,     /* R172 - Current Sink Driver A */
-       0x0000,     /* R173 - CSA Flash control */
-       0x0000,     /* R174 */
-       0x0000,     /* R175 */
-       0x0000,     /* R176 - DCDC/LDO requested */
-       0x032D,     /* R177 - DCDC Active options */
-       0x0000,     /* R178 - DCDC Sleep options */
-       0x0025,     /* R179 - Power-check comparator */
-       0x000E,     /* R180 - DCDC1 Control */
-       0x0C00,     /* R181 - DCDC1 Timeouts */
-       0x1006,     /* R182 - DCDC1 Low Power */
-       0x0018,     /* R183 - DCDC2 Control */
-       0x0000,     /* R184 - DCDC2 Timeouts */
-       0x0000,     /* R185 */
-       0x0026,     /* R186 - DCDC3 Control */
-       0x0400,     /* R187 - DCDC3 Timeouts */
-       0x0006,     /* R188 - DCDC3 Low Power */
-       0x0062,     /* R189 - DCDC4 Control */
-       0x0800,     /* R190 - DCDC4 Timeouts */
-       0x0006,     /* R191 - DCDC4 Low Power */
-       0x0008,     /* R192 */
-       0x0000,     /* R193 */
-       0x0000,     /* R194 */
-       0x000A,     /* R195 */
-       0x1000,     /* R196 */
-       0x0006,     /* R197 */
-       0x0000,     /* R198 */
-       0x0003,     /* R199 - Limit Switch Control */
-       0x0006,     /* R200 - LDO1 Control */
-       0x0000,     /* R201 - LDO1 Timeouts */
-       0x001C,     /* R202 - LDO1 Low Power */
-       0x0010,     /* R203 - LDO2 Control */
-       0x0C00,     /* R204 - LDO2 Timeouts */
-       0x001C,     /* R205 - LDO2 Low Power */
-       0x001F,     /* R206 - LDO3 Control */
-       0x0800,     /* R207 - LDO3 Timeouts */
-       0x001C,     /* R208 - LDO3 Low Power */
-       0x000A,     /* R209 - LDO4 Control */
-       0x0800,     /* R210 - LDO4 Timeouts */
-       0x001C,     /* R211 - LDO4 Low Power */
-       0x0000,     /* R212 */
-       0x0000,     /* R213 */
-       0x0000,     /* R214 */
-       0x0000,     /* R215 - VCC_FAULT Masks */
-       0x001F,     /* R216 - Main Bandgap Control */
-       0x0000,     /* R217 - OSC Control */
-       0x9000,     /* R218 - RTC Tick Control */
-       0x0000,     /* R219 - Security1 */
-       0x4000,     /* R220 */
-       0x0000,     /* R221 */
-       0x0000,     /* R222 */
-       0x0000,     /* R223 */
-       0x0000,     /* R224 - Signal overrides */
-       0x0000,     /* R225 - DCDC/LDO status */
-       0x0000,     /* R226 - Charger Overides/status */
-       0x0000,     /* R227 - misc overrides */
-       0x0000,     /* R228 - Supply overrides/status 1 */
-       0x0000,     /* R229 - Supply overrides/status 2 */
-       0xE000,     /* R230 - GPIO Pin Status */
-       0x0000,     /* R231 - comparotor overrides */
-       0x0000,     /* R232 */
-       0x0000,     /* R233 - State Machine status */
-       0x1200,     /* R234 - FLL Test 1 */
-       0x0000,     /* R235 */
-       0x8000,     /* R236 */
-       0x0000,     /* R237 */
-       0x0000,     /* R238 */
-       0x0000,     /* R239 */
-       0x0003,     /* R240 */
-       0x0000,     /* R241 */
-       0x0000,     /* R242 */
-       0x0004,     /* R243 */
-       0x0300,     /* R244 */
-       0x0000,     /* R245 */
-       0x0200,     /* R246 */
-       0x1000,     /* R247 */
-       0x1000,     /* R248 - DCDC1 Test Controls */
-       0x1000,     /* R249 */
-       0x1000,     /* R250 - DCDC3 Test Controls */
-       0x1000,     /* R251 - DCDC4 Test Controls */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8351_mode2_defaults[] = {
-       0x6143,     /* R0   - Reset/ID */
-       0x0000,     /* R1   - ID */
-       0x0001,     /* R2   - Revision */
-       0x1C02,     /* R3   - System Control 1 */
-       0x0214,     /* R4   - System Control 2 */
-       0x0000,     /* R5   - System Hibernate */
-       0x8A00,     /* R6   - Interface Control */
-       0x0000,     /* R7 */
-       0x8000,     /* R8   - Power mgmt (1) */
-       0x0000,     /* R9   - Power mgmt (2) */
-       0x0000,     /* R10  - Power mgmt (3) */
-       0x2000,     /* R11  - Power mgmt (4) */
-       0x0E00,     /* R12  - Power mgmt (5) */
-       0x0000,     /* R13  - Power mgmt (6) */
-       0x0000,     /* R14  - Power mgmt (7) */
-       0x0000,     /* R15 */
-       0x0000,     /* R16  - RTC Seconds/Minutes */
-       0x0100,     /* R17  - RTC Hours/Day */
-       0x0101,     /* R18  - RTC Date/Month */
-       0x1400,     /* R19  - RTC Year */
-       0x0000,     /* R20  - Alarm Seconds/Minutes */
-       0x0000,     /* R21  - Alarm Hours/Day */
-       0x0000,     /* R22  - Alarm Date/Month */
-       0x0320,     /* R23  - RTC Time Control */
-       0x0000,     /* R24  - System Interrupts */
-       0x0000,     /* R25  - Interrupt Status 1 */
-       0x0000,     /* R26  - Interrupt Status 2 */
-       0x0000,     /* R27 */
-       0x0000,     /* R28  - Under Voltage Interrupt status */
-       0x0000,     /* R29  - Over Current Interrupt status */
-       0x0000,     /* R30  - GPIO Interrupt Status */
-       0x0000,     /* R31  - Comparator Interrupt Status */
-       0x3FFF,     /* R32  - System Interrupts Mask */
-       0x0000,     /* R33  - Interrupt Status 1 Mask */
-       0x0000,     /* R34  - Interrupt Status 2 Mask */
-       0x0000,     /* R35 */
-       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
-       0x0000,     /* R37  - Over Current Interrupt status Mask */
-       0x0000,     /* R38  - GPIO Interrupt Status Mask */
-       0x0000,     /* R39  - Comparator Interrupt Status Mask */
-       0x0040,     /* R40  - Clock Control 1 */
-       0x0000,     /* R41  - Clock Control 2 */
-       0x3A00,     /* R42  - FLL Control 1 */
-       0x7086,     /* R43  - FLL Control 2 */
-       0xC226,     /* R44  - FLL Control 3 */
-       0x0000,     /* R45  - FLL Control 4 */
-       0x0000,     /* R46 */
-       0x0000,     /* R47 */
-       0x0000,     /* R48  - DAC Control */
-       0x0000,     /* R49 */
-       0x00C0,     /* R50  - DAC Digital Volume L */
-       0x00C0,     /* R51  - DAC Digital Volume R */
-       0x0000,     /* R52 */
-       0x0040,     /* R53  - DAC LR Rate */
-       0x0000,     /* R54  - DAC Clock Control */
-       0x0000,     /* R55 */
-       0x0000,     /* R56 */
-       0x0000,     /* R57 */
-       0x4000,     /* R58  - DAC Mute */
-       0x0000,     /* R59  - DAC Mute Volume */
-       0x0000,     /* R60  - DAC Side */
-       0x0000,     /* R61 */
-       0x0000,     /* R62 */
-       0x0000,     /* R63 */
-       0x8000,     /* R64  - ADC Control */
-       0x0000,     /* R65 */
-       0x00C0,     /* R66  - ADC Digital Volume L */
-       0x00C0,     /* R67  - ADC Digital Volume R */
-       0x0000,     /* R68  - ADC Divider */
-       0x0000,     /* R69 */
-       0x0040,     /* R70  - ADC LR Rate */
-       0x0000,     /* R71 */
-       0x0303,     /* R72  - Input Control */
-       0x0000,     /* R73  - IN3 Input Control */
-       0x0000,     /* R74  - Mic Bias Control */
-       0x0000,     /* R75 */
-       0x0000,     /* R76  - Output Control */
-       0x0000,     /* R77  - Jack Detect */
-       0x0000,     /* R78  - Anti Pop Control */
-       0x0000,     /* R79 */
-       0x0040,     /* R80  - Left Input Volume */
-       0x0040,     /* R81  - Right Input Volume */
-       0x0000,     /* R82 */
-       0x0000,     /* R83 */
-       0x0000,     /* R84 */
-       0x0000,     /* R85 */
-       0x0000,     /* R86 */
-       0x0000,     /* R87 */
-       0x0800,     /* R88  - Left Mixer Control */
-       0x1000,     /* R89  - Right Mixer Control */
-       0x0000,     /* R90 */
-       0x0000,     /* R91 */
-       0x0000,     /* R92  - OUT3 Mixer Control */
-       0x0000,     /* R93  - OUT4 Mixer Control */
-       0x0000,     /* R94 */
-       0x0000,     /* R95 */
-       0x0000,     /* R96  - Output Left Mixer Volume */
-       0x0000,     /* R97  - Output Right Mixer Volume */
-       0x0000,     /* R98  - Input Mixer Volume L */
-       0x0000,     /* R99  - Input Mixer Volume R */
-       0x0000,     /* R100 - Input Mixer Volume */
-       0x0000,     /* R101 */
-       0x0000,     /* R102 */
-       0x0000,     /* R103 */
-       0x00E4,     /* R104 - OUT1L Volume */
-       0x00E4,     /* R105 - OUT1R Volume */
-       0x00E4,     /* R106 - OUT2L Volume */
-       0x02E4,     /* R107 - OUT2R Volume */
-       0x0000,     /* R108 */
-       0x0000,     /* R109 */
-       0x0000,     /* R110 */
-       0x0000,     /* R111 - BEEP Volume */
-       0x0A00,     /* R112 - AI Formating */
-       0x0000,     /* R113 - ADC DAC COMP */
-       0x0020,     /* R114 - AI ADC Control */
-       0x0020,     /* R115 - AI DAC Control */
-       0x0000,     /* R116 */
-       0x0000,     /* R117 */
-       0x0000,     /* R118 */
-       0x0000,     /* R119 */
-       0x0000,     /* R120 */
-       0x0000,     /* R121 */
-       0x0000,     /* R122 */
-       0x0000,     /* R123 */
-       0x0000,     /* R124 */
-       0x0000,     /* R125 */
-       0x0000,     /* R126 */
-       0x0000,     /* R127 */
-       0x1FFF,     /* R128 - GPIO Debounce */
-       0x0000,     /* R129 - GPIO Pin pull up Control */
-       0x0110,     /* R130 - GPIO Pull down Control */
-       0x0000,     /* R131 - GPIO Interrupt Mode */
-       0x0000,     /* R132 */
-       0x0000,     /* R133 - GPIO Control */
-       0x09FA,     /* R134 - GPIO Configuration (i/o) */
-       0x0DF6,     /* R135 - GPIO Pin Polarity / Type */
-       0x0000,     /* R136 */
-       0x0000,     /* R137 */
-       0x0000,     /* R138 */
-       0x0000,     /* R139 */
-       0x1310,     /* R140 - GPIO Function Select 1 */
-       0x0003,     /* R141 - GPIO Function Select 2 */
-       0x2000,     /* R142 - GPIO Function Select 3 */
-       0x0000,     /* R143 - GPIO Function Select 4 */
-       0x0000,     /* R144 - Digitiser Control (1) */
-       0x0002,     /* R145 - Digitiser Control (2) */
-       0x0000,     /* R146 */
-       0x0000,     /* R147 */
-       0x0000,     /* R148 */
-       0x0000,     /* R149 */
-       0x0000,     /* R150 */
-       0x0000,     /* R151 */
-       0x7000,     /* R152 - AUX1 Readback */
-       0x7000,     /* R153 - AUX2 Readback */
-       0x7000,     /* R154 - AUX3 Readback */
-       0x7000,     /* R155 - AUX4 Readback */
-       0x0000,     /* R156 - USB Voltage Readback */
-       0x0000,     /* R157 - LINE Voltage Readback */
-       0x0000,     /* R158 - BATT Voltage Readback */
-       0x0000,     /* R159 - Chip Temp Readback */
-       0x0000,     /* R160 */
-       0x0000,     /* R161 */
-       0x0000,     /* R162 */
-       0x0000,     /* R163 - Generic Comparator Control */
-       0x0000,     /* R164 - Generic comparator 1 */
-       0x0000,     /* R165 - Generic comparator 2 */
-       0x0000,     /* R166 - Generic comparator 3 */
-       0x0000,     /* R167 - Generic comparator 4 */
-       0xA00F,     /* R168 - Battery Charger Control 1 */
-       0x0B06,     /* R169 - Battery Charger Control 2 */
-       0x0000,     /* R170 - Battery Charger Control 3 */
-       0x0000,     /* R171 */
-       0x0000,     /* R172 - Current Sink Driver A */
-       0x0000,     /* R173 - CSA Flash control */
-       0x0000,     /* R174 */
-       0x0000,     /* R175 */
-       0x0000,     /* R176 - DCDC/LDO requested */
-       0x032D,     /* R177 - DCDC Active options */
-       0x0000,     /* R178 - DCDC Sleep options */
-       0x0025,     /* R179 - Power-check comparator */
-       0x001A,     /* R180 - DCDC1 Control */
-       0x0800,     /* R181 - DCDC1 Timeouts */
-       0x1006,     /* R182 - DCDC1 Low Power */
-       0x0018,     /* R183 - DCDC2 Control */
-       0x0000,     /* R184 - DCDC2 Timeouts */
-       0x0000,     /* R185 */
-       0x0056,     /* R186 - DCDC3 Control */
-       0x0400,     /* R187 - DCDC3 Timeouts */
-       0x0006,     /* R188 - DCDC3 Low Power */
-       0x0026,     /* R189 - DCDC4 Control */
-       0x0C00,     /* R190 - DCDC4 Timeouts */
-       0x0006,     /* R191 - DCDC4 Low Power */
-       0x0008,     /* R192 */
-       0x0000,     /* R193 */
-       0x0000,     /* R194 */
-       0x0026,     /* R195 */
-       0x0C00,     /* R196 */
-       0x0006,     /* R197 */
-       0x0000,     /* R198 */
-       0x0003,     /* R199 - Limit Switch Control */
-       0x001C,     /* R200 - LDO1 Control */
-       0x0400,     /* R201 - LDO1 Timeouts */
-       0x001C,     /* R202 - LDO1 Low Power */
-       0x0010,     /* R203 - LDO2 Control */
-       0x0C00,     /* R204 - LDO2 Timeouts */
-       0x001C,     /* R205 - LDO2 Low Power */
-       0x0015,     /* R206 - LDO3 Control */
-       0x0000,     /* R207 - LDO3 Timeouts */
-       0x001C,     /* R208 - LDO3 Low Power */
-       0x001A,     /* R209 - LDO4 Control */
-       0x0000,     /* R210 - LDO4 Timeouts */
-       0x001C,     /* R211 - LDO4 Low Power */
-       0x0000,     /* R212 */
-       0x0000,     /* R213 */
-       0x0000,     /* R214 */
-       0x0000,     /* R215 - VCC_FAULT Masks */
-       0x001F,     /* R216 - Main Bandgap Control */
-       0x0000,     /* R217 - OSC Control */
-       0x9000,     /* R218 - RTC Tick Control */
-       0x0000,     /* R219 - Security1 */
-       0x4000,     /* R220 */
-       0x0000,     /* R221 */
-       0x0000,     /* R222 */
-       0x0000,     /* R223 */
-       0x0000,     /* R224 - Signal overrides */
-       0x0000,     /* R225 - DCDC/LDO status */
-       0x0000,     /* R226 - Charger Overides/status */
-       0x0000,     /* R227 - misc overrides */
-       0x0000,     /* R228 - Supply overrides/status 1 */
-       0x0000,     /* R229 - Supply overrides/status 2 */
-       0xE000,     /* R230 - GPIO Pin Status */
-       0x0000,     /* R231 - comparotor overrides */
-       0x0000,     /* R232 */
-       0x0000,     /* R233 - State Machine status */
-       0x1200,     /* R234 - FLL Test 1 */
-       0x0000,     /* R235 */
-       0x8000,     /* R236 */
-       0x0000,     /* R237 */
-       0x0000,     /* R238 */
-       0x0000,     /* R239 */
-       0x0003,     /* R240 */
-       0x0000,     /* R241 */
-       0x0000,     /* R242 */
-       0x0004,     /* R243 */
-       0x0300,     /* R244 */
-       0x0000,     /* R245 */
-       0x0200,     /* R246 */
-       0x0000,     /* R247 */
-       0x1000,     /* R248 - DCDC1 Test Controls */
-       0x1000,     /* R249 */
-       0x1000,     /* R250 - DCDC3 Test Controls */
-       0x1000,     /* R251 - DCDC4 Test Controls */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8351_mode3_defaults[] = {
-       0x6143,     /* R0   - Reset/ID */
-       0x0000,     /* R1   - ID */
-       0x0001,     /* R2   - Revision */
-       0x1C02,     /* R3   - System Control 1 */
-       0x0204,     /* R4   - System Control 2 */
-       0x0000,     /* R5   - System Hibernate */
-       0x8A00,     /* R6   - Interface Control */
-       0x0000,     /* R7 */
-       0x8000,     /* R8   - Power mgmt (1) */
-       0x0000,     /* R9   - Power mgmt (2) */
-       0x0000,     /* R10  - Power mgmt (3) */
-       0x2000,     /* R11  - Power mgmt (4) */
-       0x0E00,     /* R12  - Power mgmt (5) */
-       0x0000,     /* R13  - Power mgmt (6) */
-       0x0000,     /* R14  - Power mgmt (7) */
-       0x0000,     /* R15 */
-       0x0000,     /* R16  - RTC Seconds/Minutes */
-       0x0100,     /* R17  - RTC Hours/Day */
-       0x0101,     /* R18  - RTC Date/Month */
-       0x1400,     /* R19  - RTC Year */
-       0x0000,     /* R20  - Alarm Seconds/Minutes */
-       0x0000,     /* R21  - Alarm Hours/Day */
-       0x0000,     /* R22  - Alarm Date/Month */
-       0x0320,     /* R23  - RTC Time Control */
-       0x0000,     /* R24  - System Interrupts */
-       0x0000,     /* R25  - Interrupt Status 1 */
-       0x0000,     /* R26  - Interrupt Status 2 */
-       0x0000,     /* R27 */
-       0x0000,     /* R28  - Under Voltage Interrupt status */
-       0x0000,     /* R29  - Over Current Interrupt status */
-       0x0000,     /* R30  - GPIO Interrupt Status */
-       0x0000,     /* R31  - Comparator Interrupt Status */
-       0x3FFF,     /* R32  - System Interrupts Mask */
-       0x0000,     /* R33  - Interrupt Status 1 Mask */
-       0x0000,     /* R34  - Interrupt Status 2 Mask */
-       0x0000,     /* R35 */
-       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
-       0x0000,     /* R37  - Over Current Interrupt status Mask */
-       0x0000,     /* R38  - GPIO Interrupt Status Mask */
-       0x0000,     /* R39  - Comparator Interrupt Status Mask */
-       0x0040,     /* R40  - Clock Control 1 */
-       0x0000,     /* R41  - Clock Control 2 */
-       0x3A00,     /* R42  - FLL Control 1 */
-       0x7086,     /* R43  - FLL Control 2 */
-       0xC226,     /* R44  - FLL Control 3 */
-       0x0000,     /* R45  - FLL Control 4 */
-       0x0000,     /* R46 */
-       0x0000,     /* R47 */
-       0x0000,     /* R48  - DAC Control */
-       0x0000,     /* R49 */
-       0x00C0,     /* R50  - DAC Digital Volume L */
-       0x00C0,     /* R51  - DAC Digital Volume R */
-       0x0000,     /* R52 */
-       0x0040,     /* R53  - DAC LR Rate */
-       0x0000,     /* R54  - DAC Clock Control */
-       0x0000,     /* R55 */
-       0x0000,     /* R56 */
-       0x0000,     /* R57 */
-       0x4000,     /* R58  - DAC Mute */
-       0x0000,     /* R59  - DAC Mute Volume */
-       0x0000,     /* R60  - DAC Side */
-       0x0000,     /* R61 */
-       0x0000,     /* R62 */
-       0x0000,     /* R63 */
-       0x8000,     /* R64  - ADC Control */
-       0x0000,     /* R65 */
-       0x00C0,     /* R66  - ADC Digital Volume L */
-       0x00C0,     /* R67  - ADC Digital Volume R */
-       0x0000,     /* R68  - ADC Divider */
-       0x0000,     /* R69 */
-       0x0040,     /* R70  - ADC LR Rate */
-       0x0000,     /* R71 */
-       0x0303,     /* R72  - Input Control */
-       0x0000,     /* R73  - IN3 Input Control */
-       0x0000,     /* R74  - Mic Bias Control */
-       0x0000,     /* R75 */
-       0x0000,     /* R76  - Output Control */
-       0x0000,     /* R77  - Jack Detect */
-       0x0000,     /* R78  - Anti Pop Control */
-       0x0000,     /* R79 */
-       0x0040,     /* R80  - Left Input Volume */
-       0x0040,     /* R81  - Right Input Volume */
-       0x0000,     /* R82 */
-       0x0000,     /* R83 */
-       0x0000,     /* R84 */
-       0x0000,     /* R85 */
-       0x0000,     /* R86 */
-       0x0000,     /* R87 */
-       0x0800,     /* R88  - Left Mixer Control */
-       0x1000,     /* R89  - Right Mixer Control */
-       0x0000,     /* R90 */
-       0x0000,     /* R91 */
-       0x0000,     /* R92  - OUT3 Mixer Control */
-       0x0000,     /* R93  - OUT4 Mixer Control */
-       0x0000,     /* R94 */
-       0x0000,     /* R95 */
-       0x0000,     /* R96  - Output Left Mixer Volume */
-       0x0000,     /* R97  - Output Right Mixer Volume */
-       0x0000,     /* R98  - Input Mixer Volume L */
-       0x0000,     /* R99  - Input Mixer Volume R */
-       0x0000,     /* R100 - Input Mixer Volume */
-       0x0000,     /* R101 */
-       0x0000,     /* R102 */
-       0x0000,     /* R103 */
-       0x00E4,     /* R104 - OUT1L Volume */
-       0x00E4,     /* R105 - OUT1R Volume */
-       0x00E4,     /* R106 - OUT2L Volume */
-       0x02E4,     /* R107 - OUT2R Volume */
-       0x0000,     /* R108 */
-       0x0000,     /* R109 */
-       0x0000,     /* R110 */
-       0x0000,     /* R111 - BEEP Volume */
-       0x0A00,     /* R112 - AI Formating */
-       0x0000,     /* R113 - ADC DAC COMP */
-       0x0020,     /* R114 - AI ADC Control */
-       0x0020,     /* R115 - AI DAC Control */
-       0x0000,     /* R116 */
-       0x0000,     /* R117 */
-       0x0000,     /* R118 */
-       0x0000,     /* R119 */
-       0x0000,     /* R120 */
-       0x0000,     /* R121 */
-       0x0000,     /* R122 */
-       0x0000,     /* R123 */
-       0x0000,     /* R124 */
-       0x0000,     /* R125 */
-       0x0000,     /* R126 */
-       0x0000,     /* R127 */
-       0x1FFF,     /* R128 - GPIO Debounce */
-       0x0010,     /* R129 - GPIO Pin pull up Control */
-       0x0000,     /* R130 - GPIO Pull down Control */
-       0x0000,     /* R131 - GPIO Interrupt Mode */
-       0x0000,     /* R132 */
-       0x0000,     /* R133 - GPIO Control */
-       0x0BFB,     /* R134 - GPIO Configuration (i/o) */
-       0x0FFD,     /* R135 - GPIO Pin Polarity / Type */
-       0x0000,     /* R136 */
-       0x0000,     /* R137 */
-       0x0000,     /* R138 */
-       0x0000,     /* R139 */
-       0x0310,     /* R140 - GPIO Function Select 1 */
-       0x0001,     /* R141 - GPIO Function Select 2 */
-       0x2300,     /* R142 - GPIO Function Select 3 */
-       0x0003,     /* R143 - GPIO Function Select 4 */
-       0x0000,     /* R144 - Digitiser Control (1) */
-       0x0002,     /* R145 - Digitiser Control (2) */
-       0x0000,     /* R146 */
-       0x0000,     /* R147 */
-       0x0000,     /* R148 */
-       0x0000,     /* R149 */
-       0x0000,     /* R150 */
-       0x0000,     /* R151 */
-       0x7000,     /* R152 - AUX1 Readback */
-       0x7000,     /* R153 - AUX2 Readback */
-       0x7000,     /* R154 - AUX3 Readback */
-       0x7000,     /* R155 - AUX4 Readback */
-       0x0000,     /* R156 - USB Voltage Readback */
-       0x0000,     /* R157 - LINE Voltage Readback */
-       0x0000,     /* R158 - BATT Voltage Readback */
-       0x0000,     /* R159 - Chip Temp Readback */
-       0x0000,     /* R160 */
-       0x0000,     /* R161 */
-       0x0000,     /* R162 */
-       0x0000,     /* R163 - Generic Comparator Control */
-       0x0000,     /* R164 - Generic comparator 1 */
-       0x0000,     /* R165 - Generic comparator 2 */
-       0x0000,     /* R166 - Generic comparator 3 */
-       0x0000,     /* R167 - Generic comparator 4 */
-       0xA00F,     /* R168 - Battery Charger Control 1 */
-       0x0B06,     /* R169 - Battery Charger Control 2 */
-       0x0000,     /* R170 - Battery Charger Control 3 */
-       0x0000,     /* R171 */
-       0x0000,     /* R172 - Current Sink Driver A */
-       0x0000,     /* R173 - CSA Flash control */
-       0x0000,     /* R174 */
-       0x0000,     /* R175 */
-       0x0000,     /* R176 - DCDC/LDO requested */
-       0x032D,     /* R177 - DCDC Active options */
-       0x0000,     /* R178 - DCDC Sleep options */
-       0x0025,     /* R179 - Power-check comparator */
-       0x000E,     /* R180 - DCDC1 Control */
-       0x0400,     /* R181 - DCDC1 Timeouts */
-       0x1006,     /* R182 - DCDC1 Low Power */
-       0x0018,     /* R183 - DCDC2 Control */
-       0x0000,     /* R184 - DCDC2 Timeouts */
-       0x0000,     /* R185 */
-       0x0026,     /* R186 - DCDC3 Control */
-       0x0800,     /* R187 - DCDC3 Timeouts */
-       0x0006,     /* R188 - DCDC3 Low Power */
-       0x0062,     /* R189 - DCDC4 Control */
-       0x1400,     /* R190 - DCDC4 Timeouts */
-       0x0006,     /* R191 - DCDC4 Low Power */
-       0x0008,     /* R192 */
-       0x0000,     /* R193 */
-       0x0000,     /* R194 */
-       0x0026,     /* R195 */
-       0x0400,     /* R196 */
-       0x0006,     /* R197 */
-       0x0000,     /* R198 */
-       0x0003,     /* R199 - Limit Switch Control */
-       0x0006,     /* R200 - LDO1 Control */
-       0x0C00,     /* R201 - LDO1 Timeouts */
-       0x001C,     /* R202 - LDO1 Low Power */
-       0x0016,     /* R203 - LDO2 Control */
-       0x0000,     /* R204 - LDO2 Timeouts */
-       0x001C,     /* R205 - LDO2 Low Power */
-       0x0019,     /* R206 - LDO3 Control */
-       0x0000,     /* R207 - LDO3 Timeouts */
-       0x001C,     /* R208 - LDO3 Low Power */
-       0x001A,     /* R209 - LDO4 Control */
-       0x1000,     /* R210 - LDO4 Timeouts */
-       0x001C,     /* R211 - LDO4 Low Power */
-       0x0000,     /* R212 */
-       0x0000,     /* R213 */
-       0x0000,     /* R214 */
-       0x0000,     /* R215 - VCC_FAULT Masks */
-       0x001F,     /* R216 - Main Bandgap Control */
-       0x0000,     /* R217 - OSC Control */
-       0x9000,     /* R218 - RTC Tick Control */
-       0x0000,     /* R219 - Security1 */
-       0x4000,     /* R220 */
-       0x0000,     /* R221 */
-       0x0000,     /* R222 */
-       0x0000,     /* R223 */
-       0x0000,     /* R224 - Signal overrides */
-       0x0000,     /* R225 - DCDC/LDO status */
-       0x0000,     /* R226 - Charger Overides/status */
-       0x0000,     /* R227 - misc overrides */
-       0x0000,     /* R228 - Supply overrides/status 1 */
-       0x0000,     /* R229 - Supply overrides/status 2 */
-       0xE000,     /* R230 - GPIO Pin Status */
-       0x0000,     /* R231 - comparotor overrides */
-       0x0000,     /* R232 */
-       0x0000,     /* R233 - State Machine status */
-       0x1200,     /* R234 - FLL Test 1 */
-       0x0000,     /* R235 */
-       0x8000,     /* R236 */
-       0x0000,     /* R237 */
-       0x0000,     /* R238 */
-       0x0000,     /* R239 */
-       0x0003,     /* R240 */
-       0x0000,     /* R241 */
-       0x0000,     /* R242 */
-       0x0004,     /* R243 */
-       0x0300,     /* R244 */
-       0x0000,     /* R245 */
-       0x0200,     /* R246 */
-       0x0000,     /* R247 */
-       0x1000,     /* R248 - DCDC1 Test Controls */
-       0x1000,     /* R249 */
-       0x1000,     /* R250 - DCDC3 Test Controls */
-       0x1000,     /* R251 - DCDC4 Test Controls */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8352_mode0_defaults[] = {
-       0x6143,     /* R0   - Reset/ID */
-       0x0000,     /* R1   - ID */
-       0x0002,     /* R2   - Revision */
-       0x1C02,     /* R3   - System Control 1 */
-       0x0004,     /* R4   - System Control 2 */
-       0x0000,     /* R5   - System Hibernate */
-       0x8A00,     /* R6   - Interface Control */
-       0x0000,     /* R7 */
-       0x8000,     /* R8   - Power mgmt (1) */
-       0x0000,     /* R9   - Power mgmt (2) */
-       0x0000,     /* R10  - Power mgmt (3) */
-       0x2000,     /* R11  - Power mgmt (4) */
-       0x0E00,     /* R12  - Power mgmt (5) */
-       0x0000,     /* R13  - Power mgmt (6) */
-       0x0000,     /* R14  - Power mgmt (7) */
-       0x0000,     /* R15 */
-       0x0000,     /* R16  - RTC Seconds/Minutes */
-       0x0100,     /* R17  - RTC Hours/Day */
-       0x0101,     /* R18  - RTC Date/Month */
-       0x1400,     /* R19  - RTC Year */
-       0x0000,     /* R20  - Alarm Seconds/Minutes */
-       0x0000,     /* R21  - Alarm Hours/Day */
-       0x0000,     /* R22  - Alarm Date/Month */
-       0x0320,     /* R23  - RTC Time Control */
-       0x0000,     /* R24  - System Interrupts */
-       0x0000,     /* R25  - Interrupt Status 1 */
-       0x0000,     /* R26  - Interrupt Status 2 */
-       0x0000,     /* R27 */
-       0x0000,     /* R28  - Under Voltage Interrupt status */
-       0x0000,     /* R29  - Over Current Interrupt status */
-       0x0000,     /* R30  - GPIO Interrupt Status */
-       0x0000,     /* R31  - Comparator Interrupt Status */
-       0x3FFF,     /* R32  - System Interrupts Mask */
-       0x0000,     /* R33  - Interrupt Status 1 Mask */
-       0x0000,     /* R34  - Interrupt Status 2 Mask */
-       0x0000,     /* R35 */
-       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
-       0x0000,     /* R37  - Over Current Interrupt status Mask */
-       0x0000,     /* R38  - GPIO Interrupt Status Mask */
-       0x0000,     /* R39  - Comparator Interrupt Status Mask */
-       0x0040,     /* R40  - Clock Control 1 */
-       0x0000,     /* R41  - Clock Control 2 */
-       0x3A00,     /* R42  - FLL Control 1 */
-       0x7086,     /* R43  - FLL Control 2 */
-       0xC226,     /* R44  - FLL Control 3 */
-       0x0000,     /* R45  - FLL Control 4 */
-       0x0000,     /* R46 */
-       0x0000,     /* R47 */
-       0x0000,     /* R48  - DAC Control */
-       0x0000,     /* R49 */
-       0x00C0,     /* R50  - DAC Digital Volume L */
-       0x00C0,     /* R51  - DAC Digital Volume R */
-       0x0000,     /* R52 */
-       0x0040,     /* R53  - DAC LR Rate */
-       0x0000,     /* R54  - DAC Clock Control */
-       0x0000,     /* R55 */
-       0x0000,     /* R56 */
-       0x0000,     /* R57 */
-       0x4000,     /* R58  - DAC Mute */
-       0x0000,     /* R59  - DAC Mute Volume */
-       0x0000,     /* R60  - DAC Side */
-       0x0000,     /* R61 */
-       0x0000,     /* R62 */
-       0x0000,     /* R63 */
-       0x8000,     /* R64  - ADC Control */
-       0x0000,     /* R65 */
-       0x00C0,     /* R66  - ADC Digital Volume L */
-       0x00C0,     /* R67  - ADC Digital Volume R */
-       0x0000,     /* R68  - ADC Divider */
-       0x0000,     /* R69 */
-       0x0040,     /* R70  - ADC LR Rate */
-       0x0000,     /* R71 */
-       0x0303,     /* R72  - Input Control */
-       0x0000,     /* R73  - IN3 Input Control */
-       0x0000,     /* R74  - Mic Bias Control */
-       0x0000,     /* R75 */
-       0x0000,     /* R76  - Output Control */
-       0x0000,     /* R77  - Jack Detect */
-       0x0000,     /* R78  - Anti Pop Control */
-       0x0000,     /* R79 */
-       0x0040,     /* R80  - Left Input Volume */
-       0x0040,     /* R81  - Right Input Volume */
-       0x0000,     /* R82 */
-       0x0000,     /* R83 */
-       0x0000,     /* R84 */
-       0x0000,     /* R85 */
-       0x0000,     /* R86 */
-       0x0000,     /* R87 */
-       0x0800,     /* R88  - Left Mixer Control */
-       0x1000,     /* R89  - Right Mixer Control */
-       0x0000,     /* R90 */
-       0x0000,     /* R91 */
-       0x0000,     /* R92  - OUT3 Mixer Control */
-       0x0000,     /* R93  - OUT4 Mixer Control */
-       0x0000,     /* R94 */
-       0x0000,     /* R95 */
-       0x0000,     /* R96  - Output Left Mixer Volume */
-       0x0000,     /* R97  - Output Right Mixer Volume */
-       0x0000,     /* R98  - Input Mixer Volume L */
-       0x0000,     /* R99  - Input Mixer Volume R */
-       0x0000,     /* R100 - Input Mixer Volume */
-       0x0000,     /* R101 */
-       0x0000,     /* R102 */
-       0x0000,     /* R103 */
-       0x00E4,     /* R104 - OUT1L Volume */
-       0x00E4,     /* R105 - OUT1R Volume */
-       0x00E4,     /* R106 - OUT2L Volume */
-       0x02E4,     /* R107 - OUT2R Volume */
-       0x0000,     /* R108 */
-       0x0000,     /* R109 */
-       0x0000,     /* R110 */
-       0x0000,     /* R111 - BEEP Volume */
-       0x0A00,     /* R112 - AI Formating */
-       0x0000,     /* R113 - ADC DAC COMP */
-       0x0020,     /* R114 - AI ADC Control */
-       0x0020,     /* R115 - AI DAC Control */
-       0x0000,     /* R116 */
-       0x0000,     /* R117 */
-       0x0000,     /* R118 */
-       0x0000,     /* R119 */
-       0x0000,     /* R120 */
-       0x0000,     /* R121 */
-       0x0000,     /* R122 */
-       0x0000,     /* R123 */
-       0x0000,     /* R124 */
-       0x0000,     /* R125 */
-       0x0000,     /* R126 */
-       0x0000,     /* R127 */
-       0x1FFF,     /* R128 - GPIO Debounce */
-       0x0000,     /* R129 - GPIO Pin pull up Control */
-       0x0000,     /* R130 - GPIO Pull down Control */
-       0x0000,     /* R131 - GPIO Interrupt Mode */
-       0x0000,     /* R132 */
-       0x0000,     /* R133 - GPIO Control */
-       0x0FFC,     /* R134 - GPIO Configuration (i/o) */
-       0x0FFC,     /* R135 - GPIO Pin Polarity / Type */
-       0x0000,     /* R136 */
-       0x0000,     /* R137 */
-       0x0000,     /* R138 */
-       0x0000,     /* R139 */
-       0x0013,     /* R140 - GPIO Function Select 1 */
-       0x0000,     /* R141 - GPIO Function Select 2 */
-       0x0000,     /* R142 - GPIO Function Select 3 */
-       0x0003,     /* R143 - GPIO Function Select 4 */
-       0x0000,     /* R144 - Digitiser Control (1) */
-       0x0002,     /* R145 - Digitiser Control (2) */
-       0x0000,     /* R146 */
-       0x0000,     /* R147 */
-       0x0000,     /* R148 */
-       0x0000,     /* R149 */
-       0x0000,     /* R150 */
-       0x0000,     /* R151 */
-       0x7000,     /* R152 - AUX1 Readback */
-       0x7000,     /* R153 - AUX2 Readback */
-       0x7000,     /* R154 - AUX3 Readback */
-       0x7000,     /* R155 - AUX4 Readback */
-       0x0000,     /* R156 - USB Voltage Readback */
-       0x0000,     /* R157 - LINE Voltage Readback */
-       0x0000,     /* R158 - BATT Voltage Readback */
-       0x0000,     /* R159 - Chip Temp Readback */
-       0x0000,     /* R160 */
-       0x0000,     /* R161 */
-       0x0000,     /* R162 */
-       0x0000,     /* R163 - Generic Comparator Control */
-       0x0000,     /* R164 - Generic comparator 1 */
-       0x0000,     /* R165 - Generic comparator 2 */
-       0x0000,     /* R166 - Generic comparator 3 */
-       0x0000,     /* R167 - Generic comparator 4 */
-       0xA00F,     /* R168 - Battery Charger Control 1 */
-       0x0B06,     /* R169 - Battery Charger Control 2 */
-       0x0000,     /* R170 - Battery Charger Control 3 */
-       0x0000,     /* R171 */
-       0x0000,     /* R172 - Current Sink Driver A */
-       0x0000,     /* R173 - CSA Flash control */
-       0x0000,     /* R174 - Current Sink Driver B */
-       0x0000,     /* R175 - CSB Flash control */
-       0x0000,     /* R176 - DCDC/LDO requested */
-       0x032D,     /* R177 - DCDC Active options */
-       0x0000,     /* R178 - DCDC Sleep options */
-       0x0025,     /* R179 - Power-check comparator */
-       0x000E,     /* R180 - DCDC1 Control */
-       0x0000,     /* R181 - DCDC1 Timeouts */
-       0x1006,     /* R182 - DCDC1 Low Power */
-       0x0018,     /* R183 - DCDC2 Control */
-       0x0000,     /* R184 - DCDC2 Timeouts */
-       0x0000,     /* R185 */
-       0x0000,     /* R186 - DCDC3 Control */
-       0x0000,     /* R187 - DCDC3 Timeouts */
-       0x0006,     /* R188 - DCDC3 Low Power */
-       0x0000,     /* R189 - DCDC4 Control */
-       0x0000,     /* R190 - DCDC4 Timeouts */
-       0x0006,     /* R191 - DCDC4 Low Power */
-       0x0008,     /* R192 - DCDC5 Control */
-       0x0000,     /* R193 - DCDC5 Timeouts */
-       0x0000,     /* R194 */
-       0x0000,     /* R195 - DCDC6 Control */
-       0x0000,     /* R196 - DCDC6 Timeouts */
-       0x0006,     /* R197 - DCDC6 Low Power */
-       0x0000,     /* R198 */
-       0x0003,     /* R199 - Limit Switch Control */
-       0x001C,     /* R200 - LDO1 Control */
-       0x0000,     /* R201 - LDO1 Timeouts */
-       0x001C,     /* R202 - LDO1 Low Power */
-       0x001B,     /* R203 - LDO2 Control */
-       0x0000,     /* R204 - LDO2 Timeouts */
-       0x001C,     /* R205 - LDO2 Low Power */
-       0x001B,     /* R206 - LDO3 Control */
-       0x0000,     /* R207 - LDO3 Timeouts */
-       0x001C,     /* R208 - LDO3 Low Power */
-       0x001B,     /* R209 - LDO4 Control */
-       0x0000,     /* R210 - LDO4 Timeouts */
-       0x001C,     /* R211 - LDO4 Low Power */
-       0x0000,     /* R212 */
-       0x0000,     /* R213 */
-       0x0000,     /* R214 */
-       0x0000,     /* R215 - VCC_FAULT Masks */
-       0x001F,     /* R216 - Main Bandgap Control */
-       0x0000,     /* R217 - OSC Control */
-       0x9000,     /* R218 - RTC Tick Control */
-       0x0000,     /* R219 - Security1 */
-       0x4000,     /* R220 */
-       0x0000,     /* R221 */
-       0x0000,     /* R222 */
-       0x0000,     /* R223 */
-       0x0000,     /* R224 - Signal overrides */
-       0x0000,     /* R225 - DCDC/LDO status */
-       0x0000,     /* R226 - Charger Overides/status */
-       0x0000,     /* R227 - misc overrides */
-       0x0000,     /* R228 - Supply overrides/status 1 */
-       0x0000,     /* R229 - Supply overrides/status 2 */
-       0xE000,     /* R230 - GPIO Pin Status */
-       0x0000,     /* R231 - comparotor overrides */
-       0x0000,     /* R232 */
-       0x0000,     /* R233 - State Machine status */
-       0x1200,     /* R234 */
-       0x0000,     /* R235 */
-       0x8000,     /* R236 */
-       0x0000,     /* R237 */
-       0x0000,     /* R238 */
-       0x0000,     /* R239 */
-       0x0003,     /* R240 */
-       0x0000,     /* R241 */
-       0x0000,     /* R242 */
-       0x0004,     /* R243 */
-       0x0300,     /* R244 */
-       0x0000,     /* R245 */
-       0x0200,     /* R246 */
-       0x0000,     /* R247 */
-       0x1000,     /* R248 - DCDC1 Test Controls */
-       0x5000,     /* R249 */
-       0x1000,     /* R250 - DCDC3 Test Controls */
-       0x1000,     /* R251 - DCDC4 Test Controls */
-       0x5100,     /* R252 */
-       0x1000,     /* R253 - DCDC6 Test Controls */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8352_mode1_defaults[] = {
-       0x6143,     /* R0   - Reset/ID */
-       0x0000,     /* R1   - ID */
-       0x0002,     /* R2   - Revision */
-       0x1C02,     /* R3   - System Control 1 */
-       0x0204,     /* R4   - System Control 2 */
-       0x0000,     /* R5   - System Hibernate */
-       0x8A00,     /* R6   - Interface Control */
-       0x0000,     /* R7 */
-       0x8000,     /* R8   - Power mgmt (1) */
-       0x0000,     /* R9   - Power mgmt (2) */
-       0x0000,     /* R10  - Power mgmt (3) */
-       0x2000,     /* R11  - Power mgmt (4) */
-       0x0E00,     /* R12  - Power mgmt (5) */
-       0x0000,     /* R13  - Power mgmt (6) */
-       0x0000,     /* R14  - Power mgmt (7) */
-       0x0000,     /* R15 */
-       0x0000,     /* R16  - RTC Seconds/Minutes */
-       0x0100,     /* R17  - RTC Hours/Day */
-       0x0101,     /* R18  - RTC Date/Month */
-       0x1400,     /* R19  - RTC Year */
-       0x0000,     /* R20  - Alarm Seconds/Minutes */
-       0x0000,     /* R21  - Alarm Hours/Day */
-       0x0000,     /* R22  - Alarm Date/Month */
-       0x0320,     /* R23  - RTC Time Control */
-       0x0000,     /* R24  - System Interrupts */
-       0x0000,     /* R25  - Interrupt Status 1 */
-       0x0000,     /* R26  - Interrupt Status 2 */
-       0x0000,     /* R27 */
-       0x0000,     /* R28  - Under Voltage Interrupt status */
-       0x0000,     /* R29  - Over Current Interrupt status */
-       0x0000,     /* R30  - GPIO Interrupt Status */
-       0x0000,     /* R31  - Comparator Interrupt Status */
-       0x3FFF,     /* R32  - System Interrupts Mask */
-       0x0000,     /* R33  - Interrupt Status 1 Mask */
-       0x0000,     /* R34  - Interrupt Status 2 Mask */
-       0x0000,     /* R35 */
-       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
-       0x0000,     /* R37  - Over Current Interrupt status Mask */
-       0x0000,     /* R38  - GPIO Interrupt Status Mask */
-       0x0000,     /* R39  - Comparator Interrupt Status Mask */
-       0x0040,     /* R40  - Clock Control 1 */
-       0x0000,     /* R41  - Clock Control 2 */
-       0x3A00,     /* R42  - FLL Control 1 */
-       0x7086,     /* R43  - FLL Control 2 */
-       0xC226,     /* R44  - FLL Control 3 */
-       0x0000,     /* R45  - FLL Control 4 */
-       0x0000,     /* R46 */
-       0x0000,     /* R47 */
-       0x0000,     /* R48  - DAC Control */
-       0x0000,     /* R49 */
-       0x00C0,     /* R50  - DAC Digital Volume L */
-       0x00C0,     /* R51  - DAC Digital Volume R */
-       0x0000,     /* R52 */
-       0x0040,     /* R53  - DAC LR Rate */
-       0x0000,     /* R54  - DAC Clock Control */
-       0x0000,     /* R55 */
-       0x0000,     /* R56 */
-       0x0000,     /* R57 */
-       0x4000,     /* R58  - DAC Mute */
-       0x0000,     /* R59  - DAC Mute Volume */
-       0x0000,     /* R60  - DAC Side */
-       0x0000,     /* R61 */
-       0x0000,     /* R62 */
-       0x0000,     /* R63 */
-       0x8000,     /* R64  - ADC Control */
-       0x0000,     /* R65 */
-       0x00C0,     /* R66  - ADC Digital Volume L */
-       0x00C0,     /* R67  - ADC Digital Volume R */
-       0x0000,     /* R68  - ADC Divider */
-       0x0000,     /* R69 */
-       0x0040,     /* R70  - ADC LR Rate */
-       0x0000,     /* R71 */
-       0x0303,     /* R72  - Input Control */
-       0x0000,     /* R73  - IN3 Input Control */
-       0x0000,     /* R74  - Mic Bias Control */
-       0x0000,     /* R75 */
-       0x0000,     /* R76  - Output Control */
-       0x0000,     /* R77  - Jack Detect */
-       0x0000,     /* R78  - Anti Pop Control */
-       0x0000,     /* R79 */
-       0x0040,     /* R80  - Left Input Volume */
-       0x0040,     /* R81  - Right Input Volume */
-       0x0000,     /* R82 */
-       0x0000,     /* R83 */
-       0x0000,     /* R84 */
-       0x0000,     /* R85 */
-       0x0000,     /* R86 */
-       0x0000,     /* R87 */
-       0x0800,     /* R88  - Left Mixer Control */
-       0x1000,     /* R89  - Right Mixer Control */
-       0x0000,     /* R90 */
-       0x0000,     /* R91 */
-       0x0000,     /* R92  - OUT3 Mixer Control */
-       0x0000,     /* R93  - OUT4 Mixer Control */
-       0x0000,     /* R94 */
-       0x0000,     /* R95 */
-       0x0000,     /* R96  - Output Left Mixer Volume */
-       0x0000,     /* R97  - Output Right Mixer Volume */
-       0x0000,     /* R98  - Input Mixer Volume L */
-       0x0000,     /* R99  - Input Mixer Volume R */
-       0x0000,     /* R100 - Input Mixer Volume */
-       0x0000,     /* R101 */
-       0x0000,     /* R102 */
-       0x0000,     /* R103 */
-       0x00E4,     /* R104 - OUT1L Volume */
-       0x00E4,     /* R105 - OUT1R Volume */
-       0x00E4,     /* R106 - OUT2L Volume */
-       0x02E4,     /* R107 - OUT2R Volume */
-       0x0000,     /* R108 */
-       0x0000,     /* R109 */
-       0x0000,     /* R110 */
-       0x0000,     /* R111 - BEEP Volume */
-       0x0A00,     /* R112 - AI Formating */
-       0x0000,     /* R113 - ADC DAC COMP */
-       0x0020,     /* R114 - AI ADC Control */
-       0x0020,     /* R115 - AI DAC Control */
-       0x0000,     /* R116 */
-       0x0000,     /* R117 */
-       0x0000,     /* R118 */
-       0x0000,     /* R119 */
-       0x0000,     /* R120 */
-       0x0000,     /* R121 */
-       0x0000,     /* R122 */
-       0x0000,     /* R123 */
-       0x0000,     /* R124 */
-       0x0000,     /* R125 */
-       0x0000,     /* R126 */
-       0x0000,     /* R127 */
-       0x1FFF,     /* R128 - GPIO Debounce */
-       0x0000,     /* R129 - GPIO Pin pull up Control */
-       0x0000,     /* R130 - GPIO Pull down Control */
-       0x0000,     /* R131 - GPIO Interrupt Mode */
-       0x0000,     /* R132 */
-       0x0000,     /* R133 - GPIO Control */
-       0x0BFB,     /* R134 - GPIO Configuration (i/o) */
-       0x0FFF,     /* R135 - GPIO Pin Polarity / Type */
-       0x0000,     /* R136 */
-       0x0000,     /* R137 */
-       0x0000,     /* R138 */
-       0x0000,     /* R139 */
-       0x0300,     /* R140 - GPIO Function Select 1 */
-       0x0000,     /* R141 - GPIO Function Select 2 */
-       0x2300,     /* R142 - GPIO Function Select 3 */
-       0x0003,     /* R143 - GPIO Function Select 4 */
-       0x0000,     /* R144 - Digitiser Control (1) */
-       0x0002,     /* R145 - Digitiser Control (2) */
-       0x0000,     /* R146 */
-       0x0000,     /* R147 */
-       0x0000,     /* R148 */
-       0x0000,     /* R149 */
-       0x0000,     /* R150 */
-       0x0000,     /* R151 */
-       0x7000,     /* R152 - AUX1 Readback */
-       0x7000,     /* R153 - AUX2 Readback */
-       0x7000,     /* R154 - AUX3 Readback */
-       0x7000,     /* R155 - AUX4 Readback */
-       0x0000,     /* R156 - USB Voltage Readback */
-       0x0000,     /* R157 - LINE Voltage Readback */
-       0x0000,     /* R158 - BATT Voltage Readback */
-       0x0000,     /* R159 - Chip Temp Readback */
-       0x0000,     /* R160 */
-       0x0000,     /* R161 */
-       0x0000,     /* R162 */
-       0x0000,     /* R163 - Generic Comparator Control */
-       0x0000,     /* R164 - Generic comparator 1 */
-       0x0000,     /* R165 - Generic comparator 2 */
-       0x0000,     /* R166 - Generic comparator 3 */
-       0x0000,     /* R167 - Generic comparator 4 */
-       0xA00F,     /* R168 - Battery Charger Control 1 */
-       0x0B06,     /* R169 - Battery Charger Control 2 */
-       0x0000,     /* R170 - Battery Charger Control 3 */
-       0x0000,     /* R171 */
-       0x0000,     /* R172 - Current Sink Driver A */
-       0x0000,     /* R173 - CSA Flash control */
-       0x0000,     /* R174 - Current Sink Driver B */
-       0x0000,     /* R175 - CSB Flash control */
-       0x0000,     /* R176 - DCDC/LDO requested */
-       0x032D,     /* R177 - DCDC Active options */
-       0x0000,     /* R178 - DCDC Sleep options */
-       0x0025,     /* R179 - Power-check comparator */
-       0x0062,     /* R180 - DCDC1 Control */
-       0x0400,     /* R181 - DCDC1 Timeouts */
-       0x1006,     /* R182 - DCDC1 Low Power */
-       0x0018,     /* R183 - DCDC2 Control */
-       0x0000,     /* R184 - DCDC2 Timeouts */
-       0x0000,     /* R185 */
-       0x0006,     /* R186 - DCDC3 Control */
-       0x0800,     /* R187 - DCDC3 Timeouts */
-       0x0006,     /* R188 - DCDC3 Low Power */
-       0x0006,     /* R189 - DCDC4 Control */
-       0x0C00,     /* R190 - DCDC4 Timeouts */
-       0x0006,     /* R191 - DCDC4 Low Power */
-       0x0008,     /* R192 - DCDC5 Control */
-       0x0000,     /* R193 - DCDC5 Timeouts */
-       0x0000,     /* R194 */
-       0x0026,     /* R195 - DCDC6 Control */
-       0x1000,     /* R196 - DCDC6 Timeouts */
-       0x0006,     /* R197 - DCDC6 Low Power */
-       0x0000,     /* R198 */
-       0x0003,     /* R199 - Limit Switch Control */
-       0x0002,     /* R200 - LDO1 Control */
-       0x0000,     /* R201 - LDO1 Timeouts */
-       0x001C,     /* R202 - LDO1 Low Power */
-       0x001A,     /* R203 - LDO2 Control */
-       0x0000,     /* R204 - LDO2 Timeouts */
-       0x001C,     /* R205 - LDO2 Low Power */
-       0x001F,     /* R206 - LDO3 Control */
-       0x0000,     /* R207 - LDO3 Timeouts */
-       0x001C,     /* R208 - LDO3 Low Power */
-       0x001F,     /* R209 - LDO4 Control */
-       0x0000,     /* R210 - LDO4 Timeouts */
-       0x001C,     /* R211 - LDO4 Low Power */
-       0x0000,     /* R212 */
-       0x0000,     /* R213 */
-       0x0000,     /* R214 */
-       0x0000,     /* R215 - VCC_FAULT Masks */
-       0x001F,     /* R216 - Main Bandgap Control */
-       0x0000,     /* R217 - OSC Control */
-       0x9000,     /* R218 - RTC Tick Control */
-       0x0000,     /* R219 - Security1 */
-       0x4000,     /* R220 */
-       0x0000,     /* R221 */
-       0x0000,     /* R222 */
-       0x0000,     /* R223 */
-       0x0000,     /* R224 - Signal overrides */
-       0x0000,     /* R225 - DCDC/LDO status */
-       0x0000,     /* R226 - Charger Overides/status */
-       0x0000,     /* R227 - misc overrides */
-       0x0000,     /* R228 - Supply overrides/status 1 */
-       0x0000,     /* R229 - Supply overrides/status 2 */
-       0xE000,     /* R230 - GPIO Pin Status */
-       0x0000,     /* R231 - comparotor overrides */
-       0x0000,     /* R232 */
-       0x0000,     /* R233 - State Machine status */
-       0x1200,     /* R234 */
-       0x0000,     /* R235 */
-       0x8000,     /* R236 */
-       0x0000,     /* R237 */
-       0x0000,     /* R238 */
-       0x0000,     /* R239 */
-       0x0003,     /* R240 */
-       0x0000,     /* R241 */
-       0x0000,     /* R242 */
-       0x0004,     /* R243 */
-       0x0300,     /* R244 */
-       0x0000,     /* R245 */
-       0x0200,     /* R246 */
-       0x0000,     /* R247 */
-       0x1000,     /* R248 - DCDC1 Test Controls */
-       0x5000,     /* R249 */
-       0x1000,     /* R250 - DCDC3 Test Controls */
-       0x1000,     /* R251 - DCDC4 Test Controls */
-       0x5100,     /* R252 */
-       0x1000,     /* R253 - DCDC6 Test Controls */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8352_mode2_defaults[] = {
-       0x6143,     /* R0   - Reset/ID */
-       0x0000,     /* R1   - ID */
-       0x0002,     /* R2   - Revision */
-       0x1C02,     /* R3   - System Control 1 */
-       0x0204,     /* R4   - System Control 2 */
-       0x0000,     /* R5   - System Hibernate */
-       0x8A00,     /* R6   - Interface Control */
-       0x0000,     /* R7 */
-       0x8000,     /* R8   - Power mgmt (1) */
-       0x0000,     /* R9   - Power mgmt (2) */
-       0x0000,     /* R10  - Power mgmt (3) */
-       0x2000,     /* R11  - Power mgmt (4) */
-       0x0E00,     /* R12  - Power mgmt (5) */
-       0x0000,     /* R13  - Power mgmt (6) */
-       0x0000,     /* R14  - Power mgmt (7) */
-       0x0000,     /* R15 */
-       0x0000,     /* R16  - RTC Seconds/Minutes */
-       0x0100,     /* R17  - RTC Hours/Day */
-       0x0101,     /* R18  - RTC Date/Month */
-       0x1400,     /* R19  - RTC Year */
-       0x0000,     /* R20  - Alarm Seconds/Minutes */
-       0x0000,     /* R21  - Alarm Hours/Day */
-       0x0000,     /* R22  - Alarm Date/Month */
-       0x0320,     /* R23  - RTC Time Control */
-       0x0000,     /* R24  - System Interrupts */
-       0x0000,     /* R25  - Interrupt Status 1 */
-       0x0000,     /* R26  - Interrupt Status 2 */
-       0x0000,     /* R27 */
-       0x0000,     /* R28  - Under Voltage Interrupt status */
-       0x0000,     /* R29  - Over Current Interrupt status */
-       0x0000,     /* R30  - GPIO Interrupt Status */
-       0x0000,     /* R31  - Comparator Interrupt Status */
-       0x3FFF,     /* R32  - System Interrupts Mask */
-       0x0000,     /* R33  - Interrupt Status 1 Mask */
-       0x0000,     /* R34  - Interrupt Status 2 Mask */
-       0x0000,     /* R35 */
-       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
-       0x0000,     /* R37  - Over Current Interrupt status Mask */
-       0x0000,     /* R38  - GPIO Interrupt Status Mask */
-       0x0000,     /* R39  - Comparator Interrupt Status Mask */
-       0x0040,     /* R40  - Clock Control 1 */
-       0x0000,     /* R41  - Clock Control 2 */
-       0x3A00,     /* R42  - FLL Control 1 */
-       0x7086,     /* R43  - FLL Control 2 */
-       0xC226,     /* R44  - FLL Control 3 */
-       0x0000,     /* R45  - FLL Control 4 */
-       0x0000,     /* R46 */
-       0x0000,     /* R47 */
-       0x0000,     /* R48  - DAC Control */
-       0x0000,     /* R49 */
-       0x00C0,     /* R50  - DAC Digital Volume L */
-       0x00C0,     /* R51  - DAC Digital Volume R */
-       0x0000,     /* R52 */
-       0x0040,     /* R53  - DAC LR Rate */
-       0x0000,     /* R54  - DAC Clock Control */
-       0x0000,     /* R55 */
-       0x0000,     /* R56 */
-       0x0000,     /* R57 */
-       0x4000,     /* R58  - DAC Mute */
-       0x0000,     /* R59  - DAC Mute Volume */
-       0x0000,     /* R60  - DAC Side */
-       0x0000,     /* R61 */
-       0x0000,     /* R62 */
-       0x0000,     /* R63 */
-       0x8000,     /* R64  - ADC Control */
-       0x0000,     /* R65 */
-       0x00C0,     /* R66  - ADC Digital Volume L */
-       0x00C0,     /* R67  - ADC Digital Volume R */
-       0x0000,     /* R68  - ADC Divider */
-       0x0000,     /* R69 */
-       0x0040,     /* R70  - ADC LR Rate */
-       0x0000,     /* R71 */
-       0x0303,     /* R72  - Input Control */
-       0x0000,     /* R73  - IN3 Input Control */
-       0x0000,     /* R74  - Mic Bias Control */
-       0x0000,     /* R75 */
-       0x0000,     /* R76  - Output Control */
-       0x0000,     /* R77  - Jack Detect */
-       0x0000,     /* R78  - Anti Pop Control */
-       0x0000,     /* R79 */
-       0x0040,     /* R80  - Left Input Volume */
-       0x0040,     /* R81  - Right Input Volume */
-       0x0000,     /* R82 */
-       0x0000,     /* R83 */
-       0x0000,     /* R84 */
-       0x0000,     /* R85 */
-       0x0000,     /* R86 */
-       0x0000,     /* R87 */
-       0x0800,     /* R88  - Left Mixer Control */
-       0x1000,     /* R89  - Right Mixer Control */
-       0x0000,     /* R90 */
-       0x0000,     /* R91 */
-       0x0000,     /* R92  - OUT3 Mixer Control */
-       0x0000,     /* R93  - OUT4 Mixer Control */
-       0x0000,     /* R94 */
-       0x0000,     /* R95 */
-       0x0000,     /* R96  - Output Left Mixer Volume */
-       0x0000,     /* R97  - Output Right Mixer Volume */
-       0x0000,     /* R98  - Input Mixer Volume L */
-       0x0000,     /* R99  - Input Mixer Volume R */
-       0x0000,     /* R100 - Input Mixer Volume */
-       0x0000,     /* R101 */
-       0x0000,     /* R102 */
-       0x0000,     /* R103 */
-       0x00E4,     /* R104 - OUT1L Volume */
-       0x00E4,     /* R105 - OUT1R Volume */
-       0x00E4,     /* R106 - OUT2L Volume */
-       0x02E4,     /* R107 - OUT2R Volume */
-       0x0000,     /* R108 */
-       0x0000,     /* R109 */
-       0x0000,     /* R110 */
-       0x0000,     /* R111 - BEEP Volume */
-       0x0A00,     /* R112 - AI Formating */
-       0x0000,     /* R113 - ADC DAC COMP */
-       0x0020,     /* R114 - AI ADC Control */
-       0x0020,     /* R115 - AI DAC Control */
-       0x0000,     /* R116 */
-       0x0000,     /* R117 */
-       0x0000,     /* R118 */
-       0x0000,     /* R119 */
-       0x0000,     /* R120 */
-       0x0000,     /* R121 */
-       0x0000,     /* R122 */
-       0x0000,     /* R123 */
-       0x0000,     /* R124 */
-       0x0000,     /* R125 */
-       0x0000,     /* R126 */
-       0x0000,     /* R127 */
-       0x1FFF,     /* R128 - GPIO Debounce */
-       0x0000,     /* R129 - GPIO Pin pull up Control */
-       0x0110,     /* R130 - GPIO Pull down Control */
-       0x0000,     /* R131 - GPIO Interrupt Mode */
-       0x0000,     /* R132 */
-       0x0000,     /* R133 - GPIO Control */
-       0x09DA,     /* R134 - GPIO Configuration (i/o) */
-       0x0DD6,     /* R135 - GPIO Pin Polarity / Type */
-       0x0000,     /* R136 */
-       0x0000,     /* R137 */
-       0x0000,     /* R138 */
-       0x0000,     /* R139 */
-       0x1310,     /* R140 - GPIO Function Select 1 */
-       0x0033,     /* R141 - GPIO Function Select 2 */
-       0x2000,     /* R142 - GPIO Function Select 3 */
-       0x0000,     /* R143 - GPIO Function Select 4 */
-       0x0000,     /* R144 - Digitiser Control (1) */
-       0x0002,     /* R145 - Digitiser Control (2) */
-       0x0000,     /* R146 */
-       0x0000,     /* R147 */
-       0x0000,     /* R148 */
-       0x0000,     /* R149 */
-       0x0000,     /* R150 */
-       0x0000,     /* R151 */
-       0x7000,     /* R152 - AUX1 Readback */
-       0x7000,     /* R153 - AUX2 Readback */
-       0x7000,     /* R154 - AUX3 Readback */
-       0x7000,     /* R155 - AUX4 Readback */
-       0x0000,     /* R156 - USB Voltage Readback */
-       0x0000,     /* R157 - LINE Voltage Readback */
-       0x0000,     /* R158 - BATT Voltage Readback */
-       0x0000,     /* R159 - Chip Temp Readback */
-       0x0000,     /* R160 */
-       0x0000,     /* R161 */
-       0x0000,     /* R162 */
-       0x0000,     /* R163 - Generic Comparator Control */
-       0x0000,     /* R164 - Generic comparator 1 */
-       0x0000,     /* R165 - Generic comparator 2 */
-       0x0000,     /* R166 - Generic comparator 3 */
-       0x0000,     /* R167 - Generic comparator 4 */
-       0xA00F,     /* R168 - Battery Charger Control 1 */
-       0x0B06,     /* R169 - Battery Charger Control 2 */
-       0x0000,     /* R170 - Battery Charger Control 3 */
-       0x0000,     /* R171 */
-       0x0000,     /* R172 - Current Sink Driver A */
-       0x0000,     /* R173 - CSA Flash control */
-       0x0000,     /* R174 - Current Sink Driver B */
-       0x0000,     /* R175 - CSB Flash control */
-       0x0000,     /* R176 - DCDC/LDO requested */
-       0x032D,     /* R177 - DCDC Active options */
-       0x0000,     /* R178 - DCDC Sleep options */
-       0x0025,     /* R179 - Power-check comparator */
-       0x000E,     /* R180 - DCDC1 Control */
-       0x0800,     /* R181 - DCDC1 Timeouts */
-       0x1006,     /* R182 - DCDC1 Low Power */
-       0x0018,     /* R183 - DCDC2 Control */
-       0x0000,     /* R184 - DCDC2 Timeouts */
-       0x0000,     /* R185 */
-       0x0056,     /* R186 - DCDC3 Control */
-       0x1800,     /* R187 - DCDC3 Timeouts */
-       0x0006,     /* R188 - DCDC3 Low Power */
-       0x000E,     /* R189 - DCDC4 Control */
-       0x1000,     /* R190 - DCDC4 Timeouts */
-       0x0006,     /* R191 - DCDC4 Low Power */
-       0x0008,     /* R192 - DCDC5 Control */
-       0x0000,     /* R193 - DCDC5 Timeouts */
-       0x0000,     /* R194 */
-       0x0026,     /* R195 - DCDC6 Control */
-       0x0C00,     /* R196 - DCDC6 Timeouts */
-       0x0006,     /* R197 - DCDC6 Low Power */
-       0x0000,     /* R198 */
-       0x0003,     /* R199 - Limit Switch Control */
-       0x001C,     /* R200 - LDO1 Control */
-       0x0000,     /* R201 - LDO1 Timeouts */
-       0x001C,     /* R202 - LDO1 Low Power */
-       0x0006,     /* R203 - LDO2 Control */
-       0x0400,     /* R204 - LDO2 Timeouts */
-       0x001C,     /* R205 - LDO2 Low Power */
-       0x001C,     /* R206 - LDO3 Control */
-       0x1400,     /* R207 - LDO3 Timeouts */
-       0x001C,     /* R208 - LDO3 Low Power */
-       0x001A,     /* R209 - LDO4 Control */
-       0x0000,     /* R210 - LDO4 Timeouts */
-       0x001C,     /* R211 - LDO4 Low Power */
-       0x0000,     /* R212 */
-       0x0000,     /* R213 */
-       0x0000,     /* R214 */
-       0x0000,     /* R215 - VCC_FAULT Masks */
-       0x001F,     /* R216 - Main Bandgap Control */
-       0x0000,     /* R217 - OSC Control */
-       0x9000,     /* R218 - RTC Tick Control */
-       0x0000,     /* R219 - Security1 */
-       0x4000,     /* R220 */
-       0x0000,     /* R221 */
-       0x0000,     /* R222 */
-       0x0000,     /* R223 */
-       0x0000,     /* R224 - Signal overrides */
-       0x0000,     /* R225 - DCDC/LDO status */
-       0x0000,     /* R226 - Charger Overides/status */
-       0x0000,     /* R227 - misc overrides */
-       0x0000,     /* R228 - Supply overrides/status 1 */
-       0x0000,     /* R229 - Supply overrides/status 2 */
-       0xE000,     /* R230 - GPIO Pin Status */
-       0x0000,     /* R231 - comparotor overrides */
-       0x0000,     /* R232 */
-       0x0000,     /* R233 - State Machine status */
-       0x1200,     /* R234 */
-       0x0000,     /* R235 */
-       0x8000,     /* R236 */
-       0x0000,     /* R237 */
-       0x0000,     /* R238 */
-       0x0000,     /* R239 */
-       0x0003,     /* R240 */
-       0x0000,     /* R241 */
-       0x0000,     /* R242 */
-       0x0004,     /* R243 */
-       0x0300,     /* R244 */
-       0x0000,     /* R245 */
-       0x0200,     /* R246 */
-       0x0000,     /* R247 */
-       0x1000,     /* R248 - DCDC1 Test Controls */
-       0x5000,     /* R249 */
-       0x1000,     /* R250 - DCDC3 Test Controls */
-       0x1000,     /* R251 - DCDC4 Test Controls */
-       0x5100,     /* R252 */
-       0x1000,     /* R253 - DCDC6 Test Controls */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8352_mode3_defaults[] = {
-       0x6143,     /* R0   - Reset/ID */
-       0x0000,     /* R1   - ID */
-       0x0002,     /* R2   - Revision */
-       0x1C02,     /* R3   - System Control 1 */
-       0x0204,     /* R4   - System Control 2 */
-       0x0000,     /* R5   - System Hibernate */
-       0x8A00,     /* R6   - Interface Control */
-       0x0000,     /* R7 */
-       0x8000,     /* R8   - Power mgmt (1) */
-       0x0000,     /* R9   - Power mgmt (2) */
-       0x0000,     /* R10  - Power mgmt (3) */
-       0x2000,     /* R11  - Power mgmt (4) */
-       0x0E00,     /* R12  - Power mgmt (5) */
-       0x0000,     /* R13  - Power mgmt (6) */
-       0x0000,     /* R14  - Power mgmt (7) */
-       0x0000,     /* R15 */
-       0x0000,     /* R16  - RTC Seconds/Minutes */
-       0x0100,     /* R17  - RTC Hours/Day */
-       0x0101,     /* R18  - RTC Date/Month */
-       0x1400,     /* R19  - RTC Year */
-       0x0000,     /* R20  - Alarm Seconds/Minutes */
-       0x0000,     /* R21  - Alarm Hours/Day */
-       0x0000,     /* R22  - Alarm Date/Month */
-       0x0320,     /* R23  - RTC Time Control */
-       0x0000,     /* R24  - System Interrupts */
-       0x0000,     /* R25  - Interrupt Status 1 */
-       0x0000,     /* R26  - Interrupt Status 2 */
-       0x0000,     /* R27 */
-       0x0000,     /* R28  - Under Voltage Interrupt status */
-       0x0000,     /* R29  - Over Current Interrupt status */
-       0x0000,     /* R30  - GPIO Interrupt Status */
-       0x0000,     /* R31  - Comparator Interrupt Status */
-       0x3FFF,     /* R32  - System Interrupts Mask */
-       0x0000,     /* R33  - Interrupt Status 1 Mask */
-       0x0000,     /* R34  - Interrupt Status 2 Mask */
-       0x0000,     /* R35 */
-       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
-       0x0000,     /* R37  - Over Current Interrupt status Mask */
-       0x0000,     /* R38  - GPIO Interrupt Status Mask */
-       0x0000,     /* R39  - Comparator Interrupt Status Mask */
-       0x0040,     /* R40  - Clock Control 1 */
-       0x0000,     /* R41  - Clock Control 2 */
-       0x3A00,     /* R42  - FLL Control 1 */
-       0x7086,     /* R43  - FLL Control 2 */
-       0xC226,     /* R44  - FLL Control 3 */
-       0x0000,     /* R45  - FLL Control 4 */
-       0x0000,     /* R46 */
-       0x0000,     /* R47 */
-       0x0000,     /* R48  - DAC Control */
-       0x0000,     /* R49 */
-       0x00C0,     /* R50  - DAC Digital Volume L */
-       0x00C0,     /* R51  - DAC Digital Volume R */
-       0x0000,     /* R52 */
-       0x0040,     /* R53  - DAC LR Rate */
-       0x0000,     /* R54  - DAC Clock Control */
-       0x0000,     /* R55 */
-       0x0000,     /* R56 */
-       0x0000,     /* R57 */
-       0x4000,     /* R58  - DAC Mute */
-       0x0000,     /* R59  - DAC Mute Volume */
-       0x0000,     /* R60  - DAC Side */
-       0x0000,     /* R61 */
-       0x0000,     /* R62 */
-       0x0000,     /* R63 */
-       0x8000,     /* R64  - ADC Control */
-       0x0000,     /* R65 */
-       0x00C0,     /* R66  - ADC Digital Volume L */
-       0x00C0,     /* R67  - ADC Digital Volume R */
-       0x0000,     /* R68  - ADC Divider */
-       0x0000,     /* R69 */
-       0x0040,     /* R70  - ADC LR Rate */
-       0x0000,     /* R71 */
-       0x0303,     /* R72  - Input Control */
-       0x0000,     /* R73  - IN3 Input Control */
-       0x0000,     /* R74  - Mic Bias Control */
-       0x0000,     /* R75 */
-       0x0000,     /* R76  - Output Control */
-       0x0000,     /* R77  - Jack Detect */
-       0x0000,     /* R78  - Anti Pop Control */
-       0x0000,     /* R79 */
-       0x0040,     /* R80  - Left Input Volume */
-       0x0040,     /* R81  - Right Input Volume */
-       0x0000,     /* R82 */
-       0x0000,     /* R83 */
-       0x0000,     /* R84 */
-       0x0000,     /* R85 */
-       0x0000,     /* R86 */
-       0x0000,     /* R87 */
-       0x0800,     /* R88  - Left Mixer Control */
-       0x1000,     /* R89  - Right Mixer Control */
-       0x0000,     /* R90 */
-       0x0000,     /* R91 */
-       0x0000,     /* R92  - OUT3 Mixer Control */
-       0x0000,     /* R93  - OUT4 Mixer Control */
-       0x0000,     /* R94 */
-       0x0000,     /* R95 */
-       0x0000,     /* R96  - Output Left Mixer Volume */
-       0x0000,     /* R97  - Output Right Mixer Volume */
-       0x0000,     /* R98  - Input Mixer Volume L */
-       0x0000,     /* R99  - Input Mixer Volume R */
-       0x0000,     /* R100 - Input Mixer Volume */
-       0x0000,     /* R101 */
-       0x0000,     /* R102 */
-       0x0000,     /* R103 */
-       0x00E4,     /* R104 - OUT1L Volume */
-       0x00E4,     /* R105 - OUT1R Volume */
-       0x00E4,     /* R106 - OUT2L Volume */
-       0x02E4,     /* R107 - OUT2R Volume */
-       0x0000,     /* R108 */
-       0x0000,     /* R109 */
-       0x0000,     /* R110 */
-       0x0000,     /* R111 - BEEP Volume */
-       0x0A00,     /* R112 - AI Formating */
-       0x0000,     /* R113 - ADC DAC COMP */
-       0x0020,     /* R114 - AI ADC Control */
-       0x0020,     /* R115 - AI DAC Control */
-       0x0000,     /* R116 */
-       0x0000,     /* R117 */
-       0x0000,     /* R118 */
-       0x0000,     /* R119 */
-       0x0000,     /* R120 */
-       0x0000,     /* R121 */
-       0x0000,     /* R122 */
-       0x0000,     /* R123 */
-       0x0000,     /* R124 */
-       0x0000,     /* R125 */
-       0x0000,     /* R126 */
-       0x0000,     /* R127 */
-       0x1FFF,     /* R128 - GPIO Debounce */
-       0x0010,     /* R129 - GPIO Pin pull up Control */
-       0x0000,     /* R130 - GPIO Pull down Control */
-       0x0000,     /* R131 - GPIO Interrupt Mode */
-       0x0000,     /* R132 */
-       0x0000,     /* R133 - GPIO Control */
-       0x0BFB,     /* R134 - GPIO Configuration (i/o) */
-       0x0FFD,     /* R135 - GPIO Pin Polarity / Type */
-       0x0000,     /* R136 */
-       0x0000,     /* R137 */
-       0x0000,     /* R138 */
-       0x0000,     /* R139 */
-       0x0310,     /* R140 - GPIO Function Select 1 */
-       0x0001,     /* R141 - GPIO Function Select 2 */
-       0x2300,     /* R142 - GPIO Function Select 3 */
-       0x0003,     /* R143 - GPIO Function Select 4 */
-       0x0000,     /* R144 - Digitiser Control (1) */
-       0x0002,     /* R145 - Digitiser Control (2) */
-       0x0000,     /* R146 */
-       0x0000,     /* R147 */
-       0x0000,     /* R148 */
-       0x0000,     /* R149 */
-       0x0000,     /* R150 */
-       0x0000,     /* R151 */
-       0x7000,     /* R152 - AUX1 Readback */
-       0x7000,     /* R153 - AUX2 Readback */
-       0x7000,     /* R154 - AUX3 Readback */
-       0x7000,     /* R155 - AUX4 Readback */
-       0x0000,     /* R156 - USB Voltage Readback */
-       0x0000,     /* R157 - LINE Voltage Readback */
-       0x0000,     /* R158 - BATT Voltage Readback */
-       0x0000,     /* R159 - Chip Temp Readback */
-       0x0000,     /* R160 */
-       0x0000,     /* R161 */
-       0x0000,     /* R162 */
-       0x0000,     /* R163 - Generic Comparator Control */
-       0x0000,     /* R164 - Generic comparator 1 */
-       0x0000,     /* R165 - Generic comparator 2 */
-       0x0000,     /* R166 - Generic comparator 3 */
-       0x0000,     /* R167 - Generic comparator 4 */
-       0xA00F,     /* R168 - Battery Charger Control 1 */
-       0x0B06,     /* R169 - Battery Charger Control 2 */
-       0x0000,     /* R170 - Battery Charger Control 3 */
-       0x0000,     /* R171 */
-       0x0000,     /* R172 - Current Sink Driver A */
-       0x0000,     /* R173 - CSA Flash control */
-       0x0000,     /* R174 - Current Sink Driver B */
-       0x0000,     /* R175 - CSB Flash control */
-       0x0000,     /* R176 - DCDC/LDO requested */
-       0x032D,     /* R177 - DCDC Active options */
-       0x0000,     /* R178 - DCDC Sleep options */
-       0x0025,     /* R179 - Power-check comparator */
-       0x0006,     /* R180 - DCDC1 Control */
-       0x0400,     /* R181 - DCDC1 Timeouts */
-       0x1006,     /* R182 - DCDC1 Low Power */
-       0x0018,     /* R183 - DCDC2 Control */
-       0x0000,     /* R184 - DCDC2 Timeouts */
-       0x0000,     /* R185 */
-       0x0050,     /* R186 - DCDC3 Control */
-       0x0C00,     /* R187 - DCDC3 Timeouts */
-       0x0006,     /* R188 - DCDC3 Low Power */
-       0x000E,     /* R189 - DCDC4 Control */
-       0x0400,     /* R190 - DCDC4 Timeouts */
-       0x0006,     /* R191 - DCDC4 Low Power */
-       0x0008,     /* R192 - DCDC5 Control */
-       0x0000,     /* R193 - DCDC5 Timeouts */
-       0x0000,     /* R194 */
-       0x0029,     /* R195 - DCDC6 Control */
-       0x0800,     /* R196 - DCDC6 Timeouts */
-       0x0006,     /* R197 - DCDC6 Low Power */
-       0x0000,     /* R198 */
-       0x0003,     /* R199 - Limit Switch Control */
-       0x001D,     /* R200 - LDO1 Control */
-       0x1000,     /* R201 - LDO1 Timeouts */
-       0x001C,     /* R202 - LDO1 Low Power */
-       0x0017,     /* R203 - LDO2 Control */
-       0x1000,     /* R204 - LDO2 Timeouts */
-       0x001C,     /* R205 - LDO2 Low Power */
-       0x0006,     /* R206 - LDO3 Control */
-       0x1000,     /* R207 - LDO3 Timeouts */
-       0x001C,     /* R208 - LDO3 Low Power */
-       0x0010,     /* R209 - LDO4 Control */
-       0x1000,     /* R210 - LDO4 Timeouts */
-       0x001C,     /* R211 - LDO4 Low Power */
-       0x0000,     /* R212 */
-       0x0000,     /* R213 */
-       0x0000,     /* R214 */
-       0x0000,     /* R215 - VCC_FAULT Masks */
-       0x001F,     /* R216 - Main Bandgap Control */
-       0x0000,     /* R217 - OSC Control */
-       0x9000,     /* R218 - RTC Tick Control */
-       0x0000,     /* R219 - Security1 */
-       0x4000,     /* R220 */
-       0x0000,     /* R221 */
-       0x0000,     /* R222 */
-       0x0000,     /* R223 */
-       0x0000,     /* R224 - Signal overrides */
-       0x0000,     /* R225 - DCDC/LDO status */
-       0x0000,     /* R226 - Charger Overides/status */
-       0x0000,     /* R227 - misc overrides */
-       0x0000,     /* R228 - Supply overrides/status 1 */
-       0x0000,     /* R229 - Supply overrides/status 2 */
-       0xE000,     /* R230 - GPIO Pin Status */
-       0x0000,     /* R231 - comparotor overrides */
-       0x0000,     /* R232 */
-       0x0000,     /* R233 - State Machine status */
-       0x1200,     /* R234 */
-       0x0000,     /* R235 */
-       0x8000,     /* R236 */
-       0x0000,     /* R237 */
-       0x0000,     /* R238 */
-       0x0000,     /* R239 */
-       0x0003,     /* R240 */
-       0x0000,     /* R241 */
-       0x0000,     /* R242 */
-       0x0004,     /* R243 */
-       0x0300,     /* R244 */
-       0x0000,     /* R245 */
-       0x0200,     /* R246 */
-       0x0000,     /* R247 */
-       0x1000,     /* R248 - DCDC1 Test Controls */
-       0x5000,     /* R249 */
-       0x1000,     /* R250 - DCDC3 Test Controls */
-       0x1000,     /* R251 - DCDC4 Test Controls */
-       0x5100,     /* R252 */
-       0x1000,     /* R253 - DCDC6 Test Controls */
-};
-#endif
-
 /*
  * Access masks.
  */
 
-const struct wm8350_reg_access wm8350_reg_io_map[] = {
+static const struct wm8350_reg_access {
+       u16 readable;           /* Mask of readable bits */
+       u16 writable;           /* Mask of writable bits */
+       u16 vol;                /* Mask of volatile bits */
+} wm8350_reg_io_map[] = {
        /*  read    write volatile */
-       { 0xFFFF, 0xFFFF, 0xFFFF }, /* R0   - Reset/ID */
-       { 0x7CFF, 0x0C00, 0x7FFF }, /* R1   - ID */
+       { 0xFFFF, 0xFFFF, 0x0000 }, /* R0   - Reset/ID */
+       { 0x7CFF, 0x0C00, 0x0000 }, /* R1   - ID */
        { 0x007F, 0x0000, 0x0000 }, /* R2   - ROM Mask ID */
        { 0xBE3B, 0xBE3B, 0x8000 }, /* R3   - System Control 1 */
        { 0xFEF7, 0xFEF7, 0xF800 }, /* R4   - System Control 2 */
@@ -3433,3 +281,59 @@ const struct wm8350_reg_access wm8350_reg_io_map[] = {
        { 0x0000, 0x0000, 0x0000 }, /* R254 */
        { 0x0000, 0x0000, 0x0000 }, /* R255 */
 };
+
+static bool wm8350_readable(struct device *dev, unsigned int reg)
+{
+       return wm8350_reg_io_map[reg].readable;
+}
+
+static bool wm8350_writeable(struct device *dev, unsigned int reg)
+{
+       struct wm8350 *wm8350 = dev_get_drvdata(dev);
+
+       if (!wm8350->unlocked) {
+               if ((reg >= WM8350_GPIO_FUNCTION_SELECT_1 &&
+                    reg <= WM8350_GPIO_FUNCTION_SELECT_4) ||
+                   (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 &&
+                    reg <= WM8350_BATTERY_CHARGER_CONTROL_3))
+                       return false;
+       }
+
+       return wm8350_reg_io_map[reg].writable;
+}
+
+static bool wm8350_volatile(struct device *dev, unsigned int reg)
+{
+       return wm8350_reg_io_map[reg].vol;
+}
+
+static bool wm8350_precious(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case WM8350_SYSTEM_INTERRUPTS:
+       case WM8350_INT_STATUS_1:
+       case WM8350_INT_STATUS_2:
+       case WM8350_POWER_UP_INT_STATUS:
+       case WM8350_UNDER_VOLTAGE_INT_STATUS:
+       case WM8350_OVER_CURRENT_INT_STATUS:
+       case WM8350_GPIO_INT_STATUS:
+       case WM8350_COMPARATOR_INT_STATUS:
+               return true;
+
+       default:
+               return false;
+       }
+}
+
+const struct regmap_config wm8350_regmap = {
+       .reg_bits = 8,
+       .val_bits = 16,
+
+       .cache_type = REGCACHE_RBTREE,
+
+       .max_register = WM8350_MAX_REGISTER,
+       .readable_reg = wm8350_readable,
+       .writeable_reg = wm8350_writeable,
+       .volatile_reg = wm8350_volatile,
+       .precious_reg = wm8350_precious,
+};
index 1e321d349777199dad0bdd050288f72af60917a6..eec74aa55fdfe28c46476c3360e1632bfd51cf7f 100644 (file)
@@ -283,9 +283,24 @@ static int wm8994_suspend(struct device *dev)
        wm8994_reg_write(wm8994, WM8994_SOFTWARE_RESET,
                         wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET));
 
-       regcache_cache_only(wm8994->regmap, true);
        regcache_mark_dirty(wm8994->regmap);
 
+       /* Restore GPIO registers to prevent problems with mismatched
+        * pin configurations.
+        */
+       ret = regcache_sync_region(wm8994->regmap, WM8994_GPIO_1,
+                                  WM8994_GPIO_11);
+       if (ret != 0)
+               dev_err(dev, "Failed to restore GPIO registers: %d\n", ret);
+
+       /* In case one of the GPIOs is used as a wake input. */
+       ret = regcache_sync_region(wm8994->regmap,
+                                  WM8994_INTERRUPT_STATUS_1_MASK,
+                                  WM8994_INTERRUPT_STATUS_1_MASK);
+       if (ret != 0)
+               dev_err(dev, "Failed to restore interrupt mask: %d\n", ret);
+
+       regcache_cache_only(wm8994->regmap, true);
        wm8994->suspended = true;
 
        ret = regulator_bulk_disable(wm8994->num_supplies,
index f1837f669755d4671615cb7a109e4d69ea827d21..0aac4aff17a5f6cf4d66144883608157a720d7ed 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/regmap.h>
 
 #include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/pdata.h>
 #include <linux/mfd/wm8994/registers.h>
 
 #include <linux/delay.h>
@@ -139,6 +140,8 @@ static struct regmap_irq_chip wm8994_irq_chip = {
 int wm8994_irq_init(struct wm8994 *wm8994)
 {
        int ret;
+       unsigned long irqflags;
+       struct wm8994_pdata *pdata = wm8994->dev->platform_data;
 
        if (!wm8994->irq) {
                dev_warn(wm8994->dev,
@@ -147,8 +150,13 @@ int wm8994_irq_init(struct wm8994 *wm8994)
                return 0;
        }
 
+       /* select user or default irq flags */
+       irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
+       if (pdata->irq_flags)
+               irqflags = pdata->irq_flags;
+
        ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq,
-                                 IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                                 irqflags,
                                  wm8994->irq_base, &wm8994_irq_chip,
                                  &wm8994->irq_data);
        if (ret != 0) {
index 154f3ef076313f44008167eb94267239fda7cbaa..98a442da892a23ae21f19f1ef548b536bac9ede4 100644 (file)
@@ -64,6 +64,7 @@ config AB8500_PWM
        bool "AB8500 PWM support"
        depends on AB8500_CORE && ARCH_U8500
        select HAVE_PWM
+       depends on !PWM
        help
          This driver exports functions to enable/disble/config/free Pulse
          Width Modulation in the Analog Baseband Chip AB8500.
index 042a8fe4efaabd2ab57db4299275c19750cad23d..d7a9aa14e5d5aafd8c0efc907b127147a202d197 100644 (file)
@@ -142,16 +142,10 @@ static int __devexit ab8500_pwm_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id ab8500_pwm_match[] = {
-       { .compatible = "stericsson,ab8500-pwm", },
-       {}
-};
-
 static struct platform_driver ab8500_pwm_driver = {
        .driver = {
                .name = "ab8500-pwm",
                .owner = THIS_MODULE,
-               .of_match_table = ab8500_pwm_match,
        },
        .probe = ab8500_pwm_probe,
        .remove = __devexit_p(ab8500_pwm_remove),
index 28adefe70f96274c93198ee50acc9ee65adec1ae..08aad69c8da4e3f4d8572d52eadd46c00bb65941 100644 (file)
@@ -477,6 +477,8 @@ static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf,
        int i, n, out;
 
        buf = (char *)__get_free_page(GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
 
        n = snprintf(buf, PAGE_SIZE, "Available crash types:\n");
        for (i = 0; i < ARRAY_SIZE(cp_type); i++)
index 2b62232c2c6a3476551f11f86253dcf545234d07..acfaeeb9e01a98e7f296d5c2081f35ef4d53b325 100644 (file)
@@ -349,6 +349,11 @@ void st_int_recv(void *disc_data,
                        st_gdata->rx_skb = alloc_skb(
                                        st_gdata->list[type]->max_frame_size,
                                        GFP_ATOMIC);
+                       if (st_gdata->rx_skb == NULL) {
+                               pr_err("out of memory: dropping\n");
+                               goto done;
+                       }
+
                        skb_reserve(st_gdata->rx_skb,
                                        st_gdata->list[type]->reserve);
                        /* next 2 required for BT only */
index 3e8dcf8d2e051efea306e933fa701d7f92d53333..50e08f03aa65ce72ce3876deb304db2fbed8a22f 100644 (file)
 #include <linux/ioport.h>
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
+#include <linux/dmaengine.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
 #include <linux/spinlock.h>
 #include <linux/timer.h>
+#include <linux/omap-dma.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
 #include <linux/clk.h>
@@ -128,6 +130,10 @@ struct mmc_omap_host {
        unsigned char           id; /* 16xx chips have 2 MMC blocks */
        struct clk *            iclk;
        struct clk *            fclk;
+       struct dma_chan         *dma_rx;
+       u32                     dma_rx_burst;
+       struct dma_chan         *dma_tx;
+       u32                     dma_tx_burst;
        struct resource         *mem_res;
        void __iomem            *virt_base;
        unsigned int            phys_base;
@@ -153,12 +159,8 @@ struct mmc_omap_host {
 
        unsigned                use_dma:1;
        unsigned                brs_received:1, dma_done:1;
-       unsigned                dma_is_read:1;
        unsigned                dma_in_use:1;
-       int                     dma_ch;
        spinlock_t              dma_lock;
-       struct timer_list       dma_timer;
-       unsigned                dma_len;
 
        struct mmc_omap_slot    *slots[OMAP_MMC_MAX_SLOTS];
        struct mmc_omap_slot    *current_slot;
@@ -406,18 +408,25 @@ mmc_omap_release_dma(struct mmc_omap_host *host, struct mmc_data *data,
                     int abort)
 {
        enum dma_data_direction dma_data_dir;
+       struct device *dev = mmc_dev(host->mmc);
+       struct dma_chan *c;
 
-       BUG_ON(host->dma_ch < 0);
-       if (data->error)
-               omap_stop_dma(host->dma_ch);
-       /* Release DMA channel lazily */
-       mod_timer(&host->dma_timer, jiffies + HZ);
-       if (data->flags & MMC_DATA_WRITE)
+       if (data->flags & MMC_DATA_WRITE) {
                dma_data_dir = DMA_TO_DEVICE;
-       else
+               c = host->dma_tx;
+       } else {
                dma_data_dir = DMA_FROM_DEVICE;
-       dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
-                    dma_data_dir);
+               c = host->dma_rx;
+       }
+       if (c) {
+               if (data->error) {
+                       dmaengine_terminate_all(c);
+                       /* Claim nothing transferred on error... */
+                       data->bytes_xfered = 0;
+               }
+               dev = c->device->dev;
+       }
+       dma_unmap_sg(dev, data->sg, host->sg_len, dma_data_dir);
 }
 
 static void mmc_omap_send_stop_work(struct work_struct *work)
@@ -524,16 +533,6 @@ mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data)
                mmc_omap_xfer_done(host, data);
 }
 
-static void
-mmc_omap_dma_timer(unsigned long data)
-{
-       struct mmc_omap_host *host = (struct mmc_omap_host *) data;
-
-       BUG_ON(host->dma_ch < 0);
-       omap_free_dma(host->dma_ch);
-       host->dma_ch = -1;
-}
-
 static void
 mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data)
 {
@@ -891,159 +890,15 @@ static void mmc_omap_cover_handler(unsigned long param)
                  jiffies + msecs_to_jiffies(OMAP_MMC_COVER_POLL_DELAY));
 }
 
-/* Prepare to transfer the next segment of a scatterlist */
-static void
-mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data)
+static void mmc_omap_dma_callback(void *priv)
 {
-       int dma_ch = host->dma_ch;
-       unsigned long data_addr;
-       u16 buf, frame;
-       u32 count;
-       struct scatterlist *sg = &data->sg[host->sg_idx];
-       int src_port = 0;
-       int dst_port = 0;
-       int sync_dev = 0;
-
-       data_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
-       frame = data->blksz;
-       count = sg_dma_len(sg);
-
-       if ((data->blocks == 1) && (count > data->blksz))
-               count = frame;
-
-       host->dma_len = count;
-
-       /* FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx and 24xx.
-        * Use 16 or 32 word frames when the blocksize is at least that large.
-        * Blocksize is usually 512 bytes; but not for some SD reads.
-        */
-       if (cpu_is_omap15xx() && frame > 32)
-               frame = 32;
-       else if (frame > 64)
-               frame = 64;
-       count /= frame;
-       frame >>= 1;
-
-       if (!(data->flags & MMC_DATA_WRITE)) {
-               buf = 0x800f | ((frame - 1) << 8);
-
-               if (cpu_class_is_omap1()) {
-                       src_port = OMAP_DMA_PORT_TIPB;
-                       dst_port = OMAP_DMA_PORT_EMIFF;
-               }
-               if (cpu_is_omap24xx())
-                       sync_dev = OMAP24XX_DMA_MMC1_RX;
-
-               omap_set_dma_src_params(dma_ch, src_port,
-                                       OMAP_DMA_AMODE_CONSTANT,
-                                       data_addr, 0, 0);
-               omap_set_dma_dest_params(dma_ch, dst_port,
-                                        OMAP_DMA_AMODE_POST_INC,
-                                        sg_dma_address(sg), 0, 0);
-               omap_set_dma_dest_data_pack(dma_ch, 1);
-               omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
-       } else {
-               buf = 0x0f80 | ((frame - 1) << 0);
-
-               if (cpu_class_is_omap1()) {
-                       src_port = OMAP_DMA_PORT_EMIFF;
-                       dst_port = OMAP_DMA_PORT_TIPB;
-               }
-               if (cpu_is_omap24xx())
-                       sync_dev = OMAP24XX_DMA_MMC1_TX;
-
-               omap_set_dma_dest_params(dma_ch, dst_port,
-                                        OMAP_DMA_AMODE_CONSTANT,
-                                        data_addr, 0, 0);
-               omap_set_dma_src_params(dma_ch, src_port,
-                                       OMAP_DMA_AMODE_POST_INC,
-                                       sg_dma_address(sg), 0, 0);
-               omap_set_dma_src_data_pack(dma_ch, 1);
-               omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
-       }
+       struct mmc_omap_host *host = priv;
+       struct mmc_data *data = host->data;
 
-       /* Max limit for DMA frame count is 0xffff */
-       BUG_ON(count > 0xffff);
+       /* If we got to the end of DMA, assume everything went well */
+       data->bytes_xfered += data->blocks * data->blksz;
 
-       OMAP_MMC_WRITE(host, BUF, buf);
-       omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16,
-                                    frame, count, OMAP_DMA_SYNC_FRAME,
-                                    sync_dev, 0);
-}
-
-/* A scatterlist segment completed */
-static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
-{
-       struct mmc_omap_host *host = (struct mmc_omap_host *) data;
-       struct mmc_data *mmcdat = host->data;
-
-       if (unlikely(host->dma_ch < 0)) {
-               dev_err(mmc_dev(host->mmc),
-                       "DMA callback while DMA not enabled\n");
-               return;
-       }
-       /* FIXME: We really should do something to _handle_ the errors */
-       if (ch_status & OMAP1_DMA_TOUT_IRQ) {
-               dev_err(mmc_dev(host->mmc),"DMA timeout\n");
-               return;
-       }
-       if (ch_status & OMAP_DMA_DROP_IRQ) {
-               dev_err(mmc_dev(host->mmc), "DMA sync error\n");
-               return;
-       }
-       if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
-               return;
-       }
-       mmcdat->bytes_xfered += host->dma_len;
-       host->sg_idx++;
-       if (host->sg_idx < host->sg_len) {
-               mmc_omap_prepare_dma(host, host->data);
-               omap_start_dma(host->dma_ch);
-       } else
-               mmc_omap_dma_done(host, host->data);
-}
-
-static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data *data)
-{
-       const char *dma_dev_name;
-       int sync_dev, dma_ch, is_read, r;
-
-       is_read = !(data->flags & MMC_DATA_WRITE);
-       del_timer_sync(&host->dma_timer);
-       if (host->dma_ch >= 0) {
-               if (is_read == host->dma_is_read)
-                       return 0;
-               omap_free_dma(host->dma_ch);
-               host->dma_ch = -1;
-       }
-
-       if (is_read) {
-               if (host->id == 0) {
-                       sync_dev = OMAP_DMA_MMC_RX;
-                       dma_dev_name = "MMC1 read";
-               } else {
-                       sync_dev = OMAP_DMA_MMC2_RX;
-                       dma_dev_name = "MMC2 read";
-               }
-       } else {
-               if (host->id == 0) {
-                       sync_dev = OMAP_DMA_MMC_TX;
-                       dma_dev_name = "MMC1 write";
-               } else {
-                       sync_dev = OMAP_DMA_MMC2_TX;
-                       dma_dev_name = "MMC2 write";
-               }
-       }
-       r = omap_request_dma(sync_dev, dma_dev_name, mmc_omap_dma_cb,
-                            host, &dma_ch);
-       if (r != 0) {
-               dev_dbg(mmc_dev(host->mmc), "omap_request_dma() failed with %d\n", r);
-               return r;
-       }
-       host->dma_ch = dma_ch;
-       host->dma_is_read = is_read;
-
-       return 0;
+       mmc_omap_dma_done(host, data);
 }
 
 static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req)
@@ -1118,33 +973,85 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
 
        host->sg_idx = 0;
        if (use_dma) {
-               if (mmc_omap_get_dma_channel(host, data) == 0) {
-                       enum dma_data_direction dma_data_dir;
-
-                       if (data->flags & MMC_DATA_WRITE)
-                               dma_data_dir = DMA_TO_DEVICE;
-                       else
-                               dma_data_dir = DMA_FROM_DEVICE;
-
-                       host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
-                                               sg_len, dma_data_dir);
-                       host->total_bytes_left = 0;
-                       mmc_omap_prepare_dma(host, req->data);
-                       host->brs_received = 0;
-                       host->dma_done = 0;
-                       host->dma_in_use = 1;
-               } else
-                       use_dma = 0;
+               enum dma_data_direction dma_data_dir;
+               struct dma_async_tx_descriptor *tx;
+               struct dma_chan *c;
+               u32 burst, *bp;
+               u16 buf;
+
+               /*
+                * FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx
+                * and 24xx. Use 16 or 32 word frames when the
+                * blocksize is at least that large. Blocksize is
+                * usually 512 bytes; but not for some SD reads.
+                */
+               burst = cpu_is_omap15xx() ? 32 : 64;
+               if (burst > data->blksz)
+                       burst = data->blksz;
+
+               burst >>= 1;
+
+               if (data->flags & MMC_DATA_WRITE) {
+                       c = host->dma_tx;
+                       bp = &host->dma_tx_burst;
+                       buf = 0x0f80 | (burst - 1) << 0;
+                       dma_data_dir = DMA_TO_DEVICE;
+               } else {
+                       c = host->dma_rx;
+                       bp = &host->dma_rx_burst;
+                       buf = 0x800f | (burst - 1) << 8;
+                       dma_data_dir = DMA_FROM_DEVICE;
+               }
+
+               if (!c)
+                       goto use_pio;
+
+               /* Only reconfigure if we have a different burst size */
+               if (*bp != burst) {
+                       struct dma_slave_config cfg;
+
+                       cfg.src_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
+                       cfg.dst_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
+                       cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+                       cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+                       cfg.src_maxburst = burst;
+                       cfg.dst_maxburst = burst;
+
+                       if (dmaengine_slave_config(c, &cfg))
+                               goto use_pio;
+
+                       *bp = burst;
+               }
+
+               host->sg_len = dma_map_sg(c->device->dev, data->sg, sg_len,
+                                         dma_data_dir);
+               if (host->sg_len == 0)
+                       goto use_pio;
+
+               tx = dmaengine_prep_slave_sg(c, data->sg, host->sg_len,
+                       data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+                       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+               if (!tx)
+                       goto use_pio;
+
+               OMAP_MMC_WRITE(host, BUF, buf);
+
+               tx->callback = mmc_omap_dma_callback;
+               tx->callback_param = host;
+               dmaengine_submit(tx);
+               host->brs_received = 0;
+               host->dma_done = 0;
+               host->dma_in_use = 1;
+               return;
        }
+ use_pio:
 
        /* Revert to PIO? */
-       if (!use_dma) {
-               OMAP_MMC_WRITE(host, BUF, 0x1f1f);
-               host->total_bytes_left = data->blocks * block_size;
-               host->sg_len = sg_len;
-               mmc_omap_sg_to_buf(host);
-               host->dma_in_use = 0;
-       }
+       OMAP_MMC_WRITE(host, BUF, 0x1f1f);
+       host->total_bytes_left = data->blocks * block_size;
+       host->sg_len = sg_len;
+       mmc_omap_sg_to_buf(host);
+       host->dma_in_use = 0;
 }
 
 static void mmc_omap_start_request(struct mmc_omap_host *host,
@@ -1157,8 +1064,12 @@ static void mmc_omap_start_request(struct mmc_omap_host *host,
        /* only touch fifo AFTER the controller readies it */
        mmc_omap_prepare_data(host, req);
        mmc_omap_start_command(host, req->cmd);
-       if (host->dma_in_use)
-               omap_start_dma(host->dma_ch);
+       if (host->dma_in_use) {
+               struct dma_chan *c = host->data->flags & MMC_DATA_WRITE ?
+                               host->dma_tx : host->dma_rx;
+
+               dma_async_issue_pending(c);
+       }
 }
 
 static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)
@@ -1400,6 +1311,8 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
        struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
        struct mmc_omap_host *host = NULL;
        struct resource *res;
+       dma_cap_mask_t mask;
+       unsigned sig;
        int i, ret = 0;
        int irq;
 
@@ -1439,7 +1352,6 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
        setup_timer(&host->clk_timer, mmc_omap_clk_timer, (unsigned long) host);
 
        spin_lock_init(&host->dma_lock);
-       setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host);
        spin_lock_init(&host->slot_lock);
        init_waitqueue_head(&host->slot_wq);
 
@@ -1450,11 +1362,7 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
        host->id = pdev->id;
        host->mem_res = res;
        host->irq = irq;
-
        host->use_dma = 1;
-       host->dev->dma_mask = &pdata->dma_mask;
-       host->dma_ch = -1;
-
        host->irq = irq;
        host->phys_base = host->mem_res->start;
        host->virt_base = ioremap(res->start, resource_size(res));
@@ -1474,9 +1382,48 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
                goto err_free_iclk;
        }
 
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+
+       host->dma_tx_burst = -1;
+       host->dma_rx_burst = -1;
+
+       if (cpu_is_omap24xx())
+               sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX : OMAP24XX_DMA_MMC2_TX;
+       else
+               sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
+       host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+#if 0
+       if (!host->dma_tx) {
+               dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
+                       sig);
+               goto err_dma;
+       }
+#else
+       if (!host->dma_tx)
+               dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n",
+                       sig);
+#endif
+       if (cpu_is_omap24xx())
+               sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX;
+       else
+               sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
+       host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+#if 0
+       if (!host->dma_rx) {
+               dev_err(host->dev, "unable to obtain RX DMA engine channel %u\n",
+                       sig);
+               goto err_dma;
+       }
+#else
+       if (!host->dma_rx)
+               dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n",
+                       sig);
+#endif
+
        ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);
        if (ret)
-               goto err_free_fclk;
+               goto err_free_dma;
 
        if (pdata->init != NULL) {
                ret = pdata->init(&pdev->dev);
@@ -1510,7 +1457,11 @@ err_plat_cleanup:
                pdata->cleanup(&pdev->dev);
 err_free_irq:
        free_irq(host->irq, host);
-err_free_fclk:
+err_free_dma:
+       if (host->dma_tx)
+               dma_release_channel(host->dma_tx);
+       if (host->dma_rx)
+               dma_release_channel(host->dma_rx);
        clk_put(host->fclk);
 err_free_iclk:
        clk_disable(host->iclk);
@@ -1545,6 +1496,11 @@ static int __devexit mmc_omap_remove(struct platform_device *pdev)
        clk_disable(host->iclk);
        clk_put(host->iclk);
 
+       if (host->dma_tx)
+               dma_release_channel(host->dma_tx);
+       if (host->dma_rx)
+               dma_release_channel(host->dma_rx);
+
        iounmap(host->virt_base);
        release_mem_region(pdev->resource[0].start,
                           pdev->resource[0].end - pdev->resource[0].start + 1);
index bc28627af66b961372f0f38cc0bbe61d621cc99a..3a09f93cc3b6f846a2b4cd327a7fd2a4524e31bc 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/debugfs.h>
+#include <linux/dmaengine.h>
 #include <linux/seq_file.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
@@ -29,6 +30,7 @@
 #include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/of_device.h>
+#include <linux/omap-dma.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/core.h>
 #include <linux/mmc/mmc.h>
@@ -37,7 +39,6 @@
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
 #include <linux/pm_runtime.h>
-#include <plat/dma.h>
 #include <mach/hardware.h>
 #include <plat/board.h>
 #include <plat/mmc.h>
@@ -166,7 +167,8 @@ struct omap_hsmmc_host {
        int                     suspended;
        int                     irq;
        int                     use_dma, dma_ch;
-       int                     dma_line_tx, dma_line_rx;
+       struct dma_chan         *tx_chan;
+       struct dma_chan         *rx_chan;
        int                     slot_id;
        int                     response_busy;
        int                     context_loss;
@@ -797,6 +799,12 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data)
                return DMA_FROM_DEVICE;
 }
 
+static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host,
+       struct mmc_data *data)
+{
+       return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
+}
+
 static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
 {
        int dma_ch;
@@ -889,10 +897,13 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
        spin_unlock_irqrestore(&host->irq_lock, flags);
 
        if (host->use_dma && dma_ch != -1) {
-               dma_unmap_sg(mmc_dev(host->mmc), host->data->sg,
-                       host->data->sg_len,
+               struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data);
+
+               dmaengine_terminate_all(chan);
+               dma_unmap_sg(chan->device->dev,
+                       host->data->sg, host->data->sg_len,
                        omap_hsmmc_get_dma_dir(host, host->data));
-               omap_free_dma(dma_ch);
+
                host->data->host_cookie = 0;
        }
        host->data = NULL;
@@ -1190,90 +1201,29 @@ static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static int omap_hsmmc_get_dma_sync_dev(struct omap_hsmmc_host *host,
-                                    struct mmc_data *data)
-{
-       int sync_dev;
-
-       if (data->flags & MMC_DATA_WRITE)
-               sync_dev = host->dma_line_tx;
-       else
-               sync_dev = host->dma_line_rx;
-       return sync_dev;
-}
-
-static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host,
-                                      struct mmc_data *data,
-                                      struct scatterlist *sgl)
-{
-       int blksz, nblk, dma_ch;
-
-       dma_ch = host->dma_ch;
-       if (data->flags & MMC_DATA_WRITE) {
-               omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
-                       (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
-               omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
-                       sg_dma_address(sgl), 0, 0);
-       } else {
-               omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
-                       (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
-               omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
-                       sg_dma_address(sgl), 0, 0);
-       }
-
-       blksz = host->data->blksz;
-       nblk = sg_dma_len(sgl) / blksz;
-
-       omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
-                       blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
-                       omap_hsmmc_get_dma_sync_dev(host, data),
-                       !(data->flags & MMC_DATA_WRITE));
-
-       omap_start_dma(dma_ch);
-}
-
-/*
- * DMA call back function
- */
-static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
+static void omap_hsmmc_dma_callback(void *param)
 {
-       struct omap_hsmmc_host *host = cb_data;
+       struct omap_hsmmc_host *host = param;
+       struct dma_chan *chan;
        struct mmc_data *data;
-       int dma_ch, req_in_progress;
-       unsigned long flags;
-
-       if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
-               dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
-                       ch_status);
-               return;
-       }
+       int req_in_progress;
 
-       spin_lock_irqsave(&host->irq_lock, flags);
+       spin_lock_irq(&host->irq_lock);
        if (host->dma_ch < 0) {
-               spin_unlock_irqrestore(&host->irq_lock, flags);
+               spin_unlock_irq(&host->irq_lock);
                return;
        }
 
        data = host->mrq->data;
-       host->dma_sg_idx++;
-       if (host->dma_sg_idx < host->dma_len) {
-               /* Fire up the next transfer. */
-               omap_hsmmc_config_dma_params(host, data,
-                                          data->sg + host->dma_sg_idx);
-               spin_unlock_irqrestore(&host->irq_lock, flags);
-               return;
-       }
-
+       chan = omap_hsmmc_get_dma_chan(host, data);
        if (!data->host_cookie)
-               dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+               dma_unmap_sg(chan->device->dev,
+                            data->sg, data->sg_len,
                             omap_hsmmc_get_dma_dir(host, data));
 
        req_in_progress = host->req_in_progress;
-       dma_ch = host->dma_ch;
        host->dma_ch = -1;
-       spin_unlock_irqrestore(&host->irq_lock, flags);
-
-       omap_free_dma(dma_ch);
+       spin_unlock_irq(&host->irq_lock);
 
        /* If DMA has finished after TC, complete the request */
        if (!req_in_progress) {
@@ -1286,7 +1236,8 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
 
 static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
                                       struct mmc_data *data,
-                                      struct omap_hsmmc_next *next)
+                                      struct omap_hsmmc_next *next,
+                                      struct dma_chan *chan)
 {
        int dma_len;
 
@@ -1301,8 +1252,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
        /* Check if next job is already prepared */
        if (next ||
            (!next && data->host_cookie != host->next_data.cookie)) {
-               dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
-                                    data->sg_len,
+               dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
                                     omap_hsmmc_get_dma_dir(host, data));
 
        } else {
@@ -1329,8 +1279,11 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
 static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
                                        struct mmc_request *req)
 {
-       int dma_ch = 0, ret = 0, i;
+       struct dma_slave_config cfg;
+       struct dma_async_tx_descriptor *tx;
+       int ret = 0, i;
        struct mmc_data *data = req->data;
+       struct dma_chan *chan;
 
        /* Sanity check: all the SG entries must be aligned by block size. */
        for (i = 0; i < data->sg_len; i++) {
@@ -1348,22 +1301,41 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
 
        BUG_ON(host->dma_ch != -1);
 
-       ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data),
-                              "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
-       if (ret != 0) {
-               dev_err(mmc_dev(host->mmc),
-                       "%s: omap_request_dma() failed with %d\n",
-                       mmc_hostname(host->mmc), ret);
+       chan = omap_hsmmc_get_dma_chan(host, data);
+
+       cfg.src_addr = host->mapbase + OMAP_HSMMC_DATA;
+       cfg.dst_addr = host->mapbase + OMAP_HSMMC_DATA;
+       cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       cfg.src_maxburst = data->blksz / 4;
+       cfg.dst_maxburst = data->blksz / 4;
+
+       ret = dmaengine_slave_config(chan, &cfg);
+       if (ret)
                return ret;
-       }
-       ret = omap_hsmmc_pre_dma_transfer(host, data, NULL);
+
+       ret = omap_hsmmc_pre_dma_transfer(host, data, NULL, chan);
        if (ret)
                return ret;
 
-       host->dma_ch = dma_ch;
-       host->dma_sg_idx = 0;
+       tx = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len,
+               data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+               DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       if (!tx) {
+               dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n");
+               /* FIXME: cleanup */
+               return -1;
+       }
+
+       tx->callback = omap_hsmmc_dma_callback;
+       tx->callback_param = host;
 
-       omap_hsmmc_config_dma_params(host, data, data->sg);
+       /* Does not fail */
+       dmaengine_submit(tx);
+
+       host->dma_ch = 1;
+
+       dma_async_issue_pending(chan);
 
        return 0;
 }
@@ -1445,11 +1417,11 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
        struct omap_hsmmc_host *host = mmc_priv(mmc);
        struct mmc_data *data = mrq->data;
 
-       if (host->use_dma) {
-               if (data->host_cookie)
-                       dma_unmap_sg(mmc_dev(host->mmc), data->sg,
-                                    data->sg_len,
-                                    omap_hsmmc_get_dma_dir(host, data));
+       if (host->use_dma && data->host_cookie) {
+               struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data);
+
+               dma_unmap_sg(c->device->dev, data->sg, data->sg_len,
+                            omap_hsmmc_get_dma_dir(host, data));
                data->host_cookie = 0;
        }
 }
@@ -1464,10 +1436,13 @@ static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
                return ;
        }
 
-       if (host->use_dma)
+       if (host->use_dma) {
+               struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data);
+
                if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
-                                               &host->next_data))
+                                               &host->next_data, c))
                        mrq->data->host_cookie = 0;
+       }
 }
 
 /*
@@ -1800,6 +1775,8 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
        struct resource *res;
        int ret, irq;
        const struct of_device_id *match;
+       dma_cap_mask_t mask;
+       unsigned tx_req, rx_req;
 
        match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
        if (match) {
@@ -1844,7 +1821,6 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
        host->pdata     = pdata;
        host->dev       = &pdev->dev;
        host->use_dma   = 1;
-       host->dev->dma_mask = &pdata->dma_mask;
        host->dma_ch    = -1;
        host->irq       = irq;
        host->slot_id   = 0;
@@ -1934,7 +1910,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
                ret = -ENXIO;
                goto err_irq;
        }
-       host->dma_line_tx = res->start;
+       tx_req = res->start;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
        if (!res) {
@@ -1942,7 +1918,24 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
                ret = -ENXIO;
                goto err_irq;
        }
-       host->dma_line_rx = res->start;
+       rx_req = res->start;
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+
+       host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &rx_req);
+       if (!host->rx_chan) {
+               dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
+               ret = -ENXIO;
+               goto err_irq;
+       }
+
+       host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &tx_req);
+       if (!host->tx_chan) {
+               dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req);
+               ret = -ENXIO;
+               goto err_irq;
+       }
 
        /* Request IRQ for MMC operations */
        ret = request_irq(host->irq, omap_hsmmc_irq, 0,
@@ -2021,6 +2014,10 @@ err_reg:
 err_irq_cd_init:
        free_irq(host->irq, host);
 err_irq:
+       if (host->tx_chan)
+               dma_release_channel(host->tx_chan);
+       if (host->rx_chan)
+               dma_release_channel(host->rx_chan);
        pm_runtime_put_sync(host->dev);
        pm_runtime_disable(host->dev);
        clk_put(host->fclk);
@@ -2056,6 +2053,11 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
        if (mmc_slot(host).card_detect_irq)
                free_irq(mmc_slot(host).card_detect_irq, host);
 
+       if (host->tx_chan)
+               dma_release_channel(host->tx_chan);
+       if (host->rx_chan)
+               dma_release_channel(host->rx_chan);
+
        pm_runtime_put_sync(host->dev);
        pm_runtime_disable(host->dev);
        clk_put(host->fclk);
index a6fa884ae49bb08deba807ae6aeca7f0fcbabff7..100b6775e175f9bafb62b868de4710b07c338637 100644 (file)
 
 #define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1)
 #define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1)
+#define JZ_NAND_CTRL_ASSERT_CHIP_MASK 0xaa
 
-#define JZ_NAND_MEM_ADDR_OFFSET 0x10000
 #define JZ_NAND_MEM_CMD_OFFSET 0x08000
+#define JZ_NAND_MEM_ADDR_OFFSET 0x10000
 
 struct jz_nand {
        struct mtd_info mtd;
@@ -62,8 +63,11 @@ struct jz_nand {
        void __iomem *base;
        struct resource *mem;
 
-       void __iomem *bank_base;
-       struct resource *bank_mem;
+       unsigned char banks[JZ_NAND_NUM_BANKS];
+       void __iomem *bank_base[JZ_NAND_NUM_BANKS];
+       struct resource *bank_mem[JZ_NAND_NUM_BANKS];
+
+       int selected_bank;
 
        struct jz_nand_platform_data *pdata;
        bool is_reading;
@@ -74,26 +78,50 @@ static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd)
        return container_of(mtd, struct jz_nand, mtd);
 }
 
+static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+       struct jz_nand *nand = mtd_to_jz_nand(mtd);
+       struct nand_chip *chip = mtd->priv;
+       uint32_t ctrl;
+       int banknr;
+
+       ctrl = readl(nand->base + JZ_REG_NAND_CTRL);
+       ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK;
+
+       if (chipnr == -1) {
+               banknr = -1;
+       } else {
+               banknr = nand->banks[chipnr] - 1;
+               chip->IO_ADDR_R = nand->bank_base[banknr];
+               chip->IO_ADDR_W = nand->bank_base[banknr];
+       }
+       writel(ctrl, nand->base + JZ_REG_NAND_CTRL);
+
+       nand->selected_bank = banknr;
+}
+
 static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
 {
        struct jz_nand *nand = mtd_to_jz_nand(mtd);
        struct nand_chip *chip = mtd->priv;
        uint32_t reg;
+       void __iomem *bank_base = nand->bank_base[nand->selected_bank];
+
+       BUG_ON(nand->selected_bank < 0);
 
        if (ctrl & NAND_CTRL_CHANGE) {
                BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE));
                if (ctrl & NAND_ALE)
-                       chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_ADDR_OFFSET;
+                       bank_base += JZ_NAND_MEM_ADDR_OFFSET;
                else if (ctrl & NAND_CLE)
-                       chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_CMD_OFFSET;
-               else
-                       chip->IO_ADDR_W = nand->bank_base;
+                       bank_base += JZ_NAND_MEM_CMD_OFFSET;
+               chip->IO_ADDR_W = bank_base;
 
                reg = readl(nand->base + JZ_REG_NAND_CTRL);
                if (ctrl & NAND_NCE)
-                       reg |= JZ_NAND_CTRL_ASSERT_CHIP(0);
+                       reg |= JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank);
                else
-                       reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0);
+                       reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank);
                writel(reg, nand->base + JZ_REG_NAND_CTRL);
        }
        if (dat != NAND_CMD_NONE)
@@ -252,7 +280,7 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat,
 }
 
 static int jz_nand_ioremap_resource(struct platform_device *pdev,
-       const char *name, struct resource **res, void __iomem **base)
+       const char *name, struct resource **res, void *__iomem *base)
 {
        int ret;
 
@@ -288,6 +316,90 @@ err:
        return ret;
 }
 
+static inline void jz_nand_iounmap_resource(struct resource *res, void __iomem *base)
+{
+       iounmap(base);
+       release_mem_region(res->start, resource_size(res));
+}
+
+static int __devinit jz_nand_detect_bank(struct platform_device *pdev, struct jz_nand *nand, unsigned char bank, size_t chipnr, uint8_t *nand_maf_id, uint8_t *nand_dev_id) {
+       int ret;
+       int gpio;
+       char gpio_name[9];
+       char res_name[6];
+       uint32_t ctrl;
+       struct mtd_info *mtd = &nand->mtd;
+       struct nand_chip *chip = &nand->chip;
+
+       /* Request GPIO port. */
+       gpio = JZ_GPIO_MEM_CS0 + bank - 1;
+       sprintf(gpio_name, "NAND CS%d", bank);
+       ret = gpio_request(gpio, gpio_name);
+       if (ret) {
+               dev_warn(&pdev->dev,
+                       "Failed to request %s gpio %d: %d\n",
+                       gpio_name, gpio, ret);
+               goto notfound_gpio;
+       }
+
+       /* Request I/O resource. */
+       sprintf(res_name, "bank%d", bank);
+       ret = jz_nand_ioremap_resource(pdev, res_name,
+                                       &nand->bank_mem[bank - 1],
+                                       &nand->bank_base[bank - 1]);
+       if (ret)
+               goto notfound_resource;
+
+       /* Enable chip in bank. */
+       jz_gpio_set_function(gpio, JZ_GPIO_FUNC_MEM_CS0);
+       ctrl = readl(nand->base + JZ_REG_NAND_CTRL);
+       ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1);
+       writel(ctrl, nand->base + JZ_REG_NAND_CTRL);
+
+       if (chipnr == 0) {
+               /* Detect first chip. */
+               ret = nand_scan_ident(mtd, 1, NULL);
+               if (ret)
+                       goto notfound_id;
+
+               /* Retrieve the IDs from the first chip. */
+               chip->select_chip(mtd, 0);
+               chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+               chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+               *nand_maf_id = chip->read_byte(mtd);
+               *nand_dev_id = chip->read_byte(mtd);
+       } else {
+               /* Detect additional chip. */
+               chip->select_chip(mtd, chipnr);
+               chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+               chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+               if (*nand_maf_id != chip->read_byte(mtd)
+                || *nand_dev_id != chip->read_byte(mtd)) {
+                       ret = -ENODEV;
+                       goto notfound_id;
+               }
+
+               /* Update size of the MTD. */
+               chip->numchips++;
+               mtd->size += chip->chipsize;
+       }
+
+       dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank);
+       return 0;
+
+notfound_id:
+       dev_info(&pdev->dev, "No chip found on bank %i\n", bank);
+       ctrl &= ~(JZ_NAND_CTRL_ENABLE_CHIP(bank - 1));
+       writel(ctrl, nand->base + JZ_REG_NAND_CTRL);
+       jz_gpio_set_function(gpio, JZ_GPIO_FUNC_NONE);
+       jz_nand_iounmap_resource(nand->bank_mem[bank - 1],
+                                nand->bank_base[bank - 1]);
+notfound_resource:
+       gpio_free(gpio);
+notfound_gpio:
+       return ret;
+}
+
 static int __devinit jz_nand_probe(struct platform_device *pdev)
 {
        int ret;
@@ -295,6 +407,8 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
        struct nand_chip *chip;
        struct mtd_info *mtd;
        struct jz_nand_platform_data *pdata = pdev->dev.platform_data;
+       size_t chipnr, bank_idx;
+       uint8_t nand_maf_id = 0, nand_dev_id = 0;
 
        nand = kzalloc(sizeof(*nand), GFP_KERNEL);
        if (!nand) {
@@ -305,10 +419,6 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
        ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base);
        if (ret)
                goto err_free;
-       ret = jz_nand_ioremap_resource(pdev, "bank", &nand->bank_mem,
-                       &nand->bank_base);
-       if (ret)
-               goto err_iounmap_mmio;
 
        if (pdata && gpio_is_valid(pdata->busy_gpio)) {
                ret = gpio_request(pdata->busy_gpio, "NAND busy pin");
@@ -316,7 +426,7 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev,
                                "Failed to request busy gpio %d: %d\n",
                                pdata->busy_gpio, ret);
-                       goto err_iounmap_mem;
+                       goto err_iounmap_mmio;
                }
        }
 
@@ -339,22 +449,51 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
 
        chip->chip_delay = 50;
        chip->cmd_ctrl = jz_nand_cmd_ctrl;
+       chip->select_chip = jz_nand_select_chip;
 
        if (pdata && gpio_is_valid(pdata->busy_gpio))
                chip->dev_ready = jz_nand_dev_ready;
 
-       chip->IO_ADDR_R = nand->bank_base;
-       chip->IO_ADDR_W = nand->bank_base;
-
        nand->pdata = pdata;
        platform_set_drvdata(pdev, nand);
 
-       writel(JZ_NAND_CTRL_ENABLE_CHIP(0), nand->base + JZ_REG_NAND_CTRL);
-
-       ret = nand_scan_ident(mtd, 1, NULL);
-       if (ret) {
-               dev_err(&pdev->dev,  "Failed to scan nand\n");
-               goto err_gpio_free;
+       /* We are going to autodetect NAND chips in the banks specified in the
+        * platform data. Although nand_scan_ident() can detect multiple chips,
+        * it requires those chips to be numbered consecuitively, which is not
+        * always the case for external memory banks. And a fixed chip-to-bank
+        * mapping is not practical either, since for example Dingoo units
+        * produced at different times have NAND chips in different banks.
+        */
+       chipnr = 0;
+       for (bank_idx = 0; bank_idx < JZ_NAND_NUM_BANKS; bank_idx++) {
+               unsigned char bank;
+
+               /* If there is no platform data, look for NAND in bank 1,
+                * which is the most likely bank since it is the only one
+                * that can be booted from.
+                */
+               bank = pdata ? pdata->banks[bank_idx] : bank_idx ^ 1;
+               if (bank == 0)
+                       break;
+               if (bank > JZ_NAND_NUM_BANKS) {
+                       dev_warn(&pdev->dev,
+                               "Skipping non-existing bank: %d\n", bank);
+                       continue;
+               }
+               /* The detection routine will directly or indirectly call
+                * jz_nand_select_chip(), so nand->banks has to contain the
+                * bank we're checking.
+                */
+               nand->banks[chipnr] = bank;
+               if (jz_nand_detect_bank(pdev, nand, bank, chipnr,
+                                       &nand_maf_id, &nand_dev_id) == 0)
+                       chipnr++;
+               else
+                       nand->banks[chipnr] = 0;
+       }
+       if (chipnr == 0) {
+               dev_err(&pdev->dev, "No NAND chips found\n");
+               goto err_gpio_busy;
        }
 
        if (pdata && pdata->ident_callback) {
@@ -364,8 +503,8 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
 
        ret = nand_scan_tail(mtd);
        if (ret) {
-               dev_err(&pdev->dev,  "Failed to scan nand\n");
-               goto err_gpio_free;
+               dev_err(&pdev->dev,  "Failed to scan NAND\n");
+               goto err_unclaim_banks;
        }
 
        ret = mtd_device_parse_register(mtd, NULL, NULL,
@@ -382,14 +521,21 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
        return 0;
 
 err_nand_release:
-       nand_release(&nand->mtd);
-err_gpio_free:
+       nand_release(mtd);
+err_unclaim_banks:
+       while (chipnr--) {
+               unsigned char bank = nand->banks[chipnr];
+               gpio_free(JZ_GPIO_MEM_CS0 + bank - 1);
+               jz_nand_iounmap_resource(nand->bank_mem[bank - 1],
+                                        nand->bank_base[bank - 1]);
+       }
+       writel(0, nand->base + JZ_REG_NAND_CTRL);
+err_gpio_busy:
+       if (pdata && gpio_is_valid(pdata->busy_gpio))
+               gpio_free(pdata->busy_gpio);
        platform_set_drvdata(pdev, NULL);
-       gpio_free(pdata->busy_gpio);
-err_iounmap_mem:
-       iounmap(nand->bank_base);
 err_iounmap_mmio:
-       iounmap(nand->base);
+       jz_nand_iounmap_resource(nand->mem, nand->base);
 err_free:
        kfree(nand);
        return ret;
@@ -398,16 +544,26 @@ err_free:
 static int __devexit jz_nand_remove(struct platform_device *pdev)
 {
        struct jz_nand *nand = platform_get_drvdata(pdev);
+       struct jz_nand_platform_data *pdata = pdev->dev.platform_data;
+       size_t i;
 
        nand_release(&nand->mtd);
 
        /* Deassert and disable all chips */
        writel(0, nand->base + JZ_REG_NAND_CTRL);
 
-       iounmap(nand->bank_base);
-       release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem));
-       iounmap(nand->base);
-       release_mem_region(nand->mem->start, resource_size(nand->mem));
+       for (i = 0; i < JZ_NAND_NUM_BANKS; ++i) {
+               unsigned char bank = nand->banks[i];
+               if (bank != 0) {
+                       jz_nand_iounmap_resource(nand->bank_mem[bank - 1],
+                                                nand->bank_base[bank - 1]);
+                       gpio_free(JZ_GPIO_MEM_CS0 + bank - 1);
+               }
+       }
+       if (pdata && gpio_is_valid(pdata->busy_gpio))
+               gpio_free(pdata->busy_gpio);
+
+       jz_nand_iounmap_resource(nand->mem, nand->base);
 
        platform_set_drvdata(pdev, NULL);
        kfree(nand);
index d7f681d0c9b98e54023cdce755b2febf6a983708..e9309b3659e746096b08a953fd055e2517babb3d 100644 (file)
@@ -9,6 +9,7 @@
  */
 
 #include <linux/platform_device.h>
+#include <linux/dmaengine.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
 #include <linux/module.h>
@@ -18,6 +19,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
+#include <linux/omap-dma.h>
 #include <linux/io.h>
 #include <linux/slab.h>
 
@@ -123,7 +125,7 @@ struct omap_nand_info {
        int                             gpmc_cs;
        unsigned long                   phys_base;
        struct completion               comp;
-       int                             dma_ch;
+       struct dma_chan                 *dma;
        int                             gpmc_irq;
        enum {
                OMAP_NAND_IO_READ = 0,  /* read */
@@ -336,12 +338,10 @@ static void omap_write_buf_pref(struct mtd_info *mtd,
 }
 
 /*
- * omap_nand_dma_cb: callback on the completion of dma transfer
- * @lch: logical channel
- * @ch_satuts: channel status
+ * omap_nand_dma_callback: callback on the completion of dma transfer
  * @data: pointer to completion data structure
  */
-static void omap_nand_dma_cb(int lch, u16 ch_status, void *data)
+static void omap_nand_dma_callback(void *data)
 {
        complete((struct completion *) data);
 }
@@ -358,17 +358,13 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
 {
        struct omap_nand_info *info = container_of(mtd,
                                        struct omap_nand_info, mtd);
+       struct dma_async_tx_descriptor *tx;
        enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
                                                        DMA_FROM_DEVICE;
-       dma_addr_t dma_addr;
-       int ret;
+       struct scatterlist sg;
        unsigned long tim, limit;
-
-       /* The fifo depth is 64 bytes max.
-        * But configure the FIFO-threahold to 32 to get a sync at each frame
-        * and frame length is 32 bytes.
-        */
-       int buf_len = len >> 6;
+       unsigned n;
+       int ret;
 
        if (addr >= high_memory) {
                struct page *p1;
@@ -382,40 +378,33 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
                addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK);
        }
 
-       dma_addr = dma_map_single(&info->pdev->dev, addr, len, dir);
-       if (dma_mapping_error(&info->pdev->dev, dma_addr)) {
+       sg_init_one(&sg, addr, len);
+       n = dma_map_sg(info->dma->device->dev, &sg, 1, dir);
+       if (n == 0) {
                dev_err(&info->pdev->dev,
                        "Couldn't DMA map a %d byte buffer\n", len);
                goto out_copy;
        }
 
-       if (is_write) {
-           omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
-                                               info->phys_base, 0, 0);
-           omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
-                                                       dma_addr, 0, 0);
-           omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32,
-                                       0x10, buf_len, OMAP_DMA_SYNC_FRAME,
-                                       OMAP24XX_DMA_GPMC, OMAP_DMA_DST_SYNC);
-       } else {
-           omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
-                                               info->phys_base, 0, 0);
-           omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
-                                                       dma_addr, 0, 0);
-           omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32,
-                                       0x10, buf_len, OMAP_DMA_SYNC_FRAME,
-                                       OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC);
-       }
-       /*  configure and start prefetch transfer */
+       tx = dmaengine_prep_slave_sg(info->dma, &sg, n,
+               is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+               DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       if (!tx)
+               goto out_copy_unmap;
+
+       tx->callback = omap_nand_dma_callback;
+       tx->callback_param = &info->comp;
+       dmaengine_submit(tx);
+
+       /* configure and start prefetch transfer */
        ret = gpmc_prefetch_enable(info->gpmc_cs,
-                       PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write);
+               PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write);
        if (ret)
                /* PFPW engine is busy, use cpu copy method */
                goto out_copy_unmap;
 
        init_completion(&info->comp);
-
-       omap_start_dma(info->dma_ch);
+       dma_async_issue_pending(info->dma);
 
        /* setup and start DMA using dma_addr */
        wait_for_completion(&info->comp);
@@ -427,11 +416,11 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
        /* disable and stop the PFPW engine */
        gpmc_prefetch_reset(info->gpmc_cs);
 
-       dma_unmap_single(&info->pdev->dev, dma_addr, len, dir);
+       dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
        return 0;
 
 out_copy_unmap:
-       dma_unmap_single(&info->pdev->dev, dma_addr, len, dir);
+       dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
 out_copy:
        if (info->nand.options & NAND_BUSWIDTH_16)
                is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len)
@@ -1164,6 +1153,8 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
        struct omap_nand_platform_data  *pdata;
        int                             err;
        int                             i, offset;
+       dma_cap_mask_t mask;
+       unsigned sig;
 
        pdata = pdev->dev.platform_data;
        if (pdata == NULL) {
@@ -1244,18 +1235,31 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
                break;
 
        case NAND_OMAP_PREFETCH_DMA:
-               err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND",
-                               omap_nand_dma_cb, &info->comp, &info->dma_ch);
-               if (err < 0) {
-                       info->dma_ch = -1;
-                       dev_err(&pdev->dev, "DMA request failed!\n");
+               dma_cap_zero(mask);
+               dma_cap_set(DMA_SLAVE, mask);
+               sig = OMAP24XX_DMA_GPMC;
+               info->dma = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+               if (!info->dma) {
+                       dev_err(&pdev->dev, "DMA engine request failed\n");
+                       err = -ENXIO;
                        goto out_release_mem_region;
                } else {
-                       omap_set_dma_dest_burst_mode(info->dma_ch,
-                                       OMAP_DMA_DATA_BURST_16);
-                       omap_set_dma_src_burst_mode(info->dma_ch,
-                                       OMAP_DMA_DATA_BURST_16);
-
+                       struct dma_slave_config cfg;
+                       int rc;
+
+                       memset(&cfg, 0, sizeof(cfg));
+                       cfg.src_addr = info->phys_base;
+                       cfg.dst_addr = info->phys_base;
+                       cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+                       cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+                       cfg.src_maxburst = 16;
+                       cfg.dst_maxburst = 16;
+                       rc = dmaengine_slave_config(info->dma, &cfg);
+                       if (rc) {
+                               dev_err(&pdev->dev, "DMA engine slave config failed: %d\n",
+                                       rc);
+                               goto out_release_mem_region;
+                       }
                        info->nand.read_buf   = omap_read_buf_dma_pref;
                        info->nand.write_buf  = omap_write_buf_dma_pref;
                }
@@ -1358,6 +1362,8 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
        return 0;
 
 out_release_mem_region:
+       if (info->dma)
+               dma_release_channel(info->dma);
        release_mem_region(info->phys_base, NAND_IO_SIZE);
 out_free_info:
        kfree(info);
@@ -1373,8 +1379,8 @@ static int omap_nand_remove(struct platform_device *pdev)
        omap3_free_bch(&info->mtd);
 
        platform_set_drvdata(pdev, NULL);
-       if (info->dma_ch != -1)
-               omap_free_dma(info->dma_ch);
+       if (info->dma)
+               dma_release_channel(info->dma);
 
        if (info->gpmc_irq)
                free_irq(info->gpmc_irq, info);
index 6fae5f3ec7f6d646e4efc79aac1a0449ca5bde21..d95fbc34b2294b781ac209f457acc09db6808659 100644 (file)
@@ -1120,10 +1120,10 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
                        write_unlock_bh(&bond->curr_slave_lock);
                        read_unlock(&bond->lock);
 
-                       netdev_bonding_change(bond->dev, NETDEV_BONDING_FAILOVER);
+                       call_netdevice_notifiers(NETDEV_BONDING_FAILOVER, bond->dev);
                        if (should_notify_peers)
-                               netdev_bonding_change(bond->dev,
-                                                     NETDEV_NOTIFY_PEERS);
+                               call_netdevice_notifiers(NETDEV_NOTIFY_PEERS,
+                                                        bond->dev);
 
                        read_lock(&bond->lock);
                        write_lock_bh(&bond->curr_slave_lock);
@@ -1560,8 +1560,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                                 bond_dev->name,
                                 bond_dev->type, slave_dev->type);
 
-                       res = netdev_bonding_change(bond_dev,
-                                                   NETDEV_PRE_TYPE_CHANGE);
+                       res = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE,
+                                                      bond_dev);
                        res = notifier_to_errno(res);
                        if (res) {
                                pr_err("%s: refused to change device type\n",
@@ -1581,8 +1581,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                                bond_dev->priv_flags &= ~IFF_TX_SKB_SHARING;
                        }
 
-                       netdev_bonding_change(bond_dev,
-                                             NETDEV_POST_TYPE_CHANGE);
+                       call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE,
+                                                bond_dev);
                }
        } else if (bond_dev->type != slave_dev->type) {
                pr_err("%s ether type (%d) is different from other slaves (%d), can not enslave it.\n",
@@ -1943,7 +1943,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
        }
 
        block_netpoll_tx();
-       netdev_bonding_change(bond_dev, NETDEV_RELEASE);
+       call_netdevice_notifiers(NETDEV_RELEASE, bond_dev);
        write_lock_bh(&bond->lock);
 
        slave = bond_get_slave_by_dev(bond, slave_dev);
@@ -2586,7 +2586,7 @@ re_arm:
                        read_unlock(&bond->lock);
                        return;
                }
-               netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS);
+               call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, bond->dev);
                rtnl_unlock();
        }
 }
@@ -3205,7 +3205,7 @@ re_arm:
                        read_unlock(&bond->lock);
                        return;
                }
-               netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS);
+               call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, bond->dev);
                rtnl_unlock();
        }
 }
index f0921d16f0a941ff5049fd8ce18dff6c379b76f9..6ff7ad006c300b5a9c499e6bf3465e4cb8b7f2bc 100644 (file)
@@ -74,7 +74,6 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev)
        const struct platform_device_id *id;
        struct resource *mem;
        int irq;
-#ifdef CONFIG_HAVE_CLK
        struct clk *clk;
 
        /* get the appropriate clk */
@@ -84,7 +83,6 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev)
                ret = -ENODEV;
                goto exit;
        }
-#endif
 
        /* get the platform data */
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -145,10 +143,8 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev)
 
        dev->irq = irq;
        priv->base = addr;
-#ifdef CONFIG_HAVE_CLK
        priv->can.clock.freq = clk_get_rate(clk);
        priv->priv = clk;
-#endif
 
        platform_set_drvdata(pdev, dev);
        SET_NETDEV_DEV(dev, &pdev->dev);
@@ -172,10 +168,8 @@ exit_iounmap:
 exit_release_mem:
        release_mem_region(mem->start, resource_size(mem));
 exit_free_clk:
-#ifdef CONFIG_HAVE_CLK
        clk_put(clk);
 exit:
-#endif
        dev_err(&pdev->dev, "probe failed\n");
 
        return ret;
@@ -196,9 +190,7 @@ static int __devexit c_can_plat_remove(struct platform_device *pdev)
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        release_mem_region(mem->start, resource_size(mem));
 
-#ifdef CONFIG_HAVE_CLK
        clk_put(priv->priv);
-#endif
 
        return 0;
 }
index f0c8bd54ce2930d87a5c6479fef7e9f39e4ca37d..021d69c5d9bc951797ede8e72dde15bad706613b 100644 (file)
@@ -1712,7 +1712,7 @@ e100_set_network_leds(int active)
 static void
 e100_netpoll(struct net_device* netdev)
 {
-       e100rxtx_interrupt(NETWORK_DMA_TX_IRQ_NBR, netdev, NULL);
+       e100rxtx_interrupt(NETWORK_DMA_TX_IRQ_NBR, netdev);
 }
 #endif
 
index 734fd87cd99093e0d30899006cb12cacd10a027c..62f754bd0dfe65704a1af826a754a720dc8cfd43 100644 (file)
@@ -2485,6 +2485,7 @@ static int bnx2x_mcast_enqueue_cmd(struct bnx2x *bp,
                break;
 
        default:
+               kfree(new_cmd);
                BNX2X_ERR("Unknown command: %d\n", cmd);
                return -EINVAL;
        }
index 8596acaa402b4af43d3d0b9c59ac2dd107f478c1..d49933ed551f7a4fca4d6b71f0528aaf9f6a80d6 100644 (file)
@@ -528,7 +528,7 @@ static unsigned int refill_fl(struct adapter *adap, struct sge_fl *q, int n,
 #endif
 
        while (n--) {
-               pg = alloc_page(gfp);
+               pg = __skb_alloc_page(gfp, NULL);
                if (unlikely(!pg)) {
                        q->alloc_failed++;
                        break;
index f2d1ecdcaf98efe0801de112f36ba12ef81fbefd..8877fbfefb639ed5e7d6282981dd6f722dd6f5f5 100644 (file)
@@ -653,7 +653,7 @@ static unsigned int refill_fl(struct adapter *adapter, struct sge_fl *fl,
 
 alloc_small_pages:
        while (n--) {
-               page = alloc_page(gfp | __GFP_NOWARN | __GFP_COLD);
+               page = __skb_alloc_page(gfp | __GFP_NOWARN, NULL);
                if (unlikely(!page)) {
                        fl->alloc_failed++;
                        break;
index c60de89b66696759a453ff4d69650c1905209c58..90a903d83d8747ca26d711b7b3a188f81a554611 100644 (file)
@@ -1948,7 +1948,7 @@ static int be_rx_cqs_create(struct be_adapter *adapter)
 
        if (adapter->num_rx_qs != MAX_RX_QS)
                dev_info(&adapter->pdev->dev,
-                       "Created only %d receive queues", adapter->num_rx_qs);
+                       "Created only %d receive queues\n", adapter->num_rx_qs);
 
        return 0;
 }
index 1050411e7ca337f8cd28dff367d5cb2e94838ddf..b7c2d5050572828c8d07e228b2e44235e9f01801 100644 (file)
@@ -6235,7 +6235,7 @@ static bool igb_alloc_mapped_page(struct igb_ring *rx_ring,
                return true;
 
        if (!page) {
-               page = alloc_page(GFP_ATOMIC | __GFP_COLD);
+               page = __skb_alloc_page(GFP_ATOMIC, bi->skb);
                bi->page = page;
                if (unlikely(!page)) {
                        rx_ring->rx_stats.alloc_failed++;
index c709eae58c630a9a1e2c5e5e7539d733cf609e31..4326f74f7137a333461683f00c8fc1cadfcee4b4 100644 (file)
@@ -1141,8 +1141,8 @@ static bool ixgbe_alloc_mapped_page(struct ixgbe_ring *rx_ring,
 
        /* alloc new page for storage */
        if (likely(!page)) {
-               page = alloc_pages(GFP_ATOMIC | __GFP_COLD | __GFP_COMP,
-                                  ixgbe_rx_pg_order(rx_ring));
+               page = __skb_alloc_pages(GFP_ATOMIC | __GFP_COLD | __GFP_COMP,
+                                        bi->skb, ixgbe_rx_pg_order(rx_ring));
                if (unlikely(!page)) {
                        rx_ring->rx_stats.alloc_rx_page_failed++;
                        return false;
index 3f9841d619adae00002d3f662f5067f3b6092d63..60ef64587412a6a70af056f93e253f4fe407cce2 100644 (file)
@@ -352,7 +352,6 @@ static void ixgbevf_alloc_rx_buffers(struct ixgbevf_adapter *adapter,
                                adapter->alloc_rx_buff_failed++;
                                goto no_buffers;
                        }
-
                        bi->skb = skb;
                }
                if (!bi->dma) {
index f32e703007702ba0652bca4bce23f7ac44287c14..5aba5ecdf1e28157fadf44072c87a4a98511d249 100644 (file)
@@ -614,8 +614,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
                /* If source MAC is equal to our own MAC and not performing
                 * the selftest or flb disabled - drop the packet */
                if (s_mac == priv->mac &&
-                       (!(dev->features & NETIF_F_LOOPBACK) ||
-                        !priv->validate_loopback))
+                   !((dev->features & NETIF_F_LOOPBACK) ||
+                     priv->validate_loopback))
                        goto next;
 
                /*
index 019d856b1334fc4e7b389eb00d16da872d63ee4a..10bba09c44ea508d047123aeafb5e001b6bbd1be 100644 (file)
@@ -164,7 +164,6 @@ int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv,
        ring->cons = 0xffffffff;
        ring->last_nr_txbb = 1;
        ring->poll_cnt = 0;
-       ring->blocked = 0;
        memset(ring->tx_info, 0, ring->size * sizeof(struct mlx4_en_tx_info));
        memset(ring->buf, 0, ring->buf_size);
 
@@ -365,14 +364,13 @@ static void mlx4_en_process_tx_cq(struct net_device *dev, struct mlx4_en_cq *cq)
        ring->cons += txbbs_skipped;
        netdev_tx_completed_queue(ring->tx_queue, packets, bytes);
 
-       /* Wakeup Tx queue if this ring stopped it */
-       if (unlikely(ring->blocked)) {
-               if ((u32) (ring->prod - ring->cons) <=
-                    ring->size - HEADROOM - MAX_DESC_TXBBS) {
-                       ring->blocked = 0;
-                       netif_tx_wake_queue(ring->tx_queue);
-                       priv->port_stats.wake_queue++;
-               }
+       /*
+        * Wakeup Tx queue if this stopped, and at least 1 packet
+        * was completed
+        */
+       if (netif_tx_queue_stopped(ring->tx_queue) && txbbs_skipped > 0) {
+               netif_tx_wake_queue(ring->tx_queue);
+               priv->port_stats.wake_queue++;
        }
 }
 
@@ -592,7 +590,6 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
                     ring->size - HEADROOM - MAX_DESC_TXBBS)) {
                /* every full Tx ring stops queue */
                netif_tx_stop_queue(ring->tx_queue);
-               ring->blocked = 1;
                priv->port_stats.queue_stopped++;
 
                return NETDEV_TX_BUSY;
index 48d0e90194cb19d2a322990425b478f9e178b623..827b72dfce99690093f0fe81ee3ccf804d59db85 100644 (file)
@@ -157,9 +157,6 @@ int mlx4_check_port_params(struct mlx4_dev *dev,
                                         "on this HCA, aborting.\n");
                                return -EINVAL;
                        }
-                       if (port_type[i] == MLX4_PORT_TYPE_ETH &&
-                           port_type[i + 1] == MLX4_PORT_TYPE_IB)
-                               return -EINVAL;
                }
        }
 
index 5f1ab105debc28b4b8ba7bdaca3f0bcb2f1cdb66..9d27e42264e2b64aea3827d760c409ab3de0c301 100644 (file)
@@ -248,7 +248,6 @@ struct mlx4_en_tx_ring {
        u32 doorbell_qpn;
        void *buf;
        u16 poll_cnt;
-       int blocked;
        struct mlx4_en_tx_info *tx_info;
        u8 *bounce_buf;
        u32 last_nr_txbb;
index 802498293528307b5c2e123eae089f84929759ea..34ee09bae36e98dcb28c8ff9b4c8d12a2da97292 100644 (file)
@@ -80,20 +80,6 @@ void mlx4_do_sense_ports(struct mlx4_dev *dev,
                        stype[i - 1] = defaults[i - 1];
        }
 
-       /*
-        * Adjust port configuration:
-        * If port 1 sensed nothing and port 2 is IB, set both as IB
-        * If port 2 sensed nothing and port 1 is Eth, set both as Eth
-        */
-       if (stype[0] == MLX4_PORT_TYPE_ETH) {
-               for (i = 1; i < dev->caps.num_ports; i++)
-                       stype[i] = stype[i] ? stype[i] : MLX4_PORT_TYPE_ETH;
-       }
-       if (stype[dev->caps.num_ports - 1] == MLX4_PORT_TYPE_IB) {
-               for (i = 0; i < dev->caps.num_ports - 1; i++)
-                       stype[i] = stype[i] ? stype[i] : MLX4_PORT_TYPE_IB;
-       }
-
        /*
         * If sensed nothing, remain in current configuration.
         */
index cd827ff4a021f74284574f75cc27de97b4c3e7fe..c42bbb16cdaebdeb7147dc887705abcfe91e86e0 100644 (file)
@@ -6,19 +6,21 @@
  * Copyright (C) 2009 Cavium Networks
  */
 
-#include <linux/capability.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
-#include <linux/init.h>
-#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/capability.h>
 #include <linux/interrupt.h>
-#include <linux/platform_device.h>
 #include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/if.h>
+#include <linux/spinlock.h>
 #include <linux/if_vlan.h>
+#include <linux/of_mdio.h>
+#include <linux/module.h>
+#include <linux/of_net.h>
+#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/phy.h>
-#include <linux/spinlock.h>
+#include <linux/io.h>
 
 #include <asm/octeon/octeon.h>
 #include <asm/octeon/cvmx-mixx-defs.h>
@@ -58,8 +60,56 @@ union mgmt_port_ring_entry {
        } s;
 };
 
+#define MIX_ORING1     0x0
+#define MIX_ORING2     0x8
+#define MIX_IRING1     0x10
+#define MIX_IRING2     0x18
+#define MIX_CTL                0x20
+#define MIX_IRHWM      0x28
+#define MIX_IRCNT      0x30
+#define MIX_ORHWM      0x38
+#define MIX_ORCNT      0x40
+#define MIX_ISR                0x48
+#define MIX_INTENA     0x50
+#define MIX_REMCNT     0x58
+#define MIX_BIST       0x78
+
+#define AGL_GMX_PRT_CFG                        0x10
+#define AGL_GMX_RX_FRM_CTL             0x18
+#define AGL_GMX_RX_FRM_MAX             0x30
+#define AGL_GMX_RX_JABBER              0x38
+#define AGL_GMX_RX_STATS_CTL           0x50
+
+#define AGL_GMX_RX_STATS_PKTS_DRP      0xb0
+#define AGL_GMX_RX_STATS_OCTS_DRP      0xb8
+#define AGL_GMX_RX_STATS_PKTS_BAD      0xc0
+
+#define AGL_GMX_RX_ADR_CTL             0x100
+#define AGL_GMX_RX_ADR_CAM_EN          0x108
+#define AGL_GMX_RX_ADR_CAM0            0x180
+#define AGL_GMX_RX_ADR_CAM1            0x188
+#define AGL_GMX_RX_ADR_CAM2            0x190
+#define AGL_GMX_RX_ADR_CAM3            0x198
+#define AGL_GMX_RX_ADR_CAM4            0x1a0
+#define AGL_GMX_RX_ADR_CAM5            0x1a8
+
+#define AGL_GMX_TX_STATS_CTL           0x268
+#define AGL_GMX_TX_CTL                 0x270
+#define AGL_GMX_TX_STAT0               0x280
+#define AGL_GMX_TX_STAT1               0x288
+#define AGL_GMX_TX_STAT2               0x290
+#define AGL_GMX_TX_STAT3               0x298
+#define AGL_GMX_TX_STAT4               0x2a0
+#define AGL_GMX_TX_STAT5               0x2a8
+#define AGL_GMX_TX_STAT6               0x2b0
+#define AGL_GMX_TX_STAT7               0x2b8
+#define AGL_GMX_TX_STAT8               0x2c0
+#define AGL_GMX_TX_STAT9               0x2c8
+
 struct octeon_mgmt {
        struct net_device *netdev;
+       u64 mix;
+       u64 agl;
        int port;
        int irq;
        u64 *tx_ring;
@@ -85,31 +135,34 @@ struct octeon_mgmt {
        struct napi_struct napi;
        struct tasklet_struct tx_clean_tasklet;
        struct phy_device *phydev;
+       struct device_node *phy_np;
+       resource_size_t mix_phys;
+       resource_size_t mix_size;
+       resource_size_t agl_phys;
+       resource_size_t agl_size;
 };
 
 static void octeon_mgmt_set_rx_irq(struct octeon_mgmt *p, int enable)
 {
-       int port = p->port;
        union cvmx_mixx_intena mix_intena;
        unsigned long flags;
 
        spin_lock_irqsave(&p->lock, flags);
-       mix_intena.u64 = cvmx_read_csr(CVMX_MIXX_INTENA(port));
+       mix_intena.u64 = cvmx_read_csr(p->mix + MIX_INTENA);
        mix_intena.s.ithena = enable ? 1 : 0;
-       cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64);
+       cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64);
        spin_unlock_irqrestore(&p->lock, flags);
 }
 
 static void octeon_mgmt_set_tx_irq(struct octeon_mgmt *p, int enable)
 {
-       int port = p->port;
        union cvmx_mixx_intena mix_intena;
        unsigned long flags;
 
        spin_lock_irqsave(&p->lock, flags);
-       mix_intena.u64 = cvmx_read_csr(CVMX_MIXX_INTENA(port));
+       mix_intena.u64 = cvmx_read_csr(p->mix + MIX_INTENA);
        mix_intena.s.othena = enable ? 1 : 0;
-       cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64);
+       cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64);
        spin_unlock_irqrestore(&p->lock, flags);
 }
 
@@ -146,7 +199,6 @@ static unsigned int ring_size_to_bytes(unsigned int ring_size)
 static void octeon_mgmt_rx_fill_ring(struct net_device *netdev)
 {
        struct octeon_mgmt *p = netdev_priv(netdev);
-       int port = p->port;
 
        while (p->rx_current_fill < ring_max_fill(OCTEON_MGMT_RX_RING_SIZE)) {
                unsigned int size;
@@ -177,24 +229,23 @@ static void octeon_mgmt_rx_fill_ring(struct net_device *netdev)
                        (p->rx_next_fill + 1) % OCTEON_MGMT_RX_RING_SIZE;
                p->rx_current_fill++;
                /* Ring the bell.  */
-               cvmx_write_csr(CVMX_MIXX_IRING2(port), 1);
+               cvmx_write_csr(p->mix + MIX_IRING2, 1);
        }
 }
 
 static void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p)
 {
-       int port = p->port;
        union cvmx_mixx_orcnt mix_orcnt;
        union mgmt_port_ring_entry re;
        struct sk_buff *skb;
        int cleaned = 0;
        unsigned long flags;
 
-       mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port));
+       mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT);
        while (mix_orcnt.s.orcnt) {
                spin_lock_irqsave(&p->tx_list.lock, flags);
 
-               mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port));
+               mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT);
 
                if (mix_orcnt.s.orcnt == 0) {
                        spin_unlock_irqrestore(&p->tx_list.lock, flags);
@@ -214,7 +265,7 @@ static void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p)
                mix_orcnt.s.orcnt = 1;
 
                /* Acknowledge to hardware that we have the buffer.  */
-               cvmx_write_csr(CVMX_MIXX_ORCNT(port), mix_orcnt.u64);
+               cvmx_write_csr(p->mix + MIX_ORCNT, mix_orcnt.u64);
                p->tx_current_fill--;
 
                spin_unlock_irqrestore(&p->tx_list.lock, flags);
@@ -224,7 +275,7 @@ static void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p)
                dev_kfree_skb_any(skb);
                cleaned++;
 
-               mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port));
+               mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT);
        }
 
        if (cleaned && netif_queue_stopped(p->netdev))
@@ -241,13 +292,12 @@ static void octeon_mgmt_clean_tx_tasklet(unsigned long arg)
 static void octeon_mgmt_update_rx_stats(struct net_device *netdev)
 {
        struct octeon_mgmt *p = netdev_priv(netdev);
-       int port = p->port;
        unsigned long flags;
        u64 drop, bad;
 
        /* These reads also clear the count registers.  */
-       drop = cvmx_read_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_DRP(port));
-       bad = cvmx_read_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_BAD(port));
+       drop = cvmx_read_csr(p->agl + AGL_GMX_RX_STATS_PKTS_DRP);
+       bad = cvmx_read_csr(p->agl + AGL_GMX_RX_STATS_PKTS_BAD);
 
        if (drop || bad) {
                /* Do an atomic update. */
@@ -261,15 +311,14 @@ static void octeon_mgmt_update_rx_stats(struct net_device *netdev)
 static void octeon_mgmt_update_tx_stats(struct net_device *netdev)
 {
        struct octeon_mgmt *p = netdev_priv(netdev);
-       int port = p->port;
        unsigned long flags;
 
        union cvmx_agl_gmx_txx_stat0 s0;
        union cvmx_agl_gmx_txx_stat1 s1;
 
        /* These reads also clear the count registers.  */
-       s0.u64 = cvmx_read_csr(CVMX_AGL_GMX_TXX_STAT0(port));
-       s1.u64 = cvmx_read_csr(CVMX_AGL_GMX_TXX_STAT1(port));
+       s0.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_STAT0);
+       s1.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_STAT1);
 
        if (s0.s.xsdef || s0.s.xscol || s1.s.scol || s1.s.mcol) {
                /* Do an atomic update. */
@@ -308,7 +357,6 @@ static u64 octeon_mgmt_dequeue_rx_buffer(struct octeon_mgmt *p,
 
 static int octeon_mgmt_receive_one(struct octeon_mgmt *p)
 {
-       int port = p->port;
        struct net_device *netdev = p->netdev;
        union cvmx_mixx_ircnt mix_ircnt;
        union mgmt_port_ring_entry re;
@@ -381,18 +429,17 @@ done:
        /* Tell the hardware we processed a packet.  */
        mix_ircnt.u64 = 0;
        mix_ircnt.s.ircnt = 1;
-       cvmx_write_csr(CVMX_MIXX_IRCNT(port), mix_ircnt.u64);
+       cvmx_write_csr(p->mix + MIX_IRCNT, mix_ircnt.u64);
        return rc;
 }
 
 static int octeon_mgmt_receive_packets(struct octeon_mgmt *p, int budget)
 {
-       int port = p->port;
        unsigned int work_done = 0;
        union cvmx_mixx_ircnt mix_ircnt;
        int rc;
 
-       mix_ircnt.u64 = cvmx_read_csr(CVMX_MIXX_IRCNT(port));
+       mix_ircnt.u64 = cvmx_read_csr(p->mix + MIX_IRCNT);
        while (work_done < budget && mix_ircnt.s.ircnt) {
 
                rc = octeon_mgmt_receive_one(p);
@@ -400,7 +447,7 @@ static int octeon_mgmt_receive_packets(struct octeon_mgmt *p, int budget)
                        work_done++;
 
                /* Check for more packets. */
-               mix_ircnt.u64 = cvmx_read_csr(CVMX_MIXX_IRCNT(port));
+               mix_ircnt.u64 = cvmx_read_csr(p->mix + MIX_IRCNT);
        }
 
        octeon_mgmt_rx_fill_ring(p->netdev);
@@ -434,16 +481,16 @@ static void octeon_mgmt_reset_hw(struct octeon_mgmt *p)
        union cvmx_agl_gmx_bist agl_gmx_bist;
 
        mix_ctl.u64 = 0;
-       cvmx_write_csr(CVMX_MIXX_CTL(p->port), mix_ctl.u64);
+       cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
        do {
-               mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(p->port));
+               mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL);
        } while (mix_ctl.s.busy);
        mix_ctl.s.reset = 1;
-       cvmx_write_csr(CVMX_MIXX_CTL(p->port), mix_ctl.u64);
-       cvmx_read_csr(CVMX_MIXX_CTL(p->port));
+       cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
+       cvmx_read_csr(p->mix + MIX_CTL);
        cvmx_wait(64);
 
-       mix_bist.u64 = cvmx_read_csr(CVMX_MIXX_BIST(p->port));
+       mix_bist.u64 = cvmx_read_csr(p->mix + MIX_BIST);
        if (mix_bist.u64)
                dev_warn(p->dev, "MIX failed BIST (0x%016llx)\n",
                        (unsigned long long)mix_bist.u64);
@@ -474,7 +521,6 @@ static void octeon_mgmt_cam_state_add(struct octeon_mgmt_cam_state *cs,
 static void octeon_mgmt_set_rx_filtering(struct net_device *netdev)
 {
        struct octeon_mgmt *p = netdev_priv(netdev);
-       int port = p->port;
        union cvmx_agl_gmx_rxx_adr_ctl adr_ctl;
        union cvmx_agl_gmx_prtx_cfg agl_gmx_prtx;
        unsigned long flags;
@@ -520,29 +566,29 @@ static void octeon_mgmt_set_rx_filtering(struct net_device *netdev)
        spin_lock_irqsave(&p->lock, flags);
 
        /* Disable packet I/O. */
-       agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
+       agl_gmx_prtx.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
        prev_packet_enable = agl_gmx_prtx.s.en;
        agl_gmx_prtx.s.en = 0;
-       cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64);
+       cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, agl_gmx_prtx.u64);
 
        adr_ctl.u64 = 0;
        adr_ctl.s.cam_mode = cam_mode;
        adr_ctl.s.mcst = multicast_mode;
        adr_ctl.s.bcst = 1;     /* Allow broadcast */
 
-       cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CTL(port), adr_ctl.u64);
+       cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CTL, adr_ctl.u64);
 
-       cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM0(port), cam_state.cam[0]);
-       cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM1(port), cam_state.cam[1]);
-       cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM2(port), cam_state.cam[2]);
-       cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM3(port), cam_state.cam[3]);
-       cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM4(port), cam_state.cam[4]);
-       cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM5(port), cam_state.cam[5]);
-       cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), cam_state.cam_mask);
+       cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM0, cam_state.cam[0]);
+       cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM1, cam_state.cam[1]);
+       cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM2, cam_state.cam[2]);
+       cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM3, cam_state.cam[3]);
+       cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM4, cam_state.cam[4]);
+       cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM5, cam_state.cam[5]);
+       cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM_EN, cam_state.cam_mask);
 
        /* Restore packet I/O. */
        agl_gmx_prtx.s.en = prev_packet_enable;
-       cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64);
+       cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, agl_gmx_prtx.u64);
 
        spin_unlock_irqrestore(&p->lock, flags);
 }
@@ -564,7 +610,6 @@ static int octeon_mgmt_set_mac_address(struct net_device *netdev, void *addr)
 static int octeon_mgmt_change_mtu(struct net_device *netdev, int new_mtu)
 {
        struct octeon_mgmt *p = netdev_priv(netdev);
-       int port = p->port;
        int size_without_fcs = new_mtu + OCTEON_MGMT_RX_HEADROOM;
 
        /*
@@ -580,8 +625,8 @@ static int octeon_mgmt_change_mtu(struct net_device *netdev, int new_mtu)
 
        netdev->mtu = new_mtu;
 
-       cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_MAX(port), size_without_fcs);
-       cvmx_write_csr(CVMX_AGL_GMX_RXX_JABBER(port),
+       cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_MAX, size_without_fcs);
+       cvmx_write_csr(p->agl + AGL_GMX_RX_JABBER,
                       (size_without_fcs + 7) & 0xfff8);
 
        return 0;
@@ -591,14 +636,13 @@ static irqreturn_t octeon_mgmt_interrupt(int cpl, void *dev_id)
 {
        struct net_device *netdev = dev_id;
        struct octeon_mgmt *p = netdev_priv(netdev);
-       int port = p->port;
        union cvmx_mixx_isr mixx_isr;
 
-       mixx_isr.u64 = cvmx_read_csr(CVMX_MIXX_ISR(port));
+       mixx_isr.u64 = cvmx_read_csr(p->mix + MIX_ISR);
 
        /* Clear any pending interrupts */
-       cvmx_write_csr(CVMX_MIXX_ISR(port), mixx_isr.u64);
-       cvmx_read_csr(CVMX_MIXX_ISR(port));
+       cvmx_write_csr(p->mix + MIX_ISR, mixx_isr.u64);
+       cvmx_read_csr(p->mix + MIX_ISR);
 
        if (mixx_isr.s.irthresh) {
                octeon_mgmt_disable_rx_irq(p);
@@ -629,7 +673,6 @@ static int octeon_mgmt_ioctl(struct net_device *netdev,
 static void octeon_mgmt_adjust_link(struct net_device *netdev)
 {
        struct octeon_mgmt *p = netdev_priv(netdev);
-       int port = p->port;
        union cvmx_agl_gmx_prtx_cfg prtx_cfg;
        unsigned long flags;
        int link_changed = 0;
@@ -640,11 +683,9 @@ static void octeon_mgmt_adjust_link(struct net_device *netdev)
                        link_changed = 1;
                if (p->last_duplex != p->phydev->duplex) {
                        p->last_duplex = p->phydev->duplex;
-                       prtx_cfg.u64 =
-                               cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
+                       prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
                        prtx_cfg.s.duplex = p->phydev->duplex;
-                       cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port),
-                                      prtx_cfg.u64);
+                       cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64);
                }
        } else {
                if (p->last_link)
@@ -670,18 +711,16 @@ static void octeon_mgmt_adjust_link(struct net_device *netdev)
 static int octeon_mgmt_init_phy(struct net_device *netdev)
 {
        struct octeon_mgmt *p = netdev_priv(netdev);
-       char phy_id[MII_BUS_ID_SIZE + 3];
 
-       if (octeon_is_simulation()) {
+       if (octeon_is_simulation() || p->phy_np == NULL) {
                /* No PHYs in the simulator. */
                netif_carrier_on(netdev);
                return 0;
        }
 
-       snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "mdio-octeon-0", p->port);
-
-       p->phydev = phy_connect(netdev, phy_id, octeon_mgmt_adjust_link, 0,
-                               PHY_INTERFACE_MODE_MII);
+       p->phydev = of_phy_connect(netdev, p->phy_np,
+                                  octeon_mgmt_adjust_link, 0,
+                                  PHY_INTERFACE_MODE_MII);
 
        if (IS_ERR(p->phydev)) {
                p->phydev = NULL;
@@ -737,14 +776,14 @@ static int octeon_mgmt_open(struct net_device *netdev)
 
        octeon_mgmt_reset_hw(p);
 
-       mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port));
+       mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL);
 
        /* Bring it out of reset if needed. */
        if (mix_ctl.s.reset) {
                mix_ctl.s.reset = 0;
-               cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64);
+               cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
                do {
-                       mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port));
+                       mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL);
                } while (mix_ctl.s.reset);
        }
 
@@ -755,17 +794,17 @@ static int octeon_mgmt_open(struct net_device *netdev)
        oring1.u64 = 0;
        oring1.s.obase = p->tx_ring_handle >> 3;
        oring1.s.osize = OCTEON_MGMT_TX_RING_SIZE;
-       cvmx_write_csr(CVMX_MIXX_ORING1(port), oring1.u64);
+       cvmx_write_csr(p->mix + MIX_ORING1, oring1.u64);
 
        iring1.u64 = 0;
        iring1.s.ibase = p->rx_ring_handle >> 3;
        iring1.s.isize = OCTEON_MGMT_RX_RING_SIZE;
-       cvmx_write_csr(CVMX_MIXX_IRING1(port), iring1.u64);
+       cvmx_write_csr(p->mix + MIX_IRING1, iring1.u64);
 
        /* Disable packet I/O. */
-       prtx_cfg.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
+       prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
        prtx_cfg.s.en = 0;
-       cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), prtx_cfg.u64);
+       cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64);
 
        memcpy(sa.sa_data, netdev->dev_addr, ETH_ALEN);
        octeon_mgmt_set_mac_address(netdev, &sa);
@@ -782,7 +821,7 @@ static int octeon_mgmt_open(struct net_device *netdev)
        mix_ctl.s.nbtarb = 0;       /* Arbitration mode */
        /* MII CB-request FIFO programmable high watermark */
        mix_ctl.s.mrq_hwm = 1;
-       cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64);
+       cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
 
        if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X)
            || OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X)) {
@@ -809,16 +848,16 @@ static int octeon_mgmt_open(struct net_device *netdev)
 
        /* Clear statistics. */
        /* Clear on read. */
-       cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_CTL(port), 1);
-       cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_DRP(port), 0);
-       cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_BAD(port), 0);
+       cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_CTL, 1);
+       cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_PKTS_DRP, 0);
+       cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_PKTS_BAD, 0);
 
-       cvmx_write_csr(CVMX_AGL_GMX_TXX_STATS_CTL(port), 1);
-       cvmx_write_csr(CVMX_AGL_GMX_TXX_STAT0(port), 0);
-       cvmx_write_csr(CVMX_AGL_GMX_TXX_STAT1(port), 0);
+       cvmx_write_csr(p->agl + AGL_GMX_TX_STATS_CTL, 1);
+       cvmx_write_csr(p->agl + AGL_GMX_TX_STAT0, 0);
+       cvmx_write_csr(p->agl + AGL_GMX_TX_STAT1, 0);
 
        /* Clear any pending interrupts */
-       cvmx_write_csr(CVMX_MIXX_ISR(port), cvmx_read_csr(CVMX_MIXX_ISR(port)));
+       cvmx_write_csr(p->mix + MIX_ISR, cvmx_read_csr(p->mix + MIX_ISR));
 
        if (request_irq(p->irq, octeon_mgmt_interrupt, 0, netdev->name,
                        netdev)) {
@@ -829,18 +868,18 @@ static int octeon_mgmt_open(struct net_device *netdev)
        /* Interrupt every single RX packet */
        mix_irhwm.u64 = 0;
        mix_irhwm.s.irhwm = 0;
-       cvmx_write_csr(CVMX_MIXX_IRHWM(port), mix_irhwm.u64);
+       cvmx_write_csr(p->mix + MIX_IRHWM, mix_irhwm.u64);
 
        /* Interrupt when we have 1 or more packets to clean.  */
        mix_orhwm.u64 = 0;
        mix_orhwm.s.orhwm = 1;
-       cvmx_write_csr(CVMX_MIXX_ORHWM(port), mix_orhwm.u64);
+       cvmx_write_csr(p->mix + MIX_ORHWM, mix_orhwm.u64);
 
        /* Enable receive and transmit interrupts */
        mix_intena.u64 = 0;
        mix_intena.s.ithena = 1;
        mix_intena.s.othena = 1;
-       cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64);
+       cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64);
 
 
        /* Enable packet I/O. */
@@ -871,7 +910,7 @@ static int octeon_mgmt_open(struct net_device *netdev)
         * frame.  GMX checks that the PREAMBLE is sent correctly.
         */
        rxx_frm_ctl.s.pre_chk = 1;
-       cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_CTL(port), rxx_frm_ctl.u64);
+       cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64);
 
        /* Enable the AGL block */
        agl_gmx_inf_mode.u64 = 0;
@@ -879,13 +918,13 @@ static int octeon_mgmt_open(struct net_device *netdev)
        cvmx_write_csr(CVMX_AGL_GMX_INF_MODE, agl_gmx_inf_mode.u64);
 
        /* Configure the port duplex and enables */
-       prtx_cfg.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
+       prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
        prtx_cfg.s.tx_en = 1;
        prtx_cfg.s.rx_en = 1;
        prtx_cfg.s.en = 1;
        p->last_duplex = 1;
        prtx_cfg.s.duplex = p->last_duplex;
-       cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), prtx_cfg.u64);
+       cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64);
 
        p->last_link = 0;
        netif_carrier_off(netdev);
@@ -949,7 +988,6 @@ static int octeon_mgmt_stop(struct net_device *netdev)
 static int octeon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev)
 {
        struct octeon_mgmt *p = netdev_priv(netdev);
-       int port = p->port;
        union mgmt_port_ring_entry re;
        unsigned long flags;
        int rv = NETDEV_TX_BUSY;
@@ -993,7 +1031,7 @@ static int octeon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev)
        netdev->stats.tx_bytes += skb->len;
 
        /* Ring the bell.  */
-       cvmx_write_csr(CVMX_MIXX_ORING2(port), 1);
+       cvmx_write_csr(p->mix + MIX_ORING2, 1);
 
        rv = NETDEV_TX_OK;
 out:
@@ -1071,10 +1109,14 @@ static const struct net_device_ops octeon_mgmt_ops = {
 
 static int __devinit octeon_mgmt_probe(struct platform_device *pdev)
 {
-       struct resource *res_irq;
        struct net_device *netdev;
        struct octeon_mgmt *p;
-       int i;
+       const __be32 *data;
+       const u8 *mac;
+       struct resource *res_mix;
+       struct resource *res_agl;
+       int len;
+       int result;
 
        netdev = alloc_etherdev(sizeof(struct octeon_mgmt));
        if (netdev == NULL)
@@ -1088,14 +1130,63 @@ static int __devinit octeon_mgmt_probe(struct platform_device *pdev)
        p->netdev = netdev;
        p->dev = &pdev->dev;
 
-       p->port = pdev->id;
+       data = of_get_property(pdev->dev.of_node, "cell-index", &len);
+       if (data && len == sizeof(*data)) {
+               p->port = be32_to_cpup(data);
+       } else {
+               dev_err(&pdev->dev, "no 'cell-index' property\n");
+               result = -ENXIO;
+               goto err;
+       }
+
        snprintf(netdev->name, IFNAMSIZ, "mgmt%d", p->port);
 
-       res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (!res_irq)
+       result = platform_get_irq(pdev, 0);
+       if (result < 0)
+               goto err;
+
+       p->irq = result;
+
+       res_mix = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res_mix == NULL) {
+               dev_err(&pdev->dev, "no 'reg' resource\n");
+               result = -ENXIO;
+               goto err;
+       }
+
+       res_agl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (res_agl == NULL) {
+               dev_err(&pdev->dev, "no 'reg' resource\n");
+               result = -ENXIO;
+               goto err;
+       }
+
+       p->mix_phys = res_mix->start;
+       p->mix_size = resource_size(res_mix);
+       p->agl_phys = res_agl->start;
+       p->agl_size = resource_size(res_agl);
+
+
+       if (!devm_request_mem_region(&pdev->dev, p->mix_phys, p->mix_size,
+                                    res_mix->name)) {
+               dev_err(&pdev->dev, "request_mem_region (%s) failed\n",
+                       res_mix->name);
+               result = -ENXIO;
+               goto err;
+       }
+
+       if (!devm_request_mem_region(&pdev->dev, p->agl_phys, p->agl_size,
+                                    res_agl->name)) {
+               result = -ENXIO;
+               dev_err(&pdev->dev, "request_mem_region (%s) failed\n",
+                       res_agl->name);
                goto err;
+       }
+
+
+       p->mix = (u64)devm_ioremap(&pdev->dev, p->mix_phys, p->mix_size);
+       p->agl = (u64)devm_ioremap(&pdev->dev, p->agl_phys, p->agl_size);
 
-       p->irq = res_irq->start;
        spin_lock_init(&p->lock);
 
        skb_queue_head_init(&p->tx_list);
@@ -1108,24 +1199,26 @@ static int __devinit octeon_mgmt_probe(struct platform_device *pdev)
        netdev->netdev_ops = &octeon_mgmt_ops;
        netdev->ethtool_ops = &octeon_mgmt_ethtool_ops;
 
-       /* The mgmt ports get the first N MACs.  */
-       for (i = 0; i < 6; i++)
-               netdev->dev_addr[i] = octeon_bootinfo->mac_addr_base[i];
-       netdev->dev_addr[5] += p->port;
+       mac = of_get_mac_address(pdev->dev.of_node);
+
+       if (mac)
+               memcpy(netdev->dev_addr, mac, 6);
 
-       if (p->port >= octeon_bootinfo->mac_addr_count)
-               dev_err(&pdev->dev,
-                       "Error %s: Using MAC outside of the assigned range: %pM\n",
-                       netdev->name, netdev->dev_addr);
+       p->phy_np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
 
-       if (register_netdev(netdev))
+       pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64);
+       pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+
+       result = register_netdev(netdev);
+       if (result)
                goto err;
 
        dev_info(&pdev->dev, "Version " DRV_VERSION "\n");
        return 0;
+
 err:
        free_netdev(netdev);
-       return -ENOENT;
+       return result;
 }
 
 static int __devexit octeon_mgmt_remove(struct platform_device *pdev)
@@ -1137,10 +1230,19 @@ static int __devexit octeon_mgmt_remove(struct platform_device *pdev)
        return 0;
 }
 
+static struct of_device_id octeon_mgmt_match[] = {
+       {
+               .compatible = "cavium,octeon-5750-mix",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, octeon_mgmt_match);
+
 static struct platform_driver octeon_mgmt_driver = {
        .driver = {
                .name           = "octeon_mgmt",
                .owner          = THIS_MODULE,
+               .of_match_table = octeon_mgmt_match,
        },
        .probe          = octeon_mgmt_probe,
        .remove         = __devexit_p(octeon_mgmt_remove),
index 70554a1b2b02dd37440db75ba06ec9eed8c84fe9..65a8d49106a4c63c6a7faf04c8e3334421ce6472 100644 (file)
@@ -1503,6 +1503,11 @@ static int efx_probe_all(struct efx_nic *efx)
                goto fail2;
        }
 
+       BUILD_BUG_ON(EFX_DEFAULT_DMAQ_SIZE < EFX_RXQ_MIN_ENT);
+       if (WARN_ON(EFX_DEFAULT_DMAQ_SIZE < EFX_TXQ_MIN_ENT(efx))) {
+               rc = -EINVAL;
+               goto fail3;
+       }
        efx->rxq_entries = efx->txq_entries = EFX_DEFAULT_DMAQ_SIZE;
 
        rc = efx_probe_filters(efx);
@@ -2070,6 +2075,7 @@ static int efx_register_netdev(struct efx_nic *efx)
        net_dev->irq = efx->pci_dev->irq;
        net_dev->netdev_ops = &efx_netdev_ops;
        SET_ETHTOOL_OPS(net_dev, &efx_ethtool_ops);
+       net_dev->gso_max_segs = EFX_TSO_MAX_SEGS;
 
        rtnl_lock();
 
index be8f9158a7149388b5e4edfeb41ee2c848319a28..70755c97251aaab4e9cafe969b0a8b95821a1cbb 100644 (file)
@@ -30,6 +30,7 @@ extern netdev_tx_t
 efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb);
 extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index);
 extern int efx_setup_tc(struct net_device *net_dev, u8 num_tc);
+extern unsigned int efx_tx_max_skb_descs(struct efx_nic *efx);
 
 /* RX */
 extern int efx_probe_rx_queue(struct efx_rx_queue *rx_queue);
@@ -52,10 +53,15 @@ extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue);
 #define EFX_MAX_EVQ_SIZE 16384UL
 #define EFX_MIN_EVQ_SIZE 512UL
 
-/* The smallest [rt]xq_entries that the driver supports. Callers of
- * efx_wake_queue() assume that they can subsequently send at least one
- * skb. Falcon/A1 may require up to three descriptors per skb_frag. */
-#define EFX_MIN_RING_SIZE (roundup_pow_of_two(2 * 3 * MAX_SKB_FRAGS))
+/* Maximum number of TCP segments we support for soft-TSO */
+#define EFX_TSO_MAX_SEGS       100
+
+/* The smallest [rt]xq_entries that the driver supports.  RX minimum
+ * is a bit arbitrary.  For TX, we must have space for at least 2
+ * TSO skbs.
+ */
+#define EFX_RXQ_MIN_ENT                128U
+#define EFX_TXQ_MIN_ENT(efx)   (2 * efx_tx_max_skb_descs(efx))
 
 /* Filters */
 extern int efx_probe_filters(struct efx_nic *efx);
index 10536f93b5611d15a22a56b3ca280e0f107ea828..8cba2df82b18b9f2dfa1ff82eee3b22ef9eeb0c9 100644 (file)
@@ -680,21 +680,27 @@ static int efx_ethtool_set_ringparam(struct net_device *net_dev,
                                     struct ethtool_ringparam *ring)
 {
        struct efx_nic *efx = netdev_priv(net_dev);
+       u32 txq_entries;
 
        if (ring->rx_mini_pending || ring->rx_jumbo_pending ||
            ring->rx_pending > EFX_MAX_DMAQ_SIZE ||
            ring->tx_pending > EFX_MAX_DMAQ_SIZE)
                return -EINVAL;
 
-       if (ring->rx_pending < EFX_MIN_RING_SIZE ||
-           ring->tx_pending < EFX_MIN_RING_SIZE) {
+       if (ring->rx_pending < EFX_RXQ_MIN_ENT) {
                netif_err(efx, drv, efx->net_dev,
-                         "TX and RX queues cannot be smaller than %ld\n",
-                         EFX_MIN_RING_SIZE);
+                         "RX queues cannot be smaller than %u\n",
+                         EFX_RXQ_MIN_ENT);
                return -EINVAL;
        }
 
-       return efx_realloc_channels(efx, ring->rx_pending, ring->tx_pending);
+       txq_entries = max(ring->tx_pending, EFX_TXQ_MIN_ENT(efx));
+       if (txq_entries != ring->tx_pending)
+               netif_warn(efx, drv, efx->net_dev,
+                          "increasing TX queue size to minimum of %u\n",
+                          txq_entries);
+
+       return efx_realloc_channels(efx, ring->rx_pending, txq_entries);
 }
 
 static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
index 9b225a7769f765a1f1bf3ef024ae3b73a18a66c2..18713436b44345a110ed263d54c5411d1e93c6fa 100644 (file)
@@ -119,6 +119,25 @@ efx_max_tx_len(struct efx_nic *efx, dma_addr_t dma_addr)
        return len;
 }
 
+unsigned int efx_tx_max_skb_descs(struct efx_nic *efx)
+{
+       /* Header and payload descriptor for each output segment, plus
+        * one for every input fragment boundary within a segment
+        */
+       unsigned int max_descs = EFX_TSO_MAX_SEGS * 2 + MAX_SKB_FRAGS;
+
+       /* Possibly one more per segment for the alignment workaround */
+       if (EFX_WORKAROUND_5391(efx))
+               max_descs += EFX_TSO_MAX_SEGS;
+
+       /* Possibly more for PCIe page boundaries within input fragments */
+       if (PAGE_SIZE > EFX_PAGE_SIZE)
+               max_descs += max_t(unsigned int, MAX_SKB_FRAGS,
+                                  DIV_ROUND_UP(GSO_MAX_SIZE, EFX_PAGE_SIZE));
+
+       return max_descs;
+}
+
 /*
  * Add a socket buffer to a TX queue
  *
index ab4c376cb2763bf8c98ceac42d3e290b16ec341b..f2d3665430ad455b82552d975d9f91066a5fc913 100644 (file)
@@ -82,9 +82,7 @@ struct stmmac_priv {
        struct stmmac_counters mmc;
        struct dma_features dma_cap;
        int hw_cap_support;
-#ifdef CONFIG_HAVE_CLK
        struct clk *stmmac_clk;
-#endif
        int clk_csr;
        int synopsys_id;
        struct timer_list eee_ctrl_timer;
@@ -113,46 +111,6 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
 void stmmac_disable_eee_mode(struct stmmac_priv *priv);
 bool stmmac_eee_init(struct stmmac_priv *priv);
 
-#ifdef CONFIG_HAVE_CLK
-static inline int stmmac_clk_enable(struct stmmac_priv *priv)
-{
-       if (!IS_ERR(priv->stmmac_clk))
-               return clk_prepare_enable(priv->stmmac_clk);
-
-       return 0;
-}
-
-static inline void stmmac_clk_disable(struct stmmac_priv *priv)
-{
-       if (IS_ERR(priv->stmmac_clk))
-               return;
-
-       clk_disable_unprepare(priv->stmmac_clk);
-}
-static inline int stmmac_clk_get(struct stmmac_priv *priv)
-{
-       priv->stmmac_clk = clk_get(priv->device, NULL);
-
-       if (IS_ERR(priv->stmmac_clk))
-               return PTR_ERR(priv->stmmac_clk);
-
-       return 0;
-}
-#else
-static inline int stmmac_clk_enable(struct stmmac_priv *priv)
-{
-       return 0;
-}
-static inline void stmmac_clk_disable(struct stmmac_priv *priv)
-{
-}
-static inline int stmmac_clk_get(struct stmmac_priv *priv)
-{
-       return 0;
-}
-#endif /* CONFIG_HAVE_CLK */
-
-
 #ifdef CONFIG_STMMAC_PLATFORM
 extern struct platform_driver stmmac_pltfr_driver;
 static inline int stmmac_register_platform(void)
index f6b04c1a3672ea2c556f249c75f36f72c3721c68..fd8882f9602afc43b6fe6ecd79a14defb5ac8dbe 100644 (file)
@@ -28,6 +28,7 @@
        https://bugzilla.stlinux.com/
 *******************************************************************************/
 
+#include <linux/clk.h>
 #include <linux/kernel.h>
 #include <linux/interrupt.h>
 #include <linux/ip.h>
@@ -173,12 +174,8 @@ static void stmmac_verify_args(void)
 
 static void stmmac_clk_csr_set(struct stmmac_priv *priv)
 {
-#ifdef CONFIG_HAVE_CLK
        u32 clk_rate;
 
-       if (IS_ERR(priv->stmmac_clk))
-               return;
-
        clk_rate = clk_get_rate(priv->stmmac_clk);
 
        /* Platform provided default clk_csr would be assumed valid
@@ -200,7 +197,6 @@ static void stmmac_clk_csr_set(struct stmmac_priv *priv)
           * we can not estimate the proper divider as it is not known
           * the frequency of clk_csr_i. So we do not change the default
           * divider. */
-#endif
 }
 
 #if defined(STMMAC_XMIT_DEBUG) || defined(STMMAC_RX_DEBUG)
@@ -1070,7 +1066,7 @@ static int stmmac_open(struct net_device *dev)
        } else
                priv->tm->enable = 1;
 #endif
-       stmmac_clk_enable(priv);
+       clk_enable(priv->stmmac_clk);
 
        stmmac_check_ether_addr(priv);
 
@@ -1192,7 +1188,7 @@ open_error:
        if (priv->phydev)
                phy_disconnect(priv->phydev);
 
-       stmmac_clk_disable(priv);
+       clk_disable(priv->stmmac_clk);
 
        return ret;
 }
@@ -1250,7 +1246,7 @@ static int stmmac_release(struct net_device *dev)
 #ifdef CONFIG_STMMAC_DEBUG_FS
        stmmac_exit_fs();
 #endif
-       stmmac_clk_disable(priv);
+       clk_disable(priv->stmmac_clk);
 
        return 0;
 }
@@ -2078,11 +2074,14 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
        ret = register_netdev(ndev);
        if (ret) {
                pr_err("%s: ERROR %i registering the device\n", __func__, ret);
-               goto error;
+               goto error_netdev_register;
        }
 
-       if (stmmac_clk_get(priv))
+       priv->stmmac_clk = clk_get(priv->device, NULL);
+       if (IS_ERR(priv->stmmac_clk)) {
                pr_warning("%s: warning: cannot get CSR clock\n", __func__);
+               goto error_clk_get;
+       }
 
        /* If a specific clk_csr value is passed from the platform
         * this means that the CSR Clock Range selection cannot be
@@ -2100,15 +2099,17 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
        if (ret < 0) {
                pr_debug("%s: MDIO bus (id: %d) registration failed",
                         __func__, priv->plat->bus_id);
-               goto error;
+               goto error_mdio_register;
        }
 
        return priv;
 
-error:
-       netif_napi_del(&priv->napi);
-
+error_mdio_register:
+       clk_put(priv->stmmac_clk);
+error_clk_get:
        unregister_netdev(ndev);
+error_netdev_register:
+       netif_napi_del(&priv->napi);
        free_netdev(ndev);
 
        return NULL;
@@ -2177,7 +2178,7 @@ int stmmac_suspend(struct net_device *ndev)
        else {
                stmmac_set_mac(priv->ioaddr, false);
                /* Disable clock in case of PWM is off */
-               stmmac_clk_disable(priv);
+               clk_disable(priv->stmmac_clk);
        }
        spin_unlock_irqrestore(&priv->lock, flags);
        return 0;
@@ -2202,7 +2203,7 @@ int stmmac_resume(struct net_device *ndev)
                priv->hw->mac->pmt(priv->ioaddr, 0);
        else
                /* enable the clk prevously disabled */
-               stmmac_clk_enable(priv);
+               clk_enable(priv->stmmac_clk);
 
        netif_device_attach(ndev);
 
index 1b173a6145d642fb3c2fe26c58c8d8b995ce50b8..b26cbda5efa9b5264dd4e2bb885d35ea7e26b692 100644 (file)
@@ -32,7 +32,7 @@ config TI_DAVINCI_EMAC
 
 config TI_DAVINCI_MDIO
        tristate "TI DaVinci MDIO Support"
-       depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
+       depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 || SOC_AM33XX )
        select PHYLIB
        ---help---
          This driver supports TI's DaVinci MDIO module.
@@ -42,7 +42,7 @@ config TI_DAVINCI_MDIO
 
 config TI_DAVINCI_CPDMA
        tristate "TI DaVinci CPDMA Support"
-       depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
+       depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 || SOC_AM33XX )
        ---help---
          This driver supports TI's DaVinci CPDMA dma engine.
 
index 1e5d85b06e71b1dea196be8e0f4db1a928c62c4b..0cbc0e59252c52ebdc00daeec1ca7e97cd539492 100644 (file)
@@ -28,6 +28,9 @@
 #include <linux/workqueue.h>
 #include <linux/delay.h>
 #include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/of_device.h>
 
 #include <linux/platform_data/cpsw.h>
 
@@ -709,6 +712,158 @@ static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv)
        slave->sliver   = regs + data->sliver_reg_ofs;
 }
 
+static int cpsw_probe_dt(struct cpsw_platform_data *data,
+                        struct platform_device *pdev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       struct device_node *slave_node;
+       int i = 0, ret;
+       u32 prop;
+
+       if (!node)
+               return -EINVAL;
+
+       if (of_property_read_u32(node, "slaves", &prop)) {
+               pr_err("Missing slaves property in the DT.\n");
+               return -EINVAL;
+       }
+       data->slaves = prop;
+
+       data->slave_data = kzalloc(sizeof(struct cpsw_slave_data) *
+                                  data->slaves, GFP_KERNEL);
+       if (!data->slave_data) {
+               pr_err("Could not allocate slave memory.\n");
+               return -EINVAL;
+       }
+
+       data->no_bd_ram = of_property_read_bool(node, "no_bd_ram");
+
+       if (of_property_read_u32(node, "cpdma_channels", &prop)) {
+               pr_err("Missing cpdma_channels property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       data->channels = prop;
+
+       if (of_property_read_u32(node, "host_port_no", &prop)) {
+               pr_err("Missing host_port_no property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       data->host_port_num = prop;
+
+       if (of_property_read_u32(node, "cpdma_reg_ofs", &prop)) {
+               pr_err("Missing cpdma_reg_ofs property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       data->cpdma_reg_ofs = prop;
+
+       if (of_property_read_u32(node, "cpdma_sram_ofs", &prop)) {
+               pr_err("Missing cpdma_sram_ofs property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       data->cpdma_sram_ofs = prop;
+
+       if (of_property_read_u32(node, "ale_reg_ofs", &prop)) {
+               pr_err("Missing ale_reg_ofs property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       data->ale_reg_ofs = prop;
+
+       if (of_property_read_u32(node, "ale_entries", &prop)) {
+               pr_err("Missing ale_entries property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       data->ale_entries = prop;
+
+       if (of_property_read_u32(node, "host_port_reg_ofs", &prop)) {
+               pr_err("Missing host_port_reg_ofs property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       data->host_port_reg_ofs = prop;
+
+       if (of_property_read_u32(node, "hw_stats_reg_ofs", &prop)) {
+               pr_err("Missing hw_stats_reg_ofs property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       data->hw_stats_reg_ofs = prop;
+
+       if (of_property_read_u32(node, "bd_ram_ofs", &prop)) {
+               pr_err("Missing bd_ram_ofs property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       data->bd_ram_ofs = prop;
+
+       if (of_property_read_u32(node, "bd_ram_size", &prop)) {
+               pr_err("Missing bd_ram_size property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       data->bd_ram_size = prop;
+
+       if (of_property_read_u32(node, "rx_descs", &prop)) {
+               pr_err("Missing rx_descs property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       data->rx_descs = prop;
+
+       if (of_property_read_u32(node, "mac_control", &prop)) {
+               pr_err("Missing mac_control property in the DT.\n");
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       data->mac_control = prop;
+
+       for_each_child_of_node(node, slave_node) {
+               struct cpsw_slave_data *slave_data = data->slave_data + i;
+               const char *phy_id = NULL;
+               const void *mac_addr = NULL;
+
+               if (of_property_read_string(slave_node, "phy_id", &phy_id)) {
+                       pr_err("Missing slave[%d] phy_id property\n", i);
+                       ret = -EINVAL;
+                       goto error_ret;
+               }
+               slave_data->phy_id = phy_id;
+
+               if (of_property_read_u32(slave_node, "slave_reg_ofs", &prop)) {
+                       pr_err("Missing slave[%d] slave_reg_ofs property\n", i);
+                       ret = -EINVAL;
+                       goto error_ret;
+               }
+               slave_data->slave_reg_ofs = prop;
+
+               if (of_property_read_u32(slave_node, "sliver_reg_ofs",
+                                        &prop)) {
+                       pr_err("Missing slave[%d] sliver_reg_ofs property\n",
+                               i);
+                       ret = -EINVAL;
+                       goto error_ret;
+               }
+               slave_data->sliver_reg_ofs = prop;
+
+               mac_addr = of_get_mac_address(slave_node);
+               if (mac_addr)
+                       memcpy(slave_data->mac_addr, mac_addr, ETH_ALEN);
+
+               i++;
+       }
+
+       return 0;
+
+error_ret:
+       kfree(data->slave_data);
+       return ret;
+}
+
 static int __devinit cpsw_probe(struct platform_device *pdev)
 {
        struct cpsw_platform_data       *data = pdev->dev.platform_data;
@@ -720,11 +875,6 @@ static int __devinit cpsw_probe(struct platform_device *pdev)
        struct resource                 *res;
        int ret = 0, i, k = 0;
 
-       if (!data) {
-               pr_err("platform data missing\n");
-               return -ENODEV;
-       }
-
        ndev = alloc_etherdev(sizeof(struct cpsw_priv));
        if (!ndev) {
                pr_err("error allocating net_device\n");
@@ -734,13 +884,19 @@ static int __devinit cpsw_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, ndev);
        priv = netdev_priv(ndev);
        spin_lock_init(&priv->lock);
-       priv->data = *data;
        priv->pdev = pdev;
        priv->ndev = ndev;
        priv->dev  = &ndev->dev;
        priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
        priv->rx_packet_max = max(rx_packet_max, 128);
 
+       if (cpsw_probe_dt(&priv->data, pdev)) {
+               pr_err("cpsw: platform data missing\n");
+               ret = -ENODEV;
+               goto clean_ndev_ret;
+       }
+       data = &priv->data;
+
        if (is_valid_ether_addr(data->slave_data[0].mac_addr)) {
                memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN);
                pr_info("Detected MACID = %pM", priv->mac_addr);
@@ -996,11 +1152,17 @@ static const struct dev_pm_ops cpsw_pm_ops = {
        .resume         = cpsw_resume,
 };
 
+static const struct of_device_id cpsw_of_mtable[] = {
+       { .compatible = "ti,cpsw", },
+       { /* sentinel */ },
+};
+
 static struct platform_driver cpsw_driver = {
        .driver = {
                .name    = "cpsw",
                .owner   = THIS_MODULE,
                .pm      = &cpsw_pm_ops,
+               .of_match_table = of_match_ptr(cpsw_of_mtable),
        },
        .probe = cpsw_probe,
        .remove = __devexit_p(cpsw_remove),
index cd7ee204e94a10abaa88b3288dcd2cace5ceca3e..573f3be5f42173d80b5ccca91fc06301127713b5 100644 (file)
@@ -36,6 +36,8 @@
 #include <linux/io.h>
 #include <linux/pm_runtime.h>
 #include <linux/davinci_emac.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 
 /*
  * This timeout definition is a worst-case ultra defensive measure against
@@ -289,6 +291,25 @@ static int davinci_mdio_write(struct mii_bus *bus, int phy_id,
        return 0;
 }
 
+static int davinci_mdio_probe_dt(struct mdio_platform_data *data,
+                        struct platform_device *pdev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       u32 prop;
+
+       if (!node)
+               return -EINVAL;
+
+       if (of_property_read_u32(node, "bus_freq", &prop)) {
+               pr_err("Missing bus_freq property in the DT.\n");
+               return -EINVAL;
+       }
+       data->bus_freq = prop;
+
+       return 0;
+}
+
+
 static int __devinit davinci_mdio_probe(struct platform_device *pdev)
 {
        struct mdio_platform_data *pdata = pdev->dev.platform_data;
@@ -304,8 +325,6 @@ static int __devinit davinci_mdio_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-       data->pdata = pdata ? (*pdata) : default_pdata;
-
        data->bus = mdiobus_alloc();
        if (!data->bus) {
                dev_err(dev, "failed to alloc mii bus\n");
@@ -313,14 +332,22 @@ static int __devinit davinci_mdio_probe(struct platform_device *pdev)
                goto bail_out;
        }
 
+       if (dev->of_node) {
+               if (davinci_mdio_probe_dt(&data->pdata, pdev))
+                       data->pdata = default_pdata;
+               snprintf(data->bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
+       } else {
+               data->pdata = pdata ? (*pdata) : default_pdata;
+               snprintf(data->bus->id, MII_BUS_ID_SIZE, "%s-%x",
+                        pdev->name, pdev->id);
+       }
+
        data->bus->name         = dev_name(dev);
        data->bus->read         = davinci_mdio_read,
        data->bus->write        = davinci_mdio_write,
        data->bus->reset        = davinci_mdio_reset,
        data->bus->parent       = dev;
        data->bus->priv         = data;
-       snprintf(data->bus->id, MII_BUS_ID_SIZE, "%s-%x",
-               pdev->name, pdev->id);
 
        pm_runtime_enable(&pdev->dev);
        pm_runtime_get_sync(&pdev->dev);
@@ -454,11 +481,17 @@ static const struct dev_pm_ops davinci_mdio_pm_ops = {
        .resume         = davinci_mdio_resume,
 };
 
+static const struct of_device_id davinci_mdio_of_mtable[] = {
+       { .compatible = "ti,davinci_mdio", },
+       { /* sentinel */ },
+};
+
 static struct platform_driver davinci_mdio_driver = {
        .driver = {
                .name    = "davinci_mdio",
                .owner   = THIS_MODULE,
                .pm      = &davinci_mdio_pm_ops,
+               .of_match_table = of_match_ptr(davinci_mdio_of_mtable),
        },
        .probe = davinci_mdio_probe,
        .remove = __devexit_p(davinci_mdio_remove),
index 6cee2917eb0276369e6933ebf933c386167fa9a4..4a1a5f58fa73ffd7899429bcaf28062e29f83dc3 100644 (file)
@@ -383,13 +383,6 @@ int netvsc_device_remove(struct hv_device *device)
        unsigned long flags;
 
        net_device = hv_get_drvdata(device);
-       spin_lock_irqsave(&device->channel->inbound_lock, flags);
-       net_device->destroy = true;
-       spin_unlock_irqrestore(&device->channel->inbound_lock, flags);
-
-       /* Wait for all send completions */
-       wait_event(net_device->wait_drain,
-                  atomic_read(&net_device->num_outstanding_sends) == 0);
 
        netvsc_disconnect_vsp(net_device);
 
index 8c5a1c43c81d257c09a67385e01da93f3c24465f..e91111a656f715b77398e1208208f4b0ab5644a8 100644 (file)
@@ -400,7 +400,7 @@ static void netvsc_send_garp(struct work_struct *w)
        ndev_ctx = container_of(w, struct net_device_context, dwork.work);
        net_device = hv_get_drvdata(ndev_ctx->device_ctx);
        net = net_device->ndev;
-       netif_notify_peers(net);
+       netdev_notify_peers(net);
 }
 
 
index e5d6146937fa0aade2476662ce87084b7337187c..06f8601f32fcb8eb82ce6429e08438f451e05e7d 100644 (file)
@@ -46,8 +46,14 @@ struct rndis_request {
        /* Simplify allocation by having a netvsc packet inline */
        struct hv_netvsc_packet pkt;
        struct hv_page_buffer buf;
-       /* FIXME: We assumed a fixed size request here. */
+
        struct rndis_message request_msg;
+       /*
+        * The buffer for the extended info after the RNDIS message. It's
+        * referenced based on the data offset in the RNDIS message. Its size
+        * is enough for current needs, and should be sufficient for the near
+        * future.
+        */
        u8 ext[100];
 };
 
@@ -718,6 +724,9 @@ static void rndis_filter_halt_device(struct rndis_device *dev)
 {
        struct rndis_request *request;
        struct rndis_halt_request *halt;
+       struct netvsc_device *nvdev = dev->net_dev;
+       struct hv_device *hdev = nvdev->dev;
+       ulong flags;
 
        /* Attempt to do a rndis device halt */
        request = get_rndis_request(dev, RNDIS_MSG_HALT,
@@ -735,6 +744,14 @@ static void rndis_filter_halt_device(struct rndis_device *dev)
        dev->state = RNDIS_DEV_UNINITIALIZED;
 
 cleanup:
+       spin_lock_irqsave(&hdev->channel->inbound_lock, flags);
+       nvdev->destroy = true;
+       spin_unlock_irqrestore(&hdev->channel->inbound_lock, flags);
+
+       /* Wait for all send completions */
+       wait_event(nvdev->wait_drain,
+               atomic_read(&nvdev->num_outstanding_sends) == 0);
+
        if (request)
                put_rndis_request(dev, request);
        return;
index e2a06fd996d51409ed711fb360105d7f423f27a8..4a075babe1937b9d9b4cc98b18360b181acfdb56 100644 (file)
@@ -197,6 +197,7 @@ static __net_init int loopback_net_init(struct net *net)
        if (err)
                goto out_free_netdev;
 
+       BUG_ON(dev->ifindex != LOOPBACK_IFINDEX);
        net->loopback_dev = dev;
        return 0;
 
index 826d961f39f74ff7bbd2a9c839f36f3c5b2ed851..d4015aa663e6422f7be98e6d8f7f07f1561592de 100644 (file)
@@ -3,14 +3,17 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 2009 Cavium Networks
+ * Copyright (C) 2009,2011 Cavium, Inc.
  */
 
-#include <linux/gfp.h>
-#include <linux/init.h>
-#include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/gfp.h>
 #include <linux/phy.h>
+#include <linux/io.h>
 
 #include <asm/octeon/octeon.h>
 #include <asm/octeon/cvmx-smix-defs.h>
 #define DRV_VERSION "1.0"
 #define DRV_DESCRIPTION "Cavium Networks Octeon SMI/MDIO driver"
 
+#define SMI_CMD                0x0
+#define SMI_WR_DAT     0x8
+#define SMI_RD_DAT     0x10
+#define SMI_CLK                0x18
+#define SMI_EN         0x20
+
 struct octeon_mdiobus {
        struct mii_bus *mii_bus;
-       int unit;
+       u64 register_base;
+       resource_size_t mdio_phys;
+       resource_size_t regsize;
        int phy_irq[PHY_MAX_ADDR];
 };
 
@@ -35,15 +46,15 @@ static int octeon_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)
        smi_cmd.s.phy_op = 1; /* MDIO_CLAUSE_22_READ */
        smi_cmd.s.phy_adr = phy_id;
        smi_cmd.s.reg_adr = regnum;
-       cvmx_write_csr(CVMX_SMIX_CMD(p->unit), smi_cmd.u64);
+       cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64);
 
        do {
                /*
                 * Wait 1000 clocks so we don't saturate the RSL bus
                 * doing reads.
                 */
-               cvmx_wait(1000);
-               smi_rd.u64 = cvmx_read_csr(CVMX_SMIX_RD_DAT(p->unit));
+               __delay(1000);
+               smi_rd.u64 = cvmx_read_csr(p->register_base + SMI_RD_DAT);
        } while (smi_rd.s.pending && --timeout);
 
        if (smi_rd.s.val)
@@ -62,21 +73,21 @@ static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id,
 
        smi_wr.u64 = 0;
        smi_wr.s.dat = val;
-       cvmx_write_csr(CVMX_SMIX_WR_DAT(p->unit), smi_wr.u64);
+       cvmx_write_csr(p->register_base + SMI_WR_DAT, smi_wr.u64);
 
        smi_cmd.u64 = 0;
        smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_22_WRITE */
        smi_cmd.s.phy_adr = phy_id;
        smi_cmd.s.reg_adr = regnum;
-       cvmx_write_csr(CVMX_SMIX_CMD(p->unit), smi_cmd.u64);
+       cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64);
 
        do {
                /*
                 * Wait 1000 clocks so we don't saturate the RSL bus
                 * doing reads.
                 */
-               cvmx_wait(1000);
-               smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(p->unit));
+               __delay(1000);
+               smi_wr.u64 = cvmx_read_csr(p->register_base + SMI_WR_DAT);
        } while (smi_wr.s.pending && --timeout);
 
        if (timeout <= 0)
@@ -88,38 +99,44 @@ static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id,
 static int __devinit octeon_mdiobus_probe(struct platform_device *pdev)
 {
        struct octeon_mdiobus *bus;
+       struct resource *res_mem;
        union cvmx_smix_en smi_en;
-       int i;
        int err = -ENOENT;
 
        bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
        if (!bus)
                return -ENOMEM;
 
-       /* The platform_device id is our unit number.  */
-       bus->unit = pdev->id;
+       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       if (res_mem == NULL) {
+               dev_err(&pdev->dev, "found no memory resource\n");
+               err = -ENXIO;
+               goto fail;
+       }
+       bus->mdio_phys = res_mem->start;
+       bus->regsize = resource_size(res_mem);
+       if (!devm_request_mem_region(&pdev->dev, bus->mdio_phys, bus->regsize,
+                                    res_mem->name)) {
+               dev_err(&pdev->dev, "request_mem_region failed\n");
+               goto fail;
+       }
+       bus->register_base =
+               (u64)devm_ioremap(&pdev->dev, bus->mdio_phys, bus->regsize);
 
        bus->mii_bus = mdiobus_alloc();
 
        if (!bus->mii_bus)
-               goto err;
+               goto fail;
 
        smi_en.u64 = 0;
        smi_en.s.en = 1;
-       cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64);
-
-       /*
-        * Standard Octeon evaluation boards don't support phy
-        * interrupts, we need to poll.
-        */
-       for (i = 0; i < PHY_MAX_ADDR; i++)
-               bus->phy_irq[i] = PHY_POLL;
+       cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64);
 
        bus->mii_bus->priv = bus;
        bus->mii_bus->irq = bus->phy_irq;
        bus->mii_bus->name = "mdio-octeon";
-       snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
-               bus->mii_bus->name, bus->unit);
+       snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%llx", bus->register_base);
        bus->mii_bus->parent = &pdev->dev;
 
        bus->mii_bus->read = octeon_mdiobus_read;
@@ -127,20 +144,18 @@ static int __devinit octeon_mdiobus_probe(struct platform_device *pdev)
 
        dev_set_drvdata(&pdev->dev, bus);
 
-       err = mdiobus_register(bus->mii_bus);
+       err = of_mdiobus_register(bus->mii_bus, pdev->dev.of_node);
        if (err)
-               goto err_register;
+               goto fail_register;
 
        dev_info(&pdev->dev, "Version " DRV_VERSION "\n");
 
        return 0;
-err_register:
+fail_register:
        mdiobus_free(bus->mii_bus);
-
-err:
-       devm_kfree(&pdev->dev, bus);
+fail:
        smi_en.u64 = 0;
-       cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64);
+       cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64);
        return err;
 }
 
@@ -154,14 +169,23 @@ static int __devexit octeon_mdiobus_remove(struct platform_device *pdev)
        mdiobus_unregister(bus->mii_bus);
        mdiobus_free(bus->mii_bus);
        smi_en.u64 = 0;
-       cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64);
+       cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64);
        return 0;
 }
 
+static struct of_device_id octeon_mdiobus_match[] = {
+       {
+               .compatible = "cavium,octeon-3860-mdio",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, octeon_mdiobus_match);
+
 static struct platform_driver octeon_mdiobus_driver = {
        .driver = {
                .name           = "mdio-octeon",
                .owner          = THIS_MODULE,
+               .of_match_table = octeon_mdiobus_match,
        },
        .probe          = octeon_mdiobus_probe,
        .remove         = __devexit_p(octeon_mdiobus_remove),
index 5c0557222f20b26a10fa450146098b699ff4e98e..eb3f5cefeba3c6ddcbd53a44cb63a759d9b803ef 100644 (file)
@@ -93,6 +93,18 @@ struct ppp_file {
 #define PF_TO_PPP(pf)          PF_TO_X(pf, struct ppp)
 #define PF_TO_CHANNEL(pf)      PF_TO_X(pf, struct channel)
 
+/*
+ * Data structure to hold primary network stats for which
+ * we want to use 64 bit storage.  Other network stats
+ * are stored in dev->stats of the ppp strucute.
+ */
+struct ppp_link_stats {
+       u64 rx_packets;
+       u64 tx_packets;
+       u64 rx_bytes;
+       u64 tx_bytes;
+};
+
 /*
  * Data structure describing one ppp unit.
  * A ppp unit corresponds to a ppp network interface device
@@ -136,6 +148,7 @@ struct ppp {
        unsigned pass_len, active_len;
 #endif /* CONFIG_PPP_FILTER */
        struct net      *ppp_net;       /* the net we belong to */
+       struct ppp_link_stats stats64;  /* 64 bit network stats */
 };
 
 /*
@@ -1021,9 +1034,34 @@ ppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        return err;
 }
 
+struct rtnl_link_stats64*
+ppp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64)
+{
+       struct ppp *ppp = netdev_priv(dev);
+
+       ppp_recv_lock(ppp);
+       stats64->rx_packets = ppp->stats64.rx_packets;
+       stats64->rx_bytes   = ppp->stats64.rx_bytes;
+       ppp_recv_unlock(ppp);
+
+       ppp_xmit_lock(ppp);
+       stats64->tx_packets = ppp->stats64.tx_packets;
+       stats64->tx_bytes   = ppp->stats64.tx_bytes;
+       ppp_xmit_unlock(ppp);
+
+       stats64->rx_errors        = dev->stats.rx_errors;
+       stats64->tx_errors        = dev->stats.tx_errors;
+       stats64->rx_dropped       = dev->stats.rx_dropped;
+       stats64->tx_dropped       = dev->stats.tx_dropped;
+       stats64->rx_length_errors = dev->stats.rx_length_errors;
+
+       return stats64;
+}
+
 static const struct net_device_ops ppp_netdev_ops = {
-       .ndo_start_xmit = ppp_start_xmit,
-       .ndo_do_ioctl   = ppp_net_ioctl,
+       .ndo_start_xmit  = ppp_start_xmit,
+       .ndo_do_ioctl    = ppp_net_ioctl,
+       .ndo_get_stats64 = ppp_get_stats64,
 };
 
 static void ppp_setup(struct net_device *dev)
@@ -1157,8 +1195,8 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
 #endif /* CONFIG_PPP_FILTER */
        }
 
-       ++ppp->dev->stats.tx_packets;
-       ppp->dev->stats.tx_bytes += skb->len - 2;
+       ++ppp->stats64.tx_packets;
+       ppp->stats64.tx_bytes += skb->len - 2;
 
        switch (proto) {
        case PPP_IP:
@@ -1745,8 +1783,8 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
                break;
        }
 
-       ++ppp->dev->stats.rx_packets;
-       ppp->dev->stats.rx_bytes += skb->len - 2;
+       ++ppp->stats64.rx_packets;
+       ppp->stats64.rx_bytes += skb->len - 2;
 
        npi = proto_to_npindex(proto);
        if (npi < 0) {
@@ -2570,12 +2608,12 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st)
        struct slcompress *vj = ppp->vj;
 
        memset(st, 0, sizeof(*st));
-       st->p.ppp_ipackets = ppp->dev->stats.rx_packets;
+       st->p.ppp_ipackets = ppp->stats64.rx_packets;
        st->p.ppp_ierrors = ppp->dev->stats.rx_errors;
-       st->p.ppp_ibytes = ppp->dev->stats.rx_bytes;
-       st->p.ppp_opackets = ppp->dev->stats.tx_packets;
+       st->p.ppp_ibytes = ppp->stats64.rx_bytes;
+       st->p.ppp_opackets = ppp->stats64.tx_packets;
        st->p.ppp_oerrors = ppp->dev->stats.tx_errors;
-       st->p.ppp_obytes = ppp->dev->stats.tx_bytes;
+       st->p.ppp_obytes = ppp->stats64.tx_bytes;
        if (!vj)
                return;
        st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed;
index 87707ab3943084821e1b7e5c5b9a201d51a944ba..ba10c469b02bdd7d9d6d11f9122c5f7251cc1c48 100644 (file)
@@ -658,6 +658,122 @@ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
 }
 
 
+/*************************************
+ * Multiqueue Tx port select override
+ *************************************/
+
+static int team_queue_override_init(struct team *team)
+{
+       struct list_head *listarr;
+       unsigned int queue_cnt = team->dev->num_tx_queues - 1;
+       unsigned int i;
+
+       if (!queue_cnt)
+               return 0;
+       listarr = kmalloc(sizeof(struct list_head) * queue_cnt, GFP_KERNEL);
+       if (!listarr)
+               return -ENOMEM;
+       team->qom_lists = listarr;
+       for (i = 0; i < queue_cnt; i++)
+               INIT_LIST_HEAD(listarr++);
+       return 0;
+}
+
+static void team_queue_override_fini(struct team *team)
+{
+       kfree(team->qom_lists);
+}
+
+static struct list_head *__team_get_qom_list(struct team *team, u16 queue_id)
+{
+       return &team->qom_lists[queue_id - 1];
+}
+
+/*
+ * note: already called with rcu_read_lock
+ */
+static bool team_queue_override_transmit(struct team *team, struct sk_buff *skb)
+{
+       struct list_head *qom_list;
+       struct team_port *port;
+
+       if (!team->queue_override_enabled || !skb->queue_mapping)
+               return false;
+       qom_list = __team_get_qom_list(team, skb->queue_mapping);
+       list_for_each_entry_rcu(port, qom_list, qom_list) {
+               if (!team_dev_queue_xmit(team, port, skb))
+                       return true;
+       }
+       return false;
+}
+
+static void __team_queue_override_port_del(struct team *team,
+                                          struct team_port *port)
+{
+       list_del_rcu(&port->qom_list);
+       synchronize_rcu();
+       INIT_LIST_HEAD(&port->qom_list);
+}
+
+static bool team_queue_override_port_has_gt_prio_than(struct team_port *port,
+                                                     struct team_port *cur)
+{
+       if (port->priority < cur->priority)
+               return true;
+       if (port->priority > cur->priority)
+               return false;
+       if (port->index < cur->index)
+               return true;
+       return false;
+}
+
+static void __team_queue_override_port_add(struct team *team,
+                                          struct team_port *port)
+{
+       struct team_port *cur;
+       struct list_head *qom_list;
+       struct list_head *node;
+
+       if (!port->queue_id || !team_port_enabled(port))
+               return;
+
+       qom_list = __team_get_qom_list(team, port->queue_id);
+       node = qom_list;
+       list_for_each_entry(cur, qom_list, qom_list) {
+               if (team_queue_override_port_has_gt_prio_than(port, cur))
+                       break;
+               node = &cur->qom_list;
+       }
+       list_add_tail_rcu(&port->qom_list, node);
+}
+
+static void __team_queue_override_enabled_check(struct team *team)
+{
+       struct team_port *port;
+       bool enabled = false;
+
+       list_for_each_entry(port, &team->port_list, list) {
+               if (!list_empty(&port->qom_list)) {
+                       enabled = true;
+                       break;
+               }
+       }
+       if (enabled == team->queue_override_enabled)
+               return;
+       netdev_dbg(team->dev, "%s queue override\n",
+                  enabled ? "Enabling" : "Disabling");
+       team->queue_override_enabled = enabled;
+}
+
+static void team_queue_override_port_refresh(struct team *team,
+                                            struct team_port *port)
+{
+       __team_queue_override_port_del(team, port);
+       __team_queue_override_port_add(team, port);
+       __team_queue_override_enabled_check(team);
+}
+
+
 /****************
  * Port handling
  ****************/
@@ -688,6 +804,7 @@ static void team_port_enable(struct team *team,
        hlist_add_head_rcu(&port->hlist,
                           team_port_index_hash(team, port->index));
        team_adjust_ops(team);
+       team_queue_override_port_refresh(team, port);
        if (team->ops.port_enabled)
                team->ops.port_enabled(team, port);
 }
@@ -716,6 +833,7 @@ static void team_port_disable(struct team *team,
        hlist_del_rcu(&port->hlist);
        __reconstruct_port_hlist(team, port->index);
        port->index = -1;
+       team_queue_override_port_refresh(team, port);
        __team_adjust_ops(team, team->en_port_count - 1);
        /*
         * Wait until readers see adjusted ops. This ensures that
@@ -881,6 +999,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
 
        port->dev = port_dev;
        port->team = team;
+       INIT_LIST_HEAD(&port->qom_list);
 
        port->orig.mtu = port_dev->mtu;
        err = dev_set_mtu(port_dev, dev->mtu);
@@ -1092,6 +1211,49 @@ static int team_user_linkup_en_option_set(struct team *team,
        return 0;
 }
 
+static int team_priority_option_get(struct team *team,
+                                   struct team_gsetter_ctx *ctx)
+{
+       struct team_port *port = ctx->info->port;
+
+       ctx->data.s32_val = port->priority;
+       return 0;
+}
+
+static int team_priority_option_set(struct team *team,
+                                   struct team_gsetter_ctx *ctx)
+{
+       struct team_port *port = ctx->info->port;
+
+       port->priority = ctx->data.s32_val;
+       team_queue_override_port_refresh(team, port);
+       return 0;
+}
+
+static int team_queue_id_option_get(struct team *team,
+                                   struct team_gsetter_ctx *ctx)
+{
+       struct team_port *port = ctx->info->port;
+
+       ctx->data.u32_val = port->queue_id;
+       return 0;
+}
+
+static int team_queue_id_option_set(struct team *team,
+                                   struct team_gsetter_ctx *ctx)
+{
+       struct team_port *port = ctx->info->port;
+
+       if (port->queue_id == ctx->data.u32_val)
+               return 0;
+       if (ctx->data.u32_val >= team->dev->real_num_tx_queues)
+               return -EINVAL;
+       port->queue_id = ctx->data.u32_val;
+       team_queue_override_port_refresh(team, port);
+       return 0;
+}
+
+
 static const struct team_option team_options[] = {
        {
                .name = "mode",
@@ -1120,6 +1282,20 @@ static const struct team_option team_options[] = {
                .getter = team_user_linkup_en_option_get,
                .setter = team_user_linkup_en_option_set,
        },
+       {
+               .name = "priority",
+               .type = TEAM_OPTION_TYPE_S32,
+               .per_port = true,
+               .getter = team_priority_option_get,
+               .setter = team_priority_option_set,
+       },
+       {
+               .name = "queue_id",
+               .type = TEAM_OPTION_TYPE_U32,
+               .per_port = true,
+               .getter = team_queue_id_option_get,
+               .setter = team_queue_id_option_set,
+       },
 };
 
 static struct lock_class_key team_netdev_xmit_lock_key;
@@ -1155,6 +1331,9 @@ static int team_init(struct net_device *dev)
        for (i = 0; i < TEAM_PORT_HASHENTRIES; i++)
                INIT_HLIST_HEAD(&team->en_port_hlist[i]);
        INIT_LIST_HEAD(&team->port_list);
+       err = team_queue_override_init(team);
+       if (err)
+               goto err_team_queue_override_init;
 
        team_adjust_ops(team);
 
@@ -1170,6 +1349,8 @@ static int team_init(struct net_device *dev)
        return 0;
 
 err_options_register:
+       team_queue_override_fini(team);
+err_team_queue_override_init:
        free_percpu(team->pcpu_stats);
 
        return err;
@@ -1187,6 +1368,7 @@ static void team_uninit(struct net_device *dev)
 
        __team_change_mode(team, NULL); /* cleanup */
        __team_options_unregister(team, team_options, ARRAY_SIZE(team_options));
+       team_queue_override_fini(team);
        mutex_unlock(&team->lock);
 }
 
@@ -1216,10 +1398,12 @@ static int team_close(struct net_device *dev)
 static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct team *team = netdev_priv(dev);
-       bool tx_success = false;
+       bool tx_success;
        unsigned int len = skb->len;
 
-       tx_success = team->ops.transmit(team, skb);
+       tx_success = team_queue_override_transmit(team, skb);
+       if (!tx_success)
+               tx_success = team->ops.transmit(team, skb);
        if (tx_success) {
                struct team_pcpu_stats *pcpu_stats;
 
@@ -1787,6 +1971,12 @@ static int team_nl_fill_one_option_get(struct sk_buff *skb, struct team *team,
                    nla_put_flag(skb, TEAM_ATTR_OPTION_DATA))
                        goto nest_cancel;
                break;
+       case TEAM_OPTION_TYPE_S32:
+               if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_S32))
+                       goto nest_cancel;
+               if (nla_put_s32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.s32_val))
+                       goto nest_cancel;
+               break;
        default:
                BUG();
        }
@@ -1975,6 +2165,9 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
                case NLA_FLAG:
                        opt_type = TEAM_OPTION_TYPE_BOOL;
                        break;
+               case NLA_S32:
+                       opt_type = TEAM_OPTION_TYPE_S32;
+                       break;
                default:
                        goto team_put;
                }
@@ -2031,6 +2224,9 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
                        case TEAM_OPTION_TYPE_BOOL:
                                ctx.data.bool_val = attr_data ? true : false;
                                break;
+                       case TEAM_OPTION_TYPE_S32:
+                               ctx.data.s32_val = nla_get_s32(attr_data);
+                               break;
                        default:
                                BUG();
                        }
index 187c144c5e5bc23f3fd9b6286374349f7883862f..64610048ce87c178770059b163395a285cb69e77 100644 (file)
@@ -130,7 +130,7 @@ static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags)
        struct page *page;
        int err;
 
-       page = alloc_page(gfp_flags);
+       page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL);
        if (!page)
                return -ENOMEM;
 
index f4ce5957df326bb94131031fabdb1daf6dcabf42..4cd582a4f625d55d5edd8c2cd3952d5571074921 100644 (file)
@@ -1225,6 +1225,26 @@ static const struct usb_device_id cdc_devs[] = {
          .driver_info = (unsigned long) &wwan_info,
        },
 
+       /* Dell branded MBM devices like DW5550 */
+       { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+               | USB_DEVICE_ID_MATCH_VENDOR,
+         .idVendor = 0x413c,
+         .bInterfaceClass = USB_CLASS_COMM,
+         .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,
+         .bInterfaceProtocol = USB_CDC_PROTO_NONE,
+         .driver_info = (unsigned long) &wwan_info,
+       },
+
+       /* Toshiba branded MBM devices */
+       { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+               | USB_DEVICE_ID_MATCH_VENDOR,
+         .idVendor = 0x0930,
+         .bInterfaceClass = USB_CLASS_COMM,
+         .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,
+         .bInterfaceProtocol = USB_CDC_PROTO_NONE,
+         .driver_info = (unsigned long) &wwan_info,
+       },
+
        /* Generic CDC-NCM devices */
        { USB_INTERFACE_INFO(USB_CLASS_COMM,
                USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
index 5852361032c459735e915db5443ac08d560f2c7c..e522ff70444cd0d7e8f1ce34132055e438ded7ce 100644 (file)
@@ -348,6 +348,9 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
        if (tbp[IFLA_ADDRESS] == NULL)
                eth_hw_addr_random(peer);
 
+       if (ifmp && (dev->ifindex != 0))
+               peer->ifindex = ifmp->ifi_index;
+
        err = register_netdevice(peer);
        put_net(net);
        net = NULL;
index 83d2b0c34c5e63045eaeb63c3e40b14ab68e7875..81a64c58e8adb09c9d868a0eb4ccc3bb782d9182 100644 (file)
@@ -993,7 +993,7 @@ static void virtnet_config_changed_work(struct work_struct *work)
                goto done;
 
        if (v & VIRTIO_NET_S_ANNOUNCE) {
-               netif_notify_peers(vi->dev);
+               netdev_notify_peers(vi->dev);
                virtnet_ack_link_announce(vi);
        }
 
index cfa91ab7acf8b1fd7c4b29fab82cb812f6423d5d..60b6a9daff7e21cde68fb6eda800e4ca065c62aa 100644 (file)
@@ -730,6 +730,7 @@ int ath9k_hw_init(struct ath_hw *ah)
        case AR9300_DEVID_QCA955X:
        case AR9300_DEVID_AR9580:
        case AR9300_DEVID_AR9462:
+       case AR9485_DEVID_AR1111:
                break;
        default:
                if (common->bus_ops->ath_bus_type == ATH_USB)
index dd0c146d81dccd77d1d735db888884be6227c782..ce7332c64efb2c5b417cd40fec2428522186142a 100644 (file)
@@ -49,6 +49,7 @@
 #define AR9300_DEVID_AR9462    0x0034
 #define AR9300_DEVID_AR9330    0x0035
 #define AR9300_DEVID_QCA955X   0x0038
+#define AR9485_DEVID_AR1111    0x0037
 
 #define AR5416_AR9100_DEVID    0x000b
 
index 87b89d55e637758febf06629599ebe9e60478b85..d455de9162ec672cdb2f1174bb27ca742a16bf74 100644 (file)
@@ -37,6 +37,7 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
        { PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E  AR9485 */
        { PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E  AR9580 */
        { PCI_VDEVICE(ATHEROS, 0x0034) }, /* PCI-E  AR9462 */
+       { PCI_VDEVICE(ATHEROS, 0x0037) }, /* PCI-E  AR1111/AR9485 */
        { 0 }
 };
 
index 5efa778847043248f465a93ba382b1564cd08b1d..d97a95b1addbb92f58912c5f27de693636055afc 100644 (file)
@@ -2719,32 +2719,37 @@ static int b43_gpio_init(struct b43_wldev *dev)
        if (dev->dev->chip_id == 0x4301) {
                mask |= 0x0060;
                set |= 0x0060;
+       } else if (dev->dev->chip_id == 0x5354) {
+               /* Don't allow overtaking buttons GPIOs */
+               set &= 0x2; /* 0x2 is LED GPIO on BCM5354 */
        }
-       if (dev->dev->chip_id == 0x5354)
-               set &= 0xff02;
+
        if (0 /* FIXME: conditional unknown */ ) {
                b43_write16(dev, B43_MMIO_GPIO_MASK,
                            b43_read16(dev, B43_MMIO_GPIO_MASK)
                            | 0x0100);
-               mask |= 0x0180;
-               set |= 0x0180;
+               /* BT Coexistance Input */
+               mask |= 0x0080;
+               set |= 0x0080;
+               /* BT Coexistance Out */
+               mask |= 0x0100;
+               set |= 0x0100;
        }
        if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) {
+               /* PA is controlled by gpio 9, let ucode handle it */
                b43_write16(dev, B43_MMIO_GPIO_MASK,
                            b43_read16(dev, B43_MMIO_GPIO_MASK)
                            | 0x0200);
                mask |= 0x0200;
                set |= 0x0200;
        }
-       if (dev->dev->core_rev >= 2)
-               mask |= 0x0010; /* FIXME: This is redundant. */
 
        switch (dev->dev->bus_type) {
 #ifdef CONFIG_B43_BCMA
        case B43_BUS_BCMA:
                bcma_cc_write32(&dev->dev->bdev->bus->drv_cc, BCMA_CC_GPIOCTL,
                                (bcma_cc_read32(&dev->dev->bdev->bus->drv_cc,
-                                       BCMA_CC_GPIOCTL) & mask) | set);
+                                       BCMA_CC_GPIOCTL) & ~mask) | set);
                break;
 #endif
 #ifdef CONFIG_B43_SSB
@@ -2753,7 +2758,7 @@ static int b43_gpio_init(struct b43_wldev *dev)
                if (gpiodev)
                        ssb_write32(gpiodev, B43_GPIO_CONTROL,
                                    (ssb_read32(gpiodev, B43_GPIO_CONTROL)
-                                   & mask) | set);
+                                   & ~mask) | set);
                break;
 #endif
        }
index 57bf1d7ee80fc76591621f51ffd4fbdf56a656b7..9ab24528f9b9c0e54d40854ae6e43e8ffd9f654c 100644 (file)
@@ -1188,7 +1188,7 @@ exit:
        kfree(buf);
        /* close file before return */
        if (fp)
-               filp_close(fp, current->files);
+               filp_close(fp, NULL);
        /* restore previous address limit */
        set_fs(old_fs);
 
index 9a4c63f927cb1817ee666cc070f14d1a810083c0..7ed7d7577024628d228a78f6e94433c810295bc2 100644 (file)
@@ -382,9 +382,7 @@ brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec,
 {
        struct brcms_c_info *wlc = wlc_cm->wlc;
        struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.channel;
-       const struct ieee80211_reg_rule *reg_rule;
        struct txpwr_limits txpwr;
-       int ret;
 
        brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr);
 
@@ -393,8 +391,7 @@ brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec,
        );
 
        /* set or restore gmode as required by regulatory */
-       ret = freq_reg_info(wlc->wiphy, ch->center_freq, 0, &reg_rule);
-       if (!ret && (reg_rule->flags & NL80211_RRF_NO_OFDM))
+       if (ch->flags & IEEE80211_CHAN_NO_OFDM)
                brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false);
        else
                brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false);
index 683a8652e1551b4653221f01bada0502a8a0215e..1c70defba6c308401cc685c73cdcd97c978caf4a 100644 (file)
@@ -123,7 +123,8 @@ static struct ieee80211_channel brcms_2ghz_chantable[] = {
                 IEEE80211_CHAN_NO_HT40PLUS),
        CHAN2GHZ(14, 2484,
                 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
-                IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
+                IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS |
+                IEEE80211_CHAN_NO_OFDM)
 };
 
 static struct ieee80211_channel brcms_5ghz_nphy_chantable[] = {
index eb5de800ed9038109e9a80bfc9cd15863d3491e4..1c10b542ab231a45e5134a58ceb858c8ab82d331 100644 (file)
@@ -1254,6 +1254,7 @@ static int lbs_associate(struct lbs_private *priv,
                        netif_tx_wake_all_queues(priv->dev);
        }
 
+       kfree(cmd);
 done:
        lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
        return ret;
index 76caebaa43977f722cbd707d666f1f014002792e..e970897f6ab52370a632a64a62cc10bb3c39a3c6 100644 (file)
@@ -1314,6 +1314,7 @@ static void if_sdio_remove(struct sdio_func *func)
                kfree(packet);
        }
 
+       kfree(card);
        lbs_deb_leave(LBS_DEB_SDIO);
 }
 
index 58048189bd24475f3ca18960822b6b18219e637c..fe1ea43c5149ef03e9ed0e2809695773fa2a576d 100644 (file)
@@ -571,7 +571,10 @@ static int lbs_thread(void *data)
                        netdev_info(dev, "Timeout submitting command 0x%04x\n",
                                    le16_to_cpu(cmdnode->cmdbuf->command));
                        lbs_complete_command(priv, cmdnode, -ETIMEDOUT);
-                       if (priv->reset_card)
+
+                       /* Reset card, but only when it isn't in the process
+                        * of being shutdown anyway. */
+                       if (!dev->dismantle && priv->reset_card)
                                priv->reset_card(priv);
                }
                priv->cmd_timed_out = 0;
index 88455b1b9fe05b299aff2cc667d3d90e7822855a..cb8c2aca54e4dfdac4a7e223a8070f4e4543399e 100644 (file)
@@ -221,6 +221,67 @@ static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev,
        mutex_unlock(&rt2x00dev->csr_mutex);
 }
 
+static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev)
+{
+       u32 reg;
+       int i, count;
+
+       rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, &reg);
+       if (rt2x00_get_field32(reg, WLAN_EN))
+               return 0;
+
+       rt2x00_set_field32(&reg, WLAN_GPIO_OUT_OE_BIT_ALL, 0xff);
+       rt2x00_set_field32(&reg, FRC_WL_ANT_SET, 1);
+       rt2x00_set_field32(&reg, WLAN_CLK_EN, 0);
+       rt2x00_set_field32(&reg, WLAN_EN, 1);
+       rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
+
+       udelay(REGISTER_BUSY_DELAY);
+
+       count = 0;
+       do {
+               /*
+                * Check PLL_LD & XTAL_RDY.
+                */
+               for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+                       rt2800_register_read(rt2x00dev, CMB_CTRL, &reg);
+                       if (rt2x00_get_field32(reg, PLL_LD) &&
+                           rt2x00_get_field32(reg, XTAL_RDY))
+                               break;
+                       udelay(REGISTER_BUSY_DELAY);
+               }
+
+               if (i >= REGISTER_BUSY_COUNT) {
+
+                       if (count >= 10)
+                               return -EIO;
+
+                       rt2800_register_write(rt2x00dev, 0x58, 0x018);
+                       udelay(REGISTER_BUSY_DELAY);
+                       rt2800_register_write(rt2x00dev, 0x58, 0x418);
+                       udelay(REGISTER_BUSY_DELAY);
+                       rt2800_register_write(rt2x00dev, 0x58, 0x618);
+                       udelay(REGISTER_BUSY_DELAY);
+                       count++;
+               } else {
+                       count = 0;
+               }
+
+               rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, &reg);
+               rt2x00_set_field32(&reg, PCIE_APP0_CLK_REQ, 0);
+               rt2x00_set_field32(&reg, WLAN_CLK_EN, 1);
+               rt2x00_set_field32(&reg, WLAN_RESET, 1);
+               rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
+               udelay(10);
+               rt2x00_set_field32(&reg, WLAN_RESET, 0);
+               rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
+               udelay(10);
+               rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, 0x7fffffff);
+       } while (count != 0);
+
+       return 0;
+}
+
 void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
                        const u8 command, const u8 token,
                        const u8 arg0, const u8 arg1)
@@ -400,6 +461,13 @@ int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
 {
        unsigned int i;
        u32 reg;
+       int retval;
+
+       if (rt2x00_rt(rt2x00dev, RT3290)) {
+               retval = rt2800_enable_wlan_rt3290(rt2x00dev);
+               if (retval)
+                       return -EBUSY;
+       }
 
        /*
         * If driver doesn't wake up firmware here,
index 235376e9cb04c0066032493f2d86b008d604c14c..98aa426a35649828e3c70c0f2bab0acf24e49381 100644 (file)
@@ -980,66 +980,6 @@ static int rt2800pci_validate_eeprom(struct rt2x00_dev *rt2x00dev)
        return rt2800_validate_eeprom(rt2x00dev);
 }
 
-static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev)
-{
-       u32 reg;
-       int i, count;
-
-       rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, &reg);
-       if (rt2x00_get_field32(reg, WLAN_EN))
-               return 0;
-
-       rt2x00_set_field32(&reg, WLAN_GPIO_OUT_OE_BIT_ALL, 0xff);
-       rt2x00_set_field32(&reg, FRC_WL_ANT_SET, 1);
-       rt2x00_set_field32(&reg, WLAN_CLK_EN, 0);
-       rt2x00_set_field32(&reg, WLAN_EN, 1);
-       rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
-
-       udelay(REGISTER_BUSY_DELAY);
-
-       count = 0;
-       do {
-               /*
-                * Check PLL_LD & XTAL_RDY.
-                */
-               for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
-                       rt2800_register_read(rt2x00dev, CMB_CTRL, &reg);
-                       if (rt2x00_get_field32(reg, PLL_LD) &&
-                           rt2x00_get_field32(reg, XTAL_RDY))
-                               break;
-                       udelay(REGISTER_BUSY_DELAY);
-               }
-
-               if (i >= REGISTER_BUSY_COUNT) {
-
-                       if (count >= 10)
-                               return -EIO;
-
-                       rt2800_register_write(rt2x00dev, 0x58, 0x018);
-                       udelay(REGISTER_BUSY_DELAY);
-                       rt2800_register_write(rt2x00dev, 0x58, 0x418);
-                       udelay(REGISTER_BUSY_DELAY);
-                       rt2800_register_write(rt2x00dev, 0x58, 0x618);
-                       udelay(REGISTER_BUSY_DELAY);
-                       count++;
-               } else {
-                       count = 0;
-               }
-
-               rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, &reg);
-               rt2x00_set_field32(&reg, PCIE_APP0_CLK_REQ, 0);
-               rt2x00_set_field32(&reg, WLAN_CLK_EN, 1);
-               rt2x00_set_field32(&reg, WLAN_RESET, 1);
-               rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
-               udelay(10);
-               rt2x00_set_field32(&reg, WLAN_RESET, 0);
-               rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
-               udelay(10);
-               rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, 0x7fffffff);
-       } while (count != 0);
-
-       return 0;
-}
 static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
 {
        int retval;
@@ -1062,17 +1002,6 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
        if (retval)
                return retval;
 
-       /*
-        * In probe phase call rt2800_enable_wlan_rt3290 to enable wlan
-        * clk for rt3290. That avoid the MCU fail in start phase.
-        */
-       if (rt2x00_rt(rt2x00dev, RT3290)) {
-               retval = rt2800_enable_wlan_rt3290(rt2x00dev);
-
-               if (retval)
-                       return retval;
-       }
-
        /*
         * This device has multiple filters for control frames
         * and has a separate filter for PS Poll frames.
index 30899901aef56b00e05c5f062c86cabcb54127d8..39afd37e62b366b9701240c83eba7b8b9381979d 100644 (file)
@@ -1731,7 +1731,7 @@ static void netback_changed(struct xenbus_device *dev,
                break;
 
        case XenbusStateConnected:
-               netif_notify_peers(netdev);
+               netdev_notify_peers(netdev);
                break;
 
        case XenbusStateClosing:
index c8f40c9c0428310c8dcf7db2110c73a119dc3150..3782e1cd3697020219d34b81aa039a8c767be486 100644 (file)
@@ -95,6 +95,7 @@ MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026");
 
 enum acer_wmi_event_ids {
        WMID_HOTKEY_EVENT = 0x1,
+       WMID_ACCEL_EVENT = 0x5,
 };
 
 static const struct key_entry acer_wmi_keymap[] = {
@@ -130,6 +131,7 @@ static const struct key_entry acer_wmi_keymap[] = {
 };
 
 static struct input_dev *acer_wmi_input_dev;
+static struct input_dev *acer_wmi_accel_dev;
 
 struct event_return_value {
        u8 function;
@@ -200,6 +202,7 @@ struct hotkey_function_type_aa {
 #define ACER_CAP_BLUETOOTH             (1<<2)
 #define ACER_CAP_BRIGHTNESS            (1<<3)
 #define ACER_CAP_THREEG                        (1<<4)
+#define ACER_CAP_ACCEL                 (1<<5)
 #define ACER_CAP_ANY                   (0xFFFFFFFF)
 
 /*
@@ -1398,6 +1401,60 @@ static void acer_backlight_exit(void)
        backlight_device_unregister(acer_backlight_device);
 }
 
+/*
+ * Accelerometer device
+ */
+static acpi_handle gsensor_handle;
+
+static int acer_gsensor_init(void)
+{
+       acpi_status status;
+       struct acpi_buffer output;
+       union acpi_object out_obj;
+
+       output.length = sizeof(out_obj);
+       output.pointer = &out_obj;
+       status = acpi_evaluate_object(gsensor_handle, "_INI", NULL, &output);
+       if (ACPI_FAILURE(status))
+               return -1;
+
+       return 0;
+}
+
+static int acer_gsensor_open(struct input_dev *input)
+{
+       return acer_gsensor_init();
+}
+
+static int acer_gsensor_event(void)
+{
+       acpi_status status;
+       struct acpi_buffer output;
+       union acpi_object out_obj[5];
+
+       if (!has_cap(ACER_CAP_ACCEL))
+               return -1;
+
+       output.length = sizeof(out_obj);
+       output.pointer = out_obj;
+
+       status = acpi_evaluate_object(gsensor_handle, "RDVL", NULL, &output);
+       if (ACPI_FAILURE(status))
+               return -1;
+
+       if (out_obj->package.count != 4)
+               return -1;
+
+       input_report_abs(acer_wmi_accel_dev, ABS_X,
+               (s16)out_obj->package.elements[0].integer.value);
+       input_report_abs(acer_wmi_accel_dev, ABS_Y,
+               (s16)out_obj->package.elements[1].integer.value);
+       input_report_abs(acer_wmi_accel_dev, ABS_Z,
+               (s16)out_obj->package.elements[2].integer.value);
+       input_sync(acer_wmi_accel_dev);
+       return 0;
+}
+
 /*
  * Rfkill devices
  */
@@ -1673,6 +1730,9 @@ static void acer_wmi_notify(u32 value, void *context)
                                                   1, true);
                }
                break;
+       case WMID_ACCEL_EVENT:
+               acer_gsensor_event();
+               break;
        default:
                pr_warn("Unknown function number - %d - %d\n",
                        return_value.function, return_value.key_num);
@@ -1758,6 +1818,73 @@ static int acer_wmi_enable_lm(void)
        return status;
 }
 
+static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level,
+                                               void *ctx, void **retval)
+{
+       *(acpi_handle *)retval = ah;
+       return AE_OK;
+}
+
+static int __init acer_wmi_get_handle(const char *name, const char *prop,
+                                       acpi_handle *ah)
+{
+       acpi_status status;
+       acpi_handle handle;
+
+       BUG_ON(!name || !ah);
+
+       handle = NULL;
+       status = acpi_get_devices(prop, acer_wmi_get_handle_cb,
+                                       (void *)name, &handle);
+
+       if (ACPI_SUCCESS(status)) {
+               *ah = handle;
+               return 0;
+       } else {
+               return -ENODEV;
+       }
+}
+
+static int __init acer_wmi_accel_setup(void)
+{
+       int err;
+
+       err = acer_wmi_get_handle("SENR", "BST0001", &gsensor_handle);
+       if (err)
+               return err;
+
+       interface->capability |= ACER_CAP_ACCEL;
+
+       acer_wmi_accel_dev = input_allocate_device();
+       if (!acer_wmi_accel_dev)
+               return -ENOMEM;
+
+       acer_wmi_accel_dev->open = acer_gsensor_open;
+
+       acer_wmi_accel_dev->name = "Acer BMA150 accelerometer";
+       acer_wmi_accel_dev->phys = "wmi/input1";
+       acer_wmi_accel_dev->id.bustype = BUS_HOST;
+       acer_wmi_accel_dev->evbit[0] = BIT_MASK(EV_ABS);
+       input_set_abs_params(acer_wmi_accel_dev, ABS_X, -16384, 16384, 0, 0);
+       input_set_abs_params(acer_wmi_accel_dev, ABS_Y, -16384, 16384, 0, 0);
+       input_set_abs_params(acer_wmi_accel_dev, ABS_Z, -16384, 16384, 0, 0);
+
+       err = input_register_device(acer_wmi_accel_dev);
+       if (err)
+               goto err_free_dev;
+
+       return 0;
+
+err_free_dev:
+       input_free_device(acer_wmi_accel_dev);
+       return err;
+}
+
+static void acer_wmi_accel_destroy(void)
+{
+       input_unregister_device(acer_wmi_accel_dev);
+}
+
 static int __init acer_wmi_input_setup(void)
 {
        acpi_status status;
@@ -1912,6 +2039,9 @@ static int acer_resume(struct device *dev)
        if (has_cap(ACER_CAP_BRIGHTNESS))
                set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
 
+       if (has_cap(ACER_CAP_ACCEL))
+               acer_gsensor_init();
+
        return 0;
 }
 
@@ -2060,14 +2190,16 @@ static int __init acer_wmi_init(void)
 
        set_quirks();
 
+       if (dmi_check_system(video_vendor_dmi_table))
+               acpi_video_dmi_promote_vendor();
        if (acpi_video_backlight_support()) {
-               if (dmi_check_system(video_vendor_dmi_table)) {
-                       acpi_video_unregister();
-               } else {
-                       interface->capability &= ~ACER_CAP_BRIGHTNESS;
-                       pr_info("Brightness must be controlled by "
-                               "acpi video driver\n");
-               }
+               interface->capability &= ~ACER_CAP_BRIGHTNESS;
+               pr_info("Brightness must be controlled by acpi video driver\n");
+       } else {
+#ifdef CONFIG_ACPI_VIDEO
+               pr_info("Disabling ACPI video driver\n");
+               acpi_video_unregister();
+#endif
        }
 
        if (wmi_has_guid(WMID_GUID3)) {
@@ -2090,6 +2222,8 @@ static int __init acer_wmi_init(void)
                        return err;
        }
 
+       acer_wmi_accel_setup();
+
        err = platform_driver_register(&acer_platform_driver);
        if (err) {
                pr_err("Unable to register platform driver\n");
@@ -2133,6 +2267,8 @@ error_device_alloc:
 error_platform_register:
        if (wmi_has_guid(ACERWMID_EVENT_GUID))
                acer_wmi_input_destroy();
+       if (has_cap(ACER_CAP_ACCEL))
+               acer_wmi_accel_destroy();
 
        return err;
 }
@@ -2142,6 +2278,9 @@ static void __exit acer_wmi_exit(void)
        if (wmi_has_guid(ACERWMID_EVENT_GUID))
                acer_wmi_input_destroy();
 
+       if (has_cap(ACER_CAP_ACCEL))
+               acer_wmi_accel_destroy();
+
        remove_sysfs(acer_platform_device);
        remove_debugfs();
        platform_device_unregister(acer_platform_device);
index 694a15a56230668c4eee12c7f5e7bc421a09cb0a..905fa01ac8df7abe86893964fd50c15a96b3b5d6 100644 (file)
@@ -193,7 +193,10 @@ static int __devinit gmux_probe(struct pnp_dev *pnp,
         * backlight control and supports more levels than other options.
         * Disable the other backlight choices.
         */
+       acpi_video_dmi_promote_vendor();
+#ifdef CONFIG_ACPI_VIDEO
        acpi_video_unregister();
+#endif
        apple_bl_unregister();
 
        return 0;
@@ -213,7 +216,10 @@ static void __devexit gmux_remove(struct pnp_dev *pnp)
        release_region(gmux_data->iostart, gmux_data->iolen);
        kfree(gmux_data);
 
+       acpi_video_dmi_demote_vendor();
+#ifdef CONFIG_ACPI_VIDEO
        acpi_video_register();
+#endif
        apple_bl_register();
 }
 
index 99a30b513137c50fe5224318c55d5d081fdf8973..6b0ebdeae916c727393aab1472e431bfd13e715a 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/input.h>
 #include <linux/input/sparse-keymap.h>
 #include <linux/fb.h>
+#include <linux/dmi.h>
 
 #include "asus-wmi.h"
 
@@ -48,18 +49,115 @@ MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
  *  1  | Hardware  | Software
  *  4  | Software  | Software
  */
-static uint wapf;
+static int wapf = -1;
 module_param(wapf, uint, 0444);
 MODULE_PARM_DESC(wapf, "WAPF value");
 
+static struct quirk_entry *quirks;
+
 static struct quirk_entry quirk_asus_unknown = {
+       .wapf = 0,
+};
+
+static struct quirk_entry quirk_asus_x401u = {
+       .wapf = 4,
+};
+
+static int dmi_matched(const struct dmi_system_id *dmi)
+{
+       quirks = dmi->driver_data;
+       return 1;
+}
+
+static struct dmi_system_id asus_quirks[] = {
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. X401U",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X401U"),
+               },
+               .driver_data = &quirk_asus_x401u,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. X401A1",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X401A1"),
+               },
+               .driver_data = &quirk_asus_x401u,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. X501U",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X501U"),
+               },
+               .driver_data = &quirk_asus_x401u,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. X501A1",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X501A1"),
+               },
+               .driver_data = &quirk_asus_x401u,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. X55A",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X55A"),
+               },
+               .driver_data = &quirk_asus_x401u,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. X55C",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X55C"),
+               },
+               .driver_data = &quirk_asus_x401u,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. X55U",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X55U"),
+               },
+               .driver_data = &quirk_asus_x401u,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. X55VD",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X55VD"),
+               },
+               .driver_data = &quirk_asus_x401u,
+       },
+       {},
 };
 
 static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
 {
-       driver->quirks = &quirk_asus_unknown;
-       driver->quirks->wapf = wapf;
+       quirks = &quirk_asus_unknown;
+       dmi_check_system(asus_quirks);
+
+       driver->quirks = quirks;
        driver->panel_power = FB_BLANK_UNBLANK;
+
+       /* overwrite the wapf setting if the wapf paramater is specified */
+       if (wapf != -1)
+               quirks->wapf = wapf;
+       else
+               wapf = quirks->wapf;
 }
 
 static const struct key_entry asus_nb_wmi_keymap[] = {
@@ -94,6 +192,10 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
        { KE_KEY, 0x8A, { KEY_PROG1 } },
        { KE_KEY, 0x95, { KEY_MEDIA } },
        { KE_KEY, 0x99, { KEY_PHONE } },
+       { KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
+       { KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
+       { KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
+       { KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */
        { KE_KEY, 0xb5, { KEY_CALC } },
        { KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
        { KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
index 77aadde5281c97ae423e35f5255c7e9b63d36b70..c7a36f6b058048eb092717eff06a9e413a6a2546 100644 (file)
@@ -47,6 +47,9 @@
 #include <linux/thermal.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
+#ifdef CONFIG_ACPI_VIDEO
+#include <acpi/video.h>
+#endif
 
 #include "asus-wmi.h"
 
@@ -136,6 +139,9 @@ MODULE_LICENSE("GPL");
 /* Power */
 #define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012
 
+/* Deep S3 / Resume on LID open */
+#define ASUS_WMI_DEVID_LID_RESUME      0x00120031
+
 /* DSTS masks */
 #define ASUS_WMI_DSTS_STATUS_BIT       0x00000001
 #define ASUS_WMI_DSTS_UNKNOWN_BIT      0x00000002
@@ -1365,6 +1371,7 @@ static ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf)
 ASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD);
 ASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA);
 ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER);
+ASUS_WMI_CREATE_DEVICE_ATTR(lid_resume, 0644, ASUS_WMI_DEVID_LID_RESUME);
 
 static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
                           const char *buf, size_t count)
@@ -1390,6 +1397,7 @@ static struct attribute *platform_attributes[] = {
        &dev_attr_camera.attr,
        &dev_attr_cardr.attr,
        &dev_attr_touchpad.attr,
+       &dev_attr_lid_resume.attr,
        NULL
 };
 
@@ -1408,6 +1416,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
                devid = ASUS_WMI_DEVID_CARDREADER;
        else if (attr == &dev_attr_touchpad.attr)
                devid = ASUS_WMI_DEVID_TOUCHPAD;
+       else if (attr == &dev_attr_lid_resume.attr)
+               devid = ASUS_WMI_DEVID_LID_RESUME;
 
        if (devid != -1)
                ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
@@ -1467,14 +1477,9 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
         */
        if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL))
                asus->dsts_id = ASUS_WMI_METHODID_DSTS;
-       else if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS2, 0, 0, NULL))
+       else
                asus->dsts_id = ASUS_WMI_METHODID_DSTS2;
 
-       if (!asus->dsts_id) {
-               pr_err("Can't find DSTS");
-               return -ENODEV;
-       }
-
        /* CWAP allow to define the behavior of the Fn+F2 key,
         * this method doesn't seems to be present on Eee PCs */
        if (asus->driver->quirks->wapf >= 0)
@@ -1681,7 +1686,13 @@ static int asus_wmi_add(struct platform_device *pdev)
        if (err)
                goto fail_rfkill;
 
+       if (asus->driver->quirks->wmi_backlight_power)
+               acpi_video_dmi_promote_vendor();
        if (!acpi_video_backlight_support()) {
+#ifdef CONFIG_ACPI_VIDEO
+               pr_info("Disabling ACPI video driver\n");
+               acpi_video_unregister();
+#endif
                err = asus_wmi_backlight_init(asus);
                if (err && err != -ENODEV)
                        goto fail_backlight;
index d43b66742004bae8cc0a819ca082a5e1065ef51f..9c1da8b81bea50f9007eac48f82da8f0bb29a73d 100644 (file)
@@ -39,6 +39,7 @@ struct quirk_entry {
        bool hotplug_wireless;
        bool scalar_panel_brightness;
        bool store_backlight_power;
+       bool wmi_backlight_power;
        int wapf;
 };
 
index e2230a2b2f8e3e82056ef7d197eb877999f337e9..2ca7dd1ab3e452e49eb0c25561274cea435acbcf 100644 (file)
@@ -31,15 +31,21 @@ MODULE_LICENSE("GPL");
 
 struct cmpc_accel {
        int sensitivity;
+       int g_select;
+       int inputdev_state;
 };
 
-#define CMPC_ACCEL_SENSITIVITY_DEFAULT         5
+#define CMPC_ACCEL_DEV_STATE_CLOSED    0
+#define CMPC_ACCEL_DEV_STATE_OPEN      1
 
+#define CMPC_ACCEL_SENSITIVITY_DEFAULT         5
+#define CMPC_ACCEL_G_SELECT_DEFAULT            0
 
 #define CMPC_ACCEL_HID         "ACCE0000"
+#define CMPC_ACCEL_HID_V4      "ACCE0001"
 #define CMPC_TABLET_HID                "TBLT0000"
 #define CMPC_IPML_HID  "IPML200"
-#define CMPC_KEYS_HID          "FnBT0000"
+#define CMPC_KEYS_HID          "FNBT0000"
 
 /*
  * Generic input device code.
@@ -76,7 +82,391 @@ static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
 }
 
 /*
- * Accelerometer code.
+ * Accelerometer code for Classmate V4
+ */
+static acpi_status cmpc_start_accel_v4(acpi_handle handle)
+{
+       union acpi_object param[4];
+       struct acpi_object_list input;
+       acpi_status status;
+
+       param[0].type = ACPI_TYPE_INTEGER;
+       param[0].integer.value = 0x3;
+       param[1].type = ACPI_TYPE_INTEGER;
+       param[1].integer.value = 0;
+       param[2].type = ACPI_TYPE_INTEGER;
+       param[2].integer.value = 0;
+       param[3].type = ACPI_TYPE_INTEGER;
+       param[3].integer.value = 0;
+       input.count = 4;
+       input.pointer = param;
+       status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
+       return status;
+}
+
+static acpi_status cmpc_stop_accel_v4(acpi_handle handle)
+{
+       union acpi_object param[4];
+       struct acpi_object_list input;
+       acpi_status status;
+
+       param[0].type = ACPI_TYPE_INTEGER;
+       param[0].integer.value = 0x4;
+       param[1].type = ACPI_TYPE_INTEGER;
+       param[1].integer.value = 0;
+       param[2].type = ACPI_TYPE_INTEGER;
+       param[2].integer.value = 0;
+       param[3].type = ACPI_TYPE_INTEGER;
+       param[3].integer.value = 0;
+       input.count = 4;
+       input.pointer = param;
+       status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
+       return status;
+}
+
+static acpi_status cmpc_accel_set_sensitivity_v4(acpi_handle handle, int val)
+{
+       union acpi_object param[4];
+       struct acpi_object_list input;
+
+       param[0].type = ACPI_TYPE_INTEGER;
+       param[0].integer.value = 0x02;
+       param[1].type = ACPI_TYPE_INTEGER;
+       param[1].integer.value = val;
+       param[2].type = ACPI_TYPE_INTEGER;
+       param[2].integer.value = 0;
+       param[3].type = ACPI_TYPE_INTEGER;
+       param[3].integer.value = 0;
+       input.count = 4;
+       input.pointer = param;
+       return acpi_evaluate_object(handle, "ACMD", &input, NULL);
+}
+
+static acpi_status cmpc_accel_set_g_select_v4(acpi_handle handle, int val)
+{
+       union acpi_object param[4];
+       struct acpi_object_list input;
+
+       param[0].type = ACPI_TYPE_INTEGER;
+       param[0].integer.value = 0x05;
+       param[1].type = ACPI_TYPE_INTEGER;
+       param[1].integer.value = val;
+       param[2].type = ACPI_TYPE_INTEGER;
+       param[2].integer.value = 0;
+       param[3].type = ACPI_TYPE_INTEGER;
+       param[3].integer.value = 0;
+       input.count = 4;
+       input.pointer = param;
+       return acpi_evaluate_object(handle, "ACMD", &input, NULL);
+}
+
+static acpi_status cmpc_get_accel_v4(acpi_handle handle,
+                                    int16_t *x,
+                                    int16_t *y,
+                                    int16_t *z)
+{
+       union acpi_object param[4];
+       struct acpi_object_list input;
+       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+       int16_t *locs;
+       acpi_status status;
+
+       param[0].type = ACPI_TYPE_INTEGER;
+       param[0].integer.value = 0x01;
+       param[1].type = ACPI_TYPE_INTEGER;
+       param[1].integer.value = 0;
+       param[2].type = ACPI_TYPE_INTEGER;
+       param[2].integer.value = 0;
+       param[3].type = ACPI_TYPE_INTEGER;
+       param[3].integer.value = 0;
+       input.count = 4;
+       input.pointer = param;
+       status = acpi_evaluate_object(handle, "ACMD", &input, &output);
+       if (ACPI_SUCCESS(status)) {
+               union acpi_object *obj;
+               obj = output.pointer;
+               locs = (int16_t *) obj->buffer.pointer;
+               *x = locs[0];
+               *y = locs[1];
+               *z = locs[2];
+               kfree(output.pointer);
+       }
+       return status;
+}
+
+static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event)
+{
+       if (event == 0x81) {
+               int16_t x, y, z;
+               acpi_status status;
+
+               status = cmpc_get_accel_v4(dev->handle, &x, &y, &z);
+               if (ACPI_SUCCESS(status)) {
+                       struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
+
+                       input_report_abs(inputdev, ABS_X, x);
+                       input_report_abs(inputdev, ABS_Y, y);
+                       input_report_abs(inputdev, ABS_Z, z);
+                       input_sync(inputdev);
+               }
+       }
+}
+
+static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev,
+                                             struct device_attribute *attr,
+                                             char *buf)
+{
+       struct acpi_device *acpi;
+       struct input_dev *inputdev;
+       struct cmpc_accel *accel;
+
+       acpi = to_acpi_device(dev);
+       inputdev = dev_get_drvdata(&acpi->dev);
+       accel = dev_get_drvdata(&inputdev->dev);
+
+       return sprintf(buf, "%d\n", accel->sensitivity);
+}
+
+static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev,
+                                              struct device_attribute *attr,
+                                              const char *buf, size_t count)
+{
+       struct acpi_device *acpi;
+       struct input_dev *inputdev;
+       struct cmpc_accel *accel;
+       unsigned long sensitivity;
+       int r;
+
+       acpi = to_acpi_device(dev);
+       inputdev = dev_get_drvdata(&acpi->dev);
+       accel = dev_get_drvdata(&inputdev->dev);
+
+       r = kstrtoul(buf, 0, &sensitivity);
+       if (r)
+               return r;
+
+       /* sensitivity must be between 1 and 127 */
+       if (sensitivity < 1 || sensitivity > 127)
+               return -EINVAL;
+
+       accel->sensitivity = sensitivity;
+       cmpc_accel_set_sensitivity_v4(acpi->handle, sensitivity);
+
+       return strnlen(buf, count);
+}
+
+static struct device_attribute cmpc_accel_sensitivity_attr_v4 = {
+       .attr = { .name = "sensitivity", .mode = 0660 },
+       .show = cmpc_accel_sensitivity_show_v4,
+       .store = cmpc_accel_sensitivity_store_v4
+};
+
+static ssize_t cmpc_accel_g_select_show_v4(struct device *dev,
+                                          struct device_attribute *attr,
+                                          char *buf)
+{
+       struct acpi_device *acpi;
+       struct input_dev *inputdev;
+       struct cmpc_accel *accel;
+
+       acpi = to_acpi_device(dev);
+       inputdev = dev_get_drvdata(&acpi->dev);
+       accel = dev_get_drvdata(&inputdev->dev);
+
+       return sprintf(buf, "%d\n", accel->g_select);
+}
+
+static ssize_t cmpc_accel_g_select_store_v4(struct device *dev,
+                                           struct device_attribute *attr,
+                                           const char *buf, size_t count)
+{
+       struct acpi_device *acpi;
+       struct input_dev *inputdev;
+       struct cmpc_accel *accel;
+       unsigned long g_select;
+       int r;
+
+       acpi = to_acpi_device(dev);
+       inputdev = dev_get_drvdata(&acpi->dev);
+       accel = dev_get_drvdata(&inputdev->dev);
+
+       r = kstrtoul(buf, 0, &g_select);
+       if (r)
+               return r;
+
+       /* 0 means 1.5g, 1 means 6g, everything else is wrong */
+       if (g_select != 0 && g_select != 1)
+               return -EINVAL;
+
+       accel->g_select = g_select;
+       cmpc_accel_set_g_select_v4(acpi->handle, g_select);
+
+       return strnlen(buf, count);
+}
+
+static struct device_attribute cmpc_accel_g_select_attr_v4 = {
+       .attr = { .name = "g_select", .mode = 0660 },
+       .show = cmpc_accel_g_select_show_v4,
+       .store = cmpc_accel_g_select_store_v4
+};
+
+static int cmpc_accel_open_v4(struct input_dev *input)
+{
+       struct acpi_device *acpi;
+       struct cmpc_accel *accel;
+
+       acpi = to_acpi_device(input->dev.parent);
+       accel = dev_get_drvdata(&input->dev);
+
+       cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
+       cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
+
+       if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) {
+               accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN;
+               return 0;
+       }
+       return -EIO;
+}
+
+static void cmpc_accel_close_v4(struct input_dev *input)
+{
+       struct acpi_device *acpi;
+       struct cmpc_accel *accel;
+
+       acpi = to_acpi_device(input->dev.parent);
+       accel = dev_get_drvdata(&input->dev);
+
+       cmpc_stop_accel_v4(acpi->handle);
+       accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
+}
+
+static void cmpc_accel_idev_init_v4(struct input_dev *inputdev)
+{
+       set_bit(EV_ABS, inputdev->evbit);
+       input_set_abs_params(inputdev, ABS_X, -255, 255, 16, 0);
+       input_set_abs_params(inputdev, ABS_Y, -255, 255, 16, 0);
+       input_set_abs_params(inputdev, ABS_Z, -255, 255, 16, 0);
+       inputdev->open = cmpc_accel_open_v4;
+       inputdev->close = cmpc_accel_close_v4;
+}
+
+static int cmpc_accel_suspend_v4(struct device *dev)
+{
+       struct input_dev *inputdev;
+       struct cmpc_accel *accel;
+
+       inputdev = dev_get_drvdata(dev);
+       accel = dev_get_drvdata(&inputdev->dev);
+
+       if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN)
+               return cmpc_stop_accel_v4(to_acpi_device(dev)->handle);
+
+       return 0;
+}
+
+static int cmpc_accel_resume_v4(struct device *dev)
+{
+       struct input_dev *inputdev;
+       struct cmpc_accel *accel;
+
+       inputdev = dev_get_drvdata(dev);
+       accel = dev_get_drvdata(&inputdev->dev);
+
+       if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) {
+               cmpc_accel_set_sensitivity_v4(to_acpi_device(dev)->handle,
+                                             accel->sensitivity);
+               cmpc_accel_set_g_select_v4(to_acpi_device(dev)->handle,
+                                          accel->g_select);
+
+               if (ACPI_FAILURE(cmpc_start_accel_v4(to_acpi_device(dev)->handle)))
+                       return -EIO;
+       }
+
+       return 0;
+}
+
+static int cmpc_accel_add_v4(struct acpi_device *acpi)
+{
+       int error;
+       struct input_dev *inputdev;
+       struct cmpc_accel *accel;
+
+       accel = kmalloc(sizeof(*accel), GFP_KERNEL);
+       if (!accel)
+               return -ENOMEM;
+
+       accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
+
+       accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
+       cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
+
+       error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
+       if (error)
+               goto failed_sensitivity;
+
+       accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT;
+       cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
+
+       error = device_create_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
+       if (error)
+               goto failed_g_select;
+
+       error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4",
+                                           cmpc_accel_idev_init_v4);
+       if (error)
+               goto failed_input;
+
+       inputdev = dev_get_drvdata(&acpi->dev);
+       dev_set_drvdata(&inputdev->dev, accel);
+
+       return 0;
+
+failed_input:
+       device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
+failed_g_select:
+       device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
+failed_sensitivity:
+       kfree(accel);
+       return error;
+}
+
+static int cmpc_accel_remove_v4(struct acpi_device *acpi, int type)
+{
+       struct input_dev *inputdev;
+       struct cmpc_accel *accel;
+
+       inputdev = dev_get_drvdata(&acpi->dev);
+       accel = dev_get_drvdata(&inputdev->dev);
+
+       device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
+       device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
+       return cmpc_remove_acpi_notify_device(acpi);
+}
+
+static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4,
+                        cmpc_accel_resume_v4);
+
+static const struct acpi_device_id cmpc_accel_device_ids_v4[] = {
+       {CMPC_ACCEL_HID_V4, 0},
+       {"", 0}
+};
+
+static struct acpi_driver cmpc_accel_acpi_driver_v4 = {
+       .owner = THIS_MODULE,
+       .name = "cmpc_accel_v4",
+       .class = "cmpc_accel_v4",
+       .ids = cmpc_accel_device_ids_v4,
+       .ops = {
+               .add = cmpc_accel_add_v4,
+               .remove = cmpc_accel_remove_v4,
+               .notify = cmpc_accel_handler_v4,
+       },
+       .drv.pm = &cmpc_accel_pm,
+};
+
+
+/*
+ * Accelerometer code for Classmate versions prior to V4
  */
 static acpi_status cmpc_start_accel(acpi_handle handle)
 {
@@ -726,8 +1116,15 @@ static int cmpc_init(void)
        if (r)
                goto failed_accel;
 
+       r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4);
+       if (r)
+               goto failed_accel_v4;
+
        return r;
 
+failed_accel_v4:
+       acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
+
 failed_accel:
        acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
 
@@ -743,6 +1140,7 @@ failed_keys:
 
 static void cmpc_exit(void)
 {
+       acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4);
        acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
        acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
        acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
@@ -754,6 +1152,7 @@ module_exit(cmpc_exit);
 
 static const struct acpi_device_id cmpc_device_ids[] = {
        {CMPC_ACCEL_HID, 0},
+       {CMPC_ACCEL_HID_V4, 0},
        {CMPC_TABLET_HID, 0},
        {CMPC_IPML_HID, 0},
        {CMPC_KEYS_HID, 0},
index 5f78aac9b163bf21c957f0beccbdff80d095228a..4e96e8c0b60f83dfb04e835fe0ec4ed40fddb6a6 100644 (file)
@@ -206,6 +206,60 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
                },
                .driver_data = &quirk_dell_vostro_v130,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "Dell Inspiron 5420",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5420"),
+               },
+               .driver_data = &quirk_dell_vostro_v130,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Dell Inspiron 5520",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5520"),
+               },
+               .driver_data = &quirk_dell_vostro_v130,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Dell Inspiron 5720",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5720"),
+               },
+               .driver_data = &quirk_dell_vostro_v130,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Dell Inspiron 7420",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7420"),
+               },
+               .driver_data = &quirk_dell_vostro_v130,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Dell Inspiron 7520",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7520"),
+               },
+               .driver_data = &quirk_dell_vostro_v130,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Dell Inspiron 7720",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7720"),
+               },
+               .driver_data = &quirk_dell_vostro_v130,
+       },
        { }
 };
 
index 656761380342006d8b13ad344610b8e70f9822d2..5838332ea5bd2f67c6e0f5c875a459027b8032e9 100644 (file)
@@ -79,7 +79,7 @@ static const struct key_entry eeepc_wmi_keymap[] = {
        { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */
        { KE_KEY, HOME_PRESS, { KEY_CONFIG } }, /* Home/Express gate key */
        { KE_KEY, 0xe8, { KEY_SCREENLOCK } },
-       { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } },
+       { KE_KEY, 0xe9, { KEY_DISPLAYTOGGLE } },
        { KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } },
        { KE_KEY, 0xec, { KEY_CAMERA_UP } },
        { KE_KEY, 0xed, { KEY_CAMERA_DOWN } },
@@ -107,6 +107,11 @@ static struct quirk_entry quirk_asus_et2012_type3 = {
        .store_backlight_power = true,
 };
 
+static struct quirk_entry quirk_asus_x101ch = {
+       /* We need this when ACPI function doesn't do this well */
+       .wmi_backlight_power = true,
+};
+
 static struct quirk_entry *quirks;
 
 static void et2012_quirks(void)
@@ -157,6 +162,24 @@ static struct dmi_system_id asus_quirks[] = {
                },
                .driver_data = &quirk_asus_unknown,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK Computer INC. X101CH",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X101CH"),
+               },
+               .driver_data = &quirk_asus_x101ch,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK Computer INC. 1015CX",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "1015CX"),
+               },
+               .driver_data = &quirk_asus_x101ch,
+       },
        {},
 };
 
index e2a34b42ddc1d584b5333fe9921d0d2c2b12dda4..c1ca7bcebb66b52bfb033fc24cc0bc2856bd313d 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
 #include <linux/ctype.h>
-#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
+#ifdef CONFIG_ACPI_VIDEO
 #include <acpi/video.h>
 #endif
 
@@ -1465,6 +1465,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
                        DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
                },
        },
+       /* DMI ids for laptops with bad Chassis Type */
+       {
+         .ident = "R40/R41",
+         .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+               DMI_MATCH(DMI_PRODUCT_NAME, "R40/R41"),
+               DMI_MATCH(DMI_BOARD_NAME, "R40/R41"),
+               },
+       },
        /* Specific DMI ids for laptop with quirks */
        {
         .callback = samsung_dmi_matched,
@@ -1506,6 +1515,16 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
                },
         .driver_data = &samsung_broken_acpi_video,
        },
+       {
+        .callback = samsung_dmi_matched,
+        .ident = "X360",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+               DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
+               DMI_MATCH(DMI_BOARD_NAME, "X360"),
+               },
+        .driver_data = &samsung_broken_acpi_video,
+       },
        { },
 };
 MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
@@ -1530,15 +1549,18 @@ static int __init samsung_init(void)
        samsung->quirks = quirks;
 
 
-#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
+#ifdef CONFIG_ACPI
+       if (samsung->quirks->broken_acpi_video)
+               acpi_video_dmi_promote_vendor();
+
        /* Don't handle backlight here if the acpi video already handle it */
        if (acpi_video_backlight_support()) {
-               if (samsung->quirks->broken_acpi_video) {
-                       pr_info("Disabling ACPI video driver\n");
-                       acpi_video_unregister();
-               } else {
-                       samsung->handle_backlight = false;
-               }
+               samsung->handle_backlight = false;
+       } else if (samsung->quirks->broken_acpi_video) {
+               pr_info("Disabling ACPI video driver\n");
+#ifdef CONFIG_ACPI_VIDEO
+               acpi_video_unregister();
+#endif
        }
 #endif
 
@@ -1552,8 +1574,7 @@ static int __init samsung_init(void)
 
 #ifdef CONFIG_ACPI
        /* Only log that if we are really on a sabi platform */
-       if (acpi_video_backlight_support() &&
-           !samsung->quirks->broken_acpi_video)
+       if (acpi_video_backlight_support())
                pr_info("Backlight controlled by ACPI video driver\n");
 #endif
 
index d5fd4a1193f84e9fd3db93835962a86bd8aefbac..e7f73287636cd9bdabb3f0b32104d4b7257e5e69 100644 (file)
@@ -3015,8 +3015,6 @@ static void hotkey_exit(void)
        if (hotkey_dev_attributes)
                delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
 
-       kfree(hotkey_keycode_map);
-
        dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY,
                   "restoring original HKEY status and mask\n");
        /* yes, there is a bitwise or below, we want the
@@ -5217,6 +5215,7 @@ static void led_exit(void)
                        led_classdev_unregister(&tpacpi_leds[i].led_classdev);
        }
 
+       flush_workqueue(tpacpi_wq);
        kfree(tpacpi_leds);
 }
 
@@ -8936,6 +8935,7 @@ static void thinkpad_acpi_module_exit(void)
                        input_unregister_device(tpacpi_inputdev);
                else
                        input_free_device(tpacpi_inputdev);
+               kfree(hotkey_keycode_map);
        }
 
        if (tpacpi_hwmon)
@@ -8969,6 +8969,7 @@ static void thinkpad_acpi_module_exit(void)
        kfree(thinkpad_id.bios_version_str);
        kfree(thinkpad_id.ec_version_str);
        kfree(thinkpad_id.model_str);
+       kfree(thinkpad_id.nummodel_str);
 }
 
 
index aa764ecc4e608373ce43590825ab7ee7c5530b0a..c1892f321c4636c09d9136b5736c78c13844f262 100644 (file)
@@ -268,6 +268,7 @@ config CHARGER_GPIO
 config CHARGER_MANAGER
        bool "Battery charger manager for multiple chargers"
        depends on REGULATOR && RTC_CLASS
+       select EXTCON
        help
           Say Y to enable charger-manager support, which allows multiple
           chargers attached to a battery and multiple batteries attached to a
index f5d6d379f2fb2f4c95ade1f73c4b4678900ee575..181ddece5181afceb5d025fbdcf9bfb7f641f007 100644 (file)
@@ -22,6 +22,7 @@
  * Datasheets:
  * http://focus.ti.com/docs/prod/folders/print/bq27000.html
  * http://focus.ti.com/docs/prod/folders/print/bq27500.html
+ * http://www.ti.com/product/bq27425-g1
  */
 
 #include <linux/module.h>
@@ -51,6 +52,7 @@
 #define BQ27x00_REG_LMD                        0x12 /* Last measured discharge */
 #define BQ27x00_REG_CYCT               0x2A /* Cycle count total */
 #define BQ27x00_REG_AE                 0x22 /* Available energy */
+#define BQ27x00_POWER_AVG              0x24
 
 #define BQ27000_REG_RSOC               0x0B /* Relative State-of-Charge */
 #define BQ27000_REG_ILMD               0x76 /* Initial last measured discharge */
 #define BQ27500_FLAG_SOCF              BIT(1) /* State-of-Charge threshold final */
 #define BQ27500_FLAG_SOC1              BIT(2) /* State-of-Charge threshold 1 */
 #define BQ27500_FLAG_FC                        BIT(9)
+#define BQ27500_FLAG_OTC               BIT(15)
+
+/* bq27425 register addresses are same as bq27x00 addresses minus 4 */
+#define BQ27425_REG_OFFSET             0x04
+#define BQ27425_REG_SOC                        0x18 /* Register address plus offset */
 
 #define BQ27000_RS                     20 /* Resistor sense */
+#define BQ27x00_POWER_CONSTANT         (256 * 29200 / 1000)
 
 struct bq27x00_device_info;
 struct bq27x00_access_methods {
        int (*read)(struct bq27x00_device_info *di, u8 reg, bool single);
 };
 
-enum bq27x00_chip { BQ27000, BQ27500 };
+enum bq27x00_chip { BQ27000, BQ27500, BQ27425};
 
 struct bq27x00_reg_cache {
        int temperature;
@@ -86,6 +94,8 @@ struct bq27x00_reg_cache {
        int capacity;
        int energy;
        int flags;
+       int power_avg;
+       int health;
 };
 
 struct bq27x00_device_info {
@@ -123,6 +133,22 @@ static enum power_supply_property bq27x00_battery_props[] = {
        POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
        POWER_SUPPLY_PROP_CYCLE_COUNT,
        POWER_SUPPLY_PROP_ENERGY_NOW,
+       POWER_SUPPLY_PROP_POWER_AVG,
+       POWER_SUPPLY_PROP_HEALTH,
+};
+
+static enum power_supply_property bq27425_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
 };
 
 static unsigned int poll_interval = 360;
@@ -137,9 +163,23 @@ MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \
 static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg,
                bool single)
 {
+       if (di->chip == BQ27425)
+               return di->bus.read(di, reg - BQ27425_REG_OFFSET, single);
        return di->bus.read(di, reg, single);
 }
 
+/*
+ * Higher versions of the chip like BQ27425 and BQ27500
+ * differ from BQ27000 and BQ27200 in calculation of certain
+ * parameters. Hence we need to check for the chip type.
+ */
+static bool bq27xxx_is_chip_version_higher(struct bq27x00_device_info *di)
+{
+       if (di->chip == BQ27425 || di->chip == BQ27500)
+               return true;
+       return false;
+}
+
 /*
  * Return the battery Relative State-of-Charge
  * Or < 0 if something fails.
@@ -150,6 +190,8 @@ static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di)
 
        if (di->chip == BQ27500)
                rsoc = bq27x00_read(di, BQ27500_REG_SOC, false);
+       else if (di->chip == BQ27425)
+               rsoc = bq27x00_read(di, BQ27425_REG_SOC, false);
        else
                rsoc = bq27x00_read(di, BQ27000_REG_RSOC, true);
 
@@ -174,7 +216,7 @@ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg)
                return charge;
        }
 
-       if (di->chip == BQ27500)
+       if (bq27xxx_is_chip_version_higher(di))
                charge *= 1000;
        else
                charge = charge * 3570 / BQ27000_RS;
@@ -208,7 +250,7 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di)
 {
        int ilmd;
 
-       if (di->chip == BQ27500)
+       if (bq27xxx_is_chip_version_higher(di))
                ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false);
        else
                ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true);
@@ -218,7 +260,7 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di)
                return ilmd;
        }
 
-       if (di->chip == BQ27500)
+       if (bq27xxx_is_chip_version_higher(di))
                ilmd *= 1000;
        else
                ilmd = ilmd * 256 * 3570 / BQ27000_RS;
@@ -262,7 +304,7 @@ static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di)
                return temp;
        }
 
-       if (di->chip == BQ27500)
+       if (bq27xxx_is_chip_version_higher(di))
                temp -= 2731;
        else
                temp = ((temp * 5) - 5463) / 2;
@@ -306,14 +348,70 @@ static int bq27x00_battery_read_time(struct bq27x00_device_info *di, u8 reg)
        return tval * 60;
 }
 
+/*
+ * Read a power avg register.
+ * Return < 0 if something fails.
+ */
+static int bq27x00_battery_read_pwr_avg(struct bq27x00_device_info *di, u8 reg)
+{
+       int tval;
+
+       tval = bq27x00_read(di, reg, false);
+       if (tval < 0) {
+               dev_err(di->dev, "error reading power avg rgister  %02x: %d\n",
+                       reg, tval);
+               return tval;
+       }
+
+       if (di->chip == BQ27500)
+               return tval;
+       else
+               return (tval * BQ27x00_POWER_CONSTANT) / BQ27000_RS;
+}
+
+/*
+ * Read flag register.
+ * Return < 0 if something fails.
+ */
+static int bq27x00_battery_read_health(struct bq27x00_device_info *di)
+{
+       int tval;
+
+       tval = bq27x00_read(di, BQ27x00_REG_FLAGS, false);
+       if (tval < 0) {
+               dev_err(di->dev, "error reading flag register:%d\n", tval);
+               return tval;
+       }
+
+       if ((di->chip == BQ27500)) {
+               if (tval & BQ27500_FLAG_SOCF)
+                       tval = POWER_SUPPLY_HEALTH_DEAD;
+               else if (tval & BQ27500_FLAG_OTC)
+                       tval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else
+                       tval = POWER_SUPPLY_HEALTH_GOOD;
+               return tval;
+       } else {
+               if (tval & BQ27000_FLAG_EDV1)
+                       tval = POWER_SUPPLY_HEALTH_DEAD;
+               else
+                       tval = POWER_SUPPLY_HEALTH_GOOD;
+               return tval;
+       }
+
+       return -1;
+}
+
 static void bq27x00_update(struct bq27x00_device_info *di)
 {
        struct bq27x00_reg_cache cache = {0, };
        bool is_bq27500 = di->chip == BQ27500;
+       bool is_bq27425 = di->chip == BQ27425;
 
        cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500);
        if (cache.flags >= 0) {
-               if (!is_bq27500 && (cache.flags & BQ27000_FLAG_CI)) {
+               if (!is_bq27500 && !is_bq27425
+                               && (cache.flags & BQ27000_FLAG_CI)) {
                        dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n");
                        cache.capacity = -ENODATA;
                        cache.energy = -ENODATA;
@@ -321,16 +419,30 @@ static void bq27x00_update(struct bq27x00_device_info *di)
                        cache.time_to_empty_avg = -ENODATA;
                        cache.time_to_full = -ENODATA;
                        cache.charge_full = -ENODATA;
+                       cache.health = -ENODATA;
                } else {
                        cache.capacity = bq27x00_battery_read_rsoc(di);
-                       cache.energy = bq27x00_battery_read_energy(di);
-                       cache.time_to_empty = bq27x00_battery_read_time(di, BQ27x00_REG_TTE);
-                       cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27x00_REG_TTECP);
-                       cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF);
+                       if (!is_bq27425) {
+                               cache.energy = bq27x00_battery_read_energy(di);
+                               cache.time_to_empty =
+                                       bq27x00_battery_read_time(di,
+                                                       BQ27x00_REG_TTE);
+                               cache.time_to_empty_avg =
+                                       bq27x00_battery_read_time(di,
+                                                       BQ27x00_REG_TTECP);
+                               cache.time_to_full =
+                                       bq27x00_battery_read_time(di,
+                                                       BQ27x00_REG_TTF);
+                       }
                        cache.charge_full = bq27x00_battery_read_lmd(di);
+                       cache.health = bq27x00_battery_read_health(di);
                }
                cache.temperature = bq27x00_battery_read_temperature(di);
+               if (!is_bq27425)
+                       cache.cycle_count = bq27x00_battery_read_cyct(di);
                cache.cycle_count = bq27x00_battery_read_cyct(di);
+               cache.power_avg =
+                       bq27x00_battery_read_pwr_avg(di, BQ27x00_POWER_AVG);
 
                /* We only have to read charge design full once */
                if (di->charge_design_full <= 0)
@@ -376,7 +488,7 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di,
                return curr;
        }
 
-       if (di->chip == BQ27500) {
+       if (bq27xxx_is_chip_version_higher(di)) {
                /* bq27500 returns signed value */
                val->intval = (int)((s16)curr) * 1000;
        } else {
@@ -397,7 +509,7 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di,
 {
        int status;
 
-       if (di->chip == BQ27500) {
+       if (bq27xxx_is_chip_version_higher(di)) {
                if (di->cache.flags & BQ27500_FLAG_FC)
                        status = POWER_SUPPLY_STATUS_FULL;
                else if (di->cache.flags & BQ27500_FLAG_DSC)
@@ -425,7 +537,7 @@ static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di,
 {
        int level;
 
-       if (di->chip == BQ27500) {
+       if (bq27xxx_is_chip_version_higher(di)) {
                if (di->cache.flags & BQ27500_FLAG_FC)
                        level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
                else if (di->cache.flags & BQ27500_FLAG_SOC1)
@@ -550,6 +662,12 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
        case POWER_SUPPLY_PROP_ENERGY_NOW:
                ret = bq27x00_simple_value(di->cache.energy, val);
                break;
+       case POWER_SUPPLY_PROP_POWER_AVG:
+               ret = bq27x00_simple_value(di->cache.power_avg, val);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = bq27x00_simple_value(di->cache.health, val);
+               break;
        default:
                return -EINVAL;
        }
@@ -570,8 +688,14 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di)
        int ret;
 
        di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
-       di->bat.properties = bq27x00_battery_props;
-       di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props);
+       di->chip = BQ27425;
+       if (di->chip == BQ27425) {
+               di->bat.properties = bq27425_battery_props;
+               di->bat.num_properties = ARRAY_SIZE(bq27425_battery_props);
+       } else {
+               di->bat.properties = bq27x00_battery_props;
+               di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props);
+       }
        di->bat.get_property = bq27x00_battery_get_property;
        di->bat.external_power_changed = bq27x00_external_power_changed;
 
@@ -729,6 +853,7 @@ static int bq27x00_battery_remove(struct i2c_client *client)
 static const struct i2c_device_id bq27x00_id[] = {
        { "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */
        { "bq27500", BQ27500 },
+       { "bq27425", BQ27425 },
        {},
 };
 MODULE_DEVICE_TABLE(i2c, bq27x00_id);
index 86935ec1895431aac77c47de1860711ae26e21eb..526e5c9312945bdf480f57a4673b478d22250be8 100644 (file)
@@ -271,16 +271,13 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
        if (enable) {
                if (cm->emergency_stop)
                        return -EAGAIN;
-               err = regulator_bulk_enable(desc->num_charger_regulators,
-                                       desc->charger_regulators);
+               for (i = 0 ; i < desc->num_charger_regulators ; i++)
+                       regulator_enable(desc->charger_regulators[i].consumer);
        } else {
                /*
                 * Abnormal battery state - Stop charging forcibly,
                 * even if charger was enabled at the other places
                 */
-               err = regulator_bulk_disable(desc->num_charger_regulators,
-                                       desc->charger_regulators);
-
                for (i = 0; i < desc->num_charger_regulators; i++) {
                        if (regulator_is_enabled(
                                    desc->charger_regulators[i].consumer)) {
@@ -288,7 +285,7 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
                                        desc->charger_regulators[i].consumer);
                                dev_warn(cm->dev,
                                        "Disable regulator(%s) forcibly.\n",
-                                       desc->charger_regulators[i].supply);
+                                       desc->charger_regulators[i].regulator_name);
                        }
                }
        }
@@ -994,11 +991,92 @@ int setup_charger_manager(struct charger_global_desc *gd)
 }
 EXPORT_SYMBOL_GPL(setup_charger_manager);
 
+/**
+ * charger_extcon_work - enable/diable charger according to the state
+ *                     of charger cable
+ *
+ * @work: work_struct of the function charger_extcon_work.
+ */
+static void charger_extcon_work(struct work_struct *work)
+{
+       struct charger_cable *cable =
+                       container_of(work, struct charger_cable, wq);
+       int ret;
+
+       if (cable->attached && cable->min_uA != 0 && cable->max_uA != 0) {
+               ret = regulator_set_current_limit(cable->charger->consumer,
+                                       cable->min_uA, cable->max_uA);
+               if (ret < 0) {
+                       pr_err("Cannot set current limit of %s (%s)\n",
+                               cable->charger->regulator_name, cable->name);
+                       return;
+               }
+
+               pr_info("Set current limit of %s : %duA ~ %duA\n",
+                                       cable->charger->regulator_name,
+                                       cable->min_uA, cable->max_uA);
+       }
+
+       try_charger_enable(cable->cm, cable->attached);
+}
+
+/**
+ * charger_extcon_notifier - receive the state of charger cable
+ *                     when registered cable is attached or detached.
+ *
+ * @self: the notifier block of the charger_extcon_notifier.
+ * @event: the cable state.
+ * @ptr: the data pointer of notifier block.
+ */
+static int charger_extcon_notifier(struct notifier_block *self,
+                       unsigned long event, void *ptr)
+{
+       struct charger_cable *cable =
+               container_of(self, struct charger_cable, nb);
+
+       cable->attached = event;
+       schedule_work(&cable->wq);
+
+       return NOTIFY_DONE;
+}
+
+/**
+ * charger_extcon_init - register external connector to use it
+ *                     as the charger cable
+ *
+ * @cm: the Charger Manager representing the battery.
+ * @cable: the Charger cable representing the external connector.
+ */
+static int charger_extcon_init(struct charger_manager *cm,
+               struct charger_cable *cable)
+{
+       int ret = 0;
+
+       /*
+        * Charger manager use Extcon framework to identify
+        * the charger cable among various external connector
+        * cable (e.g., TA, USB, MHL, Dock).
+        */
+       INIT_WORK(&cable->wq, charger_extcon_work);
+       cable->nb.notifier_call = charger_extcon_notifier;
+       ret = extcon_register_interest(&cable->extcon_dev,
+                       cable->extcon_name, cable->name, &cable->nb);
+       if (ret < 0) {
+               pr_info("Cannot register extcon_dev for %s(cable: %s).\n",
+                               cable->extcon_name,
+                               cable->name);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
 static int charger_manager_probe(struct platform_device *pdev)
 {
        struct charger_desc *desc = dev_get_platdata(&pdev->dev);
        struct charger_manager *cm;
        int ret = 0, i = 0;
+       int j = 0;
        union power_supply_propval val;
 
        if (g_desc && !rtc_dev && g_desc->rtc_name) {
@@ -1167,11 +1245,31 @@ static int charger_manager_probe(struct platform_device *pdev)
                goto err_register;
        }
 
-       ret = regulator_bulk_get(&pdev->dev, desc->num_charger_regulators,
-                                desc->charger_regulators);
-       if (ret) {
-               dev_err(&pdev->dev, "Cannot get charger regulators.\n");
-               goto err_bulk_get;
+       for (i = 0 ; i < desc->num_charger_regulators ; i++) {
+               struct charger_regulator *charger
+                                       = &desc->charger_regulators[i];
+
+               charger->consumer = regulator_get(&pdev->dev,
+                                       charger->regulator_name);
+               if (charger->consumer == NULL) {
+                       dev_err(&pdev->dev, "Cannot find charger(%s)n",
+                                               charger->regulator_name);
+                       ret = -EINVAL;
+                       goto err_chg_get;
+               }
+
+               for (j = 0 ; j < charger->num_cables ; j++) {
+                       struct charger_cable *cable = &charger->cables[j];
+
+                       ret = charger_extcon_init(cm, cable);
+                       if (ret < 0) {
+                               dev_err(&pdev->dev, "Cannot find charger(%s)n",
+                                               charger->regulator_name);
+                               goto err_extcon;
+                       }
+                       cable->charger = charger;
+                       cable->cm = cm;
+               }
        }
 
        ret = try_charger_enable(cm, true);
@@ -1197,9 +1295,19 @@ static int charger_manager_probe(struct platform_device *pdev)
        return 0;
 
 err_chg_enable:
-       regulator_bulk_free(desc->num_charger_regulators,
-                           desc->charger_regulators);
-err_bulk_get:
+err_extcon:
+       for (i = 0 ; i < desc->num_charger_regulators ; i++) {
+               struct charger_regulator *charger
+                               = &desc->charger_regulators[i];
+               for (j = 0 ; j < charger->num_cables ; j++) {
+                       struct charger_cable *cable = &charger->cables[j];
+                       extcon_unregister_interest(&cable->extcon_dev);
+               }
+       }
+err_chg_get:
+       for (i = 0 ; i < desc->num_charger_regulators ; i++)
+               regulator_put(desc->charger_regulators[i].consumer);
+
        power_supply_unregister(&cm->charger_psy);
 err_register:
        kfree(cm->charger_psy.properties);
@@ -1218,6 +1326,8 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
 {
        struct charger_manager *cm = platform_get_drvdata(pdev);
        struct charger_desc *desc = cm->desc;
+       int i = 0;
+       int j = 0;
 
        /* Remove from the list */
        mutex_lock(&cm_list_mtx);
@@ -1229,8 +1339,18 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
        if (delayed_work_pending(&cm_monitor_work))
                cancel_delayed_work_sync(&cm_monitor_work);
 
-       regulator_bulk_free(desc->num_charger_regulators,
-                           desc->charger_regulators);
+       for (i = 0 ; i < desc->num_charger_regulators ; i++) {
+               struct charger_regulator *charger
+                               = &desc->charger_regulators[i];
+               for (j = 0 ; j < charger->num_cables ; j++) {
+                       struct charger_cable *cable = &charger->cables[j];
+                       extcon_unregister_interest(&cable->extcon_dev);
+               }
+       }
+
+       for (i = 0 ; i < desc->num_charger_regulators ; i++)
+               regulator_put(desc->charger_regulators[i].consumer);
+
        power_supply_unregister(&cm->charger_psy);
 
        try_charger_enable(cm, false);
index 5f92a4bb33f95aaafe772c3aeea1b2bc54a00186..7a1ff4e4cf9a6452f1b58b2074638aac105cdc93 100644 (file)
@@ -64,7 +64,7 @@ static inline int ds2781_battery_io(struct ds2781_device_info *dev_info,
        return w1_ds2781_io(dev_info->w1_dev, buf, addr, count, io);
 }
 
-int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf,
+static int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf,
                int addr, size_t count)
 {
        return ds2781_battery_io(dev_info, buf, addr, count, 0);
index 8672c9177dd70f481937233fcd5fe0083bd8796c..cb2aa3195687b909d456f9058bbca605331795b2 100644 (file)
@@ -54,7 +54,7 @@ static int gpio_charger_get_property(struct power_supply *psy,
 
        switch (psp) {
        case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = gpio_get_value(pdata->gpio);
+               val->intval = gpio_get_value_cansleep(pdata->gpio);
                val->intval ^= pdata->gpio_active_low;
                break;
        default:
index d8b75780bfef15718e9f578618381eeb4bc69560..6a364f4798f78765b40298451474a876e10f71da 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/interrupt.h>
 #include <linux/i2c.h>
 #include <linux/power_supply.h>
-#include <linux/lp8727.h>
+#include <linux/platform_data/lp8727.h>
 
 #define DEBOUNCE_MSEC  270
 
index 140788b309f84fe26056e77636eff26b954d8cb8..74abc6c755b498bf023fcd540b9232743f1cd744 100644 (file)
@@ -113,6 +113,7 @@ static enum power_supply_property max17042_battery_props[] = {
        POWER_SUPPLY_PROP_VOLTAGE_OCV,
        POWER_SUPPLY_PROP_CAPACITY,
        POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_COUNTER,
        POWER_SUPPLY_PROP_TEMP,
        POWER_SUPPLY_PROP_CURRENT_NOW,
        POWER_SUPPLY_PROP_CURRENT_AVG,
@@ -199,6 +200,13 @@ static int max17042_get_property(struct power_supply *psy,
                if (ret < 0)
                        return ret;
 
+               val->intval = ret * 1000 / 2;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+               ret = max17042_read_reg(chip->client, MAX17042_QH);
+               if (ret < 0)
+                       return ret;
+
                val->intval = ret * 1000 / 2;
                break;
        case POWER_SUPPLY_PROP_TEMP:
index 7385092f9bc8a98a8dc09155238d6ee924ab6034..55b10b81335384f203c3fb91304a86679b93f868 100644 (file)
@@ -231,11 +231,9 @@ static int olpc_bat_get_charge_full_design(union power_supply_propval *val)
 
        case POWER_SUPPLY_TECHNOLOGY_LiFe:
                switch (mfr) {
-               case 1: /* Gold Peak */
-                       val->intval = 2800000;
-                       break;
+               case 1: /* Gold Peak, fall through */
                case 2: /* BYD */
-                       val->intval = 3100000;
+                       val->intval = 2800000;
                        break;
                default:
                        return -EIO;
@@ -267,6 +265,55 @@ static int olpc_bat_get_charge_now(union power_supply_propval *val)
        return 0;
 }
 
+static int olpc_bat_get_voltage_max_design(union power_supply_propval *val)
+{
+       uint8_t ec_byte;
+       union power_supply_propval tech;
+       int mfr;
+       int ret;
+
+       ret = olpc_bat_get_tech(&tech);
+       if (ret)
+               return ret;
+
+       ec_byte = BAT_ADDR_MFR_TYPE;
+       ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
+       if (ret)
+               return ret;
+
+       mfr = ec_byte >> 4;
+
+       switch (tech.intval) {
+       case POWER_SUPPLY_TECHNOLOGY_NiMH:
+               switch (mfr) {
+               case 1: /* Gold Peak */
+                       val->intval = 6000000;
+                       break;
+               default:
+                       return -EIO;
+               }
+               break;
+
+       case POWER_SUPPLY_TECHNOLOGY_LiFe:
+               switch (mfr) {
+               case 1: /* Gold Peak */
+                       val->intval = 6400000;
+                       break;
+               case 2: /* BYD */
+                       val->intval = 6500000;
+                       break;
+               default:
+                       return -EIO;
+               }
+               break;
+
+       default:
+               return -EIO;
+       }
+
+       return ret;
+}
+
 /*********************************************************************
  *             Battery properties
  *********************************************************************/
@@ -401,6 +448,11 @@ static int olpc_bat_get_property(struct power_supply *psy,
                sprintf(bat_serial, "%016llx", (long long)be64_to_cpu(ser_buf));
                val->strval = bat_serial;
                break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               ret = olpc_bat_get_voltage_max_design(val);
+               if (ret)
+                       return ret;
+               break;
        default:
                ret = -EINVAL;
                break;
@@ -428,6 +480,7 @@ static enum power_supply_property olpc_xo1_bat_props[] = {
        POWER_SUPPLY_PROP_MANUFACTURER,
        POWER_SUPPLY_PROP_SERIAL_NUMBER,
        POWER_SUPPLY_PROP_CHARGE_COUNTER,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
 };
 
 /* XO-1.5 does not have ambient temperature property */
@@ -449,6 +502,7 @@ static enum power_supply_property olpc_xo15_bat_props[] = {
        POWER_SUPPLY_PROP_MANUFACTURER,
        POWER_SUPPLY_PROP_SERIAL_NUMBER,
        POWER_SUPPLY_PROP_CHARGE_COUNTER,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
 };
 
 /* EEPROM reading goes completely around the power_supply API, sadly */
index 8dbcd53c5e67b2a8868de2028169139e0472a74e..7312f2651647fbb97e64caa56fe03e778f2508a0 100644 (file)
 
 static inline unsigned int get_irq_flags(struct resource *res)
 {
-       unsigned int flags = IRQF_SAMPLE_RANDOM | IRQF_SHARED;
-
-       flags |= res->flags & IRQF_TRIGGER_MASK;
-
-       return flags;
+       return IRQF_SHARED | (res->flags & IRQF_TRIGGER_MASK);
 }
 
 static struct device *dev;
@@ -134,13 +130,13 @@ static void update_charger(void)
                        regulator_set_current_limit(ac_draw, max_uA, max_uA);
                        if (!regulator_enabled) {
                                dev_dbg(dev, "charger on (AC)\n");
-                               regulator_enable(ac_draw);
+                               WARN_ON(regulator_enable(ac_draw));
                                regulator_enabled = 1;
                        }
                } else {
                        if (regulator_enabled) {
                                dev_dbg(dev, "charger off\n");
-                               regulator_disable(ac_draw);
+                               WARN_ON(regulator_disable(ac_draw));
                                regulator_enabled = 0;
                        }
                }
index 6ad612726785f48cdd8dbcc2785de8e211aae873..08cc8a3c15afb29b8147c1184c3477e543fdc01e 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/power_supply.h>
+#include <linux/thermal.h>
 #include "power_supply.h"
 
 /* exported for the APM Power driver, APM emulation */
@@ -169,6 +170,63 @@ static void power_supply_dev_release(struct device *dev)
        kfree(dev);
 }
 
+#ifdef CONFIG_THERMAL
+static int power_supply_read_temp(struct thermal_zone_device *tzd,
+               unsigned long *temp)
+{
+       struct power_supply *psy;
+       union power_supply_propval val;
+       int ret;
+
+       WARN_ON(tzd == NULL);
+       psy = tzd->devdata;
+       ret = psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &val);
+
+       /* Convert tenths of degree Celsius to milli degree Celsius. */
+       if (!ret)
+               *temp = val.intval * 100;
+
+       return ret;
+}
+
+static struct thermal_zone_device_ops psy_tzd_ops = {
+       .get_temp = power_supply_read_temp,
+};
+
+static int psy_register_thermal(struct power_supply *psy)
+{
+       int i;
+
+       /* Register battery zone device psy reports temperature */
+       for (i = 0; i < psy->num_properties; i++) {
+               if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) {
+                       psy->tzd = thermal_zone_device_register(psy->name, 0, 0,
+                                       psy, &psy_tzd_ops, 0, 0, 0, 0);
+                       if (IS_ERR(psy->tzd))
+                               return PTR_ERR(psy->tzd);
+                       break;
+               }
+       }
+       return 0;
+}
+
+static void psy_unregister_thermal(struct power_supply *psy)
+{
+       if (IS_ERR_OR_NULL(psy->tzd))
+               return;
+       thermal_zone_device_unregister(psy->tzd);
+}
+#else
+static int psy_register_thermal(struct power_supply *psy)
+{
+       return 0;
+}
+
+static void psy_unregister_thermal(struct power_supply *psy)
+{
+}
+#endif
+
 int power_supply_register(struct device *parent, struct power_supply *psy)
 {
        struct device *dev;
@@ -197,6 +255,10 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
        if (rc)
                goto device_add_failed;
 
+       rc = psy_register_thermal(psy);
+       if (rc)
+               goto register_thermal_failed;
+
        rc = power_supply_create_triggers(psy);
        if (rc)
                goto create_triggers_failed;
@@ -206,6 +268,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
        goto success;
 
 create_triggers_failed:
+       psy_unregister_thermal(psy);
+register_thermal_failed:
        device_del(dev);
 kobject_set_name_failed:
 device_add_failed:
@@ -220,6 +284,7 @@ void power_supply_unregister(struct power_supply *psy)
        cancel_work_sync(&psy->changed_work);
        sysfs_remove_link(&psy->dev->kobj, "powers");
        power_supply_remove_triggers(psy);
+       psy_unregister_thermal(psy);
        device_unregister(psy->dev);
 }
 EXPORT_SYMBOL_GPL(power_supply_unregister);
index 4150747f9186f8dfaff9faf81bf03a7ade8cb9fa..1d96614a17a42d7e8c48d4bb0421301c6ea997ea 100644 (file)
@@ -159,6 +159,8 @@ static struct device_attribute power_supply_attrs[] = {
        POWER_SUPPLY_ATTR(charge_now),
        POWER_SUPPLY_ATTR(charge_avg),
        POWER_SUPPLY_ATTR(charge_counter),
+       POWER_SUPPLY_ATTR(constant_charge_current),
+       POWER_SUPPLY_ATTR(constant_charge_voltage),
        POWER_SUPPLY_ATTR(energy_full_design),
        POWER_SUPPLY_ATTR(energy_empty_design),
        POWER_SUPPLY_ATTR(energy_full),
@@ -166,9 +168,15 @@ static struct device_attribute power_supply_attrs[] = {
        POWER_SUPPLY_ATTR(energy_now),
        POWER_SUPPLY_ATTR(energy_avg),
        POWER_SUPPLY_ATTR(capacity),
+       POWER_SUPPLY_ATTR(capacity_alert_min),
+       POWER_SUPPLY_ATTR(capacity_alert_max),
        POWER_SUPPLY_ATTR(capacity_level),
        POWER_SUPPLY_ATTR(temp),
+       POWER_SUPPLY_ATTR(temp_alert_min),
+       POWER_SUPPLY_ATTR(temp_alert_max),
        POWER_SUPPLY_ATTR(temp_ambient),
+       POWER_SUPPLY_ATTR(temp_ambient_alert_min),
+       POWER_SUPPLY_ATTR(temp_ambient_alert_max),
        POWER_SUPPLY_ATTR(time_to_empty_now),
        POWER_SUPPLY_ATTR(time_to_empty_avg),
        POWER_SUPPLY_ATTR(time_to_full_now),
index a5b6849d4123b51b60d2431bbcd72cc9f13c86f4..a65e8f54157efe4fe046367e6a28d10fefe0c141 100644 (file)
@@ -469,7 +469,7 @@ static int sbs_get_property(struct power_supply *psy,
 
        case POWER_SUPPLY_PROP_TECHNOLOGY:
                val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
-               break;
+               goto done; /* don't trigger power_supply_changed()! */
 
        case POWER_SUPPLY_PROP_ENERGY_NOW:
        case POWER_SUPPLY_PROP_ENERGY_FULL:
index f8eedd8a676fc68ad21f45b8bfa4ddec55add723..332dd0110bda36e6c7c09a60c98d5bed23d5d3cd 100644 (file)
@@ -196,6 +196,14 @@ static const unsigned int ccc_tbl[] = {
        1200000,
 };
 
+/* Convert register value to current using lookup table */
+static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val)
+{
+       if (val >= size)
+               return -EINVAL;
+       return tbl[val];
+}
+
 /* Convert current to register value using lookup table */
 static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val)
 {
@@ -841,22 +849,101 @@ fail:
        return ret;
 }
 
+/*
+ * Returns the constant charge current programmed
+ * into the charger in uA.
+ */
+static int get_const_charge_current(struct smb347_charger *smb)
+{
+       int ret, intval;
+       unsigned int v;
+
+       if (!smb347_is_ps_online(smb))
+               return -ENODATA;
+
+       ret = regmap_read(smb->regmap, STAT_B, &v);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * The current value is composition of FCC and PCC values
+        * and we can detect which table to use from bit 5.
+        */
+       if (v & 0x20) {
+               intval = hw_to_current(fcc_tbl, ARRAY_SIZE(fcc_tbl), v & 7);
+       } else {
+               v >>= 3;
+               intval = hw_to_current(pcc_tbl, ARRAY_SIZE(pcc_tbl), v & 7);
+       }
+
+       return intval;
+}
+
+/*
+ * Returns the constant charge voltage programmed
+ * into the charger in uV.
+ */
+static int get_const_charge_voltage(struct smb347_charger *smb)
+{
+       int ret, intval;
+       unsigned int v;
+
+       if (!smb347_is_ps_online(smb))
+               return -ENODATA;
+
+       ret = regmap_read(smb->regmap, STAT_A, &v);
+       if (ret < 0)
+               return ret;
+
+       v &= STAT_A_FLOAT_VOLTAGE_MASK;
+       if (v > 0x3d)
+               v = 0x3d;
+
+       intval = 3500000 + v * 20000;
+
+       return intval;
+}
+
 static int smb347_mains_get_property(struct power_supply *psy,
                                     enum power_supply_property prop,
                                     union power_supply_propval *val)
 {
        struct smb347_charger *smb =
                container_of(psy, struct smb347_charger, mains);
+       int ret;
 
-       if (prop == POWER_SUPPLY_PROP_ONLINE) {
+       switch (prop) {
+       case POWER_SUPPLY_PROP_ONLINE:
                val->intval = smb->mains_online;
-               return 0;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               ret = get_const_charge_voltage(smb);
+               if (ret < 0)
+                       return ret;
+               else
+                       val->intval = ret;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               ret = get_const_charge_current(smb);
+               if (ret < 0)
+                       return ret;
+               else
+                       val->intval = ret;
+               break;
+
+       default:
+               return -EINVAL;
        }
-       return -EINVAL;
+
+       return 0;
 }
 
 static enum power_supply_property smb347_mains_properties[] = {
        POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
 };
 
 static int smb347_usb_get_property(struct power_supply *psy,
@@ -865,16 +952,40 @@ static int smb347_usb_get_property(struct power_supply *psy,
 {
        struct smb347_charger *smb =
                container_of(psy, struct smb347_charger, usb);
+       int ret;
 
-       if (prop == POWER_SUPPLY_PROP_ONLINE) {
+       switch (prop) {
+       case POWER_SUPPLY_PROP_ONLINE:
                val->intval = smb->usb_online;
-               return 0;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               ret = get_const_charge_voltage(smb);
+               if (ret < 0)
+                       return ret;
+               else
+                       val->intval = ret;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               ret = get_const_charge_current(smb);
+               if (ret < 0)
+                       return ret;
+               else
+                       val->intval = ret;
+               break;
+
+       default:
+               return -EINVAL;
        }
-       return -EINVAL;
+
+       return 0;
 }
 
 static enum power_supply_property smb347_usb_properties[] = {
        POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
 };
 
 static int smb347_battery_get_property(struct power_supply *psy,
index b527c93bf2f3fbb11869333aa0cb36cad22ffea5..b99a452a4fda40165493a1c3640dbbc23084f18f 100644 (file)
 #include <linux/vermagic.h>
 
 static int ac_online                   = 1;
+static int usb_online                  = 1;
 static int battery_status              = POWER_SUPPLY_STATUS_DISCHARGING;
 static int battery_health              = POWER_SUPPLY_HEALTH_GOOD;
 static int battery_present             = 1; /* true */
 static int battery_technology          = POWER_SUPPLY_TECHNOLOGY_LION;
 static int battery_capacity            = 50;
+static int battery_voltage             = 3300;
 
 static int test_power_get_ac_property(struct power_supply *psy,
                                      enum power_supply_property psp,
@@ -42,6 +44,20 @@ static int test_power_get_ac_property(struct power_supply *psy,
        return 0;
 }
 
+static int test_power_get_usb_property(struct power_supply *psy,
+                                     enum power_supply_property psp,
+                                     union power_supply_propval *val)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = usb_online;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
 static int test_power_get_battery_property(struct power_supply *psy,
                                           enum power_supply_property psp,
                                           union power_supply_propval *val)
@@ -86,6 +102,12 @@ static int test_power_get_battery_property(struct power_supply *psy,
        case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
                val->intval = 3600;
                break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = 26;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = battery_voltage;
+               break;
        default:
                pr_info("%s: some properties deliberately report errors.\n",
                        __func__);
@@ -114,6 +136,8 @@ static enum power_supply_property test_power_battery_props[] = {
        POWER_SUPPLY_PROP_MODEL_NAME,
        POWER_SUPPLY_PROP_MANUFACTURER,
        POWER_SUPPLY_PROP_SERIAL_NUMBER,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
 };
 
 static char *test_power_ac_supplied_to[] = {
@@ -135,6 +159,14 @@ static struct power_supply test_power_supplies[] = {
                .properties = test_power_battery_props,
                .num_properties = ARRAY_SIZE(test_power_battery_props),
                .get_property = test_power_get_battery_property,
+       }, {
+               .name = "test_usb",
+               .type = POWER_SUPPLY_TYPE_USB,
+               .supplied_to = test_power_ac_supplied_to,
+               .num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to),
+               .properties = test_power_ac_props,
+               .num_properties = ARRAY_SIZE(test_power_ac_props),
+               .get_property = test_power_get_usb_property,
        },
 };
 
@@ -167,6 +199,7 @@ static void __exit test_power_exit(void)
 
        /* Let's see how we handle changes... */
        ac_online = 0;
+       usb_online = 0;
        battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
        for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++)
                power_supply_changed(&test_power_supplies[i]);
@@ -275,6 +308,19 @@ static int param_get_ac_online(char *buffer, const struct kernel_param *kp)
        return strlen(buffer);
 }
 
+static int param_set_usb_online(const char *key, const struct kernel_param *kp)
+{
+       usb_online = map_get_value(map_ac_online, key, usb_online);
+       power_supply_changed(&test_power_supplies[2]);
+       return 0;
+}
+
+static int param_get_usb_online(char *buffer, const struct kernel_param *kp)
+{
+       strcpy(buffer, map_get_key(map_ac_online, usb_online, "unknown"));
+       return strlen(buffer);
+}
+
 static int param_set_battery_status(const char *key,
                                        const struct kernel_param *kp)
 {
@@ -350,13 +396,31 @@ static int param_set_battery_capacity(const char *key,
 
 #define param_get_battery_capacity param_get_int
 
+static int param_set_battery_voltage(const char *key,
+                                       const struct kernel_param *kp)
+{
+       int tmp;
+
+       if (1 != sscanf(key, "%d", &tmp))
+               return -EINVAL;
+
+       battery_voltage = tmp;
+       power_supply_changed(&test_power_supplies[1]);
+       return 0;
+}
 
+#define param_get_battery_voltage param_get_int
 
 static struct kernel_param_ops param_ops_ac_online = {
        .set = param_set_ac_online,
        .get = param_get_ac_online,
 };
 
+static struct kernel_param_ops param_ops_usb_online = {
+       .set = param_set_usb_online,
+       .get = param_get_usb_online,
+};
+
 static struct kernel_param_ops param_ops_battery_status = {
        .set = param_set_battery_status,
        .get = param_get_battery_status,
@@ -382,18 +446,27 @@ static struct kernel_param_ops param_ops_battery_capacity = {
        .get = param_get_battery_capacity,
 };
 
+static struct kernel_param_ops param_ops_battery_voltage = {
+       .set = param_set_battery_voltage,
+       .get = param_get_battery_voltage,
+};
 
 #define param_check_ac_online(name, p) __param_check(name, p, void);
+#define param_check_usb_online(name, p) __param_check(name, p, void);
 #define param_check_battery_status(name, p) __param_check(name, p, void);
 #define param_check_battery_present(name, p) __param_check(name, p, void);
 #define param_check_battery_technology(name, p) __param_check(name, p, void);
 #define param_check_battery_health(name, p) __param_check(name, p, void);
 #define param_check_battery_capacity(name, p) __param_check(name, p, void);
+#define param_check_battery_voltage(name, p) __param_check(name, p, void);
 
 
 module_param(ac_online, ac_online, 0644);
 MODULE_PARM_DESC(ac_online, "AC charging state <on|off>");
 
+module_param(usb_online, usb_online, 0644);
+MODULE_PARM_DESC(usb_online, "USB charging state <on|off>");
+
 module_param(battery_status, battery_status, 0644);
 MODULE_PARM_DESC(battery_status,
        "battery status <charging|discharging|not-charging|full>");
@@ -413,6 +486,8 @@ MODULE_PARM_DESC(battery_health,
 module_param(battery_capacity, battery_capacity, 0644);
 MODULE_PARM_DESC(battery_capacity, "battery capacity (percentage)");
 
+module_param(battery_voltage, battery_voltage, 0644);
+MODULE_PARM_DESC(battery_voltage, "battery voltage (millivolts)");
 
 MODULE_DESCRIPTION("Power supply driver for testing");
 MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>");
index 7cacbaa68efe410902c64bb04c3a68619c4cd689..15f4d5d8611b554b5750b77a7fee430ba3448ead 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/power_supply.h>
 #include <linux/notifier.h>
 #include <linux/usb/otg.h>
+#include <linux/regulator/machine.h>
 
 #define TWL4030_BCIMSTATEC     0x02
 #define TWL4030_BCIICHG                0x08
@@ -29,6 +30,7 @@
 #define TWL4030_BCIVBUS                0x0c
 #define TWL4030_BCIMFSTS4      0x10
 #define TWL4030_BCICTL1                0x23
+#define TWL4030_BB_CFG         0x12
 
 #define TWL4030_BCIAUTOWEN     BIT(5)
 #define TWL4030_CONFIG_DONE    BIT(4)
 #define TWL4030_USBFASTMCHG    BIT(2)
 #define TWL4030_STS_VBUS       BIT(7)
 #define TWL4030_STS_USB_ID     BIT(2)
+#define TWL4030_BBCHEN         BIT(4)
+#define TWL4030_BBSEL_MASK     0b1100
+#define TWL4030_BBSEL_2V5      0b0000
+#define TWL4030_BBSEL_3V0      0b0100
+#define TWL4030_BBSEL_3V1      0b1000
+#define TWL4030_BBSEL_3V2      0b1100
+#define TWL4030_BBISEL_MASK    0b11
+#define TWL4030_BBISEL_25uA    0b00
+#define TWL4030_BBISEL_150uA   0b01
+#define TWL4030_BBISEL_500uA   0b10
+#define TWL4030_BBISEL_1000uA  0b11
 
 /* BCI interrupts */
 #define TWL4030_WOVF           BIT(0) /* Watchdog overflow */
@@ -75,6 +88,8 @@ struct twl4030_bci {
        struct work_struct      work;
        int                     irq_chg;
        int                     irq_bci;
+       struct regulator        *usb_reg;
+       int                     usb_enabled;
 
        unsigned long           event;
 };
@@ -104,7 +119,7 @@ static int twl4030_bci_read(u8 reg, u8 *val)
 
 static int twl4030_clear_set_boot_bci(u8 clear, u8 set)
 {
-       return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, 0,
+       return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, clear,
                        TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set,
                        TWL4030_PM_MASTER_BOOT_BCI);
 }
@@ -152,14 +167,14 @@ static int twl4030_bci_have_vbus(struct twl4030_bci *bci)
 }
 
 /*
- * Enable/Disable USB Charge funtionality.
+ * Enable/Disable USB Charge functionality.
  */
 static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
 {
        int ret;
 
        if (enable) {
-               /* Check for USB charger conneted */
+               /* Check for USB charger connected */
                if (!twl4030_bci_have_vbus(bci))
                        return -ENODEV;
 
@@ -172,6 +187,12 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
                        return -EACCES;
                }
 
+               /* Need to keep regulator on */
+               if (!bci->usb_enabled) {
+                       regulator_enable(bci->usb_reg);
+                       bci->usb_enabled = 1;
+               }
+
                /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
                ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
                if (ret < 0)
@@ -182,6 +203,10 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
                        TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4);
        } else {
                ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0);
+               if (bci->usb_enabled) {
+                       regulator_disable(bci->usb_reg);
+                       bci->usb_enabled = 0;
+               }
        }
 
        return ret;
@@ -202,6 +227,49 @@ static int twl4030_charger_enable_ac(bool enable)
        return ret;
 }
 
+/*
+ * Enable/Disable charging of Backup Battery.
+ */
+static int twl4030_charger_enable_backup(int uvolt, int uamp)
+{
+       int ret;
+       u8 flags;
+
+       if (uvolt < 2500000 ||
+           uamp < 25) {
+               /* disable charging of backup battery */
+               ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER,
+                                       TWL4030_BBCHEN, 0, TWL4030_BB_CFG);
+               return ret;
+       }
+
+       flags = TWL4030_BBCHEN;
+       if (uvolt >= 3200000)
+               flags |= TWL4030_BBSEL_3V2;
+       else if (uvolt >= 3100000)
+               flags |= TWL4030_BBSEL_3V1;
+       else if (uvolt >= 3000000)
+               flags |= TWL4030_BBSEL_3V0;
+       else
+               flags |= TWL4030_BBSEL_2V5;
+
+       if (uamp >= 1000)
+               flags |= TWL4030_BBISEL_1000uA;
+       else if (uamp >= 500)
+               flags |= TWL4030_BBISEL_500uA;
+       else if (uamp >= 150)
+               flags |= TWL4030_BBISEL_150uA;
+       else
+               flags |= TWL4030_BBISEL_25uA;
+
+       ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER,
+                               TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK,
+                               flags,
+                               TWL4030_BB_CFG);
+
+       return ret;
+}
+
 /*
  * TWL4030 CHG_PRES (AC charger presence) events
  */
@@ -425,6 +493,7 @@ static enum power_supply_property twl4030_charger_props[] = {
 static int __init twl4030_bci_probe(struct platform_device *pdev)
 {
        struct twl4030_bci *bci;
+       struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
        int ret;
        u32 reg;
 
@@ -456,6 +525,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
        bci->usb.num_properties = ARRAY_SIZE(twl4030_charger_props);
        bci->usb.get_property = twl4030_bci_get_property;
 
+       bci->usb_reg = regulator_get(bci->dev, "bci3v1");
+
        ret = power_supply_register(&pdev->dev, &bci->usb);
        if (ret) {
                dev_err(&pdev->dev, "failed to register usb: %d\n", ret);
@@ -504,6 +575,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
 
        twl4030_charger_enable_ac(true);
        twl4030_charger_enable_usb(bci, true);
+       twl4030_charger_enable_backup(pdata->bb_uvolt,
+                                     pdata->bb_uamp);
 
        return 0;
 
@@ -532,6 +605,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev)
 
        twl4030_charger_enable_ac(false);
        twl4030_charger_enable_usb(bci, false);
+       twl4030_charger_enable_backup(0, 0);
 
        /* mask interrupts */
        twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
index 98fbe62694d4eddbead50fde0de22fe834ce67a4..e771487132f7542123a0e8a669cd8582481ab051 100644 (file)
@@ -327,8 +327,10 @@ int pps_register_cdev(struct pps_device *pps)
        }
        pps->dev = device_create(pps_class, pps->info.dev, devt, pps,
                                                        "pps%d", pps->id);
-       if (IS_ERR(pps->dev))
+       if (IS_ERR(pps->dev)) {
+               err = PTR_ERR(pps->dev);
                goto del_cdev;
+       }
 
        pps->dev->release = pps_device_destruct;
 
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
new file mode 100644 (file)
index 0000000..8fc3808
--- /dev/null
@@ -0,0 +1,108 @@
+menuconfig PWM
+       bool "PWM Support"
+       depends on !MACH_JZ4740 && !PUV3_PWM
+       help
+         This enables PWM support through the generic PWM framework.
+         You only need to enable this, if you also want to enable
+         one or more of the PWM drivers below.
+
+         If unsure, say N.
+
+if PWM
+
+config PWM_BFIN
+       tristate "Blackfin PWM support"
+       depends on BFIN_GPTIMERS
+       help
+         Generic PWM framework driver for Blackfin.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-bfin.
+
+config PWM_IMX
+       tristate "i.MX pwm support"
+       depends on ARCH_MXC
+       help
+         Generic PWM framework driver for i.MX.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-imx.
+
+config PWM_LPC32XX
+       tristate "LPC32XX PWM support"
+       depends on ARCH_LPC32XX
+       help
+         Generic PWM framework driver for LPC32XX. The LPC32XX SOC has two
+         PWM controllers.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-lpc32xx.
+
+config PWM_MXS
+       tristate "Freescale MXS PWM support"
+       depends on ARCH_MXS && OF
+       select STMP_DEVICE
+       help
+         Generic PWM framework driver for Freescale MXS.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-mxs.
+
+config PWM_PXA
+       tristate "PXA PWM support"
+       depends on ARCH_PXA
+       help
+         Generic PWM framework driver for PXA.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-pxa.
+
+config PWM_SAMSUNG
+       tristate "Samsung pwm support"
+       depends on PLAT_SAMSUNG
+       help
+         Generic PWM framework driver for Samsung.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-samsung.
+
+config PWM_TEGRA
+       tristate "NVIDIA Tegra PWM support"
+       depends on ARCH_TEGRA
+       help
+         Generic PWM framework driver for the PWFM controller found on NVIDIA
+         Tegra SoCs.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-tegra.
+
+config  PWM_TIECAP
+       tristate "ECAP PWM support"
+       depends on SOC_AM33XX
+       help
+         PWM driver support for the ECAP APWM controller found on AM33XX
+         TI SOC
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-tiecap.
+
+config  PWM_TIEHRPWM
+       tristate "EHRPWM PWM support"
+       depends on SOC_AM33XX
+       help
+         PWM driver support for the EHRPWM controller found on AM33XX
+         TI SOC
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-tiehrpwm.
+
+config PWM_VT8500
+       tristate "vt8500 pwm support"
+       depends on ARCH_VT8500
+       help
+         Generic PWM framework driver for vt8500.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-vt8500.
+
+endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
new file mode 100644 (file)
index 0000000..e4b2c89
--- /dev/null
@@ -0,0 +1,11 @@
+obj-$(CONFIG_PWM)              += core.o
+obj-$(CONFIG_PWM_BFIN)         += pwm-bfin.o
+obj-$(CONFIG_PWM_IMX)          += pwm-imx.o
+obj-$(CONFIG_PWM_LPC32XX)      += pwm-lpc32xx.o
+obj-$(CONFIG_PWM_MXS)          += pwm-mxs.o
+obj-$(CONFIG_PWM_PXA)          += pwm-pxa.o
+obj-$(CONFIG_PWM_SAMSUNG)      += pwm-samsung.o
+obj-$(CONFIG_PWM_TEGRA)                += pwm-tegra.o
+obj-$(CONFIG_PWM_TIECAP)       += pwm-tiecap.o
+obj-$(CONFIG_PWM_TIEHRPWM)     += pwm-tiehrpwm.o
+obj-$(CONFIG_PWM_VT8500)       += pwm-vt8500.o
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
new file mode 100644 (file)
index 0000000..ecb7690
--- /dev/null
@@ -0,0 +1,713 @@
+/*
+ * Generic pwmlib implementation
+ *
+ * Copyright (C) 2011 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2011-2012 Avionic Design GmbH
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/pwm.h>
+#include <linux/radix-tree.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#define MAX_PWMS 1024
+
+static DEFINE_MUTEX(pwm_lookup_lock);
+static LIST_HEAD(pwm_lookup_list);
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_chips);
+static DECLARE_BITMAP(allocated_pwms, MAX_PWMS);
+static RADIX_TREE(pwm_tree, GFP_KERNEL);
+
+static struct pwm_device *pwm_to_device(unsigned int pwm)
+{
+       return radix_tree_lookup(&pwm_tree, pwm);
+}
+
+static int alloc_pwms(int pwm, unsigned int count)
+{
+       unsigned int from = 0;
+       unsigned int start;
+
+       if (pwm >= MAX_PWMS)
+               return -EINVAL;
+
+       if (pwm >= 0)
+               from = pwm;
+
+       start = bitmap_find_next_zero_area(allocated_pwms, MAX_PWMS, from,
+                                          count, 0);
+
+       if (pwm >= 0 && start != pwm)
+               return -EEXIST;
+
+       if (start + count > MAX_PWMS)
+               return -ENOSPC;
+
+       return start;
+}
+
+static void free_pwms(struct pwm_chip *chip)
+{
+       unsigned int i;
+
+       for (i = 0; i < chip->npwm; i++) {
+               struct pwm_device *pwm = &chip->pwms[i];
+               radix_tree_delete(&pwm_tree, pwm->pwm);
+       }
+
+       bitmap_clear(allocated_pwms, chip->base, chip->npwm);
+
+       kfree(chip->pwms);
+       chip->pwms = NULL;
+}
+
+static struct pwm_chip *pwmchip_find_by_name(const char *name)
+{
+       struct pwm_chip *chip;
+
+       if (!name)
+               return NULL;
+
+       mutex_lock(&pwm_lock);
+
+       list_for_each_entry(chip, &pwm_chips, list) {
+               const char *chip_name = dev_name(chip->dev);
+
+               if (chip_name && strcmp(chip_name, name) == 0) {
+                       mutex_unlock(&pwm_lock);
+                       return chip;
+               }
+       }
+
+       mutex_unlock(&pwm_lock);
+
+       return NULL;
+}
+
+static int pwm_device_request(struct pwm_device *pwm, const char *label)
+{
+       int err;
+
+       if (test_bit(PWMF_REQUESTED, &pwm->flags))
+               return -EBUSY;
+
+       if (!try_module_get(pwm->chip->ops->owner))
+               return -ENODEV;
+
+       if (pwm->chip->ops->request) {
+               err = pwm->chip->ops->request(pwm->chip, pwm);
+               if (err) {
+                       module_put(pwm->chip->ops->owner);
+                       return err;
+               }
+       }
+
+       set_bit(PWMF_REQUESTED, &pwm->flags);
+       pwm->label = label;
+
+       return 0;
+}
+
+static struct pwm_device *of_pwm_simple_xlate(struct pwm_chip *pc,
+                                             const struct of_phandle_args *args)
+{
+       struct pwm_device *pwm;
+
+       if (pc->of_pwm_n_cells < 2)
+               return ERR_PTR(-EINVAL);
+
+       if (args->args[0] >= pc->npwm)
+               return ERR_PTR(-EINVAL);
+
+       pwm = pwm_request_from_chip(pc, args->args[0], NULL);
+       if (IS_ERR(pwm))
+               return pwm;
+
+       pwm_set_period(pwm, args->args[1]);
+
+       return pwm;
+}
+
+void of_pwmchip_add(struct pwm_chip *chip)
+{
+       if (!chip->dev || !chip->dev->of_node)
+               return;
+
+       if (!chip->of_xlate) {
+               chip->of_xlate = of_pwm_simple_xlate;
+               chip->of_pwm_n_cells = 2;
+       }
+
+       of_node_get(chip->dev->of_node);
+}
+
+void of_pwmchip_remove(struct pwm_chip *chip)
+{
+       if (chip->dev && chip->dev->of_node)
+               of_node_put(chip->dev->of_node);
+}
+
+/**
+ * pwm_set_chip_data() - set private chip data for a PWM
+ * @pwm: PWM device
+ * @data: pointer to chip-specific data
+ */
+int pwm_set_chip_data(struct pwm_device *pwm, void *data)
+{
+       if (!pwm)
+               return -EINVAL;
+
+       pwm->chip_data = data;
+
+       return 0;
+}
+
+/**
+ * pwm_get_chip_data() - get private chip data for a PWM
+ * @pwm: PWM device
+ */
+void *pwm_get_chip_data(struct pwm_device *pwm)
+{
+       return pwm ? pwm->chip_data : NULL;
+}
+
+/**
+ * pwmchip_add() - register a new PWM chip
+ * @chip: the PWM chip to add
+ *
+ * Register a new PWM chip. If chip->base < 0 then a dynamically assigned base
+ * will be used.
+ */
+int pwmchip_add(struct pwm_chip *chip)
+{
+       struct pwm_device *pwm;
+       unsigned int i;
+       int ret;
+
+       if (!chip || !chip->dev || !chip->ops || !chip->ops->config ||
+           !chip->ops->enable || !chip->ops->disable)
+               return -EINVAL;
+
+       mutex_lock(&pwm_lock);
+
+       ret = alloc_pwms(chip->base, chip->npwm);
+       if (ret < 0)
+               goto out;
+
+       chip->pwms = kzalloc(chip->npwm * sizeof(*pwm), GFP_KERNEL);
+       if (!chip->pwms) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       chip->base = ret;
+
+       for (i = 0; i < chip->npwm; i++) {
+               pwm = &chip->pwms[i];
+
+               pwm->chip = chip;
+               pwm->pwm = chip->base + i;
+               pwm->hwpwm = i;
+
+               radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
+       }
+
+       bitmap_set(allocated_pwms, chip->base, chip->npwm);
+
+       INIT_LIST_HEAD(&chip->list);
+       list_add(&chip->list, &pwm_chips);
+
+       ret = 0;
+
+       if (IS_ENABLED(CONFIG_OF))
+               of_pwmchip_add(chip);
+
+out:
+       mutex_unlock(&pwm_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pwmchip_add);
+
+/**
+ * pwmchip_remove() - remove a PWM chip
+ * @chip: the PWM chip to remove
+ *
+ * Removes a PWM chip. This function may return busy if the PWM chip provides
+ * a PWM device that is still requested.
+ */
+int pwmchip_remove(struct pwm_chip *chip)
+{
+       unsigned int i;
+       int ret = 0;
+
+       mutex_lock(&pwm_lock);
+
+       for (i = 0; i < chip->npwm; i++) {
+               struct pwm_device *pwm = &chip->pwms[i];
+
+               if (test_bit(PWMF_REQUESTED, &pwm->flags)) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+       }
+
+       list_del_init(&chip->list);
+
+       if (IS_ENABLED(CONFIG_OF))
+               of_pwmchip_remove(chip);
+
+       free_pwms(chip);
+
+out:
+       mutex_unlock(&pwm_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pwmchip_remove);
+
+/**
+ * pwm_request() - request a PWM device
+ * @pwm_id: global PWM device index
+ * @label: PWM device label
+ *
+ * This function is deprecated, use pwm_get() instead.
+ */
+struct pwm_device *pwm_request(int pwm, const char *label)
+{
+       struct pwm_device *dev;
+       int err;
+
+       if (pwm < 0 || pwm >= MAX_PWMS)
+               return ERR_PTR(-EINVAL);
+
+       mutex_lock(&pwm_lock);
+
+       dev = pwm_to_device(pwm);
+       if (!dev) {
+               dev = ERR_PTR(-EPROBE_DEFER);
+               goto out;
+       }
+
+       err = pwm_device_request(dev, label);
+       if (err < 0)
+               dev = ERR_PTR(err);
+
+out:
+       mutex_unlock(&pwm_lock);
+
+       return dev;
+}
+EXPORT_SYMBOL_GPL(pwm_request);
+
+/**
+ * pwm_request_from_chip() - request a PWM device relative to a PWM chip
+ * @chip: PWM chip
+ * @index: per-chip index of the PWM to request
+ * @label: a literal description string of this PWM
+ *
+ * Returns the PWM at the given index of the given PWM chip. A negative error
+ * code is returned if the index is not valid for the specified PWM chip or
+ * if the PWM device cannot be requested.
+ */
+struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
+                                        unsigned int index,
+                                        const char *label)
+{
+       struct pwm_device *pwm;
+       int err;
+
+       if (!chip || index >= chip->npwm)
+               return ERR_PTR(-EINVAL);
+
+       mutex_lock(&pwm_lock);
+       pwm = &chip->pwms[index];
+
+       err = pwm_device_request(pwm, label);
+       if (err < 0)
+               pwm = ERR_PTR(err);
+
+       mutex_unlock(&pwm_lock);
+       return pwm;
+}
+EXPORT_SYMBOL_GPL(pwm_request_from_chip);
+
+/**
+ * pwm_free() - free a PWM device
+ * @pwm: PWM device
+ *
+ * This function is deprecated, use pwm_put() instead.
+ */
+void pwm_free(struct pwm_device *pwm)
+{
+       pwm_put(pwm);
+}
+EXPORT_SYMBOL_GPL(pwm_free);
+
+/**
+ * pwm_config() - change a PWM device configuration
+ * @pwm: PWM device
+ * @duty_ns: "on" time (in nanoseconds)
+ * @period_ns: duration (in nanoseconds) of one cycle
+ */
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+       if (!pwm || period_ns == 0 || duty_ns > period_ns)
+               return -EINVAL;
+
+       return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
+}
+EXPORT_SYMBOL_GPL(pwm_config);
+
+/**
+ * pwm_enable() - start a PWM output toggling
+ * @pwm: PWM device
+ */
+int pwm_enable(struct pwm_device *pwm)
+{
+       if (pwm && !test_and_set_bit(PWMF_ENABLED, &pwm->flags))
+               return pwm->chip->ops->enable(pwm->chip, pwm);
+
+       return pwm ? 0 : -EINVAL;
+}
+EXPORT_SYMBOL_GPL(pwm_enable);
+
+/**
+ * pwm_disable() - stop a PWM output toggling
+ * @pwm: PWM device
+ */
+void pwm_disable(struct pwm_device *pwm)
+{
+       if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags))
+               pwm->chip->ops->disable(pwm->chip, pwm);
+}
+EXPORT_SYMBOL_GPL(pwm_disable);
+
+static struct pwm_chip *of_node_to_pwmchip(struct device_node *np)
+{
+       struct pwm_chip *chip;
+
+       mutex_lock(&pwm_lock);
+
+       list_for_each_entry(chip, &pwm_chips, list)
+               if (chip->dev && chip->dev->of_node == np) {
+                       mutex_unlock(&pwm_lock);
+                       return chip;
+               }
+
+       mutex_unlock(&pwm_lock);
+
+       return ERR_PTR(-EPROBE_DEFER);
+}
+
+/**
+ * of_pwm_request() - request a PWM via the PWM framework
+ * @np: device node to get the PWM from
+ * @con_id: consumer name
+ *
+ * Returns the PWM device parsed from the phandle and index specified in the
+ * "pwms" property of a device tree node or a negative error-code on failure.
+ * Values parsed from the device tree are stored in the returned PWM device
+ * object.
+ *
+ * If con_id is NULL, the first PWM device listed in the "pwms" property will
+ * be requested. Otherwise the "pwm-names" property is used to do a reverse
+ * lookup of the PWM index. This also means that the "pwm-names" property
+ * becomes mandatory for devices that look up the PWM device via the con_id
+ * parameter.
+ */
+static struct pwm_device *of_pwm_request(struct device_node *np,
+                                        const char *con_id)
+{
+       struct pwm_device *pwm = NULL;
+       struct of_phandle_args args;
+       struct pwm_chip *pc;
+       int index = 0;
+       int err;
+
+       if (con_id) {
+               index = of_property_match_string(np, "pwm-names", con_id);
+               if (index < 0)
+                       return ERR_PTR(index);
+       }
+
+       err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index,
+                                        &args);
+       if (err) {
+               pr_debug("%s(): can't parse \"pwms\" property\n", __func__);
+               return ERR_PTR(err);
+       }
+
+       pc = of_node_to_pwmchip(args.np);
+       if (IS_ERR(pc)) {
+               pr_debug("%s(): PWM chip not found\n", __func__);
+               pwm = ERR_CAST(pc);
+               goto put;
+       }
+
+       if (args.args_count != pc->of_pwm_n_cells) {
+               pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name,
+                        args.np->full_name);
+               pwm = ERR_PTR(-EINVAL);
+               goto put;
+       }
+
+       pwm = pc->of_xlate(pc, &args);
+       if (IS_ERR(pwm))
+               goto put;
+
+       /*
+        * If a consumer name was not given, try to look it up from the
+        * "pwm-names" property if it exists. Otherwise use the name of
+        * the user device node.
+        */
+       if (!con_id) {
+               err = of_property_read_string_index(np, "pwm-names", index,
+                                                   &con_id);
+               if (err < 0)
+                       con_id = np->name;
+       }
+
+       pwm->label = con_id;
+
+put:
+       of_node_put(args.np);
+
+       return pwm;
+}
+
+/**
+ * pwm_add_table() - register PWM device consumers
+ * @table: array of consumers to register
+ * @num: number of consumers in table
+ */
+void __init pwm_add_table(struct pwm_lookup *table, size_t num)
+{
+       mutex_lock(&pwm_lookup_lock);
+
+       while (num--) {
+               list_add_tail(&table->list, &pwm_lookup_list);
+               table++;
+       }
+
+       mutex_unlock(&pwm_lookup_lock);
+}
+
+/**
+ * pwm_get() - look up and request a PWM device
+ * @dev: device for PWM consumer
+ * @con_id: consumer name
+ *
+ * Lookup is first attempted using DT. If the device was not instantiated from
+ * a device tree, a PWM chip and a relative index is looked up via a table
+ * supplied by board setup code (see pwm_add_table()).
+ *
+ * Once a PWM chip has been found the specified PWM device will be requested
+ * and is ready to be used.
+ */
+struct pwm_device *pwm_get(struct device *dev, const char *con_id)
+{
+       struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER);
+       const char *dev_id = dev ? dev_name(dev): NULL;
+       struct pwm_chip *chip = NULL;
+       unsigned int index = 0;
+       unsigned int best = 0;
+       struct pwm_lookup *p;
+       unsigned int match;
+
+       /* look up via DT first */
+       if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
+               return of_pwm_request(dev->of_node, con_id);
+
+       /*
+        * We look up the provider in the static table typically provided by
+        * board setup code. We first try to lookup the consumer device by
+        * name. If the consumer device was passed in as NULL or if no match
+        * was found, we try to find the consumer by directly looking it up
+        * by name.
+        *
+        * If a match is found, the provider PWM chip is looked up by name
+        * and a PWM device is requested using the PWM device per-chip index.
+        *
+        * The lookup algorithm was shamelessly taken from the clock
+        * framework:
+        *
+        * We do slightly fuzzy matching here:
+        *  An entry with a NULL ID is assumed to be a wildcard.
+        *  If an entry has a device ID, it must match
+        *  If an entry has a connection ID, it must match
+        * Then we take the most specific entry - with the following order
+        * of precedence: dev+con > dev only > con only.
+        */
+       mutex_lock(&pwm_lookup_lock);
+
+       list_for_each_entry(p, &pwm_lookup_list, list) {
+               match = 0;
+
+               if (p->dev_id) {
+                       if (!dev_id || strcmp(p->dev_id, dev_id))
+                               continue;
+
+                       match += 2;
+               }
+
+               if (p->con_id) {
+                       if (!con_id || strcmp(p->con_id, con_id))
+                               continue;
+
+                       match += 1;
+               }
+
+               if (match > best) {
+                       chip = pwmchip_find_by_name(p->provider);
+                       index = p->index;
+
+                       if (match != 3)
+                               best = match;
+                       else
+                               break;
+               }
+       }
+
+       if (chip)
+               pwm = pwm_request_from_chip(chip, index, con_id ?: dev_id);
+
+       mutex_unlock(&pwm_lookup_lock);
+
+       return pwm;
+}
+EXPORT_SYMBOL_GPL(pwm_get);
+
+/**
+ * pwm_put() - release a PWM device
+ * @pwm: PWM device
+ */
+void pwm_put(struct pwm_device *pwm)
+{
+       if (!pwm)
+               return;
+
+       mutex_lock(&pwm_lock);
+
+       if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
+               pr_warning("PWM device already freed\n");
+               goto out;
+       }
+
+       if (pwm->chip->ops->free)
+               pwm->chip->ops->free(pwm->chip, pwm);
+
+       pwm->label = NULL;
+
+       module_put(pwm->chip->ops->owner);
+out:
+       mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL_GPL(pwm_put);
+
+#ifdef CONFIG_DEBUG_FS
+static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
+{
+       unsigned int i;
+
+       for (i = 0; i < chip->npwm; i++) {
+               struct pwm_device *pwm = &chip->pwms[i];
+
+               seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label);
+
+               if (test_bit(PWMF_REQUESTED, &pwm->flags))
+                       seq_printf(s, " requested");
+
+               if (test_bit(PWMF_ENABLED, &pwm->flags))
+                       seq_printf(s, " enabled");
+
+               seq_printf(s, "\n");
+       }
+}
+
+static void *pwm_seq_start(struct seq_file *s, loff_t *pos)
+{
+       mutex_lock(&pwm_lock);
+       s->private = "";
+
+       return seq_list_start(&pwm_chips, *pos);
+}
+
+static void *pwm_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+       s->private = "\n";
+
+       return seq_list_next(v, &pwm_chips, pos);
+}
+
+static void pwm_seq_stop(struct seq_file *s, void *v)
+{
+       mutex_unlock(&pwm_lock);
+}
+
+static int pwm_seq_show(struct seq_file *s, void *v)
+{
+       struct pwm_chip *chip = list_entry(v, struct pwm_chip, list);
+
+       seq_printf(s, "%s%s/%s, %d PWM device%s\n", (char *)s->private,
+                  chip->dev->bus ? chip->dev->bus->name : "no-bus",
+                  dev_name(chip->dev), chip->npwm,
+                  (chip->npwm != 1) ? "s" : "");
+
+       if (chip->ops->dbg_show)
+               chip->ops->dbg_show(chip, s);
+       else
+               pwm_dbg_show(chip, s);
+
+       return 0;
+}
+
+static const struct seq_operations pwm_seq_ops = {
+       .start = pwm_seq_start,
+       .next = pwm_seq_next,
+       .stop = pwm_seq_stop,
+       .show = pwm_seq_show,
+};
+
+static int pwm_seq_open(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &pwm_seq_ops);
+}
+
+static const struct file_operations pwm_debugfs_ops = {
+       .owner = THIS_MODULE,
+       .open = pwm_seq_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = seq_release,
+};
+
+static int __init pwm_debugfs_init(void)
+{
+       debugfs_create_file("pwm", S_IFREG | S_IRUGO, NULL, NULL,
+                           &pwm_debugfs_ops);
+
+       return 0;
+}
+
+subsys_initcall(pwm_debugfs_init);
+#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/pwm/pwm-bfin.c b/drivers/pwm/pwm-bfin.c
new file mode 100644 (file)
index 0000000..d53c4e7
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Blackfin Pulse Width Modulation (PWM) core
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#include <asm/gptimers.h>
+#include <asm/portmux.h>
+
+struct bfin_pwm_chip {
+       struct pwm_chip chip;
+};
+
+struct bfin_pwm {
+       unsigned short pin;
+};
+
+static const unsigned short pwm_to_gptimer_per[] = {
+       P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR4, P_TMR5,
+       P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11,
+};
+
+static int bfin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct bfin_pwm *priv;
+       int ret;
+
+       if (pwm->hwpwm >= ARRAY_SIZE(pwm_to_gptimer_per))
+               return -EINVAL;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->pin = pwm_to_gptimer_per[pwm->hwpwm];
+
+       ret = peripheral_request(priv->pin, NULL);
+       if (ret) {
+               kfree(priv);
+               return ret;
+       }
+
+       pwm_set_chip_data(pwm, priv);
+
+       return 0;
+}
+
+static void bfin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+       if (priv) {
+               peripheral_free(priv->pin);
+               kfree(priv);
+       }
+}
+
+static int bfin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+               int duty_ns, int period_ns)
+{
+       struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+       unsigned long period, duty;
+       unsigned long long val;
+
+       if (duty_ns < 0 || duty_ns > period_ns)
+               return -EINVAL;
+
+       val = (unsigned long long)get_sclk() * period_ns;
+       do_div(val, NSEC_PER_SEC);
+       period = val;
+
+       val = (unsigned long long)period * duty_ns;
+       do_div(val, period_ns);
+       duty = period - val;
+
+       if (duty >= period)
+               duty = period - 1;
+
+       set_gptimer_config(priv->pin, TIMER_MODE_PWM | TIMER_PERIOD_CNT);
+       set_gptimer_pwidth(priv->pin, duty);
+       set_gptimer_period(priv->pin, period);
+
+       return 0;
+}
+
+static int bfin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+       enable_gptimer(priv->pin);
+
+       return 0;
+}
+
+static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+       disable_gptimer(priv->pin);
+}
+
+static struct pwm_ops bfin_pwm_ops = {
+       .request = bfin_pwm_request,
+       .free = bfin_pwm_free,
+       .config = bfin_pwm_config,
+       .enable = bfin_pwm_enable,
+       .disable = bfin_pwm_disable,
+       .owner = THIS_MODULE,
+};
+
+static int bfin_pwm_probe(struct platform_device *pdev)
+{
+       struct bfin_pwm_chip *pwm;
+       int ret;
+
+       pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+       if (!pwm) {
+               dev_err(&pdev->dev, "failed to allocate memory\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(pdev, pwm);
+
+       pwm->chip.dev = &pdev->dev;
+       pwm->chip.ops = &bfin_pwm_ops;
+       pwm->chip.base = -1;
+       pwm->chip.npwm = 12;
+
+       ret = pwmchip_add(&pwm->chip);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int __devexit bfin_pwm_remove(struct platform_device *pdev)
+{
+       struct bfin_pwm_chip *pwm = platform_get_drvdata(pdev);
+
+       return pwmchip_remove(&pwm->chip);
+}
+
+static struct platform_driver bfin_pwm_driver = {
+       .driver = {
+               .name = "bfin-pwm",
+       },
+       .probe = bfin_pwm_probe,
+       .remove = __devexit_p(bfin_pwm_remove),
+};
+
+module_platform_driver(bfin_pwm_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
new file mode 100644 (file)
index 0000000..2a0b353
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * simple driver for PWM (Pulse Width Modulator) controller
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <mach/hardware.h>
+
+
+/* i.MX1 and i.MX21 share the same PWM function block: */
+
+#define MX1_PWMC    0x00   /* PWM Control Register */
+#define MX1_PWMS    0x04   /* PWM Sample Register */
+#define MX1_PWMP    0x08   /* PWM Period Register */
+
+
+/* i.MX27, i.MX31, i.MX35 share the same PWM function block: */
+
+#define MX3_PWMCR                 0x00    /* PWM Control Register */
+#define MX3_PWMSAR                0x0C    /* PWM Sample Register */
+#define MX3_PWMPR                 0x10    /* PWM Period Register */
+#define MX3_PWMCR_PRESCALER(x)    (((x - 1) & 0xFFF) << 4)
+#define MX3_PWMCR_DOZEEN                (1 << 24)
+#define MX3_PWMCR_WAITEN                (1 << 23)
+#define MX3_PWMCR_DBGEN                        (1 << 22)
+#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16)
+#define MX3_PWMCR_CLKSRC_IPG      (1 << 16)
+#define MX3_PWMCR_EN              (1 << 0)
+
+struct imx_chip {
+       struct clk      *clk;
+
+       int             clk_enabled;
+       void __iomem    *mmio_base;
+
+       struct pwm_chip chip;
+};
+
+#define to_imx_chip(chip)      container_of(chip, struct imx_chip, chip)
+
+static int imx_pwm_config(struct pwm_chip *chip,
+               struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+       struct imx_chip *imx = to_imx_chip(chip);
+
+       if (!(cpu_is_mx1() || cpu_is_mx21())) {
+               unsigned long long c;
+               unsigned long period_cycles, duty_cycles, prescale;
+               u32 cr;
+
+               c = clk_get_rate(imx->clk);
+               c = c * period_ns;
+               do_div(c, 1000000000);
+               period_cycles = c;
+
+               prescale = period_cycles / 0x10000 + 1;
+
+               period_cycles /= prescale;
+               c = (unsigned long long)period_cycles * duty_ns;
+               do_div(c, period_ns);
+               duty_cycles = c;
+
+               /*
+                * according to imx pwm RM, the real period value should be
+                * PERIOD value in PWMPR plus 2.
+                */
+               if (period_cycles > 2)
+                       period_cycles -= 2;
+               else
+                       period_cycles = 0;
+
+               writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
+               writel(period_cycles, imx->mmio_base + MX3_PWMPR);
+
+               cr = MX3_PWMCR_PRESCALER(prescale) |
+                       MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
+                       MX3_PWMCR_DBGEN | MX3_PWMCR_EN;
+
+               if (cpu_is_mx25())
+                       cr |= MX3_PWMCR_CLKSRC_IPG;
+               else
+                       cr |= MX3_PWMCR_CLKSRC_IPG_HIGH;
+
+               writel(cr, imx->mmio_base + MX3_PWMCR);
+       } else if (cpu_is_mx1() || cpu_is_mx21()) {
+               /* The PWM subsystem allows for exact frequencies. However,
+                * I cannot connect a scope on my device to the PWM line and
+                * thus cannot provide the program the PWM controller
+                * exactly. Instead, I'm relying on the fact that the
+                * Bootloader (u-boot or WinCE+haret) has programmed the PWM
+                * function group already. So I'll just modify the PWM sample
+                * register to follow the ratio of duty_ns vs. period_ns
+                * accordingly.
+                *
+                * This is good enough for programming the brightness of
+                * the LCD backlight.
+                *
+                * The real implementation would divide PERCLK[0] first by
+                * both the prescaler (/1 .. /128) and then by CLKSEL
+                * (/2 .. /16).
+                */
+               u32 max = readl(imx->mmio_base + MX1_PWMP);
+               u32 p = max * duty_ns / period_ns;
+               writel(max - p, imx->mmio_base + MX1_PWMS);
+       } else {
+               BUG();
+       }
+
+       return 0;
+}
+
+static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct imx_chip *imx = to_imx_chip(chip);
+       int rc = 0;
+
+       if (!imx->clk_enabled) {
+               rc = clk_prepare_enable(imx->clk);
+               if (!rc)
+                       imx->clk_enabled = 1;
+       }
+       return rc;
+}
+
+static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct imx_chip *imx = to_imx_chip(chip);
+
+       writel(0, imx->mmio_base + MX3_PWMCR);
+
+       if (imx->clk_enabled) {
+               clk_disable_unprepare(imx->clk);
+               imx->clk_enabled = 0;
+       }
+}
+
+static struct pwm_ops imx_pwm_ops = {
+       .enable = imx_pwm_enable,
+       .disable = imx_pwm_disable,
+       .config = imx_pwm_config,
+       .owner = THIS_MODULE,
+};
+
+static int __devinit imx_pwm_probe(struct platform_device *pdev)
+{
+       struct imx_chip *imx;
+       struct resource *r;
+       int ret = 0;
+
+       imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
+       if (imx == NULL) {
+               dev_err(&pdev->dev, "failed to allocate memory\n");
+               return -ENOMEM;
+       }
+
+       imx->clk = devm_clk_get(&pdev->dev, "pwm");
+
+       if (IS_ERR(imx->clk))
+               return PTR_ERR(imx->clk);
+
+       imx->chip.ops = &imx_pwm_ops;
+       imx->chip.dev = &pdev->dev;
+       imx->chip.base = -1;
+       imx->chip.npwm = 1;
+
+       imx->clk_enabled = 0;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (r == NULL) {
+               dev_err(&pdev->dev, "no memory resource defined\n");
+               return -ENODEV;
+       }
+
+       imx->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+       if (imx->mmio_base == NULL)
+               return -EADDRNOTAVAIL;
+
+       ret = pwmchip_add(&imx->chip);
+       if (ret < 0)
+               return ret;
+
+       platform_set_drvdata(pdev, imx);
+       return 0;
+}
+
+static int __devexit imx_pwm_remove(struct platform_device *pdev)
+{
+       struct imx_chip *imx;
+
+       imx = platform_get_drvdata(pdev);
+       if (imx == NULL)
+               return -ENODEV;
+
+       return pwmchip_remove(&imx->chip);
+}
+
+static struct platform_driver imx_pwm_driver = {
+       .driver         = {
+               .name   = "mxc_pwm",
+       },
+       .probe          = imx_pwm_probe,
+       .remove         = __devexit_p(imx_pwm_remove),
+};
+
+static int __init imx_pwm_init(void)
+{
+       return platform_driver_register(&imx_pwm_driver);
+}
+arch_initcall(imx_pwm_init);
+
+static void __exit imx_pwm_exit(void)
+{
+       platform_driver_unregister(&imx_pwm_driver);
+}
+module_exit(imx_pwm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c
new file mode 100644 (file)
index 0000000..adb87f0
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2012 Alexandre Pereira da Silva <aletes.xgr@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; version 2.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+struct lpc32xx_pwm_chip {
+       struct pwm_chip chip;
+       struct clk *clk;
+       void __iomem *base;
+};
+
+#define PWM_ENABLE     (1 << 31)
+#define PWM_RELOADV(x) (((x) & 0xFF) << 8)
+#define PWM_DUTY(x)    ((x) & 0xFF)
+
+#define to_lpc32xx_pwm_chip(_chip) \
+       container_of(_chip, struct lpc32xx_pwm_chip, chip)
+
+static int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+                             int duty_ns, int period_ns)
+{
+       struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
+       unsigned long long c;
+       int period_cycles, duty_cycles;
+
+       c = clk_get_rate(lpc32xx->clk) / 256;
+       c = c * period_ns;
+       do_div(c, NSEC_PER_SEC);
+
+       /* Handle high and low extremes */
+       if (c == 0)
+               c = 1;
+       if (c > 255)
+               c = 0; /* 0 set division by 256 */
+       period_cycles = c;
+
+       c = 256 * duty_ns;
+       do_div(c, period_ns);
+       duty_cycles = c;
+
+       writel(PWM_ENABLE | PWM_RELOADV(period_cycles) | PWM_DUTY(duty_cycles),
+               lpc32xx->base + (pwm->hwpwm << 2));
+
+       return 0;
+}
+
+static int lpc32xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
+
+       return clk_enable(lpc32xx->clk);
+}
+
+static void lpc32xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
+
+       writel(0, lpc32xx->base + (pwm->hwpwm << 2));
+       clk_disable(lpc32xx->clk);
+}
+
+static const struct pwm_ops lpc32xx_pwm_ops = {
+       .config = lpc32xx_pwm_config,
+       .enable = lpc32xx_pwm_enable,
+       .disable = lpc32xx_pwm_disable,
+       .owner = THIS_MODULE,
+};
+
+static int lpc32xx_pwm_probe(struct platform_device *pdev)
+{
+       struct lpc32xx_pwm_chip *lpc32xx;
+       struct resource *res;
+       int ret;
+
+       lpc32xx = devm_kzalloc(&pdev->dev, sizeof(*lpc32xx), GFP_KERNEL);
+       if (!lpc32xx)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -EINVAL;
+
+       lpc32xx->base = devm_request_and_ioremap(&pdev->dev, res);
+       if (!lpc32xx->base)
+               return -EADDRNOTAVAIL;
+
+       lpc32xx->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(lpc32xx->clk))
+               return PTR_ERR(lpc32xx->clk);
+
+       lpc32xx->chip.dev = &pdev->dev;
+       lpc32xx->chip.ops = &lpc32xx_pwm_ops;
+       lpc32xx->chip.npwm = 2;
+
+       ret = pwmchip_add(&lpc32xx->chip);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to add PWM chip, error %d\n", ret);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, lpc32xx);
+
+       return 0;
+}
+
+static int __devexit lpc32xx_pwm_remove(struct platform_device *pdev)
+{
+       struct lpc32xx_pwm_chip *lpc32xx = platform_get_drvdata(pdev);
+
+       clk_disable(lpc32xx->clk);
+       return pwmchip_remove(&lpc32xx->chip);
+}
+
+static struct of_device_id lpc32xx_pwm_dt_ids[] = {
+       { .compatible = "nxp,lpc3220-pwm", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_pwm_dt_ids);
+
+static struct platform_driver lpc32xx_pwm_driver = {
+       .driver = {
+               .name = "lpc32xx-pwm",
+               .of_match_table = of_match_ptr(lpc32xx_pwm_dt_ids),
+       },
+       .probe = lpc32xx_pwm_probe,
+       .remove = __devexit_p(lpc32xx_pwm_remove),
+};
+module_platform_driver(lpc32xx_pwm_driver);
+
+MODULE_ALIAS("platform:lpc32xx-pwm");
+MODULE_AUTHOR("Alexandre Pereira da Silva <aletes.xgr@gmail.com>");
+MODULE_DESCRIPTION("LPC32XX PWM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c
new file mode 100644 (file)
index 0000000..e585264
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/stmp_device.h>
+
+#define SET    0x4
+#define CLR    0x8
+#define TOG    0xc
+
+#define PWM_CTRL               0x0
+#define PWM_ACTIVE0            0x10
+#define PWM_PERIOD0            0x20
+#define  PERIOD_PERIOD(p)      ((p) & 0xffff)
+#define  PERIOD_PERIOD_MAX     0x10000
+#define  PERIOD_ACTIVE_HIGH    (3 << 16)
+#define  PERIOD_INACTIVE_LOW   (2 << 18)
+#define  PERIOD_CDIV(div)      (((div) & 0x7) << 20)
+#define  PERIOD_CDIV_MAX       8
+
+struct mxs_pwm_chip {
+       struct pwm_chip chip;
+       struct device *dev;
+       struct clk *clk;
+       void __iomem *base;
+};
+
+#define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip)
+
+static int mxs_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+                         int duty_ns, int period_ns)
+{
+       struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
+       int ret, div = 0;
+       unsigned int period_cycles, duty_cycles;
+       unsigned long rate;
+       unsigned long long c;
+
+       rate = clk_get_rate(mxs->clk);
+       while (1) {
+               c = rate / (1 << div);
+               c = c * period_ns;
+               do_div(c, 1000000000);
+               if (c < PERIOD_PERIOD_MAX)
+                       break;
+               div++;
+               if (div > PERIOD_CDIV_MAX)
+                       return -EINVAL;
+       }
+
+       period_cycles = c;
+       c *= duty_ns;
+       do_div(c, period_ns);
+       duty_cycles = c;
+
+       /*
+        * If the PWM channel is disabled, make sure to turn on the clock
+        * before writing the register. Otherwise, keep it enabled.
+        */
+       if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+               ret = clk_prepare_enable(mxs->clk);
+               if (ret)
+                       return ret;
+       }
+
+       writel(duty_cycles << 16,
+                       mxs->base + PWM_ACTIVE0 + pwm->hwpwm * 0x20);
+       writel(PERIOD_PERIOD(period_cycles) | PERIOD_ACTIVE_HIGH |
+              PERIOD_INACTIVE_LOW | PERIOD_CDIV(div),
+                       mxs->base + PWM_PERIOD0 + pwm->hwpwm * 0x20);
+
+       /*
+        * If the PWM is not enabled, turn the clock off again to save power.
+        */
+       if (!test_bit(PWMF_ENABLED, &pwm->flags))
+               clk_disable_unprepare(mxs->clk);
+
+       return 0;
+}
+
+static int mxs_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
+       int ret;
+
+       ret = clk_prepare_enable(mxs->clk);
+       if (ret)
+               return ret;
+
+       writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + SET);
+
+       return 0;
+}
+
+static void mxs_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
+
+       writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + CLR);
+
+       clk_disable_unprepare(mxs->clk);
+}
+
+static const struct pwm_ops mxs_pwm_ops = {
+       .config = mxs_pwm_config,
+       .enable = mxs_pwm_enable,
+       .disable = mxs_pwm_disable,
+       .owner = THIS_MODULE,
+};
+
+static int mxs_pwm_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct mxs_pwm_chip *mxs;
+       struct resource *res;
+       struct pinctrl *pinctrl;
+       int ret;
+
+       mxs = devm_kzalloc(&pdev->dev, sizeof(*mxs), GFP_KERNEL);
+       if (!mxs)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       mxs->base = devm_request_and_ioremap(&pdev->dev, res);
+       if (!mxs->base)
+               return -EADDRNOTAVAIL;
+
+       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       if (IS_ERR(pinctrl))
+               return PTR_ERR(pinctrl);
+
+       mxs->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(mxs->clk))
+               return PTR_ERR(mxs->clk);
+
+       mxs->chip.dev = &pdev->dev;
+       mxs->chip.ops = &mxs_pwm_ops;
+       mxs->chip.base = -1;
+       ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to get pwm number: %d\n", ret);
+               return ret;
+       }
+
+       ret = pwmchip_add(&mxs->chip);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to add pwm chip %d\n", ret);
+               return ret;
+       }
+
+       mxs->dev = &pdev->dev;
+       platform_set_drvdata(pdev, mxs);
+
+       stmp_reset_block(mxs->base);
+
+       return 0;
+}
+
+static int __devexit mxs_pwm_remove(struct platform_device *pdev)
+{
+       struct mxs_pwm_chip *mxs = platform_get_drvdata(pdev);
+
+       return pwmchip_remove(&mxs->chip);
+}
+
+static struct of_device_id mxs_pwm_dt_ids[] = {
+       { .compatible = "fsl,imx23-pwm", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_pwm_dt_ids);
+
+static struct platform_driver mxs_pwm_driver = {
+       .driver = {
+               .name = "mxs-pwm",
+               .of_match_table = of_match_ptr(mxs_pwm_dt_ids),
+       },
+       .probe = mxs_pwm_probe,
+       .remove = __devexit_p(mxs_pwm_remove),
+};
+module_platform_driver(mxs_pwm_driver);
+
+MODULE_ALIAS("platform:mxs-pwm");
+MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
+MODULE_DESCRIPTION("Freescale MXS PWM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c
new file mode 100644 (file)
index 0000000..bd5867a
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * drivers/pwm/pwm-pxa.c
+ *
+ * simple driver for PWM (Pulse Width Modulator) controller
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 2008-02-13  initial version
+ *             eric miao <eric.miao@marvell.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#include <asm/div64.h>
+
+#define HAS_SECONDARY_PWM      0x10
+#define PWM_ID_BASE(d)         ((d) & 0xf)
+
+static const struct platform_device_id pwm_id_table[] = {
+       /*   PWM    has_secondary_pwm? */
+       { "pxa25x-pwm", 0 },
+       { "pxa27x-pwm", 0 | HAS_SECONDARY_PWM },
+       { "pxa168-pwm", 1 },
+       { "pxa910-pwm", 1 },
+       { },
+};
+MODULE_DEVICE_TABLE(platform, pwm_id_table);
+
+/* PWM registers and bits definitions */
+#define PWMCR          (0x00)
+#define PWMDCR         (0x04)
+#define PWMPCR         (0x08)
+
+#define PWMCR_SD       (1 << 6)
+#define PWMDCR_FD      (1 << 10)
+
+struct pxa_pwm_chip {
+       struct pwm_chip chip;
+       struct device   *dev;
+
+       struct clk      *clk;
+       int             clk_enabled;
+       void __iomem    *mmio_base;
+};
+
+static inline struct pxa_pwm_chip *to_pxa_pwm_chip(struct pwm_chip *chip)
+{
+       return container_of(chip, struct pxa_pwm_chip, chip);
+}
+
+/*
+ * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
+ * duty_ns   = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
+ */
+static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+                         int duty_ns, int period_ns)
+{
+       struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
+       unsigned long long c;
+       unsigned long period_cycles, prescale, pv, dc;
+       unsigned long offset;
+       int rc;
+
+       if (period_ns == 0 || duty_ns > period_ns)
+               return -EINVAL;
+
+       offset = pwm->hwpwm ? 0x10 : 0;
+
+       c = clk_get_rate(pc->clk);
+       c = c * period_ns;
+       do_div(c, 1000000000);
+       period_cycles = c;
+
+       if (period_cycles < 1)
+               period_cycles = 1;
+       prescale = (period_cycles - 1) / 1024;
+       pv = period_cycles / (prescale + 1) - 1;
+
+       if (prescale > 63)
+               return -EINVAL;
+
+       if (duty_ns == period_ns)
+               dc = PWMDCR_FD;
+       else
+               dc = (pv + 1) * duty_ns / period_ns;
+
+       /* NOTE: the clock to PWM has to be enabled first
+        * before writing to the registers
+        */
+       rc = clk_prepare_enable(pc->clk);
+       if (rc < 0)
+               return rc;
+
+       writel(prescale, pc->mmio_base + offset + PWMCR);
+       writel(dc, pc->mmio_base + offset + PWMDCR);
+       writel(pv, pc->mmio_base + offset + PWMPCR);
+
+       clk_disable_unprepare(pc->clk);
+       return 0;
+}
+
+static int pxa_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
+       int rc = 0;
+
+       if (!pc->clk_enabled) {
+               rc = clk_prepare_enable(pc->clk);
+               if (!rc)
+                       pc->clk_enabled++;
+       }
+       return rc;
+}
+
+static void pxa_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
+
+       if (pc->clk_enabled) {
+               clk_disable_unprepare(pc->clk);
+               pc->clk_enabled--;
+       }
+}
+
+static struct pwm_ops pxa_pwm_ops = {
+       .config = pxa_pwm_config,
+       .enable = pxa_pwm_enable,
+       .disable = pxa_pwm_disable,
+       .owner = THIS_MODULE,
+};
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+       const struct platform_device_id *id = platform_get_device_id(pdev);
+       struct pxa_pwm_chip *pwm;
+       struct resource *r;
+       int ret = 0;
+
+       pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+       if (pwm == NULL) {
+               dev_err(&pdev->dev, "failed to allocate memory\n");
+               return -ENOMEM;
+       }
+
+       pwm->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(pwm->clk))
+               return PTR_ERR(pwm->clk);
+
+       pwm->clk_enabled = 0;
+
+       pwm->chip.dev = &pdev->dev;
+       pwm->chip.ops = &pxa_pwm_ops;
+       pwm->chip.base = -1;
+       pwm->chip.npwm = (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (r == NULL) {
+               dev_err(&pdev->dev, "no memory resource defined\n");
+               return -ENODEV;
+       }
+
+       pwm->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+       if (pwm->mmio_base == NULL)
+               return -EADDRNOTAVAIL;
+
+       ret = pwmchip_add(&pwm->chip);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, pwm);
+       return 0;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+       struct pxa_pwm_chip *chip;
+
+       chip = platform_get_drvdata(pdev);
+       if (chip == NULL)
+               return -ENODEV;
+
+       return pwmchip_remove(&chip->chip);
+}
+
+static struct platform_driver pwm_driver = {
+       .driver         = {
+               .name   = "pxa25x-pwm",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = pwm_probe,
+       .remove         = __devexit_p(pwm_remove),
+       .id_table       = pwm_id_table,
+};
+
+static int __init pwm_init(void)
+{
+       return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+       platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c
new file mode 100644 (file)
index 0000000..d103865
--- /dev/null
@@ -0,0 +1,356 @@
+/* drivers/pwm/pwm-samsung.c
+ *
+ * Copyright (c) 2007 Ben Dooks
+ * Copyright (c) 2008 Simtec Electronics
+ *     Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org>
+ *
+ * S3C series PWM device core
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+*/
+
+#define pr_fmt(fmt) "pwm-samsung: " fmt
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#include <mach/map.h>
+
+#include <plat/regs-timer.h>
+
+struct s3c_chip {
+       struct platform_device  *pdev;
+
+       struct clk              *clk_div;
+       struct clk              *clk;
+       const char              *label;
+
+       unsigned int             period_ns;
+       unsigned int             duty_ns;
+
+       unsigned char            tcon_base;
+       unsigned char            pwm_id;
+       struct pwm_chip          chip;
+};
+
+#define to_s3c_chip(chip)      container_of(chip, struct s3c_chip, chip)
+
+#define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg)
+
+static struct clk *clk_scaler[2];
+
+static inline int pwm_is_tdiv(struct s3c_chip *chip)
+{
+       return clk_get_parent(chip->clk) == chip->clk_div;
+}
+
+#define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0))
+#define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2))
+#define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3))
+#define pwm_tcon_manulupdate(pwm) (1 << (pwm->tcon_base + 1))
+
+static int s3c_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct s3c_chip *s3c = to_s3c_chip(chip);
+       unsigned long flags;
+       unsigned long tcon;
+
+       local_irq_save(flags);
+
+       tcon = __raw_readl(S3C2410_TCON);
+       tcon |= pwm_tcon_start(s3c);
+       __raw_writel(tcon, S3C2410_TCON);
+
+       local_irq_restore(flags);
+
+       return 0;
+}
+
+static void s3c_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct s3c_chip *s3c = to_s3c_chip(chip);
+       unsigned long flags;
+       unsigned long tcon;
+
+       local_irq_save(flags);
+
+       tcon = __raw_readl(S3C2410_TCON);
+       tcon &= ~pwm_tcon_start(s3c);
+       __raw_writel(tcon, S3C2410_TCON);
+
+       local_irq_restore(flags);
+}
+
+static unsigned long pwm_calc_tin(struct s3c_chip *s3c, unsigned long freq)
+{
+       unsigned long tin_parent_rate;
+       unsigned int div;
+
+       tin_parent_rate = clk_get_rate(clk_get_parent(s3c->clk_div));
+       pwm_dbg(s3c, "tin parent at %lu\n", tin_parent_rate);
+
+       for (div = 2; div <= 16; div *= 2) {
+               if ((tin_parent_rate / (div << 16)) < freq)
+                       return tin_parent_rate / div;
+       }
+
+       return tin_parent_rate / 16;
+}
+
+#define NS_IN_HZ (1000000000UL)
+
+static int s3c_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+               int duty_ns, int period_ns)
+{
+       struct s3c_chip *s3c = to_s3c_chip(chip);
+       unsigned long tin_rate;
+       unsigned long tin_ns;
+       unsigned long period;
+       unsigned long flags;
+       unsigned long tcon;
+       unsigned long tcnt;
+       long tcmp;
+
+       /* We currently avoid using 64bit arithmetic by using the
+        * fact that anything faster than 1Hz is easily representable
+        * by 32bits. */
+
+       if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ)
+               return -ERANGE;
+
+       if (duty_ns > period_ns)
+               return -EINVAL;
+
+       if (period_ns == s3c->period_ns &&
+           duty_ns == s3c->duty_ns)
+               return 0;
+
+       /* The TCMP and TCNT can be read without a lock, they're not
+        * shared between the timers. */
+
+       tcmp = __raw_readl(S3C2410_TCMPB(s3c->pwm_id));
+       tcnt = __raw_readl(S3C2410_TCNTB(s3c->pwm_id));
+
+       period = NS_IN_HZ / period_ns;
+
+       pwm_dbg(s3c, "duty_ns=%d, period_ns=%d (%lu)\n",
+               duty_ns, period_ns, period);
+
+       /* Check to see if we are changing the clock rate of the PWM */
+
+       if (s3c->period_ns != period_ns) {
+               if (pwm_is_tdiv(s3c)) {
+                       tin_rate = pwm_calc_tin(s3c, period);
+                       clk_set_rate(s3c->clk_div, tin_rate);
+               } else
+                       tin_rate = clk_get_rate(s3c->clk);
+
+               s3c->period_ns = period_ns;
+
+               pwm_dbg(s3c, "tin_rate=%lu\n", tin_rate);
+
+               tin_ns = NS_IN_HZ / tin_rate;
+               tcnt = period_ns / tin_ns;
+       } else
+               tin_ns = NS_IN_HZ / clk_get_rate(s3c->clk);
+
+       /* Note, counters count down */
+
+       tcmp = duty_ns / tin_ns;
+       tcmp = tcnt - tcmp;
+       /* the pwm hw only checks the compare register after a decrement,
+          so the pin never toggles if tcmp = tcnt */
+       if (tcmp == tcnt)
+               tcmp--;
+
+       pwm_dbg(s3c, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt);
+
+       if (tcmp < 0)
+               tcmp = 0;
+
+       /* Update the PWM register block. */
+
+       local_irq_save(flags);
+
+       __raw_writel(tcmp, S3C2410_TCMPB(s3c->pwm_id));
+       __raw_writel(tcnt, S3C2410_TCNTB(s3c->pwm_id));
+
+       tcon = __raw_readl(S3C2410_TCON);
+       tcon |= pwm_tcon_manulupdate(s3c);
+       tcon |= pwm_tcon_autoreload(s3c);
+       __raw_writel(tcon, S3C2410_TCON);
+
+       tcon &= ~pwm_tcon_manulupdate(s3c);
+       __raw_writel(tcon, S3C2410_TCON);
+
+       local_irq_restore(flags);
+
+       return 0;
+}
+
+static struct pwm_ops s3c_pwm_ops = {
+       .enable = s3c_pwm_enable,
+       .disable = s3c_pwm_disable,
+       .config = s3c_pwm_config,
+       .owner = THIS_MODULE,
+};
+
+static int s3c_pwm_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct s3c_chip *s3c;
+       unsigned long flags;
+       unsigned long tcon;
+       unsigned int id = pdev->id;
+       int ret;
+
+       if (id == 4) {
+               dev_err(dev, "TIMER4 is currently not supported\n");
+               return -ENXIO;
+       }
+
+       s3c = devm_kzalloc(&pdev->dev, sizeof(*s3c), GFP_KERNEL);
+       if (s3c == NULL) {
+               dev_err(dev, "failed to allocate pwm_device\n");
+               return -ENOMEM;
+       }
+
+       /* calculate base of control bits in TCON */
+       s3c->tcon_base = id == 0 ? 0 : (id * 4) + 4;
+       s3c->chip.ops = &s3c_pwm_ops;
+       s3c->chip.base = -1;
+       s3c->chip.npwm = 1;
+
+       s3c->clk = devm_clk_get(dev, "pwm-tin");
+       if (IS_ERR(s3c->clk)) {
+               dev_err(dev, "failed to get pwm tin clk\n");
+               return PTR_ERR(s3c->clk);
+       }
+
+       s3c->clk_div = devm_clk_get(dev, "pwm-tdiv");
+       if (IS_ERR(s3c->clk_div)) {
+               dev_err(dev, "failed to get pwm tdiv clk\n");
+               return PTR_ERR(s3c->clk_div);
+       }
+
+       clk_enable(s3c->clk);
+       clk_enable(s3c->clk_div);
+
+       local_irq_save(flags);
+
+       tcon = __raw_readl(S3C2410_TCON);
+       tcon |= pwm_tcon_invert(s3c);
+       __raw_writel(tcon, S3C2410_TCON);
+
+       local_irq_restore(flags);
+
+       ret = pwmchip_add(&s3c->chip);
+       if (ret < 0) {
+               dev_err(dev, "failed to register pwm\n");
+               goto err_clk_tdiv;
+       }
+
+       pwm_dbg(s3c, "config bits %02x\n",
+               (__raw_readl(S3C2410_TCON) >> s3c->tcon_base) & 0x0f);
+
+       dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n",
+                clk_get_rate(s3c->clk),
+                clk_get_rate(s3c->clk_div),
+                pwm_is_tdiv(s3c) ? "div" : "ext", s3c->tcon_base);
+
+       platform_set_drvdata(pdev, s3c);
+       return 0;
+
+ err_clk_tdiv:
+       clk_disable(s3c->clk_div);
+       clk_disable(s3c->clk);
+       return ret;
+}
+
+static int __devexit s3c_pwm_remove(struct platform_device *pdev)
+{
+       struct s3c_chip *s3c = platform_get_drvdata(pdev);
+       int err;
+
+       err = pwmchip_remove(&s3c->chip);
+       if (err < 0)
+               return err;
+
+       clk_disable(s3c->clk_div);
+       clk_disable(s3c->clk);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct s3c_chip *s3c = platform_get_drvdata(pdev);
+
+       /* No one preserve these values during suspend so reset them
+        * Otherwise driver leaves PWM unconfigured if same values
+        * passed to pwm_config
+        */
+       s3c->period_ns = 0;
+       s3c->duty_ns = 0;
+
+       return 0;
+}
+
+static int s3c_pwm_resume(struct platform_device *pdev)
+{
+       struct s3c_chip *s3c = platform_get_drvdata(pdev);
+       unsigned long tcon;
+
+       /* Restore invertion */
+       tcon = __raw_readl(S3C2410_TCON);
+       tcon |= pwm_tcon_invert(s3c);
+       __raw_writel(tcon, S3C2410_TCON);
+
+       return 0;
+}
+
+#else
+#define s3c_pwm_suspend NULL
+#define s3c_pwm_resume NULL
+#endif
+
+static struct platform_driver s3c_pwm_driver = {
+       .driver         = {
+               .name   = "s3c24xx-pwm",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = s3c_pwm_probe,
+       .remove         = __devexit_p(s3c_pwm_remove),
+       .suspend        = s3c_pwm_suspend,
+       .resume         = s3c_pwm_resume,
+};
+
+static int __init pwm_init(void)
+{
+       int ret;
+
+       clk_scaler[0] = clk_get(NULL, "pwm-scaler0");
+       clk_scaler[1] = clk_get(NULL, "pwm-scaler1");
+
+       if (IS_ERR(clk_scaler[0]) || IS_ERR(clk_scaler[1])) {
+               pr_err("failed to get scaler clocks\n");
+               return -EINVAL;
+       }
+
+       ret = platform_driver_register(&s3c_pwm_driver);
+       if (ret)
+               pr_err("failed to add pwm driver\n");
+
+       return ret;
+}
+
+arch_initcall(pwm_init);
diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
new file mode 100644 (file)
index 0000000..02ce18d
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * drivers/pwm/pwm-tegra.c
+ *
+ * Tegra pulse-width-modulation controller driver
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ * Based on arch/arm/plat-mxc/pwm.c by Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pwm.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define PWM_ENABLE     (1 << 31)
+#define PWM_DUTY_WIDTH 8
+#define PWM_DUTY_SHIFT 16
+#define PWM_SCALE_WIDTH        13
+#define PWM_SCALE_SHIFT        0
+
+#define NUM_PWM 4
+
+struct tegra_pwm_chip {
+       struct pwm_chip         chip;
+       struct device           *dev;
+
+       struct clk              *clk;
+
+       void __iomem            *mmio_base;
+};
+
+static inline struct tegra_pwm_chip *to_tegra_pwm_chip(struct pwm_chip *chip)
+{
+       return container_of(chip, struct tegra_pwm_chip, chip);
+}
+
+static inline u32 pwm_readl(struct tegra_pwm_chip *chip, unsigned int num)
+{
+       return readl(chip->mmio_base + (num << 4));
+}
+
+static inline void pwm_writel(struct tegra_pwm_chip *chip, unsigned int num,
+                            unsigned long val)
+{
+       writel(val, chip->mmio_base + (num << 4));
+}
+
+static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+                           int duty_ns, int period_ns)
+{
+       struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+       unsigned long long c;
+       unsigned long rate, hz;
+       u32 val = 0;
+       int err;
+
+       /*
+        * Convert from duty_ns / period_ns to a fixed number of duty ticks
+        * per (1 << PWM_DUTY_WIDTH) cycles and make sure to round to the
+        * nearest integer during division.
+        */
+       c = duty_ns * ((1 << PWM_DUTY_WIDTH) - 1) + period_ns / 2;
+       do_div(c, period_ns);
+
+       val = (u32)c << PWM_DUTY_SHIFT;
+
+       /*
+        * Compute the prescaler value for which (1 << PWM_DUTY_WIDTH)
+        * cycles at the PWM clock rate will take period_ns nanoseconds.
+        */
+       rate = clk_get_rate(pc->clk) >> PWM_DUTY_WIDTH;
+       hz = 1000000000ul / period_ns;
+
+       rate = (rate + (hz / 2)) / hz;
+
+       /*
+        * Since the actual PWM divider is the register's frequency divider
+        * field minus 1, we need to decrement to get the correct value to
+        * write to the register.
+        */
+       if (rate > 0)
+               rate--;
+
+       /*
+        * Make sure that the rate will fit in the register's frequency
+        * divider field.
+        */
+       if (rate >> PWM_SCALE_WIDTH)
+               return -EINVAL;
+
+       val |= rate << PWM_SCALE_SHIFT;
+
+       /*
+        * If the PWM channel is disabled, make sure to turn on the clock
+        * before writing the register. Otherwise, keep it enabled.
+        */
+       if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+               err = clk_prepare_enable(pc->clk);
+               if (err < 0)
+                       return err;
+       } else
+               val |= PWM_ENABLE;
+
+       pwm_writel(pc, pwm->hwpwm, val);
+
+       /*
+        * If the PWM is not enabled, turn the clock off again to save power.
+        */
+       if (!test_bit(PWMF_ENABLED, &pwm->flags))
+               clk_disable_unprepare(pc->clk);
+
+       return 0;
+}
+
+static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+       int rc = 0;
+       u32 val;
+
+       rc = clk_prepare_enable(pc->clk);
+       if (rc < 0)
+               return rc;
+
+       val = pwm_readl(pc, pwm->hwpwm);
+       val |= PWM_ENABLE;
+       pwm_writel(pc, pwm->hwpwm, val);
+
+       return 0;
+}
+
+static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+       u32 val;
+
+       val = pwm_readl(pc, pwm->hwpwm);
+       val &= ~PWM_ENABLE;
+       pwm_writel(pc, pwm->hwpwm, val);
+
+       clk_disable_unprepare(pc->clk);
+}
+
+static const struct pwm_ops tegra_pwm_ops = {
+       .config = tegra_pwm_config,
+       .enable = tegra_pwm_enable,
+       .disable = tegra_pwm_disable,
+       .owner = THIS_MODULE,
+};
+
+static int tegra_pwm_probe(struct platform_device *pdev)
+{
+       struct tegra_pwm_chip *pwm;
+       struct resource *r;
+       int ret;
+
+       pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+       if (!pwm) {
+               dev_err(&pdev->dev, "failed to allocate memory\n");
+               return -ENOMEM;
+       }
+
+       pwm->dev = &pdev->dev;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               dev_err(&pdev->dev, "no memory resources defined\n");
+               return -ENODEV;
+       }
+
+       pwm->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+       if (!pwm->mmio_base) {
+               dev_err(&pdev->dev, "failed to ioremap() region\n");
+               return -EADDRNOTAVAIL;
+       }
+
+       platform_set_drvdata(pdev, pwm);
+
+       pwm->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(pwm->clk))
+               return PTR_ERR(pwm->clk);
+
+       pwm->chip.dev = &pdev->dev;
+       pwm->chip.ops = &tegra_pwm_ops;
+       pwm->chip.base = -1;
+       pwm->chip.npwm = NUM_PWM;
+
+       ret = pwmchip_add(&pwm->chip);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int __devexit tegra_pwm_remove(struct platform_device *pdev)
+{
+       struct tegra_pwm_chip *pc = platform_get_drvdata(pdev);
+       int i;
+
+       if (WARN_ON(!pc))
+               return -ENODEV;
+
+       for (i = 0; i < NUM_PWM; i++) {
+               struct pwm_device *pwm = &pc->chip.pwms[i];
+
+               if (!test_bit(PWMF_ENABLED, &pwm->flags))
+                       if (clk_prepare_enable(pc->clk) < 0)
+                               continue;
+
+               pwm_writel(pc, i, 0);
+
+               clk_disable_unprepare(pc->clk);
+       }
+
+       return pwmchip_remove(&pc->chip);
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id tegra_pwm_of_match[] = {
+       { .compatible = "nvidia,tegra20-pwm" },
+       { .compatible = "nvidia,tegra30-pwm" },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, tegra_pwm_of_match);
+#endif
+
+static struct platform_driver tegra_pwm_driver = {
+       .driver = {
+               .name = "tegra-pwm",
+               .of_match_table = of_match_ptr(tegra_pwm_of_match),
+       },
+       .probe = tegra_pwm_probe,
+       .remove = __devexit_p(tegra_pwm_remove),
+};
+
+module_platform_driver(tegra_pwm_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("NVIDIA Corporation");
+MODULE_ALIAS("platform:tegra-pwm");
diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c
new file mode 100644 (file)
index 0000000..3c2ad28
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * ECAP PWM driver
+ *
+ * Copyright (C) 2012 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; 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/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+
+/* ECAP registers and bits definitions */
+#define CAP1                   0x08
+#define CAP2                   0x0C
+#define CAP3                   0x10
+#define CAP4                   0x14
+#define ECCTL2                 0x2A
+#define ECCTL2_APWM_MODE       BIT(9)
+#define ECCTL2_SYNC_SEL_DISA   (BIT(7) | BIT(6))
+#define ECCTL2_TSCTR_FREERUN   BIT(4)
+
+struct ecap_pwm_chip {
+       struct pwm_chip chip;
+       unsigned int    clk_rate;
+       void __iomem    *mmio_base;
+};
+
+static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip)
+{
+       return container_of(chip, struct ecap_pwm_chip, chip);
+}
+
+/*
+ * period_ns = 10^9 * period_cycles / PWM_CLK_RATE
+ * duty_ns   = 10^9 * duty_cycles / PWM_CLK_RATE
+ */
+static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+               int duty_ns, int period_ns)
+{
+       struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+       unsigned long long c;
+       unsigned long period_cycles, duty_cycles;
+       unsigned int reg_val;
+
+       if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC)
+               return -ERANGE;
+
+       c = pc->clk_rate;
+       c = c * period_ns;
+       do_div(c, NSEC_PER_SEC);
+       period_cycles = (unsigned long)c;
+
+       if (period_cycles < 1) {
+               period_cycles = 1;
+               duty_cycles = 1;
+       } else {
+               c = pc->clk_rate;
+               c = c * duty_ns;
+               do_div(c, NSEC_PER_SEC);
+               duty_cycles = (unsigned long)c;
+       }
+
+       pm_runtime_get_sync(pc->chip.dev);
+
+       reg_val = readw(pc->mmio_base + ECCTL2);
+
+       /* Configure APWM mode & disable sync option */
+       reg_val |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA;
+
+       writew(reg_val, pc->mmio_base + ECCTL2);
+
+       if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+               /* Update active registers if not running */
+               writel(duty_cycles, pc->mmio_base + CAP2);
+               writel(period_cycles, pc->mmio_base + CAP1);
+       } else {
+               /*
+                * Update shadow registers to configure period and
+                * compare values. This helps current PWM period to
+                * complete on reconfiguring
+                */
+               writel(duty_cycles, pc->mmio_base + CAP4);
+               writel(period_cycles, pc->mmio_base + CAP3);
+       }
+
+       pm_runtime_put_sync(pc->chip.dev);
+       return 0;
+}
+
+static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+       unsigned int reg_val;
+
+       /* Leave clock enabled on enabling PWM */
+       pm_runtime_get_sync(pc->chip.dev);
+
+       /*
+        * Enable 'Free run Time stamp counter mode' to start counter
+        * and  'APWM mode' to enable APWM output
+        */
+       reg_val = readw(pc->mmio_base + ECCTL2);
+       reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
+       writew(reg_val, pc->mmio_base + ECCTL2);
+       return 0;
+}
+
+static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+       unsigned int reg_val;
+
+       /*
+        * Disable 'Free run Time stamp counter mode' to stop counter
+        * and 'APWM mode' to put APWM output to low
+        */
+       reg_val = readw(pc->mmio_base + ECCTL2);
+       reg_val &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE);
+       writew(reg_val, pc->mmio_base + ECCTL2);
+
+       /* Disable clock on PWM disable */
+       pm_runtime_put_sync(pc->chip.dev);
+}
+
+static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       if (test_bit(PWMF_ENABLED, &pwm->flags)) {
+               dev_warn(chip->dev, "Removing PWM device without disabling\n");
+               pm_runtime_put_sync(chip->dev);
+       }
+}
+
+static const struct pwm_ops ecap_pwm_ops = {
+       .free           = ecap_pwm_free,
+       .config         = ecap_pwm_config,
+       .enable         = ecap_pwm_enable,
+       .disable        = ecap_pwm_disable,
+       .owner          = THIS_MODULE,
+};
+
+static int __devinit ecap_pwm_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct resource *r;
+       struct clk *clk;
+       struct ecap_pwm_chip *pc;
+
+       pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
+       if (!pc) {
+               dev_err(&pdev->dev, "failed to allocate memory\n");
+               return -ENOMEM;
+       }
+
+       clk = devm_clk_get(&pdev->dev, "fck");
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "failed to get clock\n");
+               return PTR_ERR(clk);
+       }
+
+       pc->clk_rate = clk_get_rate(clk);
+       if (!pc->clk_rate) {
+               dev_err(&pdev->dev, "failed to get clock rate\n");
+               return -EINVAL;
+       }
+
+       pc->chip.dev = &pdev->dev;
+       pc->chip.ops = &ecap_pwm_ops;
+       pc->chip.base = -1;
+       pc->chip.npwm = 1;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               dev_err(&pdev->dev, "no memory resource defined\n");
+               return -ENODEV;
+       }
+
+       pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+       if (!pc->mmio_base) {
+               dev_err(&pdev->dev, "failed to ioremap() registers\n");
+               return -EADDRNOTAVAIL;
+       }
+
+       ret = pwmchip_add(&pc->chip);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+               return ret;
+       }
+
+       pm_runtime_enable(&pdev->dev);
+       platform_set_drvdata(pdev, pc);
+       return 0;
+}
+
+static int __devexit ecap_pwm_remove(struct platform_device *pdev)
+{
+       struct ecap_pwm_chip *pc = platform_get_drvdata(pdev);
+
+       pm_runtime_put_sync(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+       return pwmchip_remove(&pc->chip);
+}
+
+static struct platform_driver ecap_pwm_driver = {
+       .driver = {
+               .name = "ecap",
+       },
+       .probe = ecap_pwm_probe,
+       .remove = __devexit_p(ecap_pwm_remove),
+};
+
+module_platform_driver(ecap_pwm_driver);
+
+MODULE_DESCRIPTION("ECAP PWM driver");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
new file mode 100644 (file)
index 0000000..010d232
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * EHRPWM PWM driver
+ *
+ * Copyright (C) 2012 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; 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/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+
+/* EHRPWM registers and bits definitions */
+
+/* Time base module registers */
+#define TBCTL                  0x00
+#define TBPRD                  0x0A
+
+#define TBCTL_RUN_MASK         (BIT(15) | BIT(14))
+#define TBCTL_STOP_NEXT                0
+#define TBCTL_STOP_ON_CYCLE    BIT(14)
+#define TBCTL_FREE_RUN         (BIT(15) | BIT(14))
+#define TBCTL_PRDLD_MASK       BIT(3)
+#define TBCTL_PRDLD_SHDW       0
+#define TBCTL_PRDLD_IMDT       BIT(3)
+#define TBCTL_CLKDIV_MASK      (BIT(12) | BIT(11) | BIT(10) | BIT(9) | \
+                               BIT(8) | BIT(7))
+#define TBCTL_CTRMODE_MASK     (BIT(1) | BIT(0))
+#define TBCTL_CTRMODE_UP       0
+#define TBCTL_CTRMODE_DOWN     BIT(0)
+#define TBCTL_CTRMODE_UPDOWN   BIT(1)
+#define TBCTL_CTRMODE_FREEZE   (BIT(1) | BIT(0))
+
+#define TBCTL_HSPCLKDIV_SHIFT  7
+#define TBCTL_CLKDIV_SHIFT     10
+
+#define CLKDIV_MAX             7
+#define HSPCLKDIV_MAX          7
+#define PERIOD_MAX             0xFFFF
+
+/* compare module registers */
+#define CMPA                   0x12
+#define CMPB                   0x14
+
+/* Action qualifier module registers */
+#define AQCTLA                 0x16
+#define AQCTLB                 0x18
+#define AQSFRC                 0x1A
+#define AQCSFRC                        0x1C
+
+#define AQCTL_CBU_MASK         (BIT(9) | BIT(8))
+#define AQCTL_CBU_FRCLOW       BIT(8)
+#define AQCTL_CBU_FRCHIGH      BIT(9)
+#define AQCTL_CBU_FRCTOGGLE    (BIT(9) | BIT(8))
+#define AQCTL_CAU_MASK         (BIT(5) | BIT(4))
+#define AQCTL_CAU_FRCLOW       BIT(4)
+#define AQCTL_CAU_FRCHIGH      BIT(5)
+#define AQCTL_CAU_FRCTOGGLE    (BIT(5) | BIT(4))
+#define AQCTL_PRD_MASK         (BIT(3) | BIT(2))
+#define AQCTL_PRD_FRCLOW       BIT(2)
+#define AQCTL_PRD_FRCHIGH      BIT(3)
+#define AQCTL_PRD_FRCTOGGLE    (BIT(3) | BIT(2))
+#define AQCTL_ZRO_MASK         (BIT(1) | BIT(0))
+#define AQCTL_ZRO_FRCLOW       BIT(0)
+#define AQCTL_ZRO_FRCHIGH      BIT(1)
+#define AQCTL_ZRO_FRCTOGGLE    (BIT(1) | BIT(0))
+
+#define AQSFRC_RLDCSF_MASK     (BIT(7) | BIT(6))
+#define AQSFRC_RLDCSF_ZRO      0
+#define AQSFRC_RLDCSF_PRD      BIT(6)
+#define AQSFRC_RLDCSF_ZROPRD   BIT(7)
+#define AQSFRC_RLDCSF_IMDT     (BIT(7) | BIT(6))
+
+#define AQCSFRC_CSFB_MASK      (BIT(3) | BIT(2))
+#define AQCSFRC_CSFB_FRCDIS    0
+#define AQCSFRC_CSFB_FRCLOW    BIT(2)
+#define AQCSFRC_CSFB_FRCHIGH   BIT(3)
+#define AQCSFRC_CSFB_DISSWFRC  (BIT(3) | BIT(2))
+#define AQCSFRC_CSFA_MASK      (BIT(1) | BIT(0))
+#define AQCSFRC_CSFA_FRCDIS    0
+#define AQCSFRC_CSFA_FRCLOW    BIT(0)
+#define AQCSFRC_CSFA_FRCHIGH   BIT(1)
+#define AQCSFRC_CSFA_DISSWFRC  (BIT(1) | BIT(0))
+
+#define NUM_PWM_CHANNEL                2       /* EHRPWM channels */
+
+struct ehrpwm_pwm_chip {
+       struct pwm_chip chip;
+       unsigned int    clk_rate;
+       void __iomem    *mmio_base;
+};
+
+static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip)
+{
+       return container_of(chip, struct ehrpwm_pwm_chip, chip);
+}
+
+static void ehrpwm_write(void *base, int offset, unsigned int val)
+{
+       writew(val & 0xFFFF, base + offset);
+}
+
+static void ehrpwm_modify(void *base, int offset,
+               unsigned short mask, unsigned short val)
+{
+       unsigned short regval;
+
+       regval = readw(base + offset);
+       regval &= ~mask;
+       regval |= val & mask;
+       writew(regval, base + offset);
+}
+
+/**
+ * set_prescale_div -  Set up the prescaler divider function
+ * @rqst_prescaler:    prescaler value min
+ * @prescale_div:      prescaler value set
+ * @tb_clk_div:                Time Base Control prescaler bits
+ */
+static int set_prescale_div(unsigned long rqst_prescaler,
+               unsigned short *prescale_div, unsigned short *tb_clk_div)
+{
+       unsigned int clkdiv, hspclkdiv;
+
+       for (clkdiv = 0; clkdiv <= CLKDIV_MAX; clkdiv++) {
+               for (hspclkdiv = 0; hspclkdiv <= HSPCLKDIV_MAX; hspclkdiv++) {
+
+                       /*
+                        * calculations for prescaler value :
+                        * prescale_div = HSPCLKDIVIDER * CLKDIVIDER.
+                        * HSPCLKDIVIDER =  2 ** hspclkdiv
+                        * CLKDIVIDER = (1),            if clkdiv == 0 *OR*
+                        *              (2 * clkdiv),   if clkdiv != 0
+                        *
+                        * Configure prescale_div value such that period
+                        * register value is less than 65535.
+                        */
+
+                       *prescale_div = (1 << clkdiv) *
+                                       (hspclkdiv ? (hspclkdiv * 2) : 1);
+                       if (*prescale_div > rqst_prescaler) {
+                               *tb_clk_div = (clkdiv << TBCTL_CLKDIV_SHIFT) |
+                                       (hspclkdiv << TBCTL_HSPCLKDIV_SHIFT);
+                               return 0;
+                       }
+               }
+       }
+       return 1;
+}
+
+static void configure_chans(struct ehrpwm_pwm_chip *pc, int chan,
+               unsigned long duty_cycles)
+{
+       int cmp_reg, aqctl_reg;
+       unsigned short aqctl_val, aqctl_mask;
+
+       /*
+        * Channels can be configured from action qualifier module.
+        * Channel 0 configured with compare A register and for
+        * up-counter mode.
+        * Channel 1 configured with compare B register and for
+        * up-counter mode.
+        */
+       if (chan == 1) {
+               aqctl_reg = AQCTLB;
+               cmp_reg = CMPB;
+               /* Configure PWM Low from compare B value */
+               aqctl_val = AQCTL_CBU_FRCLOW;
+               aqctl_mask = AQCTL_CBU_MASK;
+       } else {
+               cmp_reg = CMPA;
+               aqctl_reg = AQCTLA;
+               /* Configure PWM Low from compare A value*/
+               aqctl_val = AQCTL_CAU_FRCLOW;
+               aqctl_mask = AQCTL_CAU_MASK;
+       }
+
+       /* Configure PWM High from period value and zero value */
+       aqctl_val |= AQCTL_PRD_FRCHIGH | AQCTL_ZRO_FRCHIGH;
+       aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK;
+       ehrpwm_modify(pc->mmio_base,  aqctl_reg, aqctl_mask, aqctl_val);
+
+       ehrpwm_write(pc->mmio_base,  cmp_reg, duty_cycles);
+}
+
+/*
+ * period_ns = 10^9 * (ps_divval * period_cycles) / PWM_CLK_RATE
+ * duty_ns   = 10^9 * (ps_divval * duty_cycles) / PWM_CLK_RATE
+ */
+static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+               int duty_ns, int period_ns)
+{
+       struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+       unsigned long long c;
+       unsigned long period_cycles, duty_cycles;
+       unsigned short ps_divval, tb_divval;
+
+       if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC)
+               return -ERANGE;
+
+       c = pc->clk_rate;
+       c = c * period_ns;
+       do_div(c, NSEC_PER_SEC);
+       period_cycles = (unsigned long)c;
+
+       if (period_cycles < 1) {
+               period_cycles = 1;
+               duty_cycles = 1;
+       } else {
+               c = pc->clk_rate;
+               c = c * duty_ns;
+               do_div(c, NSEC_PER_SEC);
+               duty_cycles = (unsigned long)c;
+       }
+
+       /* Configure clock prescaler to support Low frequency PWM wave */
+       if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval,
+                               &tb_divval)) {
+               dev_err(chip->dev, "Unsupported values\n");
+               return -EINVAL;
+       }
+
+       pm_runtime_get_sync(chip->dev);
+
+       /* Update clock prescaler values */
+       ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CLKDIV_MASK, tb_divval);
+
+       /* Update period & duty cycle with presacler division */
+       period_cycles = period_cycles / ps_divval;
+       duty_cycles = duty_cycles / ps_divval;
+
+       /* Configure shadow loading on Period register */
+       ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_PRDLD_MASK, TBCTL_PRDLD_SHDW);
+
+       ehrpwm_write(pc->mmio_base, TBPRD, period_cycles);
+
+       /* Configure ehrpwm counter for up-count mode */
+       ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK,
+                       TBCTL_CTRMODE_UP);
+
+       /* Configure the channel for duty cycle */
+       configure_chans(pc, pwm->hwpwm, duty_cycles);
+       pm_runtime_put_sync(chip->dev);
+       return 0;
+}
+
+static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+       unsigned short aqcsfrc_val, aqcsfrc_mask;
+
+       /* Leave clock enabled on enabling PWM */
+       pm_runtime_get_sync(chip->dev);
+
+       /* Disabling Action Qualifier on PWM output */
+       if (pwm->hwpwm) {
+               aqcsfrc_val = AQCSFRC_CSFB_FRCDIS;
+               aqcsfrc_mask = AQCSFRC_CSFB_MASK;
+       } else {
+               aqcsfrc_val = AQCSFRC_CSFA_FRCDIS;
+               aqcsfrc_mask = AQCSFRC_CSFA_MASK;
+       }
+
+       /* Changes to shadow mode */
+       ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK,
+                       AQSFRC_RLDCSF_ZRO);
+
+       ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
+
+       /* Enable time counter for free_run */
+       ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN);
+       return 0;
+}
+
+static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+       unsigned short aqcsfrc_val, aqcsfrc_mask;
+
+       /* Action Qualifier puts PWM output low forcefully */
+       if (pwm->hwpwm) {
+               aqcsfrc_val = AQCSFRC_CSFB_FRCLOW;
+               aqcsfrc_mask = AQCSFRC_CSFB_MASK;
+       } else {
+               aqcsfrc_val = AQCSFRC_CSFA_FRCLOW;
+               aqcsfrc_mask = AQCSFRC_CSFA_MASK;
+       }
+
+       /*
+        * Changes to immediate action on Action Qualifier. This puts
+        * Action Qualifier control on PWM output from next TBCLK
+        */
+       ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK,
+                       AQSFRC_RLDCSF_IMDT);
+
+       ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
+
+       /* Stop Time base counter */
+       ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT);
+
+       /* Disable clock on PWM disable */
+       pm_runtime_put_sync(chip->dev);
+}
+
+static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       if (test_bit(PWMF_ENABLED, &pwm->flags)) {
+               dev_warn(chip->dev, "Removing PWM device without disabling\n");
+               pm_runtime_put_sync(chip->dev);
+       }
+}
+
+static const struct pwm_ops ehrpwm_pwm_ops = {
+       .free           = ehrpwm_pwm_free,
+       .config         = ehrpwm_pwm_config,
+       .enable         = ehrpwm_pwm_enable,
+       .disable        = ehrpwm_pwm_disable,
+       .owner          = THIS_MODULE,
+};
+
+static int __devinit ehrpwm_pwm_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct resource *r;
+       struct clk *clk;
+       struct ehrpwm_pwm_chip *pc;
+
+       pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
+       if (!pc) {
+               dev_err(&pdev->dev, "failed to allocate memory\n");
+               return -ENOMEM;
+       }
+
+       clk = devm_clk_get(&pdev->dev, "fck");
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "failed to get clock\n");
+               return PTR_ERR(clk);
+       }
+
+       pc->clk_rate = clk_get_rate(clk);
+       if (!pc->clk_rate) {
+               dev_err(&pdev->dev, "failed to get clock rate\n");
+               return -EINVAL;
+       }
+
+       pc->chip.dev = &pdev->dev;
+       pc->chip.ops = &ehrpwm_pwm_ops;
+       pc->chip.base = -1;
+       pc->chip.npwm = NUM_PWM_CHANNEL;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               dev_err(&pdev->dev, "no memory resource defined\n");
+               return -ENODEV;
+       }
+
+       pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+       if (!pc->mmio_base) {
+               dev_err(&pdev->dev, "failed to ioremap() registers\n");
+               return  -EADDRNOTAVAIL;
+       }
+
+       ret = pwmchip_add(&pc->chip);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+               return ret;
+       }
+
+       pm_runtime_enable(&pdev->dev);
+       platform_set_drvdata(pdev, pc);
+       return 0;
+}
+
+static int __devexit ehrpwm_pwm_remove(struct platform_device *pdev)
+{
+       struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev);
+
+       pm_runtime_put_sync(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+       return pwmchip_remove(&pc->chip);
+}
+
+static struct platform_driver ehrpwm_pwm_driver = {
+       .driver = {
+               .name = "ehrpwm",
+       },
+       .probe = ehrpwm_pwm_probe,
+       .remove = __devexit_p(ehrpwm_pwm_remove),
+};
+
+module_platform_driver(ehrpwm_pwm_driver);
+
+MODULE_DESCRIPTION("EHRPWM PWM driver");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c
new file mode 100644 (file)
index 0000000..5480214
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * drivers/pwm/pwm-vt8500.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+
+#include <asm/div64.h>
+
+#define VT8500_NR_PWMS 4
+
+struct vt8500_chip {
+       struct pwm_chip chip;
+       void __iomem *base;
+};
+
+#define to_vt8500_chip(chip)   container_of(chip, struct vt8500_chip, chip)
+
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
+{
+       int loops = msecs_to_loops(10);
+       while ((readb(reg) & bitmask) && --loops)
+               cpu_relax();
+
+       if (unlikely(!loops))
+               pr_warning("Waiting for status bits 0x%x to clear timed out\n",
+                          bitmask);
+}
+
+static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+               int duty_ns, int period_ns)
+{
+       struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
+       unsigned long long c;
+       unsigned long period_cycles, prescale, pv, dc;
+
+       c = 25000000/2; /* wild guess --- need to implement clocks */
+       c = c * period_ns;
+       do_div(c, 1000000000);
+       period_cycles = c;
+
+       if (period_cycles < 1)
+               period_cycles = 1;
+       prescale = (period_cycles - 1) / 4096;
+       pv = period_cycles / (prescale + 1) - 1;
+       if (pv > 4095)
+               pv = 4095;
+
+       if (prescale > 1023)
+               return -EINVAL;
+
+       c = (unsigned long long)pv * duty_ns;
+       do_div(c, period_ns);
+       dc = c;
+
+       pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 1));
+       writel(prescale, vt8500->base + 0x4 + (pwm->hwpwm << 4));
+
+       pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 2));
+       writel(pv, vt8500->base + 0x8 + (pwm->hwpwm << 4));
+
+       pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 3));
+       writel(dc, vt8500->base + 0xc + (pwm->hwpwm << 4));
+
+       return 0;
+}
+
+static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
+
+       pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0));
+       writel(5, vt8500->base + (pwm->hwpwm << 4));
+       return 0;
+}
+
+static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
+
+       pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0));
+       writel(0, vt8500->base + (pwm->hwpwm << 4));
+}
+
+static struct pwm_ops vt8500_pwm_ops = {
+       .enable = vt8500_pwm_enable,
+       .disable = vt8500_pwm_disable,
+       .config = vt8500_pwm_config,
+       .owner = THIS_MODULE,
+};
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+       struct vt8500_chip *chip;
+       struct resource *r;
+       int ret;
+
+       chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+       if (chip == NULL) {
+               dev_err(&pdev->dev, "failed to allocate memory\n");
+               return -ENOMEM;
+       }
+
+       chip->chip.dev = &pdev->dev;
+       chip->chip.ops = &vt8500_pwm_ops;
+       chip->chip.base = -1;
+       chip->chip.npwm = VT8500_NR_PWMS;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (r == NULL) {
+               dev_err(&pdev->dev, "no memory resource defined\n");
+               return -ENODEV;
+       }
+
+       chip->base = devm_request_and_ioremap(&pdev->dev, r);
+       if (chip->base == NULL)
+               return -EADDRNOTAVAIL;
+
+       ret = pwmchip_add(&chip->chip);
+       if (ret < 0)
+               return ret;
+
+       platform_set_drvdata(pdev, chip);
+       return ret;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+       struct vt8500_chip *chip;
+
+       chip = platform_get_drvdata(pdev);
+       if (chip == NULL)
+               return -ENODEV;
+
+       return pwmchip_remove(&chip->chip);
+}
+
+static struct platform_driver pwm_driver = {
+       .driver         = {
+               .name   = "vt8500-pwm",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = pwm_probe,
+       .remove         = __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+       return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+       platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL");
index f34c3be6c9fed92bdaf227016f4540b8edda0f89..4e932cc695e9311e811c9be2942798c4ecad5077 100644 (file)
@@ -272,7 +272,7 @@ config REGULATOR_S2MPS11
 
 config REGULATOR_S5M8767
        tristate "Samsung S5M8767A voltage regulator"
-       depends on MFD_S5M_CORE
+       depends on MFD_SEC_CORE
        help
         This driver supports a Samsung S5M8767A voltage output regulator
         via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and
index 13d424fc1c14c675958014846b48391ff468dec7..10f2f4d4d190254cfa9602aa19223984c38fa691 100644 (file)
@@ -848,18 +848,12 @@ static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id ab8500_regulator_match[] = {
-        { .compatible = "stericsson,ab8500-regulator", },
-        {}
-};
-
 static struct platform_driver ab8500_regulator_driver = {
        .probe = ab8500_regulator_probe,
        .remove = __devexit_p(ab8500_regulator_remove),
        .driver         = {
                .name   = "ab8500-regulator",
                .owner  = THIS_MODULE,
-               .of_match_table = ab8500_regulator_match,
        },
 };
 
index 9dbb491b6efa827a64198baccfc42f6fe6612f79..359f8d18fc3f35f03f0314661aba33def33846f7 100644 (file)
@@ -547,16 +547,10 @@ static int __exit db8500_regulator_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id db8500_prcmu_regulator_match[] = {
-        { .compatible = "stericsson,db8500-prcmu-regulator", },
-        {}
-};
-
 static struct platform_driver db8500_regulator_driver = {
        .driver = {
                .name = "db8500-prcmu-regulators",
                .owner = THIS_MODULE,
-               .of_match_table = db8500_prcmu_regulator_match,
        },
        .probe = db8500_regulator_probe,
        .remove = __exit_p(db8500_regulator_remove),
index 102287fa7ecb48df1aae1de76c484b3fe683051d..abe64a32aedf3a4b38ee05ea7c59849b4959642c 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
-#include <linux/mfd/s5m87xx/s5m-core.h>
-#include <linux/mfd/s5m87xx/s5m-pmic.h>
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/s5m8767.h>
 
 struct s5m8767_info {
        struct device *dev;
-       struct s5m87xx_dev *iodev;
+       struct sec_pmic_dev *iodev;
        int num_regulators;
        struct regulator_dev **rdev;
-       struct s5m_opmode_data *opmode;
+       struct sec_opmode_data *opmode;
 
        int ramp_delay;
        bool buck2_ramp;
@@ -45,43 +45,43 @@ struct s5m8767_info {
        int buck_gpioindex;
 };
 
-struct s5m_voltage_desc {
+struct sec_voltage_desc {
        int max;
        int min;
        int step;
 };
 
-static const struct s5m_voltage_desc buck_voltage_val1 = {
+static const struct sec_voltage_desc buck_voltage_val1 = {
        .max = 2225000,
        .min =  650000,
        .step =   6250,
 };
 
-static const struct s5m_voltage_desc buck_voltage_val2 = {
+static const struct sec_voltage_desc buck_voltage_val2 = {
        .max = 1600000,
        .min =  600000,
        .step =   6250,
 };
 
-static const struct s5m_voltage_desc buck_voltage_val3 = {
+static const struct sec_voltage_desc buck_voltage_val3 = {
        .max = 3000000,
        .min =  750000,
        .step =  12500,
 };
 
-static const struct s5m_voltage_desc ldo_voltage_val1 = {
+static const struct sec_voltage_desc ldo_voltage_val1 = {
        .max = 3950000,
        .min =  800000,
        .step =  50000,
 };
 
-static const struct s5m_voltage_desc ldo_voltage_val2 = {
+static const struct sec_voltage_desc ldo_voltage_val2 = {
        .max = 2375000,
        .min =  800000,
        .step =  25000,
 };
 
-static const struct s5m_voltage_desc *reg_voltage_map[] = {
+static const struct sec_voltage_desc *reg_voltage_map[] = {
        [S5M8767_LDO1] = &ldo_voltage_val2,
        [S5M8767_LDO2] = &ldo_voltage_val2,
        [S5M8767_LDO3] = &ldo_voltage_val1,
@@ -213,7 +213,7 @@ static int s5m8767_reg_is_enabled(struct regulator_dev *rdev)
        else if (ret)
                return ret;
 
-       ret = s5m_reg_read(s5m8767->iodev, reg, &val);
+       ret = sec_reg_read(s5m8767->iodev, reg, &val);
        if (ret)
                return ret;
 
@@ -230,7 +230,7 @@ static int s5m8767_reg_enable(struct regulator_dev *rdev)
        if (ret)
                return ret;
 
-       return s5m_reg_update(s5m8767->iodev, reg, enable_ctrl, mask);
+       return sec_reg_update(s5m8767->iodev, reg, enable_ctrl, mask);
 }
 
 static int s5m8767_reg_disable(struct regulator_dev *rdev)
@@ -243,7 +243,7 @@ static int s5m8767_reg_disable(struct regulator_dev *rdev)
        if (ret)
                return ret;
 
-       return s5m_reg_update(s5m8767->iodev, reg, ~mask, mask);
+       return sec_reg_update(s5m8767->iodev, reg, ~mask, mask);
 }
 
 static int s5m8767_get_voltage_register(struct regulator_dev *rdev, int *_reg)
@@ -305,7 +305,7 @@ static int s5m8767_get_voltage_sel(struct regulator_dev *rdev)
 
        mask = (reg_id < S5M8767_BUCK1) ? 0x3f : 0xff;
 
-       ret = s5m_reg_read(s5m8767->iodev, reg, &val);
+       ret = sec_reg_read(s5m8767->iodev, reg, &val);
        if (ret)
                return ret;
 
@@ -315,7 +315,7 @@ static int s5m8767_get_voltage_sel(struct regulator_dev *rdev)
 }
 
 static int s5m8767_convert_voltage_to_sel(
-               const struct s5m_voltage_desc *desc,
+               const struct sec_voltage_desc *desc,
                int min_vol, int max_vol)
 {
        int selector = 0;
@@ -407,7 +407,7 @@ static int s5m8767_set_voltage_sel(struct regulator_dev *rdev,
                if (ret)
                        return ret;
 
-               return s5m_reg_update(s5m8767->iodev, reg, selector, mask);
+               return sec_reg_update(s5m8767->iodev, reg, selector, mask);
        }
 }
 
@@ -416,7 +416,7 @@ static int s5m8767_set_voltage_time_sel(struct regulator_dev *rdev,
                                             unsigned int new_sel)
 {
        struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
-       const struct s5m_voltage_desc *desc;
+       const struct sec_voltage_desc *desc;
        int reg_id = rdev_get_id(rdev);
 
        desc = reg_voltage_map[reg_id];
@@ -501,8 +501,8 @@ static struct regulator_desc regulators[] = {
 
 static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
 {
-       struct s5m87xx_dev *iodev = dev_get_drvdata(pdev->dev.parent);
-       struct s5m_platform_data *pdata = dev_get_platdata(iodev->dev);
+       struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+       struct sec_platform_data *pdata = dev_get_platdata(iodev->dev);
        struct regulator_config config = { };
        struct regulator_dev **rdev;
        struct s5m8767_info *s5m8767;
@@ -572,21 +572,21 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
                                                pdata->buck2_init +
                                                buck_voltage_val2.step);
 
-       s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS2, buck_init);
+       sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS2, buck_init);
 
        buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2,
                                                pdata->buck3_init,
                                                pdata->buck3_init +
                                                buck_voltage_val2.step);
 
-       s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS2, buck_init);
+       sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS2, buck_init);
 
        buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2,
                                                pdata->buck4_init,
                                                pdata->buck4_init +
                                                buck_voltage_val2.step);
 
-       s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS2, buck_init);
+       sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS2, buck_init);
 
        for (i = 0; i < 8; i++) {
                if (s5m8767->buck2_gpiodvs) {
@@ -671,13 +671,13 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
 
        if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs ||
           pdata->buck4_gpiodvs) {
-               s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL,
+               sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL,
                                (pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1),
                                1 << 1);
-               s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL,
+               sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL,
                                (pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1),
                                1 << 1);
-               s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL,
+               sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL,
                                (pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1),
                                1 << 1);
        }
@@ -685,61 +685,61 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
        /* Initialize GPIO DVS registers */
        for (i = 0; i < 8; i++) {
                if (s5m8767->buck2_gpiodvs) {
-                       s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS1 + i,
+                       sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS1 + i,
                                           s5m8767->buck2_vol[i]);
                }
 
                if (s5m8767->buck3_gpiodvs) {
-                       s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS1 + i,
+                       sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS1 + i,
                                           s5m8767->buck3_vol[i]);
                }
 
                if (s5m8767->buck4_gpiodvs) {
-                       s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS1 + i,
+                       sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS1 + i,
                                           s5m8767->buck4_vol[i]);
                }
        }
 
        if (s5m8767->buck2_ramp)
-               s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x08, 0x08);
+               sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x08, 0x08);
 
        if (s5m8767->buck3_ramp)
-               s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x04, 0x04);
+               sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x04, 0x04);
 
        if (s5m8767->buck4_ramp)
-               s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x02, 0x02);
+               sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x02, 0x02);
 
        if (s5m8767->buck2_ramp || s5m8767->buck3_ramp
                || s5m8767->buck4_ramp) {
                switch (s5m8767->ramp_delay) {
                case 5:
-                       s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+                       sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
                                        0x40, 0xf0);
                        break;
                case 10:
-                       s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+                       sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
                                        0x90, 0xf0);
                        break;
                case 25:
-                       s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+                       sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
                                        0xd0, 0xf0);
                        break;
                case 50:
-                       s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+                       sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
                                        0xe0, 0xf0);
                        break;
                case 100:
-                       s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+                       sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
                                        0xf0, 0xf0);
                        break;
                default:
-                       s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+                       sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
                                        0x90, 0xf0);
                }
        }
 
        for (i = 0; i < pdata->num_regulators; i++) {
-               const struct s5m_voltage_desc *desc;
+               const struct sec_voltage_desc *desc;
                int id = pdata->regulators[i].id;
 
                desc = reg_voltage_map[id];
index 08cbdb900a18a290a7065bc73850015f8278ca10..fabc99a75c6596d2b797e55a59fa7ecd2db49fc8 100644 (file)
@@ -135,6 +135,16 @@ config RTC_DRV_88PM860X
          This driver can also be built as a module. If so, the module
          will be called rtc-88pm860x.
 
+config RTC_DRV_88PM80X
+       tristate "Marvell 88PM80x"
+       depends on RTC_CLASS && I2C && MFD_88PM800
+       help
+         If you say yes here you get support for RTC function in Marvell
+         88PM80x chips.
+
+         This driver can also be built as a module. If so, the module
+         will be called rtc-88pm80x.
+
 config RTC_DRV_DS1307
        tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025"
        help
@@ -694,6 +704,7 @@ config RTC_DRV_AB3100
 config RTC_DRV_AB8500
        tristate "ST-Ericsson AB8500 RTC"
        depends on AB8500_CORE
+       select RTC_INTF_DEV_UIE_EMUL
        help
          Select this to enable the ST-Ericsson AB8500 power management IC RTC
          support. This chip contains a battery- and capacitor-backed RTC.
index 2973921c30d84d70eafea434dd3e678e4c54c3c1..0d5b2b66f90d4f47f97b3cf88fec79ebb2ff4028 100644 (file)
@@ -16,6 +16,7 @@ rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
 # Keep the list ordered.
 
 obj-$(CONFIG_RTC_DRV_88PM860X)  += rtc-88pm860x.o
+obj-$(CONFIG_RTC_DRV_88PM80X)  += rtc-88pm80x.o
 obj-$(CONFIG_RTC_DRV_AB3100)   += rtc-ab3100.o
 obj-$(CONFIG_RTC_DRV_AB8500)   += rtc-ab8500.o
 obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c
new file mode 100644 (file)
index 0000000..6367984
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * Real Time Clock driver for Marvell 88PM80x PMIC
+ *
+ * Copyright (c) 2012 Marvell International Ltd.
+ *  Wenzeng Chen<wzch@marvell.com>
+ *  Qiao Zhou <zhouqiao@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/rtc.h>
+
+#define PM800_RTC_COUNTER1             (0xD1)
+#define PM800_RTC_COUNTER2             (0xD2)
+#define PM800_RTC_COUNTER3             (0xD3)
+#define PM800_RTC_COUNTER4             (0xD4)
+#define PM800_RTC_EXPIRE1_1            (0xD5)
+#define PM800_RTC_EXPIRE1_2            (0xD6)
+#define PM800_RTC_EXPIRE1_3            (0xD7)
+#define PM800_RTC_EXPIRE1_4            (0xD8)
+#define PM800_RTC_TRIM1                        (0xD9)
+#define PM800_RTC_TRIM2                        (0xDA)
+#define PM800_RTC_TRIM3                        (0xDB)
+#define PM800_RTC_TRIM4                        (0xDC)
+#define PM800_RTC_EXPIRE2_1            (0xDD)
+#define PM800_RTC_EXPIRE2_2            (0xDE)
+#define PM800_RTC_EXPIRE2_3            (0xDF)
+#define PM800_RTC_EXPIRE2_4            (0xE0)
+
+#define PM800_POWER_DOWN_LOG1  (0xE5)
+#define PM800_POWER_DOWN_LOG2  (0xE6)
+
+struct pm80x_rtc_info {
+       struct pm80x_chip *chip;
+       struct regmap *map;
+       struct rtc_device *rtc_dev;
+       struct device *dev;
+       struct delayed_work calib_work;
+
+       int irq;
+       int vrtc;
+};
+
+static irqreturn_t rtc_update_handler(int irq, void *data)
+{
+       struct pm80x_rtc_info *info = (struct pm80x_rtc_info *)data;
+       int mask;
+
+       mask = PM800_ALARM | PM800_ALARM_WAKEUP;
+       regmap_update_bits(info->map, PM800_RTC_CONTROL, mask | PM800_ALARM1_EN,
+                          mask);
+       rtc_update_irq(info->rtc_dev, 1, RTC_AF);
+       return IRQ_HANDLED;
+}
+
+static int pm80x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+       struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+
+       if (enabled)
+               regmap_update_bits(info->map, PM800_RTC_CONTROL,
+                                  PM800_ALARM1_EN, PM800_ALARM1_EN);
+       else
+               regmap_update_bits(info->map, PM800_RTC_CONTROL,
+                                  PM800_ALARM1_EN, 0);
+       return 0;
+}
+
+/*
+ * Calculate the next alarm time given the requested alarm time mask
+ * and the current time.
+ */
+static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now,
+                               struct rtc_time *alrm)
+{
+       unsigned long next_time;
+       unsigned long now_time;
+
+       next->tm_year = now->tm_year;
+       next->tm_mon = now->tm_mon;
+       next->tm_mday = now->tm_mday;
+       next->tm_hour = alrm->tm_hour;
+       next->tm_min = alrm->tm_min;
+       next->tm_sec = alrm->tm_sec;
+
+       rtc_tm_to_time(now, &now_time);
+       rtc_tm_to_time(next, &next_time);
+
+       if (next_time < now_time) {
+               /* Advance one day */
+               next_time += 60 * 60 * 24;
+               rtc_time_to_tm(next_time, next);
+       }
+}
+
+static int pm80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+       struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+       unsigned char buf[4];
+       unsigned long ticks, base, data;
+       regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
+       base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
+
+       /* load 32-bit read-only counter */
+       regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
+       data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       ticks = base + data;
+       dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+               base, data, ticks);
+       rtc_time_to_tm(ticks, tm);
+       return 0;
+}
+
+static int pm80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+       struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+       unsigned char buf[4];
+       unsigned long ticks, base, data;
+       if ((tm->tm_year < 70) || (tm->tm_year > 138)) {
+               dev_dbg(info->dev,
+                       "Set time %d out of range. Please set time between 1970 to 2038.\n",
+                       1900 + tm->tm_year);
+               return -EINVAL;
+       }
+       rtc_tm_to_time(tm, &ticks);
+
+       /* load 32-bit read-only counter */
+       regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
+       data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       base = ticks - data;
+       dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+               base, data, ticks);
+       buf[0] = base & 0xFF;
+       buf[1] = (base >> 8) & 0xFF;
+       buf[2] = (base >> 16) & 0xFF;
+       buf[3] = (base >> 24) & 0xFF;
+       regmap_raw_write(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
+
+       return 0;
+}
+
+static int pm80x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+       struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+       unsigned char buf[4];
+       unsigned long ticks, base, data;
+       int ret;
+
+       regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
+       base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
+
+       regmap_raw_read(info->map, PM800_RTC_EXPIRE1_1, buf, 4);
+       data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       ticks = base + data;
+       dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+               base, data, ticks);
+
+       rtc_time_to_tm(ticks, &alrm->time);
+       regmap_read(info->map, PM800_RTC_CONTROL, &ret);
+       alrm->enabled = (ret & PM800_ALARM1_EN) ? 1 : 0;
+       alrm->pending = (ret & (PM800_ALARM | PM800_ALARM_WAKEUP)) ? 1 : 0;
+       return 0;
+}
+
+static int pm80x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+       struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+       struct rtc_time now_tm, alarm_tm;
+       unsigned long ticks, base, data;
+       unsigned char buf[4];
+       int mask;
+
+       regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_ALARM1_EN, 0);
+
+       regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
+       base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
+
+       /* load 32-bit read-only counter */
+       regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
+       data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+       ticks = base + data;
+       dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+               base, data, ticks);
+
+       rtc_time_to_tm(ticks, &now_tm);
+       dev_dbg(info->dev, "%s, now time : %lu\n", __func__, ticks);
+       rtc_next_alarm_time(&alarm_tm, &now_tm, &alrm->time);
+       /* get new ticks for alarm in 24 hours */
+       rtc_tm_to_time(&alarm_tm, &ticks);
+       dev_dbg(info->dev, "%s, alarm time: %lu\n", __func__, ticks);
+       data = ticks - base;
+
+       buf[0] = data & 0xff;
+       buf[1] = (data >> 8) & 0xff;
+       buf[2] = (data >> 16) & 0xff;
+       buf[3] = (data >> 24) & 0xff;
+       regmap_raw_write(info->map, PM800_RTC_EXPIRE1_1, buf, 4);
+       if (alrm->enabled) {
+               mask = PM800_ALARM | PM800_ALARM_WAKEUP | PM800_ALARM1_EN;
+               regmap_update_bits(info->map, PM800_RTC_CONTROL, mask, mask);
+       } else {
+               mask = PM800_ALARM | PM800_ALARM_WAKEUP | PM800_ALARM1_EN;
+               regmap_update_bits(info->map, PM800_RTC_CONTROL, mask,
+                                  PM800_ALARM | PM800_ALARM_WAKEUP);
+       }
+       return 0;
+}
+
+static const struct rtc_class_ops pm80x_rtc_ops = {
+       .read_time = pm80x_rtc_read_time,
+       .set_time = pm80x_rtc_set_time,
+       .read_alarm = pm80x_rtc_read_alarm,
+       .set_alarm = pm80x_rtc_set_alarm,
+       .alarm_irq_enable = pm80x_rtc_alarm_irq_enable,
+};
+
+#ifdef CONFIG_PM
+static int pm80x_rtc_suspend(struct device *dev)
+{
+       return pm80x_dev_suspend(dev);
+}
+
+static int pm80x_rtc_resume(struct device *dev)
+{
+       return pm80x_dev_resume(dev);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm80x_rtc_pm_ops, pm80x_rtc_suspend, pm80x_rtc_resume);
+
+static int __devinit pm80x_rtc_probe(struct platform_device *pdev)
+{
+       struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+       struct pm80x_platform_data *pm80x_pdata;
+       struct pm80x_rtc_pdata *pdata = NULL;
+       struct pm80x_rtc_info *info;
+       struct rtc_time tm;
+       unsigned long ticks = 0;
+       int ret;
+
+       pdata = pdev->dev.platform_data;
+       if (pdata == NULL)
+               dev_warn(&pdev->dev, "No platform data!\n");
+
+       info =
+           devm_kzalloc(&pdev->dev, sizeof(struct pm80x_rtc_info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+       info->irq = platform_get_irq(pdev, 0);
+       if (info->irq < 0) {
+               dev_err(&pdev->dev, "No IRQ resource!\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       info->chip = chip;
+       info->map = chip->regmap;
+       if (!info->map) {
+               dev_err(&pdev->dev, "no regmap!\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       info->dev = &pdev->dev;
+       dev_set_drvdata(&pdev->dev, info);
+
+       ret = pm80x_request_irq(chip, info->irq, rtc_update_handler,
+                               IRQF_ONESHOT, "rtc", info);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+                       info->irq, ret);
+               goto out;
+       }
+
+       ret = pm80x_rtc_read_time(&pdev->dev, &tm);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to read initial time.\n");
+               goto out_rtc;
+       }
+       if ((tm.tm_year < 70) || (tm.tm_year > 138)) {
+               tm.tm_year = 70;
+               tm.tm_mon = 0;
+               tm.tm_mday = 1;
+               tm.tm_hour = 0;
+               tm.tm_min = 0;
+               tm.tm_sec = 0;
+               ret = pm80x_rtc_set_time(&pdev->dev, &tm);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "Failed to set initial time.\n");
+                       goto out_rtc;
+               }
+       }
+       rtc_tm_to_time(&tm, &ticks);
+
+       info->rtc_dev = rtc_device_register("88pm80x-rtc", &pdev->dev,
+                                           &pm80x_rtc_ops, THIS_MODULE);
+       if (IS_ERR(info->rtc_dev)) {
+               ret = PTR_ERR(info->rtc_dev);
+               dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+               goto out_rtc;
+       }
+       /*
+        * enable internal XO instead of internal 3.25MHz clock since it can
+        * free running in PMIC power-down state.
+        */
+       regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_RTC1_USE_XO,
+                          PM800_RTC1_USE_XO);
+
+       if (pdev->dev.parent->platform_data) {
+               pm80x_pdata = pdev->dev.parent->platform_data;
+               pdata = pm80x_pdata->rtc;
+               if (pdata)
+                       info->rtc_dev->dev.platform_data = &pdata->rtc_wakeup;
+       }
+
+       device_init_wakeup(&pdev->dev, 1);
+
+       return 0;
+out_rtc:
+       pm80x_free_irq(chip, info->irq, info);
+out:
+       return ret;
+}
+
+static int __devexit pm80x_rtc_remove(struct platform_device *pdev)
+{
+       struct pm80x_rtc_info *info = platform_get_drvdata(pdev);
+       platform_set_drvdata(pdev, NULL);
+       rtc_device_unregister(info->rtc_dev);
+       pm80x_free_irq(info->chip, info->irq, info);
+       return 0;
+}
+
+static struct platform_driver pm80x_rtc_driver = {
+       .driver = {
+                  .name = "88pm80x-rtc",
+                  .owner = THIS_MODULE,
+                  .pm = &pm80x_rtc_pm_ops,
+                  },
+       .probe = pm80x_rtc_probe,
+       .remove = __devexit_p(pm80x_rtc_remove),
+};
+
+module_platform_driver(pm80x_rtc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Marvell 88PM80x RTC driver");
+MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
+MODULE_ALIAS("platform:88pm80x-rtc");
index 370889d0489bf9e4f196abcbbcd6f11b25e6f5a8..bf3c2f669c3c50584a1c63760ede523b9aa10cfb 100644 (file)
@@ -89,22 +89,17 @@ static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
        if (retval < 0)
                return retval;
 
-       /* Early AB8500 chips will not clear the rtc read request bit */
-       if (abx500_get_chip_id(dev) == 0) {
-               usleep_range(1000, 1000);
-       } else {
-               /* Wait for some cycles after enabling the rtc read in ab8500 */
-               while (time_before(jiffies, timeout)) {
-                       retval = abx500_get_register_interruptible(dev,
-                               AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value);
-                       if (retval < 0)
-                               return retval;
-
-                       if (!(value & RTC_READ_REQUEST))
-                               break;
-
-                       usleep_range(1000, 5000);
-               }
+       /* Wait for some cycles after enabling the rtc read in ab8500 */
+       while (time_before(jiffies, timeout)) {
+               retval = abx500_get_register_interruptible(dev,
+                       AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value);
+               if (retval < 0)
+                       return retval;
+
+               if (!(value & RTC_READ_REQUEST))
+                       break;
+
+               usleep_range(1000, 5000);
        }
 
        /* Read the Watchtime registers */
@@ -225,7 +220,8 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 {
        int retval, i;
        unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)];
-       unsigned long mins, secs = 0;
+       unsigned long mins, secs = 0, cursec = 0;
+       struct rtc_time curtm;
 
        if (alarm->time.tm_year < (AB8500_RTC_EPOCH - 1900)) {
                dev_dbg(dev, "year should be equal to or greater than %d\n",
@@ -236,6 +232,18 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
        /* Get the number of seconds since 1970 */
        rtc_tm_to_time(&alarm->time, &secs);
 
+       /*
+        * Check whether alarm is set less than 1min.
+        * Since our RTC doesn't support alarm resolution less than 1min,
+        * return -EINVAL, so UIE EMUL can take it up, incase of UIE_ON
+        */
+       ab8500_rtc_read_time(dev, &curtm); /* Read current time */
+       rtc_tm_to_time(&curtm, &cursec);
+       if ((secs - cursec) < 59) {
+               dev_dbg(dev, "Alarm less than 1 minute not supported\r\n");
+               return -EINVAL;
+       }
+
        /*
         * Convert it to the number of seconds since 01-01-2000 00:00:00, since
         * we only have a small counter in the RTC.
index a5b8a0c4ea842078a31bcfcf5be54c60240ec50e..76b2156d3c62252c80aec1eacce8fe0684950d75 100644 (file)
@@ -155,13 +155,10 @@ static int __exit coh901331_remove(struct platform_device *pdev)
        struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev);
 
        if (rtap) {
-               free_irq(rtap->irq, rtap);
                rtc_device_unregister(rtap->rtc);
+               clk_unprepare(rtap->clk);
                clk_put(rtap->clk);
-               iounmap(rtap->virtbase);
-               release_mem_region(rtap->phybase, rtap->physize);
                platform_set_drvdata(pdev, NULL);
-               kfree(rtap);
        }
 
        return 0;
@@ -174,49 +171,43 @@ static int __init coh901331_probe(struct platform_device *pdev)
        struct coh901331_port *rtap;
        struct resource *res;
 
-       rtap = kzalloc(sizeof(struct coh901331_port), GFP_KERNEL);
+       rtap = devm_kzalloc(&pdev->dev,
+                           sizeof(struct coh901331_port), GFP_KERNEL);
        if (!rtap)
                return -ENOMEM;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               ret = -ENOENT;
-               goto out_no_resource;
-       }
+       if (!res)
+               return -ENOENT;
+
        rtap->phybase = res->start;
        rtap->physize = resource_size(res);
 
-       if (request_mem_region(rtap->phybase, rtap->physize,
-                              "rtc-coh901331") == NULL) {
-               ret = -EBUSY;
-               goto out_no_memregion;
-       }
+       if (devm_request_mem_region(&pdev->dev, rtap->phybase, rtap->physize,
+                                   "rtc-coh901331") == NULL)
+               return -EBUSY;
 
-       rtap->virtbase = ioremap(rtap->phybase, rtap->physize);
-       if (!rtap->virtbase) {
-               ret = -ENOMEM;
-               goto out_no_remap;
-       }
+       rtap->virtbase = devm_ioremap(&pdev->dev, rtap->phybase, rtap->physize);
+       if (!rtap->virtbase)
+               return -ENOMEM;
 
        rtap->irq = platform_get_irq(pdev, 0);
-       if (request_irq(rtap->irq, coh901331_interrupt, 0,
-                       "RTC COH 901 331 Alarm", rtap)) {
-               ret = -EIO;
-               goto out_no_irq;
-       }
+       if (devm_request_irq(&pdev->dev, rtap->irq, coh901331_interrupt, 0,
+                            "RTC COH 901 331 Alarm", rtap))
+               return -EIO;
 
        rtap->clk = clk_get(&pdev->dev, NULL);
        if (IS_ERR(rtap->clk)) {
                ret = PTR_ERR(rtap->clk);
                dev_err(&pdev->dev, "could not get clock\n");
-               goto out_no_clk;
+               return ret;
        }
 
        /* We enable/disable the clock only to assure it works */
-       ret = clk_enable(rtap->clk);
+       ret = clk_prepare_enable(rtap->clk);
        if (ret) {
                dev_err(&pdev->dev, "could not enable clock\n");
-               goto out_no_clk_enable;
+               goto out_no_clk_prepenable;
        }
        clk_disable(rtap->clk);
 
@@ -232,18 +223,9 @@ static int __init coh901331_probe(struct platform_device *pdev)
 
  out_no_rtc:
        platform_set_drvdata(pdev, NULL);
- out_no_clk_enable:
+       clk_unprepare(rtap->clk);
+ out_no_clk_prepenable:
        clk_put(rtap->clk);
- out_no_clk:
-       free_irq(rtap->irq, rtap);
- out_no_irq:
-       iounmap(rtap->virtbase);
- out_no_remap:
-       platform_set_drvdata(pdev, NULL);
- out_no_memregion:
-       release_mem_region(rtap->phybase, SZ_4K);
- out_no_resource:
-       kfree(rtap);
        return ret;
 }
 
@@ -265,6 +247,7 @@ static int coh901331_suspend(struct platform_device *pdev, pm_message_t state)
                writel(0, rtap->virtbase + COH901331_IRQ_MASK);
                clk_disable(rtap->clk);
        }
+       clk_unprepare(rtap->clk);
        return 0;
 }
 
@@ -272,6 +255,7 @@ static int coh901331_resume(struct platform_device *pdev)
 {
        struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev);
 
+       clk_prepare(rtap->clk);
        if (device_may_wakeup(&pdev->dev)) {
                disable_irq_wake(rtap->irq);
        } else {
@@ -293,6 +277,7 @@ static void coh901331_shutdown(struct platform_device *pdev)
        clk_enable(rtap->clk);
        writel(0, rtap->virtbase + COH901331_IRQ_MASK);
        clk_disable(rtap->clk);
+       clk_unprepare(rtap->clk);
 }
 
 static struct platform_driver coh901331_driver = {
index da6ab5291a414c70b8ed29b57243f8a9192d14c3..78070255bd3f834d7aa8efa937e5ca552c07f4d1 100644 (file)
@@ -245,7 +245,7 @@ static int __devinit da9052_rtc_probe(struct platform_device *pdev)
                                   "ALM", rtc);
        if (ret != 0) {
                rtc_err(rtc->da9052, "irq registration failed: %d\n", ret);
-               goto err_mem;
+               return ret;
        }
 
        rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
@@ -259,8 +259,6 @@ static int __devinit da9052_rtc_probe(struct platform_device *pdev)
 
 err_free_irq:
        free_irq(rtc->irq, rtc);
-err_mem:
-       devm_kfree(&pdev->dev, rtc);
        return ret;
 }
 
@@ -271,7 +269,6 @@ static int __devexit da9052_rtc_remove(struct platform_device *pdev)
        rtc_device_unregister(rtc->rtc);
        free_irq(rtc->irq, rtc);
        platform_set_drvdata(pdev, NULL);
-       devm_kfree(&pdev->dev, rtc);
 
        return 0;
 }
index 1459055a83aaad94a522f093b045bad2a9812b6b..34e4349611dbdd6302a33d7b2917f1144a7ef306 100644 (file)
@@ -69,6 +69,7 @@ struct max8925_rtc_info {
        struct max8925_chip     *chip;
        struct i2c_client       *rtc;
        struct device           *dev;
+       int                     irq;
 };
 
 static irqreturn_t rtc_update_handler(int irq, void *data)
@@ -250,7 +251,7 @@ static int __devinit max8925_rtc_probe(struct platform_device *pdev)
 {
        struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
        struct max8925_rtc_info *info;
-       int irq, ret;
+       int ret;
 
        info = kzalloc(sizeof(struct max8925_rtc_info), GFP_KERNEL);
        if (!info)
@@ -258,13 +259,13 @@ static int __devinit max8925_rtc_probe(struct platform_device *pdev)
        info->chip = chip;
        info->rtc = chip->rtc;
        info->dev = &pdev->dev;
-       irq = chip->irq_base + MAX8925_IRQ_RTC_ALARM0;
+       info->irq = platform_get_irq(pdev, 0);
 
-       ret = request_threaded_irq(irq, NULL, rtc_update_handler,
+       ret = request_threaded_irq(info->irq, NULL, rtc_update_handler,
                                   IRQF_ONESHOT, "rtc-alarm0", info);
        if (ret < 0) {
                dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
-                       irq, ret);
+                       info->irq, ret);
                goto out_irq;
        }
 
@@ -285,7 +286,7 @@ static int __devinit max8925_rtc_probe(struct platform_device *pdev)
        return 0;
 out_rtc:
        platform_set_drvdata(pdev, NULL);
-       free_irq(chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
+       free_irq(info->irq, info);
 out_irq:
        kfree(info);
        return ret;
@@ -296,7 +297,7 @@ static int __devexit max8925_rtc_remove(struct platform_device *pdev)
        struct max8925_rtc_info *info = platform_get_drvdata(pdev);
 
        if (info) {
-               free_irq(info->chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
+               free_irq(info->irq, info);
                rtc_device_unregister(info->rtc_dev);
                kfree(info);
        }
index 546f6850bffbd9d0a9f884528b36d81296a08110..2643d8874925c6d10e82c4939724e1d94e63e0d9 100644 (file)
@@ -404,9 +404,12 @@ static const struct platform_device_id mc13xxx_rtc_idtable[] = {
                .name = "mc13783-rtc",
        }, {
                .name = "mc13892-rtc",
+       }, {
+               .name = "mc34708-rtc",
        },
-       { }
+       { /* sentinel */ }
 };
+MODULE_DEVICE_TABLE(platform, mc13xxx_rtc_idtable);
 
 static struct platform_driver mc13xxx_rtc_driver = {
        .id_table = mc13xxx_rtc_idtable,
@@ -432,4 +435,3 @@ module_exit(mc13xxx_rtc_exit);
 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
 MODULE_DESCRIPTION("RTC driver for Freescale MC13XXX PMIC");
 MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" DRIVER_NAME);
index 97a3284bb7c60920be7204156fab79b2dabafec6..c2fe426a6ef2d384e50835c1f6f05bc7e3418b9e 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/rtc.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/of.h>
 
 #define DRV_VERSION "0.4.3"
 
@@ -285,9 +286,19 @@ static const struct i2c_device_id pcf8563_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, pcf8563_id);
 
+#ifdef CONFIG_OF
+static const struct of_device_id pcf8563_of_match[] __devinitconst = {
+       { .compatible = "nxp,pcf8563" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, pcf8563_of_match);
+#endif
+
 static struct i2c_driver pcf8563_driver = {
        .driver         = {
                .name   = "rtc-pcf8563",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(pcf8563_of_match),
        },
        .probe          = pcf8563_probe,
        .remove         = pcf8563_remove,
index cc0533994f6e0650bbd7c2bc2a4976cfb947593e..08378e3cc21cf8209796d50876ee64ae5c0a4aaa 100644 (file)
 
 #define RTC_TIMER_FREQ 32768
 
+/**
+ * struct pl031_vendor_data - per-vendor variations
+ * @ops: the vendor-specific operations used on this silicon version
+ * @clockwatch: if this is an ST Microelectronics silicon version with a
+ *     clockwatch function
+ * @st_weekday: if this is an ST Microelectronics silicon version that need
+ *     the weekday fix
+ * @irqflags: special IRQ flags per variant
+ */
+struct pl031_vendor_data {
+       struct rtc_class_ops ops;
+       bool clockwatch;
+       bool st_weekday;
+       unsigned long irqflags;
+};
+
 struct pl031_local {
+       struct pl031_vendor_data *vendor;
        struct rtc_device *rtc;
        void __iomem *base;
-       u8 hw_designer;
-       u8 hw_revision:4;
 };
 
 static int pl031_alarm_irq_enable(struct device *dev,
@@ -303,7 +318,8 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
 {
        int ret;
        struct pl031_local *ldata;
-       struct rtc_class_ops *ops = id->data;
+       struct pl031_vendor_data *vendor = id->data;
+       struct rtc_class_ops *ops = &vendor->ops;
        unsigned long time;
 
        ret = amba_request_regions(adev, NULL);
@@ -315,6 +331,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
                ret = -ENOMEM;
                goto out;
        }
+       ldata->vendor = vendor;
 
        ldata->base = ioremap(adev->res.start, resource_size(&adev->res));
 
@@ -325,14 +342,11 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
 
        amba_set_drvdata(adev, ldata);
 
-       ldata->hw_designer = amba_manf(adev);
-       ldata->hw_revision = amba_rev(adev);
-
-       dev_dbg(&adev->dev, "designer ID = 0x%02x\n", ldata->hw_designer);
-       dev_dbg(&adev->dev, "revision = 0x%01x\n", ldata->hw_revision);
+       dev_dbg(&adev->dev, "designer ID = 0x%02x\n", amba_manf(adev));
+       dev_dbg(&adev->dev, "revision = 0x%01x\n", amba_rev(adev));
 
        /* Enable the clockwatch on ST Variants */
-       if (ldata->hw_designer == AMBA_VENDOR_ST)
+       if (vendor->clockwatch)
                writel(readl(ldata->base + RTC_CR) | RTC_CR_CWEN,
                       ldata->base + RTC_CR);
 
@@ -340,7 +354,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
         * On ST PL031 variants, the RTC reset value does not provide correct
         * weekday for 2000-01-01. Correct the erroneous sunday to saturday.
         */
-       if (ldata->hw_designer == AMBA_VENDOR_ST) {
+       if (vendor->st_weekday) {
                if (readl(ldata->base + RTC_YDR) == 0x2000) {
                        time = readl(ldata->base + RTC_DR);
                        if ((time &
@@ -361,7 +375,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
        }
 
        if (request_irq(adev->irq[0], pl031_interrupt,
-                       0, "rtc-pl031", ldata)) {
+                       vendor->irqflags, "rtc-pl031", ldata)) {
                ret = -EIO;
                goto out_no_irq;
        }
@@ -383,48 +397,65 @@ err_req:
 }
 
 /* Operations for the original ARM version */
-static struct rtc_class_ops arm_pl031_ops = {
-       .read_time = pl031_read_time,
-       .set_time = pl031_set_time,
-       .read_alarm = pl031_read_alarm,
-       .set_alarm = pl031_set_alarm,
-       .alarm_irq_enable = pl031_alarm_irq_enable,
+static struct pl031_vendor_data arm_pl031 = {
+       .ops = {
+               .read_time = pl031_read_time,
+               .set_time = pl031_set_time,
+               .read_alarm = pl031_read_alarm,
+               .set_alarm = pl031_set_alarm,
+               .alarm_irq_enable = pl031_alarm_irq_enable,
+       },
+       .irqflags = IRQF_NO_SUSPEND,
 };
 
 /* The First ST derivative */
-static struct rtc_class_ops stv1_pl031_ops = {
-       .read_time = pl031_read_time,
-       .set_time = pl031_set_time,
-       .read_alarm = pl031_read_alarm,
-       .set_alarm = pl031_set_alarm,
-       .alarm_irq_enable = pl031_alarm_irq_enable,
+static struct pl031_vendor_data stv1_pl031 = {
+       .ops = {
+               .read_time = pl031_read_time,
+               .set_time = pl031_set_time,
+               .read_alarm = pl031_read_alarm,
+               .set_alarm = pl031_set_alarm,
+               .alarm_irq_enable = pl031_alarm_irq_enable,
+       },
+       .clockwatch = true,
+       .st_weekday = true,
+       .irqflags = IRQF_NO_SUSPEND,
 };
 
 /* And the second ST derivative */
-static struct rtc_class_ops stv2_pl031_ops = {
-       .read_time = pl031_stv2_read_time,
-       .set_time = pl031_stv2_set_time,
-       .read_alarm = pl031_stv2_read_alarm,
-       .set_alarm = pl031_stv2_set_alarm,
-       .alarm_irq_enable = pl031_alarm_irq_enable,
+static struct pl031_vendor_data stv2_pl031 = {
+       .ops = {
+               .read_time = pl031_stv2_read_time,
+               .set_time = pl031_stv2_set_time,
+               .read_alarm = pl031_stv2_read_alarm,
+               .set_alarm = pl031_stv2_set_alarm,
+               .alarm_irq_enable = pl031_alarm_irq_enable,
+       },
+       .clockwatch = true,
+       .st_weekday = true,
+       /*
+        * This variant shares the IRQ with another block and must not
+        * suspend that IRQ line.
+        */
+       .irqflags = IRQF_SHARED | IRQF_NO_SUSPEND,
 };
 
 static struct amba_id pl031_ids[] = {
        {
                .id = 0x00041031,
                .mask = 0x000fffff,
-               .data = &arm_pl031_ops,
+               .data = &arm_pl031,
        },
        /* ST Micro variants */
        {
                .id = 0x00180031,
                .mask = 0x00ffffff,
-               .data = &stv1_pl031_ops,
+               .data = &stv1_pl031,
        },
        {
                .id = 0x00280031,
                .mask = 0x00ffffff,
-               .data = &stv2_pl031_ops,
+               .data = &stv2_pl031,
        },
        {0, 0},
 };
index 33b6ba0afa0de1b5034970ef7b78d7d3b468ed4f..2c183ebff715f7acc277a82558544af53083ef4b 100644 (file)
@@ -138,8 +138,7 @@ static int __devinit r9701_probe(struct spi_device *spi)
         * contain invalid values. If so, try to write a default date:
         * 2000/1/1 00:00:00
         */
-       r9701_get_datetime(&spi->dev, &dt);
-       if (rtc_valid_tm(&dt)) {
+       if (r9701_get_datetime(&spi->dev, &dt)) {
                dev_info(&spi->dev, "trying to repair invalid date/time\n");
                dt.tm_sec  = 0;
                dt.tm_min  = 0;
@@ -148,7 +147,8 @@ static int __devinit r9701_probe(struct spi_device *spi)
                dt.tm_mon  = 0;
                dt.tm_year = 100;
 
-               if (r9701_set_datetime(&spi->dev, &dt)) {
+               if (r9701_set_datetime(&spi->dev, &dt) ||
+                               r9701_get_datetime(&spi->dev, &dt)) {
                        dev_err(&spi->dev, "cannot repair RTC register\n");
                        return -ENODEV;
                }
index 7e6af0b22f17d0e3d54bad2e8981689e82be8e81..bfbd92c8d1c9d9cba042b494cfde450066320dea 100644 (file)
 #include <linux/log2.h>
 #include <linux/slab.h>
 #include <linux/of.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
 
 #include <mach/hardware.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
 #include <asm/irq.h>
 #include <plat/regs-rtc.h>
 
index 59c6245e0421f21a70548108a2607ebdaa4bbd33..ea5c6f857ca5db35ebbfd3c581b0d5e7e32050d4 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/mfd/wm831x/core.h>
 #include <linux/delay.h>
 #include <linux/platform_device.h>
-
+#include <linux/random.h>
 
 /*
  * R16416 (0x4020) - RTC Write Counter
@@ -96,6 +96,26 @@ struct wm831x_rtc {
        unsigned int alarm_enabled:1;
 };
 
+static void wm831x_rtc_add_randomness(struct wm831x *wm831x)
+{
+       int ret;
+       u16 reg;
+
+       /*
+        * The write counter contains a pseudo-random number which is
+        * regenerated every time we set the RTC so it should be a
+        * useful per-system source of entropy.
+        */
+       ret = wm831x_reg_read(wm831x, WM831X_RTC_WRITE_COUNTER);
+       if (ret >= 0) {
+               reg = ret;
+               add_device_randomness(&reg, sizeof(reg));
+       } else {
+               dev_warn(wm831x->dev, "Failed to read RTC write counter: %d\n",
+                        ret);
+       }
+}
+
 /*
  * Read current time and date in RTC
  */
@@ -431,6 +451,8 @@ static int wm831x_rtc_probe(struct platform_device *pdev)
                        alm_irq, ret);
        }
 
+       wm831x_rtc_add_randomness(wm831x);
+
        return 0;
 
 err:
index 2d1e68db9b3ffb5828482cdda3d797e3fa1d6677..e894ca7b54c0c895cd92cf15134f0b6196d71c43 100644 (file)
@@ -4146,45 +4146,7 @@ fc_bsg_rportadd(struct Scsi_Host *shost, struct fc_rport *rport)
 static void
 fc_bsg_remove(struct request_queue *q)
 {
-       struct request *req; /* block request */
-       int counts; /* totals for request_list count and starved */
-
        if (q) {
-               /* Stop taking in new requests */
-               spin_lock_irq(q->queue_lock);
-               blk_stop_queue(q);
-
-               /* drain all requests in the queue */
-               while (1) {
-                       /* need the lock to fetch a request
-                        * this may fetch the same reqeust as the previous pass
-                        */
-                       req = blk_fetch_request(q);
-                       /* save requests in use and starved */
-                       counts = q->rq.count[0] + q->rq.count[1] +
-                               q->rq.starved[0] + q->rq.starved[1];
-                       spin_unlock_irq(q->queue_lock);
-                       /* any requests still outstanding? */
-                       if (counts == 0)
-                               break;
-
-                       /* This may be the same req as the previous iteration,
-                        * always send the blk_end_request_all after a prefetch.
-                        * It is not okay to not end the request because the
-                        * prefetch started the request.
-                        */
-                       if (req) {
-                               /* return -ENXIO to indicate that this queue is
-                                * going away
-                                */
-                               req->errors = -ENXIO;
-                               blk_end_request_all(req, -ENXIO);
-                       }
-
-                       msleep(200); /* allow bsg to possibly finish */
-                       spin_lock_irq(q->queue_lock);
-               }
-
                bsg_unregister_queue(q);
                blk_cleanup_queue(q);
        }
index 09809d06eccb3eba2b63b91767d410454d1854fa..fa1dfaa83e32986061586c4fcb2f6f8e9e23eaf9 100644 (file)
@@ -575,7 +575,7 @@ static int iscsi_remove_host(struct transport_container *tc,
        struct iscsi_cls_host *ihost = shost->shost_data;
 
        if (ihost->bsg_q) {
-               bsg_remove_queue(ihost->bsg_q);
+               bsg_unregister_queue(ihost->bsg_q);
                blk_cleanup_queue(ihost->bsg_q);
        }
        return 0;
index 4cde4fb0cd6cf491fff0c954f75d900b2ebbd7bd..5f84b5563c2d1baff1cd7a61ea2a9b3400388484 100644 (file)
@@ -144,6 +144,15 @@ config SPI_EP93XX
          This enables using the Cirrus EP93xx SPI controller in master
          mode.
 
+config SPI_FALCON
+       tristate "Falcon SPI controller support"
+       depends on SOC_FALCON
+       help
+         The external bus unit (EBU) found on the FALC-ON SoC has SPI
+         emulation that is designed for serial flash access. This driver
+         has only been tested with m25p80 type chips. The hardware has no
+         support for other types of SPI peripherals.
+
 config SPI_GPIO
        tristate "GPIO-based bitbanging SPI Master"
        depends on GENERIC_GPIO
index 273f50d1127a57041319953d13028a8303302ec8..3920dcf4c7400e2c64bfb82803316f5f27535a63 100644 (file)
@@ -26,6 +26,7 @@ obj-$(CONFIG_SPI_DW_MMIO)             += spi-dw-mmio.o
 obj-$(CONFIG_SPI_DW_PCI)               += spi-dw-midpci.o
 spi-dw-midpci-objs                     := spi-dw-pci.o spi-dw-mid.o
 obj-$(CONFIG_SPI_EP93XX)               += spi-ep93xx.o
+obj-$(CONFIG_SPI_FALCON)               += spi-falcon.o
 obj-$(CONFIG_SPI_FSL_LIB)              += spi-fsl-lib.o
 obj-$(CONFIG_SPI_FSL_ESPI)             += spi-fsl-espi.o
 obj-$(CONFIG_SPI_FSL_SPI)              += spi-fsl-spi.o
diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c
new file mode 100644 (file)
index 0000000..8f6aa73
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ *  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.
+ *
+ *  Copyright (C) 2012 Thomas Langer <thomas.langer@lantiq.com>
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include <lantiq_soc.h>
+
+#define DRV_NAME               "sflash-falcon"
+
+#define FALCON_SPI_XFER_BEGIN  (1 << 0)
+#define FALCON_SPI_XFER_END    (1 << 1)
+
+/* Bus Read Configuration Register0 */
+#define BUSRCON0               0x00000010
+/* Bus Write Configuration Register0 */
+#define BUSWCON0               0x00000018
+/* Serial Flash Configuration Register */
+#define SFCON                  0x00000080
+/* Serial Flash Time Register */
+#define SFTIME                 0x00000084
+/* Serial Flash Status Register */
+#define SFSTAT                 0x00000088
+/* Serial Flash Command Register */
+#define SFCMD                  0x0000008C
+/* Serial Flash Address Register */
+#define SFADDR                 0x00000090
+/* Serial Flash Data Register */
+#define SFDATA                 0x00000094
+/* Serial Flash I/O Control Register */
+#define SFIO                   0x00000098
+/* EBU Clock Control Register */
+#define EBUCC                  0x000000C4
+
+/* Dummy Phase Length */
+#define SFCMD_DUMLEN_OFFSET    16
+#define SFCMD_DUMLEN_MASK      0x000F0000
+/* Chip Select */
+#define SFCMD_CS_OFFSET                24
+#define SFCMD_CS_MASK          0x07000000
+/* field offset */
+#define SFCMD_ALEN_OFFSET      20
+#define SFCMD_ALEN_MASK                0x00700000
+/* SCK Rise-edge Position */
+#define SFTIME_SCKR_POS_OFFSET 8
+#define SFTIME_SCKR_POS_MASK   0x00000F00
+/* SCK Period */
+#define SFTIME_SCK_PER_OFFSET  0
+#define SFTIME_SCK_PER_MASK    0x0000000F
+/* SCK Fall-edge Position */
+#define SFTIME_SCKF_POS_OFFSET 12
+#define SFTIME_SCKF_POS_MASK   0x0000F000
+/* Device Size */
+#define SFCON_DEV_SIZE_A23_0   0x03000000
+#define SFCON_DEV_SIZE_MASK    0x0F000000
+/* Read Data Position */
+#define SFTIME_RD_POS_MASK     0x000F0000
+/* Data Output */
+#define SFIO_UNUSED_WD_MASK    0x0000000F
+/* Command Opcode mask */
+#define SFCMD_OPC_MASK         0x000000FF
+/* dlen bytes of data to write */
+#define SFCMD_DIR_WRITE                0x00000100
+/* Data Length offset */
+#define SFCMD_DLEN_OFFSET      9
+/* Command Error */
+#define SFSTAT_CMD_ERR         0x20000000
+/* Access Command Pending */
+#define SFSTAT_CMD_PEND                0x00400000
+/* Frequency set to 100MHz. */
+#define EBUCC_EBUDIV_SELF100   0x00000001
+/* Serial Flash */
+#define BUSRCON0_AGEN_SERIAL_FLASH     0xF0000000
+/* 8-bit multiplexed */
+#define BUSRCON0_PORTW_8_BIT_MUX       0x00000000
+/* Serial Flash */
+#define BUSWCON0_AGEN_SERIAL_FLASH     0xF0000000
+/* Chip Select after opcode */
+#define SFCMD_KEEP_CS_KEEP_SELECTED    0x00008000
+
+#define CLOCK_100M     100000000
+#define CLOCK_50M      50000000
+
+struct falcon_sflash {
+       u32 sfcmd; /* for caching of opcode, direction, ... */
+       struct spi_master *master;
+};
+
+int falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t,
+               unsigned long flags)
+{
+       struct device *dev = &spi->dev;
+       struct falcon_sflash *priv = spi_master_get_devdata(spi->master);
+       const u8 *txp = t->tx_buf;
+       u8 *rxp = t->rx_buf;
+       unsigned int bytelen = ((8 * t->len + 7) / 8);
+       unsigned int len, alen, dumlen;
+       u32 val;
+       enum {
+               state_init,
+               state_command_prepare,
+               state_write,
+               state_read,
+               state_disable_cs,
+               state_end
+       } state = state_init;
+
+       do {
+               switch (state) {
+               case state_init: /* detect phase of upper layer sequence */
+               {
+                       /* initial write ? */
+                       if (flags & FALCON_SPI_XFER_BEGIN) {
+                               if (!txp) {
+                                       dev_err(dev,
+                                               "BEGIN without tx data!\n");
+                                       return -ENODATA;
+                               }
+                               /*
+                                * Prepare the parts of the sfcmd register,
+                                * which should not change during a sequence!
+                                * Only exception are the length fields,
+                                * especially alen and dumlen.
+                                */
+
+                               priv->sfcmd = ((spi->chip_select
+                                               << SFCMD_CS_OFFSET)
+                                              & SFCMD_CS_MASK);
+                               priv->sfcmd |= SFCMD_KEEP_CS_KEEP_SELECTED;
+                               priv->sfcmd |= *txp;
+                               txp++;
+                               bytelen--;
+                               if (bytelen) {
+                                       /*
+                                        * more data:
+                                        * maybe address and/or dummy
+                                        */
+                                       state = state_command_prepare;
+                                       break;
+                               } else {
+                                       dev_dbg(dev, "write cmd %02X\n",
+                                               priv->sfcmd & SFCMD_OPC_MASK);
+                               }
+                       }
+                       /* continued write ? */
+                       if (txp && bytelen) {
+                               state = state_write;
+                               break;
+                       }
+                       /* read data? */
+                       if (rxp && bytelen) {
+                               state = state_read;
+                               break;
+                       }
+                       /* end of sequence? */
+                       if (flags & FALCON_SPI_XFER_END)
+                               state = state_disable_cs;
+                       else
+                               state = state_end;
+                       break;
+               }
+               /* collect tx data for address and dummy phase */
+               case state_command_prepare:
+               {
+                       /* txp is valid, already checked */
+                       val = 0;
+                       alen = 0;
+                       dumlen = 0;
+                       while (bytelen > 0) {
+                               if (alen < 3) {
+                                       val = (val << 8) | (*txp++);
+                                       alen++;
+                               } else if ((dumlen < 15) && (*txp == 0)) {
+                                       /*
+                                        * assume dummy bytes are set to 0
+                                        * from upper layer
+                                        */
+                                       dumlen++;
+                                       txp++;
+                               } else {
+                                       break;
+                               }
+                               bytelen--;
+                       }
+                       priv->sfcmd &= ~(SFCMD_ALEN_MASK | SFCMD_DUMLEN_MASK);
+                       priv->sfcmd |= (alen << SFCMD_ALEN_OFFSET) |
+                                        (dumlen << SFCMD_DUMLEN_OFFSET);
+                       if (alen > 0)
+                               ltq_ebu_w32(val, SFADDR);
+
+                       dev_dbg(dev, "wr %02X, alen=%d (addr=%06X) dlen=%d\n",
+                               priv->sfcmd & SFCMD_OPC_MASK,
+                               alen, val, dumlen);
+
+                       if (bytelen > 0) {
+                               /* continue with write */
+                               state = state_write;
+                       } else if (flags & FALCON_SPI_XFER_END) {
+                               /* end of sequence? */
+                               state = state_disable_cs;
+                       } else {
+                               /*
+                                * go to end and expect another
+                                * call (read or write)
+                                */
+                               state = state_end;
+                       }
+                       break;
+               }
+               case state_write:
+               {
+                       /* txp still valid */
+                       priv->sfcmd |= SFCMD_DIR_WRITE;
+                       len = 0;
+                       val = 0;
+                       do {
+                               if (bytelen--)
+                                       val |= (*txp++) << (8 * len++);
+                               if ((flags & FALCON_SPI_XFER_END)
+                                   && (bytelen == 0)) {
+                                       priv->sfcmd &=
+                                               ~SFCMD_KEEP_CS_KEEP_SELECTED;
+                               }
+                               if ((len == 4) || (bytelen == 0)) {
+                                       ltq_ebu_w32(val, SFDATA);
+                                       ltq_ebu_w32(priv->sfcmd
+                                               | (len<<SFCMD_DLEN_OFFSET),
+                                               SFCMD);
+                                       len = 0;
+                                       val = 0;
+                                       priv->sfcmd &= ~(SFCMD_ALEN_MASK
+                                                        | SFCMD_DUMLEN_MASK);
+                               }
+                       } while (bytelen);
+                       state = state_end;
+                       break;
+               }
+               case state_read:
+               {
+                       /* read data */
+                       priv->sfcmd &= ~SFCMD_DIR_WRITE;
+                       do {
+                               if ((flags & FALCON_SPI_XFER_END)
+                                   && (bytelen <= 4)) {
+                                       priv->sfcmd &=
+                                               ~SFCMD_KEEP_CS_KEEP_SELECTED;
+                               }
+                               len = (bytelen > 4) ? 4 : bytelen;
+                               bytelen -= len;
+                               ltq_ebu_w32(priv->sfcmd
+                                       | (len << SFCMD_DLEN_OFFSET), SFCMD);
+                               priv->sfcmd &= ~(SFCMD_ALEN_MASK
+                                                | SFCMD_DUMLEN_MASK);
+                               do {
+                                       val = ltq_ebu_r32(SFSTAT);
+                                       if (val & SFSTAT_CMD_ERR) {
+                                               /* reset error status */
+                                               dev_err(dev, "SFSTAT: CMD_ERR");
+                                               dev_err(dev, " (%x)\n", val);
+                                               ltq_ebu_w32(SFSTAT_CMD_ERR,
+                                                       SFSTAT);
+                                               return -EBADE;
+                                       }
+                               } while (val & SFSTAT_CMD_PEND);
+                               val = ltq_ebu_r32(SFDATA);
+                               do {
+                                       *rxp = (val & 0xFF);
+                                       rxp++;
+                                       val >>= 8;
+                                       len--;
+                               } while (len);
+                       } while (bytelen);
+                       state = state_end;
+                       break;
+               }
+               case state_disable_cs:
+               {
+                       priv->sfcmd &= ~SFCMD_KEEP_CS_KEEP_SELECTED;
+                       ltq_ebu_w32(priv->sfcmd | (0 << SFCMD_DLEN_OFFSET),
+                               SFCMD);
+                       val = ltq_ebu_r32(SFSTAT);
+                       if (val & SFSTAT_CMD_ERR) {
+                               /* reset error status */
+                               dev_err(dev, "SFSTAT: CMD_ERR (%x)\n", val);
+                               ltq_ebu_w32(SFSTAT_CMD_ERR, SFSTAT);
+                               return -EBADE;
+                       }
+                       state = state_end;
+                       break;
+               }
+               case state_end:
+                       break;
+               }
+       } while (state != state_end);
+
+       return 0;
+}
+
+static int falcon_sflash_setup(struct spi_device *spi)
+{
+       unsigned int i;
+       unsigned long flags;
+
+       if (spi->chip_select > 0)
+               return -ENODEV;
+
+       spin_lock_irqsave(&ebu_lock, flags);
+
+       if (spi->max_speed_hz >= CLOCK_100M) {
+               /* set EBU clock to 100 MHz */
+               ltq_sys1_w32_mask(0, EBUCC_EBUDIV_SELF100, EBUCC);
+               i = 1; /* divider */
+       } else {
+               /* set EBU clock to 50 MHz */
+               ltq_sys1_w32_mask(EBUCC_EBUDIV_SELF100, 0, EBUCC);
+
+               /* search for suitable divider */
+               for (i = 1; i < 7; i++) {
+                       if (CLOCK_50M / i <= spi->max_speed_hz)
+                               break;
+               }
+       }
+
+       /* setup period of serial clock */
+       ltq_ebu_w32_mask(SFTIME_SCKF_POS_MASK
+                    | SFTIME_SCKR_POS_MASK
+                    | SFTIME_SCK_PER_MASK,
+                    (i << SFTIME_SCKR_POS_OFFSET)
+                    | (i << (SFTIME_SCK_PER_OFFSET + 1)),
+                    SFTIME);
+
+       /*
+        * set some bits of unused_wd, to not trigger HOLD/WP
+        * signals on non QUAD flashes
+        */
+       ltq_ebu_w32((SFIO_UNUSED_WD_MASK & (0x8 | 0x4)), SFIO);
+
+       ltq_ebu_w32(BUSRCON0_AGEN_SERIAL_FLASH | BUSRCON0_PORTW_8_BIT_MUX,
+                       BUSRCON0);
+       ltq_ebu_w32(BUSWCON0_AGEN_SERIAL_FLASH, BUSWCON0);
+       /* set address wrap around to maximum for 24-bit addresses */
+       ltq_ebu_w32_mask(SFCON_DEV_SIZE_MASK, SFCON_DEV_SIZE_A23_0, SFCON);
+
+       spin_unlock_irqrestore(&ebu_lock, flags);
+
+       return 0;
+}
+
+static int falcon_sflash_prepare_xfer(struct spi_master *master)
+{
+       return 0;
+}
+
+static int falcon_sflash_unprepare_xfer(struct spi_master *master)
+{
+       return 0;
+}
+
+static int falcon_sflash_xfer_one(struct spi_master *master,
+                                       struct spi_message *m)
+{
+       struct falcon_sflash *priv = spi_master_get_devdata(master);
+       struct spi_transfer *t;
+       unsigned long spi_flags;
+       unsigned long flags;
+       int ret = 0;
+
+       priv->sfcmd = 0;
+       m->actual_length = 0;
+
+       spi_flags = FALCON_SPI_XFER_BEGIN;
+       list_for_each_entry(t, &m->transfers, transfer_list) {
+               if (list_is_last(&t->transfer_list, &m->transfers))
+                       spi_flags |= FALCON_SPI_XFER_END;
+
+               spin_lock_irqsave(&ebu_lock, flags);
+               ret = falcon_sflash_xfer(m->spi, t, spi_flags);
+               spin_unlock_irqrestore(&ebu_lock, flags);
+
+               if (ret)
+                       break;
+
+               m->actual_length += t->len;
+
+               WARN_ON(t->delay_usecs || t->cs_change);
+               spi_flags = 0;
+       }
+
+       m->status = ret;
+       m->complete(m->context);
+
+       return 0;
+}
+
+static int __devinit falcon_sflash_probe(struct platform_device *pdev)
+{
+       struct falcon_sflash *priv;
+       struct spi_master *master;
+       int ret;
+
+       if (ltq_boot_select() != BS_SPI) {
+               dev_err(&pdev->dev, "invalid bootstrap options\n");
+               return -ENODEV;
+       }
+
+       master = spi_alloc_master(&pdev->dev, sizeof(*priv));
+       if (!master)
+               return -ENOMEM;
+
+       priv = spi_master_get_devdata(master);
+       priv->master = master;
+
+       master->mode_bits = SPI_MODE_3;
+       master->num_chipselect = 1;
+       master->bus_num = -1;
+       master->setup = falcon_sflash_setup;
+       master->prepare_transfer_hardware = falcon_sflash_prepare_xfer;
+       master->transfer_one_message = falcon_sflash_xfer_one;
+       master->unprepare_transfer_hardware = falcon_sflash_unprepare_xfer;
+       master->dev.of_node = pdev->dev.of_node;
+
+       platform_set_drvdata(pdev, priv);
+
+       ret = spi_register_master(master);
+       if (ret)
+               spi_master_put(master);
+       return ret;
+}
+
+static int __devexit falcon_sflash_remove(struct platform_device *pdev)
+{
+       struct falcon_sflash *priv = platform_get_drvdata(pdev);
+
+       spi_unregister_master(priv->master);
+
+       return 0;
+}
+
+static const struct of_device_id falcon_sflash_match[] = {
+       { .compatible = "lantiq,sflash-falcon" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, falcon_sflash_match);
+
+static struct platform_driver falcon_sflash_driver = {
+       .probe  = falcon_sflash_probe,
+       .remove = __devexit_p(falcon_sflash_remove),
+       .driver = {
+               .name   = DRV_NAME,
+               .owner  = THIS_MODULE,
+               .of_match_table = falcon_sflash_match,
+       }
+};
+
+module_platform_driver(falcon_sflash_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Lantiq Falcon SPI/SFLASH controller driver");
index 7d46b15e1520d0613b02cca28a5206f5123db4db..bc4778175e343c6a9ebeceac09b0cc543c935bc2 100644 (file)
@@ -28,6 +28,8 @@
 #include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/omap-dma.h>
 #include <linux/platform_device.h>
 #include <linux/err.h>
 #include <linux/clk.h>
@@ -39,7 +41,6 @@
 
 #include <linux/spi/spi.h>
 
-#include <plat/dma.h>
 #include <plat/clock.h>
 #include <plat/mcspi.h>
 
@@ -93,8 +94,8 @@
 
 /* We have 2 DMA channels per CS, one for RX and one for TX */
 struct omap2_mcspi_dma {
-       int dma_tx_channel;
-       int dma_rx_channel;
+       struct dma_chan *dma_tx;
+       struct dma_chan *dma_rx;
 
        int dma_tx_sync_dev;
        int dma_rx_sync_dev;
@@ -300,20 +301,46 @@ static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
        return 0;
 }
 
+static void omap2_mcspi_rx_callback(void *data)
+{
+       struct spi_device *spi = data;
+       struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master);
+       struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+
+       complete(&mcspi_dma->dma_rx_completion);
+
+       /* We must disable the DMA RX request */
+       omap2_mcspi_set_dma_req(spi, 1, 0);
+}
+
+static void omap2_mcspi_tx_callback(void *data)
+{
+       struct spi_device *spi = data;
+       struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master);
+       struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+
+       complete(&mcspi_dma->dma_tx_completion);
+
+       /* We must disable the DMA TX request */
+       omap2_mcspi_set_dma_req(spi, 0, 0);
+}
+
 static unsigned
 omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 {
        struct omap2_mcspi      *mcspi;
        struct omap2_mcspi_cs   *cs = spi->controller_state;
        struct omap2_mcspi_dma  *mcspi_dma;
-       unsigned int            count, c;
-       unsigned long           base, tx_reg, rx_reg;
-       int                     word_len, data_type, element_count;
+       unsigned int            count;
+       int                     word_len, element_count;
        int                     elements = 0;
        u32                     l;
        u8                      * rx;
        const u8                * tx;
        void __iomem            *chstat_reg;
+       struct dma_slave_config cfg;
+       enum dma_slave_buswidth width;
+       unsigned es;
 
        mcspi = spi_master_get_devdata(spi->master);
        mcspi_dma = &mcspi->dma_channels[spi->chip_select];
@@ -321,68 +348,92 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 
        chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
 
+       if (cs->word_len <= 8) {
+               width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+               es = 1;
+       } else if (cs->word_len <= 16) {
+               width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+               es = 2;
+       } else {
+               width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+               es = 4;
+       }
+
+       memset(&cfg, 0, sizeof(cfg));
+       cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0;
+       cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0;
+       cfg.src_addr_width = width;
+       cfg.dst_addr_width = width;
+       cfg.src_maxburst = 1;
+       cfg.dst_maxburst = 1;
+
+       if (xfer->tx_buf && mcspi_dma->dma_tx) {
+               struct dma_async_tx_descriptor *tx;
+               struct scatterlist sg;
+
+               dmaengine_slave_config(mcspi_dma->dma_tx, &cfg);
+
+               sg_init_table(&sg, 1);
+               sg_dma_address(&sg) = xfer->tx_dma;
+               sg_dma_len(&sg) = xfer->len;
+
+               tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1,
+                       DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+               if (tx) {
+                       tx->callback = omap2_mcspi_tx_callback;
+                       tx->callback_param = spi;
+                       dmaengine_submit(tx);
+               } else {
+                       /* FIXME: fall back to PIO? */
+               }
+       }
+
+       if (xfer->rx_buf && mcspi_dma->dma_rx) {
+               struct dma_async_tx_descriptor *tx;
+               struct scatterlist sg;
+               size_t len = xfer->len - es;
+
+               dmaengine_slave_config(mcspi_dma->dma_rx, &cfg);
+
+               if (l & OMAP2_MCSPI_CHCONF_TURBO)
+                       len -= es;
+
+               sg_init_table(&sg, 1);
+               sg_dma_address(&sg) = xfer->rx_dma;
+               sg_dma_len(&sg) = len;
+
+               tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1,
+                       DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+               if (tx) {
+                       tx->callback = omap2_mcspi_rx_callback;
+                       tx->callback_param = spi;
+                       dmaengine_submit(tx);
+               } else {
+                       /* FIXME: fall back to PIO? */
+               }
+       }
+
        count = xfer->len;
-       c = count;
        word_len = cs->word_len;
 
-       base = cs->phys;
-       tx_reg = base + OMAP2_MCSPI_TX0;
-       rx_reg = base + OMAP2_MCSPI_RX0;
        rx = xfer->rx_buf;
        tx = xfer->tx_buf;
 
        if (word_len <= 8) {
-               data_type = OMAP_DMA_DATA_TYPE_S8;
                element_count = count;
        } else if (word_len <= 16) {
-               data_type = OMAP_DMA_DATA_TYPE_S16;
                element_count = count >> 1;
        } else /* word_len <= 32 */ {
-               data_type = OMAP_DMA_DATA_TYPE_S32;
                element_count = count >> 2;
        }
 
        if (tx != NULL) {
-               omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel,
-                               data_type, element_count, 1,
-                               OMAP_DMA_SYNC_ELEMENT,
-                               mcspi_dma->dma_tx_sync_dev, 0);
-
-               omap_set_dma_dest_params(mcspi_dma->dma_tx_channel, 0,
-                               OMAP_DMA_AMODE_CONSTANT,
-                               tx_reg, 0, 0);
-
-               omap_set_dma_src_params(mcspi_dma->dma_tx_channel, 0,
-                               OMAP_DMA_AMODE_POST_INC,
-                               xfer->tx_dma, 0, 0);
-       }
-
-       if (rx != NULL) {
-               elements = element_count - 1;
-               if (l & OMAP2_MCSPI_CHCONF_TURBO)
-                       elements--;
-
-               omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel,
-                               data_type, elements, 1,
-                               OMAP_DMA_SYNC_ELEMENT,
-                               mcspi_dma->dma_rx_sync_dev, 1);
-
-               omap_set_dma_src_params(mcspi_dma->dma_rx_channel, 0,
-                               OMAP_DMA_AMODE_CONSTANT,
-                               rx_reg, 0, 0);
-
-               omap_set_dma_dest_params(mcspi_dma->dma_rx_channel, 0,
-                               OMAP_DMA_AMODE_POST_INC,
-                               xfer->rx_dma, 0, 0);
-       }
-
-       if (tx != NULL) {
-               omap_start_dma(mcspi_dma->dma_tx_channel);
+               dma_async_issue_pending(mcspi_dma->dma_tx);
                omap2_mcspi_set_dma_req(spi, 0, 1);
        }
 
        if (rx != NULL) {
-               omap_start_dma(mcspi_dma->dma_rx_channel);
+               dma_async_issue_pending(mcspi_dma->dma_rx);
                omap2_mcspi_set_dma_req(spi, 1, 1);
        }
 
@@ -408,7 +459,10 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
                                 DMA_FROM_DEVICE);
                omap2_mcspi_set_enable(spi, 0);
 
+               elements = element_count - 1;
+
                if (l & OMAP2_MCSPI_CHCONF_TURBO) {
+                       elements--;
 
                        if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
                                   & OMAP2_MCSPI_CHSTAT_RXS)) {
@@ -725,64 +779,38 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
        return 0;
 }
 
-static void omap2_mcspi_dma_rx_callback(int lch, u16 ch_status, void *data)
-{
-       struct spi_device       *spi = data;
-       struct omap2_mcspi      *mcspi;
-       struct omap2_mcspi_dma  *mcspi_dma;
-
-       mcspi = spi_master_get_devdata(spi->master);
-       mcspi_dma = &(mcspi->dma_channels[spi->chip_select]);
-
-       complete(&mcspi_dma->dma_rx_completion);
-
-       /* We must disable the DMA RX request */
-       omap2_mcspi_set_dma_req(spi, 1, 0);
-}
-
-static void omap2_mcspi_dma_tx_callback(int lch, u16 ch_status, void *data)
-{
-       struct spi_device       *spi = data;
-       struct omap2_mcspi      *mcspi;
-       struct omap2_mcspi_dma  *mcspi_dma;
-
-       mcspi = spi_master_get_devdata(spi->master);
-       mcspi_dma = &(mcspi->dma_channels[spi->chip_select]);
-
-       complete(&mcspi_dma->dma_tx_completion);
-
-       /* We must disable the DMA TX request */
-       omap2_mcspi_set_dma_req(spi, 0, 0);
-}
-
 static int omap2_mcspi_request_dma(struct spi_device *spi)
 {
        struct spi_master       *master = spi->master;
        struct omap2_mcspi      *mcspi;
        struct omap2_mcspi_dma  *mcspi_dma;
+       dma_cap_mask_t mask;
+       unsigned sig;
 
        mcspi = spi_master_get_devdata(master);
        mcspi_dma = mcspi->dma_channels + spi->chip_select;
 
-       if (omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX",
-                       omap2_mcspi_dma_rx_callback, spi,
-                       &mcspi_dma->dma_rx_channel)) {
-               dev_err(&spi->dev, "no RX DMA channel for McSPI\n");
+       init_completion(&mcspi_dma->dma_rx_completion);
+       init_completion(&mcspi_dma->dma_tx_completion);
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       sig = mcspi_dma->dma_rx_sync_dev;
+       mcspi_dma->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+       if (!mcspi_dma->dma_rx) {
+               dev_err(&spi->dev, "no RX DMA engine channel for McSPI\n");
                return -EAGAIN;
        }
 
-       if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX",
-                       omap2_mcspi_dma_tx_callback, spi,
-                       &mcspi_dma->dma_tx_channel)) {
-               omap_free_dma(mcspi_dma->dma_rx_channel);
-               mcspi_dma->dma_rx_channel = -1;
-               dev_err(&spi->dev, "no TX DMA channel for McSPI\n");
+       sig = mcspi_dma->dma_tx_sync_dev;
+       mcspi_dma->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+       if (!mcspi_dma->dma_tx) {
+               dev_err(&spi->dev, "no TX DMA engine channel for McSPI\n");
+               dma_release_channel(mcspi_dma->dma_rx);
+               mcspi_dma->dma_rx = NULL;
                return -EAGAIN;
        }
 
-       init_completion(&mcspi_dma->dma_rx_completion);
-       init_completion(&mcspi_dma->dma_tx_completion);
-
        return 0;
 }
 
@@ -814,8 +842,7 @@ static int omap2_mcspi_setup(struct spi_device *spi)
                list_add_tail(&cs->node, &ctx->cs);
        }
 
-       if (mcspi_dma->dma_rx_channel == -1
-                       || mcspi_dma->dma_tx_channel == -1) {
+       if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) {
                ret = omap2_mcspi_request_dma(spi);
                if (ret < 0)
                        return ret;
@@ -850,13 +877,13 @@ static void omap2_mcspi_cleanup(struct spi_device *spi)
        if (spi->chip_select < spi->master->num_chipselect) {
                mcspi_dma = &mcspi->dma_channels[spi->chip_select];
 
-               if (mcspi_dma->dma_rx_channel != -1) {
-                       omap_free_dma(mcspi_dma->dma_rx_channel);
-                       mcspi_dma->dma_rx_channel = -1;
+               if (mcspi_dma->dma_rx) {
+                       dma_release_channel(mcspi_dma->dma_rx);
+                       mcspi_dma->dma_rx = NULL;
                }
-               if (mcspi_dma->dma_tx_channel != -1) {
-                       omap_free_dma(mcspi_dma->dma_tx_channel);
-                       mcspi_dma->dma_tx_channel = -1;
+               if (mcspi_dma->dma_tx) {
+                       dma_release_channel(mcspi_dma->dma_tx);
+                       mcspi_dma->dma_tx = NULL;
                }
        }
 }
@@ -1176,7 +1203,6 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
                        break;
                }
 
-               mcspi->dma_channels[i].dma_rx_channel = -1;
                mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start;
                sprintf(dma_ch_name, "tx%d", i);
                dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
@@ -1187,7 +1213,6 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
                        break;
                }
 
-               mcspi->dma_channels[i].dma_tx_channel = -1;
                mcspi->dma_channels[i].dma_tx_sync_dev = dma_res->start;
        }
 
index 9a60d4cd2184aa188e80b946e5ba398bac04943f..f545716c666d06fac279d87bf8cfe69664eb595e 100644 (file)
@@ -157,12 +157,7 @@ static int create_worker_threads(struct bcm_mini_adapter *psAdapter)
 
 static struct file *open_firmware_file(struct bcm_mini_adapter *Adapter, const char *path)
 {
-       struct file *flp = NULL;
-       mm_segment_t oldfs;
-       oldfs = get_fs();
-       set_fs(get_ds());
-       flp = filp_open(path, O_RDONLY, S_IRWXU);
-       set_fs(oldfs);
+       struct file *flp = filp_open(path, O_RDONLY, S_IRWXU);
        if (IS_ERR(flp)) {
                pr_err(DRV_NAME "Unable To Open File %s, err %ld", path, PTR_ERR(flp));
                flp = NULL;
@@ -183,14 +178,12 @@ static int BcmFileDownload(struct bcm_mini_adapter *Adapter, const char *path, u
 {
        int errorno = 0;
        struct file *flp = NULL;
-       mm_segment_t oldfs;
        struct timeval tv = {0};
 
        flp = open_firmware_file(Adapter, path);
        if (!flp) {
-               errorno = -ENOENT;
                BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Unable to Open %s\n", path);
-               goto exit_download;
+               return -ENOENT;
        }
        BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Opened file is = %s and length =0x%lx to be downloaded at =0x%x", path, (unsigned long)flp->f_dentry->d_inode->i_size, loc);
        do_gettimeofday(&tv);
@@ -201,10 +194,7 @@ static int BcmFileDownload(struct bcm_mini_adapter *Adapter, const char *path, u
                errorno = -EIO;
                goto exit_download;
        }
-       oldfs = get_fs();
-       set_fs(get_ds());
        vfs_llseek(flp, 0, 0);
-       set_fs(oldfs);
        if (Adapter->bcm_file_readback_from_chip(Adapter->pvInterfaceAdapter, flp, loc)) {
                BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Failed to read back firmware!");
                errorno = -EIO;
@@ -212,12 +202,7 @@ static int BcmFileDownload(struct bcm_mini_adapter *Adapter, const char *path, u
        }
 
 exit_download:
-       oldfs = get_fs();
-       set_fs(get_ds());
-       if (flp && !(IS_ERR(flp)))
-               filp_close(flp, current->files);
-       set_fs(oldfs);
-
+       filp_close(flp, NULL);
        return errorno;
 }
 
@@ -1056,10 +1041,8 @@ OUT:
 static int bcm_parse_target_params(struct bcm_mini_adapter *Adapter)
 {
        struct file *flp = NULL;
-       mm_segment_t oldfs = {0};
        char *buff;
        int len = 0;
-       loff_t pos = 0;
 
        buff = kmalloc(BUFFER_1K, GFP_KERNEL);
        if (!buff)
@@ -1079,20 +1062,16 @@ static int bcm_parse_target_params(struct bcm_mini_adapter *Adapter)
                Adapter->pstargetparams = NULL;
                return -ENOENT;
        }
-       oldfs = get_fs();
-       set_fs(get_ds());
-       len = vfs_read(flp, (void __user __force *)buff, BUFFER_1K, &pos);
-       set_fs(oldfs);
+       len = kernel_read(flp, 0, buff, BUFFER_1K);
+       filp_close(flp, NULL);
 
        if (len != sizeof(STARGETPARAMS)) {
                BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Mismatch in Target Param Structure!\n");
                kfree(buff);
                kfree(Adapter->pstargetparams);
                Adapter->pstargetparams = NULL;
-               filp_close(flp, current->files);
                return -ENOENT;
        }
-       filp_close(flp, current->files);
 
        /* Check for autolink in config params */
        /*
index 760efee23d4a875ff1f3d402812af161f7f10f29..65624bca8b3ab354f0cfb312bff1f69229a59357 100644 (file)
@@ -66,9 +66,8 @@ static int download_image(struct sdio_func *func, char *img_name)
                return -ENOENT;
        }
 
-       if (filp->f_dentry)
-               inode = filp->f_dentry->d_inode;
-       if (!inode || !S_ISREG(inode->i_mode)) {
+       inode = filp->f_dentry->d_inode;
+       if (!S_ISREG(inode->i_mode)) {
                printk(KERN_ERR "Invalid file type: %s\n", img_name);
                ret = -EINVAL;
                goto out;
@@ -123,7 +122,7 @@ static int download_image(struct sdio_func *func, char *img_name)
                pno++;
        }
 out:
-       filp_close(filp, current->files);
+       filp_close(filp, NULL);
        return ret;
 }
 
index fef290c38db67ae8620a8ff92179275fb564730b..e3dbd5a552ca00c1349f2ea7f20fc629a7ddc5e5 100644 (file)
@@ -173,14 +173,12 @@ int usb_boot(struct usb_device *usbdev, u16 pid)
        filp = filp_open(img_name, O_RDONLY | O_LARGEFILE, 0);
        if (IS_ERR(filp)) {
                printk(KERN_ERR "Can't find %s.\n", img_name);
-               set_fs(fs);
                ret = PTR_ERR(filp);
                goto restore_fs;
        }
 
-       if (filp->f_dentry)
-               inode = filp->f_dentry->d_inode;
-       if (!inode || !S_ISREG(inode->i_mode)) {
+       inode = filp->f_dentry->d_inode;
+       if (!S_ISREG(inode->i_mode)) {
                printk(KERN_ERR "Invalid file type: %s\n", img_name);
                ret = -EINVAL;
                goto out;
@@ -262,7 +260,7 @@ int usb_boot(struct usb_device *usbdev, u16 pid)
                ret = -EINVAL;
        }
 out:
-       filp_close(filp, current->files);
+       filp_close(filp, NULL);
 
 restore_fs:
        set_fs(fs);
@@ -322,13 +320,11 @@ static int em_download_image(struct usb_device *usbdev, char *path,
                goto restore_fs;
        }
 
-       if (filp->f_dentry) {
-               inode = filp->f_dentry->d_inode;
-               if (!inode || !S_ISREG(inode->i_mode)) {
-                       printk(KERN_ERR "Invalid file type: %s\n", path);
-                       ret = -EINVAL;
-                       goto out;
-               }
+       inode = filp->f_dentry->d_inode;
+       if (!S_ISREG(inode->i_mode)) {
+               printk(KERN_ERR "Invalid file type: %s\n", path);
+               ret = -EINVAL;
+               goto out;
        }
 
        buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL);
@@ -364,7 +360,7 @@ static int em_download_image(struct usb_device *usbdev, char *path,
                goto out;
 
 out:
-       filp_close(filp, current->files);
+       filp_close(filp, NULL);
 
 restore_fs:
        set_fs(fs);
index c365cdf714ea5c05b3feb9b0d36f38ab12fa960b..ebe5a27c06f5b97219f3f306f9f6d16744b402e4 100644 (file)
@@ -971,20 +971,7 @@ static struct pci_driver pci_driver = {
        .remove = __devexit_p(dt3155_remove),
 };
 
-static int __init
-dt3155_init_module(void)
-{
-       return pci_register_driver(&pci_driver);
-}
-
-static void __exit
-dt3155_exit_module(void)
-{
-       pci_unregister_driver(&pci_driver);
-}
-
-module_init(dt3155_init_module);
-module_exit(dt3155_exit_module);
+module_pci_driver(pci_driver);
 
 MODULE_DESCRIPTION("video4linux pci-driver for dt3155 frame grabber");
 MODULE_AUTHOR("Marin Mitov <mitov@issp.bas.bg>");
index a1c45e4dcdce37e0b1ac885fca0e8750591add80..8269c77dbf7df5b2a9d860d304a06c806c88448a 100644 (file)
@@ -3083,6 +3083,7 @@ static int create_video_urbs(struct easycap *peasycap)
                peasycap->allocation_video_urb += 1;
                pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL);
                if (!pdata_urb) {
+                       usb_free_urb(purb);
                        SAM("ERROR: Could not allocate struct data_urb.\n");
                        return -ENOMEM;
                }
index 4d20e9f741187460dee46dc23707d9f86882744a..951007a3fc96aa30e74632dc425d99f358dc4781 100644 (file)
@@ -171,7 +171,7 @@ static void cycle_delay(int cycle)
 }
 
 
-static int poll_main()
+static int poll_main(void)
 {
        unsigned char status_high, status_low;
 
index 945d9623550b621c2731091a7804c66e3949e25e..4afc3b4197385e4cda191553a5fcdfefca080988 100644 (file)
@@ -52,6 +52,7 @@
 #include <linux/io.h>
 #include <asm/irq.h>
 #include <linux/fcntl.h>
+#include <linux/platform_device.h>
 #ifdef LIRC_ON_SA1100
 #include <asm/hardware.h>
 #ifdef CONFIG_SA1100_COLLIE
@@ -487,9 +488,11 @@ static struct lirc_driver driver = {
        .owner          = THIS_MODULE,
 };
 
+static struct platform_device *lirc_sir_dev;
 
 static int init_chrdev(void)
 {
+       driver.dev = &lirc_sir_dev->dev;
        driver.minor = lirc_register_driver(&driver);
        if (driver.minor < 0) {
                printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
@@ -1215,20 +1218,71 @@ static int init_lirc_sir(void)
        return 0;
 }
 
+static int __devinit lirc_sir_probe(struct platform_device *dev)
+{
+       return 0;
+}
+
+static int __devexit lirc_sir_remove(struct platform_device *dev)
+{
+       return 0;
+}
+
+static struct platform_driver lirc_sir_driver = {
+       .probe          = lirc_sir_probe,
+       .remove         = __devexit_p(lirc_sir_remove),
+       .driver         = {
+               .name   = "lirc_sir",
+               .owner  = THIS_MODULE,
+       },
+};
 
 static int __init lirc_sir_init(void)
 {
        int retval;
 
+       retval = platform_driver_register(&lirc_sir_driver);
+       if (retval) {
+               printk(KERN_ERR LIRC_DRIVER_NAME ": Platform driver register "
+                      "failed!\n");
+               return -ENODEV;
+       }
+
+       lirc_sir_dev = platform_device_alloc("lirc_dev", 0);
+       if (!lirc_sir_dev) {
+               printk(KERN_ERR LIRC_DRIVER_NAME ": Platform device alloc "
+                      "failed!\n");
+               retval = -ENOMEM;
+               goto pdev_alloc_fail;
+       }
+
+       retval = platform_device_add(lirc_sir_dev);
+       if (retval) {
+               printk(KERN_ERR LIRC_DRIVER_NAME ": Platform device add "
+                      "failed!\n");
+               retval = -ENODEV;
+               goto pdev_add_fail;
+       }
+
        retval = init_chrdev();
        if (retval < 0)
-               return retval;
+               goto fail;
+
        retval = init_lirc_sir();
        if (retval) {
                drop_chrdev();
-               return retval;
+               goto fail;
        }
+
        return 0;
+
+fail:
+       platform_device_del(lirc_sir_dev);
+pdev_add_fail:
+       platform_device_put(lirc_sir_dev);
+pdev_alloc_fail:
+       platform_driver_unregister(&lirc_sir_driver);
+       return retval;
 }
 
 static void __exit lirc_sir_exit(void)
@@ -1236,6 +1290,8 @@ static void __exit lirc_sir_exit(void)
        drop_hardware();
        drop_chrdev();
        drop_port();
+       platform_device_unregister(lirc_sir_dev);
+       platform_driver_unregister(&lirc_sir_driver);
        printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
 }
 
index 7e6c4fa130dfb60bbd3dd6b002f5c563ec146aa8..539f739fe9e650562b64b4fb2a0d8ddfd3e9c57c 100644 (file)
@@ -20,5 +20,5 @@ TODO (general):
          - implement loopback of external sound jack with incoming audio?
          - implement pause/resume
 
-Plase send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc Ben Collins
+Plase send patches to Mauro Carvalho Chehab <mchehab@redhat.com> and Cc Ben Collins
 <bcollins@bluecherry.net>
index d2fd842e37cfce0a38ef45866bb11817a9d80e0a..3ee9b125797fec3e8aacd471be72f25c6fb429e5 100644 (file)
@@ -318,15 +318,4 @@ static struct pci_driver solo_pci_driver = {
        .remove = solo_pci_remove,
 };
 
-static int __init solo_module_init(void)
-{
-       return pci_register_driver(&solo_pci_driver);
-}
-
-static void __exit solo_module_exit(void)
-{
-       pci_unregister_driver(&solo_pci_driver);
-}
-
-module_init(solo_module_init);
-module_exit(solo_module_exit);
+module_pci_driver(solo_pci_driver);
index ef95a500b4dad28684adc99383bc795a6e396066..398070a3d29326b738553df6e280f93b1e5c03f9 100644 (file)
@@ -175,7 +175,7 @@ int solo_i2c_isr(struct solo_dev *solo_dev)
 
        solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_IIC);
 
-       if (status & (SOLO_IIC_STATE_TRNS & SOLO_IIC_STATE_SIG_ERR) ||
+       if (status & (SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR) ||
            solo_dev->i2c_id < 0) {
                solo_i2c_stop(solo_dev);
                return -ENXIO;
index e31949c9c87e37121376d71790acf0ad31af6c22..f15b31b37ca50920a6e4a314eea40f9e8d3185d5 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/ethtool.h>
 #include <linux/phy.h>
 #include <linux/ratelimit.h>
+#include <linux/of_mdio.h>
 
 #include <net/dst.h>
 
@@ -161,22 +162,23 @@ static void cvm_oct_adjust_link(struct net_device *dev)
 int cvm_oct_phy_setup_device(struct net_device *dev)
 {
        struct octeon_ethernet *priv = netdev_priv(dev);
+       struct device_node *phy_node;
 
-       int phy_addr = cvmx_helper_board_get_mii_address(priv->port);
-       if (phy_addr != -1) {
-               char phy_id[MII_BUS_ID_SIZE + 3];
+       if (!priv->of_node)
+               return 0;
 
-               snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "mdio-octeon-0", phy_addr);
+       phy_node = of_parse_phandle(priv->of_node, "phy-handle", 0);
+       if (!phy_node)
+               return 0;
 
-               priv->phydev = phy_connect(dev, phy_id, cvm_oct_adjust_link, 0,
-                                       PHY_INTERFACE_MODE_GMII);
+       priv->phydev = of_phy_connect(dev, phy_node, cvm_oct_adjust_link, 0,
+                                     PHY_INTERFACE_MODE_GMII);
+
+       if (priv->phydev == NULL)
+               return -ENODEV;
+
+       priv->last_link = 0;
+       phy_start_aneg(priv->phydev);
 
-               if (IS_ERR(priv->phydev)) {
-                       priv->phydev = NULL;
-                       return -1;
-               }
-               priv->last_link = 0;
-               phy_start_aneg(priv->phydev);
-       }
        return 0;
 }
index 18f7a790f73d52b42a33d0373c5f69174c076a33..683bedc74ddecfe9965b564c52437e53288648fa 100644 (file)
@@ -24,6 +24,7 @@
  * This file may also be available under a different license from Cavium.
  * Contact Cavium Networks for more information
 **********************************************************************/
+#include <linux/platform_device.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -32,6 +33,7 @@
 #include <linux/phy.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
+#include <linux/of_net.h>
 
 #include <net/dst.h>
 
@@ -113,15 +115,6 @@ int rx_napi_weight = 32;
 module_param(rx_napi_weight, int, 0444);
 MODULE_PARM_DESC(rx_napi_weight, "The NAPI WEIGHT parameter.");
 
-/*
- * The offset from mac_addr_base that should be used for the next port
- * that is configured.  By convention, if any mgmt ports exist on the
- * chip, they get the first mac addresses, The ports controlled by
- * this driver are numbered sequencially following any mgmt addresses
- * that may exist.
- */
-static unsigned int cvm_oct_mac_addr_offset;
-
 /**
  * cvm_oct_poll_queue - Workqueue for polling operations.
  */
@@ -176,7 +169,7 @@ static void cvm_oct_periodic_worker(struct work_struct *work)
                queue_delayed_work(cvm_oct_poll_queue, &priv->port_periodic_work, HZ);
  }
 
-static __init void cvm_oct_configure_common_hw(void)
+static __devinit void cvm_oct_configure_common_hw(void)
 {
        /* Setup the FPA */
        cvmx_fpa_enable();
@@ -396,23 +389,21 @@ static void cvm_oct_common_set_multicast_list(struct net_device *dev)
 
  * Returns Zero on success
  */
-static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
+static int cvm_oct_set_mac_filter(struct net_device *dev)
 {
        struct octeon_ethernet *priv = netdev_priv(dev);
        union cvmx_gmxx_prtx_cfg gmx_cfg;
        int interface = INTERFACE(priv->port);
        int index = INDEX(priv->port);
 
-       memcpy(dev->dev_addr, addr + 2, 6);
-
        if ((interface < 2)
            && (cvmx_helper_interface_get_mode(interface) !=
                CVMX_HELPER_INTERFACE_MODE_SPI)) {
                int i;
-               uint8_t *ptr = addr;
+               uint8_t *ptr = dev->dev_addr;
                uint64_t mac = 0;
                for (i = 0; i < 6; i++)
-                       mac = (mac << 8) | (uint64_t) (ptr[i + 2]);
+                       mac = (mac << 8) | (uint64_t)ptr[i];
 
                gmx_cfg.u64 =
                    cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
@@ -421,17 +412,17 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
 
                cvmx_write_csr(CVMX_GMXX_SMACX(index, interface), mac);
                cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM0(index, interface),
-                              ptr[2]);
+                              ptr[0]);
                cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM1(index, interface),
-                              ptr[3]);
+                              ptr[1]);
                cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM2(index, interface),
-                              ptr[4]);
+                              ptr[2]);
                cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM3(index, interface),
-                              ptr[5]);
+                              ptr[3]);
                cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM4(index, interface),
-                              ptr[6]);
+                              ptr[4]);
                cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM5(index, interface),
-                              ptr[7]);
+                              ptr[5]);
                cvm_oct_common_set_multicast_list(dev);
                cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
                               gmx_cfg.u64);
@@ -439,6 +430,15 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
        return 0;
 }
 
+static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
+{
+       int r = eth_mac_addr(dev, addr);
+
+       if (r)
+               return r;
+       return cvm_oct_set_mac_filter(dev);
+}
+
 /**
  * cvm_oct_common_init - per network device initialization
  * @dev:    Device to initialize
@@ -448,26 +448,17 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
 int cvm_oct_common_init(struct net_device *dev)
 {
        struct octeon_ethernet *priv = netdev_priv(dev);
-       struct sockaddr sa;
-       u64 mac = ((u64)(octeon_bootinfo->mac_addr_base[0] & 0xff) << 40) |
-               ((u64)(octeon_bootinfo->mac_addr_base[1] & 0xff) << 32) |
-               ((u64)(octeon_bootinfo->mac_addr_base[2] & 0xff) << 24) |
-               ((u64)(octeon_bootinfo->mac_addr_base[3] & 0xff) << 16) |
-               ((u64)(octeon_bootinfo->mac_addr_base[4] & 0xff) << 8) |
-               (u64)(octeon_bootinfo->mac_addr_base[5] & 0xff);
-
-       mac += cvm_oct_mac_addr_offset;
-       sa.sa_data[0] = (mac >> 40) & 0xff;
-       sa.sa_data[1] = (mac >> 32) & 0xff;
-       sa.sa_data[2] = (mac >> 24) & 0xff;
-       sa.sa_data[3] = (mac >> 16) & 0xff;
-       sa.sa_data[4] = (mac >> 8) & 0xff;
-       sa.sa_data[5] = mac & 0xff;
-
-       if (cvm_oct_mac_addr_offset >= octeon_bootinfo->mac_addr_count)
-               printk(KERN_DEBUG "%s: Using MAC outside of the assigned range:"
-                       " %pM\n", dev->name, sa.sa_data);
-       cvm_oct_mac_addr_offset++;
+       const u8 *mac = NULL;
+
+       if (priv->of_node)
+               mac = of_get_mac_address(priv->of_node);
+
+       if (mac && is_valid_ether_addr(mac)) {
+               memcpy(dev->dev_addr, mac, ETH_ALEN);
+               dev->addr_assign_type &= ~NET_ADDR_RANDOM;
+       } else {
+               eth_hw_addr_random(dev);
+       }
 
        /*
         * Force the interface to use the POW send if always_use_pow
@@ -488,7 +479,7 @@ int cvm_oct_common_init(struct net_device *dev)
        SET_ETHTOOL_OPS(dev, &cvm_oct_ethtool_ops);
 
        cvm_oct_phy_setup_device(dev);
-       dev->netdev_ops->ndo_set_mac_address(dev, &sa);
+       cvm_oct_set_mac_filter(dev);
        dev->netdev_ops->ndo_change_mtu(dev, dev->mtu);
 
        /*
@@ -595,22 +586,55 @@ static const struct net_device_ops cvm_oct_pow_netdev_ops = {
 
 extern void octeon_mdiobus_force_mod_depencency(void);
 
-static int __init cvm_oct_init_module(void)
+static struct device_node * __devinit cvm_oct_of_get_child(const struct device_node *parent,
+                                                          int reg_val)
+{
+       struct device_node *node = NULL;
+       int size;
+       const __be32 *addr;
+
+       for (;;) {
+               node = of_get_next_child(parent, node);
+               if (!node)
+                       break;
+               addr = of_get_property(node, "reg", &size);
+               if (addr && (be32_to_cpu(*addr) == reg_val))
+                       break;
+       }
+       return node;
+}
+
+static struct device_node * __devinit cvm_oct_node_for_port(struct device_node *pip,
+                                                           int interface, int port)
+{
+       struct device_node *ni, *np;
+
+       ni = cvm_oct_of_get_child(pip, interface);
+       if (!ni)
+               return NULL;
+
+       np = cvm_oct_of_get_child(ni, port);
+       of_node_put(ni);
+
+       return np;
+}
+
+static int __devinit cvm_oct_probe(struct platform_device *pdev)
 {
        int num_interfaces;
        int interface;
        int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
        int qos;
+       struct device_node *pip;
 
        octeon_mdiobus_force_mod_depencency();
        pr_notice("cavium-ethernet %s\n", OCTEON_ETHERNET_VERSION);
 
-       if (OCTEON_IS_MODEL(OCTEON_CN52XX))
-               cvm_oct_mac_addr_offset = 2; /* First two are the mgmt ports. */
-       else if (OCTEON_IS_MODEL(OCTEON_CN56XX))
-               cvm_oct_mac_addr_offset = 1; /* First one is the mgmt port. */
-       else
-               cvm_oct_mac_addr_offset = 0;
+       pip = pdev->dev.of_node;
+       if (!pip) {
+               pr_err("Error: No 'pip' in /aliases\n");
+               return -EINVAL;
+       }
 
        cvm_oct_poll_queue = create_singlethread_workqueue("octeon-ethernet");
        if (cvm_oct_poll_queue == NULL) {
@@ -689,10 +713,11 @@ static int __init cvm_oct_init_module(void)
                    cvmx_helper_interface_get_mode(interface);
                int num_ports = cvmx_helper_ports_on_interface(interface);
                int port;
+               int port_index;
 
-               for (port = cvmx_helper_get_ipd_port(interface, 0);
+               for (port_index = 0, port = cvmx_helper_get_ipd_port(interface, 0);
                     port < cvmx_helper_get_ipd_port(interface, num_ports);
-                    port++) {
+                    port_index++, port++) {
                        struct octeon_ethernet *priv;
                        struct net_device *dev =
                            alloc_etherdev(sizeof(struct octeon_ethernet));
@@ -703,6 +728,7 @@ static int __init cvm_oct_init_module(void)
 
                        /* Initialize the device private structure. */
                        priv = netdev_priv(dev);
+                       priv->of_node = cvm_oct_node_for_port(pip, interface, port_index);
 
                        INIT_DELAYED_WORK(&priv->port_periodic_work,
                                          cvm_oct_periodic_worker);
@@ -787,7 +813,7 @@ static int __init cvm_oct_init_module(void)
        return 0;
 }
 
-static void __exit cvm_oct_cleanup_module(void)
+static int __devexit cvm_oct_remove(struct platform_device *pdev)
 {
        int port;
 
@@ -835,10 +861,29 @@ static void __exit cvm_oct_cleanup_module(void)
        if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
                cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
                                      CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
+       return 0;
 }
 
+static struct of_device_id cvm_oct_match[] = {
+       {
+               .compatible = "cavium,octeon-3860-pip",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, cvm_oct_match);
+
+static struct platform_driver cvm_oct_driver = {
+       .probe          = cvm_oct_probe,
+       .remove         = __devexit_p(cvm_oct_remove),
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = KBUILD_MODNAME,
+               .of_match_table = cvm_oct_match,
+       },
+};
+
+module_platform_driver(cvm_oct_driver);
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Cavium Networks <support@caviumnetworks.com>");
 MODULE_DESCRIPTION("Cavium Networks Octeon ethernet driver.");
-module_init(cvm_oct_init_module);
-module_exit(cvm_oct_cleanup_module);
index d58192563552db54a555f489c97ca675d8f822cd..9360e22e07398d9da6f3dcb5734546d27abc25a2 100644 (file)
@@ -31,6 +31,8 @@
 #ifndef OCTEON_ETHERNET_H
 #define OCTEON_ETHERNET_H
 
+#include <linux/of.h>
+
 /**
  * This is the definition of the Ethernet driver's private
  * driver state stored in netdev_priv(dev).
@@ -59,6 +61,7 @@ struct octeon_ethernet {
        void (*poll) (struct net_device *dev);
        struct delayed_work     port_periodic_work;
        struct work_struct      port_work;      /* may be unused. */
+       struct device_node      *of_node;
 };
 
 int cvm_oct_free_work(void *work_queue_entry);
index 9e2100551c789f70b033e420d0840b1f2e340f5d..cbb5aaf3e567f5d52f552cc940f3e9556ac8ee40 100644 (file)
@@ -109,46 +109,29 @@ static struct se_device *fd_create_virtdevice(
        struct se_subsystem_dev *se_dev,
        void *p)
 {
-       char *dev_p = NULL;
        struct se_device *dev;
        struct se_dev_limits dev_limits;
        struct queue_limits *limits;
        struct fd_dev *fd_dev = p;
        struct fd_host *fd_host = hba->hba_ptr;
-       mm_segment_t old_fs;
        struct file *file;
        struct inode *inode = NULL;
        int dev_flags = 0, flags, ret = -EINVAL;
 
        memset(&dev_limits, 0, sizeof(struct se_dev_limits));
 
-       old_fs = get_fs();
-       set_fs(get_ds());
-       dev_p = getname(fd_dev->fd_dev_name);
-       set_fs(old_fs);
-
-       if (IS_ERR(dev_p)) {
-               pr_err("getname(%s) failed: %lu\n",
-                       fd_dev->fd_dev_name, IS_ERR(dev_p));
-               ret = PTR_ERR(dev_p);
-               goto fail;
-       }
        /*
         * Use O_DSYNC by default instead of O_SYNC to forgo syncing
         * of pure timestamp updates.
         */
        flags = O_RDWR | O_CREAT | O_LARGEFILE | O_DSYNC;
 
-       file = filp_open(dev_p, flags, 0600);
+       file = filp_open(fd_dev->fd_dev_name, flags, 0600);
        if (IS_ERR(file)) {
-               pr_err("filp_open(%s) failed\n", dev_p);
+               pr_err("filp_open(%s) failed\n", fd_dev->fd_dev_name);
                ret = PTR_ERR(file);
                goto fail;
        }
-       if (!file || !file->f_dentry) {
-               pr_err("filp_open(%s) failed\n", dev_p);
-               goto fail;
-       }
        fd_dev->fd_file = file;
        /*
         * If using a block backend with this struct file, we extract
@@ -212,14 +195,12 @@ static struct se_device *fd_create_virtdevice(
                " %llu total bytes\n", fd_host->fd_host_id, fd_dev->fd_dev_id,
                        fd_dev->fd_dev_name, fd_dev->fd_dev_size);
 
-       putname(dev_p);
        return dev;
 fail:
        if (fd_dev->fd_file) {
                filp_close(fd_dev->fd_file, NULL);
                fd_dev->fd_file = NULL;
        }
-       putname(dev_p);
        return ERR_PTR(ret);
 }
 
@@ -452,14 +433,11 @@ static ssize_t fd_set_configfs_dev_params(
                token = match_token(ptr, tokens, args);
                switch (token) {
                case Opt_fd_dev_name:
-                       arg_p = match_strdup(&args[0]);
-                       if (!arg_p) {
-                               ret = -ENOMEM;
+                       if (match_strlcpy(fd_dev->fd_dev_name, &args[0],
+                               FD_MAX_DEV_NAME) == 0) {
+                               ret = -EINVAL;
                                break;
                        }
-                       snprintf(fd_dev->fd_dev_name, FD_MAX_DEV_NAME,
-                                       "%s", arg_p);
-                       kfree(arg_p);
                        pr_debug("FILEIO: Referencing Path: %s\n",
                                        fd_dev->fd_dev_name);
                        fd_dev->fbd_flags |= FBDF_HAS_PATH;
index 2d7a9fe8f365b64670dca63b98f925e7b0a15e73..2ab31e4f02cc741ee5d7b1c603a9158b34f335c4 100644 (file)
@@ -1251,7 +1251,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz)
  * longer needed. The passive cooling formula uses tc1 and tc2 as described in
  * section 11.1.5.1 of the ACPI specification 3.0.
  */
-struct thermal_zone_device *thermal_zone_device_register(char *type,
+struct thermal_zone_device *thermal_zone_device_register(const char *type,
        int trips, int mask, void *devdata,
        const struct thermal_zone_device_ops *ops,
        int tc1, int tc2, int passive_delay, int polling_delay)
index 6cd414341d5ecebe293ecddc326d3be5832b88af..6579ffdd8e9ba3d2e4a6cf5a51031ebe60a092d0 100644 (file)
@@ -216,8 +216,7 @@ static int ulite_startup(struct uart_port *port)
 {
        int ret;
 
-       ret = request_irq(port->irq, ulite_isr,
-                         IRQF_SHARED | IRQF_SAMPLE_RANDOM, "uartlite", port);
+       ret = request_irq(port->irq, ulite_isr, IRQF_SHARED, "uartlite", port);
        if (ret)
                return ret;
 
index 821126eb81762ee2b57c971eec611c1e9a4e7ef6..128a804c42f406b6c6b1f32f57110ff6b0810b41 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/kthread.h>
 #include <linux/mutex.h>
 #include <linux/freezer.h>
+#include <linux/random.h>
 
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
@@ -2181,6 +2182,14 @@ int usb_new_device(struct usb_device *udev)
        /* Tell the world! */
        announce_device(udev);
 
+       if (udev->serial)
+               add_device_randomness(udev->serial, strlen(udev->serial));
+       if (udev->product)
+               add_device_randomness(udev->product, strlen(udev->product));
+       if (udev->manufacturer)
+               add_device_randomness(udev->manufacturer,
+                                     strlen(udev->manufacturer));
+
        device_enable_async_suspend(&udev->dev);
 
        /*
index 965a6293206acc5bb52facb12d8acf50d3c3c6a2..8ee9268fe253f2dc01de4f84f451ef3a1870a69a 100644 (file)
@@ -301,7 +301,7 @@ pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags)
        struct page *page;
        int err;
 
-       page = alloc_page(gfp_flags);
+       page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL);
        if (!page)
                return -ENOMEM;
 
index 3d28fb976c7821898ea44fc4c8ac4656011d546a..9fd7886cfa9a7aac60035d7357e5ed00bfff40ff 100644 (file)
@@ -1836,7 +1836,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        /* init to known state, then setup irqs */
        udc_reset(dev);
        udc_reinit (dev);
-       if (request_irq(pdev->irq, goku_irq, IRQF_SHARED/*|IRQF_SAMPLE_RANDOM*/,
+       if (request_irq(pdev->irq, goku_irq, IRQF_SHARED,
                        driver_name, dev) != 0) {
                DBG(dev, "request interrupt %d failed\n", pdev->irq);
                retval = -EBUSY;
index 8981fbb5748ccf4b7acf3207af2ac0e54d65e46c..cf6bd626f3fe1e3731b062328226b842a84fcb50 100644 (file)
@@ -1583,12 +1583,10 @@ static int __exit m66592_remove(struct platform_device *pdev)
        iounmap(m66592->reg);
        free_irq(platform_get_irq(pdev, 0), m66592);
        m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
-#ifdef CONFIG_HAVE_CLK
        if (m66592->pdata->on_chip) {
                clk_disable(m66592->clk);
                clk_put(m66592->clk);
        }
-#endif
        kfree(m66592);
        return 0;
 }
@@ -1602,9 +1600,7 @@ static int __init m66592_probe(struct platform_device *pdev)
        struct resource *res, *ires;
        void __iomem *reg = NULL;
        struct m66592 *m66592 = NULL;
-#ifdef CONFIG_HAVE_CLK
        char clk_name[8];
-#endif
        int ret = 0;
        int i;
 
@@ -1671,7 +1667,6 @@ static int __init m66592_probe(struct platform_device *pdev)
                goto clean_up;
        }
 
-#ifdef CONFIG_HAVE_CLK
        if (m66592->pdata->on_chip) {
                snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id);
                m66592->clk = clk_get(&pdev->dev, clk_name);
@@ -1683,7 +1678,7 @@ static int __init m66592_probe(struct platform_device *pdev)
                }
                clk_enable(m66592->clk);
        }
-#endif
+
        INIT_LIST_HEAD(&m66592->gadget.ep_list);
        m66592->gadget.ep0 = &m66592->ep[0].ep;
        INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list);
@@ -1731,13 +1726,11 @@ err_add_udc:
        m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
 
 clean_up3:
-#ifdef CONFIG_HAVE_CLK
        if (m66592->pdata->on_chip) {
                clk_disable(m66592->clk);
                clk_put(m66592->clk);
        }
 clean_up2:
-#endif
        free_irq(ires->start, m66592);
 clean_up:
        if (m66592) {
index 88c85b4116a2f1234820db626982b0b3bcedd9f5..16c7e14678b8d220b12a33ac98ec40be436c275c 100644 (file)
 #ifndef __M66592_UDC_H__
 #define __M66592_UDC_H__
 
-#ifdef CONFIG_HAVE_CLK
 #include <linux/clk.h>
-#endif
-
 #include <linux/usb/m66592.h>
 
 #define M66592_SYSCFG          0x00
@@ -468,9 +465,7 @@ struct m66592_ep {
 struct m66592 {
        spinlock_t              lock;
        void __iomem            *reg;
-#ifdef CONFIG_HAVE_CLK
        struct clk *clk;
-#endif
        struct m66592_platdata  *pdata;
        unsigned long           irq_trigger;
 
index 53c093b941e5915302775b0916785fe24c7db866..907ad3ecb341b0c3dc285b478b3d34dc57b450d5 100644 (file)
@@ -2201,19 +2201,15 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
 
 #ifdef CONFIG_ARCH_LUBBOCK
        if (machine_is_lubbock()) {
-               retval = request_irq(LUBBOCK_USB_DISC_IRQ,
-                               lubbock_vbus_irq,
-                               IRQF_SAMPLE_RANDOM,
-                               driver_name, dev);
+               retval = request_irq(LUBBOCK_USB_DISC_IRQ, lubbock_vbus_irq,
+                                    0, driver_name, dev);
                if (retval != 0) {
                        pr_err("%s: can't get irq %i, err %d\n",
                                driver_name, LUBBOCK_USB_DISC_IRQ, retval);
                        goto err_irq_lub;
                }
-               retval = request_irq(LUBBOCK_USB_IRQ,
-                               lubbock_vbus_irq,
-                               IRQF_SAMPLE_RANDOM,
-                               driver_name, dev);
+               retval = request_irq(LUBBOCK_USB_IRQ, lubbock_vbus_irq,
+                                    0, driver_name, dev);
                if (retval != 0) {
                        pr_err("%s: can't get irq %i, err %d\n",
                                driver_name, LUBBOCK_USB_IRQ, retval);
index f3ac2a20c27caac8097db4526c725bc084850585..5a80751accb7de937af730a988fa0f0f2474232a 100644 (file)
@@ -1831,12 +1831,12 @@ static int __exit r8a66597_remove(struct platform_device *pdev)
                iounmap(r8a66597->sudmac_reg);
        free_irq(platform_get_irq(pdev, 0), r8a66597);
        r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
-#ifdef CONFIG_HAVE_CLK
+
        if (r8a66597->pdata->on_chip) {
                clk_disable(r8a66597->clk);
                clk_put(r8a66597->clk);
        }
-#endif
+
        device_unregister(&r8a66597->gadget.dev);
        kfree(r8a66597);
        return 0;
@@ -1868,9 +1868,7 @@ static int __init r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597,
 
 static int __init r8a66597_probe(struct platform_device *pdev)
 {
-#ifdef CONFIG_HAVE_CLK
        char clk_name[8];
-#endif
        struct resource *res, *ires;
        int irq;
        void __iomem *reg = NULL;
@@ -1934,7 +1932,6 @@ static int __init r8a66597_probe(struct platform_device *pdev)
        r8a66597->timer.data = (unsigned long)r8a66597;
        r8a66597->reg = reg;
 
-#ifdef CONFIG_HAVE_CLK
        if (r8a66597->pdata->on_chip) {
                snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
                r8a66597->clk = clk_get(&pdev->dev, clk_name);
@@ -1946,7 +1943,7 @@ static int __init r8a66597_probe(struct platform_device *pdev)
                }
                clk_enable(r8a66597->clk);
        }
-#endif
+
        if (r8a66597->pdata->sudmac) {
                ret = r8a66597_sudmac_ioremap(r8a66597, pdev);
                if (ret < 0)
@@ -2006,13 +2003,11 @@ err_add_udc:
 clean_up3:
        free_irq(irq, r8a66597);
 clean_up2:
-#ifdef CONFIG_HAVE_CLK
        if (r8a66597->pdata->on_chip) {
                clk_disable(r8a66597->clk);
                clk_put(r8a66597->clk);
        }
 clean_up_dev:
-#endif
        device_unregister(&r8a66597->gadget.dev);
 clean_up:
        if (r8a66597) {
index 99908c76ccd1cf6972fdb42b1fc65c0f516f6fdd..45c4b2df1785d0de45bbd19669b351abaf85fd55 100644 (file)
 #ifndef __R8A66597_H__
 #define __R8A66597_H__
 
-#ifdef CONFIG_HAVE_CLK
 #include <linux/clk.h>
-#endif
-
 #include <linux/usb/r8a66597.h>
 
 #define R8A66597_MAX_SAMPLING  10
@@ -92,9 +89,7 @@ struct r8a66597 {
        void __iomem            *reg;
        void __iomem            *sudmac_reg;
 
-#ifdef CONFIG_HAVE_CLK
        struct clk *clk;
-#endif
        struct r8a66597_platdata        *pdata;
 
        struct usb_gadget               gadget;
index ae8b18869b8c0189b6ef2d2c68760cb12e2307e5..8d9bcd8207c8d1fee7e47813d7d6ee7f76ca0b6b 100644 (file)
@@ -656,9 +656,8 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
        if (!(filp->f_mode & FMODE_WRITE))
                ro = 1;
 
-       if (filp->f_path.dentry)
-               inode = filp->f_path.dentry->d_inode;
-       if (!inode || (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
+       inode = filp->f_path.dentry->d_inode;
+       if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
                LINFO(curlun, "invalid file type: %s\n", filename);
                goto out;
        }
@@ -667,7 +666,7 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
         * If we can't read the file, it's no good.
         * If we can't write the file, use it read-only.
         */
-       if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) {
+       if (!(filp->f_op->read || filp->f_op->aio_read)) {
                LINFO(curlun, "file not readable: %s\n", filename);
                goto out;
        }
@@ -712,7 +711,6 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
        if (fsg_lun_is_open(curlun))
                fsg_lun_close(curlun);
 
-       get_file(filp);
        curlun->blksize = blksize;
        curlun->blkbits = blkbits;
        curlun->ro = ro;
@@ -720,10 +718,10 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
        curlun->file_length = size;
        curlun->num_sectors = num_sectors;
        LDBG(curlun, "open backing file: %s\n", filename);
-       rc = 0;
+       return 0;
 
 out:
-       filp_close(filp, current->files);
+       fput(filp);
        return rc;
 }
 
index af989898205989ca46e443b6e8d496d9c8ba459b..e0c5e88e03ed30dc02c533d0eae3a41b94bcb00b 100644 (file)
@@ -275,17 +275,17 @@ static int gaudio_close_snd_dev(struct gaudio *gau)
        /* Close control device */
        snd = &gau->control;
        if (snd->filp)
-               filp_close(snd->filp, current->files);
+               filp_close(snd->filp, NULL);
 
        /* Close PCM playback device and setup substream */
        snd = &gau->playback;
        if (snd->filp)
-               filp_close(snd->filp, current->files);
+               filp_close(snd->filp, NULL);
 
        /* Close PCM capture device and setup substream */
        snd = &gau->capture;
        if (snd->filp)
-               filp_close(snd->filp, current->files);
+               filp_close(snd->filp, NULL);
 
        return 0;
 }
index ec21f4a4a056830eff955230a8762254cd8bf529..bb55eb4a7d485b806e69ba0c5141912f04bd6791 100644 (file)
@@ -152,14 +152,14 @@ static int omap_ehci_init(struct usb_hcd *hcd)
        struct ehci_hcd_omap_platform_data      *pdata;
 
        pdata = hcd->self.controller->platform_data;
+
+       /* Hold PHYs in reset while initializing EHCI controller */
        if (pdata->phy_reset) {
                if (gpio_is_valid(pdata->reset_gpio_port[0]))
-                       gpio_request_one(pdata->reset_gpio_port[0],
-                                        GPIOF_OUT_INIT_LOW, "USB1 PHY reset");
+                       gpio_set_value_cansleep(pdata->reset_gpio_port[0], 0);
 
                if (gpio_is_valid(pdata->reset_gpio_port[1]))
-                       gpio_request_one(pdata->reset_gpio_port[1],
-                                        GPIOF_OUT_INIT_LOW, "USB2 PHY reset");
+                       gpio_set_value_cansleep(pdata->reset_gpio_port[1], 0);
 
                /* Hold the PHY in RESET for enough time till DIR is high */
                udelay(10);
index c868be65e7634bcc2b44d9d15610c8275b3be32b..4c634eb56358a1231a6c9fcb1dc05c1b983bf99d 100644 (file)
@@ -95,9 +95,7 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597)
        int i = 0;
 
        if (r8a66597->pdata->on_chip) {
-#ifdef CONFIG_HAVE_CLK
                clk_enable(r8a66597->clk);
-#endif
                do {
                        r8a66597_write(r8a66597, SCKE, SYSCFG0);
                        tmp = r8a66597_read(r8a66597, SYSCFG0);
@@ -141,9 +139,7 @@ static void r8a66597_clock_disable(struct r8a66597 *r8a66597)
        udelay(1);
 
        if (r8a66597->pdata->on_chip) {
-#ifdef CONFIG_HAVE_CLK
                clk_disable(r8a66597->clk);
-#endif
        } else {
                r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
                r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
@@ -2406,19 +2402,15 @@ static int __devexit r8a66597_remove(struct platform_device *pdev)
        del_timer_sync(&r8a66597->rh_timer);
        usb_remove_hcd(hcd);
        iounmap(r8a66597->reg);
-#ifdef CONFIG_HAVE_CLK
        if (r8a66597->pdata->on_chip)
                clk_put(r8a66597->clk);
-#endif
        usb_put_hcd(hcd);
        return 0;
 }
 
 static int __devinit r8a66597_probe(struct platform_device *pdev)
 {
-#ifdef CONFIG_HAVE_CLK
        char clk_name[8];
-#endif
        struct resource *res = NULL, *ires;
        int irq = -1;
        void __iomem *reg = NULL;
@@ -2482,7 +2474,6 @@ static int __devinit r8a66597_probe(struct platform_device *pdev)
        r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW;
 
        if (r8a66597->pdata->on_chip) {
-#ifdef CONFIG_HAVE_CLK
                snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
                r8a66597->clk = clk_get(&pdev->dev, clk_name);
                if (IS_ERR(r8a66597->clk)) {
@@ -2491,7 +2482,6 @@ static int __devinit r8a66597_probe(struct platform_device *pdev)
                        ret = PTR_ERR(r8a66597->clk);
                        goto clean_up2;
                }
-#endif
                r8a66597->max_root_hub = 1;
        } else
                r8a66597->max_root_hub = 2;
@@ -2531,11 +2521,9 @@ static int __devinit r8a66597_probe(struct platform_device *pdev)
        return 0;
 
 clean_up3:
-#ifdef CONFIG_HAVE_CLK
        if (r8a66597->pdata->on_chip)
                clk_put(r8a66597->clk);
 clean_up2:
-#endif
        usb_put_hcd(hcd);
 
 clean_up:
index f28782d20eef0eab2a0f7c124146e3c154ba8e2f..672cea307abb774639d6e362b0603309bcdb2c45 100644 (file)
 #ifndef __R8A66597_H__
 #define __R8A66597_H__
 
-#ifdef CONFIG_HAVE_CLK
 #include <linux/clk.h>
-#endif
-
 #include <linux/usb/r8a66597.h>
 
 #define R8A66597_MAX_NUM_PIPE          10
@@ -113,9 +110,7 @@ struct r8a66597_root_hub {
 struct r8a66597 {
        spinlock_t lock;
        void __iomem *reg;
-#ifdef CONFIG_HAVE_CLK
        struct clk *clk;
-#endif
        struct r8a66597_platdata        *pdata;
        struct r8a66597_device          device0;
        struct r8a66597_root_hub        root_hub[R8A66597_MAX_ROOT_HUB];
index dbcdeea30f09c199d0ebaa9b3f14a8f4d9179919..586105b55a7ceae23a79d76e2c8b35578e7827a7 100644 (file)
@@ -81,14 +81,6 @@ struct musb_ep;
 #define is_peripheral_active(m)                (!(m)->is_host)
 #define is_host_active(m)              ((m)->is_host)
 
-#ifndef CONFIG_HAVE_CLK
-/* Dummy stub for clk framework */
-#define clk_get(dev, id)       NULL
-#define clk_put(clock)         do {} while (0)
-#define clk_enable(clock)      do {} while (0)
-#define clk_disable(clock)     do {} while (0)
-#endif
-
 #ifdef CONFIG_PROC_FS
 #include <linux/fs.h>
 #define MUSB_CONFIG_PROC_FS
index 575fc815c932205a4255744f4288fd44790febde..7a88667742b663dc64f627560dfc5d34af32d0a7 100644 (file)
@@ -1576,7 +1576,6 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
                isp->irq_type = IRQF_TRIGGER_FALLING;
        }
 
-       isp->irq_type |= IRQF_SAMPLE_RANDOM;
        status = request_irq(i2c->irq, isp1301_irq,
                        isp->irq_type, DRIVER_NAME, isp);
        if (status < 0) {
diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
new file mode 100644 (file)
index 0000000..7cd5dec
--- /dev/null
@@ -0,0 +1,16 @@
+config VFIO_IOMMU_TYPE1
+       tristate
+       depends on VFIO
+       default n
+
+menuconfig VFIO
+       tristate "VFIO Non-Privileged userspace driver framework"
+       depends on IOMMU_API
+       select VFIO_IOMMU_TYPE1 if X86
+       help
+         VFIO provides a framework for secure userspace device drivers.
+         See Documentation/vfio.txt for more details.
+
+         If you don't know what to do here, say N.
+
+source "drivers/vfio/pci/Kconfig"
diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile
new file mode 100644 (file)
index 0000000..2398d4a
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_VFIO) += vfio.o
+obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
+obj-$(CONFIG_VFIO_PCI) += pci/
diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig
new file mode 100644 (file)
index 0000000..5980758
--- /dev/null
@@ -0,0 +1,8 @@
+config VFIO_PCI
+       tristate "VFIO support for PCI devices"
+       depends on VFIO && PCI && EVENTFD
+       help
+         Support for the PCI VFIO bus driver.  This is required to make
+         use of PCI drivers using the VFIO framework.
+
+         If you don't know what to do here, say N.
diff --git a/drivers/vfio/pci/Makefile b/drivers/vfio/pci/Makefile
new file mode 100644 (file)
index 0000000..1310792
--- /dev/null
@@ -0,0 +1,4 @@
+
+vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o
+
+obj-$(CONFIG_VFIO_PCI) += vfio-pci.o
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
new file mode 100644 (file)
index 0000000..6968b72
--- /dev/null
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
+ *     Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc.  All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/device.h>
+#include <linux/eventfd.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/vfio.h>
+
+#include "vfio_pci_private.h"
+
+#define DRIVER_VERSION  "0.2"
+#define DRIVER_AUTHOR   "Alex Williamson <alex.williamson@redhat.com>"
+#define DRIVER_DESC     "VFIO PCI - User Level meta-driver"
+
+static bool nointxmask;
+module_param_named(nointxmask, nointxmask, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(nointxmask,
+                 "Disable support for PCI 2.3 style INTx masking.  If this resolves problems for specific devices, report lspci -vvvxxx to linux-pci@vger.kernel.org so the device can be fixed automatically via the broken_intx_masking flag.");
+
+static int vfio_pci_enable(struct vfio_pci_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       int ret;
+       u16 cmd;
+       u8 msix_pos;
+
+       vdev->reset_works = (pci_reset_function(pdev) == 0);
+       pci_save_state(pdev);
+       vdev->pci_saved_state = pci_store_saved_state(pdev);
+       if (!vdev->pci_saved_state)
+               pr_debug("%s: Couldn't store %s saved state\n",
+                        __func__, dev_name(&pdev->dev));
+
+       ret = vfio_config_init(vdev);
+       if (ret)
+               goto out;
+
+       if (likely(!nointxmask))
+               vdev->pci_2_3 = pci_intx_mask_supported(pdev);
+
+       pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+       if (vdev->pci_2_3 && (cmd & PCI_COMMAND_INTX_DISABLE)) {
+               cmd &= ~PCI_COMMAND_INTX_DISABLE;
+               pci_write_config_word(pdev, PCI_COMMAND, cmd);
+       }
+
+       msix_pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
+       if (msix_pos) {
+               u16 flags;
+               u32 table;
+
+               pci_read_config_word(pdev, msix_pos + PCI_MSIX_FLAGS, &flags);
+               pci_read_config_dword(pdev, msix_pos + PCI_MSIX_TABLE, &table);
+
+               vdev->msix_bar = table & PCI_MSIX_FLAGS_BIRMASK;
+               vdev->msix_offset = table & ~PCI_MSIX_FLAGS_BIRMASK;
+               vdev->msix_size = ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) * 16;
+       } else
+               vdev->msix_bar = 0xFF;
+
+       ret = pci_enable_device(pdev);
+       if (ret)
+               goto out;
+
+       return ret;
+
+out:
+       kfree(vdev->pci_saved_state);
+       vdev->pci_saved_state = NULL;
+       vfio_config_free(vdev);
+       return ret;
+}
+
+static void vfio_pci_disable(struct vfio_pci_device *vdev)
+{
+       int bar;
+
+       pci_disable_device(vdev->pdev);
+
+       vfio_pci_set_irqs_ioctl(vdev, VFIO_IRQ_SET_DATA_NONE |
+                               VFIO_IRQ_SET_ACTION_TRIGGER,
+                               vdev->irq_type, 0, 0, NULL);
+
+       vdev->virq_disabled = false;
+
+       vfio_config_free(vdev);
+
+       pci_reset_function(vdev->pdev);
+
+       if (pci_load_and_free_saved_state(vdev->pdev,
+                                         &vdev->pci_saved_state) == 0)
+               pci_restore_state(vdev->pdev);
+       else
+               pr_info("%s: Couldn't reload %s saved state\n",
+                       __func__, dev_name(&vdev->pdev->dev));
+
+       for (bar = PCI_STD_RESOURCES; bar <= PCI_STD_RESOURCE_END; bar++) {
+               if (!vdev->barmap[bar])
+                       continue;
+               pci_iounmap(vdev->pdev, vdev->barmap[bar]);
+               pci_release_selected_regions(vdev->pdev, 1 << bar);
+               vdev->barmap[bar] = NULL;
+       }
+}
+
+static void vfio_pci_release(void *device_data)
+{
+       struct vfio_pci_device *vdev = device_data;
+
+       if (atomic_dec_and_test(&vdev->refcnt))
+               vfio_pci_disable(vdev);
+
+       module_put(THIS_MODULE);
+}
+
+static int vfio_pci_open(void *device_data)
+{
+       struct vfio_pci_device *vdev = device_data;
+
+       if (!try_module_get(THIS_MODULE))
+               return -ENODEV;
+
+       if (atomic_inc_return(&vdev->refcnt) == 1) {
+               int ret = vfio_pci_enable(vdev);
+               if (ret) {
+                       module_put(THIS_MODULE);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type)
+{
+       if (irq_type == VFIO_PCI_INTX_IRQ_INDEX) {
+               u8 pin;
+               pci_read_config_byte(vdev->pdev, PCI_INTERRUPT_PIN, &pin);
+               if (pin)
+                       return 1;
+
+       } else if (irq_type == VFIO_PCI_MSI_IRQ_INDEX) {
+               u8 pos;
+               u16 flags;
+
+               pos = pci_find_capability(vdev->pdev, PCI_CAP_ID_MSI);
+               if (pos) {
+                       pci_read_config_word(vdev->pdev,
+                                            pos + PCI_MSI_FLAGS, &flags);
+
+                       return 1 << (flags & PCI_MSI_FLAGS_QMASK);
+               }
+       } else if (irq_type == VFIO_PCI_MSIX_IRQ_INDEX) {
+               u8 pos;
+               u16 flags;
+
+               pos = pci_find_capability(vdev->pdev, PCI_CAP_ID_MSIX);
+               if (pos) {
+                       pci_read_config_word(vdev->pdev,
+                                            pos + PCI_MSIX_FLAGS, &flags);
+
+                       return (flags & PCI_MSIX_FLAGS_QSIZE) + 1;
+               }
+       }
+
+       return 0;
+}
+
+static long vfio_pci_ioctl(void *device_data,
+                          unsigned int cmd, unsigned long arg)
+{
+       struct vfio_pci_device *vdev = device_data;
+       unsigned long minsz;
+
+       if (cmd == VFIO_DEVICE_GET_INFO) {
+               struct vfio_device_info info;
+
+               minsz = offsetofend(struct vfio_device_info, num_irqs);
+
+               if (copy_from_user(&info, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (info.argsz < minsz)
+                       return -EINVAL;
+
+               info.flags = VFIO_DEVICE_FLAGS_PCI;
+
+               if (vdev->reset_works)
+                       info.flags |= VFIO_DEVICE_FLAGS_RESET;
+
+               info.num_regions = VFIO_PCI_NUM_REGIONS;
+               info.num_irqs = VFIO_PCI_NUM_IRQS;
+
+               return copy_to_user((void __user *)arg, &info, minsz);
+
+       } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
+               struct pci_dev *pdev = vdev->pdev;
+               struct vfio_region_info info;
+
+               minsz = offsetofend(struct vfio_region_info, offset);
+
+               if (copy_from_user(&info, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (info.argsz < minsz)
+                       return -EINVAL;
+
+               switch (info.index) {
+               case VFIO_PCI_CONFIG_REGION_INDEX:
+                       info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+                       info.size = pdev->cfg_size;
+                       info.flags = VFIO_REGION_INFO_FLAG_READ |
+                                    VFIO_REGION_INFO_FLAG_WRITE;
+                       break;
+               case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
+                       info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+                       info.size = pci_resource_len(pdev, info.index);
+                       if (!info.size) {
+                               info.flags = 0;
+                               break;
+                       }
+
+                       info.flags = VFIO_REGION_INFO_FLAG_READ |
+                                    VFIO_REGION_INFO_FLAG_WRITE;
+                       if (pci_resource_flags(pdev, info.index) &
+                           IORESOURCE_MEM && info.size >= PAGE_SIZE)
+                               info.flags |= VFIO_REGION_INFO_FLAG_MMAP;
+                       break;
+               case VFIO_PCI_ROM_REGION_INDEX:
+               {
+                       void __iomem *io;
+                       size_t size;
+
+                       info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+                       info.flags = 0;
+
+                       /* Report the BAR size, not the ROM size */
+                       info.size = pci_resource_len(pdev, info.index);
+                       if (!info.size)
+                               break;
+
+                       /* Is it really there? */
+                       io = pci_map_rom(pdev, &size);
+                       if (!io || !size) {
+                               info.size = 0;
+                               break;
+                       }
+                       pci_unmap_rom(pdev, io);
+
+                       info.flags = VFIO_REGION_INFO_FLAG_READ;
+                       break;
+               }
+               default:
+                       return -EINVAL;
+               }
+
+               return copy_to_user((void __user *)arg, &info, minsz);
+
+       } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {
+               struct vfio_irq_info info;
+
+               minsz = offsetofend(struct vfio_irq_info, count);
+
+               if (copy_from_user(&info, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS)
+                       return -EINVAL;
+
+               info.flags = VFIO_IRQ_INFO_EVENTFD;
+
+               info.count = vfio_pci_get_irq_count(vdev, info.index);
+
+               if (info.index == VFIO_PCI_INTX_IRQ_INDEX)
+                       info.flags |= (VFIO_IRQ_INFO_MASKABLE |
+                                      VFIO_IRQ_INFO_AUTOMASKED);
+               else
+                       info.flags |= VFIO_IRQ_INFO_NORESIZE;
+
+               return copy_to_user((void __user *)arg, &info, minsz);
+
+       } else if (cmd == VFIO_DEVICE_SET_IRQS) {
+               struct vfio_irq_set hdr;
+               u8 *data = NULL;
+               int ret = 0;
+
+               minsz = offsetofend(struct vfio_irq_set, count);
+
+               if (copy_from_user(&hdr, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (hdr.argsz < minsz || hdr.index >= VFIO_PCI_NUM_IRQS ||
+                   hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK |
+                                 VFIO_IRQ_SET_ACTION_TYPE_MASK))
+                       return -EINVAL;
+
+               if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) {
+                       size_t size;
+
+                       if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL)
+                               size = sizeof(uint8_t);
+                       else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD)
+                               size = sizeof(int32_t);
+                       else
+                               return -EINVAL;
+
+                       if (hdr.argsz - minsz < hdr.count * size ||
+                           hdr.count > vfio_pci_get_irq_count(vdev, hdr.index))
+                               return -EINVAL;
+
+                       data = kmalloc(hdr.count * size, GFP_KERNEL);
+                       if (!data)
+                               return -ENOMEM;
+
+                       if (copy_from_user(data, (void __user *)(arg + minsz),
+                                          hdr.count * size)) {
+                               kfree(data);
+                               return -EFAULT;
+                       }
+               }
+
+               mutex_lock(&vdev->igate);
+
+               ret = vfio_pci_set_irqs_ioctl(vdev, hdr.flags, hdr.index,
+                                             hdr.start, hdr.count, data);
+
+               mutex_unlock(&vdev->igate);
+               kfree(data);
+
+               return ret;
+
+       } else if (cmd == VFIO_DEVICE_RESET)
+               return vdev->reset_works ?
+                       pci_reset_function(vdev->pdev) : -EINVAL;
+
+       return -ENOTTY;
+}
+
+static ssize_t vfio_pci_read(void *device_data, char __user *buf,
+                            size_t count, loff_t *ppos)
+{
+       unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+       struct vfio_pci_device *vdev = device_data;
+       struct pci_dev *pdev = vdev->pdev;
+
+       if (index >= VFIO_PCI_NUM_REGIONS)
+               return -EINVAL;
+
+       if (index == VFIO_PCI_CONFIG_REGION_INDEX)
+               return vfio_pci_config_readwrite(vdev, buf, count, ppos, false);
+       else if (index == VFIO_PCI_ROM_REGION_INDEX)
+               return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false);
+       else if (pci_resource_flags(pdev, index) & IORESOURCE_IO)
+               return vfio_pci_io_readwrite(vdev, buf, count, ppos, false);
+       else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM)
+               return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false);
+
+       return -EINVAL;
+}
+
+static ssize_t vfio_pci_write(void *device_data, const char __user *buf,
+                             size_t count, loff_t *ppos)
+{
+       unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+       struct vfio_pci_device *vdev = device_data;
+       struct pci_dev *pdev = vdev->pdev;
+
+       if (index >= VFIO_PCI_NUM_REGIONS)
+               return -EINVAL;
+
+       if (index == VFIO_PCI_CONFIG_REGION_INDEX)
+               return vfio_pci_config_readwrite(vdev, (char __user *)buf,
+                                                count, ppos, true);
+       else if (index == VFIO_PCI_ROM_REGION_INDEX)
+               return -EINVAL;
+       else if (pci_resource_flags(pdev, index) & IORESOURCE_IO)
+               return vfio_pci_io_readwrite(vdev, (char __user *)buf,
+                                            count, ppos, true);
+       else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) {
+               return vfio_pci_mem_readwrite(vdev, (char __user *)buf,
+                                             count, ppos, true);
+       }
+
+       return -EINVAL;
+}
+
+static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma)
+{
+       struct vfio_pci_device *vdev = device_data;
+       struct pci_dev *pdev = vdev->pdev;
+       unsigned int index;
+       u64 phys_len, req_len, pgoff, req_start, phys;
+       int ret;
+
+       index = vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT);
+
+       if (vma->vm_end < vma->vm_start)
+               return -EINVAL;
+       if ((vma->vm_flags & VM_SHARED) == 0)
+               return -EINVAL;
+       if (index >= VFIO_PCI_ROM_REGION_INDEX)
+               return -EINVAL;
+       if (!(pci_resource_flags(pdev, index) & IORESOURCE_MEM))
+               return -EINVAL;
+
+       phys_len = pci_resource_len(pdev, index);
+       req_len = vma->vm_end - vma->vm_start;
+       pgoff = vma->vm_pgoff &
+               ((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
+       req_start = pgoff << PAGE_SHIFT;
+
+       if (phys_len < PAGE_SIZE || req_start + req_len > phys_len)
+               return -EINVAL;
+
+       if (index == vdev->msix_bar) {
+               /*
+                * Disallow mmaps overlapping the MSI-X table; users don't
+                * get to touch this directly.  We could find somewhere
+                * else to map the overlap, but page granularity is only
+                * a recommendation, not a requirement, so the user needs
+                * to know which bits are real.  Requiring them to mmap
+                * around the table makes that clear.
+                */
+
+               /* If neither entirely above nor below, then it overlaps */
+               if (!(req_start >= vdev->msix_offset + vdev->msix_size ||
+                     req_start + req_len <= vdev->msix_offset))
+                       return -EINVAL;
+       }
+
+       /*
+        * Even though we don't make use of the barmap for the mmap,
+        * we need to request the region and the barmap tracks that.
+        */
+       if (!vdev->barmap[index]) {
+               ret = pci_request_selected_regions(pdev,
+                                                  1 << index, "vfio-pci");
+               if (ret)
+                       return ret;
+
+               vdev->barmap[index] = pci_iomap(pdev, index, 0);
+       }
+
+       vma->vm_private_data = vdev;
+       vma->vm_flags |= (VM_IO | VM_RESERVED);
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+       phys = (pci_resource_start(pdev, index) >> PAGE_SHIFT) + pgoff;
+
+       return remap_pfn_range(vma, vma->vm_start, phys,
+                              req_len, vma->vm_page_prot);
+}
+
+static const struct vfio_device_ops vfio_pci_ops = {
+       .name           = "vfio-pci",
+       .open           = vfio_pci_open,
+       .release        = vfio_pci_release,
+       .ioctl          = vfio_pci_ioctl,
+       .read           = vfio_pci_read,
+       .write          = vfio_pci_write,
+       .mmap           = vfio_pci_mmap,
+};
+
+static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+       u8 type;
+       struct vfio_pci_device *vdev;
+       struct iommu_group *group;
+       int ret;
+
+       pci_read_config_byte(pdev, PCI_HEADER_TYPE, &type);
+       if ((type & PCI_HEADER_TYPE) != PCI_HEADER_TYPE_NORMAL)
+               return -EINVAL;
+
+       group = iommu_group_get(&pdev->dev);
+       if (!group)
+               return -EINVAL;
+
+       vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+       if (!vdev) {
+               iommu_group_put(group);
+               return -ENOMEM;
+       }
+
+       vdev->pdev = pdev;
+       vdev->irq_type = VFIO_PCI_NUM_IRQS;
+       mutex_init(&vdev->igate);
+       spin_lock_init(&vdev->irqlock);
+       atomic_set(&vdev->refcnt, 0);
+
+       ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev);
+       if (ret) {
+               iommu_group_put(group);
+               kfree(vdev);
+       }
+
+       return ret;
+}
+
+static void vfio_pci_remove(struct pci_dev *pdev)
+{
+       struct vfio_pci_device *vdev;
+
+       vdev = vfio_del_group_dev(&pdev->dev);
+       if (!vdev)
+               return;
+
+       iommu_group_put(pdev->dev.iommu_group);
+       kfree(vdev);
+}
+
+static struct pci_driver vfio_pci_driver = {
+       .name           = "vfio-pci",
+       .id_table       = NULL, /* only dynamic ids */
+       .probe          = vfio_pci_probe,
+       .remove         = vfio_pci_remove,
+};
+
+static void __exit vfio_pci_cleanup(void)
+{
+       pci_unregister_driver(&vfio_pci_driver);
+       vfio_pci_virqfd_exit();
+       vfio_pci_uninit_perm_bits();
+}
+
+static int __init vfio_pci_init(void)
+{
+       int ret;
+
+       /* Allocate shared config space permision data used by all devices */
+       ret = vfio_pci_init_perm_bits();
+       if (ret)
+               return ret;
+
+       /* Start the virqfd cleanup handler */
+       ret = vfio_pci_virqfd_init();
+       if (ret)
+               goto out_virqfd;
+
+       /* Register and scan for devices */
+       ret = pci_register_driver(&vfio_pci_driver);
+       if (ret)
+               goto out_driver;
+
+       return 0;
+
+out_virqfd:
+       vfio_pci_virqfd_exit();
+out_driver:
+       vfio_pci_uninit_perm_bits();
+       return ret;
+}
+
+module_init(vfio_pci_init);
+module_exit(vfio_pci_cleanup);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
new file mode 100644 (file)
index 0000000..8b8f7d1
--- /dev/null
@@ -0,0 +1,1540 @@
+/*
+ * VFIO PCI config space virtualization
+ *
+ * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
+ *     Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc.  All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+/*
+ * This code handles reading and writing of PCI configuration registers.
+ * This is hairy because we want to allow a lot of flexibility to the
+ * user driver, but cannot trust it with all of the config fields.
+ * Tables determine which fields can be read and written, as well as
+ * which fields are 'virtualized' - special actions and translations to
+ * make it appear to the user that he has control, when in fact things
+ * must be negotiated with the underlying OS.
+ */
+
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/uaccess.h>
+#include <linux/vfio.h>
+
+#include "vfio_pci_private.h"
+
+#define PCI_CFG_SPACE_SIZE     256
+
+/* Useful "pseudo" capabilities */
+#define PCI_CAP_ID_BASIC       0
+#define PCI_CAP_ID_INVALID     0xFF
+
+#define is_bar(offset) \
+       ((offset >= PCI_BASE_ADDRESS_0 && offset < PCI_BASE_ADDRESS_5 + 4) || \
+        (offset >= PCI_ROM_ADDRESS && offset < PCI_ROM_ADDRESS + 4))
+
+/*
+ * Lengths of PCI Config Capabilities
+ *   0: Removed from the user visible capability list
+ *   FF: Variable length
+ */
+static u8 pci_cap_length[] = {
+       [PCI_CAP_ID_BASIC]      = PCI_STD_HEADER_SIZEOF, /* pci config header */
+       [PCI_CAP_ID_PM]         = PCI_PM_SIZEOF,
+       [PCI_CAP_ID_AGP]        = PCI_AGP_SIZEOF,
+       [PCI_CAP_ID_VPD]        = PCI_CAP_VPD_SIZEOF,
+       [PCI_CAP_ID_SLOTID]     = 0,            /* bridge - don't care */
+       [PCI_CAP_ID_MSI]        = 0xFF,         /* 10, 14, 20, or 24 */
+       [PCI_CAP_ID_CHSWP]      = 0,            /* cpci - not yet */
+       [PCI_CAP_ID_PCIX]       = 0xFF,         /* 8 or 24 */
+       [PCI_CAP_ID_HT]         = 0xFF,         /* hypertransport */
+       [PCI_CAP_ID_VNDR]       = 0xFF,         /* variable */
+       [PCI_CAP_ID_DBG]        = 0,            /* debug - don't care */
+       [PCI_CAP_ID_CCRC]       = 0,            /* cpci - not yet */
+       [PCI_CAP_ID_SHPC]       = 0,            /* hotswap - not yet */
+       [PCI_CAP_ID_SSVID]      = 0,            /* bridge - don't care */
+       [PCI_CAP_ID_AGP3]       = 0,            /* AGP8x - not yet */
+       [PCI_CAP_ID_SECDEV]     = 0,            /* secure device not yet */
+       [PCI_CAP_ID_EXP]        = 0xFF,         /* 20 or 44 */
+       [PCI_CAP_ID_MSIX]       = PCI_CAP_MSIX_SIZEOF,
+       [PCI_CAP_ID_SATA]       = 0xFF,
+       [PCI_CAP_ID_AF]         = PCI_CAP_AF_SIZEOF,
+};
+
+/*
+ * Lengths of PCIe/PCI-X Extended Config Capabilities
+ *   0: Removed or masked from the user visible capabilty list
+ *   FF: Variable length
+ */
+static u16 pci_ext_cap_length[] = {
+       [PCI_EXT_CAP_ID_ERR]    =       PCI_ERR_ROOT_COMMAND,
+       [PCI_EXT_CAP_ID_VC]     =       0xFF,
+       [PCI_EXT_CAP_ID_DSN]    =       PCI_EXT_CAP_DSN_SIZEOF,
+       [PCI_EXT_CAP_ID_PWR]    =       PCI_EXT_CAP_PWR_SIZEOF,
+       [PCI_EXT_CAP_ID_RCLD]   =       0,      /* root only - don't care */
+       [PCI_EXT_CAP_ID_RCILC]  =       0,      /* root only - don't care */
+       [PCI_EXT_CAP_ID_RCEC]   =       0,      /* root only - don't care */
+       [PCI_EXT_CAP_ID_MFVC]   =       0xFF,
+       [PCI_EXT_CAP_ID_VC9]    =       0xFF,   /* same as CAP_ID_VC */
+       [PCI_EXT_CAP_ID_RCRB]   =       0,      /* root only - don't care */
+       [PCI_EXT_CAP_ID_VNDR]   =       0xFF,
+       [PCI_EXT_CAP_ID_CAC]    =       0,      /* obsolete */
+       [PCI_EXT_CAP_ID_ACS]    =       0xFF,
+       [PCI_EXT_CAP_ID_ARI]    =       PCI_EXT_CAP_ARI_SIZEOF,
+       [PCI_EXT_CAP_ID_ATS]    =       PCI_EXT_CAP_ATS_SIZEOF,
+       [PCI_EXT_CAP_ID_SRIOV]  =       PCI_EXT_CAP_SRIOV_SIZEOF,
+       [PCI_EXT_CAP_ID_MRIOV]  =       0,      /* not yet */
+       [PCI_EXT_CAP_ID_MCAST]  =       PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF,
+       [PCI_EXT_CAP_ID_PRI]    =       PCI_EXT_CAP_PRI_SIZEOF,
+       [PCI_EXT_CAP_ID_AMD_XXX] =      0,      /* not yet */
+       [PCI_EXT_CAP_ID_REBAR]  =       0xFF,
+       [PCI_EXT_CAP_ID_DPA]    =       0xFF,
+       [PCI_EXT_CAP_ID_TPH]    =       0xFF,
+       [PCI_EXT_CAP_ID_LTR]    =       PCI_EXT_CAP_LTR_SIZEOF,
+       [PCI_EXT_CAP_ID_SECPCI] =       0,      /* not yet */
+       [PCI_EXT_CAP_ID_PMUX]   =       0,      /* not yet */
+       [PCI_EXT_CAP_ID_PASID]  =       0,      /* not yet */
+};
+
+/*
+ * Read/Write Permission Bits - one bit for each bit in capability
+ * Any field can be read if it exists, but what is read depends on
+ * whether the field is 'virtualized', or just pass thru to the
+ * hardware.  Any virtualized field is also virtualized for writes.
+ * Writes are only permitted if they have a 1 bit here.
+ */
+struct perm_bits {
+       u8      *virt;          /* read/write virtual data, not hw */
+       u8      *write;         /* writeable bits */
+       int     (*readfn)(struct vfio_pci_device *vdev, int pos, int count,
+                         struct perm_bits *perm, int offset, __le32 *val);
+       int     (*writefn)(struct vfio_pci_device *vdev, int pos, int count,
+                          struct perm_bits *perm, int offset, __le32 val);
+};
+
+#define        NO_VIRT         0
+#define        ALL_VIRT        0xFFFFFFFFU
+#define        NO_WRITE        0
+#define        ALL_WRITE       0xFFFFFFFFU
+
+static int vfio_user_config_read(struct pci_dev *pdev, int offset,
+                                __le32 *val, int count)
+{
+       int ret = -EINVAL;
+       u32 tmp_val = 0;
+
+       switch (count) {
+       case 1:
+       {
+               u8 tmp;
+               ret = pci_user_read_config_byte(pdev, offset, &tmp);
+               tmp_val = tmp;
+               break;
+       }
+       case 2:
+       {
+               u16 tmp;
+               ret = pci_user_read_config_word(pdev, offset, &tmp);
+               tmp_val = tmp;
+               break;
+       }
+       case 4:
+               ret = pci_user_read_config_dword(pdev, offset, &tmp_val);
+               break;
+       }
+
+       *val = cpu_to_le32(tmp_val);
+
+       return pcibios_err_to_errno(ret);
+}
+
+static int vfio_user_config_write(struct pci_dev *pdev, int offset,
+                                 __le32 val, int count)
+{
+       int ret = -EINVAL;
+       u32 tmp_val = le32_to_cpu(val);
+
+       switch (count) {
+       case 1:
+               ret = pci_user_write_config_byte(pdev, offset, tmp_val);
+               break;
+       case 2:
+               ret = pci_user_write_config_word(pdev, offset, tmp_val);
+               break;
+       case 4:
+               ret = pci_user_write_config_dword(pdev, offset, tmp_val);
+               break;
+       }
+
+       return pcibios_err_to_errno(ret);
+}
+
+static int vfio_default_config_read(struct vfio_pci_device *vdev, int pos,
+                                   int count, struct perm_bits *perm,
+                                   int offset, __le32 *val)
+{
+       __le32 virt = 0;
+
+       memcpy(val, vdev->vconfig + pos, count);
+
+       memcpy(&virt, perm->virt + offset, count);
+
+       /* Any non-virtualized bits? */
+       if (cpu_to_le32(~0U >> (32 - (count * 8))) != virt) {
+               struct pci_dev *pdev = vdev->pdev;
+               __le32 phys_val = 0;
+               int ret;
+
+               ret = vfio_user_config_read(pdev, pos, &phys_val, count);
+               if (ret)
+                       return ret;
+
+               *val = (phys_val & ~virt) | (*val & virt);
+       }
+
+       return count;
+}
+
+static int vfio_default_config_write(struct vfio_pci_device *vdev, int pos,
+                                    int count, struct perm_bits *perm,
+                                    int offset, __le32 val)
+{
+       __le32 virt = 0, write = 0;
+
+       memcpy(&write, perm->write + offset, count);
+
+       if (!write)
+               return count; /* drop, no writable bits */
+
+       memcpy(&virt, perm->virt + offset, count);
+
+       /* Virtualized and writable bits go to vconfig */
+       if (write & virt) {
+               __le32 virt_val = 0;
+
+               memcpy(&virt_val, vdev->vconfig + pos, count);
+
+               virt_val &= ~(write & virt);
+               virt_val |= (val & (write & virt));
+
+               memcpy(vdev->vconfig + pos, &virt_val, count);
+       }
+
+       /* Non-virtualzed and writable bits go to hardware */
+       if (write & ~virt) {
+               struct pci_dev *pdev = vdev->pdev;
+               __le32 phys_val = 0;
+               int ret;
+
+               ret = vfio_user_config_read(pdev, pos, &phys_val, count);
+               if (ret)
+                       return ret;
+
+               phys_val &= ~(write & ~virt);
+               phys_val |= (val & (write & ~virt));
+
+               ret = vfio_user_config_write(pdev, pos, phys_val, count);
+               if (ret)
+                       return ret;
+       }
+
+       return count;
+}
+
+/* Allow direct read from hardware, except for capability next pointer */
+static int vfio_direct_config_read(struct vfio_pci_device *vdev, int pos,
+                                  int count, struct perm_bits *perm,
+                                  int offset, __le32 *val)
+{
+       int ret;
+
+       ret = vfio_user_config_read(vdev->pdev, pos, val, count);
+       if (ret)
+               return pcibios_err_to_errno(ret);
+
+       if (pos >= PCI_CFG_SPACE_SIZE) { /* Extended cap header mangling */
+               if (offset < 4)
+                       memcpy(val, vdev->vconfig + pos, count);
+       } else if (pos >= PCI_STD_HEADER_SIZEOF) { /* Std cap mangling */
+               if (offset == PCI_CAP_LIST_ID && count > 1)
+                       memcpy(val, vdev->vconfig + pos,
+                              min(PCI_CAP_FLAGS, count));
+               else if (offset == PCI_CAP_LIST_NEXT)
+                       memcpy(val, vdev->vconfig + pos, 1);
+       }
+
+       return count;
+}
+
+static int vfio_direct_config_write(struct vfio_pci_device *vdev, int pos,
+                                   int count, struct perm_bits *perm,
+                                   int offset, __le32 val)
+{
+       int ret;
+
+       ret = vfio_user_config_write(vdev->pdev, pos, val, count);
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+/* Default all regions to read-only, no-virtualization */
+static struct perm_bits cap_perms[PCI_CAP_ID_MAX + 1] = {
+       [0 ... PCI_CAP_ID_MAX] = { .readfn = vfio_direct_config_read }
+};
+static struct perm_bits ecap_perms[PCI_EXT_CAP_ID_MAX + 1] = {
+       [0 ... PCI_EXT_CAP_ID_MAX] = { .readfn = vfio_direct_config_read }
+};
+
+static void free_perm_bits(struct perm_bits *perm)
+{
+       kfree(perm->virt);
+       kfree(perm->write);
+       perm->virt = NULL;
+       perm->write = NULL;
+}
+
+static int alloc_perm_bits(struct perm_bits *perm, int size)
+{
+       /*
+        * Round up all permission bits to the next dword, this lets us
+        * ignore whether a read/write exceeds the defined capability
+        * structure.  We can do this because:
+        *  - Standard config space is already dword aligned
+        *  - Capabilities are all dword alinged (bits 0:1 of next reserved)
+        *  - Express capabilities defined as dword aligned
+        */
+       size = round_up(size, 4);
+
+       /*
+        * Zero state is
+        * - All Readable, None Writeable, None Virtualized
+        */
+       perm->virt = kzalloc(size, GFP_KERNEL);
+       perm->write = kzalloc(size, GFP_KERNEL);
+       if (!perm->virt || !perm->write) {
+               free_perm_bits(perm);
+               return -ENOMEM;
+       }
+
+       perm->readfn = vfio_default_config_read;
+       perm->writefn = vfio_default_config_write;
+
+       return 0;
+}
+
+/*
+ * Helper functions for filling in permission tables
+ */
+static inline void p_setb(struct perm_bits *p, int off, u8 virt, u8 write)
+{
+       p->virt[off] = virt;
+       p->write[off] = write;
+}
+
+/* Handle endian-ness - pci and tables are little-endian */
+static inline void p_setw(struct perm_bits *p, int off, u16 virt, u16 write)
+{
+       *(__le16 *)(&p->virt[off]) = cpu_to_le16(virt);
+       *(__le16 *)(&p->write[off]) = cpu_to_le16(write);
+}
+
+/* Handle endian-ness - pci and tables are little-endian */
+static inline void p_setd(struct perm_bits *p, int off, u32 virt, u32 write)
+{
+       *(__le32 *)(&p->virt[off]) = cpu_to_le32(virt);
+       *(__le32 *)(&p->write[off]) = cpu_to_le32(write);
+}
+
+/*
+ * Restore the *real* BARs after we detect a FLR or backdoor reset.
+ * (backdoor = some device specific technique that we didn't catch)
+ */
+static void vfio_bar_restore(struct vfio_pci_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       u32 *rbar = vdev->rbar;
+       int i;
+
+       if (pdev->is_virtfn)
+               return;
+
+       pr_info("%s: %s reset recovery - restoring bars\n",
+               __func__, dev_name(&pdev->dev));
+
+       for (i = PCI_BASE_ADDRESS_0; i <= PCI_BASE_ADDRESS_5; i += 4, rbar++)
+               pci_user_write_config_dword(pdev, i, *rbar);
+
+       pci_user_write_config_dword(pdev, PCI_ROM_ADDRESS, *rbar);
+}
+
+static __le32 vfio_generate_bar_flags(struct pci_dev *pdev, int bar)
+{
+       unsigned long flags = pci_resource_flags(pdev, bar);
+       u32 val;
+
+       if (flags & IORESOURCE_IO)
+               return cpu_to_le32(PCI_BASE_ADDRESS_SPACE_IO);
+
+       val = PCI_BASE_ADDRESS_SPACE_MEMORY;
+
+       if (flags & IORESOURCE_PREFETCH)
+               val |= PCI_BASE_ADDRESS_MEM_PREFETCH;
+
+       if (flags & IORESOURCE_MEM_64)
+               val |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+
+       return cpu_to_le32(val);
+}
+
+/*
+ * Pretend we're hardware and tweak the values of the *virtual* PCI BARs
+ * to reflect the hardware capabilities.  This implements BAR sizing.
+ */
+static void vfio_bar_fixup(struct vfio_pci_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       int i;
+       __le32 *bar;
+       u64 mask;
+
+       bar = (__le32 *)&vdev->vconfig[PCI_BASE_ADDRESS_0];
+
+       for (i = PCI_STD_RESOURCES; i <= PCI_STD_RESOURCE_END; i++, bar++) {
+               if (!pci_resource_start(pdev, i)) {
+                       *bar = 0; /* Unmapped by host = unimplemented to user */
+                       continue;
+               }
+
+               mask = ~(pci_resource_len(pdev, i) - 1);
+
+               *bar &= cpu_to_le32((u32)mask);
+               *bar |= vfio_generate_bar_flags(pdev, i);
+
+               if (*bar & cpu_to_le32(PCI_BASE_ADDRESS_MEM_TYPE_64)) {
+                       bar++;
+                       *bar &= cpu_to_le32((u32)(mask >> 32));
+                       i++;
+               }
+       }
+
+       bar = (__le32 *)&vdev->vconfig[PCI_ROM_ADDRESS];
+
+       /*
+        * NB. we expose the actual BAR size here, regardless of whether
+        * we can read it.  When we report the REGION_INFO for the ROM
+        * we report what PCI tells us is the actual ROM size.
+        */
+       if (pci_resource_start(pdev, PCI_ROM_RESOURCE)) {
+               mask = ~(pci_resource_len(pdev, PCI_ROM_RESOURCE) - 1);
+               mask |= PCI_ROM_ADDRESS_ENABLE;
+               *bar &= cpu_to_le32((u32)mask);
+       } else
+               *bar = 0;
+
+       vdev->bardirty = false;
+}
+
+static int vfio_basic_config_read(struct vfio_pci_device *vdev, int pos,
+                                 int count, struct perm_bits *perm,
+                                 int offset, __le32 *val)
+{
+       if (is_bar(offset)) /* pos == offset for basic config */
+               vfio_bar_fixup(vdev);
+
+       count = vfio_default_config_read(vdev, pos, count, perm, offset, val);
+
+       /* Mask in virtual memory enable for SR-IOV devices */
+       if (offset == PCI_COMMAND && vdev->pdev->is_virtfn) {
+               u16 cmd = le16_to_cpu(*(__le16 *)&vdev->vconfig[PCI_COMMAND]);
+               u32 tmp_val = le32_to_cpu(*val);
+
+               tmp_val |= cmd & PCI_COMMAND_MEMORY;
+               *val = cpu_to_le32(tmp_val);
+       }
+
+       return count;
+}
+
+static int vfio_basic_config_write(struct vfio_pci_device *vdev, int pos,
+                                  int count, struct perm_bits *perm,
+                                  int offset, __le32 val)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       __le16 *virt_cmd;
+       u16 new_cmd = 0;
+       int ret;
+
+       virt_cmd = (__le16 *)&vdev->vconfig[PCI_COMMAND];
+
+       if (offset == PCI_COMMAND) {
+               bool phys_mem, virt_mem, new_mem, phys_io, virt_io, new_io;
+               u16 phys_cmd;
+
+               ret = pci_user_read_config_word(pdev, PCI_COMMAND, &phys_cmd);
+               if (ret)
+                       return ret;
+
+               new_cmd = le32_to_cpu(val);
+
+               phys_mem = !!(phys_cmd & PCI_COMMAND_MEMORY);
+               virt_mem = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_MEMORY);
+               new_mem = !!(new_cmd & PCI_COMMAND_MEMORY);
+
+               phys_io = !!(phys_cmd & PCI_COMMAND_IO);
+               virt_io = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_IO);
+               new_io = !!(new_cmd & PCI_COMMAND_IO);
+
+               /*
+                * If the user is writing mem/io enable (new_mem/io) and we
+                * think it's already enabled (virt_mem/io), but the hardware
+                * shows it disabled (phys_mem/io, then the device has
+                * undergone some kind of backdoor reset and needs to be
+                * restored before we allow it to enable the bars.
+                * SR-IOV devices will trigger this, but we catch them later
+                */
+               if ((new_mem && virt_mem && !phys_mem) ||
+                   (new_io && virt_io && !phys_io))
+                       vfio_bar_restore(vdev);
+       }
+
+       count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+       if (count < 0)
+               return count;
+
+       /*
+        * Save current memory/io enable bits in vconfig to allow for
+        * the test above next time.
+        */
+       if (offset == PCI_COMMAND) {
+               u16 mask = PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+
+               *virt_cmd &= cpu_to_le16(~mask);
+               *virt_cmd |= cpu_to_le16(new_cmd & mask);
+       }
+
+       /* Emulate INTx disable */
+       if (offset >= PCI_COMMAND && offset <= PCI_COMMAND + 1) {
+               bool virt_intx_disable;
+
+               virt_intx_disable = !!(le16_to_cpu(*virt_cmd) &
+                                      PCI_COMMAND_INTX_DISABLE);
+
+               if (virt_intx_disable && !vdev->virq_disabled) {
+                       vdev->virq_disabled = true;
+                       vfio_pci_intx_mask(vdev);
+               } else if (!virt_intx_disable && vdev->virq_disabled) {
+                       vdev->virq_disabled = false;
+                       vfio_pci_intx_unmask(vdev);
+               }
+       }
+
+       if (is_bar(offset))
+               vdev->bardirty = true;
+
+       return count;
+}
+
+/* Permissions for the Basic PCI Header */
+static int __init init_pci_cap_basic_perm(struct perm_bits *perm)
+{
+       if (alloc_perm_bits(perm, PCI_STD_HEADER_SIZEOF))
+               return -ENOMEM;
+
+       perm->readfn = vfio_basic_config_read;
+       perm->writefn = vfio_basic_config_write;
+
+       /* Virtualized for SR-IOV functions, which just have FFFF */
+       p_setw(perm, PCI_VENDOR_ID, (u16)ALL_VIRT, NO_WRITE);
+       p_setw(perm, PCI_DEVICE_ID, (u16)ALL_VIRT, NO_WRITE);
+
+       /*
+        * Virtualize INTx disable, we use it internally for interrupt
+        * control and can emulate it for non-PCI 2.3 devices.
+        */
+       p_setw(perm, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE, (u16)ALL_WRITE);
+
+       /* Virtualize capability list, we might want to skip/disable */
+       p_setw(perm, PCI_STATUS, PCI_STATUS_CAP_LIST, NO_WRITE);
+
+       /* No harm to write */
+       p_setb(perm, PCI_CACHE_LINE_SIZE, NO_VIRT, (u8)ALL_WRITE);
+       p_setb(perm, PCI_LATENCY_TIMER, NO_VIRT, (u8)ALL_WRITE);
+       p_setb(perm, PCI_BIST, NO_VIRT, (u8)ALL_WRITE);
+
+       /* Virtualize all bars, can't touch the real ones */
+       p_setd(perm, PCI_BASE_ADDRESS_0, ALL_VIRT, ALL_WRITE);
+       p_setd(perm, PCI_BASE_ADDRESS_1, ALL_VIRT, ALL_WRITE);
+       p_setd(perm, PCI_BASE_ADDRESS_2, ALL_VIRT, ALL_WRITE);
+       p_setd(perm, PCI_BASE_ADDRESS_3, ALL_VIRT, ALL_WRITE);
+       p_setd(perm, PCI_BASE_ADDRESS_4, ALL_VIRT, ALL_WRITE);
+       p_setd(perm, PCI_BASE_ADDRESS_5, ALL_VIRT, ALL_WRITE);
+       p_setd(perm, PCI_ROM_ADDRESS, ALL_VIRT, ALL_WRITE);
+
+       /* Allow us to adjust capability chain */
+       p_setb(perm, PCI_CAPABILITY_LIST, (u8)ALL_VIRT, NO_WRITE);
+
+       /* Sometimes used by sw, just virtualize */
+       p_setb(perm, PCI_INTERRUPT_LINE, (u8)ALL_VIRT, (u8)ALL_WRITE);
+       return 0;
+}
+
+/* Permissions for the Power Management capability */
+static int __init init_pci_cap_pm_perm(struct perm_bits *perm)
+{
+       if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_PM]))
+               return -ENOMEM;
+
+       /*
+        * We always virtualize the next field so we can remove
+        * capabilities from the chain if we want to.
+        */
+       p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+
+       /*
+        * Power management is defined *per function*,
+        * so we let the user write this
+        */
+       p_setd(perm, PCI_PM_CTRL, NO_VIRT, ALL_WRITE);
+       return 0;
+}
+
+/* Permissions for PCI-X capability */
+static int __init init_pci_cap_pcix_perm(struct perm_bits *perm)
+{
+       /* Alloc 24, but only 8 are used in v0 */
+       if (alloc_perm_bits(perm, PCI_CAP_PCIX_SIZEOF_V2))
+               return -ENOMEM;
+
+       p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+
+       p_setw(perm, PCI_X_CMD, NO_VIRT, (u16)ALL_WRITE);
+       p_setd(perm, PCI_X_ECC_CSR, NO_VIRT, ALL_WRITE);
+       return 0;
+}
+
+/* Permissions for PCI Express capability */
+static int __init init_pci_cap_exp_perm(struct perm_bits *perm)
+{
+       /* Alloc larger of two possible sizes */
+       if (alloc_perm_bits(perm, PCI_CAP_EXP_ENDPOINT_SIZEOF_V2))
+               return -ENOMEM;
+
+       p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+
+       /*
+        * Allow writes to device control fields (includes FLR!)
+        * but not to devctl_phantom which could confuse IOMMU
+        * or to the ARI bit in devctl2 which is set at probe time
+        */
+       p_setw(perm, PCI_EXP_DEVCTL, NO_VIRT, ~PCI_EXP_DEVCTL_PHANTOM);
+       p_setw(perm, PCI_EXP_DEVCTL2, NO_VIRT, ~PCI_EXP_DEVCTL2_ARI);
+       return 0;
+}
+
+/* Permissions for Advanced Function capability */
+static int __init init_pci_cap_af_perm(struct perm_bits *perm)
+{
+       if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_AF]))
+               return -ENOMEM;
+
+       p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+       p_setb(perm, PCI_AF_CTRL, NO_VIRT, PCI_AF_CTRL_FLR);
+       return 0;
+}
+
+/* Permissions for Advanced Error Reporting extended capability */
+static int __init init_pci_ext_cap_err_perm(struct perm_bits *perm)
+{
+       u32 mask;
+
+       if (alloc_perm_bits(perm, pci_ext_cap_length[PCI_EXT_CAP_ID_ERR]))
+               return -ENOMEM;
+
+       /*
+        * Virtualize the first dword of all express capabilities
+        * because it includes the next pointer.  This lets us later
+        * remove capabilities from the chain if we need to.
+        */
+       p_setd(perm, 0, ALL_VIRT, NO_WRITE);
+
+       /* Writable bits mask */
+       mask =  PCI_ERR_UNC_TRAIN |             /* Training */
+               PCI_ERR_UNC_DLP |               /* Data Link Protocol */
+               PCI_ERR_UNC_SURPDN |            /* Surprise Down */
+               PCI_ERR_UNC_POISON_TLP |        /* Poisoned TLP */
+               PCI_ERR_UNC_FCP |               /* Flow Control Protocol */
+               PCI_ERR_UNC_COMP_TIME |         /* Completion Timeout */
+               PCI_ERR_UNC_COMP_ABORT |        /* Completer Abort */
+               PCI_ERR_UNC_UNX_COMP |          /* Unexpected Completion */
+               PCI_ERR_UNC_RX_OVER |           /* Receiver Overflow */
+               PCI_ERR_UNC_MALF_TLP |          /* Malformed TLP */
+               PCI_ERR_UNC_ECRC |              /* ECRC Error Status */
+               PCI_ERR_UNC_UNSUP |             /* Unsupported Request */
+               PCI_ERR_UNC_ACSV |              /* ACS Violation */
+               PCI_ERR_UNC_INTN |              /* internal error */
+               PCI_ERR_UNC_MCBTLP |            /* MC blocked TLP */
+               PCI_ERR_UNC_ATOMEG |            /* Atomic egress blocked */
+               PCI_ERR_UNC_TLPPRE;             /* TLP prefix blocked */
+       p_setd(perm, PCI_ERR_UNCOR_STATUS, NO_VIRT, mask);
+       p_setd(perm, PCI_ERR_UNCOR_MASK, NO_VIRT, mask);
+       p_setd(perm, PCI_ERR_UNCOR_SEVER, NO_VIRT, mask);
+
+       mask =  PCI_ERR_COR_RCVR |              /* Receiver Error Status */
+               PCI_ERR_COR_BAD_TLP |           /* Bad TLP Status */
+               PCI_ERR_COR_BAD_DLLP |          /* Bad DLLP Status */
+               PCI_ERR_COR_REP_ROLL |          /* REPLAY_NUM Rollover */
+               PCI_ERR_COR_REP_TIMER |         /* Replay Timer Timeout */
+               PCI_ERR_COR_ADV_NFAT |          /* Advisory Non-Fatal */
+               PCI_ERR_COR_INTERNAL |          /* Corrected Internal */
+               PCI_ERR_COR_LOG_OVER;           /* Header Log Overflow */
+       p_setd(perm, PCI_ERR_COR_STATUS, NO_VIRT, mask);
+       p_setd(perm, PCI_ERR_COR_MASK, NO_VIRT, mask);
+
+       mask =  PCI_ERR_CAP_ECRC_GENE |         /* ECRC Generation Enable */
+               PCI_ERR_CAP_ECRC_CHKE;          /* ECRC Check Enable */
+       p_setd(perm, PCI_ERR_CAP, NO_VIRT, mask);
+       return 0;
+}
+
+/* Permissions for Power Budgeting extended capability */
+static int __init init_pci_ext_cap_pwr_perm(struct perm_bits *perm)
+{
+       if (alloc_perm_bits(perm, pci_ext_cap_length[PCI_EXT_CAP_ID_PWR]))
+               return -ENOMEM;
+
+       p_setd(perm, 0, ALL_VIRT, NO_WRITE);
+
+       /* Writing the data selector is OK, the info is still read-only */
+       p_setb(perm, PCI_PWR_DATA, NO_VIRT, (u8)ALL_WRITE);
+       return 0;
+}
+
+/*
+ * Initialize the shared permission tables
+ */
+void vfio_pci_uninit_perm_bits(void)
+{
+       free_perm_bits(&cap_perms[PCI_CAP_ID_BASIC]);
+
+       free_perm_bits(&cap_perms[PCI_CAP_ID_PM]);
+       free_perm_bits(&cap_perms[PCI_CAP_ID_PCIX]);
+       free_perm_bits(&cap_perms[PCI_CAP_ID_EXP]);
+       free_perm_bits(&cap_perms[PCI_CAP_ID_AF]);
+
+       free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_ERR]);
+       free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_PWR]);
+}
+
+int __init vfio_pci_init_perm_bits(void)
+{
+       int ret;
+
+       /* Basic config space */
+       ret = init_pci_cap_basic_perm(&cap_perms[PCI_CAP_ID_BASIC]);
+
+       /* Capabilities */
+       ret |= init_pci_cap_pm_perm(&cap_perms[PCI_CAP_ID_PM]);
+       cap_perms[PCI_CAP_ID_VPD].writefn = vfio_direct_config_write;
+       ret |= init_pci_cap_pcix_perm(&cap_perms[PCI_CAP_ID_PCIX]);
+       cap_perms[PCI_CAP_ID_VNDR].writefn = vfio_direct_config_write;
+       ret |= init_pci_cap_exp_perm(&cap_perms[PCI_CAP_ID_EXP]);
+       ret |= init_pci_cap_af_perm(&cap_perms[PCI_CAP_ID_AF]);
+
+       /* Extended capabilities */
+       ret |= init_pci_ext_cap_err_perm(&ecap_perms[PCI_EXT_CAP_ID_ERR]);
+       ret |= init_pci_ext_cap_pwr_perm(&ecap_perms[PCI_EXT_CAP_ID_PWR]);
+       ecap_perms[PCI_EXT_CAP_ID_VNDR].writefn = vfio_direct_config_write;
+
+       if (ret)
+               vfio_pci_uninit_perm_bits();
+
+       return ret;
+}
+
+static int vfio_find_cap_start(struct vfio_pci_device *vdev, int pos)
+{
+       u8 cap;
+       int base = (pos >= PCI_CFG_SPACE_SIZE) ? PCI_CFG_SPACE_SIZE :
+                                                PCI_STD_HEADER_SIZEOF;
+       base /= 4;
+       pos /= 4;
+
+       cap = vdev->pci_config_map[pos];
+
+       if (cap == PCI_CAP_ID_BASIC)
+               return 0;
+
+       /* XXX Can we have to abutting capabilities of the same type? */
+       while (pos - 1 >= base && vdev->pci_config_map[pos - 1] == cap)
+               pos--;
+
+       return pos * 4;
+}
+
+static int vfio_msi_config_read(struct vfio_pci_device *vdev, int pos,
+                               int count, struct perm_bits *perm,
+                               int offset, __le32 *val)
+{
+       /* Update max available queue size from msi_qmax */
+       if (offset <= PCI_MSI_FLAGS && offset + count >= PCI_MSI_FLAGS) {
+               __le16 *flags;
+               int start;
+
+               start = vfio_find_cap_start(vdev, pos);
+
+               flags = (__le16 *)&vdev->vconfig[start];
+
+               *flags &= cpu_to_le16(~PCI_MSI_FLAGS_QMASK);
+               *flags |= cpu_to_le16(vdev->msi_qmax << 1);
+       }
+
+       return vfio_default_config_read(vdev, pos, count, perm, offset, val);
+}
+
+static int vfio_msi_config_write(struct vfio_pci_device *vdev, int pos,
+                                int count, struct perm_bits *perm,
+                                int offset, __le32 val)
+{
+       count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+       if (count < 0)
+               return count;
+
+       /* Fixup and write configured queue size and enable to hardware */
+       if (offset <= PCI_MSI_FLAGS && offset + count >= PCI_MSI_FLAGS) {
+               __le16 *pflags;
+               u16 flags;
+               int start, ret;
+
+               start = vfio_find_cap_start(vdev, pos);
+
+               pflags = (__le16 *)&vdev->vconfig[start + PCI_MSI_FLAGS];
+
+               flags = le16_to_cpu(*pflags);
+
+               /* MSI is enabled via ioctl */
+               if  (!is_msi(vdev))
+                       flags &= ~PCI_MSI_FLAGS_ENABLE;
+
+               /* Check queue size */
+               if ((flags & PCI_MSI_FLAGS_QSIZE) >> 4 > vdev->msi_qmax) {
+                       flags &= ~PCI_MSI_FLAGS_QSIZE;
+                       flags |= vdev->msi_qmax << 4;
+               }
+
+               /* Write back to virt and to hardware */
+               *pflags = cpu_to_le16(flags);
+               ret = pci_user_write_config_word(vdev->pdev,
+                                                start + PCI_MSI_FLAGS,
+                                                flags);
+               if (ret)
+                       return pcibios_err_to_errno(ret);
+       }
+
+       return count;
+}
+
+/*
+ * MSI determination is per-device, so this routine gets used beyond
+ * initialization time. Don't add __init
+ */
+static int init_pci_cap_msi_perm(struct perm_bits *perm, int len, u16 flags)
+{
+       if (alloc_perm_bits(perm, len))
+               return -ENOMEM;
+
+       perm->readfn = vfio_msi_config_read;
+       perm->writefn = vfio_msi_config_write;
+
+       p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+
+       /*
+        * The upper byte of the control register is reserved,
+        * just setup the lower byte.
+        */
+       p_setb(perm, PCI_MSI_FLAGS, (u8)ALL_VIRT, (u8)ALL_WRITE);
+       p_setd(perm, PCI_MSI_ADDRESS_LO, ALL_VIRT, ALL_WRITE);
+       if (flags & PCI_MSI_FLAGS_64BIT) {
+               p_setd(perm, PCI_MSI_ADDRESS_HI, ALL_VIRT, ALL_WRITE);
+               p_setw(perm, PCI_MSI_DATA_64, (u16)ALL_VIRT, (u16)ALL_WRITE);
+               if (flags & PCI_MSI_FLAGS_MASKBIT) {
+                       p_setd(perm, PCI_MSI_MASK_64, NO_VIRT, ALL_WRITE);
+                       p_setd(perm, PCI_MSI_PENDING_64, NO_VIRT, ALL_WRITE);
+               }
+       } else {
+               p_setw(perm, PCI_MSI_DATA_32, (u16)ALL_VIRT, (u16)ALL_WRITE);
+               if (flags & PCI_MSI_FLAGS_MASKBIT) {
+                       p_setd(perm, PCI_MSI_MASK_32, NO_VIRT, ALL_WRITE);
+                       p_setd(perm, PCI_MSI_PENDING_32, NO_VIRT, ALL_WRITE);
+               }
+       }
+       return 0;
+}
+
+/* Determine MSI CAP field length; initialize msi_perms on 1st call per vdev */
+static int vfio_msi_cap_len(struct vfio_pci_device *vdev, u8 pos)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       int len, ret;
+       u16 flags;
+
+       ret = pci_read_config_word(pdev, pos + PCI_MSI_FLAGS, &flags);
+       if (ret)
+               return pcibios_err_to_errno(ret);
+
+       len = 10; /* Minimum size */
+       if (flags & PCI_MSI_FLAGS_64BIT)
+               len += 4;
+       if (flags & PCI_MSI_FLAGS_MASKBIT)
+               len += 10;
+
+       if (vdev->msi_perm)
+               return len;
+
+       vdev->msi_perm = kmalloc(sizeof(struct perm_bits), GFP_KERNEL);
+       if (!vdev->msi_perm)
+               return -ENOMEM;
+
+       ret = init_pci_cap_msi_perm(vdev->msi_perm, len, flags);
+       if (ret)
+               return ret;
+
+       return len;
+}
+
+/* Determine extended capability length for VC (2 & 9) and MFVC */
+static int vfio_vc_cap_len(struct vfio_pci_device *vdev, u16 pos)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       u32 tmp;
+       int ret, evcc, phases, vc_arb;
+       int len = PCI_CAP_VC_BASE_SIZEOF;
+
+       ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG1, &tmp);
+       if (ret)
+               return pcibios_err_to_errno(ret);
+
+       evcc = tmp & PCI_VC_REG1_EVCC; /* extended vc count */
+       ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG2, &tmp);
+       if (ret)
+               return pcibios_err_to_errno(ret);
+
+       if (tmp & PCI_VC_REG2_128_PHASE)
+               phases = 128;
+       else if (tmp & PCI_VC_REG2_64_PHASE)
+               phases = 64;
+       else if (tmp & PCI_VC_REG2_32_PHASE)
+               phases = 32;
+       else
+               phases = 0;
+
+       vc_arb = phases * 4;
+
+       /*
+        * Port arbitration tables are root & switch only;
+        * function arbitration tables are function 0 only.
+        * In either case, we'll never let user write them so
+        * we don't care how big they are
+        */
+       len += (1 + evcc) * PCI_CAP_VC_PER_VC_SIZEOF;
+       if (vc_arb) {
+               len = round_up(len, 16);
+               len += vc_arb / 8;
+       }
+       return len;
+}
+
+static int vfio_cap_len(struct vfio_pci_device *vdev, u8 cap, u8 pos)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       u16 word;
+       u8 byte;
+       int ret;
+
+       switch (cap) {
+       case PCI_CAP_ID_MSI:
+               return vfio_msi_cap_len(vdev, pos);
+       case PCI_CAP_ID_PCIX:
+               ret = pci_read_config_word(pdev, pos + PCI_X_CMD, &word);
+               if (ret)
+                       return pcibios_err_to_errno(ret);
+
+               if (PCI_X_CMD_VERSION(word)) {
+                       vdev->extended_caps = true;
+                       return PCI_CAP_PCIX_SIZEOF_V2;
+               } else
+                       return PCI_CAP_PCIX_SIZEOF_V0;
+       case PCI_CAP_ID_VNDR:
+               /* length follows next field */
+               ret = pci_read_config_byte(pdev, pos + PCI_CAP_FLAGS, &byte);
+               if (ret)
+                       return pcibios_err_to_errno(ret);
+
+               return byte;
+       case PCI_CAP_ID_EXP:
+               /* length based on version */
+               ret = pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &word);
+               if (ret)
+                       return pcibios_err_to_errno(ret);
+
+               if ((word & PCI_EXP_FLAGS_VERS) == 1)
+                       return PCI_CAP_EXP_ENDPOINT_SIZEOF_V1;
+               else {
+                       vdev->extended_caps = true;
+                       return PCI_CAP_EXP_ENDPOINT_SIZEOF_V2;
+               }
+       case PCI_CAP_ID_HT:
+               ret = pci_read_config_byte(pdev, pos + 3, &byte);
+               if (ret)
+                       return pcibios_err_to_errno(ret);
+
+               return (byte & HT_3BIT_CAP_MASK) ?
+                       HT_CAP_SIZEOF_SHORT : HT_CAP_SIZEOF_LONG;
+       case PCI_CAP_ID_SATA:
+               ret = pci_read_config_byte(pdev, pos + PCI_SATA_REGS, &byte);
+               if (ret)
+                       return pcibios_err_to_errno(ret);
+
+               byte &= PCI_SATA_REGS_MASK;
+               if (byte == PCI_SATA_REGS_INLINE)
+                       return PCI_SATA_SIZEOF_LONG;
+               else
+                       return PCI_SATA_SIZEOF_SHORT;
+       default:
+               pr_warn("%s: %s unknown length for pci cap 0x%x@0x%x\n",
+                       dev_name(&pdev->dev), __func__, cap, pos);
+       }
+
+       return 0;
+}
+
+static int vfio_ext_cap_len(struct vfio_pci_device *vdev, u16 ecap, u16 epos)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       u8 byte;
+       u32 dword;
+       int ret;
+
+       switch (ecap) {
+       case PCI_EXT_CAP_ID_VNDR:
+               ret = pci_read_config_dword(pdev, epos + PCI_VSEC_HDR, &dword);
+               if (ret)
+                       return pcibios_err_to_errno(ret);
+
+               return dword >> PCI_VSEC_HDR_LEN_SHIFT;
+       case PCI_EXT_CAP_ID_VC:
+       case PCI_EXT_CAP_ID_VC9:
+       case PCI_EXT_CAP_ID_MFVC:
+               return vfio_vc_cap_len(vdev, epos);
+       case PCI_EXT_CAP_ID_ACS:
+               ret = pci_read_config_byte(pdev, epos + PCI_ACS_CAP, &byte);
+               if (ret)
+                       return pcibios_err_to_errno(ret);
+
+               if (byte & PCI_ACS_EC) {
+                       int bits;
+
+                       ret = pci_read_config_byte(pdev,
+                                                  epos + PCI_ACS_EGRESS_BITS,
+                                                  &byte);
+                       if (ret)
+                               return pcibios_err_to_errno(ret);
+
+                       bits = byte ? round_up(byte, 32) : 256;
+                       return 8 + (bits / 8);
+               }
+               return 8;
+
+       case PCI_EXT_CAP_ID_REBAR:
+               ret = pci_read_config_byte(pdev, epos + PCI_REBAR_CTRL, &byte);
+               if (ret)
+                       return pcibios_err_to_errno(ret);
+
+               byte &= PCI_REBAR_CTRL_NBAR_MASK;
+               byte >>= PCI_REBAR_CTRL_NBAR_SHIFT;
+
+               return 4 + (byte * 8);
+       case PCI_EXT_CAP_ID_DPA:
+               ret = pci_read_config_byte(pdev, epos + PCI_DPA_CAP, &byte);
+               if (ret)
+                       return pcibios_err_to_errno(ret);
+
+               byte &= PCI_DPA_CAP_SUBSTATE_MASK;
+               byte = round_up(byte + 1, 4);
+               return PCI_DPA_BASE_SIZEOF + byte;
+       case PCI_EXT_CAP_ID_TPH:
+               ret = pci_read_config_dword(pdev, epos + PCI_TPH_CAP, &dword);
+               if (ret)
+                       return pcibios_err_to_errno(ret);
+
+               if ((dword & PCI_TPH_CAP_LOC_MASK) == PCI_TPH_LOC_CAP) {
+                       int sts;
+
+                       sts = byte & PCI_TPH_CAP_ST_MASK;
+                       sts >>= PCI_TPH_CAP_ST_SHIFT;
+                       return PCI_TPH_BASE_SIZEOF + round_up(sts * 2, 4);
+               }
+               return PCI_TPH_BASE_SIZEOF;
+       default:
+               pr_warn("%s: %s unknown length for pci ecap 0x%x@0x%x\n",
+                       dev_name(&pdev->dev), __func__, ecap, epos);
+       }
+
+       return 0;
+}
+
+static int vfio_fill_vconfig_bytes(struct vfio_pci_device *vdev,
+                                  int offset, int size)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       int ret = 0;
+
+       /*
+        * We try to read physical config space in the largest chunks
+        * we can, assuming that all of the fields support dword access.
+        * pci_save_state() makes this same assumption and seems to do ok.
+        */
+       while (size) {
+               int filled;
+
+               if (size >= 4 && !(offset % 4)) {
+                       __le32 *dwordp = (__le32 *)&vdev->vconfig[offset];
+                       u32 dword;
+
+                       ret = pci_read_config_dword(pdev, offset, &dword);
+                       if (ret)
+                               return ret;
+                       *dwordp = cpu_to_le32(dword);
+                       filled = 4;
+               } else if (size >= 2 && !(offset % 2)) {
+                       __le16 *wordp = (__le16 *)&vdev->vconfig[offset];
+                       u16 word;
+
+                       ret = pci_read_config_word(pdev, offset, &word);
+                       if (ret)
+                               return ret;
+                       *wordp = cpu_to_le16(word);
+                       filled = 2;
+               } else {
+                       u8 *byte = &vdev->vconfig[offset];
+                       ret = pci_read_config_byte(pdev, offset, byte);
+                       if (ret)
+                               return ret;
+                       filled = 1;
+               }
+
+               offset += filled;
+               size -= filled;
+       }
+
+       return ret;
+}
+
+static int vfio_cap_init(struct vfio_pci_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       u8 *map = vdev->pci_config_map;
+       u16 status;
+       u8 pos, *prev, cap;
+       int loops, ret, caps = 0;
+
+       /* Any capabilities? */
+       ret = pci_read_config_word(pdev, PCI_STATUS, &status);
+       if (ret)
+               return ret;
+
+       if (!(status & PCI_STATUS_CAP_LIST))
+               return 0; /* Done */
+
+       ret = pci_read_config_byte(pdev, PCI_CAPABILITY_LIST, &pos);
+       if (ret)
+               return ret;
+
+       /* Mark the previous position in case we want to skip a capability */
+       prev = &vdev->vconfig[PCI_CAPABILITY_LIST];
+
+       /* We can bound our loop, capabilities are dword aligned */
+       loops = (PCI_CFG_SPACE_SIZE - PCI_STD_HEADER_SIZEOF) / PCI_CAP_SIZEOF;
+       while (pos && loops--) {
+               u8 next;
+               int i, len = 0;
+
+               ret = pci_read_config_byte(pdev, pos, &cap);
+               if (ret)
+                       return ret;
+
+               ret = pci_read_config_byte(pdev,
+                                          pos + PCI_CAP_LIST_NEXT, &next);
+               if (ret)
+                       return ret;
+
+               if (cap <= PCI_CAP_ID_MAX) {
+                       len = pci_cap_length[cap];
+                       if (len == 0xFF) { /* Variable length */
+                               len = vfio_cap_len(vdev, cap, pos);
+                               if (len < 0)
+                                       return len;
+                       }
+               }
+
+               if (!len) {
+                       pr_info("%s: %s hiding cap 0x%x\n",
+                               __func__, dev_name(&pdev->dev), cap);
+                       *prev = next;
+                       pos = next;
+                       continue;
+               }
+
+               /* Sanity check, do we overlap other capabilities? */
+               for (i = 0; i < len; i += 4) {
+                       if (likely(map[(pos + i) / 4] == PCI_CAP_ID_INVALID))
+                               continue;
+
+                       pr_warn("%s: %s pci config conflict @0x%x, was cap 0x%x now cap 0x%x\n",
+                               __func__, dev_name(&pdev->dev),
+                               pos + i, map[pos + i], cap);
+               }
+
+               memset(map + (pos / 4), cap, len / 4);
+               ret = vfio_fill_vconfig_bytes(vdev, pos, len);
+               if (ret)
+                       return ret;
+
+               prev = &vdev->vconfig[pos + PCI_CAP_LIST_NEXT];
+               pos = next;
+               caps++;
+       }
+
+       /* If we didn't fill any capabilities, clear the status flag */
+       if (!caps) {
+               __le16 *vstatus = (__le16 *)&vdev->vconfig[PCI_STATUS];
+               *vstatus &= ~cpu_to_le16(PCI_STATUS_CAP_LIST);
+       }
+
+       return 0;
+}
+
+static int vfio_ecap_init(struct vfio_pci_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       u8 *map = vdev->pci_config_map;
+       u16 epos;
+       __le32 *prev = NULL;
+       int loops, ret, ecaps = 0;
+
+       if (!vdev->extended_caps)
+               return 0;
+
+       epos = PCI_CFG_SPACE_SIZE;
+
+       loops = (pdev->cfg_size - PCI_CFG_SPACE_SIZE) / PCI_CAP_SIZEOF;
+
+       while (loops-- && epos >= PCI_CFG_SPACE_SIZE) {
+               u32 header;
+               u16 ecap;
+               int i, len = 0;
+               bool hidden = false;
+
+               ret = pci_read_config_dword(pdev, epos, &header);
+               if (ret)
+                       return ret;
+
+               ecap = PCI_EXT_CAP_ID(header);
+
+               if (ecap <= PCI_EXT_CAP_ID_MAX) {
+                       len = pci_ext_cap_length[ecap];
+                       if (len == 0xFF) {
+                               len = vfio_ext_cap_len(vdev, ecap, epos);
+                               if (len < 0)
+                                       return ret;
+                       }
+               }
+
+               if (!len) {
+                       pr_info("%s: %s hiding ecap 0x%x@0x%x\n",
+                               __func__, dev_name(&pdev->dev), ecap, epos);
+
+                       /* If not the first in the chain, we can skip over it */
+                       if (prev) {
+                               u32 val = epos = PCI_EXT_CAP_NEXT(header);
+                               *prev &= cpu_to_le32(~(0xffcU << 20));
+                               *prev |= cpu_to_le32(val << 20);
+                               continue;
+                       }
+
+                       /*
+                        * Otherwise, fill in a placeholder, the direct
+                        * readfn will virtualize this automatically
+                        */
+                       len = PCI_CAP_SIZEOF;
+                       hidden = true;
+               }
+
+               for (i = 0; i < len; i += 4) {
+                       if (likely(map[(epos + i) / 4] == PCI_CAP_ID_INVALID))
+                               continue;
+
+                       pr_warn("%s: %s pci config conflict @0x%x, was ecap 0x%x now ecap 0x%x\n",
+                               __func__, dev_name(&pdev->dev),
+                               epos + i, map[epos + i], ecap);
+               }
+
+               /*
+                * Even though ecap is 2 bytes, we're currently a long way
+                * from exceeding 1 byte capabilities.  If we ever make it
+                * up to 0xFF we'll need to up this to a two-byte, byte map.
+                */
+               BUILD_BUG_ON(PCI_EXT_CAP_ID_MAX >= PCI_CAP_ID_INVALID);
+
+               memset(map + (epos / 4), ecap, len / 4);
+               ret = vfio_fill_vconfig_bytes(vdev, epos, len);
+               if (ret)
+                       return ret;
+
+               /*
+                * If we're just using this capability to anchor the list,
+                * hide the real ID.  Only count real ecaps.  XXX PCI spec
+                * indicates to use cap id = 0, version = 0, next = 0 if
+                * ecaps are absent, hope users check all the way to next.
+                */
+               if (hidden)
+                       *(__le32 *)&vdev->vconfig[epos] &=
+                               cpu_to_le32((0xffcU << 20));
+               else
+                       ecaps++;
+
+               prev = (__le32 *)&vdev->vconfig[epos];
+               epos = PCI_EXT_CAP_NEXT(header);
+       }
+
+       if (!ecaps)
+               *(u32 *)&vdev->vconfig[PCI_CFG_SPACE_SIZE] = 0;
+
+       return 0;
+}
+
+/*
+ * For each device we allocate a pci_config_map that indicates the
+ * capability occupying each dword and thus the struct perm_bits we
+ * use for read and write.  We also allocate a virtualized config
+ * space which tracks reads and writes to bits that we emulate for
+ * the user.  Initial values filled from device.
+ *
+ * Using shared stuct perm_bits between all vfio-pci devices saves
+ * us from allocating cfg_size buffers for virt and write for every
+ * device.  We could remove vconfig and allocate individual buffers
+ * for each area requring emulated bits, but the array of pointers
+ * would be comparable in size (at least for standard config space).
+ */
+int vfio_config_init(struct vfio_pci_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       u8 *map, *vconfig;
+       int ret;
+
+       /*
+        * Config space, caps and ecaps are all dword aligned, so we can
+        * use one byte per dword to record the type.
+        */
+       map = kmalloc(pdev->cfg_size / 4, GFP_KERNEL);
+       if (!map)
+               return -ENOMEM;
+
+       vconfig = kmalloc(pdev->cfg_size, GFP_KERNEL);
+       if (!vconfig) {
+               kfree(map);
+               return -ENOMEM;
+       }
+
+       vdev->pci_config_map = map;
+       vdev->vconfig = vconfig;
+
+       memset(map, PCI_CAP_ID_BASIC, PCI_STD_HEADER_SIZEOF / 4);
+       memset(map + (PCI_STD_HEADER_SIZEOF / 4), PCI_CAP_ID_INVALID,
+              (pdev->cfg_size - PCI_STD_HEADER_SIZEOF) / 4);
+
+       ret = vfio_fill_vconfig_bytes(vdev, 0, PCI_STD_HEADER_SIZEOF);
+       if (ret)
+               goto out;
+
+       vdev->bardirty = true;
+
+       /*
+        * XXX can we just pci_load_saved_state/pci_restore_state?
+        * may need to rebuild vconfig after that
+        */
+
+       /* For restore after reset */
+       vdev->rbar[0] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_0]);
+       vdev->rbar[1] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_1]);
+       vdev->rbar[2] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_2]);
+       vdev->rbar[3] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_3]);
+       vdev->rbar[4] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_4]);
+       vdev->rbar[5] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_5]);
+       vdev->rbar[6] = le32_to_cpu(*(__le32 *)&vconfig[PCI_ROM_ADDRESS]);
+
+       if (pdev->is_virtfn) {
+               *(__le16 *)&vconfig[PCI_VENDOR_ID] = cpu_to_le16(pdev->vendor);
+               *(__le16 *)&vconfig[PCI_DEVICE_ID] = cpu_to_le16(pdev->device);
+       }
+
+       ret = vfio_cap_init(vdev);
+       if (ret)
+               goto out;
+
+       ret = vfio_ecap_init(vdev);
+       if (ret)
+               goto out;
+
+       return 0;
+
+out:
+       kfree(map);
+       vdev->pci_config_map = NULL;
+       kfree(vconfig);
+       vdev->vconfig = NULL;
+       return pcibios_err_to_errno(ret);
+}
+
+void vfio_config_free(struct vfio_pci_device *vdev)
+{
+       kfree(vdev->vconfig);
+       vdev->vconfig = NULL;
+       kfree(vdev->pci_config_map);
+       vdev->pci_config_map = NULL;
+       kfree(vdev->msi_perm);
+       vdev->msi_perm = NULL;
+}
+
+static ssize_t vfio_config_do_rw(struct vfio_pci_device *vdev, char __user *buf,
+                                size_t count, loff_t *ppos, bool iswrite)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       struct perm_bits *perm;
+       __le32 val = 0;
+       int cap_start = 0, offset;
+       u8 cap_id;
+       ssize_t ret = count;
+
+       if (*ppos < 0 || *ppos + count > pdev->cfg_size)
+               return -EFAULT;
+
+       /*
+        * gcc can't seem to figure out we're a static function, only called
+        * with count of 1/2/4 and hits copy_from_user_overflow without this.
+        */
+       if (count > sizeof(val))
+               return -EINVAL;
+
+       cap_id = vdev->pci_config_map[*ppos / 4];
+
+       if (cap_id == PCI_CAP_ID_INVALID) {
+               if (iswrite)
+                       return ret; /* drop */
+
+               /*
+                * Per PCI spec 3.0, section 6.1, reads from reserved and
+                * unimplemented registers return 0
+                */
+               if (copy_to_user(buf, &val, count))
+                       return -EFAULT;
+
+               return ret;
+       }
+
+       /*
+        * All capabilities are minimum 4 bytes and aligned on dword
+        * boundaries.  Since we don't support unaligned accesses, we're
+        * only ever accessing a single capability.
+        */
+       if (*ppos >= PCI_CFG_SPACE_SIZE) {
+               WARN_ON(cap_id > PCI_EXT_CAP_ID_MAX);
+
+               perm = &ecap_perms[cap_id];
+               cap_start = vfio_find_cap_start(vdev, *ppos);
+
+       } else {
+               WARN_ON(cap_id > PCI_CAP_ID_MAX);
+
+               perm = &cap_perms[cap_id];
+
+               if (cap_id == PCI_CAP_ID_MSI)
+                       perm = vdev->msi_perm;
+
+               if (cap_id > PCI_CAP_ID_BASIC)
+                       cap_start = vfio_find_cap_start(vdev, *ppos);
+       }
+
+       WARN_ON(!cap_start && cap_id != PCI_CAP_ID_BASIC);
+       WARN_ON(cap_start > *ppos);
+
+       offset = *ppos - cap_start;
+
+       if (iswrite) {
+               if (!perm->writefn)
+                       return ret;
+
+               if (copy_from_user(&val, buf, count))
+                       return -EFAULT;
+
+               ret = perm->writefn(vdev, *ppos, count, perm, offset, val);
+       } else {
+               if (perm->readfn) {
+                       ret = perm->readfn(vdev, *ppos, count,
+                                          perm, offset, &val);
+                       if (ret < 0)
+                               return ret;
+               }
+
+               if (copy_to_user(buf, &val, count))
+                       return -EFAULT;
+       }
+
+       return ret;
+}
+
+ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev,
+                                 char __user *buf, size_t count,
+                                 loff_t *ppos, bool iswrite)
+{
+       size_t done = 0;
+       int ret = 0;
+       loff_t pos = *ppos;
+
+       pos &= VFIO_PCI_OFFSET_MASK;
+
+       /*
+        * We want to both keep the access size the caller users as well as
+        * support reading large chunks of config space in a single call.
+        * PCI doesn't support unaligned accesses, so we can safely break
+        * those apart.
+        */
+       while (count) {
+               if (count >= 4 && !(pos % 4))
+                       ret = vfio_config_do_rw(vdev, buf, 4, &pos, iswrite);
+               else if (count >= 2 && !(pos % 2))
+                       ret = vfio_config_do_rw(vdev, buf, 2, &pos, iswrite);
+               else
+                       ret = vfio_config_do_rw(vdev, buf, 1, &pos, iswrite);
+
+               if (ret < 0)
+                       return ret;
+
+               count -= ret;
+               done += ret;
+               buf += ret;
+               pos += ret;
+       }
+
+       *ppos += done;
+
+       return done;
+}
diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c
new file mode 100644 (file)
index 0000000..211a492
--- /dev/null
@@ -0,0 +1,740 @@
+/*
+ * VFIO PCI interrupt handling
+ *
+ * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
+ *     Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc.  All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/eventfd.h>
+#include <linux/pci.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/vfio.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include "vfio_pci_private.h"
+
+/*
+ * IRQfd - generic
+ */
+struct virqfd {
+       struct vfio_pci_device  *vdev;
+       struct eventfd_ctx      *eventfd;
+       int                     (*handler)(struct vfio_pci_device *, void *);
+       void                    (*thread)(struct vfio_pci_device *, void *);
+       void                    *data;
+       struct work_struct      inject;
+       wait_queue_t            wait;
+       poll_table              pt;
+       struct work_struct      shutdown;
+       struct virqfd           **pvirqfd;
+};
+
+static struct workqueue_struct *vfio_irqfd_cleanup_wq;
+
+int __init vfio_pci_virqfd_init(void)
+{
+       vfio_irqfd_cleanup_wq =
+               create_singlethread_workqueue("vfio-irqfd-cleanup");
+       if (!vfio_irqfd_cleanup_wq)
+               return -ENOMEM;
+
+       return 0;
+}
+
+void vfio_pci_virqfd_exit(void)
+{
+       destroy_workqueue(vfio_irqfd_cleanup_wq);
+}
+
+static void virqfd_deactivate(struct virqfd *virqfd)
+{
+       queue_work(vfio_irqfd_cleanup_wq, &virqfd->shutdown);
+}
+
+static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key)
+{
+       struct virqfd *virqfd = container_of(wait, struct virqfd, wait);
+       unsigned long flags = (unsigned long)key;
+
+       if (flags & POLLIN) {
+               /* An event has been signaled, call function */
+               if ((!virqfd->handler ||
+                    virqfd->handler(virqfd->vdev, virqfd->data)) &&
+                   virqfd->thread)
+                       schedule_work(&virqfd->inject);
+       }
+
+       if (flags & POLLHUP)
+               /* The eventfd is closing, detach from VFIO */
+               virqfd_deactivate(virqfd);
+
+       return 0;
+}
+
+static void virqfd_ptable_queue_proc(struct file *file,
+                                    wait_queue_head_t *wqh, poll_table *pt)
+{
+       struct virqfd *virqfd = container_of(pt, struct virqfd, pt);
+       add_wait_queue(wqh, &virqfd->wait);
+}
+
+static void virqfd_shutdown(struct work_struct *work)
+{
+       struct virqfd *virqfd = container_of(work, struct virqfd, shutdown);
+       struct virqfd **pvirqfd = virqfd->pvirqfd;
+       u64 cnt;
+
+       eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt);
+       flush_work(&virqfd->inject);
+       eventfd_ctx_put(virqfd->eventfd);
+
+       kfree(virqfd);
+       *pvirqfd = NULL;
+}
+
+static void virqfd_inject(struct work_struct *work)
+{
+       struct virqfd *virqfd = container_of(work, struct virqfd, inject);
+       if (virqfd->thread)
+               virqfd->thread(virqfd->vdev, virqfd->data);
+}
+
+static int virqfd_enable(struct vfio_pci_device *vdev,
+                        int (*handler)(struct vfio_pci_device *, void *),
+                        void (*thread)(struct vfio_pci_device *, void *),
+                        void *data, struct virqfd **pvirqfd, int fd)
+{
+       struct file *file = NULL;
+       struct eventfd_ctx *ctx = NULL;
+       struct virqfd *virqfd;
+       int ret = 0;
+       unsigned int events;
+
+       if (*pvirqfd)
+               return -EBUSY;
+
+       virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL);
+       if (!virqfd)
+               return -ENOMEM;
+
+       virqfd->pvirqfd = pvirqfd;
+       *pvirqfd = virqfd;
+       virqfd->vdev = vdev;
+       virqfd->handler = handler;
+       virqfd->thread = thread;
+       virqfd->data = data;
+
+       INIT_WORK(&virqfd->shutdown, virqfd_shutdown);
+       INIT_WORK(&virqfd->inject, virqfd_inject);
+
+       file = eventfd_fget(fd);
+       if (IS_ERR(file)) {
+               ret = PTR_ERR(file);
+               goto fail;
+       }
+
+       ctx = eventfd_ctx_fileget(file);
+       if (IS_ERR(ctx)) {
+               ret = PTR_ERR(ctx);
+               goto fail;
+       }
+
+       virqfd->eventfd = ctx;
+
+       /*
+        * Install our own custom wake-up handling so we are notified via
+        * a callback whenever someone signals the underlying eventfd.
+        */
+       init_waitqueue_func_entry(&virqfd->wait, virqfd_wakeup);
+       init_poll_funcptr(&virqfd->pt, virqfd_ptable_queue_proc);
+
+       events = file->f_op->poll(file, &virqfd->pt);
+
+       /*
+        * Check if there was an event already pending on the eventfd
+        * before we registered and trigger it as if we didn't miss it.
+        */
+       if (events & POLLIN) {
+               if ((!handler || handler(vdev, data)) && thread)
+                       schedule_work(&virqfd->inject);
+       }
+
+       /*
+        * Do not drop the file until the irqfd is fully initialized,
+        * otherwise we might race against the POLLHUP.
+        */
+       fput(file);
+
+       return 0;
+
+fail:
+       if (ctx && !IS_ERR(ctx))
+               eventfd_ctx_put(ctx);
+
+       if (file && !IS_ERR(file))
+               fput(file);
+
+       kfree(virqfd);
+       *pvirqfd = NULL;
+
+       return ret;
+}
+
+static void virqfd_disable(struct virqfd *virqfd)
+{
+       if (!virqfd)
+               return;
+
+       virqfd_deactivate(virqfd);
+
+       /* Block until we know all outstanding shutdown jobs have completed. */
+       flush_workqueue(vfio_irqfd_cleanup_wq);
+}
+
+/*
+ * INTx
+ */
+static void vfio_send_intx_eventfd(struct vfio_pci_device *vdev, void *unused)
+{
+       if (likely(is_intx(vdev) && !vdev->virq_disabled))
+               eventfd_signal(vdev->ctx[0].trigger, 1);
+}
+
+void vfio_pci_intx_mask(struct vfio_pci_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&vdev->irqlock, flags);
+
+       /*
+        * Masking can come from interrupt, ioctl, or config space
+        * via INTx disable.  The latter means this can get called
+        * even when not using intx delivery.  In this case, just
+        * try to have the physical bit follow the virtual bit.
+        */
+       if (unlikely(!is_intx(vdev))) {
+               if (vdev->pci_2_3)
+                       pci_intx(pdev, 0);
+       } else if (!vdev->ctx[0].masked) {
+               /*
+                * Can't use check_and_mask here because we always want to
+                * mask, not just when something is pending.
+                */
+               if (vdev->pci_2_3)
+                       pci_intx(pdev, 0);
+               else
+                       disable_irq_nosync(pdev->irq);
+
+               vdev->ctx[0].masked = true;
+       }
+
+       spin_unlock_irqrestore(&vdev->irqlock, flags);
+}
+
+/*
+ * If this is triggered by an eventfd, we can't call eventfd_signal
+ * or else we'll deadlock on the eventfd wait queue.  Return >0 when
+ * a signal is necessary, which can then be handled via a work queue
+ * or directly depending on the caller.
+ */
+int vfio_pci_intx_unmask_handler(struct vfio_pci_device *vdev, void *unused)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&vdev->irqlock, flags);
+
+       /*
+        * Unmasking comes from ioctl or config, so again, have the
+        * physical bit follow the virtual even when not using INTx.
+        */
+       if (unlikely(!is_intx(vdev))) {
+               if (vdev->pci_2_3)
+                       pci_intx(pdev, 1);
+       } else if (vdev->ctx[0].masked && !vdev->virq_disabled) {
+               /*
+                * A pending interrupt here would immediately trigger,
+                * but we can avoid that overhead by just re-sending
+                * the interrupt to the user.
+                */
+               if (vdev->pci_2_3) {
+                       if (!pci_check_and_unmask_intx(pdev))
+                               ret = 1;
+               } else
+                       enable_irq(pdev->irq);
+
+               vdev->ctx[0].masked = (ret > 0);
+       }
+
+       spin_unlock_irqrestore(&vdev->irqlock, flags);
+
+       return ret;
+}
+
+void vfio_pci_intx_unmask(struct vfio_pci_device *vdev)
+{
+       if (vfio_pci_intx_unmask_handler(vdev, NULL) > 0)
+               vfio_send_intx_eventfd(vdev, NULL);
+}
+
+static irqreturn_t vfio_intx_handler(int irq, void *dev_id)
+{
+       struct vfio_pci_device *vdev = dev_id;
+       unsigned long flags;
+       int ret = IRQ_NONE;
+
+       spin_lock_irqsave(&vdev->irqlock, flags);
+
+       if (!vdev->pci_2_3) {
+               disable_irq_nosync(vdev->pdev->irq);
+               vdev->ctx[0].masked = true;
+               ret = IRQ_HANDLED;
+       } else if (!vdev->ctx[0].masked &&  /* may be shared */
+                  pci_check_and_mask_intx(vdev->pdev)) {
+               vdev->ctx[0].masked = true;
+               ret = IRQ_HANDLED;
+       }
+
+       spin_unlock_irqrestore(&vdev->irqlock, flags);
+
+       if (ret == IRQ_HANDLED)
+               vfio_send_intx_eventfd(vdev, NULL);
+
+       return ret;
+}
+
+static int vfio_intx_enable(struct vfio_pci_device *vdev)
+{
+       if (!is_irq_none(vdev))
+               return -EINVAL;
+
+       if (!vdev->pdev->irq)
+               return -ENODEV;
+
+       vdev->ctx = kzalloc(sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL);
+       if (!vdev->ctx)
+               return -ENOMEM;
+
+       vdev->num_ctx = 1;
+       vdev->irq_type = VFIO_PCI_INTX_IRQ_INDEX;
+
+       return 0;
+}
+
+static int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       unsigned long irqflags = IRQF_SHARED;
+       struct eventfd_ctx *trigger;
+       unsigned long flags;
+       int ret;
+
+       if (vdev->ctx[0].trigger) {
+               free_irq(pdev->irq, vdev);
+               kfree(vdev->ctx[0].name);
+               eventfd_ctx_put(vdev->ctx[0].trigger);
+               vdev->ctx[0].trigger = NULL;
+       }
+
+       if (fd < 0) /* Disable only */
+               return 0;
+
+       vdev->ctx[0].name = kasprintf(GFP_KERNEL, "vfio-intx(%s)",
+                                     pci_name(pdev));
+       if (!vdev->ctx[0].name)
+               return -ENOMEM;
+
+       trigger = eventfd_ctx_fdget(fd);
+       if (IS_ERR(trigger)) {
+               kfree(vdev->ctx[0].name);
+               return PTR_ERR(trigger);
+       }
+
+       if (!vdev->pci_2_3)
+               irqflags = 0;
+
+       ret = request_irq(pdev->irq, vfio_intx_handler,
+                         irqflags, vdev->ctx[0].name, vdev);
+       if (ret) {
+               kfree(vdev->ctx[0].name);
+               eventfd_ctx_put(trigger);
+               return ret;
+       }
+
+       vdev->ctx[0].trigger = trigger;
+
+       /*
+        * INTx disable will stick across the new irq setup,
+        * disable_irq won't.
+        */
+       spin_lock_irqsave(&vdev->irqlock, flags);
+       if (!vdev->pci_2_3 && (vdev->ctx[0].masked || vdev->virq_disabled))
+               disable_irq_nosync(pdev->irq);
+       spin_unlock_irqrestore(&vdev->irqlock, flags);
+
+       return 0;
+}
+
+static void vfio_intx_disable(struct vfio_pci_device *vdev)
+{
+       vfio_intx_set_signal(vdev, -1);
+       virqfd_disable(vdev->ctx[0].unmask);
+       virqfd_disable(vdev->ctx[0].mask);
+       vdev->irq_type = VFIO_PCI_NUM_IRQS;
+       vdev->num_ctx = 0;
+       kfree(vdev->ctx);
+}
+
+/*
+ * MSI/MSI-X
+ */
+static irqreturn_t vfio_msihandler(int irq, void *arg)
+{
+       struct eventfd_ctx *trigger = arg;
+
+       eventfd_signal(trigger, 1);
+       return IRQ_HANDLED;
+}
+
+static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       int ret;
+
+       if (!is_irq_none(vdev))
+               return -EINVAL;
+
+       vdev->ctx = kzalloc(nvec * sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL);
+       if (!vdev->ctx)
+               return -ENOMEM;
+
+       if (msix) {
+               int i;
+
+               vdev->msix = kzalloc(nvec * sizeof(struct msix_entry),
+                                    GFP_KERNEL);
+               if (!vdev->msix) {
+                       kfree(vdev->ctx);
+                       return -ENOMEM;
+               }
+
+               for (i = 0; i < nvec; i++)
+                       vdev->msix[i].entry = i;
+
+               ret = pci_enable_msix(pdev, vdev->msix, nvec);
+               if (ret) {
+                       kfree(vdev->msix);
+                       kfree(vdev->ctx);
+                       return ret;
+               }
+       } else {
+               ret = pci_enable_msi_block(pdev, nvec);
+               if (ret) {
+                       kfree(vdev->ctx);
+                       return ret;
+               }
+       }
+
+       vdev->num_ctx = nvec;
+       vdev->irq_type = msix ? VFIO_PCI_MSIX_IRQ_INDEX :
+                               VFIO_PCI_MSI_IRQ_INDEX;
+
+       if (!msix) {
+               /*
+                * Compute the virtual hardware field for max msi vectors -
+                * it is the log base 2 of the number of vectors.
+                */
+               vdev->msi_qmax = fls(nvec * 2 - 1) - 1;
+       }
+
+       return 0;
+}
+
+static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev,
+                                     int vector, int fd, bool msix)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       int irq = msix ? vdev->msix[vector].vector : pdev->irq + vector;
+       char *name = msix ? "vfio-msix" : "vfio-msi";
+       struct eventfd_ctx *trigger;
+       int ret;
+
+       if (vector >= vdev->num_ctx)
+               return -EINVAL;
+
+       if (vdev->ctx[vector].trigger) {
+               free_irq(irq, vdev->ctx[vector].trigger);
+               kfree(vdev->ctx[vector].name);
+               eventfd_ctx_put(vdev->ctx[vector].trigger);
+               vdev->ctx[vector].trigger = NULL;
+       }
+
+       if (fd < 0)
+               return 0;
+
+       vdev->ctx[vector].name = kasprintf(GFP_KERNEL, "%s[%d](%s)",
+                                          name, vector, pci_name(pdev));
+       if (!vdev->ctx[vector].name)
+               return -ENOMEM;
+
+       trigger = eventfd_ctx_fdget(fd);
+       if (IS_ERR(trigger)) {
+               kfree(vdev->ctx[vector].name);
+               return PTR_ERR(trigger);
+       }
+
+       ret = request_irq(irq, vfio_msihandler, 0,
+                         vdev->ctx[vector].name, trigger);
+       if (ret) {
+               kfree(vdev->ctx[vector].name);
+               eventfd_ctx_put(trigger);
+               return ret;
+       }
+
+       vdev->ctx[vector].trigger = trigger;
+
+       return 0;
+}
+
+static int vfio_msi_set_block(struct vfio_pci_device *vdev, unsigned start,
+                             unsigned count, int32_t *fds, bool msix)
+{
+       int i, j, ret = 0;
+
+       if (start + count > vdev->num_ctx)
+               return -EINVAL;
+
+       for (i = 0, j = start; i < count && !ret; i++, j++) {
+               int fd = fds ? fds[i] : -1;
+               ret = vfio_msi_set_vector_signal(vdev, j, fd, msix);
+       }
+
+       if (ret) {
+               for (--j; j >= start; j--)
+                       vfio_msi_set_vector_signal(vdev, j, -1, msix);
+       }
+
+       return ret;
+}
+
+static void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       int i;
+
+       vfio_msi_set_block(vdev, 0, vdev->num_ctx, NULL, msix);
+
+       for (i = 0; i < vdev->num_ctx; i++) {
+               virqfd_disable(vdev->ctx[i].unmask);
+               virqfd_disable(vdev->ctx[i].mask);
+       }
+
+       if (msix) {
+               pci_disable_msix(vdev->pdev);
+               kfree(vdev->msix);
+       } else
+               pci_disable_msi(pdev);
+
+       vdev->irq_type = VFIO_PCI_NUM_IRQS;
+       vdev->num_ctx = 0;
+       kfree(vdev->ctx);
+}
+
+/*
+ * IOCTL support
+ */
+static int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev,
+                                   unsigned index, unsigned start,
+                                   unsigned count, uint32_t flags, void *data)
+{
+       if (!is_intx(vdev) || start != 0 || count != 1)
+               return -EINVAL;
+
+       if (flags & VFIO_IRQ_SET_DATA_NONE) {
+               vfio_pci_intx_unmask(vdev);
+       } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
+               uint8_t unmask = *(uint8_t *)data;
+               if (unmask)
+                       vfio_pci_intx_unmask(vdev);
+       } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
+               int32_t fd = *(int32_t *)data;
+               if (fd >= 0)
+                       return virqfd_enable(vdev, vfio_pci_intx_unmask_handler,
+                                            vfio_send_intx_eventfd, NULL,
+                                            &vdev->ctx[0].unmask, fd);
+
+               virqfd_disable(vdev->ctx[0].unmask);
+       }
+
+       return 0;
+}
+
+static int vfio_pci_set_intx_mask(struct vfio_pci_device *vdev,
+                                 unsigned index, unsigned start,
+                                 unsigned count, uint32_t flags, void *data)
+{
+       if (!is_intx(vdev) || start != 0 || count != 1)
+               return -EINVAL;
+
+       if (flags & VFIO_IRQ_SET_DATA_NONE) {
+               vfio_pci_intx_mask(vdev);
+       } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
+               uint8_t mask = *(uint8_t *)data;
+               if (mask)
+                       vfio_pci_intx_mask(vdev);
+       } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
+               return -ENOTTY; /* XXX implement me */
+       }
+
+       return 0;
+}
+
+static int vfio_pci_set_intx_trigger(struct vfio_pci_device *vdev,
+                                    unsigned index, unsigned start,
+                                    unsigned count, uint32_t flags, void *data)
+{
+       if (is_intx(vdev) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
+               vfio_intx_disable(vdev);
+               return 0;
+       }
+
+       if (!(is_intx(vdev) || is_irq_none(vdev)) || start != 0 || count != 1)
+               return -EINVAL;
+
+       if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
+               int32_t fd = *(int32_t *)data;
+               int ret;
+
+               if (is_intx(vdev))
+                       return vfio_intx_set_signal(vdev, fd);
+
+               ret = vfio_intx_enable(vdev);
+               if (ret)
+                       return ret;
+
+               ret = vfio_intx_set_signal(vdev, fd);
+               if (ret)
+                       vfio_intx_disable(vdev);
+
+               return ret;
+       }
+
+       if (!is_intx(vdev))
+               return -EINVAL;
+
+       if (flags & VFIO_IRQ_SET_DATA_NONE) {
+               vfio_send_intx_eventfd(vdev, NULL);
+       } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
+               uint8_t trigger = *(uint8_t *)data;
+               if (trigger)
+                       vfio_send_intx_eventfd(vdev, NULL);
+       }
+       return 0;
+}
+
+static int vfio_pci_set_msi_trigger(struct vfio_pci_device *vdev,
+                                   unsigned index, unsigned start,
+                                   unsigned count, uint32_t flags, void *data)
+{
+       int i;
+       bool msix = (index == VFIO_PCI_MSIX_IRQ_INDEX) ? true : false;
+
+       if (irq_is(vdev, index) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
+               vfio_msi_disable(vdev, msix);
+               return 0;
+       }
+
+       if (!(irq_is(vdev, index) || is_irq_none(vdev)))
+               return -EINVAL;
+
+       if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
+               int32_t *fds = data;
+               int ret;
+
+               if (vdev->irq_type == index)
+                       return vfio_msi_set_block(vdev, start, count,
+                                                 fds, msix);
+
+               ret = vfio_msi_enable(vdev, start + count, msix);
+               if (ret)
+                       return ret;
+
+               ret = vfio_msi_set_block(vdev, start, count, fds, msix);
+               if (ret)
+                       vfio_msi_disable(vdev, msix);
+
+               return ret;
+       }
+
+       if (!irq_is(vdev, index) || start + count > vdev->num_ctx)
+               return -EINVAL;
+
+       for (i = start; i < start + count; i++) {
+               if (!vdev->ctx[i].trigger)
+                       continue;
+               if (flags & VFIO_IRQ_SET_DATA_NONE) {
+                       eventfd_signal(vdev->ctx[i].trigger, 1);
+               } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
+                       uint8_t *bools = data;
+                       if (bools[i - start])
+                               eventfd_signal(vdev->ctx[i].trigger, 1);
+               }
+       }
+       return 0;
+}
+
+int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags,
+                           unsigned index, unsigned start, unsigned count,
+                           void *data)
+{
+       int (*func)(struct vfio_pci_device *vdev, unsigned index,
+                   unsigned start, unsigned count, uint32_t flags,
+                   void *data) = NULL;
+
+       switch (index) {
+       case VFIO_PCI_INTX_IRQ_INDEX:
+               switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
+               case VFIO_IRQ_SET_ACTION_MASK:
+                       func = vfio_pci_set_intx_mask;
+                       break;
+               case VFIO_IRQ_SET_ACTION_UNMASK:
+                       func = vfio_pci_set_intx_unmask;
+                       break;
+               case VFIO_IRQ_SET_ACTION_TRIGGER:
+                       func = vfio_pci_set_intx_trigger;
+                       break;
+               }
+               break;
+       case VFIO_PCI_MSI_IRQ_INDEX:
+       case VFIO_PCI_MSIX_IRQ_INDEX:
+               switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
+               case VFIO_IRQ_SET_ACTION_MASK:
+               case VFIO_IRQ_SET_ACTION_UNMASK:
+                       /* XXX Need masking support exported */
+                       break;
+               case VFIO_IRQ_SET_ACTION_TRIGGER:
+                       func = vfio_pci_set_msi_trigger;
+                       break;
+               }
+               break;
+       }
+
+       if (!func)
+               return -ENOTTY;
+
+       return func(vdev, index, start, count, flags, data);
+}
diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h
new file mode 100644 (file)
index 0000000..611827c
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
+ *     Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc.  All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/mutex.h>
+#include <linux/pci.h>
+
+#ifndef VFIO_PCI_PRIVATE_H
+#define VFIO_PCI_PRIVATE_H
+
+#define VFIO_PCI_OFFSET_SHIFT   40
+
+#define VFIO_PCI_OFFSET_TO_INDEX(off)  (off >> VFIO_PCI_OFFSET_SHIFT)
+#define VFIO_PCI_INDEX_TO_OFFSET(index)        ((u64)(index) << VFIO_PCI_OFFSET_SHIFT)
+#define VFIO_PCI_OFFSET_MASK   (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1)
+
+struct vfio_pci_irq_ctx {
+       struct eventfd_ctx      *trigger;
+       struct virqfd           *unmask;
+       struct virqfd           *mask;
+       char                    *name;
+       bool                    masked;
+};
+
+struct vfio_pci_device {
+       struct pci_dev          *pdev;
+       void __iomem            *barmap[PCI_STD_RESOURCE_END + 1];
+       u8                      *pci_config_map;
+       u8                      *vconfig;
+       struct perm_bits        *msi_perm;
+       spinlock_t              irqlock;
+       struct mutex            igate;
+       struct msix_entry       *msix;
+       struct vfio_pci_irq_ctx *ctx;
+       int                     num_ctx;
+       int                     irq_type;
+       u8                      msi_qmax;
+       u8                      msix_bar;
+       u16                     msix_size;
+       u32                     msix_offset;
+       u32                     rbar[7];
+       bool                    pci_2_3;
+       bool                    virq_disabled;
+       bool                    reset_works;
+       bool                    extended_caps;
+       bool                    bardirty;
+       struct pci_saved_state  *pci_saved_state;
+       atomic_t                refcnt;
+};
+
+#define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX)
+#define is_msi(vdev) (vdev->irq_type == VFIO_PCI_MSI_IRQ_INDEX)
+#define is_msix(vdev) (vdev->irq_type == VFIO_PCI_MSIX_IRQ_INDEX)
+#define is_irq_none(vdev) (!(is_intx(vdev) || is_msi(vdev) || is_msix(vdev)))
+#define irq_is(vdev, type) (vdev->irq_type == type)
+
+extern void vfio_pci_intx_mask(struct vfio_pci_device *vdev);
+extern void vfio_pci_intx_unmask(struct vfio_pci_device *vdev);
+
+extern int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev,
+                                  uint32_t flags, unsigned index,
+                                  unsigned start, unsigned count, void *data);
+
+extern ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev,
+                                        char __user *buf, size_t count,
+                                        loff_t *ppos, bool iswrite);
+extern ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev,
+                                     char __user *buf, size_t count,
+                                     loff_t *ppos, bool iswrite);
+extern ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev,
+                                    char __user *buf, size_t count,
+                                    loff_t *ppos, bool iswrite);
+
+extern int vfio_pci_init_perm_bits(void);
+extern void vfio_pci_uninit_perm_bits(void);
+
+extern int vfio_pci_virqfd_init(void);
+extern void vfio_pci_virqfd_exit(void);
+
+extern int vfio_config_init(struct vfio_pci_device *vdev);
+extern void vfio_config_free(struct vfio_pci_device *vdev);
+#endif /* VFIO_PCI_PRIVATE_H */
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
new file mode 100644 (file)
index 0000000..4362d9e
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * VFIO PCI I/O Port & MMIO access
+ *
+ * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
+ *     Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc.  All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include "vfio_pci_private.h"
+
+/* I/O Port BAR access */
+ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, char __user *buf,
+                             size_t count, loff_t *ppos, bool iswrite)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
+       int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+       void __iomem *io;
+       size_t done = 0;
+
+       if (!pci_resource_start(pdev, bar))
+               return -EINVAL;
+
+       if (pos + count > pci_resource_len(pdev, bar))
+               return -EINVAL;
+
+       if (!vdev->barmap[bar]) {
+               int ret;
+
+               ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
+               if (ret)
+                       return ret;
+
+               vdev->barmap[bar] = pci_iomap(pdev, bar, 0);
+
+               if (!vdev->barmap[bar]) {
+                       pci_release_selected_regions(pdev, 1 << bar);
+                       return -EINVAL;
+               }
+       }
+
+       io = vdev->barmap[bar];
+
+       while (count) {
+               int filled;
+
+               if (count >= 3 && !(pos % 4)) {
+                       __le32 val;
+
+                       if (iswrite) {
+                               if (copy_from_user(&val, buf, 4))
+                                       return -EFAULT;
+
+                               iowrite32(le32_to_cpu(val), io + pos);
+                       } else {
+                               val = cpu_to_le32(ioread32(io + pos));
+
+                               if (copy_to_user(buf, &val, 4))
+                                       return -EFAULT;
+                       }
+
+                       filled = 4;
+
+               } else if ((pos % 2) == 0 && count >= 2) {
+                       __le16 val;
+
+                       if (iswrite) {
+                               if (copy_from_user(&val, buf, 2))
+                                       return -EFAULT;
+
+                               iowrite16(le16_to_cpu(val), io + pos);
+                       } else {
+                               val = cpu_to_le16(ioread16(io + pos));
+
+                               if (copy_to_user(buf, &val, 2))
+                                       return -EFAULT;
+                       }
+
+                       filled = 2;
+               } else {
+                       u8 val;
+
+                       if (iswrite) {
+                               if (copy_from_user(&val, buf, 1))
+                                       return -EFAULT;
+
+                               iowrite8(val, io + pos);
+                       } else {
+                               val = ioread8(io + pos);
+
+                               if (copy_to_user(buf, &val, 1))
+                                       return -EFAULT;
+                       }
+
+                       filled = 1;
+               }
+
+               count -= filled;
+               done += filled;
+               buf += filled;
+               pos += filled;
+       }
+
+       *ppos += done;
+
+       return done;
+}
+
+/*
+ * MMIO BAR access
+ * We handle two excluded ranges here as well, if the user tries to read
+ * the ROM beyond what PCI tells us is available or the MSI-X table region,
+ * we return 0xFF and writes are dropped.
+ */
+ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf,
+                              size_t count, loff_t *ppos, bool iswrite)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
+       int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+       void __iomem *io;
+       resource_size_t end;
+       size_t done = 0;
+       size_t x_start = 0, x_end = 0; /* excluded range */
+
+       if (!pci_resource_start(pdev, bar))
+               return -EINVAL;
+
+       end = pci_resource_len(pdev, bar);
+
+       if (pos > end)
+               return -EINVAL;
+
+       if (pos == end)
+               return 0;
+
+       if (pos + count > end)
+               count = end - pos;
+
+       if (bar == PCI_ROM_RESOURCE) {
+               io = pci_map_rom(pdev, &x_start);
+               x_end = end;
+       } else {
+               if (!vdev->barmap[bar]) {
+                       int ret;
+
+                       ret = pci_request_selected_regions(pdev, 1 << bar,
+                                                          "vfio");
+                       if (ret)
+                               return ret;
+
+                       vdev->barmap[bar] = pci_iomap(pdev, bar, 0);
+
+                       if (!vdev->barmap[bar]) {
+                               pci_release_selected_regions(pdev, 1 << bar);
+                               return -EINVAL;
+                       }
+               }
+
+               io = vdev->barmap[bar];
+
+               if (bar == vdev->msix_bar) {
+                       x_start = vdev->msix_offset;
+                       x_end = vdev->msix_offset + vdev->msix_size;
+               }
+       }
+
+       if (!io)
+               return -EINVAL;
+
+       while (count) {
+               size_t fillable, filled;
+
+               if (pos < x_start)
+                       fillable = x_start - pos;
+               else if (pos >= x_end)
+                       fillable = end - pos;
+               else
+                       fillable = 0;
+
+               if (fillable >= 4 && !(pos % 4) && (count >= 4)) {
+                       __le32 val;
+
+                       if (iswrite) {
+                               if (copy_from_user(&val, buf, 4))
+                                       goto out;
+
+                               iowrite32(le32_to_cpu(val), io + pos);
+                       } else {
+                               val = cpu_to_le32(ioread32(io + pos));
+
+                               if (copy_to_user(buf, &val, 4))
+                                       goto out;
+                       }
+
+                       filled = 4;
+               } else if (fillable >= 2 && !(pos % 2) && (count >= 2)) {
+                       __le16 val;
+
+                       if (iswrite) {
+                               if (copy_from_user(&val, buf, 2))
+                                       goto out;
+
+                               iowrite16(le16_to_cpu(val), io + pos);
+                       } else {
+                               val = cpu_to_le16(ioread16(io + pos));
+
+                               if (copy_to_user(buf, &val, 2))
+                                       goto out;
+                       }
+
+                       filled = 2;
+               } else if (fillable) {
+                       u8 val;
+
+                       if (iswrite) {
+                               if (copy_from_user(&val, buf, 1))
+                                       goto out;
+
+                               iowrite8(val, io + pos);
+                       } else {
+                               val = ioread8(io + pos);
+
+                               if (copy_to_user(buf, &val, 1))
+                                       goto out;
+                       }
+
+                       filled = 1;
+               } else {
+                       /* Drop writes, fill reads with FF */
+                       if (!iswrite) {
+                               char val = 0xFF;
+                               size_t i;
+
+                               for (i = 0; i < x_end - pos; i++) {
+                                       if (put_user(val, buf + i))
+                                               goto out;
+                               }
+                       }
+
+                       filled = x_end - pos;
+               }
+
+               count -= filled;
+               done += filled;
+               buf += filled;
+               pos += filled;
+       }
+
+       *ppos += done;
+
+out:
+       if (bar == PCI_ROM_RESOURCE)
+               pci_unmap_rom(pdev, io);
+
+       return count ? -EFAULT : done;
+}
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
new file mode 100644 (file)
index 0000000..9591e2b
--- /dev/null
@@ -0,0 +1,1420 @@
+/*
+ * VFIO core
+ *
+ * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
+ *     Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc.  All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/cdev.h>
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/anon_inodes.h>
+#include <linux/fs.h>
+#include <linux/idr.h>
+#include <linux/iommu.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/vfio.h>
+#include <linux/wait.h>
+
+#define DRIVER_VERSION "0.3"
+#define DRIVER_AUTHOR  "Alex Williamson <alex.williamson@redhat.com>"
+#define DRIVER_DESC    "VFIO - User Level meta-driver"
+
+static struct vfio {
+       struct class                    *class;
+       struct list_head                iommu_drivers_list;
+       struct mutex                    iommu_drivers_lock;
+       struct list_head                group_list;
+       struct idr                      group_idr;
+       struct mutex                    group_lock;
+       struct cdev                     group_cdev;
+       struct device                   *dev;
+       dev_t                           devt;
+       struct cdev                     cdev;
+       wait_queue_head_t               release_q;
+} vfio;
+
+struct vfio_iommu_driver {
+       const struct vfio_iommu_driver_ops      *ops;
+       struct list_head                        vfio_next;
+};
+
+struct vfio_container {
+       struct kref                     kref;
+       struct list_head                group_list;
+       struct mutex                    group_lock;
+       struct vfio_iommu_driver        *iommu_driver;
+       void                            *iommu_data;
+};
+
+struct vfio_group {
+       struct kref                     kref;
+       int                             minor;
+       atomic_t                        container_users;
+       struct iommu_group              *iommu_group;
+       struct vfio_container           *container;
+       struct list_head                device_list;
+       struct mutex                    device_lock;
+       struct device                   *dev;
+       struct notifier_block           nb;
+       struct list_head                vfio_next;
+       struct list_head                container_next;
+};
+
+struct vfio_device {
+       struct kref                     kref;
+       struct device                   *dev;
+       const struct vfio_device_ops    *ops;
+       struct vfio_group               *group;
+       struct list_head                group_next;
+       void                            *device_data;
+};
+
+/**
+ * IOMMU driver registration
+ */
+int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops)
+{
+       struct vfio_iommu_driver *driver, *tmp;
+
+       driver = kzalloc(sizeof(*driver), GFP_KERNEL);
+       if (!driver)
+               return -ENOMEM;
+
+       driver->ops = ops;
+
+       mutex_lock(&vfio.iommu_drivers_lock);
+
+       /* Check for duplicates */
+       list_for_each_entry(tmp, &vfio.iommu_drivers_list, vfio_next) {
+               if (tmp->ops == ops) {
+                       mutex_unlock(&vfio.iommu_drivers_lock);
+                       kfree(driver);
+                       return -EINVAL;
+               }
+       }
+
+       list_add(&driver->vfio_next, &vfio.iommu_drivers_list);
+
+       mutex_unlock(&vfio.iommu_drivers_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vfio_register_iommu_driver);
+
+void vfio_unregister_iommu_driver(const struct vfio_iommu_driver_ops *ops)
+{
+       struct vfio_iommu_driver *driver;
+
+       mutex_lock(&vfio.iommu_drivers_lock);
+       list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) {
+               if (driver->ops == ops) {
+                       list_del(&driver->vfio_next);
+                       mutex_unlock(&vfio.iommu_drivers_lock);
+                       kfree(driver);
+                       return;
+               }
+       }
+       mutex_unlock(&vfio.iommu_drivers_lock);
+}
+EXPORT_SYMBOL_GPL(vfio_unregister_iommu_driver);
+
+/**
+ * Group minor allocation/free - both called with vfio.group_lock held
+ */
+static int vfio_alloc_group_minor(struct vfio_group *group)
+{
+       int ret, minor;
+
+again:
+       if (unlikely(idr_pre_get(&vfio.group_idr, GFP_KERNEL) == 0))
+               return -ENOMEM;
+
+       /* index 0 is used by /dev/vfio/vfio */
+       ret = idr_get_new_above(&vfio.group_idr, group, 1, &minor);
+       if (ret == -EAGAIN)
+               goto again;
+       if (ret || minor > MINORMASK) {
+               if (minor > MINORMASK)
+                       idr_remove(&vfio.group_idr, minor);
+               return -ENOSPC;
+       }
+
+       return minor;
+}
+
+static void vfio_free_group_minor(int minor)
+{
+       idr_remove(&vfio.group_idr, minor);
+}
+
+static int vfio_iommu_group_notifier(struct notifier_block *nb,
+                                    unsigned long action, void *data);
+static void vfio_group_get(struct vfio_group *group);
+
+/**
+ * Container objects - containers are created when /dev/vfio/vfio is
+ * opened, but their lifecycle extends until the last user is done, so
+ * it's freed via kref.  Must support container/group/device being
+ * closed in any order.
+ */
+static void vfio_container_get(struct vfio_container *container)
+{
+       kref_get(&container->kref);
+}
+
+static void vfio_container_release(struct kref *kref)
+{
+       struct vfio_container *container;
+       container = container_of(kref, struct vfio_container, kref);
+
+       kfree(container);
+}
+
+static void vfio_container_put(struct vfio_container *container)
+{
+       kref_put(&container->kref, vfio_container_release);
+}
+
+/**
+ * Group objects - create, release, get, put, search
+ */
+static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group)
+{
+       struct vfio_group *group, *tmp;
+       struct device *dev;
+       int ret, minor;
+
+       group = kzalloc(sizeof(*group), GFP_KERNEL);
+       if (!group)
+               return ERR_PTR(-ENOMEM);
+
+       kref_init(&group->kref);
+       INIT_LIST_HEAD(&group->device_list);
+       mutex_init(&group->device_lock);
+       atomic_set(&group->container_users, 0);
+       group->iommu_group = iommu_group;
+
+       group->nb.notifier_call = vfio_iommu_group_notifier;
+
+       /*
+        * blocking notifiers acquire a rwsem around registering and hold
+        * it around callback.  Therefore, need to register outside of
+        * vfio.group_lock to avoid A-B/B-A contention.  Our callback won't
+        * do anything unless it can find the group in vfio.group_list, so
+        * no harm in registering early.
+        */
+       ret = iommu_group_register_notifier(iommu_group, &group->nb);
+       if (ret) {
+               kfree(group);
+               return ERR_PTR(ret);
+       }
+
+       mutex_lock(&vfio.group_lock);
+
+       minor = vfio_alloc_group_minor(group);
+       if (minor < 0) {
+               mutex_unlock(&vfio.group_lock);
+               kfree(group);
+               return ERR_PTR(minor);
+       }
+
+       /* Did we race creating this group? */
+       list_for_each_entry(tmp, &vfio.group_list, vfio_next) {
+               if (tmp->iommu_group == iommu_group) {
+                       vfio_group_get(tmp);
+                       vfio_free_group_minor(minor);
+                       mutex_unlock(&vfio.group_lock);
+                       kfree(group);
+                       return tmp;
+               }
+       }
+
+       dev = device_create(vfio.class, NULL, MKDEV(MAJOR(vfio.devt), minor),
+                           group, "%d", iommu_group_id(iommu_group));
+       if (IS_ERR(dev)) {
+               vfio_free_group_minor(minor);
+               mutex_unlock(&vfio.group_lock);
+               kfree(group);
+               return (struct vfio_group *)dev; /* ERR_PTR */
+       }
+
+       group->minor = minor;
+       group->dev = dev;
+
+       list_add(&group->vfio_next, &vfio.group_list);
+
+       mutex_unlock(&vfio.group_lock);
+
+       return group;
+}
+
+static void vfio_group_release(struct kref *kref)
+{
+       struct vfio_group *group = container_of(kref, struct vfio_group, kref);
+
+       WARN_ON(!list_empty(&group->device_list));
+
+       device_destroy(vfio.class, MKDEV(MAJOR(vfio.devt), group->minor));
+       list_del(&group->vfio_next);
+       vfio_free_group_minor(group->minor);
+
+       mutex_unlock(&vfio.group_lock);
+
+       /*
+        * Unregister outside of lock.  A spurious callback is harmless now
+        * that the group is no longer in vfio.group_list.
+        */
+       iommu_group_unregister_notifier(group->iommu_group, &group->nb);
+
+       kfree(group);
+}
+
+static void vfio_group_put(struct vfio_group *group)
+{
+       mutex_lock(&vfio.group_lock);
+       /*
+        * Release needs to unlock to unregister the notifier, so only
+        * unlock if not released.
+        */
+       if (!kref_put(&group->kref, vfio_group_release))
+               mutex_unlock(&vfio.group_lock);
+}
+
+/* Assume group_lock or group reference is held */
+static void vfio_group_get(struct vfio_group *group)
+{
+       kref_get(&group->kref);
+}
+
+/*
+ * Not really a try as we will sleep for mutex, but we need to make
+ * sure the group pointer is valid under lock and get a reference.
+ */
+static struct vfio_group *vfio_group_try_get(struct vfio_group *group)
+{
+       struct vfio_group *target = group;
+
+       mutex_lock(&vfio.group_lock);
+       list_for_each_entry(group, &vfio.group_list, vfio_next) {
+               if (group == target) {
+                       vfio_group_get(group);
+                       mutex_unlock(&vfio.group_lock);
+                       return group;
+               }
+       }
+       mutex_unlock(&vfio.group_lock);
+
+       return NULL;
+}
+
+static
+struct vfio_group *vfio_group_get_from_iommu(struct iommu_group *iommu_group)
+{
+       struct vfio_group *group;
+
+       mutex_lock(&vfio.group_lock);
+       list_for_each_entry(group, &vfio.group_list, vfio_next) {
+               if (group->iommu_group == iommu_group) {
+                       vfio_group_get(group);
+                       mutex_unlock(&vfio.group_lock);
+                       return group;
+               }
+       }
+       mutex_unlock(&vfio.group_lock);
+
+       return NULL;
+}
+
+static struct vfio_group *vfio_group_get_from_minor(int minor)
+{
+       struct vfio_group *group;
+
+       mutex_lock(&vfio.group_lock);
+       group = idr_find(&vfio.group_idr, minor);
+       if (!group) {
+               mutex_unlock(&vfio.group_lock);
+               return NULL;
+       }
+       vfio_group_get(group);
+       mutex_unlock(&vfio.group_lock);
+
+       return group;
+}
+
+/**
+ * Device objects - create, release, get, put, search
+ */
+static
+struct vfio_device *vfio_group_create_device(struct vfio_group *group,
+                                            struct device *dev,
+                                            const struct vfio_device_ops *ops,
+                                            void *device_data)
+{
+       struct vfio_device *device;
+       int ret;
+
+       device = kzalloc(sizeof(*device), GFP_KERNEL);
+       if (!device)
+               return ERR_PTR(-ENOMEM);
+
+       kref_init(&device->kref);
+       device->dev = dev;
+       device->group = group;
+       device->ops = ops;
+       device->device_data = device_data;
+
+       ret = dev_set_drvdata(dev, device);
+       if (ret) {
+               kfree(device);
+               return ERR_PTR(ret);
+       }
+
+       /* No need to get group_lock, caller has group reference */
+       vfio_group_get(group);
+
+       mutex_lock(&group->device_lock);
+       list_add(&device->group_next, &group->device_list);
+       mutex_unlock(&group->device_lock);
+
+       return device;
+}
+
+static void vfio_device_release(struct kref *kref)
+{
+       struct vfio_device *device = container_of(kref,
+                                                 struct vfio_device, kref);
+       struct vfio_group *group = device->group;
+
+       mutex_lock(&group->device_lock);
+       list_del(&device->group_next);
+       mutex_unlock(&group->device_lock);
+
+       dev_set_drvdata(device->dev, NULL);
+
+       kfree(device);
+
+       /* vfio_del_group_dev may be waiting for this device */
+       wake_up(&vfio.release_q);
+}
+
+/* Device reference always implies a group reference */
+static void vfio_device_put(struct vfio_device *device)
+{
+       kref_put(&device->kref, vfio_device_release);
+       vfio_group_put(device->group);
+}
+
+static void vfio_device_get(struct vfio_device *device)
+{
+       vfio_group_get(device->group);
+       kref_get(&device->kref);
+}
+
+static struct vfio_device *vfio_group_get_device(struct vfio_group *group,
+                                                struct device *dev)
+{
+       struct vfio_device *device;
+
+       mutex_lock(&group->device_lock);
+       list_for_each_entry(device, &group->device_list, group_next) {
+               if (device->dev == dev) {
+                       vfio_device_get(device);
+                       mutex_unlock(&group->device_lock);
+                       return device;
+               }
+       }
+       mutex_unlock(&group->device_lock);
+       return NULL;
+}
+
+/*
+ * Whitelist some drivers that we know are safe (no dma) or just sit on
+ * a device.  It's not always practical to leave a device within a group
+ * driverless as it could get re-bound to something unsafe.
+ */
+static const char * const vfio_driver_whitelist[] = { "pci-stub" };
+
+static bool vfio_whitelisted_driver(struct device_driver *drv)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(vfio_driver_whitelist); i++) {
+               if (!strcmp(drv->name, vfio_driver_whitelist[i]))
+                       return true;
+       }
+
+       return false;
+}
+
+/*
+ * A vfio group is viable for use by userspace if all devices are either
+ * driver-less or bound to a vfio or whitelisted driver.  We test the
+ * latter by the existence of a struct vfio_device matching the dev.
+ */
+static int vfio_dev_viable(struct device *dev, void *data)
+{
+       struct vfio_group *group = data;
+       struct vfio_device *device;
+
+       if (!dev->driver || vfio_whitelisted_driver(dev->driver))
+               return 0;
+
+       device = vfio_group_get_device(group, dev);
+       if (device) {
+               vfio_device_put(device);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+/**
+ * Async device support
+ */
+static int vfio_group_nb_add_dev(struct vfio_group *group, struct device *dev)
+{
+       struct vfio_device *device;
+
+       /* Do we already know about it?  We shouldn't */
+       device = vfio_group_get_device(group, dev);
+       if (WARN_ON_ONCE(device)) {
+               vfio_device_put(device);
+               return 0;
+       }
+
+       /* Nothing to do for idle groups */
+       if (!atomic_read(&group->container_users))
+               return 0;
+
+       /* TODO Prevent device auto probing */
+       WARN("Device %s added to live group %d!\n", dev_name(dev),
+            iommu_group_id(group->iommu_group));
+
+       return 0;
+}
+
+static int vfio_group_nb_del_dev(struct vfio_group *group, struct device *dev)
+{
+       struct vfio_device *device;
+
+       /*
+        * Expect to fall out here.  If a device was in use, it would
+        * have been bound to a vfio sub-driver, which would have blocked
+        * in .remove at vfio_del_group_dev.  Sanity check that we no
+        * longer track the device, so it's safe to remove.
+        */
+       device = vfio_group_get_device(group, dev);
+       if (likely(!device))
+               return 0;
+
+       WARN("Device %s removed from live group %d!\n", dev_name(dev),
+            iommu_group_id(group->iommu_group));
+
+       vfio_device_put(device);
+       return 0;
+}
+
+static int vfio_group_nb_verify(struct vfio_group *group, struct device *dev)
+{
+       /* We don't care what happens when the group isn't in use */
+       if (!atomic_read(&group->container_users))
+               return 0;
+
+       return vfio_dev_viable(dev, group);
+}
+
+static int vfio_iommu_group_notifier(struct notifier_block *nb,
+                                    unsigned long action, void *data)
+{
+       struct vfio_group *group = container_of(nb, struct vfio_group, nb);
+       struct device *dev = data;
+
+       /*
+        * Need to go through a group_lock lookup to get a reference or
+        * we risk racing a group being removed.  Leave a WARN_ON for
+        * debuging, but if the group no longer exists, a spurious notify
+        * is harmless.
+        */
+       group = vfio_group_try_get(group);
+       if (WARN_ON(!group))
+               return NOTIFY_OK;
+
+       switch (action) {
+       case IOMMU_GROUP_NOTIFY_ADD_DEVICE:
+               vfio_group_nb_add_dev(group, dev);
+               break;
+       case IOMMU_GROUP_NOTIFY_DEL_DEVICE:
+               vfio_group_nb_del_dev(group, dev);
+               break;
+       case IOMMU_GROUP_NOTIFY_BIND_DRIVER:
+               pr_debug("%s: Device %s, group %d binding to driver\n",
+                        __func__, dev_name(dev),
+                        iommu_group_id(group->iommu_group));
+               break;
+       case IOMMU_GROUP_NOTIFY_BOUND_DRIVER:
+               pr_debug("%s: Device %s, group %d bound to driver %s\n",
+                        __func__, dev_name(dev),
+                        iommu_group_id(group->iommu_group), dev->driver->name);
+               BUG_ON(vfio_group_nb_verify(group, dev));
+               break;
+       case IOMMU_GROUP_NOTIFY_UNBIND_DRIVER:
+               pr_debug("%s: Device %s, group %d unbinding from driver %s\n",
+                        __func__, dev_name(dev),
+                        iommu_group_id(group->iommu_group), dev->driver->name);
+               break;
+       case IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER:
+               pr_debug("%s: Device %s, group %d unbound from driver\n",
+                        __func__, dev_name(dev),
+                        iommu_group_id(group->iommu_group));
+               /*
+                * XXX An unbound device in a live group is ok, but we'd
+                * really like to avoid the above BUG_ON by preventing other
+                * drivers from binding to it.  Once that occurs, we have to
+                * stop the system to maintain isolation.  At a minimum, we'd
+                * want a toggle to disable driver auto probe for this device.
+                */
+               break;
+       }
+
+       vfio_group_put(group);
+       return NOTIFY_OK;
+}
+
+/**
+ * VFIO driver API
+ */
+int vfio_add_group_dev(struct device *dev,
+                      const struct vfio_device_ops *ops, void *device_data)
+{
+       struct iommu_group *iommu_group;
+       struct vfio_group *group;
+       struct vfio_device *device;
+
+       iommu_group = iommu_group_get(dev);
+       if (!iommu_group)
+               return -EINVAL;
+
+       group = vfio_group_get_from_iommu(iommu_group);
+       if (!group) {
+               group = vfio_create_group(iommu_group);
+               if (IS_ERR(group)) {
+                       iommu_group_put(iommu_group);
+                       return PTR_ERR(group);
+               }
+       }
+
+       device = vfio_group_get_device(group, dev);
+       if (device) {
+               WARN(1, "Device %s already exists on group %d\n",
+                    dev_name(dev), iommu_group_id(iommu_group));
+               vfio_device_put(device);
+               vfio_group_put(group);
+               iommu_group_put(iommu_group);
+               return -EBUSY;
+       }
+
+       device = vfio_group_create_device(group, dev, ops, device_data);
+       if (IS_ERR(device)) {
+               vfio_group_put(group);
+               iommu_group_put(iommu_group);
+               return PTR_ERR(device);
+       }
+
+       /*
+        * Added device holds reference to iommu_group and vfio_device
+        * (which in turn holds reference to vfio_group).  Drop extra
+        * group reference used while acquiring device.
+        */
+       vfio_group_put(group);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vfio_add_group_dev);
+
+/* Test whether a struct device is present in our tracking */
+static bool vfio_dev_present(struct device *dev)
+{
+       struct iommu_group *iommu_group;
+       struct vfio_group *group;
+       struct vfio_device *device;
+
+       iommu_group = iommu_group_get(dev);
+       if (!iommu_group)
+               return false;
+
+       group = vfio_group_get_from_iommu(iommu_group);
+       if (!group) {
+               iommu_group_put(iommu_group);
+               return false;
+       }
+
+       device = vfio_group_get_device(group, dev);
+       if (!device) {
+               vfio_group_put(group);
+               iommu_group_put(iommu_group);
+               return false;
+       }
+
+       vfio_device_put(device);
+       vfio_group_put(group);
+       iommu_group_put(iommu_group);
+       return true;
+}
+
+/*
+ * Decrement the device reference count and wait for the device to be
+ * removed.  Open file descriptors for the device... */
+void *vfio_del_group_dev(struct device *dev)
+{
+       struct vfio_device *device = dev_get_drvdata(dev);
+       struct vfio_group *group = device->group;
+       struct iommu_group *iommu_group = group->iommu_group;
+       void *device_data = device->device_data;
+
+       vfio_device_put(device);
+
+       /* TODO send a signal to encourage this to be released */
+       wait_event(vfio.release_q, !vfio_dev_present(dev));
+
+       iommu_group_put(iommu_group);
+
+       return device_data;
+}
+EXPORT_SYMBOL_GPL(vfio_del_group_dev);
+
+/**
+ * VFIO base fd, /dev/vfio/vfio
+ */
+static long vfio_ioctl_check_extension(struct vfio_container *container,
+                                      unsigned long arg)
+{
+       struct vfio_iommu_driver *driver = container->iommu_driver;
+       long ret = 0;
+
+       switch (arg) {
+               /* No base extensions yet */
+       default:
+               /*
+                * If no driver is set, poll all registered drivers for
+                * extensions and return the first positive result.  If
+                * a driver is already set, further queries will be passed
+                * only to that driver.
+                */
+               if (!driver) {
+                       mutex_lock(&vfio.iommu_drivers_lock);
+                       list_for_each_entry(driver, &vfio.iommu_drivers_list,
+                                           vfio_next) {
+                               if (!try_module_get(driver->ops->owner))
+                                       continue;
+
+                               ret = driver->ops->ioctl(NULL,
+                                                        VFIO_CHECK_EXTENSION,
+                                                        arg);
+                               module_put(driver->ops->owner);
+                               if (ret > 0)
+                                       break;
+                       }
+                       mutex_unlock(&vfio.iommu_drivers_lock);
+               } else
+                       ret = driver->ops->ioctl(container->iommu_data,
+                                                VFIO_CHECK_EXTENSION, arg);
+       }
+
+       return ret;
+}
+
+/* hold container->group_lock */
+static int __vfio_container_attach_groups(struct vfio_container *container,
+                                         struct vfio_iommu_driver *driver,
+                                         void *data)
+{
+       struct vfio_group *group;
+       int ret = -ENODEV;
+
+       list_for_each_entry(group, &container->group_list, container_next) {
+               ret = driver->ops->attach_group(data, group->iommu_group);
+               if (ret)
+                       goto unwind;
+       }
+
+       return ret;
+
+unwind:
+       list_for_each_entry_continue_reverse(group, &container->group_list,
+                                            container_next) {
+               driver->ops->detach_group(data, group->iommu_group);
+       }
+
+       return ret;
+}
+
+static long vfio_ioctl_set_iommu(struct vfio_container *container,
+                                unsigned long arg)
+{
+       struct vfio_iommu_driver *driver;
+       long ret = -ENODEV;
+
+       mutex_lock(&container->group_lock);
+
+       /*
+        * The container is designed to be an unprivileged interface while
+        * the group can be assigned to specific users.  Therefore, only by
+        * adding a group to a container does the user get the privilege of
+        * enabling the iommu, which may allocate finite resources.  There
+        * is no unset_iommu, but by removing all the groups from a container,
+        * the container is deprivileged and returns to an unset state.
+        */
+       if (list_empty(&container->group_list) || container->iommu_driver) {
+               mutex_unlock(&container->group_lock);
+               return -EINVAL;
+       }
+
+       mutex_lock(&vfio.iommu_drivers_lock);
+       list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) {
+               void *data;
+
+               if (!try_module_get(driver->ops->owner))
+                       continue;
+
+               /*
+                * The arg magic for SET_IOMMU is the same as CHECK_EXTENSION,
+                * so test which iommu driver reported support for this
+                * extension and call open on them.  We also pass them the
+                * magic, allowing a single driver to support multiple
+                * interfaces if they'd like.
+                */
+               if (driver->ops->ioctl(NULL, VFIO_CHECK_EXTENSION, arg) <= 0) {
+                       module_put(driver->ops->owner);
+                       continue;
+               }
+
+               /* module reference holds the driver we're working on */
+               mutex_unlock(&vfio.iommu_drivers_lock);
+
+               data = driver->ops->open(arg);
+               if (IS_ERR(data)) {
+                       ret = PTR_ERR(data);
+                       module_put(driver->ops->owner);
+                       goto skip_drivers_unlock;
+               }
+
+               ret = __vfio_container_attach_groups(container, driver, data);
+               if (!ret) {
+                       container->iommu_driver = driver;
+                       container->iommu_data = data;
+               } else {
+                       driver->ops->release(data);
+                       module_put(driver->ops->owner);
+               }
+
+               goto skip_drivers_unlock;
+       }
+
+       mutex_unlock(&vfio.iommu_drivers_lock);
+skip_drivers_unlock:
+       mutex_unlock(&container->group_lock);
+
+       return ret;
+}
+
+static long vfio_fops_unl_ioctl(struct file *filep,
+                               unsigned int cmd, unsigned long arg)
+{
+       struct vfio_container *container = filep->private_data;
+       struct vfio_iommu_driver *driver;
+       void *data;
+       long ret = -EINVAL;
+
+       if (!container)
+               return ret;
+
+       driver = container->iommu_driver;
+       data = container->iommu_data;
+
+       switch (cmd) {
+       case VFIO_GET_API_VERSION:
+               ret = VFIO_API_VERSION;
+               break;
+       case VFIO_CHECK_EXTENSION:
+               ret = vfio_ioctl_check_extension(container, arg);
+               break;
+       case VFIO_SET_IOMMU:
+               ret = vfio_ioctl_set_iommu(container, arg);
+               break;
+       default:
+               if (driver) /* passthrough all unrecognized ioctls */
+                       ret = driver->ops->ioctl(data, cmd, arg);
+       }
+
+       return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long vfio_fops_compat_ioctl(struct file *filep,
+                                  unsigned int cmd, unsigned long arg)
+{
+       arg = (unsigned long)compat_ptr(arg);
+       return vfio_fops_unl_ioctl(filep, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
+
+static int vfio_fops_open(struct inode *inode, struct file *filep)
+{
+       struct vfio_container *container;
+
+       container = kzalloc(sizeof(*container), GFP_KERNEL);
+       if (!container)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&container->group_list);
+       mutex_init(&container->group_lock);
+       kref_init(&container->kref);
+
+       filep->private_data = container;
+
+       return 0;
+}
+
+static int vfio_fops_release(struct inode *inode, struct file *filep)
+{
+       struct vfio_container *container = filep->private_data;
+
+       filep->private_data = NULL;
+
+       vfio_container_put(container);
+
+       return 0;
+}
+
+/*
+ * Once an iommu driver is set, we optionally pass read/write/mmap
+ * on to the driver, allowing management interfaces beyond ioctl.
+ */
+static ssize_t vfio_fops_read(struct file *filep, char __user *buf,
+                             size_t count, loff_t *ppos)
+{
+       struct vfio_container *container = filep->private_data;
+       struct vfio_iommu_driver *driver = container->iommu_driver;
+
+       if (unlikely(!driver || !driver->ops->read))
+               return -EINVAL;
+
+       return driver->ops->read(container->iommu_data, buf, count, ppos);
+}
+
+static ssize_t vfio_fops_write(struct file *filep, const char __user *buf,
+                              size_t count, loff_t *ppos)
+{
+       struct vfio_container *container = filep->private_data;
+       struct vfio_iommu_driver *driver = container->iommu_driver;
+
+       if (unlikely(!driver || !driver->ops->write))
+               return -EINVAL;
+
+       return driver->ops->write(container->iommu_data, buf, count, ppos);
+}
+
+static int vfio_fops_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+       struct vfio_container *container = filep->private_data;
+       struct vfio_iommu_driver *driver = container->iommu_driver;
+
+       if (unlikely(!driver || !driver->ops->mmap))
+               return -EINVAL;
+
+       return driver->ops->mmap(container->iommu_data, vma);
+}
+
+static const struct file_operations vfio_fops = {
+       .owner          = THIS_MODULE,
+       .open           = vfio_fops_open,
+       .release        = vfio_fops_release,
+       .read           = vfio_fops_read,
+       .write          = vfio_fops_write,
+       .unlocked_ioctl = vfio_fops_unl_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = vfio_fops_compat_ioctl,
+#endif
+       .mmap           = vfio_fops_mmap,
+};
+
+/**
+ * VFIO Group fd, /dev/vfio/$GROUP
+ */
+static void __vfio_group_unset_container(struct vfio_group *group)
+{
+       struct vfio_container *container = group->container;
+       struct vfio_iommu_driver *driver;
+
+       mutex_lock(&container->group_lock);
+
+       driver = container->iommu_driver;
+       if (driver)
+               driver->ops->detach_group(container->iommu_data,
+                                         group->iommu_group);
+
+       group->container = NULL;
+       list_del(&group->container_next);
+
+       /* Detaching the last group deprivileges a container, remove iommu */
+       if (driver && list_empty(&container->group_list)) {
+               driver->ops->release(container->iommu_data);
+               module_put(driver->ops->owner);
+               container->iommu_driver = NULL;
+               container->iommu_data = NULL;
+       }
+
+       mutex_unlock(&container->group_lock);
+
+       vfio_container_put(container);
+}
+
+/*
+ * VFIO_GROUP_UNSET_CONTAINER should fail if there are other users or
+ * if there was no container to unset.  Since the ioctl is called on
+ * the group, we know that still exists, therefore the only valid
+ * transition here is 1->0.
+ */
+static int vfio_group_unset_container(struct vfio_group *group)
+{
+       int users = atomic_cmpxchg(&group->container_users, 1, 0);
+
+       if (!users)
+               return -EINVAL;
+       if (users != 1)
+               return -EBUSY;
+
+       __vfio_group_unset_container(group);
+
+       return 0;
+}
+
+/*
+ * When removing container users, anything that removes the last user
+ * implicitly removes the group from the container.  That is, if the
+ * group file descriptor is closed, as well as any device file descriptors,
+ * the group is free.
+ */
+static void vfio_group_try_dissolve_container(struct vfio_group *group)
+{
+       if (0 == atomic_dec_if_positive(&group->container_users))
+               __vfio_group_unset_container(group);
+}
+
+static int vfio_group_set_container(struct vfio_group *group, int container_fd)
+{
+       struct file *filep;
+       struct vfio_container *container;
+       struct vfio_iommu_driver *driver;
+       int ret = 0;
+
+       if (atomic_read(&group->container_users))
+               return -EINVAL;
+
+       filep = fget(container_fd);
+       if (!filep)
+               return -EBADF;
+
+       /* Sanity check, is this really our fd? */
+       if (filep->f_op != &vfio_fops) {
+               fput(filep);
+               return -EINVAL;
+       }
+
+       container = filep->private_data;
+       WARN_ON(!container); /* fget ensures we don't race vfio_release */
+
+       mutex_lock(&container->group_lock);
+
+       driver = container->iommu_driver;
+       if (driver) {
+               ret = driver->ops->attach_group(container->iommu_data,
+                                               group->iommu_group);
+               if (ret)
+                       goto unlock_out;
+       }
+
+       group->container = container;
+       list_add(&group->container_next, &container->group_list);
+
+       /* Get a reference on the container and mark a user within the group */
+       vfio_container_get(container);
+       atomic_inc(&group->container_users);
+
+unlock_out:
+       mutex_unlock(&container->group_lock);
+       fput(filep);
+
+       return ret;
+}
+
+static bool vfio_group_viable(struct vfio_group *group)
+{
+       return (iommu_group_for_each_dev(group->iommu_group,
+                                        group, vfio_dev_viable) == 0);
+}
+
+static const struct file_operations vfio_device_fops;
+
+static int vfio_group_get_device_fd(struct vfio_group *group, char *buf)
+{
+       struct vfio_device *device;
+       struct file *filep;
+       int ret = -ENODEV;
+
+       if (0 == atomic_read(&group->container_users) ||
+           !group->container->iommu_driver || !vfio_group_viable(group))
+               return -EINVAL;
+
+       mutex_lock(&group->device_lock);
+       list_for_each_entry(device, &group->device_list, group_next) {
+               if (strcmp(dev_name(device->dev), buf))
+                       continue;
+
+               ret = device->ops->open(device->device_data);
+               if (ret)
+                       break;
+               /*
+                * We can't use anon_inode_getfd() because we need to modify
+                * the f_mode flags directly to allow more than just ioctls
+                */
+               ret = get_unused_fd();
+               if (ret < 0) {
+                       device->ops->release(device->device_data);
+                       break;
+               }
+
+               filep = anon_inode_getfile("[vfio-device]", &vfio_device_fops,
+                                          device, O_RDWR);
+               if (IS_ERR(filep)) {
+                       put_unused_fd(ret);
+                       ret = PTR_ERR(filep);
+                       device->ops->release(device->device_data);
+                       break;
+               }
+
+               /*
+                * TODO: add an anon_inode interface to do this.
+                * Appears to be missing by lack of need rather than
+                * explicitly prevented.  Now there's need.
+                */
+               filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+
+               fd_install(ret, filep);
+
+               vfio_device_get(device);
+               atomic_inc(&group->container_users);
+               break;
+       }
+       mutex_unlock(&group->device_lock);
+
+       return ret;
+}
+
+static long vfio_group_fops_unl_ioctl(struct file *filep,
+                                     unsigned int cmd, unsigned long arg)
+{
+       struct vfio_group *group = filep->private_data;
+       long ret = -ENOTTY;
+
+       switch (cmd) {
+       case VFIO_GROUP_GET_STATUS:
+       {
+               struct vfio_group_status status;
+               unsigned long minsz;
+
+               minsz = offsetofend(struct vfio_group_status, flags);
+
+               if (copy_from_user(&status, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (status.argsz < minsz)
+                       return -EINVAL;
+
+               status.flags = 0;
+
+               if (vfio_group_viable(group))
+                       status.flags |= VFIO_GROUP_FLAGS_VIABLE;
+
+               if (group->container)
+                       status.flags |= VFIO_GROUP_FLAGS_CONTAINER_SET;
+
+               if (copy_to_user((void __user *)arg, &status, minsz))
+                       return -EFAULT;
+
+               ret = 0;
+               break;
+       }
+       case VFIO_GROUP_SET_CONTAINER:
+       {
+               int fd;
+
+               if (get_user(fd, (int __user *)arg))
+                       return -EFAULT;
+
+               if (fd < 0)
+                       return -EINVAL;
+
+               ret = vfio_group_set_container(group, fd);
+               break;
+       }
+       case VFIO_GROUP_UNSET_CONTAINER:
+               ret = vfio_group_unset_container(group);
+               break;
+       case VFIO_GROUP_GET_DEVICE_FD:
+       {
+               char *buf;
+
+               buf = strndup_user((const char __user *)arg, PAGE_SIZE);
+               if (IS_ERR(buf))
+                       return PTR_ERR(buf);
+
+               ret = vfio_group_get_device_fd(group, buf);
+               kfree(buf);
+               break;
+       }
+       }
+
+       return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long vfio_group_fops_compat_ioctl(struct file *filep,
+                                        unsigned int cmd, unsigned long arg)
+{
+       arg = (unsigned long)compat_ptr(arg);
+       return vfio_group_fops_unl_ioctl(filep, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
+
+static int vfio_group_fops_open(struct inode *inode, struct file *filep)
+{
+       struct vfio_group *group;
+
+       group = vfio_group_get_from_minor(iminor(inode));
+       if (!group)
+               return -ENODEV;
+
+       if (group->container) {
+               vfio_group_put(group);
+               return -EBUSY;
+       }
+
+       filep->private_data = group;
+
+       return 0;
+}
+
+static int vfio_group_fops_release(struct inode *inode, struct file *filep)
+{
+       struct vfio_group *group = filep->private_data;
+
+       filep->private_data = NULL;
+
+       vfio_group_try_dissolve_container(group);
+
+       vfio_group_put(group);
+
+       return 0;
+}
+
+static const struct file_operations vfio_group_fops = {
+       .owner          = THIS_MODULE,
+       .unlocked_ioctl = vfio_group_fops_unl_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = vfio_group_fops_compat_ioctl,
+#endif
+       .open           = vfio_group_fops_open,
+       .release        = vfio_group_fops_release,
+};
+
+/**
+ * VFIO Device fd
+ */
+static int vfio_device_fops_release(struct inode *inode, struct file *filep)
+{
+       struct vfio_device *device = filep->private_data;
+
+       device->ops->release(device->device_data);
+
+       vfio_group_try_dissolve_container(device->group);
+
+       vfio_device_put(device);
+
+       return 0;
+}
+
+static long vfio_device_fops_unl_ioctl(struct file *filep,
+                                      unsigned int cmd, unsigned long arg)
+{
+       struct vfio_device *device = filep->private_data;
+
+       if (unlikely(!device->ops->ioctl))
+               return -EINVAL;
+
+       return device->ops->ioctl(device->device_data, cmd, arg);
+}
+
+static ssize_t vfio_device_fops_read(struct file *filep, char __user *buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct vfio_device *device = filep->private_data;
+
+       if (unlikely(!device->ops->read))
+               return -EINVAL;
+
+       return device->ops->read(device->device_data, buf, count, ppos);
+}
+
+static ssize_t vfio_device_fops_write(struct file *filep,
+                                     const char __user *buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct vfio_device *device = filep->private_data;
+
+       if (unlikely(!device->ops->write))
+               return -EINVAL;
+
+       return device->ops->write(device->device_data, buf, count, ppos);
+}
+
+static int vfio_device_fops_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+       struct vfio_device *device = filep->private_data;
+
+       if (unlikely(!device->ops->mmap))
+               return -EINVAL;
+
+       return device->ops->mmap(device->device_data, vma);
+}
+
+#ifdef CONFIG_COMPAT
+static long vfio_device_fops_compat_ioctl(struct file *filep,
+                                         unsigned int cmd, unsigned long arg)
+{
+       arg = (unsigned long)compat_ptr(arg);
+       return vfio_device_fops_unl_ioctl(filep, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
+
+static const struct file_operations vfio_device_fops = {
+       .owner          = THIS_MODULE,
+       .release        = vfio_device_fops_release,
+       .read           = vfio_device_fops_read,
+       .write          = vfio_device_fops_write,
+       .unlocked_ioctl = vfio_device_fops_unl_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = vfio_device_fops_compat_ioctl,
+#endif
+       .mmap           = vfio_device_fops_mmap,
+};
+
+/**
+ * Module/class support
+ */
+static char *vfio_devnode(struct device *dev, umode_t *mode)
+{
+       return kasprintf(GFP_KERNEL, "vfio/%s", dev_name(dev));
+}
+
+static int __init vfio_init(void)
+{
+       int ret;
+
+       idr_init(&vfio.group_idr);
+       mutex_init(&vfio.group_lock);
+       mutex_init(&vfio.iommu_drivers_lock);
+       INIT_LIST_HEAD(&vfio.group_list);
+       INIT_LIST_HEAD(&vfio.iommu_drivers_list);
+       init_waitqueue_head(&vfio.release_q);
+
+       vfio.class = class_create(THIS_MODULE, "vfio");
+       if (IS_ERR(vfio.class)) {
+               ret = PTR_ERR(vfio.class);
+               goto err_class;
+       }
+
+       vfio.class->devnode = vfio_devnode;
+
+       ret = alloc_chrdev_region(&vfio.devt, 0, MINORMASK, "vfio");
+       if (ret)
+               goto err_base_chrdev;
+
+       cdev_init(&vfio.cdev, &vfio_fops);
+       ret = cdev_add(&vfio.cdev, vfio.devt, 1);
+       if (ret)
+               goto err_base_cdev;
+
+       vfio.dev = device_create(vfio.class, NULL, vfio.devt, NULL, "vfio");
+       if (IS_ERR(vfio.dev)) {
+               ret = PTR_ERR(vfio.dev);
+               goto err_base_dev;
+       }
+
+       /* /dev/vfio/$GROUP */
+       cdev_init(&vfio.group_cdev, &vfio_group_fops);
+       ret = cdev_add(&vfio.group_cdev,
+                      MKDEV(MAJOR(vfio.devt), 1), MINORMASK - 1);
+       if (ret)
+               goto err_groups_cdev;
+
+       pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+       /*
+        * Attempt to load known iommu-drivers.  This gives us a working
+        * environment without the user needing to explicitly load iommu
+        * drivers.
+        */
+       request_module_nowait("vfio_iommu_type1");
+
+       return 0;
+
+err_groups_cdev:
+       device_destroy(vfio.class, vfio.devt);
+err_base_dev:
+       cdev_del(&vfio.cdev);
+err_base_cdev:
+       unregister_chrdev_region(vfio.devt, MINORMASK);
+err_base_chrdev:
+       class_destroy(vfio.class);
+       vfio.class = NULL;
+err_class:
+       return ret;
+}
+
+static void __exit vfio_cleanup(void)
+{
+       WARN_ON(!list_empty(&vfio.group_list));
+
+       idr_destroy(&vfio.group_idr);
+       cdev_del(&vfio.group_cdev);
+       device_destroy(vfio.class, vfio.devt);
+       cdev_del(&vfio.cdev);
+       unregister_chrdev_region(vfio.devt, MINORMASK);
+       class_destroy(vfio.class);
+       vfio.class = NULL;
+}
+
+module_init(vfio_init);
+module_exit(vfio_cleanup);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
new file mode 100644 (file)
index 0000000..6f3fbc4
--- /dev/null
@@ -0,0 +1,753 @@
+/*
+ * VFIO: IOMMU DMA mapping support for Type1 IOMMU
+ *
+ * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
+ *     Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc.  All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ *
+ * We arbitrarily define a Type1 IOMMU as one matching the below code.
+ * It could be called the x86 IOMMU as it's designed for AMD-Vi & Intel
+ * VT-d, but that makes it harder to re-use as theoretically anyone
+ * implementing a similar IOMMU could make use of this.  We expect the
+ * IOMMU to support the IOMMU API and have few to no restrictions around
+ * the IOVA range that can be mapped.  The Type1 IOMMU is currently
+ * optimized for relatively static mappings of a userspace process with
+ * userpsace pages pinned into memory.  We also assume devices and IOMMU
+ * domains are PCI based as the IOMMU API is still centered around a
+ * device/bus interface rather than a group interface.
+ */
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/pci.h>         /* pci_bus_type */
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/vfio.h>
+#include <linux/workqueue.h>
+
+#define DRIVER_VERSION  "0.2"
+#define DRIVER_AUTHOR   "Alex Williamson <alex.williamson@redhat.com>"
+#define DRIVER_DESC     "Type1 IOMMU driver for VFIO"
+
+static bool allow_unsafe_interrupts;
+module_param_named(allow_unsafe_interrupts,
+                  allow_unsafe_interrupts, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(allow_unsafe_interrupts,
+                "Enable VFIO IOMMU support for on platforms without interrupt remapping support.");
+
+struct vfio_iommu {
+       struct iommu_domain     *domain;
+       struct mutex            lock;
+       struct list_head        dma_list;
+       struct list_head        group_list;
+       bool                    cache;
+};
+
+struct vfio_dma {
+       struct list_head        next;
+       dma_addr_t              iova;           /* Device address */
+       unsigned long           vaddr;          /* Process virtual addr */
+       long                    npage;          /* Number of pages */
+       int                     prot;           /* IOMMU_READ/WRITE */
+};
+
+struct vfio_group {
+       struct iommu_group      *iommu_group;
+       struct list_head        next;
+};
+
+/*
+ * This code handles mapping and unmapping of user data buffers
+ * into DMA'ble space using the IOMMU
+ */
+
+#define NPAGE_TO_SIZE(npage)   ((size_t)(npage) << PAGE_SHIFT)
+
+struct vwork {
+       struct mm_struct        *mm;
+       long                    npage;
+       struct work_struct      work;
+};
+
+/* delayed decrement/increment for locked_vm */
+static void vfio_lock_acct_bg(struct work_struct *work)
+{
+       struct vwork *vwork = container_of(work, struct vwork, work);
+       struct mm_struct *mm;
+
+       mm = vwork->mm;
+       down_write(&mm->mmap_sem);
+       mm->locked_vm += vwork->npage;
+       up_write(&mm->mmap_sem);
+       mmput(mm);
+       kfree(vwork);
+}
+
+static void vfio_lock_acct(long npage)
+{
+       struct vwork *vwork;
+       struct mm_struct *mm;
+
+       if (!current->mm)
+               return; /* process exited */
+
+       if (down_write_trylock(&current->mm->mmap_sem)) {
+               current->mm->locked_vm += npage;
+               up_write(&current->mm->mmap_sem);
+               return;
+       }
+
+       /*
+        * Couldn't get mmap_sem lock, so must setup to update
+        * mm->locked_vm later. If locked_vm were atomic, we
+        * wouldn't need this silliness
+        */
+       vwork = kmalloc(sizeof(struct vwork), GFP_KERNEL);
+       if (!vwork)
+               return;
+       mm = get_task_mm(current);
+       if (!mm) {
+               kfree(vwork);
+               return;
+       }
+       INIT_WORK(&vwork->work, vfio_lock_acct_bg);
+       vwork->mm = mm;
+       vwork->npage = npage;
+       schedule_work(&vwork->work);
+}
+
+/*
+ * Some mappings aren't backed by a struct page, for example an mmap'd
+ * MMIO range for our own or another device.  These use a different
+ * pfn conversion and shouldn't be tracked as locked pages.
+ */
+static bool is_invalid_reserved_pfn(unsigned long pfn)
+{
+       if (pfn_valid(pfn)) {
+               bool reserved;
+               struct page *tail = pfn_to_page(pfn);
+               struct page *head = compound_trans_head(tail);
+               reserved = !!(PageReserved(head));
+               if (head != tail) {
+                       /*
+                        * "head" is not a dangling pointer
+                        * (compound_trans_head takes care of that)
+                        * but the hugepage may have been split
+                        * from under us (and we may not hold a
+                        * reference count on the head page so it can
+                        * be reused before we run PageReferenced), so
+                        * we've to check PageTail before returning
+                        * what we just read.
+                        */
+                       smp_rmb();
+                       if (PageTail(tail))
+                               return reserved;
+               }
+               return PageReserved(tail);
+       }
+
+       return true;
+}
+
+static int put_pfn(unsigned long pfn, int prot)
+{
+       if (!is_invalid_reserved_pfn(pfn)) {
+               struct page *page = pfn_to_page(pfn);
+               if (prot & IOMMU_WRITE)
+                       SetPageDirty(page);
+               put_page(page);
+               return 1;
+       }
+       return 0;
+}
+
+/* Unmap DMA region */
+static long __vfio_dma_do_unmap(struct vfio_iommu *iommu, dma_addr_t iova,
+                            long npage, int prot)
+{
+       long i, unlocked = 0;
+
+       for (i = 0; i < npage; i++, iova += PAGE_SIZE) {
+               unsigned long pfn;
+
+               pfn = iommu_iova_to_phys(iommu->domain, iova) >> PAGE_SHIFT;
+               if (pfn) {
+                       iommu_unmap(iommu->domain, iova, PAGE_SIZE);
+                       unlocked += put_pfn(pfn, prot);
+               }
+       }
+       return unlocked;
+}
+
+static void vfio_dma_unmap(struct vfio_iommu *iommu, dma_addr_t iova,
+                          long npage, int prot)
+{
+       long unlocked;
+
+       unlocked = __vfio_dma_do_unmap(iommu, iova, npage, prot);
+       vfio_lock_acct(-unlocked);
+}
+
+static int vaddr_get_pfn(unsigned long vaddr, int prot, unsigned long *pfn)
+{
+       struct page *page[1];
+       struct vm_area_struct *vma;
+       int ret = -EFAULT;
+
+       if (get_user_pages_fast(vaddr, 1, !!(prot & IOMMU_WRITE), page) == 1) {
+               *pfn = page_to_pfn(page[0]);
+               return 0;
+       }
+
+       down_read(&current->mm->mmap_sem);
+
+       vma = find_vma_intersection(current->mm, vaddr, vaddr + 1);
+
+       if (vma && vma->vm_flags & VM_PFNMAP) {
+               *pfn = ((vaddr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+               if (is_invalid_reserved_pfn(*pfn))
+                       ret = 0;
+       }
+
+       up_read(&current->mm->mmap_sem);
+
+       return ret;
+}
+
+/* Map DMA region */
+static int __vfio_dma_map(struct vfio_iommu *iommu, dma_addr_t iova,
+                         unsigned long vaddr, long npage, int prot)
+{
+       dma_addr_t start = iova;
+       long i, locked = 0;
+       int ret;
+
+       /* Verify that pages are not already mapped */
+       for (i = 0; i < npage; i++, iova += PAGE_SIZE)
+               if (iommu_iova_to_phys(iommu->domain, iova))
+                       return -EBUSY;
+
+       iova = start;
+
+       if (iommu->cache)
+               prot |= IOMMU_CACHE;
+
+       /*
+        * XXX We break mappings into pages and use get_user_pages_fast to
+        * pin the pages in memory.  It's been suggested that mlock might
+        * provide a more efficient mechanism, but nothing prevents the
+        * user from munlocking the pages, which could then allow the user
+        * access to random host memory.  We also have no guarantee from the
+        * IOMMU API that the iommu driver can unmap sub-pages of previous
+        * mappings.  This means we might lose an entire range if a single
+        * page within it is unmapped.  Single page mappings are inefficient,
+        * but provide the most flexibility for now.
+        */
+       for (i = 0; i < npage; i++, iova += PAGE_SIZE, vaddr += PAGE_SIZE) {
+               unsigned long pfn = 0;
+
+               ret = vaddr_get_pfn(vaddr, prot, &pfn);
+               if (ret) {
+                       __vfio_dma_do_unmap(iommu, start, i, prot);
+                       return ret;
+               }
+
+               /*
+                * Only add actual locked pages to accounting
+                * XXX We're effectively marking a page locked for every
+                * IOVA page even though it's possible the user could be
+                * backing multiple IOVAs with the same vaddr.  This over-
+                * penalizes the user process, but we currently have no
+                * easy way to do this properly.
+                */
+               if (!is_invalid_reserved_pfn(pfn))
+                       locked++;
+
+               ret = iommu_map(iommu->domain, iova,
+                               (phys_addr_t)pfn << PAGE_SHIFT,
+                               PAGE_SIZE, prot);
+               if (ret) {
+                       /* Back out mappings on error */
+                       put_pfn(pfn, prot);
+                       __vfio_dma_do_unmap(iommu, start, i, prot);
+                       return ret;
+               }
+       }
+       vfio_lock_acct(locked);
+       return 0;
+}
+
+static inline bool ranges_overlap(dma_addr_t start1, size_t size1,
+                                 dma_addr_t start2, size_t size2)
+{
+       if (start1 < start2)
+               return (start2 - start1 < size1);
+       else if (start2 < start1)
+               return (start1 - start2 < size2);
+       return (size1 > 0 && size2 > 0);
+}
+
+static struct vfio_dma *vfio_find_dma(struct vfio_iommu *iommu,
+                                               dma_addr_t start, size_t size)
+{
+       struct vfio_dma *dma;
+
+       list_for_each_entry(dma, &iommu->dma_list, next) {
+               if (ranges_overlap(dma->iova, NPAGE_TO_SIZE(dma->npage),
+                                  start, size))
+                       return dma;
+       }
+       return NULL;
+}
+
+static long vfio_remove_dma_overlap(struct vfio_iommu *iommu, dma_addr_t start,
+                                   size_t size, struct vfio_dma *dma)
+{
+       struct vfio_dma *split;
+       long npage_lo, npage_hi;
+
+       /* Existing dma region is completely covered, unmap all */
+       if (start <= dma->iova &&
+           start + size >= dma->iova + NPAGE_TO_SIZE(dma->npage)) {
+               vfio_dma_unmap(iommu, dma->iova, dma->npage, dma->prot);
+               list_del(&dma->next);
+               npage_lo = dma->npage;
+               kfree(dma);
+               return npage_lo;
+       }
+
+       /* Overlap low address of existing range */
+       if (start <= dma->iova) {
+               size_t overlap;
+
+               overlap = start + size - dma->iova;
+               npage_lo = overlap >> PAGE_SHIFT;
+
+               vfio_dma_unmap(iommu, dma->iova, npage_lo, dma->prot);
+               dma->iova += overlap;
+               dma->vaddr += overlap;
+               dma->npage -= npage_lo;
+               return npage_lo;
+       }
+
+       /* Overlap high address of existing range */
+       if (start + size >= dma->iova + NPAGE_TO_SIZE(dma->npage)) {
+               size_t overlap;
+
+               overlap = dma->iova + NPAGE_TO_SIZE(dma->npage) - start;
+               npage_hi = overlap >> PAGE_SHIFT;
+
+               vfio_dma_unmap(iommu, start, npage_hi, dma->prot);
+               dma->npage -= npage_hi;
+               return npage_hi;
+       }
+
+       /* Split existing */
+       npage_lo = (start - dma->iova) >> PAGE_SHIFT;
+       npage_hi = dma->npage - (size >> PAGE_SHIFT) - npage_lo;
+
+       split = kzalloc(sizeof *split, GFP_KERNEL);
+       if (!split)
+               return -ENOMEM;
+
+       vfio_dma_unmap(iommu, start, size >> PAGE_SHIFT, dma->prot);
+
+       dma->npage = npage_lo;
+
+       split->npage = npage_hi;
+       split->iova = start + size;
+       split->vaddr = dma->vaddr + NPAGE_TO_SIZE(npage_lo) + size;
+       split->prot = dma->prot;
+       list_add(&split->next, &iommu->dma_list);
+       return size >> PAGE_SHIFT;
+}
+
+static int vfio_dma_do_unmap(struct vfio_iommu *iommu,
+                            struct vfio_iommu_type1_dma_unmap *unmap)
+{
+       long ret = 0, npage = unmap->size >> PAGE_SHIFT;
+       struct vfio_dma *dma, *tmp;
+       uint64_t mask;
+
+       mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1;
+
+       if (unmap->iova & mask)
+               return -EINVAL;
+       if (unmap->size & mask)
+               return -EINVAL;
+
+       /* XXX We still break these down into PAGE_SIZE */
+       WARN_ON(mask & PAGE_MASK);
+
+       mutex_lock(&iommu->lock);
+
+       list_for_each_entry_safe(dma, tmp, &iommu->dma_list, next) {
+               if (ranges_overlap(dma->iova, NPAGE_TO_SIZE(dma->npage),
+                                  unmap->iova, unmap->size)) {
+                       ret = vfio_remove_dma_overlap(iommu, unmap->iova,
+                                                     unmap->size, dma);
+                       if (ret > 0)
+                               npage -= ret;
+                       if (ret < 0 || npage == 0)
+                               break;
+               }
+       }
+       mutex_unlock(&iommu->lock);
+       return ret > 0 ? 0 : (int)ret;
+}
+
+static int vfio_dma_do_map(struct vfio_iommu *iommu,
+                          struct vfio_iommu_type1_dma_map *map)
+{
+       struct vfio_dma *dma, *pdma = NULL;
+       dma_addr_t iova = map->iova;
+       unsigned long locked, lock_limit, vaddr = map->vaddr;
+       size_t size = map->size;
+       int ret = 0, prot = 0;
+       uint64_t mask;
+       long npage;
+
+       mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1;
+
+       /* READ/WRITE from device perspective */
+       if (map->flags & VFIO_DMA_MAP_FLAG_WRITE)
+               prot |= IOMMU_WRITE;
+       if (map->flags & VFIO_DMA_MAP_FLAG_READ)
+               prot |= IOMMU_READ;
+
+       if (!prot)
+               return -EINVAL; /* No READ/WRITE? */
+
+       if (vaddr & mask)
+               return -EINVAL;
+       if (iova & mask)
+               return -EINVAL;
+       if (size & mask)
+               return -EINVAL;
+
+       /* XXX We still break these down into PAGE_SIZE */
+       WARN_ON(mask & PAGE_MASK);
+
+       /* Don't allow IOVA wrap */
+       if (iova + size && iova + size < iova)
+               return -EINVAL;
+
+       /* Don't allow virtual address wrap */
+       if (vaddr + size && vaddr + size < vaddr)
+               return -EINVAL;
+
+       npage = size >> PAGE_SHIFT;
+       if (!npage)
+               return -EINVAL;
+
+       mutex_lock(&iommu->lock);
+
+       if (vfio_find_dma(iommu, iova, size)) {
+               ret = -EBUSY;
+               goto out_lock;
+       }
+
+       /* account for locked pages */
+       locked = current->mm->locked_vm + npage;
+       lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+       if (locked > lock_limit && !capable(CAP_IPC_LOCK)) {
+               pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n",
+                       __func__, rlimit(RLIMIT_MEMLOCK));
+               ret = -ENOMEM;
+               goto out_lock;
+       }
+
+       ret = __vfio_dma_map(iommu, iova, vaddr, npage, prot);
+       if (ret)
+               goto out_lock;
+
+       /* Check if we abut a region below - nothing below 0 */
+       if (iova) {
+               dma = vfio_find_dma(iommu, iova - 1, 1);
+               if (dma && dma->prot == prot &&
+                   dma->vaddr + NPAGE_TO_SIZE(dma->npage) == vaddr) {
+
+                       dma->npage += npage;
+                       iova = dma->iova;
+                       vaddr = dma->vaddr;
+                       npage = dma->npage;
+                       size = NPAGE_TO_SIZE(npage);
+
+                       pdma = dma;
+               }
+       }
+
+       /* Check if we abut a region above - nothing above ~0 + 1 */
+       if (iova + size) {
+               dma = vfio_find_dma(iommu, iova + size, 1);
+               if (dma && dma->prot == prot &&
+                   dma->vaddr == vaddr + size) {
+
+                       dma->npage += npage;
+                       dma->iova = iova;
+                       dma->vaddr = vaddr;
+
+                       /*
+                        * If merged above and below, remove previously
+                        * merged entry.  New entry covers it.
+                        */
+                       if (pdma) {
+                               list_del(&pdma->next);
+                               kfree(pdma);
+                       }
+                       pdma = dma;
+               }
+       }
+
+       /* Isolated, new region */
+       if (!pdma) {
+               dma = kzalloc(sizeof *dma, GFP_KERNEL);
+               if (!dma) {
+                       ret = -ENOMEM;
+                       vfio_dma_unmap(iommu, iova, npage, prot);
+                       goto out_lock;
+               }
+
+               dma->npage = npage;
+               dma->iova = iova;
+               dma->vaddr = vaddr;
+               dma->prot = prot;
+               list_add(&dma->next, &iommu->dma_list);
+       }
+
+out_lock:
+       mutex_unlock(&iommu->lock);
+       return ret;
+}
+
+static int vfio_iommu_type1_attach_group(void *iommu_data,
+                                        struct iommu_group *iommu_group)
+{
+       struct vfio_iommu *iommu = iommu_data;
+       struct vfio_group *group, *tmp;
+       int ret;
+
+       group = kzalloc(sizeof(*group), GFP_KERNEL);
+       if (!group)
+               return -ENOMEM;
+
+       mutex_lock(&iommu->lock);
+
+       list_for_each_entry(tmp, &iommu->group_list, next) {
+               if (tmp->iommu_group == iommu_group) {
+                       mutex_unlock(&iommu->lock);
+                       kfree(group);
+                       return -EINVAL;
+               }
+       }
+
+       /*
+        * TODO: Domain have capabilities that might change as we add
+        * groups (see iommu->cache, currently never set).  Check for
+        * them and potentially disallow groups to be attached when it
+        * would change capabilities (ugh).
+        */
+       ret = iommu_attach_group(iommu->domain, iommu_group);
+       if (ret) {
+               mutex_unlock(&iommu->lock);
+               kfree(group);
+               return ret;
+       }
+
+       group->iommu_group = iommu_group;
+       list_add(&group->next, &iommu->group_list);
+
+       mutex_unlock(&iommu->lock);
+
+       return 0;
+}
+
+static void vfio_iommu_type1_detach_group(void *iommu_data,
+                                         struct iommu_group *iommu_group)
+{
+       struct vfio_iommu *iommu = iommu_data;
+       struct vfio_group *group;
+
+       mutex_lock(&iommu->lock);
+
+       list_for_each_entry(group, &iommu->group_list, next) {
+               if (group->iommu_group == iommu_group) {
+                       iommu_detach_group(iommu->domain, iommu_group);
+                       list_del(&group->next);
+                       kfree(group);
+                       break;
+               }
+       }
+
+       mutex_unlock(&iommu->lock);
+}
+
+static void *vfio_iommu_type1_open(unsigned long arg)
+{
+       struct vfio_iommu *iommu;
+
+       if (arg != VFIO_TYPE1_IOMMU)
+               return ERR_PTR(-EINVAL);
+
+       iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
+       if (!iommu)
+               return ERR_PTR(-ENOMEM);
+
+       INIT_LIST_HEAD(&iommu->group_list);
+       INIT_LIST_HEAD(&iommu->dma_list);
+       mutex_init(&iommu->lock);
+
+       /*
+        * Wish we didn't have to know about bus_type here.
+        */
+       iommu->domain = iommu_domain_alloc(&pci_bus_type);
+       if (!iommu->domain) {
+               kfree(iommu);
+               return ERR_PTR(-EIO);
+       }
+
+       /*
+        * Wish we could specify required capabilities rather than create
+        * a domain, see what comes out and hope it doesn't change along
+        * the way.  Fortunately we know interrupt remapping is global for
+        * our iommus.
+        */
+       if (!allow_unsafe_interrupts &&
+           !iommu_domain_has_cap(iommu->domain, IOMMU_CAP_INTR_REMAP)) {
+               pr_warn("%s: No interrupt remapping support.  Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n",
+                      __func__);
+               iommu_domain_free(iommu->domain);
+               kfree(iommu);
+               return ERR_PTR(-EPERM);
+       }
+
+       return iommu;
+}
+
+static void vfio_iommu_type1_release(void *iommu_data)
+{
+       struct vfio_iommu *iommu = iommu_data;
+       struct vfio_group *group, *group_tmp;
+       struct vfio_dma *dma, *dma_tmp;
+
+       list_for_each_entry_safe(group, group_tmp, &iommu->group_list, next) {
+               iommu_detach_group(iommu->domain, group->iommu_group);
+               list_del(&group->next);
+               kfree(group);
+       }
+
+       list_for_each_entry_safe(dma, dma_tmp, &iommu->dma_list, next) {
+               vfio_dma_unmap(iommu, dma->iova, dma->npage, dma->prot);
+               list_del(&dma->next);
+               kfree(dma);
+       }
+
+       iommu_domain_free(iommu->domain);
+       iommu->domain = NULL;
+       kfree(iommu);
+}
+
+static long vfio_iommu_type1_ioctl(void *iommu_data,
+                                  unsigned int cmd, unsigned long arg)
+{
+       struct vfio_iommu *iommu = iommu_data;
+       unsigned long minsz;
+
+       if (cmd == VFIO_CHECK_EXTENSION) {
+               switch (arg) {
+               case VFIO_TYPE1_IOMMU:
+                       return 1;
+               default:
+                       return 0;
+               }
+       } else if (cmd == VFIO_IOMMU_GET_INFO) {
+               struct vfio_iommu_type1_info info;
+
+               minsz = offsetofend(struct vfio_iommu_type1_info, iova_pgsizes);
+
+               if (copy_from_user(&info, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (info.argsz < minsz)
+                       return -EINVAL;
+
+               info.flags = 0;
+
+               info.iova_pgsizes = iommu->domain->ops->pgsize_bitmap;
+
+               return copy_to_user((void __user *)arg, &info, minsz);
+
+       } else if (cmd == VFIO_IOMMU_MAP_DMA) {
+               struct vfio_iommu_type1_dma_map map;
+               uint32_t mask = VFIO_DMA_MAP_FLAG_READ |
+                               VFIO_DMA_MAP_FLAG_WRITE;
+
+               minsz = offsetofend(struct vfio_iommu_type1_dma_map, size);
+
+               if (copy_from_user(&map, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (map.argsz < minsz || map.flags & ~mask)
+                       return -EINVAL;
+
+               return vfio_dma_do_map(iommu, &map);
+
+       } else if (cmd == VFIO_IOMMU_UNMAP_DMA) {
+               struct vfio_iommu_type1_dma_unmap unmap;
+
+               minsz = offsetofend(struct vfio_iommu_type1_dma_unmap, size);
+
+               if (copy_from_user(&unmap, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (unmap.argsz < minsz || unmap.flags)
+                       return -EINVAL;
+
+               return vfio_dma_do_unmap(iommu, &unmap);
+       }
+
+       return -ENOTTY;
+}
+
+static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = {
+       .name           = "vfio-iommu-type1",
+       .owner          = THIS_MODULE,
+       .open           = vfio_iommu_type1_open,
+       .release        = vfio_iommu_type1_release,
+       .ioctl          = vfio_iommu_type1_ioctl,
+       .attach_group   = vfio_iommu_type1_attach_group,
+       .detach_group   = vfio_iommu_type1_detach_group,
+};
+
+static int __init vfio_iommu_type1_init(void)
+{
+       if (!iommu_present(&pci_bus_type))
+               return -ENODEV;
+
+       return vfio_register_iommu_driver(&vfio_iommu_driver_ops_type1);
+}
+
+static void __exit vfio_iommu_type1_cleanup(void)
+{
+       vfio_unregister_iommu_driver(&vfio_iommu_driver_ops_type1);
+}
+
+module_init(vfio_iommu_type1_init);
+module_exit(vfio_iommu_type1_cleanup);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
index b0b2ac335347c7c1c9c311c6569a38139117d823..747442d2c0f69a8207c4ab577ca6a27f21a3d037 100644 (file)
@@ -90,7 +90,8 @@
 #undef DEBUG
 
 #ifdef DEBUG
-#define DBG(fmt, args...)              printk(KERN_DEBUG "aty128fb: %s " fmt, __func__, ##args);
+#define DBG(fmt, args...) \
+       printk(KERN_DEBUG "aty128fb: %s " fmt, __func__, ##args);
 #else
 #define DBG(fmt, args...)
 #endif
@@ -449,8 +450,9 @@ static int aty128_decode_var(struct fb_var_screeninfo *var,
                              struct aty128fb_par *par);
 #if 0
 static void __devinit aty128_get_pllinfo(struct aty128fb_par *par,
-                                     void __iomem *bios);
-static void __devinit __iomem *aty128_map_ROM(struct pci_dev *pdev, const struct aty128fb_par *par);
+                                        void __iomem *bios);
+static void __devinit __iomem *aty128_map_ROM(struct pci_dev *pdev,
+                                             const struct aty128fb_par *par);
 #endif
 static void aty128_timings(struct aty128fb_par *par);
 static void aty128_init_engine(struct aty128fb_par *par);
@@ -779,7 +781,8 @@ static u32 depth_to_dst(u32 depth)
 
 
 #ifndef __sparc__
-static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, struct pci_dev *dev)
+static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par,
+                                              struct pci_dev *dev)
 {
        u16 dptr;
        u8 rom_type;
@@ -811,13 +814,14 @@ static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, s
        /* Look for the PCI data to check the ROM type */
        dptr = BIOS_IN16(0x18);
 
-       /* Check the PCI data signature. If it's wrong, we still assume a normal x86 ROM
-        * for now, until I've verified this works everywhere. The goal here is more
-        * to phase out Open Firmware images.
+       /* Check the PCI data signature. If it's wrong, we still assume a normal
+        * x86 ROM for now, until I've verified this works everywhere.
+        * The goal here is more to phase out Open Firmware images.
         *
-        * Currently, we only look at the first PCI data, we could iteratre and deal with
-        * them all, and we should use fb_bios_start relative to start of image and not
-        * relative start of ROM, but so far, I never found a dual-image ATI card
+        * Currently, we only look at the first PCI data, we could iteratre and
+        * deal with them all, and we should use fb_bios_start relative to start
+        * of image and not relative start of ROM, but so far, I never found a
+        * dual-image ATI card.
         *
         * typedef struct {
         *      u32     signature;      + 0x00
@@ -852,7 +856,8 @@ static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, s
                printk(KERN_INFO "aty128fb: Found HP PA-RISC ROM Image\n");
                goto failed;
        default:
-               printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n", rom_type);
+               printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n",
+                      rom_type);
                goto failed;
        }
  anyway:
@@ -863,7 +868,8 @@ static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, s
        return NULL;
 }
 
-static void __devinit aty128_get_pllinfo(struct aty128fb_par *par, unsigned char __iomem *bios)
+static void __devinit aty128_get_pllinfo(struct aty128fb_par *par,
+                                        unsigned char __iomem *bios)
 {
        unsigned int bios_hdr;
        unsigned int bios_pll;
@@ -1247,10 +1253,13 @@ static int aty128_crtc_to_var(const struct aty128_crtc *crtc,
 static void aty128_set_crt_enable(struct aty128fb_par *par, int on)
 {
        if (on) {
-               aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) | CRT_CRTC_ON);
-               aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) | DAC_PALETTE2_SNOOP_EN));
+               aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) |
+                           CRT_CRTC_ON);
+               aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) |
+                           DAC_PALETTE2_SNOOP_EN));
        } else
-               aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) & ~CRT_CRTC_ON);
+               aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) &
+                           ~CRT_CRTC_ON);
 }
 
 static void aty128_set_lcd_enable(struct aty128fb_par *par, int on)
@@ -1281,7 +1290,8 @@ static void aty128_set_lcd_enable(struct aty128fb_par *par, int on)
        }
 }
 
-static void aty128_set_pll(struct aty128_pll *pll, const struct aty128fb_par *par)
+static void aty128_set_pll(struct aty128_pll *pll,
+                          const struct aty128fb_par *par)
 {
        u32 div3;
 
@@ -1366,7 +1376,8 @@ static int aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll,
 }
 
 
-static int aty128_pll_to_var(const struct aty128_pll *pll, struct fb_var_screeninfo *var)
+static int aty128_pll_to_var(const struct aty128_pll *pll,
+                            struct fb_var_screeninfo *var)
 {
        var->pixclock = 100000000 / pll->vclk;
 
@@ -1512,7 +1523,8 @@ static int aty128fb_set_par(struct fb_info *info)
  *  encode/decode the User Defined Part of the Display
  */
 
-static int aty128_decode_var(struct fb_var_screeninfo *var, struct aty128fb_par *par)
+static int aty128_decode_var(struct fb_var_screeninfo *var,
+                            struct aty128fb_par *par)
 {
        int err;
        struct aty128_crtc crtc;
@@ -1559,7 +1571,8 @@ static int aty128_encode_var(struct fb_var_screeninfo *var,
 }           
 
 
-static int aty128fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+static int aty128fb_check_var(struct fb_var_screeninfo *var,
+                             struct fb_info *info)
 {
        struct aty128fb_par par;
        int err;
@@ -1575,7 +1588,8 @@ static int aty128fb_check_var(struct fb_var_screeninfo *var, struct fb_info *inf
 /*
  *  Pan or Wrap the Display
  */
-static int aty128fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fb) 
+static int aty128fb_pan_display(struct fb_var_screeninfo *var,
+                               struct fb_info *fb)
 {
        struct aty128fb_par *par = fb->par;
        u32 xoffset, yoffset;
@@ -1594,7 +1608,8 @@ static int aty128fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *f
        par->crtc.xoffset = xoffset;
        par->crtc.yoffset = yoffset;
 
-       offset = ((yoffset * par->crtc.vxres + xoffset)*(par->crtc.bpp >> 3)) & ~7;
+       offset = ((yoffset * par->crtc.vxres + xoffset) * (par->crtc.bpp >> 3))
+                                                                         & ~7;
 
        if (par->crtc.bpp == 24)
                offset += 8 * (offset % 3); /* Must be multiple of 8 and 3 */
@@ -1620,11 +1635,13 @@ static void aty128_st_pal(u_int regno, u_int red, u_int green, u_int blue,
                 * do mirroring
                 */
 
-               aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PALETTE_ACCESS_CNTL);
+               aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) |
+                           DAC_PALETTE_ACCESS_CNTL);
                aty_st_8(PALETTE_INDEX, regno);
                aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue);
 #endif
-               aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & ~DAC_PALETTE_ACCESS_CNTL);
+               aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) &
+                           ~DAC_PALETTE_ACCESS_CNTL);
        }
 
        aty_st_8(PALETTE_INDEX, regno);
@@ -1753,7 +1770,8 @@ static int aty128_bl_update_status(struct backlight_device *bd)
                        aty_st_le32(LVDS_GEN_CNTL, reg);
                }
                reg &= ~LVDS_BL_MOD_LEVEL_MASK;
-               reg |= (aty128_bl_get_level_brightness(par, level) << LVDS_BL_MOD_LEVEL_SHIFT);
+               reg |= (aty128_bl_get_level_brightness(par, level) <<
+                       LVDS_BL_MOD_LEVEL_SHIFT);
 #ifdef BACKLIGHT_LVDS_OFF
                reg |= LVDS_ON | LVDS_EN;
                reg &= ~LVDS_DISPLAY_DIS;
@@ -1764,7 +1782,8 @@ static int aty128_bl_update_status(struct backlight_device *bd)
 #endif
        } else {
                reg &= ~LVDS_BL_MOD_LEVEL_MASK;
-               reg |= (aty128_bl_get_level_brightness(par, 0) << LVDS_BL_MOD_LEVEL_SHIFT);
+               reg |= (aty128_bl_get_level_brightness(par, 0) <<
+                       LVDS_BL_MOD_LEVEL_SHIFT);
 #ifdef BACKLIGHT_LVDS_OFF
                reg |= LVDS_DISPLAY_DIS;
                aty_st_le32(LVDS_GEN_CNTL, reg);
@@ -1869,7 +1888,8 @@ static void aty128_early_resume(void *data)
 }
 #endif /* CONFIG_PPC_PMAC */
 
-static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int __devinit aty128_init(struct pci_dev *pdev,
+                                const struct pci_device_id *ent)
 {
        struct fb_info *info = pci_get_drvdata(pdev);
        struct aty128fb_par *par = info->par;
@@ -1887,7 +1907,8 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i
 
        /* range check to make sure */
        if (ent->driver_data < ARRAY_SIZE(r128_family))
-           strlcat(video_card, r128_family[ent->driver_data], sizeof(video_card));
+               strlcat(video_card, r128_family[ent->driver_data],
+                       sizeof(video_card));
 
        printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev);
 
@@ -1911,11 +1932,11 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i
                /* Indicate sleep capability */
                if (par->chip_gen == rage_M3) {
                        pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, NULL, 0, 1);
-#if 0 /* Disable the early video resume hack for now as it's causing problems, among
-       * others we now rely on the PCI core restoring the config space for us, which
-       * isn't the case with that hack, and that code path causes various things to
-       * be called with interrupts off while they shouldn't. I'm leaving the code in
-       * as it can be useful for debugging purposes
+#if 0 /* Disable the early video resume hack for now as it's causing problems,
+       * among others we now rely on the PCI core restoring the config space
+       * for us, which isn't the case with that hack, and that code path causes
+       * various things to be called with interrupts off while they shouldn't.
+       * I'm leaving the code in as it can be useful for debugging purposes
        */
                        pmac_set_early_video_resume(aty128_early_resume, par);
 #endif
@@ -1953,11 +1974,11 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i
                                default_vmode = VMODE_1152_768_60;
        
                        if (default_cmode > 16) 
-                           default_cmode = CMODE_32;
+                               default_cmode = CMODE_32;
                        else if (default_cmode > 8) 
-                           default_cmode = CMODE_16;
+                               default_cmode = CMODE_16;
                        else 
-                           default_cmode = CMODE_8;
+                               default_cmode = CMODE_8;
 
                        if (mac_vmode_to_var(default_vmode, default_cmode, &var))
                                var = default_var;
@@ -2018,7 +2039,8 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i
 
 #ifdef CONFIG_PCI
 /* register a card    ++ajoshi */
-static int __devinit aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int __devinit aty128_probe(struct pci_dev *pdev,
+                                 const struct pci_device_id *ent)
 {
        unsigned long fb_addr, reg_addr;
        struct aty128fb_par *par;
@@ -2318,39 +2340,39 @@ static inline void aty128_rectcopy(int srcx, int srcy, int dstx, int dsty,
                                   u_int width, u_int height,
                                   struct fb_info_aty128 *par)
 {
-    u32 save_dp_datatype, save_dp_cntl, dstval;
-
-    if (!width || !height)
-        return;
-
-    dstval = depth_to_dst(par->current_par.crtc.depth);
-    if (dstval == DST_24BPP) {
-        srcx *= 3;
-        dstx *= 3;
-        width *= 3;
-    } else if (dstval == -EINVAL) {
-        printk("aty128fb: invalid depth or RGBA\n");
-        return;
-    }
-
-    wait_for_fifo(2, par);
-    save_dp_datatype = aty_ld_le32(DP_DATATYPE);
-    save_dp_cntl     = aty_ld_le32(DP_CNTL);
-
-    wait_for_fifo(6, par);
-    aty_st_le32(SRC_Y_X, (srcy << 16) | srcx);
-    aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT);
-    aty_st_le32(DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
-    aty_st_le32(DP_DATATYPE, save_dp_datatype | dstval | SRC_DSTCOLOR);
-
-    aty_st_le32(DST_Y_X, (dsty << 16) | dstx);
-    aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width);
-
-    par->blitter_may_be_busy = 1;
-
-    wait_for_fifo(2, par);
-    aty_st_le32(DP_DATATYPE, save_dp_datatype);
-    aty_st_le32(DP_CNTL, save_dp_cntl); 
+       u32 save_dp_datatype, save_dp_cntl, dstval;
+
+       if (!width || !height)
+               return;
+
+       dstval = depth_to_dst(par->current_par.crtc.depth);
+       if (dstval == DST_24BPP) {
+               srcx *= 3;
+               dstx *= 3;
+               width *= 3;
+       } else if (dstval == -EINVAL) {
+               printk("aty128fb: invalid depth or RGBA\n");
+               return;
+       }
+
+       wait_for_fifo(2, par);
+       save_dp_datatype = aty_ld_le32(DP_DATATYPE);
+       save_dp_cntl     = aty_ld_le32(DP_CNTL);
+
+       wait_for_fifo(6, par);
+       aty_st_le32(SRC_Y_X, (srcy << 16) | srcx);
+       aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT);
+       aty_st_le32(DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
+       aty_st_le32(DP_DATATYPE, save_dp_datatype | dstval | SRC_DSTCOLOR);
+
+       aty_st_le32(DST_Y_X, (dsty << 16) | dstx);
+       aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width);
+
+       par->blitter_may_be_busy = 1;
+
+       wait_for_fifo(2, par);
+       aty_st_le32(DP_DATATYPE, save_dp_datatype);
+       aty_st_le32(DP_CNTL, save_dp_cntl);
 }
 
 
@@ -2358,17 +2380,17 @@ static inline void aty128_rectcopy(int srcx, int srcy, int dstx, int dsty,
      * Text mode accelerated functions
      */
 
-static void fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy, int dx,
-                       int height, int width)
+static void fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy,
+                              int dx, int height, int width)
 {
-    sx     *= fontwidth(p);
-    sy     *= fontheight(p);
-    dx     *= fontwidth(p);
-    dy     *= fontheight(p);
-    width  *= fontwidth(p);
-    height *= fontheight(p);
-
-    aty128_rectcopy(sx, sy, dx, dy, width, height,
+       sx     *= fontwidth(p);
+       sy     *= fontheight(p);
+       dx     *= fontwidth(p);
+       dy     *= fontheight(p);
+       width  *= fontwidth(p);
+       height *= fontheight(p);
+
+       aty128_rectcopy(sx, sy, dx, dy, width, height,
                        (struct fb_info_aty128 *)p->fb_info);
 }
 #endif /* 0 */
index 2979292650d6494a5fddef3f3835da5931f37ec0..cf282763a8dc941ab96554ed1cae855d959199de 100644 (file)
@@ -245,7 +245,7 @@ config BACKLIGHT_CARILLO_RANCH
 
 config BACKLIGHT_PWM
        tristate "Generic PWM based Backlight Driver"
-       depends on HAVE_PWM
+       depends on PWM
        help
          If you have a LCD backlight adjustable by PWM, say Y to enable
          this driver.
index 0443a4f718580c4381acf7d5cb538e941b7e2aa5..df1cbb7ef6ca0370551904b190ba968315ee92c7 100644 (file)
@@ -127,7 +127,8 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev)
        struct atmel_pwm_bl *pwmbl;
        int retval;
 
-       pwmbl = kzalloc(sizeof(struct atmel_pwm_bl), GFP_KERNEL);
+       pwmbl = devm_kzalloc(&pdev->dev, sizeof(struct atmel_pwm_bl),
+                               GFP_KERNEL);
        if (!pwmbl)
                return -ENOMEM;
 
@@ -154,7 +155,8 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev)
                goto err_free_mem;
 
        if (pwmbl->gpio_on != -1) {
-               retval = gpio_request(pwmbl->gpio_on, "gpio_atmel_pwm_bl");
+               retval = devm_gpio_request(&pdev->dev, pwmbl->gpio_on,
+                                       "gpio_atmel_pwm_bl");
                if (retval) {
                        pwmbl->gpio_on = -1;
                        goto err_free_pwm;
@@ -164,7 +166,7 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev)
                retval = gpio_direction_output(pwmbl->gpio_on,
                                0 ^ pdata->on_active_low);
                if (retval)
-                       goto err_free_gpio;
+                       goto err_free_pwm;
        }
 
        memset(&props, 0, sizeof(struct backlight_properties));
@@ -174,7 +176,7 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev)
                                          &atmel_pwm_bl_ops, &props);
        if (IS_ERR(bldev)) {
                retval = PTR_ERR(bldev);
-               goto err_free_gpio;
+               goto err_free_pwm;
        }
 
        pwmbl->bldev = bldev;
@@ -196,13 +198,9 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev)
 err_free_bl_dev:
        platform_set_drvdata(pdev, NULL);
        backlight_device_unregister(bldev);
-err_free_gpio:
-       if (pwmbl->gpio_on != -1)
-               gpio_free(pwmbl->gpio_on);
 err_free_pwm:
        pwm_channel_free(&pwmbl->pwmc);
 err_free_mem:
-       kfree(pwmbl);
        return retval;
 }
 
@@ -210,15 +208,12 @@ static int __exit atmel_pwm_bl_remove(struct platform_device *pdev)
 {
        struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev);
 
-       if (pwmbl->gpio_on != -1) {
+       if (pwmbl->gpio_on != -1)
                gpio_set_value(pwmbl->gpio_on, 0);
-               gpio_free(pwmbl->gpio_on);
-       }
        pwm_channel_disable(&pwmbl->pwmc);
        pwm_channel_free(&pwmbl->pwmc);
        backlight_device_unregister(pwmbl->bldev);
        platform_set_drvdata(pdev, NULL);
-       kfree(pwmbl);
 
        return 0;
 }
index 23d732677ba177e6594818dbc98bf17fb5ada229..c781768ba8923d61d4350e4a4181ede8a0808185 100644 (file)
@@ -492,7 +492,8 @@ static int setup_gpio_backlight(struct corgi_lcd *lcd,
        lcd->gpio_backlight_cont = -1;
 
        if (gpio_is_valid(pdata->gpio_backlight_on)) {
-               err = gpio_request(pdata->gpio_backlight_on, "BL_ON");
+               err = devm_gpio_request(&spi->dev, pdata->gpio_backlight_on,
+                                       "BL_ON");
                if (err) {
                        dev_err(&spi->dev, "failed to request GPIO%d for "
                                "backlight_on\n", pdata->gpio_backlight_on);
@@ -504,11 +505,12 @@ static int setup_gpio_backlight(struct corgi_lcd *lcd,
        }
 
        if (gpio_is_valid(pdata->gpio_backlight_cont)) {
-               err = gpio_request(pdata->gpio_backlight_cont, "BL_CONT");
+               err = devm_gpio_request(&spi->dev, pdata->gpio_backlight_cont,
+                                       "BL_CONT");
                if (err) {
                        dev_err(&spi->dev, "failed to request GPIO%d for "
                                "backlight_cont\n", pdata->gpio_backlight_cont);
-                       goto err_free_backlight_on;
+                       return err;
                }
 
                lcd->gpio_backlight_cont = pdata->gpio_backlight_cont;
@@ -525,11 +527,6 @@ static int setup_gpio_backlight(struct corgi_lcd *lcd,
                }
        }
        return 0;
-
-err_free_backlight_on:
-       if (gpio_is_valid(lcd->gpio_backlight_on))
-               gpio_free(lcd->gpio_backlight_on);
-       return err;
 }
 
 static int __devinit corgi_lcd_probe(struct spi_device *spi)
@@ -602,12 +599,6 @@ static int __devexit corgi_lcd_remove(struct spi_device *spi)
        backlight_update_status(lcd->bl_dev);
        backlight_device_unregister(lcd->bl_dev);
 
-       if (gpio_is_valid(lcd->gpio_backlight_on))
-               gpio_free(lcd->gpio_backlight_on);
-
-       if (gpio_is_valid(lcd->gpio_backlight_cont))
-               gpio_free(lcd->gpio_backlight_cont);
-
        corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
        lcd_device_unregister(lcd->lcd_dev);
 
index 40f606a860934a7bb21fab7fb60b457a390e1c2a..2d90c0648aa056f09992b23d4f70dde5b536137f 100644 (file)
@@ -175,28 +175,27 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
 
        priv->spi = spi;
 
-       ret = gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH,
-                                               "lcd l4f00242t03 reset");
+       ret = devm_gpio_request_one(&spi->dev, pdata->reset_gpio,
+                       GPIOF_OUT_INIT_HIGH, "lcd l4f00242t03 reset");
        if (ret) {
                dev_err(&spi->dev,
                        "Unable to get the lcd l4f00242t03 reset gpio.\n");
                return ret;
        }
 
-       ret = gpio_request_one(pdata->data_enable_gpio, GPIOF_OUT_INIT_LOW,
-                                               "lcd l4f00242t03 data enable");
+       ret = devm_gpio_request_one(&spi->dev, pdata->data_enable_gpio,
+                       GPIOF_OUT_INIT_LOW, "lcd l4f00242t03 data enable");
        if (ret) {
                dev_err(&spi->dev,
                        "Unable to get the lcd l4f00242t03 data en gpio.\n");
-               goto err;
+               return ret;
        }
 
        priv->io_reg = regulator_get(&spi->dev, "vdd");
        if (IS_ERR(priv->io_reg)) {
-               ret = PTR_ERR(priv->io_reg);
                dev_err(&spi->dev, "%s: Unable to get the IO regulator\n",
                       __func__);
-               goto err2;
+               return PTR_ERR(priv->io_reg);
        }
 
        priv->core_reg = regulator_get(&spi->dev, "vcore");
@@ -204,14 +203,14 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
                ret = PTR_ERR(priv->core_reg);
                dev_err(&spi->dev, "%s: Unable to get the core regulator\n",
                       __func__);
-               goto err3;
+               goto err1;
        }
 
        priv->ld = lcd_device_register("l4f00242t03",
                                        &spi->dev, priv, &l4f_ops);
        if (IS_ERR(priv->ld)) {
                ret = PTR_ERR(priv->ld);
-               goto err4;
+               goto err2;
        }
 
        /* Init the LCD */
@@ -223,14 +222,10 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
 
        return 0;
 
-err4:
+err2:
        regulator_put(priv->core_reg);
-err3:
+err1:
        regulator_put(priv->io_reg);
-err2:
-       gpio_free(pdata->data_enable_gpio);
-err:
-       gpio_free(pdata->reset_gpio);
 
        return ret;
 }
@@ -238,16 +233,12 @@ err:
 static int __devexit l4f00242t03_remove(struct spi_device *spi)
 {
        struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev);
-       struct l4f00242t03_pdata *pdata = priv->spi->dev.platform_data;
 
        l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN);
        lcd_device_unregister(priv->ld);
 
        dev_set_drvdata(&spi->dev, NULL);
 
-       gpio_free(pdata->data_enable_gpio);
-       gpio_free(pdata->reset_gpio);
-
        regulator_put(priv->io_reg);
        regulator_put(priv->core_reg);
 
index bebeb63607db214e7e679732d2277ac686a015fe..18dca0c29c6836456954a83e97b76fa24625db19 100644 (file)
@@ -295,7 +295,7 @@ static int __devinit lm3533_bl_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       bl = kzalloc(sizeof(*bl), GFP_KERNEL);
+       bl = devm_kzalloc(&pdev->dev, sizeof(*bl), GFP_KERNEL);
        if (!bl) {
                dev_err(&pdev->dev,
                                "failed to allocate memory for backlight\n");
@@ -317,8 +317,7 @@ static int __devinit lm3533_bl_probe(struct platform_device *pdev)
                                                &lm3533_bl_ops, &props);
        if (IS_ERR(bd)) {
                dev_err(&pdev->dev, "failed to register backlight device\n");
-               ret = PTR_ERR(bd);
-               goto err_free;
+               return PTR_ERR(bd);
        }
 
        bl->bd = bd;
@@ -348,8 +347,6 @@ err_sysfs_remove:
        sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
 err_unregister:
        backlight_device_unregister(bd);
-err_free:
-       kfree(bl);
 
        return ret;
 }
@@ -367,7 +364,6 @@ static int __devexit lm3533_bl_remove(struct platform_device *pdev)
        lm3533_ctrlbank_disable(&bl->cb);
        sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
        backlight_device_unregister(bd);
-       kfree(bl);
 
        return 0;
 }
index a9f2c36966f1ff60fcf0a8873c1d01f93017c500..ea43f2254196b65982875bd420d9c16320d2457f 100644 (file)
@@ -158,29 +158,27 @@ static int __devinit lms283gf05_probe(struct spi_device *spi)
        int ret = 0;
 
        if (pdata != NULL) {
-               ret = gpio_request(pdata->reset_gpio, "LMS285GF05 RESET");
+               ret = devm_gpio_request(&spi->dev, pdata->reset_gpio,
+                                       "LMS285GF05 RESET");
                if (ret)
                        return ret;
 
                ret = gpio_direction_output(pdata->reset_gpio,
                                                !pdata->reset_inverted);
                if (ret)
-                       goto err;
+                       return ret;
        }
 
        st = devm_kzalloc(&spi->dev, sizeof(struct lms283gf05_state),
                                GFP_KERNEL);
        if (st == NULL) {
                dev_err(&spi->dev, "No memory for device state\n");
-               ret = -ENOMEM;
-               goto err;
+               return -ENOMEM;
        }
 
        ld = lcd_device_register("lms283gf05", &spi->dev, st, &lms_ops);
-       if (IS_ERR(ld)) {
-               ret = PTR_ERR(ld);
-               goto err;
-       }
+       if (IS_ERR(ld))
+               return PTR_ERR(ld);
 
        st->spi = spi;
        st->ld = ld;
@@ -193,24 +191,14 @@ static int __devinit lms283gf05_probe(struct spi_device *spi)
        lms283gf05_toggle(spi, disp_initseq, ARRAY_SIZE(disp_initseq));
 
        return 0;
-
-err:
-       if (pdata != NULL)
-               gpio_free(pdata->reset_gpio);
-
-       return ret;
 }
 
 static int __devexit lms283gf05_remove(struct spi_device *spi)
 {
        struct lms283gf05_state *st = dev_get_drvdata(&spi->dev);
-       struct lms283gf05_pdata *pdata = st->spi->dev.platform_data;
 
        lcd_device_unregister(st->ld);
 
-       if (pdata != NULL)
-               gpio_free(pdata->reset_gpio);
-
        return 0;
 }
 
index 72a0e0c917cf3246ecf21d0b2bed41b9c3b502b3..aa6d4f71131f5b6bb22fba425bc04d5caa726a95 100644 (file)
 #include <linux/i2c.h>
 #include <linux/backlight.h>
 #include <linux/err.h>
-#include <linux/lp855x.h>
+#include <linux/platform_data/lp855x.h>
 
 /* Registers */
-#define BRIGHTNESS_CTRL                (0x00)
-#define DEVICE_CTRL            (0x01)
+#define BRIGHTNESS_CTRL                0x00
+#define DEVICE_CTRL            0x01
+#define EEPROM_START           0xA0
+#define EEPROM_END             0xA7
+#define EPROM_START            0xA0
+#define EPROM_END              0xAF
 
 #define BUF_SIZE               20
 #define DEFAULT_BL_NAME                "lcd-backlight"
index f519d55a294c5cdb97cc7b53ff4b81f1cc32d49c..469cf0f109d2a501a9d3d9aafb0ebecf0464f299 100644 (file)
@@ -84,7 +84,8 @@ static int ot200_backlight_probe(struct platform_device *pdev)
        int retval = 0;
 
        /* request gpio */
-       if (gpio_request(GPIO_DIMM, "ot200 backlight dimmer") < 0) {
+       if (devm_gpio_request(&pdev->dev, GPIO_DIMM,
+                               "ot200 backlight dimmer") < 0) {
                dev_err(&pdev->dev, "failed to request GPIO %d\n", GPIO_DIMM);
                return -ENODEV;
        }
@@ -93,14 +94,13 @@ static int ot200_backlight_probe(struct platform_device *pdev)
        pwm_timer = cs5535_mfgpt_alloc_timer(7, MFGPT_DOMAIN_ANY);
        if (!pwm_timer) {
                dev_err(&pdev->dev, "MFGPT 7 not available\n");
-               retval = -ENODEV;
-               goto error_mfgpt_alloc;
+               return -ENODEV;
        }
 
-       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
        if (!data) {
                retval = -ENOMEM;
-               goto error_kzalloc;
+               goto error_devm_kzalloc;
        }
 
        /* setup gpio */
@@ -122,26 +122,21 @@ static int ot200_backlight_probe(struct platform_device *pdev)
        if (IS_ERR(bl)) {
                dev_err(&pdev->dev, "failed to register backlight\n");
                retval = PTR_ERR(bl);
-               goto error_backlight_device_register;
+               goto error_devm_kzalloc;
        }
 
        platform_set_drvdata(pdev, bl);
 
        return 0;
 
-error_backlight_device_register:
-       kfree(data);
-error_kzalloc:
+error_devm_kzalloc:
        cs5535_mfgpt_free_timer(pwm_timer);
-error_mfgpt_alloc:
-       gpio_free(GPIO_DIMM);
        return retval;
 }
 
 static int ot200_backlight_remove(struct platform_device *pdev)
 {
        struct backlight_device *bl = platform_get_drvdata(pdev);
-       struct ot200_backlight_data *data = bl_get_data(bl);
 
        backlight_device_unregister(bl);
 
@@ -152,9 +147,7 @@ static int ot200_backlight_remove(struct platform_device *pdev)
                MAX_COMP2 - dim_table[100]);
 
        cs5535_mfgpt_free_timer(pwm_timer);
-       gpio_free(GPIO_DIMM);
 
-       kfree(data);
        return 0;
 }
 
index 342b7d7cbb632826611f856a7eaa578220d16681..995f0164c9b082c7da2836123adfcb2b7f10a6c7 100644 (file)
@@ -26,11 +26,13 @@ struct pwm_bl_data {
        struct device           *dev;
        unsigned int            period;
        unsigned int            lth_brightness;
+       unsigned int            *levels;
        int                     (*notify)(struct device *,
                                          int brightness);
        void                    (*notify_after)(struct device *,
                                        int brightness);
        int                     (*check_fb)(struct device *, struct fb_info *);
+       void                    (*exit)(struct device *);
 };
 
 static int pwm_backlight_update_status(struct backlight_device *bl)
@@ -52,9 +54,18 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
                pwm_config(pb->pwm, 0, pb->period);
                pwm_disable(pb->pwm);
        } else {
-               brightness = pb->lth_brightness +
-                       (brightness * (pb->period - pb->lth_brightness) / max);
-               pwm_config(pb->pwm, brightness, pb->period);
+               int duty_cycle;
+
+               if (pb->levels) {
+                       duty_cycle = pb->levels[brightness];
+                       max = pb->levels[max];
+               } else {
+                       duty_cycle = brightness;
+               }
+
+               duty_cycle = pb->lth_brightness +
+                    (duty_cycle * (pb->period - pb->lth_brightness) / max);
+               pwm_config(pb->pwm, duty_cycle, pb->period);
                pwm_enable(pb->pwm);
        }
 
@@ -83,17 +94,98 @@ static const struct backlight_ops pwm_backlight_ops = {
        .check_fb       = pwm_backlight_check_fb,
 };
 
+#ifdef CONFIG_OF
+static int pwm_backlight_parse_dt(struct device *dev,
+                                 struct platform_pwm_backlight_data *data)
+{
+       struct device_node *node = dev->of_node;
+       struct property *prop;
+       int length;
+       u32 value;
+       int ret;
+
+       if (!node)
+               return -ENODEV;
+
+       memset(data, 0, sizeof(*data));
+
+       /* determine the number of brightness levels */
+       prop = of_find_property(node, "brightness-levels", &length);
+       if (!prop)
+               return -EINVAL;
+
+       data->max_brightness = length / sizeof(u32);
+
+       /* read brightness levels from DT property */
+       if (data->max_brightness > 0) {
+               size_t size = sizeof(*data->levels) * data->max_brightness;
+
+               data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
+               if (!data->levels)
+                       return -ENOMEM;
+
+               ret = of_property_read_u32_array(node, "brightness-levels",
+                                                data->levels,
+                                                data->max_brightness);
+               if (ret < 0)
+                       return ret;
+
+               ret = of_property_read_u32(node, "default-brightness-level",
+                                          &value);
+               if (ret < 0)
+                       return ret;
+
+               if (value >= data->max_brightness) {
+                       dev_warn(dev, "invalid default brightness level: %u, using %u\n",
+                                value, data->max_brightness - 1);
+                       value = data->max_brightness - 1;
+               }
+
+               data->dft_brightness = value;
+               data->max_brightness--;
+       }
+
+       /*
+        * TODO: Most users of this driver use a number of GPIOs to control
+        *       backlight power. Support for specifying these needs to be
+        *       added.
+        */
+
+       return 0;
+}
+
+static struct of_device_id pwm_backlight_of_match[] = {
+       { .compatible = "pwm-backlight" },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
+#else
+static int pwm_backlight_parse_dt(struct device *dev,
+                                 struct platform_pwm_backlight_data *data)
+{
+       return -ENODEV;
+}
+#endif
+
 static int pwm_backlight_probe(struct platform_device *pdev)
 {
-       struct backlight_properties props;
        struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
+       struct platform_pwm_backlight_data defdata;
+       struct backlight_properties props;
        struct backlight_device *bl;
        struct pwm_bl_data *pb;
+       unsigned int max;
        int ret;
 
        if (!data) {
-               dev_err(&pdev->dev, "failed to find platform data\n");
-               return -EINVAL;
+               ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to find platform data\n");
+                       return ret;
+               }
+
+               data = &defdata;
        }
 
        if (data->init) {
@@ -109,21 +201,42 @@ static int pwm_backlight_probe(struct platform_device *pdev)
                goto err_alloc;
        }
 
-       pb->period = data->pwm_period_ns;
+       if (data->levels) {
+               max = data->levels[data->max_brightness];
+               pb->levels = data->levels;
+       } else
+               max = data->max_brightness;
+
        pb->notify = data->notify;
        pb->notify_after = data->notify_after;
        pb->check_fb = data->check_fb;
-       pb->lth_brightness = data->lth_brightness *
-               (data->pwm_period_ns / data->max_brightness);
+       pb->exit = data->exit;
        pb->dev = &pdev->dev;
 
-       pb->pwm = pwm_request(data->pwm_id, "backlight");
+       pb->pwm = pwm_get(&pdev->dev, NULL);
        if (IS_ERR(pb->pwm)) {
-               dev_err(&pdev->dev, "unable to request PWM for backlight\n");
-               ret = PTR_ERR(pb->pwm);
-               goto err_alloc;
-       } else
-               dev_dbg(&pdev->dev, "got pwm for backlight\n");
+               dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
+
+               pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
+               if (IS_ERR(pb->pwm)) {
+                       dev_err(&pdev->dev, "unable to request legacy PWM\n");
+                       ret = PTR_ERR(pb->pwm);
+                       goto err_alloc;
+               }
+       }
+
+       dev_dbg(&pdev->dev, "got pwm for backlight\n");
+
+       /*
+        * The DT case will set the pwm_period_ns field to 0 and store the
+        * period, parsed from the DT, in the PWM device. For the non-DT case,
+        * set the period from platform data.
+        */
+       if (data->pwm_period_ns > 0)
+               pwm_set_period(pb->pwm, data->pwm_period_ns);
+
+       pb->period = pwm_get_period(pb->pwm);
+       pb->lth_brightness = data->lth_brightness * (pb->period / max);
 
        memset(&props, 0, sizeof(struct backlight_properties));
        props.type = BACKLIGHT_RAW;
@@ -143,7 +256,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
        return 0;
 
 err_bl:
-       pwm_free(pb->pwm);
+       pwm_put(pb->pwm);
 err_alloc:
        if (data->exit)
                data->exit(&pdev->dev);
@@ -152,16 +265,15 @@ err_alloc:
 
 static int pwm_backlight_remove(struct platform_device *pdev)
 {
-       struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
        struct backlight_device *bl = platform_get_drvdata(pdev);
        struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
 
        backlight_device_unregister(bl);
        pwm_config(pb->pwm, 0, pb->period);
        pwm_disable(pb->pwm);
-       pwm_free(pb->pwm);
-       if (data->exit)
-               data->exit(&pdev->dev);
+       pwm_put(pb->pwm);
+       if (pb->exit)
+               pb->exit(&pdev->dev);
        return 0;
 }
 
@@ -195,11 +307,12 @@ static SIMPLE_DEV_PM_OPS(pwm_backlight_pm_ops, pwm_backlight_suspend,
 
 static struct platform_driver pwm_backlight_driver = {
        .driver         = {
-               .name   = "pwm-backlight",
-               .owner  = THIS_MODULE,
+               .name           = "pwm-backlight",
+               .owner          = THIS_MODULE,
 #ifdef CONFIG_PM
-               .pm     = &pwm_backlight_pm_ops,
+               .pm             = &pwm_backlight_pm_ops,
 #endif
+               .of_match_table = of_match_ptr(pwm_backlight_of_match),
        },
        .probe          = pwm_backlight_probe,
        .remove         = pwm_backlight_remove,
index 0d54e607e82d1bd3f86196944d425a8dee40fdb2..49342e1d20beaba317d948219bcea9073499c662 100644 (file)
@@ -92,14 +92,14 @@ static int __devinit tosa_bl_probe(struct i2c_client *client,
 
        data->comadj = sharpsl_param.comadj == -1 ? COMADJ_DEFAULT : sharpsl_param.comadj;
 
-       ret = gpio_request(TOSA_GPIO_BL_C20MA, "backlight");
+       ret = devm_gpio_request(&client->dev, TOSA_GPIO_BL_C20MA, "backlight");
        if (ret) {
                dev_dbg(&data->bl->dev, "Unable to request gpio!\n");
                return ret;
        }
        ret = gpio_direction_output(TOSA_GPIO_BL_C20MA, 0);
        if (ret)
-               goto err_gpio_dir;
+               return ret;
 
        i2c_set_clientdata(client, data);
        data->i2c = client;
@@ -123,8 +123,6 @@ static int __devinit tosa_bl_probe(struct i2c_client *client,
 
 err_reg:
        data->bl = NULL;
-err_gpio_dir:
-       gpio_free(TOSA_GPIO_BL_C20MA);
        return ret;
 }
 
@@ -135,8 +133,6 @@ static int __devexit tosa_bl_remove(struct i2c_client *client)
        backlight_device_unregister(data->bl);
        data->bl = NULL;
 
-       gpio_free(TOSA_GPIO_BL_C20MA);
-
        return 0;
 }
 
index 47823b8efff060b77f1ef4db5bf19554b08e73c7..33047a66cc242baf29a20c7bc0f3ced47682350f 100644 (file)
@@ -193,7 +193,7 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi)
        data->spi = spi;
        dev_set_drvdata(&spi->dev, data);
 
-       ret = gpio_request(TOSA_GPIO_TG_ON, "tg #pwr");
+       ret = devm_gpio_request(&spi->dev, TOSA_GPIO_TG_ON, "tg #pwr");
        if (ret < 0)
                goto err_gpio_tg;
 
@@ -201,7 +201,7 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi)
 
        ret = gpio_direction_output(TOSA_GPIO_TG_ON, 0);
        if (ret < 0)
-               goto err_gpio_dir;
+               goto err_gpio_tg;
 
        mdelay(60);
        tosa_lcd_tg_init(data);
@@ -221,8 +221,6 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi)
 
 err_register:
        tosa_lcd_tg_off(data);
-err_gpio_dir:
-       gpio_free(TOSA_GPIO_TG_ON);
 err_gpio_tg:
        dev_set_drvdata(&spi->dev, NULL);
        return ret;
@@ -239,7 +237,6 @@ static int __devexit tosa_lcd_remove(struct spi_device *spi)
 
        tosa_lcd_tg_off(data);
 
-       gpio_free(TOSA_GPIO_TG_ON);
        dev_set_drvdata(&spi->dev, NULL);
 
        return 0;
index 47118c75a4c07fc2190601db42d9942ad7030053..7ae9d53f2bf16bf7ca150eba98a997c1aa1afd6a 100644 (file)
 #include <linux/clk.h>
 #include <linux/cpufreq.h>
 #include <linux/console.h>
+#include <linux/spinlock.h>
 #include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/lcm.h>
 #include <video/da8xx-fb.h>
 #include <asm/div64.h>
 
@@ -160,6 +163,13 @@ struct da8xx_fb_par {
        wait_queue_head_t       vsync_wait;
        int                     vsync_flag;
        int                     vsync_timeout;
+       spinlock_t              lock_for_chan_update;
+
+       /*
+        * LCDC has 2 ping pong DMA channels, channel 0
+        * and channel 1.
+        */
+       unsigned int            which_dma_channel_done;
 #ifdef CONFIG_CPU_FREQ
        struct notifier_block   freq_transition;
        unsigned int            lcd_fck_rate;
@@ -260,10 +270,18 @@ static inline void lcd_enable_raster(void)
 {
        u32 reg;
 
+       /* Put LCDC in reset for several cycles */
+       if (lcd_revision == LCD_VERSION_2)
+               /* Write 1 to reset LCDC */
+               lcdc_write(LCD_CLK_MAIN_RESET, LCD_CLK_RESET_REG);
+       mdelay(1);
+
        /* Bring LCDC out of reset */
        if (lcd_revision == LCD_VERSION_2)
                lcdc_write(0, LCD_CLK_RESET_REG);
+       mdelay(1);
 
+       /* Above reset sequence doesnot reset register context */
        reg = lcdc_read(LCD_RASTER_CTRL_REG);
        if (!(reg & LCD_RASTER_ENABLE))
                lcdc_write(reg | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
@@ -277,10 +295,6 @@ static inline void lcd_disable_raster(void)
        reg = lcdc_read(LCD_RASTER_CTRL_REG);
        if (reg & LCD_RASTER_ENABLE)
                lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
-
-       if (lcd_revision == LCD_VERSION_2)
-               /* Write 1 to reset LCDC */
-               lcdc_write(LCD_CLK_MAIN_RESET, LCD_CLK_RESET_REG);
 }
 
 static void lcd_blit(int load_mode, struct da8xx_fb_par *par)
@@ -344,8 +358,8 @@ static void lcd_blit(int load_mode, struct da8xx_fb_par *par)
        lcd_enable_raster();
 }
 
-/* Configure the Burst Size of DMA */
-static int lcd_cfg_dma(int burst_size)
+/* Configure the Burst Size and fifo threhold of DMA */
+static int lcd_cfg_dma(int burst_size, int fifo_th)
 {
        u32 reg;
 
@@ -369,6 +383,9 @@ static int lcd_cfg_dma(int burst_size)
        default:
                return -EINVAL;
        }
+
+       reg |= (fifo_th << 8);
+
        lcdc_write(reg, LCD_DMA_CTRL_REG);
 
        return 0;
@@ -670,8 +687,8 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
                lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) &
                        ~LCD_INVERT_PIXEL_CLOCK), LCD_RASTER_TIMING_2_REG);
 
-       /* Configure the DMA burst size. */
-       ret = lcd_cfg_dma(cfg->dma_burst_sz);
+       /* Configure the DMA burst size and fifo threshold. */
+       ret = lcd_cfg_dma(cfg->dma_burst_sz, cfg->fifo_th);
        if (ret < 0)
                return ret;
 
@@ -715,7 +732,6 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
 {
        struct da8xx_fb_par *par = arg;
        u32 stat = lcdc_read(LCD_MASKED_STAT_REG);
-       u32 reg_int;
 
        if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) {
                lcd_disable_raster();
@@ -732,10 +748,8 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
 
                lcdc_write(stat, LCD_MASKED_STAT_REG);
 
-               /* Disable PL completion inerrupt */
-               reg_int = lcdc_read(LCD_INT_ENABLE_CLR_REG) |
-                      (LCD_V2_PL_INT_ENA);
-               lcdc_write(reg_int, LCD_INT_ENABLE_CLR_REG);
+               /* Disable PL completion interrupt */
+               lcdc_write(LCD_V2_PL_INT_ENA, LCD_INT_ENABLE_CLR_REG);
 
                /* Setup and start data loading mode */
                lcd_blit(LOAD_DATA, par);
@@ -743,6 +757,7 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
                lcdc_write(stat, LCD_MASKED_STAT_REG);
 
                if (stat & LCD_END_OF_FRAME0) {
+                       par->which_dma_channel_done = 0;
                        lcdc_write(par->dma_start,
                                   LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
                        lcdc_write(par->dma_end,
@@ -752,6 +767,7 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
                }
 
                if (stat & LCD_END_OF_FRAME1) {
+                       par->which_dma_channel_done = 1;
                        lcdc_write(par->dma_start,
                                   LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
                        lcdc_write(par->dma_end,
@@ -798,6 +814,7 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg)
                lcdc_write(stat, LCD_STAT_REG);
 
                if (stat & LCD_END_OF_FRAME0) {
+                       par->which_dma_channel_done = 0;
                        lcdc_write(par->dma_start,
                                   LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
                        lcdc_write(par->dma_end,
@@ -807,6 +824,7 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg)
                }
 
                if (stat & LCD_END_OF_FRAME1) {
+                       par->which_dma_channel_done = 1;
                        lcdc_write(par->dma_start,
                                   LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
                        lcdc_write(par->dma_end,
@@ -1021,11 +1039,14 @@ static int cfb_blank(int blank, struct fb_info *info)
        par->blank = blank;
        switch (blank) {
        case FB_BLANK_UNBLANK:
+               lcd_enable_raster();
+
                if (par->panel_power_ctrl)
                        par->panel_power_ctrl(1);
-
-               lcd_enable_raster();
                break;
+       case FB_BLANK_NORMAL:
+       case FB_BLANK_VSYNC_SUSPEND:
+       case FB_BLANK_HSYNC_SUSPEND:
        case FB_BLANK_POWERDOWN:
                if (par->panel_power_ctrl)
                        par->panel_power_ctrl(0);
@@ -1052,6 +1073,7 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var,
        struct fb_fix_screeninfo    *fix = &fbi->fix;
        unsigned int end;
        unsigned int start;
+       unsigned long irq_flags;
 
        if (var->xoffset != fbi->var.xoffset ||
                        var->yoffset != fbi->var.yoffset) {
@@ -1069,6 +1091,21 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var,
                        end     = start + fbi->var.yres * fix->line_length - 1;
                        par->dma_start  = start;
                        par->dma_end    = end;
+                       spin_lock_irqsave(&par->lock_for_chan_update,
+                                       irq_flags);
+                       if (par->which_dma_channel_done == 0) {
+                               lcdc_write(par->dma_start,
+                                          LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
+                               lcdc_write(par->dma_end,
+                                          LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG);
+                       } else if (par->which_dma_channel_done == 1) {
+                               lcdc_write(par->dma_start,
+                                          LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
+                               lcdc_write(par->dma_end,
+                                          LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG);
+                       }
+                       spin_unlock_irqrestore(&par->lock_for_chan_update,
+                                       irq_flags);
                }
        }
 
@@ -1114,6 +1151,7 @@ static int __devinit fb_probe(struct platform_device *device)
        struct da8xx_fb_par *par;
        resource_size_t len;
        int ret, i;
+       unsigned long ulcm;
 
        if (fb_pdata == NULL) {
                dev_err(&device->dev, "Can not get platform data\n");
@@ -1209,7 +1247,8 @@ static int __devinit fb_probe(struct platform_device *device)
 
        /* allocate frame buffer */
        par->vram_size = lcdc_info->width * lcdc_info->height * lcd_cfg->bpp;
-       par->vram_size = PAGE_ALIGN(par->vram_size/8);
+       ulcm = lcm((lcdc_info->width * lcd_cfg->bpp)/8, PAGE_SIZE);
+       par->vram_size = roundup(par->vram_size/8, ulcm);
        par->vram_size = par->vram_size * LCD_NUM_BUFFERS;
 
        par->vram_virt = dma_alloc_coherent(NULL,
@@ -1296,6 +1335,8 @@ static int __devinit fb_probe(struct platform_device *device)
        /* initialize the vsync wait queue */
        init_waitqueue_head(&par->vsync_wait);
        par->vsync_timeout = HZ / 5;
+       par->which_dma_channel_done = -1;
+       spin_lock_init(&par->lock_for_chan_update);
 
        /* Register the Frame Buffer  */
        if (register_framebuffer(da8xx_fb_info) < 0) {
@@ -1382,11 +1423,12 @@ static int fb_resume(struct platform_device *dev)
        struct da8xx_fb_par *par = info->par;
 
        console_lock();
+       clk_enable(par->lcdc_clk);
+       lcd_enable_raster();
+
        if (par->panel_power_ctrl)
                par->panel_power_ctrl(1);
 
-       clk_enable(par->lcdc_clk);
-       lcd_enable_raster();
        fb_set_suspend(info, 0);
        console_unlock();
 
index a268cbf1cbeac90aee2f34406c1eac844ae094f0..68b9b511ce80257761a75f5297fcacc0795e283b 100644 (file)
@@ -477,11 +477,11 @@ static __init unsigned int get_fb_size(struct fb_info *info)
        return size;
 }
 
-static int epson1355_width_tab[2][4] __initdata =
+static int epson1355_width_tab[2][4] __devinitdata =
     { {4, 8, 16, -1}, {9, 12, 16, -1} };
-static int epson1355_bpp_tab[8] __initdata = { 1, 2, 4, 8, 15, 16 };
+static int epson1355_bpp_tab[8] __devinitdata = { 1, 2, 4, 8, 15, 16 };
 
-static void __init fetch_hw_state(struct fb_info *info, struct epson1355_par *par)
+static void __devinit fetch_hw_state(struct fb_info *info, struct epson1355_par *par)
 {
        struct fb_var_screeninfo *var = &info->var;
        struct fb_fix_screeninfo *fix = &info->fix;
@@ -601,7 +601,7 @@ static int epson1355fb_remove(struct platform_device *dev)
        return 0;
 }
 
-int __devinit epson1355fb_probe(struct platform_device *dev)
+static int __devinit epson1355fb_probe(struct platform_device *dev)
 {
        struct epson1355_par *default_par;
        struct fb_info *info;
index a36b2d28280edfb14c90c9491322fcd00e4eb9e5..c6c016a506ce4e5799bc2582287ae39efeb787c7 100644 (file)
@@ -47,7 +47,7 @@ static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
 
        exynos_dp_init_hpd(dp);
 
-       udelay(200);
+       usleep_range(200, 210);
 
        while (exynos_dp_get_plug_in_status(dp) != 0) {
                timeout_loop++;
@@ -55,7 +55,7 @@ static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
                        dev_err(dp->dev, "failed to get hpd plug status\n");
                        return -ETIMEDOUT;
                }
-               udelay(10);
+               usleep_range(10, 11);
        }
 
        return 0;
@@ -304,7 +304,7 @@ static void exynos_dp_link_start(struct exynos_dp_device *dp)
                buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 |
                            DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0;
        exynos_dp_write_bytes_to_dpcd(dp,
-               DPCD_ADDR_TRAINING_PATTERN_SET,
+               DPCD_ADDR_TRAINING_LANE0_SET,
                lane_count, buf);
 }
 
@@ -336,7 +336,7 @@ static int exynos_dp_channel_eq_ok(u8 link_status[6], int lane_count)
        u8 lane_status;
 
        lane_align = link_status[2];
-       if ((lane_align == DPCD_INTERLANE_ALIGN_DONE) == 0)
+       if ((lane_align & DPCD_INTERLANE_ALIGN_DONE) == 0)
                return -EINVAL;
 
        for (lane = 0; lane < lane_count; lane++) {
@@ -407,6 +407,9 @@ static unsigned int exynos_dp_get_lane_link_training(
        case 3:
                reg = exynos_dp_get_lane3_link_training(dp);
                break;
+       default:
+               WARN_ON(1);
+               return 0;
        }
 
        return reg;
@@ -483,7 +486,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
        u8 pre_emphasis;
        u8 training_lane;
 
-       udelay(100);
+       usleep_range(100, 101);
 
        exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS,
                                6, link_status);
@@ -501,7 +504,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
                buf[0] = DPCD_SCRAMBLING_DISABLED |
                         DPCD_TRAINING_PATTERN_2;
                exynos_dp_write_byte_to_dpcd(dp,
-                       DPCD_ADDR_TRAINING_LANE0_SET,
+                       DPCD_ADDR_TRAINING_PATTERN_SET,
                        buf[0]);
 
                for (lane = 0; lane < lane_count; lane++) {
@@ -568,7 +571,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
 
        u8 adjust_request[2];
 
-       udelay(400);
+       usleep_range(400, 401);
 
        exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS,
                                6, link_status);
@@ -736,7 +739,7 @@ static int exynos_dp_set_link_train(struct exynos_dp_device *dp,
                if (retval == 0)
                        break;
 
-               udelay(100);
+               usleep_range(100, 110);
        }
 
        return retval;
@@ -770,7 +773,7 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp,
                        return -ETIMEDOUT;
                }
 
-               udelay(1);
+               usleep_range(1, 2);
        }
 
        /* Set to use the register calculated M/N video */
@@ -804,7 +807,7 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp,
                        return -ETIMEDOUT;
                }
 
-               mdelay(1);
+               usleep_range(1000, 1001);
        }
 
        if (retval != 0)
index 1e0f998e0c9f4c872d132aafa640442a0b2d4189..8526e548c3857a6ea299b457570afd482c964c4c 100644 (file)
@@ -85,10 +85,6 @@ void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype);
 void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype);
 void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count);
 void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count);
-void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype);
-void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype);
-void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count);
-void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count);
 void exynos_dp_enable_enhanced_mode(struct exynos_dp_device *dp, bool enable);
 void exynos_dp_set_training_pattern(struct exynos_dp_device *dp,
                                 enum pattern_set pattern);
index bcb0e3ae1e9d7a708803696cc9fe040fbdb86d0c..2db5b9aa250a067045d65b01aa0a281362db8d0e 100644 (file)
@@ -122,7 +122,7 @@ void exynos_dp_reset(struct exynos_dp_device *dp)
                LS_CLK_DOMAIN_FUNC_EN_N;
        writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
 
-       udelay(20);
+       usleep_range(20, 30);
 
        exynos_dp_lane_swap(dp, 0);
 
@@ -988,7 +988,7 @@ void exynos_dp_reset_macro(struct exynos_dp_device *dp)
        writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST);
 
        /* 10 us is the minimum reset time. */
-       udelay(10);
+       usleep_range(10, 20);
 
        reg &= ~MACRO_RST;
        writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST);
index 9908e75ae761e625dd2944207007c702efce1960..4bc2b8a5dd8b0c41efbda3fa24774e5f51683535 100644 (file)
@@ -154,7 +154,7 @@ static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power)
                if (client_drv && client_drv->power_on)
                        client_drv->power_on(client_dev, 1);
 
-               exynos_mipi_regulator_disable(dsim);
+               exynos_mipi_regulator_enable(dsim);
 
                /* enable MIPI-DSI PHY. */
                if (dsim->pd->phy_enable)
diff --git a/drivers/video/exynos/s6e8ax0.h b/drivers/video/exynos/s6e8ax0.h
deleted file mode 100644 (file)
index 1f1b270..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/* linux/drivers/video/backlight/s6e8ax0.h
- *
- * MIPI-DSI based s6e8ax0 AMOLED LCD Panel definitions.
- *
- * Copyright (c) 2011 Samsung Electronics
- *
- * Inki Dae, <inki.dae@samsung.com>
- * Donghwa Lee <dh09.lee@samsung.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 _S6E8AX0_H
-#define _S6E8AX0_H
-
-extern void s6e8ax0_init(void);
-
-#endif
-
index 1ddeb11659d4db9e0023f07d98e5c7c189e97535..64cda560c488358c9205d66e064657bd6ba7203e 100644 (file)
@@ -104,6 +104,8 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma,
        deferred framebuffer IO. then if userspace touches a page
        again, we repeat the same scheme */
 
+       file_update_time(vma->vm_file);
+
        /* protect against the workqueue changing the page list */
        mutex_lock(&fbdefio->lock);
 
index 04c01faaf7721b71041461f56c6b2032d3085d81..624ee115f129291a2fc3adfe9ff7b4781e19f0ac 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <asm/types.h>
 #include <linux/fb.h>
+#include <linux/bug.h>
 
     /*
      *  Compose two values, using a bitmask as decision value
@@ -41,7 +42,8 @@ pixel_to_pat( u32 bpp, u32 pixel)
        case 32:
                return 0x0000000100000001ul*pixel;
        default:
-               panic("pixel_to_pat(): unsupported pixelformat\n");
+               WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
+               return 0;
     }
 }
 #else
@@ -66,7 +68,8 @@ pixel_to_pat( u32 bpp, u32 pixel)
        case 32:
                return 0x00000001ul*pixel;
        default:
-               panic("pixel_to_pat(): unsupported pixelformat\n");
+               WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
+               return 0;
     }
 }
 #endif
index da066c210923479ea530b90de708f17eacfcd9bc..5245f9a71892e75ad08c8cc0439a030910abfc71 100644 (file)
@@ -354,7 +354,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
         */
        if (fb_get_options("grvga", &options)) {
                retval = -ENODEV;
-               goto err;
+               goto free_fb;
        }
 
        if (!options || !*options)
@@ -370,7 +370,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
                        if (grvga_parse_custom(this_opt, &info->var) < 0) {
                                dev_err(&dev->dev, "Failed to parse custom mode (%s).\n", this_opt);
                                retval = -EINVAL;
-                               goto err1;
+                               goto free_fb;
                        }
                } else if (!strncmp(this_opt, "addr", 4))
                        grvga_fix_addr = simple_strtoul(this_opt + 5, NULL, 16);
@@ -387,10 +387,11 @@ static int __devinit grvga_probe(struct platform_device *dev)
        info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK | FBINFO_HWACCEL_YPAN;
        info->fix.smem_len = grvga_mem_size;
 
-       if (!request_mem_region(dev->resource[0].start, resource_size(&dev->resource[0]), "grlib-svgactrl regs")) {
+       if (!devm_request_mem_region(&dev->dev, dev->resource[0].start,
+                   resource_size(&dev->resource[0]), "grlib-svgactrl regs")) {
                dev_err(&dev->dev, "registers already mapped\n");
                retval = -EBUSY;
-               goto err;
+               goto free_fb;
        }
 
        par->regs = of_ioremap(&dev->resource[0], 0,
@@ -400,14 +401,14 @@ static int __devinit grvga_probe(struct platform_device *dev)
        if (!par->regs) {
                dev_err(&dev->dev, "failed to map registers\n");
                retval = -ENOMEM;
-               goto err1;
+               goto free_fb;
        }
 
        retval = fb_alloc_cmap(&info->cmap, 256, 0);
        if (retval < 0) {
                dev_err(&dev->dev, "failed to allocate mem with fb_alloc_cmap\n");
                retval = -ENOMEM;
-               goto err2;
+               goto unmap_regs;
        }
 
        if (mode_opt) {
@@ -415,7 +416,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
                                      grvga_modedb, sizeof(grvga_modedb), &grvga_modedb[0], 8);
                if (!retval || retval == 4) {
                        retval = -EINVAL;
-                       goto err3;
+                       goto dealloc_cmap;
                }
        }
 
@@ -427,10 +428,11 @@ static int __devinit grvga_probe(struct platform_device *dev)
 
                physical_start = grvga_fix_addr;
 
-               if (!request_mem_region(physical_start, grvga_mem_size, dev->name)) {
+               if (!devm_request_mem_region(&dev->dev, physical_start,
+                                            grvga_mem_size, dev->name)) {
                        dev_err(&dev->dev, "failed to request memory region\n");
                        retval = -ENOMEM;
-                       goto err3;
+                       goto dealloc_cmap;
                }
 
                virtual_start = (unsigned long) ioremap(physical_start, grvga_mem_size);
@@ -438,7 +440,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
                if (!virtual_start) {
                        dev_err(&dev->dev, "error mapping framebuffer memory\n");
                        retval = -ENOMEM;
-                       goto err4;
+                       goto dealloc_cmap;
                }
        } else {        /* Allocate frambuffer memory */
 
@@ -451,7 +453,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
                                "unable to allocate framebuffer memory (%lu bytes)\n",
                                grvga_mem_size);
                        retval = -ENOMEM;
-                       goto err3;
+                       goto dealloc_cmap;
                }
 
                physical_start = dma_map_single(&dev->dev, (void *)virtual_start, grvga_mem_size, DMA_TO_DEVICE);
@@ -484,7 +486,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
        retval = register_framebuffer(info);
        if (retval < 0) {
                dev_err(&dev->dev, "failed to register framebuffer\n");
-               goto err4;
+               goto free_mem;
        }
 
        __raw_writel(physical_start, &par->regs->fb_pos);
@@ -493,21 +495,18 @@ static int __devinit grvga_probe(struct platform_device *dev)
 
        return 0;
 
-err4:
+free_mem:
        dev_set_drvdata(&dev->dev, NULL);
-       if (grvga_fix_addr) {
-               release_mem_region(physical_start, grvga_mem_size);
+       if (grvga_fix_addr)
                iounmap((void *)virtual_start);
-       else
+       else
                kfree((void *)virtual_start);
-err3:
+dealloc_cmap:
        fb_dealloc_cmap(&info->cmap);
-err2:
+unmap_regs:
        of_iounmap(&dev->resource[0], par->regs,
                   resource_size(&dev->resource[0]));
-err1:
-       release_mem_region(dev->resource[0].start, resource_size(&dev->resource[0]));
-err:
+free_fb:
        framebuffer_release(info);
 
        return retval;
@@ -524,12 +523,10 @@ static int __devexit grvga_remove(struct platform_device *device)
 
                of_iounmap(&device->resource[0], par->regs,
                           resource_size(&device->resource[0]));
-               release_mem_region(device->resource[0].start, resource_size(&device->resource[0]));
 
-               if (!par->fb_alloced) {
-                       release_mem_region(info->fix.smem_start, info->fix.smem_len);
+               if (!par->fb_alloced)
                        iounmap(info->screen_base);
-               else
+               else
                        kfree((void *)info->screen_base);
 
                framebuffer_release(info);
index eec0d7b748eb9e503ea8a9ffa0d007b3a91ed0c5..c89f8a8d36d2b95c61c76f5c89ff53f8a01ac4e7 100644 (file)
@@ -269,7 +269,7 @@ struct mx3fb_info {
        dma_cookie_t                    cookie;
        struct scatterlist              sg[2];
 
-       u32                             sync;   /* preserve var->sync flags */
+       struct fb_var_screeninfo        cur_var; /* current var info */
 };
 
 static void mx3fb_dma_done(void *);
@@ -698,9 +698,29 @@ static void mx3fb_dma_done(void *arg)
        complete(&mx3_fbi->flip_cmpl);
 }
 
+static bool mx3fb_must_set_par(struct fb_info *fbi)
+{
+       struct mx3fb_info *mx3_fbi = fbi->par;
+       struct fb_var_screeninfo old_var = mx3_fbi->cur_var;
+       struct fb_var_screeninfo new_var = fbi->var;
+
+       if ((fbi->var.activate & FB_ACTIVATE_FORCE) &&
+           (fbi->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW)
+               return true;
+
+       /*
+        * Ignore xoffset and yoffset update,
+        * because pan display handles this case.
+        */
+       old_var.xoffset = new_var.xoffset;
+       old_var.yoffset = new_var.yoffset;
+
+       return !!memcmp(&old_var, &new_var, sizeof(struct fb_var_screeninfo));
+}
+
 static int __set_par(struct fb_info *fbi, bool lock)
 {
-       u32 mem_len;
+       u32 mem_len, cur_xoffset, cur_yoffset;
        struct ipu_di_signal_cfg sig_cfg;
        enum ipu_panel mode = IPU_PANEL_TFT;
        struct mx3fb_info *mx3_fbi = fbi->par;
@@ -780,8 +800,25 @@ static int __set_par(struct fb_info *fbi, bool lock)
        video->out_height       = fbi->var.yres;
        video->out_stride       = fbi->var.xres_virtual;
 
-       if (mx3_fbi->blank == FB_BLANK_UNBLANK)
+       if (mx3_fbi->blank == FB_BLANK_UNBLANK) {
                sdc_enable_channel(mx3_fbi);
+               /*
+                * sg[0] points to fb smem_start address
+                * and is actually active in controller.
+                */
+               mx3_fbi->cur_var.xoffset = 0;
+               mx3_fbi->cur_var.yoffset = 0;
+       }
+
+       /*
+        * Preserve xoffset and yoffest in case they are
+        * inactive in controller as fb is blanked.
+        */
+       cur_xoffset = mx3_fbi->cur_var.xoffset;
+       cur_yoffset = mx3_fbi->cur_var.yoffset;
+       mx3_fbi->cur_var = fbi->var;
+       mx3_fbi->cur_var.xoffset = cur_xoffset;
+       mx3_fbi->cur_var.yoffset = cur_yoffset;
 
        return 0;
 }
@@ -802,7 +839,7 @@ static int mx3fb_set_par(struct fb_info *fbi)
 
        mutex_lock(&mx3_fbi->mutex);
 
-       ret = __set_par(fbi, true);
+       ret = mx3fb_must_set_par(fbi) ? __set_par(fbi, true) : 0;
 
        mutex_unlock(&mx3_fbi->mutex);
 
@@ -901,8 +938,8 @@ static int mx3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
        var->grayscale = 0;
 
        /* Preserve sync flags */
-       var->sync |= mx3_fbi->sync;
-       mx3_fbi->sync |= var->sync;
+       var->sync |= mx3_fbi->cur_var.sync;
+       mx3_fbi->cur_var.sync |= var->sync;
 
        return 0;
 }
@@ -1043,8 +1080,8 @@ static int mx3fb_pan_display(struct fb_var_screeninfo *var,
                return -EINVAL;
        }
 
-       if (fbi->var.xoffset == var->xoffset &&
-           fbi->var.yoffset == var->yoffset)
+       if (mx3_fbi->cur_var.xoffset == var->xoffset &&
+           mx3_fbi->cur_var.yoffset == var->yoffset)
                return 0;       /* No change, do nothing */
 
        y_bottom = var->yoffset;
@@ -1127,6 +1164,8 @@ static int mx3fb_pan_display(struct fb_var_screeninfo *var,
        else
                fbi->var.vmode &= ~FB_VMODE_YWRAP;
 
+       mx3_fbi->cur_var = fbi->var;
+
        mutex_unlock(&mx3_fbi->mutex);
 
        dev_dbg(fbi->device, "Update complete\n");
index ad741c3d1ae1668f985c53e8a1f52df367e0ec59..eaeed4340e04eaf49afd176ec3f95ed2b56d8734 100644 (file)
@@ -487,6 +487,13 @@ static struct omap_video_timings acx_panel_timings = {
        .vfp            = 3,
        .vsw            = 3,
        .vbp            = 4,
+
+       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+
+       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 };
 
 static int acx_panel_probe(struct omap_dss_device *dssdev)
@@ -498,8 +505,7 @@ static int acx_panel_probe(struct omap_dss_device *dssdev)
        struct backlight_properties props;
 
        dev_dbg(&dssdev->dev, "%s\n", __func__);
-       dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                       OMAP_DSS_LCD_IHS;
+
        /* FIXME AC bias ? */
        dssdev->panel.timings = acx_panel_timings;
 
index e42f9dc22123e319cde3eba127b0b57ccc05e910..bc5af2500eb923952a9c7a80aaedfb430ee50974 100644 (file)
 struct panel_config {
        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;
-
        int power_on_delay;
        int power_off_delay;
 
@@ -73,11 +67,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 11,
                        .vfp            = 3,
                        .vbp            = 2,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_LOW,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
                },
-               .acbi                   = 0x0,
-               .acb                    = 0x0,
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                       OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
                .power_on_delay         = 50,
                .power_off_delay        = 100,
                .name                   = "sharp_lq",
@@ -98,11 +94,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 1,
                        .vfp            = 1,
                        .vbp            = 1,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
                },
-               .acbi                   = 0x0,
-               .acb                    = 0x28,
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                               OMAP_DSS_LCD_IHS,
                .power_on_delay         = 50,
                .power_off_delay        = 100,
                .name                   = "sharp_ls",
@@ -123,12 +121,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vfp            = 4,
                        .vsw            = 2,
                        .vbp            = 2,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
                },
-               .acbi                   = 0x0,
-               .acb                    = 0x0,
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                       OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC |
-                                       OMAP_DSS_LCD_ONOFF,
                .power_on_delay         = 0,
                .power_off_delay        = 0,
                .name                   = "toppoly_tdo35s",
@@ -149,11 +148,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vfp            = 4,
                        .vsw            = 10,
                        .vbp            = 12 - 10,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
                },
-               .acbi                   = 0x0,
-               .acb                    = 0x0,
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                               OMAP_DSS_LCD_IHS,
                .power_on_delay         = 0,
                .power_off_delay        = 0,
                .name                   = "samsung_lte430wq_f0c",
@@ -174,11 +175,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 2,
                        .vfp            = 4,
                        .vbp            = 11,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
                },
-               .acbi                   = 0x0,
-               .acb                    = 0x0,
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                               OMAP_DSS_LCD_IHS,
                .power_on_delay         = 0,
                .power_off_delay        = 0,
                .name                   = "seiko_70wvw1tz3",
@@ -199,11 +202,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 10,
                        .vfp            = 2,
                        .vbp            = 2,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_LOW,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
                },
-               .acbi                   = 0x0,
-               .acb                    = 0x0,
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                         OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
                .power_on_delay         = 0,
                .power_off_delay        = 0,
                .name                   = "powertip_ph480272t",
@@ -224,11 +229,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 3,
                        .vfp            = 12,
                        .vbp            = 25,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
                },
-               .acbi                   = 0x0,
-               .acb                    = 0x28,
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                         OMAP_DSS_LCD_IHS,
                .power_on_delay         = 0,
                .power_off_delay        = 0,
                .name                   = "innolux_at070tn83",
@@ -249,9 +256,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 1,
                        .vfp            = 2,
                        .vbp            = 7,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
                },
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                               OMAP_DSS_LCD_IHS,
                .name                   = "nec_nl2432dr22-11b",
        },
 
@@ -270,9 +281,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 1,
                        .vfp            = 1,
                        .vbp            = 1,
-               },
-               .config                 = OMAP_DSS_LCD_TFT,
 
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+               },
                .name                   = "h4",
        },
 
@@ -291,10 +306,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 10,
                        .vfp            = 2,
                        .vbp            = 2,
-               },
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                               OMAP_DSS_LCD_IHS,
 
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+               },
                .name                   = "apollon",
        },
        /* FocalTech ETM070003DH6 */
@@ -312,9 +330,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 3,
                        .vfp            = 13,
                        .vbp            = 29,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
                },
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                         OMAP_DSS_LCD_IHS,
                .name                   = "focaltech_etm070003dh6",
        },
 
@@ -333,11 +355,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 23,
                        .vfp            = 1,
                        .vbp            = 1,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
                },
-               .acbi                   = 0x0,
-               .acb                    = 0x0,
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                         OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
                .power_on_delay         = 0,
                .power_off_delay        = 0,
                .name                   = "microtips_umsh_8173md",
@@ -358,9 +382,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 10,
                        .vfp            = 4,
                        .vbp            = 2,
-               },
-               .config                 = OMAP_DSS_LCD_TFT,
 
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+               },
                .name                   = "ortustech_com43h4m10xtc",
        },
 
@@ -379,11 +407,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 10,
                        .vfp            = 12,
                        .vbp            = 23,
-               },
-               .acb                    = 0x0,
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                         OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
 
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_LOW,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+               },
                .name                   = "innolux_at080tn52",
        },
 
@@ -401,8 +431,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 1,
                        .vfp            = 26,
                        .vbp            = 1,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
                },
-               .config                 = OMAP_DSS_LCD_TFT,
                .name                   = "mitsubishi_aa084sb01",
        },
        /* EDT ET0500G0DH6 */
@@ -419,8 +454,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 2,
                        .vfp            = 35,
                        .vbp            = 10,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
                },
-               .config                 = OMAP_DSS_LCD_TFT,
                .name                   = "edt_et0500g0dh6",
        },
 
@@ -439,9 +479,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 2,
                        .vfp            = 10,
                        .vbp            = 33,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
                },
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                         OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
                .name                   = "primeview_pd050vl1",
        },
 
@@ -460,9 +504,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 2,
                        .vfp            = 10,
                        .vbp            = 33,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
                },
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                         OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
                .name                   = "primeview_pm070wl4",
        },
 
@@ -481,9 +529,13 @@ static struct panel_config generic_dpi_panels[] = {
                        .vsw            = 4,
                        .vfp            = 1,
                        .vbp            = 23,
+
+                       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+                       .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+                       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+                       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
                },
-               .config                 = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                                         OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
                .name                   = "primeview_pd104slf",
        },
 };
@@ -573,10 +625,7 @@ static int generic_dpi_panel_probe(struct omap_dss_device *dssdev)
        if (!panel_config)
                return -EINVAL;
 
-       dssdev->panel.config = panel_config->config;
        dssdev->panel.timings = panel_config->timings;
-       dssdev->panel.acb = panel_config->acb;
-       dssdev->panel.acbi = panel_config->acbi;
 
        drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL);
        if (!drv_data)
index 0841cc2b3f777d6ecdab57e8b1002d800b23be17..802807798846a3ff976019784b51158f16df4b66 100644 (file)
@@ -40,6 +40,12 @@ static struct omap_video_timings lb035q02_timings = {
        .vsw            = 2,
        .vfp            = 4,
        .vbp            = 18,
+
+       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 };
 
 static int lb035q02_panel_power_on(struct omap_dss_device *dssdev)
@@ -82,8 +88,6 @@ static int lb035q02_panel_probe(struct omap_dss_device *dssdev)
        struct lb035q02_data *ld;
        int r;
 
-       dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-               OMAP_DSS_LCD_IHS;
        dssdev->panel.timings = lb035q02_timings;
 
        ld = kzalloc(sizeof(*ld), GFP_KERNEL);
index 4a34cdc1371b34c777c8b4747bd669186cd2d530..e6c115373c0088cce331cb9bcf74aae22ec10830 100644 (file)
@@ -473,7 +473,6 @@ static int n8x0_panel_probe(struct omap_dss_device *dssdev)
 
        mutex_init(&ddata->lock);
 
-       dssdev->panel.config = OMAP_DSS_LCD_TFT;
        dssdev->panel.timings.x_res = 800;
        dssdev->panel.timings.y_res = 480;
        dssdev->ctrl.pixel_size = 16;
index 8b38b39213f480861e981433c2c36e45cf4c26b5..b122b0f31c43b34602a0f8215ca8106819ea3512 100644 (file)
@@ -76,6 +76,12 @@ static struct omap_video_timings nec_8048_panel_timings = {
        .vfp            = 3,
        .vsw            = 1,
        .vbp            = 4,
+
+       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
 };
 
 static int nec_8048_bl_update_status(struct backlight_device *bl)
@@ -116,9 +122,6 @@ static int nec_8048_panel_probe(struct omap_dss_device *dssdev)
        struct backlight_properties props;
        int r;
 
-       dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
-                               OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_RF |
-                               OMAP_DSS_LCD_ONOFF;
        dssdev->panel.timings = nec_8048_panel_timings;
 
        necd = kzalloc(sizeof(*necd), GFP_KERNEL);
index 98ebdaddab5a323d09e6f7c37e543785c97f8075..2d35bd388860001c08ddbaf5cc1a551764a2f5a5 100644 (file)
@@ -69,6 +69,12 @@ static struct omap_video_timings pico_ls_timings = {
        .vsw            = 2,
        .vfp            = 3,
        .vbp            = 14,
+
+       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
 };
 
 static inline struct picodlp_panel_data
@@ -414,9 +420,6 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev)
        struct i2c_client *picodlp_i2c_client;
        int r = 0, picodlp_adapter_id;
 
-       dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_ONOFF |
-                               OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IVS;
-       dssdev->panel.acb = 0x0;
        dssdev->panel.timings = pico_ls_timings;
 
        picod =  kzalloc(sizeof(struct picodlp_data), GFP_KERNEL);
index ba38b3ad17d6d6c8dc8f3e0f2eb33a301f82d3eb..bd86ba9ccf7600bed53c29c257e164c7dda763ec 100644 (file)
@@ -44,6 +44,12 @@ static struct omap_video_timings sharp_ls_timings = {
        .vsw            = 1,
        .vfp            = 1,
        .vbp            = 1,
+
+       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 };
 
 static int sharp_ls_bl_update_status(struct backlight_device *bl)
@@ -86,9 +92,6 @@ static int sharp_ls_panel_probe(struct omap_dss_device *dssdev)
        struct sharp_data *sd;
        int r;
 
-       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);
index 901576eb5a8425995e57f680715c649e68dc0a54..3f5acc7771da34f6f08549c7c8db27b40d102af9 100644 (file)
@@ -882,7 +882,6 @@ static int taal_probe(struct omap_dss_device *dssdev)
                goto err;
        }
 
-       dssdev->panel.config = OMAP_DSS_LCD_TFT;
        dssdev->panel.timings = panel_config->timings;
        dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
 
index bff306e041cabef157929f4078401122c06510d9..40cc0cfa5d179c77ce8c4bcf0282391162140766 100644 (file)
@@ -39,6 +39,12 @@ static const struct omap_video_timings tfp410_default_timings = {
        .vfp            = 3,
        .vsw            = 4,
        .vbp            = 7,
+
+       .vsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
+       .hsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
+       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 };
 
 struct panel_drv_data {
@@ -95,7 +101,6 @@ static int tfp410_probe(struct omap_dss_device *dssdev)
                return -ENOMEM;
 
        dssdev->panel.timings = tfp410_default_timings;
-       dssdev->panel.config = OMAP_DSS_LCD_TFT;
 
        ddata->dssdev = dssdev;
        mutex_init(&ddata->lock);
index 4b6448b3c31f224f0919cd49d345eee9f61f52ae..fa7baa650ae06bcc18a911a3e3fed34cb4e6f0b2 100644 (file)
@@ -267,6 +267,12 @@ static const struct omap_video_timings tpo_td043_timings = {
        .vsw            = 1,
        .vfp            = 39,
        .vbp            = 34,
+
+       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
 };
 
 static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043)
@@ -423,8 +429,6 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev)
                return -ENODEV;
        }
 
-       dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IHS |
-                               OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IPC;
        dssdev->panel.timings = tpo_td043_timings;
        dssdev->ctrl.pixel_size = 24;
 
index 43324e5ed25fc2bad6712eff76397b92da97bbba..b337a8469fd85441b051b28f5cd808b52d927cf5 100644 (file)
@@ -52,7 +52,7 @@ config OMAP2_DSS_RFBI
          DBI is a bus between the host processor and a peripheral,
          such as a display or a framebuffer chip.
 
-         See http://www.mipi.org/ for DBI spesifications.
+         See http://www.mipi.org/ for DBI specifications.
 
 config OMAP2_DSS_VENC
        bool "VENC support"
@@ -92,7 +92,7 @@ config OMAP2_DSS_DSI
          DSI is a high speed half-duplex serial interface between the host
          processor and a peripheral, such as a display or a framebuffer chip.
 
-         See http://www.mipi.org/ for DSI spesifications.
+         See http://www.mipi.org/ for DSI specifications.
 
 config OMAP2_DSS_MIN_FCK_PER_PCK
        int "Minimum FCK/PCK ratio (for scaling)"
index ab22cc224f3eb8259a7d42dd2c841687703ffab7..0fefc68372b93ca326b9c194f4529fd201b6161c 100644 (file)
@@ -104,6 +104,7 @@ struct mgr_priv_data {
        bool shadow_extra_info_dirty;
 
        struct omap_video_timings timings;
+       struct dss_lcd_mgr_config lcd_config;
 };
 
 static struct {
@@ -137,6 +138,7 @@ static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr)
 void dss_apply_init(void)
 {
        const int num_ovls = dss_feat_get_num_ovls();
+       struct mgr_priv_data *mp;
        int i;
 
        spin_lock_init(&data_lock);
@@ -168,16 +170,35 @@ void dss_apply_init(void)
 
                op->user_info = op->info;
        }
+
+       /*
+        * Initialize some of the lcd_config fields for TV manager, this lets
+        * us prevent checking if the manager is LCD or TV at some places
+        */
+       mp = &dss_data.mgr_priv_data_array[OMAP_DSS_CHANNEL_DIGIT];
+
+       mp->lcd_config.video_port_width = 24;
+       mp->lcd_config.clock_info.lck_div = 1;
+       mp->lcd_config.clock_info.pck_div = 1;
 }
 
+/*
+ * A LCD manager's stallmode decides whether it is in manual or auto update. TV
+ * manager is always auto update, stallmode field for TV manager is false by
+ * default
+ */
 static bool ovl_manual_update(struct omap_overlay *ovl)
 {
-       return ovl->manager->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+       struct mgr_priv_data *mp = get_mgr_priv(ovl->manager);
+
+       return mp->lcd_config.stallmode;
 }
 
 static bool mgr_manual_update(struct omap_overlay_manager *mgr)
 {
-       return mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+       struct mgr_priv_data *mp = get_mgr_priv(mgr);
+
+       return mp->lcd_config.stallmode;
 }
 
 static int dss_check_settings_low(struct omap_overlay_manager *mgr,
@@ -214,7 +235,7 @@ static int dss_check_settings_low(struct omap_overlay_manager *mgr,
                ois[ovl->id] = oi;
        }
 
-       return dss_mgr_check(mgr, mi, &mp->timings, ois);
+       return dss_mgr_check(mgr, mi, &mp->timings, &mp->lcd_config, ois);
 }
 
 /*
@@ -537,7 +558,7 @@ static void dss_ovl_write_regs(struct omap_overlay *ovl)
 {
        struct ovl_priv_data *op = get_ovl_priv(ovl);
        struct omap_overlay_info *oi;
-       bool ilace, replication;
+       bool replication;
        struct mgr_priv_data *mp;
        int r;
 
@@ -550,11 +571,9 @@ static void dss_ovl_write_regs(struct omap_overlay *ovl)
 
        mp = get_mgr_priv(ovl->manager);
 
-       replication = dss_use_replication(ovl->manager->device, oi->color_mode);
-
-       ilace = ovl->manager->device->type == OMAP_DISPLAY_TYPE_VENC;
+       replication = dss_ovl_use_replication(mp->lcd_config, oi->color_mode);
 
-       r = dispc_ovl_setup(ovl->id, oi, ilace, replication, &mp->timings);
+       r = dispc_ovl_setup(ovl->id, oi, replication, &mp->timings);
        if (r) {
                /*
                 * We can't do much here, as this function can be called from
@@ -635,6 +654,24 @@ static void dss_mgr_write_regs_extra(struct omap_overlay_manager *mgr)
 
        dispc_mgr_set_timings(mgr->id, &mp->timings);
 
+       /* lcd_config parameters */
+       if (dss_mgr_is_lcd(mgr->id)) {
+               dispc_mgr_set_io_pad_mode(mp->lcd_config.io_pad_mode);
+
+               dispc_mgr_enable_stallmode(mgr->id, mp->lcd_config.stallmode);
+               dispc_mgr_enable_fifohandcheck(mgr->id,
+                       mp->lcd_config.fifohandcheck);
+
+               dispc_mgr_set_clock_div(mgr->id, &mp->lcd_config.clock_info);
+
+               dispc_mgr_set_tft_data_lines(mgr->id,
+                       mp->lcd_config.video_port_width);
+
+               dispc_lcd_enable_signal_polarity(mp->lcd_config.lcden_sig_polarity);
+
+               dispc_mgr_set_lcd_type_tft(mgr->id);
+       }
+
        mp->extra_info_dirty = false;
        if (mp->updating)
                mp->shadow_extra_info_dirty = true;
@@ -1294,6 +1331,44 @@ void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
        mutex_unlock(&apply_lock);
 }
 
+static void dss_apply_mgr_lcd_config(struct omap_overlay_manager *mgr,
+               const struct dss_lcd_mgr_config *config)
+{
+       struct mgr_priv_data *mp = get_mgr_priv(mgr);
+
+       mp->lcd_config = *config;
+       mp->extra_info_dirty = true;
+}
+
+void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr,
+               const struct dss_lcd_mgr_config *config)
+{
+       unsigned long flags;
+       struct mgr_priv_data *mp = get_mgr_priv(mgr);
+
+       mutex_lock(&apply_lock);
+
+       if (mp->enabled) {
+               DSSERR("cannot apply lcd config for %s: manager needs to be disabled\n",
+                       mgr->name);
+               goto out;
+       }
+
+       spin_lock_irqsave(&data_lock, flags);
+
+       dss_apply_mgr_lcd_config(mgr, config);
+
+       dss_write_regs();
+       dss_set_go_bits();
+
+       spin_unlock_irqrestore(&data_lock, flags);
+
+       wait_pending_extra_info_updates();
+
+out:
+       mutex_unlock(&apply_lock);
+}
+
 int dss_ovl_set_info(struct omap_overlay *ovl,
                struct omap_overlay_info *info)
 {
index 397d4eee11bb7715d9d01e3b8a1330abcdc2f2e4..5b289c5f695bdd7f9cb888a70dbdf7da909b1df2 100644 (file)
@@ -119,6 +119,97 @@ enum omap_color_component {
        DISPC_COLOR_COMPONENT_UV                = 1 << 1,
 };
 
+enum mgr_reg_fields {
+       DISPC_MGR_FLD_ENABLE,
+       DISPC_MGR_FLD_STNTFT,
+       DISPC_MGR_FLD_GO,
+       DISPC_MGR_FLD_TFTDATALINES,
+       DISPC_MGR_FLD_STALLMODE,
+       DISPC_MGR_FLD_TCKENABLE,
+       DISPC_MGR_FLD_TCKSELECTION,
+       DISPC_MGR_FLD_CPR,
+       DISPC_MGR_FLD_FIFOHANDCHECK,
+       /* used to maintain a count of the above fields */
+       DISPC_MGR_FLD_NUM,
+};
+
+static const struct {
+       const char *name;
+       u32 vsync_irq;
+       u32 framedone_irq;
+       u32 sync_lost_irq;
+       struct reg_field reg_desc[DISPC_MGR_FLD_NUM];
+} mgr_desc[] = {
+       [OMAP_DSS_CHANNEL_LCD] = {
+               .name           = "LCD",
+               .vsync_irq      = DISPC_IRQ_VSYNC,
+               .framedone_irq  = DISPC_IRQ_FRAMEDONE,
+               .sync_lost_irq  = DISPC_IRQ_SYNC_LOST,
+               .reg_desc       = {
+                       [DISPC_MGR_FLD_ENABLE]          = { DISPC_CONTROL,  0,  0 },
+                       [DISPC_MGR_FLD_STNTFT]          = { DISPC_CONTROL,  3,  3 },
+                       [DISPC_MGR_FLD_GO]              = { DISPC_CONTROL,  5,  5 },
+                       [DISPC_MGR_FLD_TFTDATALINES]    = { DISPC_CONTROL,  9,  8 },
+                       [DISPC_MGR_FLD_STALLMODE]       = { DISPC_CONTROL, 11, 11 },
+                       [DISPC_MGR_FLD_TCKENABLE]       = { DISPC_CONFIG,  10, 10 },
+                       [DISPC_MGR_FLD_TCKSELECTION]    = { DISPC_CONFIG,  11, 11 },
+                       [DISPC_MGR_FLD_CPR]             = { DISPC_CONFIG,  15, 15 },
+                       [DISPC_MGR_FLD_FIFOHANDCHECK]   = { DISPC_CONFIG,  16, 16 },
+               },
+       },
+       [OMAP_DSS_CHANNEL_DIGIT] = {
+               .name           = "DIGIT",
+               .vsync_irq      = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN,
+               .framedone_irq  = 0,
+               .sync_lost_irq  = DISPC_IRQ_SYNC_LOST_DIGIT,
+               .reg_desc       = {
+                       [DISPC_MGR_FLD_ENABLE]          = { DISPC_CONTROL,  1,  1 },
+                       [DISPC_MGR_FLD_STNTFT]          = { },
+                       [DISPC_MGR_FLD_GO]              = { DISPC_CONTROL,  6,  6 },
+                       [DISPC_MGR_FLD_TFTDATALINES]    = { },
+                       [DISPC_MGR_FLD_STALLMODE]       = { },
+                       [DISPC_MGR_FLD_TCKENABLE]       = { DISPC_CONFIG,  12, 12 },
+                       [DISPC_MGR_FLD_TCKSELECTION]    = { DISPC_CONFIG,  13, 13 },
+                       [DISPC_MGR_FLD_CPR]             = { },
+                       [DISPC_MGR_FLD_FIFOHANDCHECK]   = { DISPC_CONFIG,  16, 16 },
+               },
+       },
+       [OMAP_DSS_CHANNEL_LCD2] = {
+               .name           = "LCD2",
+               .vsync_irq      = DISPC_IRQ_VSYNC2,
+               .framedone_irq  = DISPC_IRQ_FRAMEDONE2,
+               .sync_lost_irq  = DISPC_IRQ_SYNC_LOST2,
+               .reg_desc       = {
+                       [DISPC_MGR_FLD_ENABLE]          = { DISPC_CONTROL2,  0,  0 },
+                       [DISPC_MGR_FLD_STNTFT]          = { DISPC_CONTROL2,  3,  3 },
+                       [DISPC_MGR_FLD_GO]              = { DISPC_CONTROL2,  5,  5 },
+                       [DISPC_MGR_FLD_TFTDATALINES]    = { DISPC_CONTROL2,  9,  8 },
+                       [DISPC_MGR_FLD_STALLMODE]       = { DISPC_CONTROL2, 11, 11 },
+                       [DISPC_MGR_FLD_TCKENABLE]       = { DISPC_CONFIG2,  10, 10 },
+                       [DISPC_MGR_FLD_TCKSELECTION]    = { DISPC_CONFIG2,  11, 11 },
+                       [DISPC_MGR_FLD_CPR]             = { DISPC_CONFIG2,  15, 15 },
+                       [DISPC_MGR_FLD_FIFOHANDCHECK]   = { DISPC_CONFIG2,  16, 16 },
+               },
+       },
+       [OMAP_DSS_CHANNEL_LCD3] = {
+               .name           = "LCD3",
+               .vsync_irq      = DISPC_IRQ_VSYNC3,
+               .framedone_irq  = DISPC_IRQ_FRAMEDONE3,
+               .sync_lost_irq  = DISPC_IRQ_SYNC_LOST3,
+               .reg_desc       = {
+                       [DISPC_MGR_FLD_ENABLE]          = { DISPC_CONTROL3,  0,  0 },
+                       [DISPC_MGR_FLD_STNTFT]          = { DISPC_CONTROL3,  3,  3 },
+                       [DISPC_MGR_FLD_GO]              = { DISPC_CONTROL3,  5,  5 },
+                       [DISPC_MGR_FLD_TFTDATALINES]    = { DISPC_CONTROL3,  9,  8 },
+                       [DISPC_MGR_FLD_STALLMODE]       = { DISPC_CONTROL3, 11, 11 },
+                       [DISPC_MGR_FLD_TCKENABLE]       = { DISPC_CONFIG3,  10, 10 },
+                       [DISPC_MGR_FLD_TCKSELECTION]    = { DISPC_CONFIG3,  11, 11 },
+                       [DISPC_MGR_FLD_CPR]             = { DISPC_CONFIG3,  15, 15 },
+                       [DISPC_MGR_FLD_FIFOHANDCHECK]   = { DISPC_CONFIG3,  16, 16 },
+               },
+       },
+};
+
 static void _omap_dispc_set_irqs(void);
 
 static inline void dispc_write_reg(const u16 idx, u32 val)
@@ -131,6 +222,18 @@ static inline u32 dispc_read_reg(const u16 idx)
        return __raw_readl(dispc.base + idx);
 }
 
+static u32 mgr_fld_read(enum omap_channel channel, enum mgr_reg_fields regfld)
+{
+       const struct reg_field rfld = mgr_desc[channel].reg_desc[regfld];
+       return REG_GET(rfld.reg, rfld.high, rfld.low);
+}
+
+static void mgr_fld_write(enum omap_channel channel,
+                                       enum mgr_reg_fields regfld, int val) {
+       const struct reg_field rfld = mgr_desc[channel].reg_desc[regfld];
+       REG_FLD_MOD(rfld.reg, val, rfld.high, rfld.low);
+}
+
 #define SR(reg) \
        dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg)
 #define RR(reg) \
@@ -153,6 +256,10 @@ static void dispc_save_context(void)
                SR(CONTROL2);
                SR(CONFIG2);
        }
+       if (dss_has_feature(FEAT_MGR_LCD3)) {
+               SR(CONTROL3);
+               SR(CONFIG3);
+       }
 
        for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
                SR(DEFAULT_COLOR(i));
@@ -266,6 +373,8 @@ static void dispc_restore_context(void)
                RR(GLOBAL_ALPHA);
        if (dss_has_feature(FEAT_MGR_LCD2))
                RR(CONFIG2);
+       if (dss_has_feature(FEAT_MGR_LCD3))
+               RR(CONFIG3);
 
        for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
                RR(DEFAULT_COLOR(i));
@@ -351,6 +460,8 @@ static void dispc_restore_context(void)
        RR(CONTROL);
        if (dss_has_feature(FEAT_MGR_LCD2))
                RR(CONTROL2);
+       if (dss_has_feature(FEAT_MGR_LCD3))
+               RR(CONTROL3);
        /* clear spurious SYNC_LOST_DIGIT interrupts */
        dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT);
 
@@ -387,101 +498,41 @@ void dispc_runtime_put(void)
        WARN_ON(r < 0 && r != -ENOSYS);
 }
 
-static inline bool dispc_mgr_is_lcd(enum omap_channel channel)
-{
-       if (channel == OMAP_DSS_CHANNEL_LCD ||
-                       channel == OMAP_DSS_CHANNEL_LCD2)
-               return true;
-       else
-               return false;
-}
-
 u32 dispc_mgr_get_vsync_irq(enum omap_channel channel)
 {
-       switch (channel) {
-       case OMAP_DSS_CHANNEL_LCD:
-               return DISPC_IRQ_VSYNC;
-       case OMAP_DSS_CHANNEL_LCD2:
-               return DISPC_IRQ_VSYNC2;
-       case OMAP_DSS_CHANNEL_DIGIT:
-               return DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
-       default:
-               BUG();
-               return 0;
-       }
+       return mgr_desc[channel].vsync_irq;
 }
 
 u32 dispc_mgr_get_framedone_irq(enum omap_channel channel)
 {
-       switch (channel) {
-       case OMAP_DSS_CHANNEL_LCD:
-               return DISPC_IRQ_FRAMEDONE;
-       case OMAP_DSS_CHANNEL_LCD2:
-               return DISPC_IRQ_FRAMEDONE2;
-       case OMAP_DSS_CHANNEL_DIGIT:
-               return 0;
-       default:
-               BUG();
-               return 0;
-       }
+       return mgr_desc[channel].framedone_irq;
 }
 
 bool dispc_mgr_go_busy(enum omap_channel channel)
 {
-       int bit;
-
-       if (dispc_mgr_is_lcd(channel))
-               bit = 5; /* GOLCD */
-       else
-               bit = 6; /* GODIGIT */
-
-       if (channel == OMAP_DSS_CHANNEL_LCD2)
-               return REG_GET(DISPC_CONTROL2, bit, bit) == 1;
-       else
-               return REG_GET(DISPC_CONTROL, bit, bit) == 1;
+       return mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1;
 }
 
 void dispc_mgr_go(enum omap_channel channel)
 {
-       int bit;
        bool enable_bit, go_bit;
 
-       if (dispc_mgr_is_lcd(channel))
-               bit = 0; /* LCDENABLE */
-       else
-               bit = 1; /* DIGITALENABLE */
-
        /* if the channel is not enabled, we don't need GO */
-       if (channel == OMAP_DSS_CHANNEL_LCD2)
-               enable_bit = REG_GET(DISPC_CONTROL2, bit, bit) == 1;
-       else
-               enable_bit = REG_GET(DISPC_CONTROL, bit, bit) == 1;
+       enable_bit = mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE) == 1;
 
        if (!enable_bit)
                return;
 
-       if (dispc_mgr_is_lcd(channel))
-               bit = 5; /* GOLCD */
-       else
-               bit = 6; /* GODIGIT */
-
-       if (channel == OMAP_DSS_CHANNEL_LCD2)
-               go_bit = REG_GET(DISPC_CONTROL2, bit, bit) == 1;
-       else
-               go_bit = REG_GET(DISPC_CONTROL, bit, bit) == 1;
+       go_bit = mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1;
 
        if (go_bit) {
                DSSERR("GO bit not down for channel %d\n", channel);
                return;
        }
 
-       DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" :
-               (channel == OMAP_DSS_CHANNEL_LCD2 ? "LCD2" : "DIGIT"));
+       DSSDBG("GO %s\n", mgr_desc[channel].name);
 
-       if (channel == OMAP_DSS_CHANNEL_LCD2)
-               REG_FLD_MOD(DISPC_CONTROL2, 1, bit, bit);
-       else
-               REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit);
+       mgr_fld_write(channel, DISPC_MGR_FLD_GO, 1);
 }
 
 static void dispc_ovl_write_firh_reg(enum omap_plane plane, int reg, u32 value)
@@ -832,6 +883,15 @@ void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel)
                        chan = 0;
                        chan2 = 1;
                        break;
+               case OMAP_DSS_CHANNEL_LCD3:
+                       if (dss_has_feature(FEAT_MGR_LCD3)) {
+                               chan = 0;
+                               chan2 = 2;
+                       } else {
+                               BUG();
+                               return;
+                       }
+                       break;
                default:
                        BUG();
                        return;
@@ -867,7 +927,14 @@ static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane)
 
        val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
 
-       if (dss_has_feature(FEAT_MGR_LCD2)) {
+       if (dss_has_feature(FEAT_MGR_LCD3)) {
+               if (FLD_GET(val, 31, 30) == 0)
+                       channel = FLD_GET(val, shift, shift);
+               else if (FLD_GET(val, 31, 30) == 1)
+                       channel = OMAP_DSS_CHANNEL_LCD2;
+               else
+                       channel = OMAP_DSS_CHANNEL_LCD3;
+       } else if (dss_has_feature(FEAT_MGR_LCD2)) {
                if (FLD_GET(val, 31, 30) == 0)
                        channel = FLD_GET(val, shift, shift);
                else
@@ -922,16 +989,10 @@ void dispc_enable_gamma_table(bool enable)
 
 static void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable)
 {
-       u16 reg;
-
-       if (channel == OMAP_DSS_CHANNEL_LCD)
-               reg = DISPC_CONFIG;
-       else if (channel == OMAP_DSS_CHANNEL_LCD2)
-               reg = DISPC_CONFIG2;
-       else
+       if (channel == OMAP_DSS_CHANNEL_DIGIT)
                return;
 
-       REG_FLD_MOD(reg, enable, 15, 15);
+       mgr_fld_write(channel, DISPC_MGR_FLD_CPR, enable);
 }
 
 static void dispc_mgr_set_cpr_coef(enum omap_channel channel,
@@ -939,7 +1000,7 @@ static void dispc_mgr_set_cpr_coef(enum omap_channel channel,
 {
        u32 coef_r, coef_g, coef_b;
 
-       if (!dispc_mgr_is_lcd(channel))
+       if (!dss_mgr_is_lcd(channel))
                return;
 
        coef_r = FLD_VAL(coefs->rr, 31, 22) | FLD_VAL(coefs->rg, 20, 11) |
@@ -1798,7 +1859,7 @@ static int check_horiz_timing_omap3(enum omap_channel channel,
 
        nonactive = t->x_res + t->hfp + t->hsw + t->hbp - out_width;
        pclk = dispc_mgr_pclk_rate(channel);
-       if (dispc_mgr_is_lcd(channel))
+       if (dss_mgr_is_lcd(channel))
                lclk = dispc_mgr_lclk_rate(channel);
        else
                lclk = dispc_fclk_rate();
@@ -2086,8 +2147,7 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane,
 }
 
 int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
-               bool ilace, bool replication,
-               const struct omap_video_timings *mgr_timings)
+               bool replication, const struct omap_video_timings *mgr_timings)
 {
        struct omap_overlay *ovl = omap_dss_get_overlay(plane);
        bool five_taps = true;
@@ -2103,6 +2163,7 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
        u16 out_width, out_height;
        enum omap_channel channel;
        int x_predecim = 1, y_predecim = 1;
+       bool ilace = mgr_timings->interlace;
 
        channel = dispc_ovl_get_channel_out(plane);
 
@@ -2254,14 +2315,9 @@ static void dispc_disable_isr(void *data, u32 mask)
 
 static void _enable_lcd_out(enum omap_channel channel, bool enable)
 {
-       if (channel == OMAP_DSS_CHANNEL_LCD2) {
-               REG_FLD_MOD(DISPC_CONTROL2, enable ? 1 : 0, 0, 0);
-               /* flush posted write */
-               dispc_read_reg(DISPC_CONTROL2);
-       } else {
-               REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0);
-               dispc_read_reg(DISPC_CONTROL);
-       }
+       mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable);
+       /* flush posted write */
+       mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
 }
 
 static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
@@ -2274,12 +2330,9 @@ static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
        /* 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 = channel == OMAP_DSS_CHANNEL_LCD2 ?
-                       REG_GET(DISPC_CONTROL2, 0, 0) :
-                       REG_GET(DISPC_CONTROL, 0, 0);
+       is_on = mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
 
-       irq = channel == OMAP_DSS_CHANNEL_LCD2 ? DISPC_IRQ_FRAMEDONE2 :
-                       DISPC_IRQ_FRAMEDONE;
+       irq = mgr_desc[channel].framedone_irq;
 
        if (!enable && is_on) {
                init_completion(&frame_done_completion);
@@ -2384,21 +2437,12 @@ static void dispc_mgr_enable_digit_out(bool enable)
 
 bool dispc_mgr_is_enabled(enum omap_channel channel)
 {
-       if (channel == OMAP_DSS_CHANNEL_LCD)
-               return !!REG_GET(DISPC_CONTROL, 0, 0);
-       else if (channel == OMAP_DSS_CHANNEL_DIGIT)
-               return !!REG_GET(DISPC_CONTROL, 1, 1);
-       else if (channel == OMAP_DSS_CHANNEL_LCD2)
-               return !!REG_GET(DISPC_CONTROL2, 0, 0);
-       else {
-               BUG();
-               return false;
-       }
+       return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
 }
 
 void dispc_mgr_enable(enum omap_channel channel, bool enable)
 {
-       if (dispc_mgr_is_lcd(channel))
+       if (dss_mgr_is_lcd(channel))
                dispc_mgr_enable_lcd_out(channel, enable);
        else if (channel == OMAP_DSS_CHANNEL_DIGIT)
                dispc_mgr_enable_digit_out(enable);
@@ -2432,36 +2476,13 @@ void dispc_pck_free_enable(bool enable)
 
 void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable)
 {
-       if (channel == OMAP_DSS_CHANNEL_LCD2)
-               REG_FLD_MOD(DISPC_CONFIG2, enable ? 1 : 0, 16, 16);
-       else
-               REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16);
+       mgr_fld_write(channel, DISPC_MGR_FLD_FIFOHANDCHECK, enable);
 }
 
 
-void dispc_mgr_set_lcd_display_type(enum omap_channel channel,
-               enum omap_lcd_display_type type)
+void dispc_mgr_set_lcd_type_tft(enum omap_channel channel)
 {
-       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;
-       }
-
-       if (channel == OMAP_DSS_CHANNEL_LCD2)
-               REG_FLD_MOD(DISPC_CONTROL2, mode, 3, 3);
-       else
-               REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3);
+       mgr_fld_write(channel, DISPC_MGR_FLD_STNTFT, 1);
 }
 
 void dispc_set_loadmode(enum omap_dss_load_mode mode)
@@ -2479,24 +2500,14 @@ static void dispc_mgr_set_trans_key(enum omap_channel ch,
                enum omap_dss_trans_key_type type,
                u32 trans_key)
 {
-       if (ch == OMAP_DSS_CHANNEL_LCD)
-               REG_FLD_MOD(DISPC_CONFIG, type, 11, 11);
-       else if (ch == OMAP_DSS_CHANNEL_DIGIT)
-               REG_FLD_MOD(DISPC_CONFIG, type, 13, 13);
-       else /* OMAP_DSS_CHANNEL_LCD2 */
-               REG_FLD_MOD(DISPC_CONFIG2, type, 11, 11);
+       mgr_fld_write(ch, DISPC_MGR_FLD_TCKSELECTION, type);
 
        dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key);
 }
 
 static void dispc_mgr_enable_trans_key(enum omap_channel ch, bool enable)
 {
-       if (ch == OMAP_DSS_CHANNEL_LCD)
-               REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10);
-       else if (ch == OMAP_DSS_CHANNEL_DIGIT)
-               REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12);
-       else /* OMAP_DSS_CHANNEL_LCD2 */
-               REG_FLD_MOD(DISPC_CONFIG2, enable, 10, 10);
+       mgr_fld_write(ch, DISPC_MGR_FLD_TCKENABLE, enable);
 }
 
 static void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch,
@@ -2547,10 +2558,7 @@ void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
                return;
        }
 
-       if (channel == OMAP_DSS_CHANNEL_LCD2)
-               REG_FLD_MOD(DISPC_CONTROL2, code, 9, 8);
-       else
-               REG_FLD_MOD(DISPC_CONTROL, code, 9, 8);
+       mgr_fld_write(channel, DISPC_MGR_FLD_TFTDATALINES, code);
 }
 
 void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
@@ -2584,10 +2592,7 @@ void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
 
 void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable)
 {
-       if (channel == OMAP_DSS_CHANNEL_LCD2)
-               REG_FLD_MOD(DISPC_CONTROL2, enable, 11, 11);
-       else
-               REG_FLD_MOD(DISPC_CONTROL, enable, 11, 11);
+       mgr_fld_write(channel, DISPC_MGR_FLD_STALLMODE, enable);
 }
 
 static bool _dispc_mgr_size_ok(u16 width, u16 height)
@@ -2627,7 +2632,7 @@ bool dispc_mgr_timings_ok(enum omap_channel channel,
 
        timings_ok = _dispc_mgr_size_ok(timings->x_res, timings->y_res);
 
-       if (dispc_mgr_is_lcd(channel))
+       if (dss_mgr_is_lcd(channel))
                timings_ok =  timings_ok && _dispc_lcd_timings_ok(timings->hsw,
                                                timings->hfp, timings->hbp,
                                                timings->vsw, timings->vfp,
@@ -2637,9 +2642,16 @@ bool dispc_mgr_timings_ok(enum omap_channel channel,
 }
 
 static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw,
-               int hfp, int hbp, int vsw, int vfp, int vbp)
+               int hfp, int hbp, int vsw, int vfp, int vbp,
+               enum omap_dss_signal_level vsync_level,
+               enum omap_dss_signal_level hsync_level,
+               enum omap_dss_signal_edge data_pclk_edge,
+               enum omap_dss_signal_level de_level,
+               enum omap_dss_signal_edge sync_pclk_edge)
+
 {
-       u32 timing_h, timing_v;
+       u32 timing_h, timing_v, l;
+       bool onoff, rf, ipc;
 
        if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) {
                timing_h = FLD_VAL(hsw-1, 5, 0) | FLD_VAL(hfp-1, 15, 8) |
@@ -2657,6 +2669,44 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw,
 
        dispc_write_reg(DISPC_TIMING_H(channel), timing_h);
        dispc_write_reg(DISPC_TIMING_V(channel), timing_v);
+
+       switch (data_pclk_edge) {
+       case OMAPDSS_DRIVE_SIG_RISING_EDGE:
+               ipc = false;
+               break;
+       case OMAPDSS_DRIVE_SIG_FALLING_EDGE:
+               ipc = true;
+               break;
+       case OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES:
+       default:
+               BUG();
+       }
+
+       switch (sync_pclk_edge) {
+       case OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES:
+               onoff = false;
+               rf = false;
+               break;
+       case OMAPDSS_DRIVE_SIG_FALLING_EDGE:
+               onoff = true;
+               rf = false;
+               break;
+       case OMAPDSS_DRIVE_SIG_RISING_EDGE:
+               onoff = true;
+               rf = true;
+               break;
+       default:
+               BUG();
+       };
+
+       l = dispc_read_reg(DISPC_POL_FREQ(channel));
+       l |= FLD_VAL(onoff, 17, 17);
+       l |= FLD_VAL(rf, 16, 16);
+       l |= FLD_VAL(de_level, 15, 15);
+       l |= FLD_VAL(ipc, 14, 14);
+       l |= FLD_VAL(hsync_level, 13, 13);
+       l |= FLD_VAL(vsync_level, 12, 12);
+       dispc_write_reg(DISPC_POL_FREQ(channel), l);
 }
 
 /* change name to mode? */
@@ -2674,9 +2724,10 @@ void dispc_mgr_set_timings(enum omap_channel channel,
                return;
        }
 
-       if (dispc_mgr_is_lcd(channel)) {
+       if (dss_mgr_is_lcd(channel)) {
                _dispc_mgr_set_lcd_timings(channel, t.hsw, t.hfp, t.hbp, t.vsw,
-                               t.vfp, t.vbp);
+                               t.vfp, t.vbp, t.vsync_level, t.hsync_level,
+                               t.data_pclk_edge, t.de_level, t.sync_pclk_edge);
 
                xtot = t.x_res + t.hfp + t.hsw + t.hbp;
                ytot = t.y_res + t.vfp + t.vsw + t.vbp;
@@ -2687,14 +2738,13 @@ void dispc_mgr_set_timings(enum omap_channel channel,
                DSSDBG("pck %u\n", timings->pixel_clock);
                DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n",
                        t.hsw, t.hfp, t.hbp, t.vsw, t.vfp, t.vbp);
+               DSSDBG("vsync_level %d hsync_level %d data_pclk_edge %d de_level %d sync_pclk_edge %d\n",
+                       t.vsync_level, t.hsync_level, t.data_pclk_edge,
+                       t.de_level, t.sync_pclk_edge);
 
                DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt);
        } else {
-               enum dss_hdmi_venc_clk_source_select source;
-
-               source = dss_get_hdmi_venc_clk_source();
-
-               if (source == DSS_VENC_TV_CLK)
+               if (t.interlace == true)
                        t.y_res /= 2;
        }
 
@@ -2780,7 +2830,7 @@ unsigned long dispc_mgr_pclk_rate(enum omap_channel channel)
 {
        unsigned long r;
 
-       if (dispc_mgr_is_lcd(channel)) {
+       if (dss_mgr_is_lcd(channel)) {
                int pcd;
                u32 l;
 
@@ -2821,12 +2871,32 @@ unsigned long dispc_core_clk_rate(void)
        return fclk / lcd;
 }
 
-void dispc_dump_clocks(struct seq_file *s)
+static void dispc_dump_clocks_channel(struct seq_file *s, enum omap_channel channel)
 {
        int lcd, pcd;
+       enum omap_dss_clk_source lcd_clk_src;
+
+       seq_printf(s, "- %s -\n", mgr_desc[channel].name);
+
+       lcd_clk_src = dss_get_lcd_clk_source(channel);
+
+       seq_printf(s, "%s clk source = %s (%s)\n", mgr_desc[channel].name,
+               dss_get_generic_clk_source_name(lcd_clk_src),
+               dss_feat_get_clk_source_name(lcd_clk_src));
+
+       dispc_mgr_get_lcd_divisor(channel, &lcd, &pcd);
+
+       seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
+               dispc_mgr_lclk_rate(channel), lcd);
+       seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
+               dispc_mgr_pclk_rate(channel), pcd);
+}
+
+void dispc_dump_clocks(struct seq_file *s)
+{
+       int lcd;
        u32 l;
        enum omap_dss_clk_source dispc_clk_src = dss_get_dispc_clk_source();
-       enum omap_dss_clk_source lcd_clk_src;
 
        if (dispc_runtime_get())
                return;
@@ -2847,36 +2917,13 @@ void dispc_dump_clocks(struct seq_file *s)
                seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
                                (dispc_fclk_rate()/lcd), lcd);
        }
-       seq_printf(s, "- LCD1 -\n");
-
-       lcd_clk_src = dss_get_lcd_clk_source(OMAP_DSS_CHANNEL_LCD);
-
-       seq_printf(s, "lcd1_clk source = %s (%s)\n",
-               dss_get_generic_clk_source_name(lcd_clk_src),
-               dss_feat_get_clk_source_name(lcd_clk_src));
-
-       dispc_mgr_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD, &lcd, &pcd);
-
-       seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
-                       dispc_mgr_lclk_rate(OMAP_DSS_CHANNEL_LCD), lcd);
-       seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
-                       dispc_mgr_pclk_rate(OMAP_DSS_CHANNEL_LCD), pcd);
-       if (dss_has_feature(FEAT_MGR_LCD2)) {
-               seq_printf(s, "- LCD2 -\n");
-
-               lcd_clk_src = dss_get_lcd_clk_source(OMAP_DSS_CHANNEL_LCD2);
 
-               seq_printf(s, "lcd2_clk source = %s (%s)\n",
-                       dss_get_generic_clk_source_name(lcd_clk_src),
-                       dss_feat_get_clk_source_name(lcd_clk_src));
+       dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD);
 
-               dispc_mgr_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD2, &lcd, &pcd);
-
-               seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
-                               dispc_mgr_lclk_rate(OMAP_DSS_CHANNEL_LCD2), lcd);
-               seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
-                               dispc_mgr_pclk_rate(OMAP_DSS_CHANNEL_LCD2), pcd);
-       }
+       if (dss_has_feature(FEAT_MGR_LCD2))
+               dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD2);
+       if (dss_has_feature(FEAT_MGR_LCD3))
+               dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD3);
 
        dispc_runtime_put();
 }
@@ -2929,6 +2976,12 @@ void dispc_dump_irqs(struct seq_file *s)
                PIS(ACBIAS_COUNT_STAT2);
                PIS(SYNC_LOST2);
        }
+       if (dss_has_feature(FEAT_MGR_LCD3)) {
+               PIS(FRAMEDONE3);
+               PIS(VSYNC3);
+               PIS(ACBIAS_COUNT_STAT3);
+               PIS(SYNC_LOST3);
+       }
 #undef PIS
 }
 #endif
@@ -2940,6 +2993,7 @@ static void dispc_dump_regs(struct seq_file *s)
                [OMAP_DSS_CHANNEL_LCD]          = "LCD",
                [OMAP_DSS_CHANNEL_DIGIT]        = "TV",
                [OMAP_DSS_CHANNEL_LCD2]         = "LCD2",
+               [OMAP_DSS_CHANNEL_LCD3]         = "LCD3",
        };
        const char *ovl_names[] = {
                [OMAP_DSS_GFX]          = "GFX",
@@ -2972,6 +3026,10 @@ static void dispc_dump_regs(struct seq_file *s)
                DUMPREG(DISPC_CONTROL2);
                DUMPREG(DISPC_CONFIG2);
        }
+       if (dss_has_feature(FEAT_MGR_LCD3)) {
+               DUMPREG(DISPC_CONTROL3);
+               DUMPREG(DISPC_CONFIG3);
+       }
 
 #undef DUMPREG
 
@@ -3093,41 +3151,8 @@ static void dispc_dump_regs(struct seq_file *s)
 #undef DUMPREG
 }
 
-static void _dispc_mgr_set_pol_freq(enum omap_channel channel, 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);
-
-       dispc_write_reg(DISPC_POL_FREQ(channel), l);
-}
-
-void dispc_mgr_set_pol_freq(enum omap_channel channel,
-               enum omap_panel_config config, u8 acbi, u8 acb)
-{
-       _dispc_mgr_set_pol_freq(channel, (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,
+void dispc_find_clk_divs(unsigned long req_pck, unsigned long fck,
                struct dispc_clock_info *cinfo)
 {
        u16 pcd_min, pcd_max;
@@ -3138,9 +3163,6 @@ void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
        pcd_min = dss_feat_get_param_min(FEAT_PARAM_DSS_PCD);
        pcd_max = dss_feat_get_param_max(FEAT_PARAM_DSS_PCD);
 
-       if (!is_tft)
-               pcd_min = 3;
-
        best_pck = 0;
        best_ld = 0;
        best_pd = 0;
@@ -3192,15 +3214,13 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
        return 0;
 }
 
-int dispc_mgr_set_clock_div(enum omap_channel channel,
+void dispc_mgr_set_clock_div(enum omap_channel channel,
                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_mgr_set_lcd_divisor(channel, cinfo->lck_div, cinfo->pck_div);
-
-       return 0;
 }
 
 int dispc_mgr_get_clock_div(enum omap_channel channel,
@@ -3354,6 +3374,8 @@ static void print_irq_status(u32 status)
        PIS(SYNC_LOST_DIGIT);
        if (dss_has_feature(FEAT_MGR_LCD2))
                PIS(SYNC_LOST2);
+       if (dss_has_feature(FEAT_MGR_LCD3))
+               PIS(SYNC_LOST3);
 #undef PIS
 
        printk("\n");
@@ -3450,12 +3472,6 @@ static void dispc_error_worker(struct work_struct *work)
                DISPC_IRQ_VID3_FIFO_UNDERFLOW,
        };
 
-       static const unsigned sync_lost_bits[] = {
-               DISPC_IRQ_SYNC_LOST,
-               DISPC_IRQ_SYNC_LOST_DIGIT,
-               DISPC_IRQ_SYNC_LOST2,
-       };
-
        spin_lock_irqsave(&dispc.irq_lock, flags);
        errors = dispc.error_irqs;
        dispc.error_irqs = 0;
@@ -3484,7 +3500,7 @@ static void dispc_error_worker(struct work_struct *work)
                unsigned bit;
 
                mgr = omap_dss_get_overlay_manager(i);
-               bit = sync_lost_bits[i];
+               bit = mgr_desc[i].sync_lost_irq;
 
                if (bit & errors) {
                        struct omap_dss_device *dssdev = mgr->device;
@@ -3603,6 +3619,8 @@ static void _omap_dispc_initialize_irq(void)
        dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR;
        if (dss_has_feature(FEAT_MGR_LCD2))
                dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST2;
+       if (dss_has_feature(FEAT_MGR_LCD3))
+               dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST3;
        if (dss_feat_get_num_ovls() > 3)
                dispc.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW;
 
index f278080e1063f2a92b102b0157ab80396bfe095d..92d8a9be86fc640a5efae0541dd410166a03550f 100644 (file)
@@ -36,6 +36,8 @@
 #define DISPC_CONTROL2                 0x0238
 #define DISPC_CONFIG2                  0x0620
 #define DISPC_DIVISOR                  0x0804
+#define DISPC_CONTROL3                  0x0848
+#define DISPC_CONFIG3                   0x084C
 
 /* DISPC overlay registers */
 #define DISPC_OVL_BA0(n)               (DISPC_OVL_BASE(n) + \
@@ -118,6 +120,8 @@ static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel)
                return 0x0050;
        case OMAP_DSS_CHANNEL_LCD2:
                return 0x03AC;
+       case OMAP_DSS_CHANNEL_LCD3:
+               return 0x0814;
        default:
                BUG();
                return 0;
@@ -133,6 +137,8 @@ static inline u16 DISPC_TRANS_COLOR(enum omap_channel channel)
                return 0x0058;
        case OMAP_DSS_CHANNEL_LCD2:
                return 0x03B0;
+       case OMAP_DSS_CHANNEL_LCD3:
+               return 0x0818;
        default:
                BUG();
                return 0;
@@ -149,6 +155,8 @@ static inline u16 DISPC_TIMING_H(enum omap_channel channel)
                return 0;
        case OMAP_DSS_CHANNEL_LCD2:
                return 0x0400;
+       case OMAP_DSS_CHANNEL_LCD3:
+               return 0x0840;
        default:
                BUG();
                return 0;
@@ -165,6 +173,8 @@ static inline u16 DISPC_TIMING_V(enum omap_channel channel)
                return 0;
        case OMAP_DSS_CHANNEL_LCD2:
                return 0x0404;
+       case OMAP_DSS_CHANNEL_LCD3:
+               return 0x0844;
        default:
                BUG();
                return 0;
@@ -181,6 +191,8 @@ static inline u16 DISPC_POL_FREQ(enum omap_channel channel)
                return 0;
        case OMAP_DSS_CHANNEL_LCD2:
                return 0x0408;
+       case OMAP_DSS_CHANNEL_LCD3:
+               return 0x083C;
        default:
                BUG();
                return 0;
@@ -197,6 +209,8 @@ static inline u16 DISPC_DIVISORo(enum omap_channel channel)
                return 0;
        case OMAP_DSS_CHANNEL_LCD2:
                return 0x040C;
+       case OMAP_DSS_CHANNEL_LCD3:
+               return 0x0838;
        default:
                BUG();
                return 0;
@@ -213,6 +227,8 @@ static inline u16 DISPC_SIZE_MGR(enum omap_channel channel)
                return 0x0078;
        case OMAP_DSS_CHANNEL_LCD2:
                return 0x03CC;
+       case OMAP_DSS_CHANNEL_LCD3:
+               return 0x0834;
        default:
                BUG();
                return 0;
@@ -229,6 +245,8 @@ static inline u16 DISPC_DATA_CYCLE1(enum omap_channel channel)
                return 0;
        case OMAP_DSS_CHANNEL_LCD2:
                return 0x03C0;
+       case OMAP_DSS_CHANNEL_LCD3:
+               return 0x0828;
        default:
                BUG();
                return 0;
@@ -245,6 +263,8 @@ static inline u16 DISPC_DATA_CYCLE2(enum omap_channel channel)
                return 0;
        case OMAP_DSS_CHANNEL_LCD2:
                return 0x03C4;
+       case OMAP_DSS_CHANNEL_LCD3:
+               return 0x082C;
        default:
                BUG();
                return 0;
@@ -261,6 +281,8 @@ static inline u16 DISPC_DATA_CYCLE3(enum omap_channel channel)
                return 0;
        case OMAP_DSS_CHANNEL_LCD2:
                return 0x03C8;
+       case OMAP_DSS_CHANNEL_LCD3:
+               return 0x0830;
        default:
                BUG();
                return 0;
@@ -277,6 +299,8 @@ static inline u16 DISPC_CPR_COEF_R(enum omap_channel channel)
                return 0;
        case OMAP_DSS_CHANNEL_LCD2:
                return 0x03BC;
+       case OMAP_DSS_CHANNEL_LCD3:
+               return 0x0824;
        default:
                BUG();
                return 0;
@@ -293,6 +317,8 @@ static inline u16 DISPC_CPR_COEF_G(enum omap_channel channel)
                return 0;
        case OMAP_DSS_CHANNEL_LCD2:
                return 0x03B8;
+       case OMAP_DSS_CHANNEL_LCD3:
+               return 0x0820;
        default:
                BUG();
                return 0;
@@ -309,6 +335,8 @@ static inline u16 DISPC_CPR_COEF_B(enum omap_channel channel)
                return 0;
        case OMAP_DSS_CHANNEL_LCD2:
                return 0x03B4;
+       case OMAP_DSS_CHANNEL_LCD3:
+               return 0x081C;
        default:
                BUG();
                return 0;
index 24901063037024f25bae5ee1ba44ec3fe074001a..5bd957e85505465990301d4a78abf4dd2f8e05a4 100644 (file)
@@ -116,7 +116,7 @@ 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;
+       struct omap_video_timings t = dssdev->panel.timings;
        int r, found;
 
        if (!dssdev->driver->set_timings || !dssdev->driver->check_timings)
@@ -316,44 +316,6 @@ void omapdss_default_get_timings(struct omap_dss_device *dssdev,
 }
 EXPORT_SYMBOL(omapdss_default_get_timings);
 
-/* 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_HDMI:
-       case OMAP_DISPLAY_TYPE_VENC:
-       case OMAP_DISPLAY_TYPE_SDI:
-               bpp = 24;
-               break;
-       case OMAP_DISPLAY_TYPE_DBI:
-               bpp = dssdev->ctrl.pixel_size;
-               break;
-       case OMAP_DISPLAY_TYPE_DSI:
-               bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
-               break;
-       default:
-               BUG();
-               return false;
-       }
-
-       return bpp > 16;
-}
-
 void dss_init_device(struct platform_device *pdev,
                struct omap_dss_device *dssdev)
 {
index 8c2056c9537bd1162d08dd0c6d63a2437b926c45..3266be23fc0dbd529e4e1b457c37462a3faef442 100644 (file)
@@ -38,6 +38,8 @@
 static struct {
        struct regulator *vdds_dsi_reg;
        struct platform_device *dsidev;
+
+       struct dss_lcd_mgr_config mgr_config;
 } dpi;
 
 static struct platform_device *dpi_get_dsidev(enum omap_dss_clk_source clk)
@@ -64,7 +66,7 @@ static bool dpi_use_dsi_pll(struct omap_dss_device *dssdev)
                return false;
 }
 
-static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft,
+static int dpi_set_dsi_clk(struct omap_dss_device *dssdev,
                unsigned long pck_req, unsigned long *fck, int *lck_div,
                int *pck_div)
 {
@@ -72,8 +74,8 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft,
        struct dispc_clock_info dispc_cinfo;
        int r;
 
-       r = dsi_pll_calc_clock_div_pck(dpi.dsidev, is_tft, pck_req,
-                       &dsi_cinfo, &dispc_cinfo);
+       r = dsi_pll_calc_clock_div_pck(dpi.dsidev, pck_req, &dsi_cinfo,
+                       &dispc_cinfo);
        if (r)
                return r;
 
@@ -83,11 +85,7 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft,
 
        dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src);
 
-       r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
-       if (r) {
-               dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
-               return r;
-       }
+       dpi.mgr_config.clock_info = dispc_cinfo;
 
        *fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
        *lck_div = dispc_cinfo.lck_div;
@@ -96,7 +94,7 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft,
        return 0;
 }
 
-static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft,
+static int dpi_set_dispc_clk(struct omap_dss_device *dssdev,
                unsigned long pck_req, unsigned long *fck, int *lck_div,
                int *pck_div)
 {
@@ -104,7 +102,7 @@ static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft,
        struct dispc_clock_info dispc_cinfo;
        int r;
 
-       r = dss_calc_clock_div(is_tft, pck_req, &dss_cinfo, &dispc_cinfo);
+       r = dss_calc_clock_div(pck_req, &dss_cinfo, &dispc_cinfo);
        if (r)
                return r;
 
@@ -112,9 +110,7 @@ static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft,
        if (r)
                return r;
 
-       r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
-       if (r)
-               return r;
+       dpi.mgr_config.clock_info = dispc_cinfo;
 
        *fck = dss_cinfo.fck;
        *lck_div = dispc_cinfo.lck_div;
@@ -129,20 +125,14 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)
        int lck_div = 0, pck_div = 0;
        unsigned long fck = 0;
        unsigned long pck;
-       bool is_tft;
        int r = 0;
 
-       dispc_mgr_set_pol_freq(dssdev->manager->id, dssdev->panel.config,
-                       dssdev->panel.acbi, dssdev->panel.acb);
-
-       is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
-
        if (dpi_use_dsi_pll(dssdev))
-               r = dpi_set_dsi_clk(dssdev, is_tft, t->pixel_clock * 1000,
-                               &fck, &lck_div, &pck_div);
+               r = dpi_set_dsi_clk(dssdev, t->pixel_clock * 1000, &fck,
+                               &lck_div, &pck_div);
        else
-               r = dpi_set_dispc_clk(dssdev, is_tft, t->pixel_clock * 1000,
-                               &fck, &lck_div, &pck_div);
+               r = dpi_set_dispc_clk(dssdev, t->pixel_clock * 1000, &fck,
+                               &lck_div, &pck_div);
        if (r)
                return r;
 
@@ -161,19 +151,18 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)
        return 0;
 }
 
-static void dpi_basic_init(struct omap_dss_device *dssdev)
+static void dpi_config_lcd_manager(struct omap_dss_device *dssdev)
 {
-       bool is_tft;
+       dpi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
+
+       dpi.mgr_config.stallmode = false;
+       dpi.mgr_config.fifohandcheck = false;
 
-       is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
+       dpi.mgr_config.video_port_width = dssdev->phy.dpi.data_lines;
 
-       dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS);
-       dispc_mgr_enable_stallmode(dssdev->manager->id, false);
+       dpi.mgr_config.lcden_sig_polarity = 0;
 
-       dispc_mgr_set_lcd_display_type(dssdev->manager->id, is_tft ?
-                       OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN);
-       dispc_mgr_set_tft_data_lines(dssdev->manager->id,
-                       dssdev->phy.dpi.data_lines);
+       dss_mgr_set_lcd_config(dssdev->manager, &dpi.mgr_config);
 }
 
 int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
@@ -206,8 +195,6 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
        if (r)
                goto err_get_dispc;
 
-       dpi_basic_init(dssdev);
-
        if (dpi_use_dsi_pll(dssdev)) {
                r = dsi_runtime_get(dpi.dsidev);
                if (r)
@@ -222,6 +209,8 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
        if (r)
                goto err_set_mode;
 
+       dpi_config_lcd_manager(dssdev);
+
        mdelay(2);
 
        r = dss_mgr_enable(dssdev->manager);
@@ -292,7 +281,6 @@ EXPORT_SYMBOL(dpi_set_timings);
 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;
@@ -305,11 +293,9 @@ int dpi_check_timings(struct omap_dss_device *dssdev,
        if (timings->pixel_clock == 0)
                return -EINVAL;
 
-       is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
-
        if (dpi_use_dsi_pll(dssdev)) {
                struct dsi_clock_info dsi_cinfo;
-               r = dsi_pll_calc_clock_div_pck(dpi.dsidev, is_tft,
+               r = dsi_pll_calc_clock_div_pck(dpi.dsidev,
                                timings->pixel_clock * 1000,
                                &dsi_cinfo, &dispc_cinfo);
 
@@ -319,7 +305,7 @@ int dpi_check_timings(struct omap_dss_device *dssdev,
                fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
        } else {
                struct dss_clock_info dss_cinfo;
-               r = dss_calc_clock_div(is_tft, timings->pixel_clock * 1000,
+               r = dss_calc_clock_div(timings->pixel_clock * 1000,
                                &dss_cinfo, &dispc_cinfo);
 
                if (r)
index 14ce8cc079e3d1840e982734b4e2644770ba9b72..b07e8864f82fd4f034f5b59cf2009850bb3ec6d1 100644 (file)
@@ -331,6 +331,8 @@ struct dsi_data {
        unsigned num_lanes_used;
 
        unsigned scp_clk_refcount;
+
+       struct dss_lcd_mgr_config mgr_config;
 };
 
 struct dsi_packet_sent_handler_data {
@@ -1085,9 +1087,9 @@ static inline void dsi_enable_pll_clock(struct platform_device *dsidev,
        struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
 
        if (enable)
-               clk_enable(dsi->sys_clk);
+               clk_prepare_enable(dsi->sys_clk);
        else
-               clk_disable(dsi->sys_clk);
+               clk_disable_unprepare(dsi->sys_clk);
 
        if (enable && dsi->pll_locked) {
                if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1)
@@ -1316,7 +1318,7 @@ static int dsi_calc_clock_rates(struct platform_device *dsidev,
        return 0;
 }
 
-int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft,
+int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev,
                unsigned long req_pck, struct dsi_clock_info *dsi_cinfo,
                struct dispc_clock_info *dispc_cinfo)
 {
@@ -1335,8 +1337,8 @@ int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft,
                        dsi->cache_cinfo.clkin == dss_sys_clk) {
                DSSDBG("DSI clock info found from cache\n");
                *dsi_cinfo = dsi->cache_cinfo;
-               dispc_find_clk_divs(is_tft, req_pck,
-                       dsi_cinfo->dsi_pll_hsdiv_dispc_clk, dispc_cinfo);
+               dispc_find_clk_divs(req_pck, dsi_cinfo->dsi_pll_hsdiv_dispc_clk,
+                       dispc_cinfo);
                return 0;
        }
 
@@ -1402,7 +1404,7 @@ retry:
 
                                match = 1;
 
-                               dispc_find_clk_divs(is_tft, req_pck,
+                               dispc_find_clk_divs(req_pck,
                                                cur.dsi_pll_hsdiv_dispc_clk,
                                                &cur_dispc);
 
@@ -3631,17 +3633,14 @@ static void dsi_config_vp_num_line_buffers(struct omap_dss_device *dssdev)
 static void dsi_config_vp_sync_events(struct omap_dss_device *dssdev)
 {
        struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
-       int de_pol = dssdev->panel.dsi_vm_data.vp_de_pol;
-       int hsync_pol = dssdev->panel.dsi_vm_data.vp_hsync_pol;
-       int vsync_pol = dssdev->panel.dsi_vm_data.vp_vsync_pol;
        bool vsync_end = dssdev->panel.dsi_vm_data.vp_vsync_end;
        bool hsync_end = dssdev->panel.dsi_vm_data.vp_hsync_end;
        u32 r;
 
        r = dsi_read_reg(dsidev, DSI_CTRL);
-       r = FLD_MOD(r, de_pol, 9, 9);           /* VP_DE_POL */
-       r = FLD_MOD(r, hsync_pol, 10, 10);      /* VP_HSYNC_POL */
-       r = FLD_MOD(r, vsync_pol, 11, 11);      /* VP_VSYNC_POL */
+       r = FLD_MOD(r, 1, 9, 9);                /* VP_DE_POL */
+       r = FLD_MOD(r, 1, 10, 10);              /* VP_HSYNC_POL */
+       r = FLD_MOD(r, 1, 11, 11);              /* VP_VSYNC_POL */
        r = FLD_MOD(r, 1, 15, 15);              /* VP_VSYNC_START */
        r = FLD_MOD(r, vsync_end, 16, 16);      /* VP_VSYNC_END */
        r = FLD_MOD(r, 1, 17, 17);              /* VP_HSYNC_START */
@@ -4340,52 +4339,101 @@ EXPORT_SYMBOL(omap_dsi_update);
 
 /* Display funcs */
 
+static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev)
+{
+       struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
+       struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+       struct dispc_clock_info dispc_cinfo;
+       int r;
+       unsigned long long fck;
+
+       fck = dsi_get_pll_hsdiv_dispc_rate(dsidev);
+
+       dispc_cinfo.lck_div = dssdev->clocks.dispc.channel.lck_div;
+       dispc_cinfo.pck_div = dssdev->clocks.dispc.channel.pck_div;
+
+       r = dispc_calc_clock_rates(fck, &dispc_cinfo);
+       if (r) {
+               DSSERR("Failed to calc dispc clocks\n");
+               return r;
+       }
+
+       dsi->mgr_config.clock_info = dispc_cinfo;
+
+       return 0;
+}
+
 static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
 {
+       struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
+       struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+       struct omap_video_timings timings;
        int r;
+       u32 irq = 0;
 
        if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) {
                u16 dw, dh;
-               u32 irq;
-               struct omap_video_timings timings = {
-                       .hsw            = 1,
-                       .hfp            = 1,
-                       .hbp            = 1,
-                       .vsw            = 1,
-                       .vfp            = 0,
-                       .vbp            = 0,
-               };
 
                dssdev->driver->get_resolution(dssdev, &dw, &dh);
+
                timings.x_res = dw;
                timings.y_res = dh;
+               timings.hsw = 1;
+               timings.hfp = 1;
+               timings.hbp = 1;
+               timings.vsw = 1;
+               timings.vfp = 0;
+               timings.vbp = 0;
 
-               irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ?
-                       DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2;
+               irq = dispc_mgr_get_framedone_irq(dssdev->manager->id);
 
                r = omap_dispc_register_isr(dsi_framedone_irq_callback,
                        (void *) dssdev, irq);
                if (r) {
                        DSSERR("can't get FRAMEDONE irq\n");
-                       return r;
+                       goto err;
                }
 
-               dispc_mgr_enable_stallmode(dssdev->manager->id, true);
-               dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 1);
-
-               dss_mgr_set_timings(dssdev->manager, &timings);
+               dsi->mgr_config.stallmode = true;
+               dsi->mgr_config.fifohandcheck = true;
        } else {
-               dispc_mgr_enable_stallmode(dssdev->manager->id, false);
-               dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 0);
+               timings = dssdev->panel.timings;
 
-               dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings);
+               dsi->mgr_config.stallmode = false;
+               dsi->mgr_config.fifohandcheck = false;
        }
 
-               dispc_mgr_set_lcd_display_type(dssdev->manager->id,
-                       OMAP_DSS_LCD_DISPLAY_TFT);
-               dispc_mgr_set_tft_data_lines(dssdev->manager->id,
-                       dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt));
+       /*
+        * override interlace, logic level and edge related parameters in
+        * omap_video_timings with default values
+        */
+       timings.interlace = false;
+       timings.hsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
+       timings.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
+       timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
+       timings.de_level = OMAPDSS_SIG_ACTIVE_HIGH;
+       timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
+
+       dss_mgr_set_timings(dssdev->manager, &timings);
+
+       r = dsi_configure_dispc_clocks(dssdev);
+       if (r)
+               goto err1;
+
+       dsi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
+       dsi->mgr_config.video_port_width =
+                       dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
+       dsi->mgr_config.lcden_sig_polarity = 0;
+
+       dss_mgr_set_lcd_config(dssdev->manager, &dsi->mgr_config);
+
        return 0;
+err1:
+       if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE)
+               omap_dispc_unregister_isr(dsi_framedone_irq_callback,
+                       (void *) dssdev, irq);
+err:
+       return r;
 }
 
 static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev)
@@ -4393,8 +4441,7 @@ static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev)
        if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) {
                u32 irq;
 
-               irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ?
-                       DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2;
+               irq = dispc_mgr_get_framedone_irq(dssdev->manager->id);
 
                omap_dispc_unregister_isr(dsi_framedone_irq_callback,
                        (void *) dssdev, irq);
@@ -4426,33 +4473,6 @@ static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev)
        return 0;
 }
 
-static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev)
-{
-       struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
-       struct dispc_clock_info dispc_cinfo;
-       int r;
-       unsigned long long fck;
-
-       fck = dsi_get_pll_hsdiv_dispc_rate(dsidev);
-
-       dispc_cinfo.lck_div = dssdev->clocks.dispc.channel.lck_div;
-       dispc_cinfo.pck_div = dssdev->clocks.dispc.channel.pck_div;
-
-       r = dispc_calc_clock_rates(fck, &dispc_cinfo);
-       if (r) {
-               DSSERR("Failed to calc dispc clocks\n");
-               return r;
-       }
-
-       r = dispc_mgr_set_clock_div(dssdev->manager->id, &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)
 {
        struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
@@ -4474,10 +4494,6 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
 
        DSSDBG("PLL OK\n");
 
-       r = dsi_configure_dispc_clocks(dssdev);
-       if (r)
-               goto err2;
-
        r = dsi_cio_init(dssdev);
        if (r)
                goto err2;
index d2b57197b292086cd95a32ef1b33136c0e216166..04b4586113e34928e1784e22c844da287d5750ed 100644 (file)
@@ -388,7 +388,8 @@ void dss_select_lcd_clk_source(enum omap_channel channel,
                dsi_wait_pll_hsdiv_dispc_active(dsidev);
                break;
        case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC:
-               BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2);
+               BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2 &&
+                      channel != OMAP_DSS_CHANNEL_LCD3);
                b = 1;
                dsidev = dsi_get_dsidev_from_id(1);
                dsi_wait_pll_hsdiv_dispc_active(dsidev);
@@ -398,10 +399,12 @@ void dss_select_lcd_clk_source(enum omap_channel channel,
                return;
        }
 
-       pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 12;
+       pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 :
+            (channel == OMAP_DSS_CHANNEL_LCD2 ? 12 : 19);
        REG_FLD_MOD(DSS_CONTROL, b, pos, pos);  /* LCDx_CLK_SWITCH */
 
-       ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1;
+       ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 :
+           (channel == OMAP_DSS_CHANNEL_LCD2 ? 1 : 2);
        dss.lcd_clk_source[ix] = clk_src;
 }
 
@@ -418,7 +421,8 @@ enum omap_dss_clk_source dss_get_dsi_clk_source(int dsi_module)
 enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel)
 {
        if (dss_has_feature(FEAT_LCD_CLK_SRC)) {
-               int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1;
+               int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 :
+                       (channel == OMAP_DSS_CHANNEL_LCD2 ? 1 : 2);
                return dss.lcd_clk_source[ix];
        } else {
                /* LCD_CLK source is the same as DISPC_FCLK source for
@@ -502,8 +506,7 @@ unsigned long dss_get_dpll4_rate(void)
                return 0;
 }
 
-int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
-               struct dss_clock_info *dss_cinfo,
+int dss_calc_clock_div(unsigned long req_pck, struct dss_clock_info *dss_cinfo,
                struct dispc_clock_info *dispc_cinfo)
 {
        unsigned long prate;
@@ -551,7 +554,7 @@ retry:
                fck = clk_get_rate(dss.dss_clk);
                fck_div = 1;
 
-               dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
+               dispc_find_clk_divs(req_pck, fck, &cur_dispc);
                match = 1;
 
                best_dss.fck = fck;
@@ -581,7 +584,7 @@ retry:
 
                        match = 1;
 
-                       dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
+                       dispc_find_clk_divs(req_pck, fck, &cur_dispc);
 
                        if (abs(cur_dispc.pck - req_pck) <
                                        abs(best_dispc.pck - req_pck)) {
index dd1092ceaeef91d0390c23d152b4c65f680cbc09..f67afe76f217f01c1aea097b49e26fefd0af0ce2 100644 (file)
@@ -152,6 +152,25 @@ struct dsi_clock_info {
        u16 lp_clk_div;
 };
 
+struct reg_field {
+       u16 reg;
+       u8 high;
+       u8 low;
+};
+
+struct dss_lcd_mgr_config {
+       enum dss_io_pad_mode io_pad_mode;
+
+       bool stallmode;
+       bool fifohandcheck;
+
+       struct dispc_clock_info clock_info;
+
+       int video_port_width;
+
+       int lcden_sig_polarity;
+};
+
 struct seq_file;
 struct platform_device;
 
@@ -188,6 +207,8 @@ int dss_mgr_set_device(struct omap_overlay_manager *mgr,
 int dss_mgr_unset_device(struct omap_overlay_manager *mgr);
 void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
                struct omap_video_timings *timings);
+void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr,
+               const struct dss_lcd_mgr_config *config);
 const struct omap_video_timings *dss_mgr_get_timings(struct omap_overlay_manager *mgr);
 
 bool dss_ovl_is_enabled(struct omap_overlay *ovl);
@@ -210,8 +231,6 @@ 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);
 
 /* manager */
 int dss_init_overlay_managers(struct platform_device *pdev);
@@ -223,8 +242,18 @@ int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
 int dss_mgr_check(struct omap_overlay_manager *mgr,
                struct omap_overlay_manager_info *info,
                const struct omap_video_timings *mgr_timings,
+               const struct dss_lcd_mgr_config *config,
                struct omap_overlay_info **overlay_infos);
 
+static inline bool dss_mgr_is_lcd(enum omap_channel id)
+{
+       if (id == OMAP_DSS_CHANNEL_LCD || id == OMAP_DSS_CHANNEL_LCD2 ||
+                       id == OMAP_DSS_CHANNEL_LCD3)
+               return true;
+       else
+               return false;
+}
+
 /* overlay */
 void dss_init_overlays(struct platform_device *pdev);
 void dss_uninit_overlays(struct platform_device *pdev);
@@ -234,6 +263,8 @@ int dss_ovl_simple_check(struct omap_overlay *ovl,
                const struct omap_overlay_info *info);
 int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info,
                const struct omap_video_timings *mgr_timings);
+bool dss_ovl_use_replication(struct dss_lcd_mgr_config config,
+               enum omap_color_mode mode);
 
 /* DSS */
 int dss_init_platform_driver(void) __init;
@@ -268,8 +299,7 @@ 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,
+int dss_calc_clock_div(unsigned long req_pck, struct dss_clock_info *dss_cinfo,
                struct dispc_clock_info *dispc_cinfo);
 
 /* SDI */
@@ -296,7 +326,7 @@ u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt);
 unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev);
 int dsi_pll_set_clock_div(struct platform_device *dsidev,
                struct dsi_clock_info *cinfo);
-int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft,
+int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev,
                unsigned long req_pck, struct dsi_clock_info *cinfo,
                struct dispc_clock_info *dispc_cinfo);
 int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk,
@@ -330,7 +360,7 @@ static inline int dsi_pll_set_clock_div(struct platform_device *dsidev,
        return -ENODEV;
 }
 static inline int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev,
-               bool is_tft, unsigned long req_pck,
+               unsigned long req_pck,
                struct dsi_clock_info *dsi_cinfo,
                struct dispc_clock_info *dispc_cinfo)
 {
@@ -387,7 +417,7 @@ void dispc_set_loadmode(enum omap_dss_load_mode mode);
 bool dispc_mgr_timings_ok(enum omap_channel channel,
                const struct omap_video_timings *timings);
 unsigned long dispc_fclk_rate(void);
-void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
+void dispc_find_clk_divs(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);
@@ -398,8 +428,7 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane,
                u32 *fifo_low, u32 *fifo_high, bool use_fifomerge,
                bool manual_update);
 int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
-               bool ilace, bool replication,
-               const struct omap_video_timings *mgr_timings);
+               bool replication, const struct omap_video_timings *mgr_timings);
 int dispc_ovl_enable(enum omap_plane plane, bool enable);
 void dispc_ovl_set_channel_out(enum omap_plane plane,
                enum omap_channel channel);
@@ -415,16 +444,13 @@ bool dispc_mgr_is_channel_enabled(enum omap_channel channel);
 void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode);
 void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable);
 void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines);
-void dispc_mgr_set_lcd_display_type(enum omap_channel channel,
-               enum omap_lcd_display_type type);
+void dispc_mgr_set_lcd_type_tft(enum omap_channel channel);
 void dispc_mgr_set_timings(enum omap_channel channel,
                struct omap_video_timings *timings);
-void dispc_mgr_set_pol_freq(enum omap_channel channel,
-               enum omap_panel_config config, u8 acbi, u8 acb);
 unsigned long dispc_mgr_lclk_rate(enum omap_channel channel);
 unsigned long dispc_mgr_pclk_rate(enum omap_channel channel);
 unsigned long dispc_core_clk_rate(void);
-int dispc_mgr_set_clock_div(enum omap_channel channel,
+void dispc_mgr_set_clock_div(enum omap_channel channel,
                struct dispc_clock_info *cinfo);
 int dispc_mgr_get_clock_div(enum omap_channel channel,
                struct dispc_clock_info *cinfo);
index bdf469f080e75e3742cb350541ec08362fb2f683..996ffcbfed58f4991b98ce7c34971dd0a400dba2 100644 (file)
@@ -24,9 +24,9 @@
 #include "ti_hdmi.h"
 #endif
 
-#define MAX_DSS_MANAGERS       3
+#define MAX_DSS_MANAGERS       4
 #define MAX_DSS_OVERLAYS       4
-#define MAX_DSS_LCD_MANAGERS   2
+#define MAX_DSS_LCD_MANAGERS   3
 #define MAX_NUM_DSI            2
 
 /* DSS has feature id */
@@ -36,6 +36,7 @@ enum dss_feat_id {
        FEAT_PCKFREEENABLE,
        FEAT_FUNCGATED,
        FEAT_MGR_LCD2,
+       FEAT_MGR_LCD3,
        FEAT_LINEBUFFERSPLIT,
        FEAT_ROWREPEATENABLE,
        FEAT_RESIZECONF,
index 26a2430a70288d6cf468357d854fe3429b245f72..060216fdc5783da6675ebddb32f5ef3aa7cdfa70 100644 (file)
@@ -78,43 +78,214 @@ static struct {
  */
 
 static const struct hdmi_config cea_timings[] = {
-{ {640, 480, 25200, 96, 16, 48, 2, 10, 33, 0, 0, 0}, {1, HDMI_HDMI} },
-{ {720, 480, 27027, 62, 16, 60, 6, 9, 30, 0, 0, 0}, {2, HDMI_HDMI} },
-{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20, 1, 1, 0}, {4, HDMI_HDMI} },
-{ {1920, 540, 74250, 44, 88, 148, 5, 2, 15, 1, 1, 1}, {5, HDMI_HDMI} },
-{ {1440, 240, 27027, 124, 38, 114, 3, 4, 15, 0, 0, 1}, {6, HDMI_HDMI} },
-{ {1920, 1080, 148500, 44, 88, 148, 5, 4, 36, 1, 1, 0}, {16, HDMI_HDMI} },
-{ {720, 576, 27000, 64, 12, 68, 5, 5, 39, 0, 0, 0}, {17, HDMI_HDMI} },
-{ {1280, 720, 74250, 40, 440, 220, 5, 5, 20, 1, 1, 0}, {19, HDMI_HDMI} },
-{ {1920, 540, 74250, 44, 528, 148, 5, 2, 15, 1, 1, 1}, {20, HDMI_HDMI} },
-{ {1440, 288, 27000, 126, 24, 138, 3, 2, 19, 0, 0, 1}, {21, HDMI_HDMI} },
-{ {1440, 576, 54000, 128, 24, 136, 5, 5, 39, 0, 0, 0}, {29, HDMI_HDMI} },
-{ {1920, 1080, 148500, 44, 528, 148, 5, 4, 36, 1, 1, 0}, {31, HDMI_HDMI} },
-{ {1920, 1080, 74250, 44, 638, 148, 5, 4, 36, 1, 1, 0}, {32, HDMI_HDMI} },
-{ {2880, 480, 108108, 248, 64, 240, 6, 9, 30, 0, 0, 0}, {35, HDMI_HDMI} },
-{ {2880, 576, 108000, 256, 48, 272, 5, 5, 39, 0, 0, 0}, {37, HDMI_HDMI} },
+       {
+               { 640, 480, 25200, 96, 16, 48, 2, 10, 33,
+                       OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+                       false, },
+               { 1, HDMI_HDMI },
+       },
+       {
+               { 720, 480, 27027, 62, 16, 60, 6, 9, 30,
+                       OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+                       false, },
+               { 2, HDMI_HDMI },
+       },
+       {
+               { 1280, 720, 74250, 40, 110, 220, 5, 5, 20,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 4, HDMI_HDMI },
+       },
+       {
+               { 1920, 540, 74250, 44, 88, 148, 5, 2, 15,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       true, },
+               { 5, HDMI_HDMI },
+       },
+       {
+               { 1440, 240, 27027, 124, 38, 114, 3, 4, 15,
+                       OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+                       true, },
+               { 6, HDMI_HDMI },
+       },
+       {
+               { 1920, 1080, 148500, 44, 88, 148, 5, 4, 36,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 16, HDMI_HDMI },
+       },
+       {
+               { 720, 576, 27000, 64, 12, 68, 5, 5, 39,
+                       OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+                       false, },
+               { 17, HDMI_HDMI },
+       },
+       {
+               { 1280, 720, 74250, 40, 440, 220, 5, 5, 20,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 19, HDMI_HDMI },
+       },
+       {
+               { 1920, 540, 74250, 44, 528, 148, 5, 2, 15,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       true, },
+               { 20, HDMI_HDMI },
+       },
+       {
+               { 1440, 288, 27000, 126, 24, 138, 3, 2, 19,
+                       OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+                       true, },
+               { 21, HDMI_HDMI },
+       },
+       {
+               { 1440, 576, 54000, 128, 24, 136, 5, 5, 39,
+                       OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+                       false, },
+               { 29, HDMI_HDMI },
+       },
+       {
+               { 1920, 1080, 148500, 44, 528, 148, 5, 4, 36,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 31, HDMI_HDMI },
+       },
+       {
+               { 1920, 1080, 74250, 44, 638, 148, 5, 4, 36,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 32, HDMI_HDMI },
+       },
+       {
+               { 2880, 480, 108108, 248, 64, 240, 6, 9, 30,
+                       OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+                       false, },
+               { 35, HDMI_HDMI },
+       },
+       {
+               { 2880, 576, 108000, 256, 48, 272, 5, 5, 39,
+                       OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+                       false, },
+               { 37, HDMI_HDMI },
+       },
 };
+
 static const struct hdmi_config vesa_timings[] = {
 /* VESA From Here */
-{ {640, 480, 25175, 96, 16, 48, 2 , 11, 31, 0, 0, 0}, {4, HDMI_DVI} },
-{ {800, 600, 40000, 128, 40, 88, 4 , 1, 23, 1, 1, 0}, {9, HDMI_DVI} },
-{ {848, 480, 33750, 112, 16, 112, 8 , 6, 23, 1, 1, 0}, {0xE, HDMI_DVI} },
-{ {1280, 768, 79500, 128, 64, 192, 7 , 3, 20, 1, 0, 0}, {0x17, HDMI_DVI} },
-{ {1280, 800, 83500, 128, 72, 200, 6 , 3, 22, 1, 0, 0}, {0x1C, HDMI_DVI} },
-{ {1360, 768, 85500, 112, 64, 256, 6 , 3, 18, 1, 1, 0}, {0x27, HDMI_DVI} },
-{ {1280, 960, 108000, 112, 96, 312, 3 , 1, 36, 1, 1, 0}, {0x20, HDMI_DVI} },
-{ {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38, 1, 1, 0}, {0x23, HDMI_DVI} },
-{ {1024, 768, 65000, 136, 24, 160, 6, 3, 29, 0, 0, 0}, {0x10, HDMI_DVI} },
-{ {1400, 1050, 121750, 144, 88, 232, 4, 3, 32, 1, 0, 0}, {0x2A, HDMI_DVI} },
-{ {1440, 900, 106500, 152, 80, 232, 6, 3, 25, 1, 0, 0}, {0x2F, HDMI_DVI} },
-{ {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30, 1, 0, 0}, {0x3A, HDMI_DVI} },
-{ {1366, 768, 85500, 143, 70, 213, 3, 3, 24, 1, 1, 0}, {0x51, HDMI_DVI} },
-{ {1920, 1080, 148500, 44, 148, 80, 5, 4, 36, 1, 1, 0}, {0x52, HDMI_DVI} },
-{ {1280, 768, 68250, 32, 48, 80, 7, 3, 12, 0, 1, 0}, {0x16, HDMI_DVI} },
-{ {1400, 1050, 101000, 32, 48, 80, 4, 3, 23, 0, 1, 0}, {0x29, HDMI_DVI} },
-{ {1680, 1050, 119000, 32, 48, 80, 6, 3, 21, 0, 1, 0}, {0x39, HDMI_DVI} },
-{ {1280, 800, 79500, 32, 48, 80, 6, 3, 14, 0, 1, 0}, {0x1B, HDMI_DVI} },
-{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20, 1, 1, 0}, {0x55, HDMI_DVI} }
+       {
+               { 640, 480, 25175, 96, 16, 48, 2, 11, 31,
+                       OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+                       false, },
+               { 4, HDMI_DVI },
+       },
+       {
+               { 800, 600, 40000, 128, 40, 88, 4, 1, 23,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 9, HDMI_DVI },
+       },
+       {
+               { 848, 480, 33750, 112, 16, 112, 8, 6, 23,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 0xE, HDMI_DVI },
+       },
+       {
+               { 1280, 768, 79500, 128, 64, 192, 7, 3, 20,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+                       false, },
+               { 0x17, HDMI_DVI },
+       },
+       {
+               { 1280, 800, 83500, 128, 72, 200, 6, 3, 22,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+                       false, },
+               { 0x1C, HDMI_DVI },
+       },
+       {
+               { 1360, 768, 85500, 112, 64, 256, 6, 3, 18,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 0x27, HDMI_DVI },
+       },
+       {
+               { 1280, 960, 108000, 112, 96, 312, 3, 1, 36,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 0x20, HDMI_DVI },
+       },
+       {
+               { 1280, 1024, 108000, 112, 48, 248, 3, 1, 38,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 0x23, HDMI_DVI },
+       },
+       {
+               { 1024, 768, 65000, 136, 24, 160, 6, 3, 29,
+                       OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+                       false, },
+               { 0x10, HDMI_DVI },
+       },
+       {
+               { 1400, 1050, 121750, 144, 88, 232, 4, 3, 32,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+                       false, },
+               { 0x2A, HDMI_DVI },
+       },
+       {
+               { 1440, 900, 106500, 152, 80, 232, 6, 3, 25,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+                       false, },
+               { 0x2F, HDMI_DVI },
+       },
+       {
+               { 1680, 1050, 146250, 176 , 104, 280, 6, 3, 30,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+                       false, },
+               { 0x3A, HDMI_DVI },
+       },
+       {
+               { 1366, 768, 85500, 143, 70, 213, 3, 3, 24,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 0x51, HDMI_DVI },
+       },
+       {
+               { 1920, 1080, 148500, 44, 148, 80, 5, 4, 36,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 0x52, HDMI_DVI },
+       },
+       {
+               { 1280, 768, 68250, 32, 48, 80, 7, 3, 12,
+                       OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 0x16, HDMI_DVI },
+       },
+       {
+               { 1400, 1050, 101000, 32, 48, 80, 4, 3, 23,
+                       OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 0x29, HDMI_DVI },
+       },
+       {
+               { 1680, 1050, 119000, 32, 48, 80, 6, 3, 21,
+                       OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 0x39, HDMI_DVI },
+       },
+       {
+               { 1280, 800, 79500, 32, 48, 80, 6, 3, 14,
+                       OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 0x1B, HDMI_DVI },
+       },
+       {
+               { 1280, 720, 74250, 40, 110, 220, 5, 5, 20,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 0x55, HDMI_DVI },
+       },
 };
 
 static int hdmi_runtime_get(void)
@@ -179,7 +350,7 @@ static const struct hdmi_config *hdmi_get_timings(void)
 }
 
 static bool hdmi_timings_compare(struct omap_video_timings *timing1,
-                               const struct hdmi_video_timings *timing2)
+                               const struct omap_video_timings *timing2)
 {
        int timing1_vsync, timing1_hsync, timing2_vsync, timing2_hsync;
 
@@ -758,6 +929,7 @@ static int __init omapdss_hdmihw_probe(struct platform_device *pdev)
        hdmi.ip_data.core_av_offset = HDMI_CORE_AV;
        hdmi.ip_data.pll_offset = HDMI_PLLCTRL;
        hdmi.ip_data.phy_offset = HDMI_PHY;
+       mutex_init(&hdmi.ip_data.lock);
 
        hdmi_panel_init();
 
@@ -785,7 +957,7 @@ static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
 
 static int hdmi_runtime_suspend(struct device *dev)
 {
-       clk_disable(hdmi.sys_clk);
+       clk_disable_unprepare(hdmi.sys_clk);
 
        dispc_runtime_put();
 
@@ -800,7 +972,7 @@ static int hdmi_runtime_resume(struct device *dev)
        if (r < 0)
                return r;
 
-       clk_enable(hdmi.sys_clk);
+       clk_prepare_enable(hdmi.sys_clk);
 
        return 0;
 }
index 1179e3c4b1c76565336b8e4a6041c5bc49da0964..e10844faadf91434df1c096aaf0e3dd5cc5e88d0 100644 (file)
@@ -43,10 +43,11 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev)
 {
        DSSDBG("ENTER hdmi_panel_probe\n");
 
-       dssdev->panel.config = OMAP_DSS_LCD_TFT |
-                       OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS;
-
-       dssdev->panel.timings = (struct omap_video_timings){640, 480, 25175, 96, 16, 48, 2 , 11, 31};
+       dssdev->panel.timings = (struct omap_video_timings)
+                       { 640, 480, 25175, 96, 16, 48, 2, 11, 31,
+                               OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+                               false,
+                       };
 
        DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n",
                dssdev->panel.timings.x_res,
index 0cbcde4c688a9e40925daf5c12f960f6bb75473b..53710fadc82de4f2a8f3802dbd2f2215e59dfd14 100644 (file)
@@ -500,16 +500,12 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
        if (r)
                return r;
 
-       if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) {
+       if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC)
                irq = DISPC_IRQ_EVSYNC_ODD;
-       } else if (mgr->device->type == OMAP_DISPLAY_TYPE_HDMI) {
+       else if (mgr->device->type == OMAP_DISPLAY_TYPE_HDMI)
                irq = DISPC_IRQ_EVSYNC_EVEN;
-       } else {
-               if (mgr->id == OMAP_DSS_CHANNEL_LCD)
-                       irq = DISPC_IRQ_VSYNC;
-               else
-                       irq = DISPC_IRQ_VSYNC2;
-       }
+       else
+               irq = dispc_mgr_get_vsync_irq(mgr->id);
 
        r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
 
@@ -545,6 +541,10 @@ int dss_init_overlay_managers(struct platform_device *pdev)
                        mgr->name = "lcd2";
                        mgr->id = OMAP_DSS_CHANNEL_LCD2;
                        break;
+               case 3:
+                       mgr->name = "lcd3";
+                       mgr->id = OMAP_DSS_CHANNEL_LCD3;
+                       break;
                }
 
                mgr->set_device = &dss_mgr_set_device;
@@ -665,9 +665,40 @@ int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
        return 0;
 }
 
+static int dss_mgr_check_lcd_config(struct omap_overlay_manager *mgr,
+               const struct dss_lcd_mgr_config *config)
+{
+       struct dispc_clock_info cinfo = config->clock_info;
+       int dl = config->video_port_width;
+       bool stallmode = config->stallmode;
+       bool fifohandcheck = config->fifohandcheck;
+
+       if (cinfo.lck_div < 1 || cinfo.lck_div > 255)
+               return -EINVAL;
+
+       if (cinfo.pck_div < 1 || cinfo.pck_div > 255)
+               return -EINVAL;
+
+       if (dl != 12 && dl != 16 && dl != 18 && dl != 24)
+               return -EINVAL;
+
+       /* fifohandcheck should be used only with stallmode */
+       if (stallmode == false && fifohandcheck == true)
+               return -EINVAL;
+
+       /*
+        * io pad mode can be only checked by using dssdev connected to the
+        * manager. Ignore checking these for now, add checks when manager
+        * is capable of holding information related to the connected interface
+        */
+
+       return 0;
+}
+
 int dss_mgr_check(struct omap_overlay_manager *mgr,
                struct omap_overlay_manager_info *info,
                const struct omap_video_timings *mgr_timings,
+               const struct dss_lcd_mgr_config *lcd_config,
                struct omap_overlay_info **overlay_infos)
 {
        struct omap_overlay *ovl;
@@ -683,6 +714,10 @@ int dss_mgr_check(struct omap_overlay_manager *mgr,
        if (r)
                return r;
 
+       r = dss_mgr_check_lcd_config(mgr, lcd_config);
+       if (r)
+               return r;
+
        list_for_each_entry(ovl, &mgr->overlays, list) {
                struct omap_overlay_info *oi;
                int r;
index b0ba60f88dd23d3fdf5d5583850ecc7282b4a09d..952c6fad9a8110393a3c2bdc15064aa4590e5367 100644 (file)
@@ -528,14 +528,24 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)
        struct omap_overlay_manager *lcd_mgr;
        struct omap_overlay_manager *tv_mgr;
        struct omap_overlay_manager *lcd2_mgr = NULL;
+       struct omap_overlay_manager *lcd3_mgr = NULL;
        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);
+       lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD);
+       tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_DIGIT);
+       if (dss_has_feature(FEAT_MGR_LCD3))
+               lcd3_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD3);
        if (dss_has_feature(FEAT_MGR_LCD2))
-               lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD2);
-
-       if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) {
+               lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD2);
+
+       if (dssdev->channel == OMAP_DSS_CHANNEL_LCD3) {
+               if (!lcd3_mgr->device || force) {
+                       if (lcd3_mgr->device)
+                               lcd3_mgr->unset_device(lcd3_mgr);
+                       lcd3_mgr->set_device(lcd3_mgr, dssdev);
+                       mgr = lcd3_mgr;
+               }
+       } else if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) {
                if (!lcd2_mgr->device || force) {
                        if (lcd2_mgr->device)
                                lcd2_mgr->unset_device(lcd2_mgr);
@@ -677,3 +687,16 @@ int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info,
 
        return 0;
 }
+
+/*
+ * Checks if replication logic should be used. Only use when overlay is in
+ * RGB12U or RGB16 mode, and video port width interface is 18bpp or 24bpp
+ */
+bool dss_ovl_use_replication(struct dss_lcd_mgr_config config,
+               enum omap_color_mode mode)
+{
+       if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16)
+               return false;
+
+       return config.video_port_width > 16;
+}
index 7985fa12b9b46c8c3f6da328f578a76b6267a66e..7c087424b63428d33cd2bbb1560cb476dfefab38 100644 (file)
@@ -300,10 +300,11 @@ void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width,
 }
 EXPORT_SYMBOL(omap_rfbi_write_pixels);
 
-static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width,
+static int rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width,
                u16 height, void (*callback)(void *data), void *data)
 {
        u32 l;
+       int r;
        struct omap_video_timings timings = {
                .hsw            = 1,
                .hfp            = 1,
@@ -322,7 +323,9 @@ static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width,
 
        dss_mgr_set_timings(dssdev->manager, &timings);
 
-       dispc_mgr_enable(dssdev->manager->id, true);
+       r = dss_mgr_enable(dssdev->manager);
+       if (r)
+               return r;
 
        rfbi.framedone_callback = callback;
        rfbi.framedone_callback_data = data;
@@ -335,6 +338,8 @@ static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width,
                l = FLD_MOD(l, 1, 4, 4); /* ITE */
 
        rfbi_write_reg(RFBI_CONTROL, l);
+
+       return 0;
 }
 
 static void framedone_callback(void *data, u32 mask)
@@ -814,8 +819,11 @@ int omap_rfbi_update(struct omap_dss_device *dssdev,
                u16 x, u16 y, u16 w, u16 h,
                void (*callback)(void *), void *data)
 {
-       rfbi_transfer_area(dssdev, w, h, callback, data);
-       return 0;
+       int r;
+
+       r = rfbi_transfer_area(dssdev, w, h, callback, data);
+
+       return r;
 }
 EXPORT_SYMBOL(omap_rfbi_update);
 
@@ -859,6 +867,22 @@ static void rfbi_dump_regs(struct seq_file *s)
 #undef DUMPREG
 }
 
+static void rfbi_config_lcd_manager(struct omap_dss_device *dssdev)
+{
+       struct dss_lcd_mgr_config mgr_config;
+
+       mgr_config.io_pad_mode = DSS_IO_PAD_MODE_RFBI;
+
+       mgr_config.stallmode = true;
+       /* Do we need fifohandcheck for RFBI? */
+       mgr_config.fifohandcheck = false;
+
+       mgr_config.video_port_width = dssdev->ctrl.pixel_size;
+       mgr_config.lcden_sig_polarity = 0;
+
+       dss_mgr_set_lcd_config(dssdev->manager, &mgr_config);
+}
+
 int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
 {
        int r;
@@ -885,13 +909,7 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
                goto err1;
        }
 
-       dispc_mgr_set_lcd_display_type(dssdev->manager->id,
-                       OMAP_DSS_LCD_DISPLAY_TFT);
-
-       dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_RFBI);
-       dispc_mgr_enable_stallmode(dssdev->manager->id, true);
-
-       dispc_mgr_set_tft_data_lines(dssdev->manager->id, dssdev->ctrl.pixel_size);
+       rfbi_config_lcd_manager(dssdev);
 
        rfbi_configure(dssdev->phy.rfbi.channel,
                               dssdev->ctrl.pixel_size,
index 3a43dc2a9b46c992b22770b163a5e47611b7bb9e..5d31699fbd3caf4c885840b77c76fd4512d52ed9 100644 (file)
 static struct {
        bool update_enabled;
        struct regulator *vdds_sdi_reg;
-} sdi;
 
-static void sdi_basic_init(struct omap_dss_device *dssdev)
+       struct dss_lcd_mgr_config mgr_config;
+} sdi;
 
+static void sdi_config_lcd_manager(struct omap_dss_device *dssdev)
 {
-       dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS);
-       dispc_mgr_enable_stallmode(dssdev->manager->id, false);
+       sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
+
+       sdi.mgr_config.stallmode = false;
+       sdi.mgr_config.fifohandcheck = false;
 
-       dispc_mgr_set_lcd_display_type(dssdev->manager->id,
-                       OMAP_DSS_LCD_DISPLAY_TFT);
+       sdi.mgr_config.video_port_width = 24;
+       sdi.mgr_config.lcden_sig_polarity = 1;
 
-       dispc_mgr_set_tft_data_lines(dssdev->manager->id, 24);
-       dispc_lcd_enable_signal_polarity(1);
+       dss_mgr_set_lcd_config(dssdev->manager, &sdi.mgr_config);
 }
 
 int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
@@ -52,8 +54,6 @@ int omapdss_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;
 
@@ -76,24 +76,17 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
        if (r)
                goto err_get_dispc;
 
-       sdi_basic_init(dssdev);
-
        /* 15.5.9.1.2 */
-       dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF;
-
-       dispc_mgr_set_pol_freq(dssdev->manager->id, dssdev->panel.config,
-                       dssdev->panel.acbi, dssdev->panel.acb);
+       dssdev->panel.timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
+       dssdev->panel.timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
 
-       r = dss_calc_clock_div(1, t->pixel_clock * 1000,
-                       &dss_cinfo, &dispc_cinfo);
+       r = dss_calc_clock_div(t->pixel_clock * 1000, &dss_cinfo, &dispc_cinfo);
        if (r)
                goto err_calc_clock_div;
 
-       fck = dss_cinfo.fck;
-       lck_div = dispc_cinfo.lck_div;
-       pck_div = dispc_cinfo.pck_div;
+       sdi.mgr_config.clock_info = dispc_cinfo;
 
-       pck = fck / lck_div / pck_div / 1000;
+       pck = dss_cinfo.fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div / 1000;
 
        if (pck != t->pixel_clock) {
                DSSWARN("Could not find exact pixel clock. Requested %d kHz, "
@@ -110,9 +103,7 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
        if (r)
                goto err_set_dss_clock_div;
 
-       r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
-       if (r)
-               goto err_set_dispc_clock_div;
+       sdi_config_lcd_manager(dssdev);
 
        dss_sdi_init(dssdev->phy.sdi.datapairs);
        r = dss_sdi_enable();
@@ -129,7 +120,6 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
 err_mgr_enable:
        dss_sdi_disable();
 err_sdi_enable:
-err_set_dispc_clock_div:
 err_set_dss_clock_div:
 err_calc_clock_div:
        dispc_runtime_put();
index e734cb444bc7ce30051adeb9e0f1ff69318bf296..b046c208cb9708fc30db861759c6a2cf01fde3be 100644 (file)
@@ -42,30 +42,13 @@ enum hdmi_clk_refsel {
        HDMI_REFSEL_SYSCLK = 3
 };
 
-/* HDMI timing structure */
-struct hdmi_video_timings {
-       u16 x_res;
-       u16 y_res;
-       /* Unit: KHz */
-       u32 pixel_clock;
-       u16 hsw;
-       u16 hfp;
-       u16 hbp;
-       u16 vsw;
-       u16 vfp;
-       u16 vbp;
-       bool vsync_pol;
-       bool hsync_pol;
-       bool interlace;
-};
-
 struct hdmi_cm {
        int     code;
        int     mode;
 };
 
 struct hdmi_config {
-       struct hdmi_video_timings timings;
+       struct omap_video_timings timings;
        struct hdmi_cm cm;
 };
 
@@ -177,7 +160,7 @@ struct hdmi_ip_data {
 
        /* ti_hdmi_4xxx_ip private data. These should be in a separate struct */
        int hpd_gpio;
-       bool phy_tx_enabled;
+       struct mutex lock;
 };
 int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data);
 void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data);
index 4dae1b291079c9e8e00f0719aa22328f176934ab..c23b85a20cdc5b84cd6aeec9743a4f931a9470a2 100644 (file)
@@ -157,6 +157,10 @@ static int hdmi_pll_init(struct hdmi_ip_data *ip_data)
 /* PHY_PWR_CMD */
 static int hdmi_set_phy_pwr(struct hdmi_ip_data *ip_data, enum hdmi_phy_pwr val)
 {
+       /* Return if already the state */
+       if (REG_GET(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, 5, 4) == val)
+               return 0;
+
        /* Command for power control of HDMI PHY */
        REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, val, 7, 6);
 
@@ -231,21 +235,13 @@ void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data)
 
 static int hdmi_check_hpd_state(struct hdmi_ip_data *ip_data)
 {
-       unsigned long flags;
        bool hpd;
        int r;
-       /* this should be in ti_hdmi_4xxx_ip private data */
-       static DEFINE_SPINLOCK(phy_tx_lock);
 
-       spin_lock_irqsave(&phy_tx_lock, flags);
+       mutex_lock(&ip_data->lock);
 
        hpd = gpio_get_value(ip_data->hpd_gpio);
 
-       if (hpd == ip_data->phy_tx_enabled) {
-               spin_unlock_irqrestore(&phy_tx_lock, flags);
-               return 0;
-       }
-
        if (hpd)
                r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON);
        else
@@ -257,9 +253,8 @@ static int hdmi_check_hpd_state(struct hdmi_ip_data *ip_data)
                goto err;
        }
 
-       ip_data->phy_tx_enabled = hpd;
 err:
-       spin_unlock_irqrestore(&phy_tx_lock, flags);
+       mutex_unlock(&ip_data->lock);
        return r;
 }
 
@@ -327,7 +322,6 @@ void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data)
        free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data);
 
        hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
-       ip_data->phy_tx_enabled = false;
 }
 
 static int hdmi_core_ddc_init(struct hdmi_ip_data *ip_data)
@@ -747,11 +741,15 @@ static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data,
 static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data)
 {
        u32 r;
+       bool vsync_pol, hsync_pol;
        pr_debug("Enter hdmi_wp_video_config_interface\n");
 
+       vsync_pol = ip_data->cfg.timings.vsync_level == OMAPDSS_SIG_ACTIVE_HIGH;
+       hsync_pol = ip_data->cfg.timings.hsync_level == OMAPDSS_SIG_ACTIVE_HIGH;
+
        r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG);
-       r = FLD_MOD(r, ip_data->cfg.timings.vsync_pol, 7, 7);
-       r = FLD_MOD(r, ip_data->cfg.timings.hsync_pol, 6, 6);
+       r = FLD_MOD(r, vsync_pol, 7, 7);
+       r = FLD_MOD(r, hsync_pol, 6, 6);
        r = FLD_MOD(r, ip_data->cfg.timings.interlace, 3, 3);
        r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */
        hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, r);
index 3907c8b6ecbca991e3cc9313c1a1473c247cb216..3a220877461ac9adbf3ab62369e9d10a7476a952 100644 (file)
@@ -272,6 +272,8 @@ const struct omap_video_timings omap_dss_pal_timings = {
        .vsw            = 5,
        .vfp            = 5,
        .vbp            = 41,
+
+       .interlace      = true,
 };
 EXPORT_SYMBOL(omap_dss_pal_timings);
 
@@ -285,6 +287,8 @@ const struct omap_video_timings omap_dss_ntsc_timings = {
        .vsw            = 6,
        .vfp            = 6,
        .vbp            = 31,
+
+       .interlace      = true,
 };
 EXPORT_SYMBOL(omap_dss_ntsc_timings);
 
@@ -930,7 +934,7 @@ static int __exit omap_venchw_remove(struct platform_device *pdev)
 static int venc_runtime_suspend(struct device *dev)
 {
        if (venc.tv_dac_clk)
-               clk_disable(venc.tv_dac_clk);
+               clk_disable_unprepare(venc.tv_dac_clk);
 
        dispc_runtime_put();
 
@@ -946,7 +950,7 @@ static int venc_runtime_resume(struct device *dev)
                return r;
 
        if (venc.tv_dac_clk)
-               clk_enable(venc.tv_dac_clk);
+               clk_prepare_enable(venc.tv_dac_clk);
 
        return 0;
 }
index 3450ea0966c97e6227145f3ad484f4850f0125ff..08ec1a7103f2b728420f9e534a2002b20f457d45 100644 (file)
@@ -733,6 +733,12 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
                var->lower_margin = timings.vfp;
                var->hsync_len = timings.hsw;
                var->vsync_len = timings.vsw;
+               var->sync |= timings.hsync_level == OMAPDSS_SIG_ACTIVE_HIGH ?
+                               FB_SYNC_HOR_HIGH_ACT : 0;
+               var->sync |= timings.vsync_level == OMAPDSS_SIG_ACTIVE_HIGH ?
+                               FB_SYNC_VERT_HIGH_ACT : 0;
+               var->vmode = timings.interlace ?
+                               FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;
        } else {
                var->pixclock = 0;
                var->left_margin = 0;
@@ -741,12 +747,10 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
                var->lower_margin = 0;
                var->hsync_len = 0;
                var->vsync_len = 0;
+               var->sync = 0;
+               var->vmode = FB_VMODE_NONINTERLACED;
        }
 
-       /* TODO: get these from panel->config */
-       var->vmode              = FB_VMODE_NONINTERLACED;
-       var->sync               = 0;
-
        return 0;
 }
 
@@ -1993,6 +1997,7 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
 }
 
 static int omapfb_mode_to_timings(const char *mode_str,
+               struct omap_dss_device *display,
                struct omap_video_timings *timings, u8 *bpp)
 {
        struct fb_info *fbi;
@@ -2046,6 +2051,14 @@ static int omapfb_mode_to_timings(const char *mode_str,
                goto err;
        }
 
+       if (display->driver->get_timings) {
+               display->driver->get_timings(display, timings);
+       } else {
+               timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
+               timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
+               timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
+       }
+
        timings->pixel_clock = PICOS2KHZ(var->pixclock);
        timings->hbp = var->left_margin;
        timings->hfp = var->right_margin;
@@ -2055,6 +2068,13 @@ static int omapfb_mode_to_timings(const char *mode_str,
        timings->vsw = var->vsync_len;
        timings->x_res = var->xres;
        timings->y_res = var->yres;
+       timings->hsync_level = var->sync & FB_SYNC_HOR_HIGH_ACT ?
+                               OMAPDSS_SIG_ACTIVE_HIGH :
+                               OMAPDSS_SIG_ACTIVE_LOW;
+       timings->vsync_level = var->sync & FB_SYNC_VERT_HIGH_ACT ?
+                               OMAPDSS_SIG_ACTIVE_HIGH :
+                               OMAPDSS_SIG_ACTIVE_LOW;
+       timings->interlace = var->vmode & FB_VMODE_INTERLACED;
 
        switch (var->bits_per_pixel) {
        case 16:
@@ -2085,7 +2105,7 @@ static int omapfb_set_def_mode(struct omapfb2_device *fbdev,
        struct omap_video_timings timings, temp_timings;
        struct omapfb_display_data *d;
 
-       r = omapfb_mode_to_timings(mode_str, &timings, &bpp);
+       r = omapfb_mode_to_timings(mode_str, display, &timings, &bpp);
        if (r)
                return r;
 
@@ -2178,8 +2198,17 @@ static int omapfb_parse_def_modes(struct omapfb2_device *fbdev)
 }
 
 static void fb_videomode_to_omap_timings(struct fb_videomode *m,
+               struct omap_dss_device *display,
                struct omap_video_timings *t)
 {
+       if (display->driver->get_timings) {
+               display->driver->get_timings(display, t);
+       } else {
+               t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
+               t->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
+               t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
+       }
+
        t->x_res = m->xres;
        t->y_res = m->yres;
        t->pixel_clock = PICOS2KHZ(m->pixclock);
@@ -2189,6 +2218,13 @@ static void fb_videomode_to_omap_timings(struct fb_videomode *m,
        t->vsw = m->vsync_len;
        t->vfp = m->lower_margin;
        t->vbp = m->upper_margin;
+       t->hsync_level = m->sync & FB_SYNC_HOR_HIGH_ACT ?
+                               OMAPDSS_SIG_ACTIVE_HIGH :
+                               OMAPDSS_SIG_ACTIVE_LOW;
+       t->vsync_level = m->sync & FB_SYNC_VERT_HIGH_ACT ?
+                               OMAPDSS_SIG_ACTIVE_HIGH :
+                               OMAPDSS_SIG_ACTIVE_LOW;
+       t->interlace = m->vmode & FB_VMODE_INTERLACED;
 }
 
 static int omapfb_find_best_mode(struct omap_dss_device *display,
@@ -2231,7 +2267,7 @@ static int omapfb_find_best_mode(struct omap_dss_device *display,
                if (m->xres == 2880 || m->xres == 1440)
                        continue;
 
-               fb_videomode_to_omap_timings(m, &t);
+               fb_videomode_to_omap_timings(m, display, &t);
 
                r = display->driver->check_timings(display, &t);
                if (r == 0 && best_xres < m->xres) {
@@ -2245,7 +2281,8 @@ static int omapfb_find_best_mode(struct omap_dss_device *display,
                goto err2;
        }
 
-       fb_videomode_to_omap_timings(&specs->modedb[best_idx], timings);
+       fb_videomode_to_omap_timings(&specs->modedb[best_idx], display,
+               timings);
 
        r = 0;
 
index 2c80246b18b88eee0b22a90c8fbc5581c02aad96..1d007366b9173d1a9af73b5f0735c0730464ae63 100644 (file)
@@ -84,7 +84,7 @@ static const char * const s3_names[] = {"S3 Unknown", "S3 Trio32", "S3 Trio64",
                        "S3 Virge/VX", "S3 Virge/DX", "S3 Virge/GX",
                        "S3 Virge/GX2", "S3 Virge/GX2+", "",
                        "S3 Trio3D/1X", "S3 Trio3D/2X", "S3 Trio3D/2X",
-                       "S3 Trio3D"};
+                       "S3 Trio3D", "S3 Virge/MX"};
 
 #define CHIP_UNKNOWN           0x00
 #define CHIP_732_TRIO32                0x01
@@ -105,6 +105,7 @@ static const char * const s3_names[] = {"S3 Unknown", "S3 Trio32", "S3 Trio64",
 #define CHIP_362_TRIO3D_2X     0x11
 #define CHIP_368_TRIO3D_2X     0x12
 #define CHIP_365_TRIO3D                0x13
+#define CHIP_260_VIRGE_MX      0x14
 
 #define CHIP_XXX_TRIO          0x80
 #define CHIP_XXX_TRIO64V2_DXGX 0x81
@@ -280,7 +281,8 @@ static int __devinit s3fb_setup_ddc_bus(struct fb_info *info)
         */
 /*     vga_wseq(par->state.vgabase, 0x08, 0x06); - not needed, already unlocked */
        if (par->chip == CHIP_357_VIRGE_GX2 ||
-           par->chip == CHIP_359_VIRGE_GX2P)
+           par->chip == CHIP_359_VIRGE_GX2P ||
+           par->chip == CHIP_260_VIRGE_MX)
                svga_wseq_mask(par->state.vgabase, 0x0d, 0x01, 0x03);
        else
                svga_wseq_mask(par->state.vgabase, 0x0d, 0x00, 0x03);
@@ -487,7 +489,8 @@ static void s3_set_pixclock(struct fb_info *info, u32 pixclock)
            par->chip == CHIP_359_VIRGE_GX2P ||
            par->chip == CHIP_360_TRIO3D_1X ||
            par->chip == CHIP_362_TRIO3D_2X ||
-           par->chip == CHIP_368_TRIO3D_2X) {
+           par->chip == CHIP_368_TRIO3D_2X ||
+           par->chip == CHIP_260_VIRGE_MX) {
                vga_wseq(par->state.vgabase, 0x12, (n - 2) | ((r & 3) << 6));   /* n and two bits of r */
                vga_wseq(par->state.vgabase, 0x29, r >> 2); /* remaining highest bit of r */
        } else
@@ -690,7 +693,8 @@ static int s3fb_set_par(struct fb_info *info)
            par->chip != CHIP_359_VIRGE_GX2P &&
            par->chip != CHIP_360_TRIO3D_1X &&
            par->chip != CHIP_362_TRIO3D_2X &&
-           par->chip != CHIP_368_TRIO3D_2X) {
+           par->chip != CHIP_368_TRIO3D_2X &&
+           par->chip != CHIP_260_VIRGE_MX) {
                vga_wcrt(par->state.vgabase, 0x54, 0x18); /* M parameter */
                vga_wcrt(par->state.vgabase, 0x60, 0xff); /* N parameter */
                vga_wcrt(par->state.vgabase, 0x61, 0xff); /* L parameter */
@@ -739,7 +743,8 @@ static int s3fb_set_par(struct fb_info *info)
            par->chip == CHIP_368_TRIO3D_2X ||
            par->chip == CHIP_365_TRIO3D    ||
            par->chip == CHIP_375_VIRGE_DX  ||
-           par->chip == CHIP_385_VIRGE_GX) {
+           par->chip == CHIP_385_VIRGE_GX  ||
+           par->chip == CHIP_260_VIRGE_MX) {
                dbytes = info->var.xres * ((bpp+7)/8);
                vga_wcrt(par->state.vgabase, 0x91, (dbytes + 7) / 8);
                vga_wcrt(par->state.vgabase, 0x90, (((dbytes + 7) / 8) >> 8) | 0x80);
@@ -751,7 +756,8 @@ static int s3fb_set_par(struct fb_info *info)
            par->chip == CHIP_359_VIRGE_GX2P ||
            par->chip == CHIP_360_TRIO3D_1X ||
            par->chip == CHIP_362_TRIO3D_2X ||
-           par->chip == CHIP_368_TRIO3D_2X)
+           par->chip == CHIP_368_TRIO3D_2X ||
+           par->chip == CHIP_260_VIRGE_MX)
                vga_wcrt(par->state.vgabase, 0x34, 0x00);
        else    /* enable Data Transfer Position Control (DTPC) */
                vga_wcrt(par->state.vgabase, 0x34, 0x10);
@@ -807,7 +813,8 @@ static int s3fb_set_par(struct fb_info *info)
                    par->chip == CHIP_359_VIRGE_GX2P ||
                    par->chip == CHIP_360_TRIO3D_1X ||
                    par->chip == CHIP_362_TRIO3D_2X ||
-                   par->chip == CHIP_368_TRIO3D_2X)
+                   par->chip == CHIP_368_TRIO3D_2X ||
+                   par->chip == CHIP_260_VIRGE_MX)
                        svga_wcrt_mask(par->state.vgabase, 0x67, 0x00, 0xF0);
                else {
                        svga_wcrt_mask(par->state.vgabase, 0x67, 0x10, 0xF0);
@@ -837,7 +844,8 @@ static int s3fb_set_par(struct fb_info *info)
                            par->chip != CHIP_359_VIRGE_GX2P &&
                            par->chip != CHIP_360_TRIO3D_1X &&
                            par->chip != CHIP_362_TRIO3D_2X &&
-                           par->chip != CHIP_368_TRIO3D_2X)
+                           par->chip != CHIP_368_TRIO3D_2X &&
+                           par->chip != CHIP_260_VIRGE_MX)
                                hmul = 2;
                }
                break;
@@ -864,7 +872,8 @@ static int s3fb_set_par(struct fb_info *info)
                            par->chip != CHIP_359_VIRGE_GX2P &&
                            par->chip != CHIP_360_TRIO3D_1X &&
                            par->chip != CHIP_362_TRIO3D_2X &&
-                           par->chip != CHIP_368_TRIO3D_2X)
+                           par->chip != CHIP_368_TRIO3D_2X &&
+                           par->chip != CHIP_260_VIRGE_MX)
                                hmul = 2;
                }
                break;
@@ -1208,7 +1217,8 @@ static int __devinit s3_pci_probe(struct pci_dev *dev, const struct pci_device_i
                        break;
                }
        } else if (par->chip == CHIP_357_VIRGE_GX2 ||
-                  par->chip == CHIP_359_VIRGE_GX2P) {
+                  par->chip == CHIP_359_VIRGE_GX2P ||
+                  par->chip == CHIP_260_VIRGE_MX) {
                switch ((regval & 0xC0) >> 6) {
                case 1: /* 4MB */
                        info->screen_size = 4 << 20;
@@ -1515,6 +1525,7 @@ static struct pci_device_id s3_devices[] __devinitdata = {
        {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A12), .driver_data = CHIP_359_VIRGE_GX2P},
        {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A13), .driver_data = CHIP_36X_TRIO3D_1X_2X},
        {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8904), .driver_data = CHIP_365_TRIO3D},
+       {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8C01), .driver_data = CHIP_260_VIRGE_MX},
 
        {0, 0, 0, 0, 0, 0, 0}
 };
index 4c6b84488561ba370a25f65bdaa16f40724cca9b..3951fdae5f6847694f97627638f6d64857554247 100644 (file)
@@ -127,8 +127,7 @@ static void sh_mipi_shutdown(struct platform_device *pdev)
        sh_mipi_dsi_enable(mipi, false);
 }
 
-static int __init sh_mipi_setup(struct sh_mipi *mipi,
-                               struct sh_mipi_dsi_info *pdata)
+static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata)
 {
        void __iomem *base = mipi->base;
        struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan;
@@ -551,7 +550,7 @@ efindslot:
        return ret;
 }
 
-static int __exit sh_mipi_remove(struct platform_device *pdev)
+static int __devexit sh_mipi_remove(struct platform_device *pdev)
 {
        struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
@@ -592,7 +591,7 @@ static int __exit sh_mipi_remove(struct platform_device *pdev)
 }
 
 static struct platform_driver sh_mipi_driver = {
-       .remove         = __exit_p(sh_mipi_remove),
+       .remove         = __devexit_p(sh_mipi_remove),
        .shutdown       = sh_mipi_shutdown,
        .driver = {
                .name   = "sh-mipi-dsi",
index e672698bd820a56be3487568ed4bd332a83b4ffa..699487c287b2b201a0d2d263aeadbcfa250a0f92 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/backlight.h>
 #include <linux/clk.h>
 #include <linux/console.h>
+#include <linux/ctype.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
 
 #include "sh_mobile_lcdcfb.h"
 
+/* ----------------------------------------------------------------------------
+ * Overlay register definitions
+ */
+
+#define LDBCR                  0xb00
+#define LDBCR_UPC(n)           (1 << ((n) + 16))
+#define LDBCR_UPF(n)           (1 << ((n) + 8))
+#define LDBCR_UPD(n)           (1 << ((n) + 0))
+#define LDBnBSIFR(n)           (0xb20 + (n) * 0x20 + 0x00)
+#define LDBBSIFR_EN            (1 << 31)
+#define LDBBSIFR_VS            (1 << 29)
+#define LDBBSIFR_BRSEL         (1 << 28)
+#define LDBBSIFR_MX            (1 << 27)
+#define LDBBSIFR_MY            (1 << 26)
+#define LDBBSIFR_CV3           (3 << 24)
+#define LDBBSIFR_CV2           (2 << 24)
+#define LDBBSIFR_CV1           (1 << 24)
+#define LDBBSIFR_CV0           (0 << 24)
+#define LDBBSIFR_CV_MASK       (3 << 24)
+#define LDBBSIFR_LAY_MASK      (0xff << 16)
+#define LDBBSIFR_LAY_SHIFT     16
+#define LDBBSIFR_ROP3_MASK     (0xff << 16)
+#define LDBBSIFR_ROP3_SHIFT    16
+#define LDBBSIFR_AL_PL8                (3 << 14)
+#define LDBBSIFR_AL_PL1                (2 << 14)
+#define LDBBSIFR_AL_PK         (1 << 14)
+#define LDBBSIFR_AL_1          (0 << 14)
+#define LDBBSIFR_AL_MASK       (3 << 14)
+#define LDBBSIFR_SWPL          (1 << 10)
+#define LDBBSIFR_SWPW          (1 << 9)
+#define LDBBSIFR_SWPB          (1 << 8)
+#define LDBBSIFR_RY            (1 << 7)
+#define LDBBSIFR_CHRR_420      (2 << 0)
+#define LDBBSIFR_CHRR_422      (1 << 0)
+#define LDBBSIFR_CHRR_444      (0 << 0)
+#define LDBBSIFR_RPKF_ARGB32   (0x00 << 0)
+#define LDBBSIFR_RPKF_RGB16    (0x03 << 0)
+#define LDBBSIFR_RPKF_RGB24    (0x0b << 0)
+#define LDBBSIFR_RPKF_MASK     (0x1f << 0)
+#define LDBnBSSZR(n)           (0xb20 + (n) * 0x20 + 0x04)
+#define LDBBSSZR_BVSS_MASK     (0xfff << 16)
+#define LDBBSSZR_BVSS_SHIFT    16
+#define LDBBSSZR_BHSS_MASK     (0xfff << 0)
+#define LDBBSSZR_BHSS_SHIFT    0
+#define LDBnBLOCR(n)           (0xb20 + (n) * 0x20 + 0x08)
+#define LDBBLOCR_CVLC_MASK     (0xfff << 16)
+#define LDBBLOCR_CVLC_SHIFT    16
+#define LDBBLOCR_CHLC_MASK     (0xfff << 0)
+#define LDBBLOCR_CHLC_SHIFT    0
+#define LDBnBSMWR(n)           (0xb20 + (n) * 0x20 + 0x0c)
+#define LDBBSMWR_BSMWA_MASK    (0xffff << 16)
+#define LDBBSMWR_BSMWA_SHIFT   16
+#define LDBBSMWR_BSMW_MASK     (0xffff << 0)
+#define LDBBSMWR_BSMW_SHIFT    0
+#define LDBnBSAYR(n)           (0xb20 + (n) * 0x20 + 0x10)
+#define LDBBSAYR_FG1A_MASK     (0xff << 24)
+#define LDBBSAYR_FG1A_SHIFT    24
+#define LDBBSAYR_FG1R_MASK     (0xff << 16)
+#define LDBBSAYR_FG1R_SHIFT    16
+#define LDBBSAYR_FG1G_MASK     (0xff << 8)
+#define LDBBSAYR_FG1G_SHIFT    8
+#define LDBBSAYR_FG1B_MASK     (0xff << 0)
+#define LDBBSAYR_FG1B_SHIFT    0
+#define LDBnBSACR(n)           (0xb20 + (n) * 0x20 + 0x14)
+#define LDBBSACR_FG2A_MASK     (0xff << 24)
+#define LDBBSACR_FG2A_SHIFT    24
+#define LDBBSACR_FG2R_MASK     (0xff << 16)
+#define LDBBSACR_FG2R_SHIFT    16
+#define LDBBSACR_FG2G_MASK     (0xff << 8)
+#define LDBBSACR_FG2G_SHIFT    8
+#define LDBBSACR_FG2B_MASK     (0xff << 0)
+#define LDBBSACR_FG2B_SHIFT    0
+#define LDBnBSAAR(n)           (0xb20 + (n) * 0x20 + 0x18)
+#define LDBBSAAR_AP_MASK       (0xff << 24)
+#define LDBBSAAR_AP_SHIFT      24
+#define LDBBSAAR_R_MASK                (0xff << 16)
+#define LDBBSAAR_R_SHIFT       16
+#define LDBBSAAR_GY_MASK       (0xff << 8)
+#define LDBBSAAR_GY_SHIFT      8
+#define LDBBSAAR_B_MASK                (0xff << 0)
+#define LDBBSAAR_B_SHIFT       0
+#define LDBnBPPCR(n)           (0xb20 + (n) * 0x20 + 0x1c)
+#define LDBBPPCR_AP_MASK       (0xff << 24)
+#define LDBBPPCR_AP_SHIFT      24
+#define LDBBPPCR_R_MASK                (0xff << 16)
+#define LDBBPPCR_R_SHIFT       16
+#define LDBBPPCR_GY_MASK       (0xff << 8)
+#define LDBBPPCR_GY_SHIFT      8
+#define LDBBPPCR_B_MASK                (0xff << 0)
+#define LDBBPPCR_B_SHIFT       0
+#define LDBnBBGCL(n)           (0xb10 + (n) * 0x04)
+#define LDBBBGCL_BGA_MASK      (0xff << 24)
+#define LDBBBGCL_BGA_SHIFT     24
+#define LDBBBGCL_BGR_MASK      (0xff << 16)
+#define LDBBBGCL_BGR_SHIFT     16
+#define LDBBBGCL_BGG_MASK      (0xff << 8)
+#define LDBBBGCL_BGG_SHIFT     8
+#define LDBBBGCL_BGB_MASK      (0xff << 0)
+#define LDBBBGCL_BGB_SHIFT     0
+
 #define SIDE_B_OFFSET 0x1000
 #define MIRROR_OFFSET 0x2000
 
 #define MAX_XRES 1920
 #define MAX_YRES 1080
 
+enum sh_mobile_lcdc_overlay_mode {
+       LCDC_OVERLAY_BLEND,
+       LCDC_OVERLAY_ROP3,
+};
+
+/*
+ * struct sh_mobile_lcdc_overlay - LCDC display overlay
+ *
+ * @channel: LCDC channel this overlay belongs to
+ * @cfg: Overlay configuration
+ * @info: Frame buffer device
+ * @index: Overlay index (0-3)
+ * @base: Overlay registers base address
+ * @enabled: True if the overlay is enabled
+ * @mode: Overlay blending mode (alpha blend or ROP3)
+ * @alpha: Global alpha blending value (0-255, for alpha blending mode)
+ * @rop3: Raster operation (for ROP3 mode)
+ * @fb_mem: Frame buffer virtual memory address
+ * @fb_size: Frame buffer size in bytes
+ * @dma_handle: Frame buffer DMA address
+ * @base_addr_y: Overlay base address (RGB or luma component)
+ * @base_addr_c: Overlay base address (chroma component)
+ * @pan_y_offset: Panning linear offset in bytes (luma component)
+ * @format: Current pixelf format
+ * @xres: Horizontal visible resolution
+ * @xres_virtual: Horizontal total resolution
+ * @yres: Vertical visible resolution
+ * @yres_virtual: Vertical total resolution
+ * @pitch: Overlay line pitch
+ * @pos_x: Horizontal overlay position
+ * @pos_y: Vertical overlay position
+ */
+struct sh_mobile_lcdc_overlay {
+       struct sh_mobile_lcdc_chan *channel;
+
+       const struct sh_mobile_lcdc_overlay_cfg *cfg;
+       struct fb_info *info;
+
+       unsigned int index;
+       unsigned long base;
+
+       bool enabled;
+       enum sh_mobile_lcdc_overlay_mode mode;
+       unsigned int alpha;
+       unsigned int rop3;
+
+       void *fb_mem;
+       unsigned long fb_size;
+
+       dma_addr_t dma_handle;
+       unsigned long base_addr_y;
+       unsigned long base_addr_c;
+       unsigned long pan_y_offset;
+
+       const struct sh_mobile_lcdc_format_info *format;
+       unsigned int xres;
+       unsigned int xres_virtual;
+       unsigned int yres;
+       unsigned int yres_virtual;
+       unsigned int pitch;
+       int pos_x;
+       int pos_y;
+};
+
 struct sh_mobile_lcdc_priv {
        void __iomem *base;
        int irq;
@@ -45,7 +210,10 @@ struct sh_mobile_lcdc_priv {
        struct device *dev;
        struct clk *dot_clk;
        unsigned long lddckr;
+
        struct sh_mobile_lcdc_chan ch[2];
+       struct sh_mobile_lcdc_overlay overlays[4];
+
        struct notifier_block notifier;
        int started;
        int forced_fourcc; /* 2 channel LCDC must share fourcc setting */
@@ -141,6 +309,13 @@ static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan,
        return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]);
 }
 
+static void lcdc_write_overlay(struct sh_mobile_lcdc_overlay *ovl,
+                              int reg, unsigned long data)
+{
+       iowrite32(data, ovl->channel->lcdc->base + reg);
+       iowrite32(data, ovl->channel->lcdc->base + reg + SIDE_B_OFFSET);
+}
+
 static void lcdc_write(struct sh_mobile_lcdc_priv *priv,
                       unsigned long reg_offs, unsigned long data)
 {
@@ -384,8 +559,8 @@ sh_mobile_lcdc_must_reconfigure(struct sh_mobile_lcdc_chan *ch,
        return true;
 }
 
-static int sh_mobile_check_var(struct fb_var_screeninfo *var,
-                              struct fb_info *info);
+static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+                                   struct fb_info *info);
 
 static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch,
                                         enum sh_mobile_lcdc_entity_event event,
@@ -439,7 +614,7 @@ static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch,
                fb_videomode_to_var(&var, mode);
                var.bits_per_pixel = info->var.bits_per_pixel;
                var.grayscale = info->var.grayscale;
-               ret = sh_mobile_check_var(&var, info);
+               ret = sh_mobile_lcdc_check_var(&var, info);
                break;
        }
 
@@ -585,7 +760,7 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static int sh_mobile_wait_for_vsync(struct sh_mobile_lcdc_chan *ch)
+static int sh_mobile_lcdc_wait_for_vsync(struct sh_mobile_lcdc_chan *ch)
 {
        unsigned long ldintr;
        int ret;
@@ -685,8 +860,98 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
        lcdc_write_chan(ch, LDHAJR, tmp);
 }
 
+static void sh_mobile_lcdc_overlay_setup(struct sh_mobile_lcdc_overlay *ovl)
+{
+       u32 format = 0;
+
+       if (!ovl->enabled) {
+               lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index));
+               lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), 0);
+               lcdc_write(ovl->channel->lcdc, LDBCR,
+                          LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index));
+               return;
+       }
+
+       ovl->base_addr_y = ovl->dma_handle;
+       ovl->base_addr_c = ovl->dma_handle
+                        + ovl->xres_virtual * ovl->yres_virtual;
+
+       switch (ovl->mode) {
+       case LCDC_OVERLAY_BLEND:
+               format = LDBBSIFR_EN | (ovl->alpha << LDBBSIFR_LAY_SHIFT);
+               break;
+
+       case LCDC_OVERLAY_ROP3:
+               format = LDBBSIFR_EN | LDBBSIFR_BRSEL
+                      | (ovl->rop3 << LDBBSIFR_ROP3_SHIFT);
+               break;
+       }
+
+       switch (ovl->format->fourcc) {
+       case V4L2_PIX_FMT_RGB565:
+       case V4L2_PIX_FMT_NV21:
+       case V4L2_PIX_FMT_NV61:
+       case V4L2_PIX_FMT_NV42:
+               format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW;
+               break;
+       case V4L2_PIX_FMT_BGR24:
+       case V4L2_PIX_FMT_NV12:
+       case V4L2_PIX_FMT_NV16:
+       case V4L2_PIX_FMT_NV24:
+               format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB;
+               break;
+       case V4L2_PIX_FMT_BGR32:
+       default:
+               format |= LDBBSIFR_SWPL;
+               break;
+       }
+
+       switch (ovl->format->fourcc) {
+       case V4L2_PIX_FMT_RGB565:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16;
+               break;
+       case V4L2_PIX_FMT_BGR24:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24;
+               break;
+       case V4L2_PIX_FMT_BGR32:
+               format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32;
+               break;
+       case V4L2_PIX_FMT_NV12:
+       case V4L2_PIX_FMT_NV21:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420;
+               break;
+       case V4L2_PIX_FMT_NV16:
+       case V4L2_PIX_FMT_NV61:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422;
+               break;
+       case V4L2_PIX_FMT_NV24:
+       case V4L2_PIX_FMT_NV42:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444;
+               break;
+       }
+
+       lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index));
+
+       lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), format);
+
+       lcdc_write_overlay(ovl, LDBnBSSZR(ovl->index),
+               (ovl->yres << LDBBSSZR_BVSS_SHIFT) |
+               (ovl->xres << LDBBSSZR_BHSS_SHIFT));
+       lcdc_write_overlay(ovl, LDBnBLOCR(ovl->index),
+               (ovl->pos_y << LDBBLOCR_CVLC_SHIFT) |
+               (ovl->pos_x << LDBBLOCR_CHLC_SHIFT));
+       lcdc_write_overlay(ovl, LDBnBSMWR(ovl->index),
+               ovl->pitch << LDBBSMWR_BSMW_SHIFT);
+
+       lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y);
+       lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c);
+
+       lcdc_write(ovl->channel->lcdc, LDBCR,
+                  LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index));
+}
+
 /*
- * __sh_mobile_lcdc_start - Configure and tart the LCDC
+ * __sh_mobile_lcdc_start - Configure and start the LCDC
  * @priv: LCDC device
  *
  * Configure all enabled channels and start the LCDC device. All external
@@ -839,27 +1104,25 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
        /* Compute frame buffer base address and pitch for each channel. */
        for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
                int pixelformat;
-               void *meram;
+               void *cache;
 
                ch = &priv->ch[k];
                if (!ch->enabled)
                        continue;
 
                ch->base_addr_y = ch->dma_handle;
-               ch->base_addr_c = ch->base_addr_y + ch->xres * ch->yres_virtual;
+               ch->base_addr_c = ch->dma_handle
+                               + ch->xres_virtual * ch->yres_virtual;
                ch->line_size = ch->pitch;
 
                /* Enable MERAM if possible. */
-               if (mdev == NULL || mdev->ops == NULL ||
-                   ch->cfg->meram_cfg == NULL)
+               if (mdev == NULL || ch->cfg->meram_cfg == NULL)
                        continue;
 
-               /* we need to de-init configured ICBs before we can
-                * re-initialize them.
-                */
-               if (ch->meram) {
-                       mdev->ops->meram_unregister(mdev, ch->meram);
-                       ch->meram = NULL;
+               /* Free the allocated MERAM cache. */
+               if (ch->cache) {
+                       sh_mobile_meram_cache_free(mdev, ch->cache);
+                       ch->cache = NULL;
                }
 
                switch (ch->format->fourcc) {
@@ -881,17 +1144,22 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
                        break;
                }
 
-               meram = mdev->ops->meram_register(mdev, ch->cfg->meram_cfg,
+               cache = sh_mobile_meram_cache_alloc(mdev, ch->cfg->meram_cfg,
                                        ch->pitch, ch->yres, pixelformat,
                                        &ch->line_size);
-               if (!IS_ERR(meram)) {
-                       mdev->ops->meram_update(mdev, meram,
+               if (!IS_ERR(cache)) {
+                       sh_mobile_meram_cache_update(mdev, cache,
                                        ch->base_addr_y, ch->base_addr_c,
                                        &ch->base_addr_y, &ch->base_addr_c);
-                       ch->meram = meram;
+                       ch->cache = cache;
                }
        }
 
+       for (k = 0; k < ARRAY_SIZE(priv->overlays); ++k) {
+               struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[k];
+               sh_mobile_lcdc_overlay_setup(ovl);
+       }
+
        /* Start the LCDC. */
        __sh_mobile_lcdc_start(priv);
 
@@ -953,12 +1221,10 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
 
                sh_mobile_lcdc_display_off(ch);
 
-               /* disable the meram */
-               if (ch->meram) {
-                       struct sh_mobile_meram_info *mdev;
-                       mdev = priv->meram_dev;
-                       mdev->ops->meram_unregister(mdev, ch->meram);
-                       ch->meram = 0;
+               /* Free the MERAM cache. */
+               if (ch->cache) {
+                       sh_mobile_meram_cache_free(priv->meram_dev, ch->cache);
+                       ch->cache = 0;
                }
 
        }
@@ -975,8 +1241,511 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
                        sh_mobile_lcdc_clk_off(priv);
 }
 
+static int __sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+                                     struct fb_info *info)
+{
+       if (var->xres > MAX_XRES || var->yres > MAX_YRES)
+               return -EINVAL;
+
+       /* Make sure the virtual resolution is at least as big as the visible
+        * resolution.
+        */
+       if (var->xres_virtual < var->xres)
+               var->xres_virtual = var->xres;
+       if (var->yres_virtual < var->yres)
+               var->yres_virtual = var->yres;
+
+       if (sh_mobile_format_is_fourcc(var)) {
+               const struct sh_mobile_lcdc_format_info *format;
+
+               format = sh_mobile_format_info(var->grayscale);
+               if (format == NULL)
+                       return -EINVAL;
+               var->bits_per_pixel = format->bpp;
+
+               /* Default to RGB and JPEG color-spaces for RGB and YUV formats
+                * respectively.
+                */
+               if (!format->yuv)
+                       var->colorspace = V4L2_COLORSPACE_SRGB;
+               else if (var->colorspace != V4L2_COLORSPACE_REC709)
+                       var->colorspace = V4L2_COLORSPACE_JPEG;
+       } else {
+               if (var->bits_per_pixel <= 16) {                /* RGB 565 */
+                       var->bits_per_pixel = 16;
+                       var->red.offset = 11;
+                       var->red.length = 5;
+                       var->green.offset = 5;
+                       var->green.length = 6;
+                       var->blue.offset = 0;
+                       var->blue.length = 5;
+                       var->transp.offset = 0;
+                       var->transp.length = 0;
+               } else if (var->bits_per_pixel <= 24) {         /* RGB 888 */
+                       var->bits_per_pixel = 24;
+                       var->red.offset = 16;
+                       var->red.length = 8;
+                       var->green.offset = 8;
+                       var->green.length = 8;
+                       var->blue.offset = 0;
+                       var->blue.length = 8;
+                       var->transp.offset = 0;
+                       var->transp.length = 0;
+               } else if (var->bits_per_pixel <= 32) {         /* RGBA 888 */
+                       var->bits_per_pixel = 32;
+                       var->red.offset = 16;
+                       var->red.length = 8;
+                       var->green.offset = 8;
+                       var->green.length = 8;
+                       var->blue.offset = 0;
+                       var->blue.length = 8;
+                       var->transp.offset = 24;
+                       var->transp.length = 8;
+               } else
+                       return -EINVAL;
+
+               var->red.msb_right = 0;
+               var->green.msb_right = 0;
+               var->blue.msb_right = 0;
+               var->transp.msb_right = 0;
+       }
+
+       /* Make sure we don't exceed our allocated memory. */
+       if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 >
+           info->fix.smem_len)
+               return -EINVAL;
+
+       return 0;
+}
+
 /* -----------------------------------------------------------------------------
- * Frame buffer operations
+ * Frame buffer operations - Overlays
+ */
+
+static ssize_t
+overlay_alpha_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->alpha);
+}
+
+static ssize_t
+overlay_alpha_store(struct device *dev, struct device_attribute *attr,
+                   const char *buf, size_t count)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct sh_mobile_lcdc_overlay *ovl = info->par;
+       unsigned int alpha;
+       char *endp;
+
+       alpha = simple_strtoul(buf, &endp, 10);
+       if (isspace(*endp))
+               endp++;
+
+       if (endp - buf != count)
+               return -EINVAL;
+
+       if (alpha > 255)
+               return -EINVAL;
+
+       if (ovl->alpha != alpha) {
+               ovl->alpha = alpha;
+
+               if (ovl->mode == LCDC_OVERLAY_BLEND && ovl->enabled)
+                       sh_mobile_lcdc_overlay_setup(ovl);
+       }
+
+       return count;
+}
+
+static ssize_t
+overlay_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->mode);
+}
+
+static ssize_t
+overlay_mode_store(struct device *dev, struct device_attribute *attr,
+                  const char *buf, size_t count)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct sh_mobile_lcdc_overlay *ovl = info->par;
+       unsigned int mode;
+       char *endp;
+
+       mode = simple_strtoul(buf, &endp, 10);
+       if (isspace(*endp))
+               endp++;
+
+       if (endp - buf != count)
+               return -EINVAL;
+
+       if (mode != LCDC_OVERLAY_BLEND && mode != LCDC_OVERLAY_ROP3)
+               return -EINVAL;
+
+       if (ovl->mode != mode) {
+               ovl->mode = mode;
+
+               if (ovl->enabled)
+                       sh_mobile_lcdc_overlay_setup(ovl);
+       }
+
+       return count;
+}
+
+static ssize_t
+overlay_position_show(struct device *dev, struct device_attribute *attr,
+                     char *buf)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+       return scnprintf(buf, PAGE_SIZE, "%d,%d\n", ovl->pos_x, ovl->pos_y);
+}
+
+static ssize_t
+overlay_position_store(struct device *dev, struct device_attribute *attr,
+                      const char *buf, size_t count)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct sh_mobile_lcdc_overlay *ovl = info->par;
+       char *endp;
+       int pos_x;
+       int pos_y;
+
+       pos_x = simple_strtol(buf, &endp, 10);
+       if (*endp != ',')
+               return -EINVAL;
+
+       pos_y = simple_strtol(endp + 1, &endp, 10);
+       if (isspace(*endp))
+               endp++;
+
+       if (endp - buf != count)
+               return -EINVAL;
+
+       if (ovl->pos_x != pos_x || ovl->pos_y != pos_y) {
+               ovl->pos_x = pos_x;
+               ovl->pos_y = pos_y;
+
+               if (ovl->enabled)
+                       sh_mobile_lcdc_overlay_setup(ovl);
+       }
+
+       return count;
+}
+
+static ssize_t
+overlay_rop3_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->rop3);
+}
+
+static ssize_t
+overlay_rop3_store(struct device *dev, struct device_attribute *attr,
+                   const char *buf, size_t count)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct sh_mobile_lcdc_overlay *ovl = info->par;
+       unsigned int rop3;
+       char *endp;
+
+       rop3 = !!simple_strtoul(buf, &endp, 10);
+       if (isspace(*endp))
+               endp++;
+
+       if (endp - buf != count)
+               return -EINVAL;
+
+       if (rop3 > 255)
+               return -EINVAL;
+
+       if (ovl->rop3 != rop3) {
+               ovl->rop3 = rop3;
+
+               if (ovl->mode == LCDC_OVERLAY_ROP3 && ovl->enabled)
+                       sh_mobile_lcdc_overlay_setup(ovl);
+       }
+
+       return count;
+}
+
+static const struct device_attribute overlay_sysfs_attrs[] = {
+       __ATTR(ovl_alpha, S_IRUGO|S_IWUSR,
+              overlay_alpha_show, overlay_alpha_store),
+       __ATTR(ovl_mode, S_IRUGO|S_IWUSR,
+              overlay_mode_show, overlay_mode_store),
+       __ATTR(ovl_position, S_IRUGO|S_IWUSR,
+              overlay_position_show, overlay_position_store),
+       __ATTR(ovl_rop3, S_IRUGO|S_IWUSR,
+              overlay_rop3_show, overlay_rop3_store),
+};
+
+static const struct fb_fix_screeninfo sh_mobile_lcdc_overlay_fix  = {
+       .id =           "SH Mobile LCDC",
+       .type =         FB_TYPE_PACKED_PIXELS,
+       .visual =       FB_VISUAL_TRUECOLOR,
+       .accel =        FB_ACCEL_NONE,
+       .xpanstep =     1,
+       .ypanstep =     1,
+       .ywrapstep =    0,
+       .capabilities = FB_CAP_FOURCC,
+};
+
+static int sh_mobile_lcdc_overlay_pan(struct fb_var_screeninfo *var,
+                                   struct fb_info *info)
+{
+       struct sh_mobile_lcdc_overlay *ovl = info->par;
+       unsigned long base_addr_y;
+       unsigned long base_addr_c;
+       unsigned long y_offset;
+       unsigned long c_offset;
+
+       if (!ovl->format->yuv) {
+               y_offset = (var->yoffset * ovl->xres_virtual + var->xoffset)
+                        * ovl->format->bpp / 8;
+               c_offset = 0;
+       } else {
+               unsigned int xsub = ovl->format->bpp < 24 ? 2 : 1;
+               unsigned int ysub = ovl->format->bpp < 16 ? 2 : 1;
+
+               y_offset = var->yoffset * ovl->xres_virtual + var->xoffset;
+               c_offset = var->yoffset / ysub * ovl->xres_virtual * 2 / xsub
+                        + var->xoffset * 2 / xsub;
+       }
+
+       /* If the Y offset hasn't changed, the C offset hasn't either. There's
+        * nothing to do in that case.
+        */
+       if (y_offset == ovl->pan_y_offset)
+               return 0;
+
+       /* Set the source address for the next refresh */
+       base_addr_y = ovl->dma_handle + y_offset;
+       base_addr_c = ovl->dma_handle + ovl->xres_virtual * ovl->yres_virtual
+                   + c_offset;
+
+       ovl->base_addr_y = base_addr_y;
+       ovl->base_addr_c = base_addr_c;
+       ovl->pan_y_offset = y_offset;
+
+       lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index));
+
+       lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y);
+       lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c);
+
+       lcdc_write(ovl->channel->lcdc, LDBCR,
+                  LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index));
+
+       return 0;
+}
+
+static int sh_mobile_lcdc_overlay_ioctl(struct fb_info *info, unsigned int cmd,
+                                     unsigned long arg)
+{
+       struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+       switch (cmd) {
+       case FBIO_WAITFORVSYNC:
+               return sh_mobile_lcdc_wait_for_vsync(ovl->channel);
+
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+static int sh_mobile_lcdc_overlay_check_var(struct fb_var_screeninfo *var,
+                                         struct fb_info *info)
+{
+       return __sh_mobile_lcdc_check_var(var, info);
+}
+
+static int sh_mobile_lcdc_overlay_set_par(struct fb_info *info)
+{
+       struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+       ovl->format =
+               sh_mobile_format_info(sh_mobile_format_fourcc(&info->var));
+
+       ovl->xres = info->var.xres;
+       ovl->xres_virtual = info->var.xres_virtual;
+       ovl->yres = info->var.yres;
+       ovl->yres_virtual = info->var.yres_virtual;
+
+       if (ovl->format->yuv)
+               ovl->pitch = info->var.xres_virtual;
+       else
+               ovl->pitch = info->var.xres_virtual * ovl->format->bpp / 8;
+
+       sh_mobile_lcdc_overlay_setup(ovl);
+
+       info->fix.line_length = ovl->pitch;
+
+       if (sh_mobile_format_is_fourcc(&info->var)) {
+               info->fix.type = FB_TYPE_FOURCC;
+               info->fix.visual = FB_VISUAL_FOURCC;
+       } else {
+               info->fix.type = FB_TYPE_PACKED_PIXELS;
+               info->fix.visual = FB_VISUAL_TRUECOLOR;
+       }
+
+       return 0;
+}
+
+/* Overlay blanking. Disable the overlay when blanked. */
+static int sh_mobile_lcdc_overlay_blank(int blank, struct fb_info *info)
+{
+       struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+       ovl->enabled = !blank;
+       sh_mobile_lcdc_overlay_setup(ovl);
+
+       /* Prevent the backlight from receiving a blanking event by returning
+        * a non-zero value.
+        */
+       return 1;
+}
+
+static struct fb_ops sh_mobile_lcdc_overlay_ops = {
+       .owner          = THIS_MODULE,
+       .fb_read        = fb_sys_read,
+       .fb_write       = fb_sys_write,
+       .fb_fillrect    = sys_fillrect,
+       .fb_copyarea    = sys_copyarea,
+       .fb_imageblit   = sys_imageblit,
+       .fb_blank       = sh_mobile_lcdc_overlay_blank,
+       .fb_pan_display = sh_mobile_lcdc_overlay_pan,
+       .fb_ioctl       = sh_mobile_lcdc_overlay_ioctl,
+       .fb_check_var   = sh_mobile_lcdc_overlay_check_var,
+       .fb_set_par     = sh_mobile_lcdc_overlay_set_par,
+};
+
+static void
+sh_mobile_lcdc_overlay_fb_unregister(struct sh_mobile_lcdc_overlay *ovl)
+{
+       struct fb_info *info = ovl->info;
+
+       if (info == NULL || info->dev == NULL)
+               return;
+
+       unregister_framebuffer(ovl->info);
+}
+
+static int __devinit
+sh_mobile_lcdc_overlay_fb_register(struct sh_mobile_lcdc_overlay *ovl)
+{
+       struct sh_mobile_lcdc_priv *lcdc = ovl->channel->lcdc;
+       struct fb_info *info = ovl->info;
+       unsigned int i;
+       int ret;
+
+       if (info == NULL)
+               return 0;
+
+       ret = register_framebuffer(info);
+       if (ret < 0)
+               return ret;
+
+       dev_info(lcdc->dev, "registered %s/overlay %u as %dx%d %dbpp.\n",
+                dev_name(lcdc->dev), ovl->index, info->var.xres,
+                info->var.yres, info->var.bits_per_pixel);
+
+       for (i = 0; i < ARRAY_SIZE(overlay_sysfs_attrs); ++i) {
+               ret = device_create_file(info->dev, &overlay_sysfs_attrs[i]);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void
+sh_mobile_lcdc_overlay_fb_cleanup(struct sh_mobile_lcdc_overlay *ovl)
+{
+       struct fb_info *info = ovl->info;
+
+       if (info == NULL || info->device == NULL)
+               return;
+
+       framebuffer_release(info);
+}
+
+static int __devinit
+sh_mobile_lcdc_overlay_fb_init(struct sh_mobile_lcdc_overlay *ovl)
+{
+       struct sh_mobile_lcdc_priv *priv = ovl->channel->lcdc;
+       struct fb_var_screeninfo *var;
+       struct fb_info *info;
+
+       /* Allocate and initialize the frame buffer device. */
+       info = framebuffer_alloc(0, priv->dev);
+       if (info == NULL) {
+               dev_err(priv->dev, "unable to allocate fb_info\n");
+               return -ENOMEM;
+       }
+
+       ovl->info = info;
+
+       info->flags = FBINFO_FLAG_DEFAULT;
+       info->fbops = &sh_mobile_lcdc_overlay_ops;
+       info->device = priv->dev;
+       info->screen_base = ovl->fb_mem;
+       info->par = ovl;
+
+       /* Initialize fixed screen information. Restrict pan to 2 lines steps
+        * for NV12 and NV21.
+        */
+       info->fix = sh_mobile_lcdc_overlay_fix;
+       snprintf(info->fix.id, sizeof(info->fix.id),
+                "SH Mobile LCDC Overlay %u", ovl->index);
+       info->fix.smem_start = ovl->dma_handle;
+       info->fix.smem_len = ovl->fb_size;
+       info->fix.line_length = ovl->pitch;
+
+       if (ovl->format->yuv)
+               info->fix.visual = FB_VISUAL_FOURCC;
+       else
+               info->fix.visual = FB_VISUAL_TRUECOLOR;
+
+       switch (ovl->format->fourcc) {
+       case V4L2_PIX_FMT_NV12:
+       case V4L2_PIX_FMT_NV21:
+               info->fix.ypanstep = 2;
+       case V4L2_PIX_FMT_NV16:
+       case V4L2_PIX_FMT_NV61:
+               info->fix.xpanstep = 2;
+       }
+
+       /* Initialize variable screen information. */
+       var = &info->var;
+       memset(var, 0, sizeof(*var));
+       var->xres = ovl->xres;
+       var->yres = ovl->yres;
+       var->xres_virtual = ovl->xres_virtual;
+       var->yres_virtual = ovl->yres_virtual;
+       var->activate = FB_ACTIVATE_NOW;
+
+       /* Use the legacy API by default for RGB formats, and the FOURCC API
+        * for YUV formats.
+        */
+       if (!ovl->format->yuv)
+               var->bits_per_pixel = ovl->format->bpp;
+       else
+               var->grayscale = ovl->format->fourcc;
+
+       return sh_mobile_lcdc_overlay_check_var(var, info);
+}
+
+/* -----------------------------------------------------------------------------
+ * Frame buffer operations - main frame buffer
  */
 
 static int sh_mobile_lcdc_setcolreg(u_int regno,
@@ -1003,12 +1772,12 @@ static int sh_mobile_lcdc_setcolreg(u_int regno,
        return 0;
 }
 
-static struct fb_fix_screeninfo sh_mobile_lcdc_fix  = {
+static const struct fb_fix_screeninfo sh_mobile_lcdc_fix  = {
        .id =           "SH Mobile LCDC",
        .type =         FB_TYPE_PACKED_PIXELS,
        .visual =       FB_VISUAL_TRUECOLOR,
        .accel =        FB_ACCEL_NONE,
-       .xpanstep =     0,
+       .xpanstep =     1,
        .ypanstep =     1,
        .ywrapstep =    0,
        .capabilities = FB_CAP_FOURCC,
@@ -1035,78 +1804,74 @@ static void sh_mobile_lcdc_imageblit(struct fb_info *info,
        sh_mobile_lcdc_deferred_io_touch(info);
 }
 
-static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
-                                    struct fb_info *info)
+static int sh_mobile_lcdc_pan(struct fb_var_screeninfo *var,
+                             struct fb_info *info)
 {
        struct sh_mobile_lcdc_chan *ch = info->par;
        struct sh_mobile_lcdc_priv *priv = ch->lcdc;
        unsigned long ldrcntr;
-       unsigned long new_pan_offset;
        unsigned long base_addr_y, base_addr_c;
+       unsigned long y_offset;
        unsigned long c_offset;
 
-       if (!ch->format->yuv)
-               new_pan_offset = var->yoffset * ch->pitch
-                              + var->xoffset * (ch->format->bpp / 8);
-       else
-               new_pan_offset = var->yoffset * ch->pitch + var->xoffset;
+       if (!ch->format->yuv) {
+               y_offset = (var->yoffset * ch->xres_virtual + var->xoffset)
+                        * ch->format->bpp / 8;
+               c_offset = 0;
+       } else {
+               unsigned int xsub = ch->format->bpp < 24 ? 2 : 1;
+               unsigned int ysub = ch->format->bpp < 16 ? 2 : 1;
 
-       if (new_pan_offset == ch->pan_offset)
-               return 0;       /* No change, do nothing */
+               y_offset = var->yoffset * ch->xres_virtual + var->xoffset;
+               c_offset = var->yoffset / ysub * ch->xres_virtual * 2 / xsub
+                        + var->xoffset * 2 / xsub;
+       }
 
-       ldrcntr = lcdc_read(priv, _LDRCNTR);
+       /* If the Y offset hasn't changed, the C offset hasn't either. There's
+        * nothing to do in that case.
+        */
+       if (y_offset == ch->pan_y_offset)
+               return 0;
 
        /* Set the source address for the next refresh */
-       base_addr_y = ch->dma_handle + new_pan_offset;
-       if (ch->format->yuv) {
-               /* Set y offset */
-               c_offset = var->yoffset * ch->pitch
-                        * (ch->format->bpp - 8) / 8;
-               base_addr_c = ch->dma_handle + ch->xres * ch->yres_virtual
-                           + c_offset;
-               /* Set x offset */
-               if (ch->format->fourcc == V4L2_PIX_FMT_NV24)
-                       base_addr_c += 2 * var->xoffset;
-               else
-                       base_addr_c += var->xoffset;
-       }
+       base_addr_y = ch->dma_handle + y_offset;
+       base_addr_c = ch->dma_handle + ch->xres_virtual * ch->yres_virtual
+                   + c_offset;
 
-       if (ch->meram) {
-               struct sh_mobile_meram_info *mdev;
-
-               mdev = priv->meram_dev;
-               mdev->ops->meram_update(mdev, ch->meram,
-                                       base_addr_y, base_addr_c,
-                                       &base_addr_y, &base_addr_c);
-       }
+       if (ch->cache)
+               sh_mobile_meram_cache_update(priv->meram_dev, ch->cache,
+                                            base_addr_y, base_addr_c,
+                                            &base_addr_y, &base_addr_c);
 
        ch->base_addr_y = base_addr_y;
        ch->base_addr_c = base_addr_c;
+       ch->pan_y_offset = y_offset;
 
        lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
        if (ch->format->yuv)
                lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
 
+       ldrcntr = lcdc_read(priv, _LDRCNTR);
        if (lcdc_chan_is_sublcd(ch))
                lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS);
        else
                lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_MRS);
 
-       ch->pan_offset = new_pan_offset;
 
        sh_mobile_lcdc_deferred_io_touch(info);
 
        return 0;
 }
 
-static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
-                      unsigned long arg)
+static int sh_mobile_lcdc_ioctl(struct fb_info *info, unsigned int cmd,
+                               unsigned long arg)
 {
+       struct sh_mobile_lcdc_chan *ch = info->par;
        int retval;
 
        switch (cmd) {
        case FBIO_WAITFORVSYNC:
-               retval = sh_mobile_wait_for_vsync(info->par);
+               retval = sh_mobile_lcdc_wait_for_vsync(ch);
                break;
 
        default:
@@ -1158,7 +1923,7 @@ static void sh_mobile_fb_reconfig(struct fb_info *info)
  * Locking: both .fb_release() and .fb_open() are called with info->lock held if
  * user == 1, or with console sem held, if user == 0.
  */
-static int sh_mobile_release(struct fb_info *info, int user)
+static int sh_mobile_lcdc_release(struct fb_info *info, int user)
 {
        struct sh_mobile_lcdc_chan *ch = info->par;
 
@@ -1179,7 +1944,7 @@ static int sh_mobile_release(struct fb_info *info, int user)
        return 0;
 }
 
-static int sh_mobile_open(struct fb_info *info, int user)
+static int sh_mobile_lcdc_open(struct fb_info *info, int user)
 {
        struct sh_mobile_lcdc_chan *ch = info->par;
 
@@ -1192,7 +1957,8 @@ static int sh_mobile_open(struct fb_info *info, int user)
        return 0;
 }
 
-static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+                                   struct fb_info *info)
 {
        struct sh_mobile_lcdc_chan *ch = info->par;
        struct sh_mobile_lcdc_priv *p = ch->lcdc;
@@ -1200,9 +1966,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
        unsigned int best_xres = 0;
        unsigned int best_yres = 0;
        unsigned int i;
-
-       if (var->xres > MAX_XRES || var->yres > MAX_YRES)
-               return -EINVAL;
+       int ret;
 
        /* If board code provides us with a list of available modes, make sure
         * we use one of them. Find the mode closest to the requested one. The
@@ -1237,73 +2001,9 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
                var->yres = best_yres;
        }
 
-       /* Make sure the virtual resolution is at least as big as the visible
-        * resolution.
-        */
-       if (var->xres_virtual < var->xres)
-               var->xres_virtual = var->xres;
-       if (var->yres_virtual < var->yres)
-               var->yres_virtual = var->yres;
-
-       if (sh_mobile_format_is_fourcc(var)) {
-               const struct sh_mobile_lcdc_format_info *format;
-
-               format = sh_mobile_format_info(var->grayscale);
-               if (format == NULL)
-                       return -EINVAL;
-               var->bits_per_pixel = format->bpp;
-
-               /* Default to RGB and JPEG color-spaces for RGB and YUV formats
-                * respectively.
-                */
-               if (!format->yuv)
-                       var->colorspace = V4L2_COLORSPACE_SRGB;
-               else if (var->colorspace != V4L2_COLORSPACE_REC709)
-                       var->colorspace = V4L2_COLORSPACE_JPEG;
-       } else {
-               if (var->bits_per_pixel <= 16) {                /* RGB 565 */
-                       var->bits_per_pixel = 16;
-                       var->red.offset = 11;
-                       var->red.length = 5;
-                       var->green.offset = 5;
-                       var->green.length = 6;
-                       var->blue.offset = 0;
-                       var->blue.length = 5;
-                       var->transp.offset = 0;
-                       var->transp.length = 0;
-               } else if (var->bits_per_pixel <= 24) {         /* RGB 888 */
-                       var->bits_per_pixel = 24;
-                       var->red.offset = 16;
-                       var->red.length = 8;
-                       var->green.offset = 8;
-                       var->green.length = 8;
-                       var->blue.offset = 0;
-                       var->blue.length = 8;
-                       var->transp.offset = 0;
-                       var->transp.length = 0;
-               } else if (var->bits_per_pixel <= 32) {         /* RGBA 888 */
-                       var->bits_per_pixel = 32;
-                       var->red.offset = 16;
-                       var->red.length = 8;
-                       var->green.offset = 8;
-                       var->green.length = 8;
-                       var->blue.offset = 0;
-                       var->blue.length = 8;
-                       var->transp.offset = 24;
-                       var->transp.length = 8;
-               } else
-                       return -EINVAL;
-
-               var->red.msb_right = 0;
-               var->green.msb_right = 0;
-               var->blue.msb_right = 0;
-               var->transp.msb_right = 0;
-       }
-
-       /* Make sure we don't exceed our allocated memory. */
-       if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 >
-           info->fix.smem_len)
-               return -EINVAL;
+       ret = __sh_mobile_lcdc_check_var(var, info);
+       if (ret < 0)
+               return ret;
 
        /* only accept the forced_fourcc for dual channel configurations */
        if (p->forced_fourcc &&
@@ -1313,7 +2013,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
        return 0;
 }
 
-static int sh_mobile_set_par(struct fb_info *info)
+static int sh_mobile_lcdc_set_par(struct fb_info *info)
 {
        struct sh_mobile_lcdc_chan *ch = info->par;
        int ret;
@@ -1329,9 +2029,9 @@ static int sh_mobile_set_par(struct fb_info *info)
        ch->yres_virtual = info->var.yres_virtual;
 
        if (ch->format->yuv)
-               ch->pitch = info->var.xres;
+               ch->pitch = info->var.xres_virtual;
        else
-               ch->pitch = info->var.xres * ch->format->bpp / 8;
+               ch->pitch = info->var.xres_virtual * ch->format->bpp / 8;
 
        ret = sh_mobile_lcdc_start(ch->lcdc);
        if (ret < 0)
@@ -1383,8 +2083,8 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info)
                 * mode will reenable the clocks and update the screen in time,
                 * so it does not need this. */
                if (!info->fbdefio) {
-                       sh_mobile_wait_for_vsync(ch);
-                       sh_mobile_wait_for_vsync(ch);
+                       sh_mobile_lcdc_wait_for_vsync(ch);
+                       sh_mobile_lcdc_wait_for_vsync(ch);
                }
                sh_mobile_lcdc_clk_off(p);
        }
@@ -1402,12 +2102,12 @@ static struct fb_ops sh_mobile_lcdc_ops = {
        .fb_copyarea    = sh_mobile_lcdc_copyarea,
        .fb_imageblit   = sh_mobile_lcdc_imageblit,
        .fb_blank       = sh_mobile_lcdc_blank,
-       .fb_pan_display = sh_mobile_fb_pan_display,
-       .fb_ioctl       = sh_mobile_ioctl,
-       .fb_open        = sh_mobile_open,
-       .fb_release     = sh_mobile_release,
-       .fb_check_var   = sh_mobile_check_var,
-       .fb_set_par     = sh_mobile_set_par,
+       .fb_pan_display = sh_mobile_lcdc_pan,
+       .fb_ioctl       = sh_mobile_lcdc_ioctl,
+       .fb_open        = sh_mobile_lcdc_open,
+       .fb_release     = sh_mobile_lcdc_release,
+       .fb_check_var   = sh_mobile_lcdc_check_var,
+       .fb_set_par     = sh_mobile_lcdc_set_par,
 };
 
 static void
@@ -1514,19 +2214,24 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
        else
                info->fix.visual = FB_VISUAL_TRUECOLOR;
 
-       if (ch->format->fourcc == V4L2_PIX_FMT_NV12 ||
-           ch->format->fourcc == V4L2_PIX_FMT_NV21)
+       switch (ch->format->fourcc) {
+       case V4L2_PIX_FMT_NV12:
+       case V4L2_PIX_FMT_NV21:
                info->fix.ypanstep = 2;
+       case V4L2_PIX_FMT_NV16:
+       case V4L2_PIX_FMT_NV61:
+               info->fix.xpanstep = 2;
+       }
 
        /* Initialize variable screen information using the first mode as
-        * default. The default Y virtual resolution is twice the panel size to
-        * allow for double-buffering.
+        * default.
         */
        var = &info->var;
        fb_videomode_to_var(var, mode);
        var->width = ch->cfg->panel_cfg.width;
        var->height = ch->cfg->panel_cfg.height;
-       var->yres_virtual = var->yres * 2;
+       var->xres_virtual = ch->xres_virtual;
+       var->yres_virtual = ch->yres_virtual;
        var->activate = FB_ACTIVATE_NOW;
 
        /* Use the legacy API by default for RGB formats, and the FOURCC API
@@ -1537,7 +2242,7 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
        else
                var->grayscale = ch->format->fourcc;
 
-       ret = sh_mobile_check_var(var, info);
+       ret = sh_mobile_lcdc_check_var(var, info);
        if (ret)
                return ret;
 
@@ -1712,15 +2417,27 @@ static const struct fb_videomode default_720p __devinitconst = {
 static int sh_mobile_lcdc_remove(struct platform_device *pdev)
 {
        struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
-       int i;
+       unsigned int i;
 
        fb_unregister_client(&priv->notifier);
 
+       for (i = 0; i < ARRAY_SIZE(priv->overlays); i++)
+               sh_mobile_lcdc_overlay_fb_unregister(&priv->overlays[i]);
        for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
                sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]);
 
        sh_mobile_lcdc_stop(priv);
 
+       for (i = 0; i < ARRAY_SIZE(priv->overlays); i++) {
+               struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i];
+
+               sh_mobile_lcdc_overlay_fb_cleanup(ovl);
+
+               if (ovl->fb_mem)
+                       dma_free_coherent(&pdev->dev, ovl->fb_size,
+                                         ovl->fb_mem, ovl->dma_handle);
+       }
+
        for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
                struct sh_mobile_lcdc_chan *ch = &priv->ch[i];
 
@@ -1737,8 +2454,11 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
        }
 
        for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
-               if (priv->ch[i].bl)
-                       sh_mobile_lcdc_bl_remove(priv->ch[i].bl);
+               struct sh_mobile_lcdc_chan *ch = &priv->ch[i];
+
+               if (ch->bl)
+                       sh_mobile_lcdc_bl_remove(ch->bl);
+               mutex_destroy(&ch->open_lock);
        }
 
        if (priv->dot_clk) {
@@ -1795,6 +2515,61 @@ static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *
        return 0;
 }
 
+static int __devinit
+sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv,
+                         struct sh_mobile_lcdc_overlay *ovl)
+{
+       const struct sh_mobile_lcdc_format_info *format;
+       int ret;
+
+       if (ovl->cfg->fourcc == 0)
+               return 0;
+
+       /* Validate the format. */
+       format = sh_mobile_format_info(ovl->cfg->fourcc);
+       if (format == NULL) {
+               dev_err(priv->dev, "Invalid FOURCC %08x\n", ovl->cfg->fourcc);
+               return -EINVAL;
+       }
+
+       ovl->enabled = false;
+       ovl->mode = LCDC_OVERLAY_BLEND;
+       ovl->alpha = 255;
+       ovl->rop3 = 0;
+       ovl->pos_x = 0;
+       ovl->pos_y = 0;
+
+       /* The default Y virtual resolution is twice the panel size to allow for
+        * double-buffering.
+        */
+       ovl->format = format;
+       ovl->xres = ovl->cfg->max_xres;
+       ovl->xres_virtual = ovl->xres;
+       ovl->yres = ovl->cfg->max_yres;
+       ovl->yres_virtual = ovl->yres * 2;
+
+       if (!format->yuv)
+               ovl->pitch = ovl->xres_virtual * format->bpp / 8;
+       else
+               ovl->pitch = ovl->xres_virtual;
+
+       /* Allocate frame buffer memory. */
+       ovl->fb_size = ovl->cfg->max_xres * ovl->cfg->max_yres
+                      * format->bpp / 8 * 2;
+       ovl->fb_mem = dma_alloc_coherent(priv->dev, ovl->fb_size,
+                                          &ovl->dma_handle, GFP_KERNEL);
+       if (!ovl->fb_mem) {
+               dev_err(priv->dev, "unable to allocate buffer\n");
+               return -ENOMEM;
+       }
+
+       ret = sh_mobile_lcdc_overlay_fb_init(ovl);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
 static int __devinit
 sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
                            struct sh_mobile_lcdc_chan *ch)
@@ -1854,7 +2629,9 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
                num_modes = cfg->num_modes;
        }
 
-       /* Use the first mode as default. */
+       /* Use the first mode as default. The default Y virtual resolution is
+        * twice the panel size to allow for double-buffering.
+        */
        ch->format = format;
        ch->xres = mode->xres;
        ch->xres_virtual = mode->xres;
@@ -1863,10 +2640,10 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
 
        if (!format->yuv) {
                ch->colorspace = V4L2_COLORSPACE_SRGB;
-               ch->pitch = ch->xres * format->bpp / 8;
+               ch->pitch = ch->xres_virtual * format->bpp / 8;
        } else {
                ch->colorspace = V4L2_COLORSPACE_REC709;
-               ch->pitch = ch->xres;
+               ch->pitch = ch->xres_virtual;
        }
 
        ch->display.width = cfg->panel_cfg.width;
@@ -1952,7 +2729,6 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
                }
                init_waitqueue_head(&ch->frame_end_wait);
                init_completion(&ch->vsync_completion);
-               ch->pan_offset = 0;
 
                /* probe the backlight is there is one defined */
                if (ch->cfg->bl_info.max_brightness)
@@ -2003,6 +2779,17 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
                        goto err1;
        }
 
+       for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) {
+               struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i];
+
+               ovl->cfg = &pdata->overlays[i];
+               ovl->channel = &priv->ch[0];
+
+               error = sh_mobile_lcdc_overlay_init(priv, ovl);
+               if (error)
+                       goto err1;
+       }
+
        error = sh_mobile_lcdc_start(priv);
        if (error) {
                dev_err(&pdev->dev, "unable to start hardware\n");
@@ -2017,6 +2804,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
                        goto err1;
        }
 
+       for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) {
+               struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i];
+
+               error = sh_mobile_lcdc_overlay_fb_register(ovl);
+               if (error)
+                       goto err1;
+       }
+
        /* Failure ignored */
        priv->notifier.notifier_call = sh_mobile_lcdc_notify;
        fb_register_client(&priv->notifier);
index 5c3bddd2cb7293871933fc057c8178678621a8a4..0f92f6544b94907a7a51b503101c07a705421aed 100644 (file)
@@ -47,6 +47,7 @@ struct sh_mobile_lcdc_entity {
 /*
  * struct sh_mobile_lcdc_chan - LCDC display channel
  *
+ * @pan_y_offset: Panning linear offset in bytes (luma component)
  * @base_addr_y: Frame buffer viewport base address (luma component)
  * @base_addr_c: Frame buffer viewport base address (chroma component)
  * @pitch: Frame buffer line pitch
@@ -59,7 +60,7 @@ struct sh_mobile_lcdc_chan {
        unsigned long *reg_offs;
        unsigned long ldmt1r_value;
        unsigned long enabled; /* ME and SE in LDCNT2R */
-       void *meram;
+       void *cache;
 
        struct mutex open_lock;         /* protects the use counter */
        int use_count;
@@ -68,7 +69,7 @@ struct sh_mobile_lcdc_chan {
        unsigned long fb_size;
 
        dma_addr_t dma_handle;
-       unsigned long pan_offset;
+       unsigned long pan_y_offset;
 
        unsigned long frame_end;
        wait_queue_head_t frame_end_wait;
index 82ba830bf95d51631776512ff0e12f124da8765f..7a0ba8bb3fbebed167711a7dd6a29d6a9e9e92a0 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <linux/device.h>
 #include <linux/err.h>
+#include <linux/export.h>
 #include <linux/genalloc.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -194,13 +195,28 @@ static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
 }
 
 /* -----------------------------------------------------------------------------
- * Allocation
+ * MERAM allocation and free
+ */
+
+static unsigned long meram_alloc(struct sh_mobile_meram_priv *priv, size_t size)
+{
+       return gen_pool_alloc(priv->pool, size);
+}
+
+static void meram_free(struct sh_mobile_meram_priv *priv, unsigned long mem,
+                      size_t size)
+{
+       gen_pool_free(priv->pool, mem, size);
+}
+
+/* -----------------------------------------------------------------------------
+ * LCDC cache planes allocation, init, cleanup and free
  */
 
 /* Allocate ICBs and MERAM for a plane. */
-static int __meram_alloc(struct sh_mobile_meram_priv *priv,
-                        struct sh_mobile_meram_fb_plane *plane,
-                        size_t size)
+static int meram_plane_alloc(struct sh_mobile_meram_priv *priv,
+                            struct sh_mobile_meram_fb_plane *plane,
+                            size_t size)
 {
        unsigned long mem;
        unsigned long idx;
@@ -215,7 +231,7 @@ static int __meram_alloc(struct sh_mobile_meram_priv *priv,
                return -ENOMEM;
        plane->marker = &priv->icbs[idx];
 
-       mem = gen_pool_alloc(priv->pool, size * 1024);
+       mem = meram_alloc(priv, size * 1024);
        if (mem == 0)
                return -ENOMEM;
 
@@ -229,11 +245,11 @@ static int __meram_alloc(struct sh_mobile_meram_priv *priv,
 }
 
 /* Free ICBs and MERAM for a plane. */
-static void __meram_free(struct sh_mobile_meram_priv *priv,
-                        struct sh_mobile_meram_fb_plane *plane)
+static void meram_plane_free(struct sh_mobile_meram_priv *priv,
+                            struct sh_mobile_meram_fb_plane *plane)
 {
-       gen_pool_free(priv->pool, priv->meram + plane->marker->offset,
-                     plane->marker->size * 1024);
+       meram_free(priv, priv->meram + plane->marker->offset,
+                  plane->marker->size * 1024);
 
        __clear_bit(plane->marker->index, &priv->used_icb);
        __clear_bit(plane->cache->index, &priv->used_icb);
@@ -248,62 +264,6 @@ static int is_nvcolor(int cspace)
        return 0;
 }
 
-/* Allocate memory for the ICBs and mark them as used. */
-static struct sh_mobile_meram_fb_cache *
-meram_alloc(struct sh_mobile_meram_priv *priv,
-           const struct sh_mobile_meram_cfg *cfg,
-           int pixelformat)
-{
-       struct sh_mobile_meram_fb_cache *cache;
-       unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
-       int ret;
-
-       if (cfg->icb[0].meram_size == 0)
-               return ERR_PTR(-EINVAL);
-
-       if (nplanes == 2 && cfg->icb[1].meram_size == 0)
-               return ERR_PTR(-EINVAL);
-
-       cache = kzalloc(sizeof(*cache), GFP_KERNEL);
-       if (cache == NULL)
-               return ERR_PTR(-ENOMEM);
-
-       cache->nplanes = nplanes;
-
-       ret = __meram_alloc(priv, &cache->planes[0], cfg->icb[0].meram_size);
-       if (ret < 0)
-               goto error;
-
-       cache->planes[0].marker->current_reg = 1;
-       cache->planes[0].marker->pixelformat = pixelformat;
-
-       if (cache->nplanes == 1)
-               return cache;
-
-       ret = __meram_alloc(priv, &cache->planes[1], cfg->icb[1].meram_size);
-       if (ret < 0) {
-               __meram_free(priv, &cache->planes[0]);
-               goto error;
-       }
-
-       return cache;
-
-error:
-       kfree(cache);
-       return ERR_PTR(-ENOMEM);
-}
-
-/* Unmark the specified ICB as used. */
-static void meram_free(struct sh_mobile_meram_priv *priv,
-                      struct sh_mobile_meram_fb_cache *cache)
-{
-       __meram_free(priv, &cache->planes[0]);
-       if (cache->nplanes == 2)
-               __meram_free(priv, &cache->planes[1]);
-
-       kfree(cache);
-}
-
 /* Set the next address to fetch. */
 static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
                                struct sh_mobile_meram_fb_cache *cache,
@@ -355,10 +315,10 @@ meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
        (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
 
 /* Initialize MERAM. */
-static int meram_init(struct sh_mobile_meram_priv *priv,
-                     struct sh_mobile_meram_fb_plane *plane,
-                     unsigned int xres, unsigned int yres,
-                     unsigned int *out_pitch)
+static int meram_plane_init(struct sh_mobile_meram_priv *priv,
+                           struct sh_mobile_meram_fb_plane *plane,
+                           unsigned int xres, unsigned int yres,
+                           unsigned int *out_pitch)
 {
        struct sh_mobile_meram_icb *marker = plane->marker;
        unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
@@ -427,8 +387,8 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
        return 0;
 }
 
-static void meram_deinit(struct sh_mobile_meram_priv *priv,
-                        struct sh_mobile_meram_fb_plane *plane)
+static void meram_plane_cleanup(struct sh_mobile_meram_priv *priv,
+                               struct sh_mobile_meram_fb_plane *plane)
 {
        /* disable ICB */
        meram_write_icb(priv->base, plane->cache->index,  MExxCTL,
@@ -441,20 +401,82 @@ static void meram_deinit(struct sh_mobile_meram_priv *priv,
 }
 
 /* -----------------------------------------------------------------------------
- * Registration/unregistration
+ * MERAM operations
  */
 
-static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
-                                     const struct sh_mobile_meram_cfg *cfg,
-                                     unsigned int xres, unsigned int yres,
-                                     unsigned int pixelformat,
-                                     unsigned int *pitch)
+unsigned long sh_mobile_meram_alloc(struct sh_mobile_meram_info *pdata,
+                                   size_t size)
+{
+       struct sh_mobile_meram_priv *priv = pdata->priv;
+
+       return meram_alloc(priv, size);
+}
+EXPORT_SYMBOL_GPL(sh_mobile_meram_alloc);
+
+void sh_mobile_meram_free(struct sh_mobile_meram_info *pdata, unsigned long mem,
+                         size_t size)
+{
+       struct sh_mobile_meram_priv *priv = pdata->priv;
+
+       meram_free(priv, mem, size);
+}
+EXPORT_SYMBOL_GPL(sh_mobile_meram_free);
+
+/* Allocate memory for the ICBs and mark them as used. */
+static struct sh_mobile_meram_fb_cache *
+meram_cache_alloc(struct sh_mobile_meram_priv *priv,
+                 const struct sh_mobile_meram_cfg *cfg,
+                 int pixelformat)
+{
+       unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
+       struct sh_mobile_meram_fb_cache *cache;
+       int ret;
+
+       cache = kzalloc(sizeof(*cache), GFP_KERNEL);
+       if (cache == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       cache->nplanes = nplanes;
+
+       ret = meram_plane_alloc(priv, &cache->planes[0],
+                               cfg->icb[0].meram_size);
+       if (ret < 0)
+               goto error;
+
+       cache->planes[0].marker->current_reg = 1;
+       cache->planes[0].marker->pixelformat = pixelformat;
+
+       if (cache->nplanes == 1)
+               return cache;
+
+       ret = meram_plane_alloc(priv, &cache->planes[1],
+                               cfg->icb[1].meram_size);
+       if (ret < 0) {
+               meram_plane_free(priv, &cache->planes[0]);
+               goto error;
+       }
+
+       return cache;
+
+error:
+       kfree(cache);
+       return ERR_PTR(-ENOMEM);
+}
+
+void *sh_mobile_meram_cache_alloc(struct sh_mobile_meram_info *pdata,
+                                 const struct sh_mobile_meram_cfg *cfg,
+                                 unsigned int xres, unsigned int yres,
+                                 unsigned int pixelformat, unsigned int *pitch)
 {
        struct sh_mobile_meram_fb_cache *cache;
        struct sh_mobile_meram_priv *priv = pdata->priv;
        struct platform_device *pdev = pdata->pdev;
+       unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
        unsigned int out_pitch;
 
+       if (priv == NULL)
+               return ERR_PTR(-ENODEV);
+
        if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
            pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
            pixelformat != SH_MOBILE_MERAM_PF_RGB)
@@ -469,10 +491,16 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
                return ERR_PTR(-EINVAL);
        }
 
+       if (cfg->icb[0].meram_size == 0)
+               return ERR_PTR(-EINVAL);
+
+       if (nplanes == 2 && cfg->icb[1].meram_size == 0)
+               return ERR_PTR(-EINVAL);
+
        mutex_lock(&priv->lock);
 
        /* We now register the ICBs and allocate the MERAM regions. */
-       cache = meram_alloc(priv, cfg, pixelformat);
+       cache = meram_cache_alloc(priv, cfg, pixelformat);
        if (IS_ERR(cache)) {
                dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
                        PTR_ERR(cache));
@@ -480,42 +508,50 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
        }
 
        /* initialize MERAM */
-       meram_init(priv, &cache->planes[0], xres, yres, &out_pitch);
+       meram_plane_init(priv, &cache->planes[0], xres, yres, &out_pitch);
        *pitch = out_pitch;
        if (pixelformat == SH_MOBILE_MERAM_PF_NV)
-               meram_init(priv, &cache->planes[1], xres, (yres + 1) / 2,
-                       &out_pitch);
+               meram_plane_init(priv, &cache->planes[1],
+                                xres, (yres + 1) / 2, &out_pitch);
        else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
-               meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2,
-                       &out_pitch);
+               meram_plane_init(priv, &cache->planes[1],
+                                2 * xres, (yres + 1) / 2, &out_pitch);
 
 err:
        mutex_unlock(&priv->lock);
        return cache;
 }
+EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_alloc);
 
-static void
-sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
+void
+sh_mobile_meram_cache_free(struct sh_mobile_meram_info *pdata, void *data)
 {
        struct sh_mobile_meram_fb_cache *cache = data;
        struct sh_mobile_meram_priv *priv = pdata->priv;
 
        mutex_lock(&priv->lock);
 
-       /* deinit & free */
-       meram_deinit(priv, &cache->planes[0]);
-       if (cache->nplanes == 2)
-               meram_deinit(priv, &cache->planes[1]);
+       /* Cleanup and free. */
+       meram_plane_cleanup(priv, &cache->planes[0]);
+       meram_plane_free(priv, &cache->planes[0]);
+
+       if (cache->nplanes == 2) {
+               meram_plane_cleanup(priv, &cache->planes[1]);
+               meram_plane_free(priv, &cache->planes[1]);
+       }
 
-       meram_free(priv, cache);
+       kfree(cache);
 
        mutex_unlock(&priv->lock);
 }
-
-static void
-sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
-                      unsigned long base_addr_y, unsigned long base_addr_c,
-                      unsigned long *icb_addr_y, unsigned long *icb_addr_c)
+EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_free);
+
+void
+sh_mobile_meram_cache_update(struct sh_mobile_meram_info *pdata, void *data,
+                            unsigned long base_addr_y,
+                            unsigned long base_addr_c,
+                            unsigned long *icb_addr_y,
+                            unsigned long *icb_addr_c)
 {
        struct sh_mobile_meram_fb_cache *cache = data;
        struct sh_mobile_meram_priv *priv = pdata->priv;
@@ -527,13 +563,7 @@ sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
 
        mutex_unlock(&priv->lock);
 }
-
-static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
-       .module                 = THIS_MODULE,
-       .meram_register         = sh_mobile_meram_register,
-       .meram_unregister       = sh_mobile_meram_unregister,
-       .meram_update           = sh_mobile_meram_update,
-};
+EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_update);
 
 /* -----------------------------------------------------------------------------
  * Power management
@@ -624,7 +654,6 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
        for (i = 0; i < MERAM_ICB_NUM; ++i)
                priv->icbs[i].index = i;
 
-       pdata->ops = &sh_mobile_meram_ops;
        pdata->priv = priv;
        pdata->pdev = pdev;
 
index 26f86428949885bf5e86ff1f801b13975ae7a293..5533a32c6ca132e8c981b7306c88d354fea7a23c 100644 (file)
@@ -904,7 +904,7 @@ static ssize_t ufx_ops_write(struct fb_info *info, const char __user *buf,
        result = fb_sys_write(info, buf, count, ppos);
 
        if (result > 0) {
-               int start = max((int)(offset / info->fix.line_length) - 1, 0);
+               int start = max((int)(offset / info->fix.line_length), 0);
                int lines = min((u32)((result / info->fix.line_length) + 1),
                                (u32)info->var.yres);
 
index 90a2e30272ad7ae90e4b106426dd37c486c73cd6..2f6b2b835f880391a9c7beccb750a2acc1347ca5 100644 (file)
@@ -1567,6 +1567,18 @@ static void w100_suspend(u32 mode)
                val = readl(remapped_regs + mmPLL_CNTL);
                val |= 0x00000004;  /* bit2=1 */
                writel(val, remapped_regs + mmPLL_CNTL);
+
+               writel(0x00000000, remapped_regs + mmLCDD_CNTL1);
+               writel(0x00000000, remapped_regs + mmLCDD_CNTL2);
+               writel(0x00000000, remapped_regs + mmGENLCD_CNTL1);
+               writel(0x00000000, remapped_regs + mmGENLCD_CNTL2);
+               writel(0x00000000, remapped_regs + mmGENLCD_CNTL3);
+
+               val = readl(remapped_regs + mmMEM_EXT_CNTL);
+               val |= 0xF0000000;
+               val &= ~(0x00000001);
+               writel(val, remapped_regs + mmMEM_EXT_CNTL);
+
                writel(0x0000001d, remapped_regs + mmPWRMGT_CNTL);
        }
 }
index 344713b1166953376b60ea1ae3cb062578342139..76628e32fed6c64d10febbaa5c01686c5df30985 100644 (file)
@@ -43,7 +43,6 @@ fw-shipped-$(CONFIG_CASSINI) += sun/cassini.bin
 fw-shipped-$(CONFIG_COMPUTONE) += intelliport2.bin
 fw-shipped-$(CONFIG_CHELSIO_T3) += cxgb3/t3b_psram-1.1.0.bin \
                                   cxgb3/t3c_psram-1.1.0.bin \
-                                  cxgb3/t3fw-7.10.0.bin \
                                   cxgb3/ael2005_opt_edc.bin \
                                   cxgb3/ael2005_twx_edc.bin \
                                   cxgb3/ael2020_twx_edc.bin
diff --git a/firmware/cxgb3/t3fw-7.10.0.bin.ihex b/firmware/cxgb3/t3fw-7.10.0.bin.ihex
deleted file mode 100644 (file)
index 96399d8..0000000
+++ /dev/null
@@ -1,1935 +0,0 @@
-:1000000060007400200380002003700000001000D6
-:1000100000002000E100028400070000E1000288E7
-:1000200000010000E0000000E00000A0010000006E
-:1000300044444440E3000183200200002001E0002A
-:100040002001FF101FFFD0001FFFC000E300043C91
-:100050000200000020006C841FFFC2A020006CCCB6
-:100060001FFFC2A420006D0C1FFFC2A820006D80DE
-:100070001FFFC2AC200003C0C00000E43100EA3121
-:1000800000A13100A03103020002ED306E2A05000C
-:10009000ED3100020002160012FFDBC03014FFDA5F
-:1000A000D30FD30FD30F03431F244C107249F0D347
-:1000B0000FD30FD30F12FFD5230A00240A00D30F4A
-:1000C000D30FD30F03431F244C107249F0D30FD327
-:1000D0000FD30F14FFCE03421F14FFCB03421F1296
-:1000E000FFCCC0302D37302D37342D37382D373CED
-:1000F000233D017233ED00020012FFC4C0302F37E0
-:10010000002F37102F37202F3730233D017233ED6A
-:1001100000020012FFBEC0302737002737102737F4
-:1001200020273730233D017233ED03020012FFB95F
-:1001300013FFBA0C0200932012FFB913FFB90C028F
-:1001400000932012FFB8C0319320822012FFB71312
-:10015000FFB7932012FFB715FFB316FFB6C030D715
-:100160002005660160001B00000000000000000088
-:10017000043605000200D30FD30F05330C6E3B1479
-:100180000747140704437631E604360505330C6F40
-:100190003BED00020012FFA615FFA3230A00D720A3
-:1001A000070443043E0505330C0747146F3BF00377
-:1001B000020012FFA1C03014FFA1D30FD30FD30F41
-:1001C0009340B4447249F2D30FD30FD30F14FF9B63
-:1001D000834014FF9B834012FF9B230A0014FF9A65
-:1001E000D30FD30FD30F9340B4447249F2D30FD33C
-:1001F0000FD30F14FF95834012FF95C92F832084DE
-:10020000218522BC22743B0F8650B4559630B433FE
-:100210007433F463FFE60000653FE1655FDE12FFC3
-:100220007C230A0028374028374428374828374C91
-:10023000233D017233ED03020000020012FF7AC079
-:1002400032032E0503020012FF7813FF819320C0B2
-:1002500011014931004831010200C00014FF7E0441
-:10026000D23115FF7D945014FF7D04D33115FF7CEE
-:10027000945014FF7C04D43115FF7C24560014FFE5
-:100280007B04D53115FF7B24560010FF7A03000054
-:10029000000000000000000000000000000000005E
-:1002A000000000000000000000000000000000004E
-:1002B000000000000000000000000000000000003E
-:1002C000000000000000000000000000000000002E
-:1002D000000000000000000000000000000000001E
-:1002E000000000000000000000000000000000000E
-:1002F00000000000000000000000000000000000FE
-:1003000000000000000000000000000000000000ED
-:1003100000000000000000000000000000000000DD
-:1003200000000000000000000000000000000000CD
-:1003300000000000000000000000000000000000BD
-:1003400000000000000000000000000000000000AD
-:10035000000000000000000000000000000000009D
-:10036000000000000000000000000000000000008D
-:10037000000000000000000000000000000000007D
-:10038000000000000000000000000000000000006D
-:10039000000000000000000000000000000000005D
-:1003A000000000000000000000000000000000004D
-:1003B000000000000000000000000000000000003D
-:1003C000000000000000000000000000000000002D
-:1003D000000000000000000000000000000000001D
-:1003E000000000000000000000000000000000000D
-:1003F00000000000000000000000000000000000FD
-:1004000000000000000000000000000000000000EC
-:1004100000000000000000000000000000000000DC
-:1004200063FFFC000000000000000000000000006E
-:100430000000000000000000000000001FFC0000A1
-:100440001FFC0000E30005C81FFC00001FFC0000AB
-:10045000E30005C81FFC00001FFC0000E30005C806
-:100460001FFFC0001FFFC000E30005C81FFFC00042
-:100470001FFFC018E30005C81FFFC0181FFFC018EA
-:10048000E30005E01FFFC0181FFFC294E30005E072
-:100490001FFFC2941FFFC294E300085C1FFFC2A0AD
-:1004A0001FFFC59CE300085C200000002000016ADB
-:1004B000E3000B582000018020000180E3000CC401
-:1004C0002000020020000203E3000CC42000021CF4
-:1004D00020000220E3000CC8200002202000022699
-:1004E000E3000CCC2000023C20000240E3000CD4CE
-:1004F0002000024020000249E3000CD82000024CFA
-:1005000020000250E3000CE42000025020000259B9
-:10051000E3000CE82000025C20000260E3000CF421
-:100520002000026020000269E3000CF82000026C49
-:1005300020000270E3000D04200002702000027908
-:10054000E3000D082000028C2000028CE3000D1453
-:100550002000029020000293E3000D14200002AC62
-:10056000200002B0E3000D18200002D0200002F2AB
-:10057000E3000D1C200003B0200003B0E3000D4099
-:10058000200003B0200003B0E3000D40200003B0C2
-:10059000200003B0E3000D40200003B0200003B0B2
-:1005A000E3000D40200003B020006EA4E3000D40E6
-:1005B00020006EA420006EA4E30078340000000048
-:1005C00000000000000000001FFC00001FFC0000F5
-:1005D0001FFFC5A01FFFC69020006EA820006EA8B8
-:1005E000DEFFFE000000080CDEADBEEF1FFFC2B054
-:1005F0001FFCFE001FFFC0A41FFFC5D0300000007D
-:10060000003FFFFF8040000010000000080FFFFFC8
-:100610001FFFC27D000FFFFF804FFFFF8000000023
-:1006200000000880B000000560500000600000007D
-:1006300040000011350000004100000010000001E2
-:100640002000000000001000400000000500000035
-:10065000800000190400000000000800E100020012
-:1006600010000005806000007000000020000009FC
-:10067000001FF8008000001EA0000000F80000002D
-:1006800007FFFFFF080000001800000001008001C4
-:10069000420000001FFFC22D1FFFC0EC00010080C0
-:1006A000604000001A0000000C0000001000000A6A
-:1006B000000030000001000080000018FC00000075
-:1006C0008000000100004000600008008000001C65
-:1006D0008000001A030000008000040004030403EB
-:1006E00050000003FFFFBFFF1FFFC3E400000FFF28
-:1006F000FFFFF000000016D00000FFF7A50000008B
-:100700001FFFC4C01FFFC4710001000800000B20C0
-:10071000202FFF801FFFC46500002C00FFFEFFF8A4
-:1007200000FFFFFF1FFFC58800002000FFFFDFFF65
-:100730000000FFEF010011001FFFC3E21FFFC5A073
-:10074000FFFFEFFF0000FFFB1FFFC6501FFFBEB003
-:10075000FFFFF7FF1FFFC0740000FFFD1FFFC64033
-:100760000001FBD01FFFC5C01FFFC6801FFFC5A132
-:10077000E0FFFE001FFFC5B0000080001FFFC54C5A
-:100780001FFFC5C41FFFC0781FFFC4E41FFCFFD8B4
-:10079000000100817FFFFFFFE1000600000027103D
-:1007A0001FFCFE301FFCFE701FFFC5481FFFC56009
-:1007B0000003D0901FFFC5742B5063802B507980AD
-:1007C0002B5090802B50A6801FFFC4790100110F81
-:1007D000202FFE0020300080202FFF000000FFFFB0
-:1007E0000001FFF82B50B2002B50B208000100109E
-:1007F0002B50B1802B50B2802B50BA000001001159
-:100800002B50BD282B50BC802B50BDA020300000A9
-:10081000DFFFFE005000000200C0000002000000E8
-:10082000FFFFF7F41FFFC07C000FF800044000003A
-:10083000001000000C4000001C400000E00000A080
-:100840001FFFC5501FFD00081FFFC5641FFFC578AF
-:100850001FFFC58CE1000690E10006EC00000000DF
-:100860000000000000000000000000000100000087
-:100870000000000000000000000000002010004008
-:10088000201000402010004020140080200C0000A8
-:10089000200C0000200C00002010004020140080DC
-:1008A0002014008020140080201800C0201C0100AB
-:1008B000201C0100201C010020200140201800C045
-:1008C000201800C0201800C0201C0100201800C003
-:1008D000201800C0201800C0201C0100202001406A
-:1008E00020200140202001402020094020200940F4
-:1008F000202009402020094020240980FFFFFFFF1D
-:10090000FFFFFFFFFFFFFFFF0000000000000000EF
-:1009100000000000000000000000000020005588DA
-:1009200020005458200055882000558820005394FA
-:100930002000539420005394200051D4200051D41F
-:10094000200051CC2000513820004FE020004DC045
-:1009500020004B94000000000000000020005558CB
-:1009600020005424200054C8200054C82000527C89
-:100970002000527C2000527C2000527C2000527CBF
-:10098000200051C42000527C20004F0020004D70F8
-:1009900020004B40000000000000000020000BF091
-:1009A00020003ADC200004C02000473020000BE883
-:1009B000200041F4200003F0200046F020004B1CF2
-:1009C00020003F0020003E1C20003A58200038E85C
-:1009D00020003658200031B820003C7820002DD06F
-:1009E0002000286420006828200023F0200020D068
-:1009F0002000207C20001D68200018602000158841
-:100A000020000E5420000C3420001134200013204C
-:100A1000200043EC20003EB420000BF8200004C06E
-:100A200000000000000000000000000000000000C6
-:100A300000000000000000000000000000000000B6
-:100A400000000000000000000000000000000000A6
-:100A50000000000000000000000000000000000096
-:100A60000000000000000000000000000000000086
-:100A70000000000000000000000000000000000076
-:100A80000000000000000000000000000000000066
-:100A90000000000000000000000000000000000056
-:100AA0003264000000000000326400006400640052
-:100AB00064006400640064006400640000000000DE
-:100AC0000000000000000000000000000000000026
-:100AD0000000000000000000000000000000000016
-:100AE0000000000000000000000000000000000006
-:100AF00000000000000000000000000000000000F6
-:100B000000000000000010000000000000000000D5
-:100B100000000000000000000000000000001000C5
-:100B200000000000000000000000000000000000C5
-:100B300000432380000000000000000000000000CF
-:100B400000000000000000000000000000000000A5
-:100B50000000000000000000005C94015D94025E53
-:100B600094035F94004300000000000000000000B8
-:100B70000000000000000000000000000000000075
-:100B80000000000000000000000000000000000065
-:100B90000000000000000000005C90015D90025E1B
-:100BA00090035F9000530000000000000000000070
-:100BB0000000000000000000000000000000000035
-:100BC0000000000000000000000000000000000025
-:100BD0000000000000000000009C94001D90019D9A
-:100BE00094029E94039F94040894050994060A9421
-:100BF000070B94004300000000000000000000000C
-:100C000000000000000000000000000000000000E4
-:100C10000000000000000000009C90019D90029EDA
-:100C200090071D90039F90047890057990067A9024
-:100C3000077B90005300000000000000000000004F
-:100C400000000000000000000000000000000000A4
-:100C5000000000000000000000DC94001D9001DD99
-:100C60009402DE9403DF940404940505940606942C
-:100C70000707940808940909940A0A940B0B940036
-:100C80004300000000000000000000000000000021
-:100C9000000000000000000000DC9001DD9002DE9A
-:100CA000900B1D9003DF9004B49005B59006B690AC
-:100CB00007B79008B89009B9900ABA900BBB90009A
-:100CC0005300000063FFFC0020006C6010FFFF0A6F
-:100CD0000000000020006C8400D23110FFFE0A00EA
-:100CE0000000000020006CCC00D33110FFFE0A0091
-:100CF0000000000020006D0C00D43110FFFE0A003F
-:100D00000000000020006D8000D53110FFFE0A00B9
-:100D10000000000063FFFC00E00000A012FFF7826B
-:100D200020028257C82163FFFC12FFF303E830045E
-:100D3000EE3005C03093209421952263FFFC000023
-:100D40001FFFD000000400201FFFC5A01FFFC6909A
-:100D5000200A0011FFFB13FFFB03E631010200161E
-:100D6000FFFA17FFFAD30F776B069060B4667763CC
-:100D7000F85415F3541AA50F140063FFF90000008E
-:100D80006C1004C020D10F006C1004C0C71AEF060D
-:100D9000D830BC2BD72085720D4211837105450BCD
-:100DA000957202330C2376017B3B04233D0893713B
-:100DB000A32D12EEFE19EEFEA2767D632C2E0A0004
-:100DC000088202280A01038E380E0E42C8EE29A6B8
-:100DD0007E6D4A0500208800308C8271D10FC0F0F2
-:100DE000028F387FC0EA63FFE400C0F1C050037E89
-:100DF0000CA2EE0E3D1208820203F538050542CB27
-:100E00005729A67E2FDC100F4F366DFA050020887B
-:100E100000308CBC75C03008E208280A0105833810
-:100E2000030342C93E29A67E0D480CD30F6D8A05E7
-:100E300000208800B08C8271D10FC05008F5387541
-:100E4000C0C163FFBBC06002863876C0DA63FFD4DE
-:100E50006C101216EED8C1F9C1E8C1C72B221E28AA
-:100E6000221DC0D07B81352920060BB702299CFAB0
-:100E7000655008282072288CFF2824726491642A07
-:100E8000B0000CA80C64816F0EA90C6492BB7FA10A
-:100E90003FC1CE7CA13669AC336000370029200603
-:100EA000D7D0299CFACC57282072288CFF2824728E
-:100EB0006491392AD0000CA80C6481680EA90C64D6
-:100EC000931F7FA10BC1CE7CA10268AC06C020D1CC
-:100ED0000F2D25028A32C0900A6E5065E5B529248F
-:100EE00067090F4765F5B12C200C1FEEB30CCE112E
-:100EF000AFEE29E286B44879830260058219EEAF2D
-:100F000009C90A2992A36890078F2009FF0C65F58B
-:100F10006E2FE28564F56865559628221D7B810554
-:100F2000D9B060000200C0908B9417EEA50B881416
-:100F300087740B0B47A87718EEA309BB100877023C
-:100F400097F018EEA117EEA208A8010B8802074738
-:100F5000021BEE9E97F10B880298F22790232B90AC
-:100F60002204781006BB1007471208BB0228902104
-:100F70000777100C88100788020B880217EE968BF3
-:100F80003307BB0187340B880298F3979997F48B4A
-:100F90009587399BF588968B3898F688979BF897B4
-:100FA000F998F717EE8D28E28507C7082D74CF084A
-:100FB000480B28E68565550F2B221E28221D7B89AC
-:100FC000022B0A0064BF052CB00728B000DA200607
-:100FD000880A28824CC0D10B8000DBA065AFE76394
-:100FE000FEEA0000292072659E946004E72A2072C0
-:100FF00065AEBF6004DE00002EB0032C2067D4E095
-:1010000065C1058A328C330AFF500C4554BC5564C7
-:10101000F4EB19EE72882A09A90109880C64821F71
-:10102000C0926000DD2ED0032A2067D4E065A0D8EE
-:101030008A328B330AFC500B4554BC5564C4BE192C
-:10104000EE67882A09A9017989D50BEA5064A4E3DF
-:101050000CEE11C0F02F16132E16168AE78CE82A14
-:1010600016128EE9DFC0AAEA7EAB01B1CF0BA85001
-:101070006583468837DBC0AE89991E789B022BCCEE
-:10108000012B161B29120E2B0A0029161A7FC307E3
-:101090007FC9027EAB01C0B165B49D8B352F0A00BC
-:1010A0002A0A007AC30564C3CB2F0A0165F4892B91
-:1010B00012162B1619005104C0C100CC1A2CCCFFFB
-:1010C0002C16170CFC132C16182B121A2A121BDCC8
-:1010D000505819B6C0D0C0902E5CF42C12172812AC
-:1010E000182F121B2A121A08FF010CAA01883407B4
-:1010F0004C0AAB8B2812192BC6162F86082A860994
-:101100002E74102924672E70038975B1EA2A74039E
-:10111000B09909490C659DB42B20672D250265B354
-:10112000FA2B221E2C221D7BC901C0B064BD9D2C50
-:10113000B00728B000DA2006880A28824CC0D10BFC
-:101140008000DBA065AFE763FD8289BAB199659045
-:101150009788341CEE2398BA8F331EEE1C0F4F5421
-:101160002FB42C8D2A8A320EDD020CAC017DC966AB
-:101170000A49516F92608A3375A65B2CB0130AED51
-:10118000510DCD010D0D410C0C417DC9492EB01200
-:10119000B0EE65E3C6C0D08E378CB88A368FB97C86
-:1011A000A3077AC9027EFB01C0D1CED988350AAD2A
-:1011B000020E8E0878EB022DAC0189B7DAC0AF9B26
-:1011C00079BB01B1CADCB0C0B07DA3077AD9027C7B
-:1011D000EB01C0B164B161C091292467C020D10F77
-:1011E00000008ADAB1AA64A0C02C20672D25026510
-:1011F000C3111DEDF68A321EEDFB0DAD010EDD0CA7
-:1012000065D28A0A4E516FE202600281C0902924A1
-:1012100067090F4765F2F828221D7B89022B0A0017
-:1012200064BCA92CB00728B000DA2006880A2882FE
-:101230004CC0D10B8000DBA065AFE763FC8E0000E3
-:101240000CE9506492ED0CEF11C080281611AFBF6D
-:101250002F16198EF88BF7DAE08FF92B1610ABFBEF
-:101260007FBB01B1EA0CA8506580D68837DCE0AFBF
-:1012700089991C789B022CEC012C161B29120C2C32
-:101280000A0029161A7AE3077AE9027FBB01C0C176
-:1012900065C2A58B352C0A002A0A007AE30564E1B1
-:1012A000CA2C0A0164CE0D60028E88341BEDCD98E5
-:1012B000DA8F331EEDC60F4F542FD42C8C2A8A326E
-:1012C0000ECC020BAB010CBB0C65BF0A0A49516E78
-:1012D000920263FF018A330AAB5064BEF92CD0132B
-:1012E0000AEE510ECE010E0E410C0C410ECC0C65D7
-:1012F000CEE42FD012B0FF65F26EC0B08E378CD81E
-:101300008A362FD2097CA3077AC9027EFB01C0B1BD
-:1013100065BEC38835DBA0AE8E78EB01B1AB89D753
-:10132000DAC0AF9D79DB01B1CAC0C07BA3077AB92F
-:10133000027DEB01C0C165CE9DC090292467C0200D
-:10134000D10F88378C3698140CE90C29161408F83C
-:101350000C981D78FB07281214B088281614891DD4
-:101360009F159B16C0F02B121429161A2B161B8BD7
-:10137000147AE30B7AE90688158E1678EB01C0F132
-:1013800065F1BA29121A2F12118A352E121B9A1AD8
-:10139000AFEE2F1210C0A0AF9F79FB01B1EE9F11ED
-:1013A000881AC0F098107AE30A7EA9052A12017AF9
-:1013B0008B01C0F164F08160018389368B37991706
-:1013C0000BE80C981F09C90C29161578EB07281291
-:1013D00015B088281615D9C09A199E188A1F2E1282
-:1013E000152A161A2E161BDAC0C0E08C177F930B35
-:1013F0007FA90688188F1978FB01C0E165E13E29B5
-:10140000121A2F12138A352E121B9A1BAFEE2F12AF
-:1014100012C0A0AF9F79FB01B1EE9F13881BC0F0F3
-:1014200098127AE30A7EA9052A12037A8B01C0F189
-:1014300065F10A2E12162E16192A121B005104C02D
-:10144000E100EE1AB0EE2E16170EFF132F16180F2E
-:10145000CC01ACAA2F121A0EBC01ACFC7FCB01B19F
-:10146000AA2A161B2C161A63FC5E00007FB30263C7
-:10147000FE3163FE2B7EB30263FC3063FC2A000066
-:101480006450C0DA20DBC058168AC020D10FC0914A
-:1014900063FD7A00C09163FA44DA20DB70C0D12E7C
-:1014A0000A80C09A2924682C7007581575D2A0D1DB
-:1014B0000F03470B18ED4DDB70A8287873022B7DC6
-:1014C000F8D9B063FA6100002A2C74DB40580EEEA4
-:1014D00063FAE4000029221D2D25027B9901C0B08A
-:1014E000C9B62CB00728B000DA2006880A28824C3A
-:1014F000C0D10B8000DBA065AFE7C020D10FC09149
-:1015000063FBFF00022A0258024C0AA202060000F6
-:10151000022A025802490AA202060000DB70DA2001
-:10152000C0D12E0A80C09E2924682C7007581554FB
-:10153000C020D10FC09463FBC9C09663FBC4C096A2
-:1015400063FBBF002A2C74DB30DC405BFE0FDBA0AA
-:10155000C2A02AB4002C200C63FF27008D358CB765
-:101560007DCB0263FDD263FC6D8F358ED77FEB029E
-:1015700063FDC563FC6000006C1004C020D10F0047
-:101580006C1004C020D10F006C10042B221E2822E6
-:101590001DC0A0C0942924062A25027B8901DBA056
-:1015A000C9B913ED04DA2028B0002CB00703880A6B
-:1015B00028824CC0D10B8000DBA065AFE7C020D1F2
-:1015C0000F0000006C10042C20062A210268C805B8
-:1015D00028CCF965812E0A094C6591048F30C1B879
-:1015E0000F8F147FB00528212365812716ECF3297E
-:1015F000629E6F98026000F819ECEF2992266890BD
-:10160000078A2009AA0C65A0E72A629D64A0E12B45
-:10161000200C0CB911A6992D92866FD9026000DBBF
-:101620001DECE70DBD0A2DD2A368D0078E200DEE6C
-:101630000C65E0C7279285C0E06470BF1DECEC68C4
-:10164000434E1CECEB8A2B0CAA029A708920089955
-:10165000110D99029971882A98748F329F752821EB
-:1016600004088811987718ECDC0CBF11A6FF2DF246
-:1016700085A8B82E84CF2DDC282DF685C85A2A2CB3
-:1016800074DB40580E81D2A0D10FC020D10F0000D2
-:101690000029CCF96490B12C20668931B1CC0C0CB6
-:1016A000472C24666EC60260008509F85065807F6D
-:1016B0001CECD18A2B0F08400B881008AA020CAA38
-:1016C000029A7089200899110D99029971883398AE
-:1016D000738C329C728A2A9A748934997563FF7D5F
-:1016E00000CC57DA20DB30DC4058155FC020D10F2A
-:1016F00000DA20C0B65815EE63FFE500DA20581571
-:10170000EC63FFDC00DA20DB30DC40DD5058167A79
-:10171000D2A0D10FC858DA20DB305814C72A2102D2
-:1017200065AFBDC09409A90229250263FFB200007C
-:101730002B21045814731DECADC0E02E24668F30AD
-:101740002B200C0F8F1463FF66292138C088798302
-:101750001F8C310CFC5064CF562B2104C0C0581490
-:10176000681DECA2C0E08F302B200C0F8F1463FF9C
-:101770003E2C20662B2104B1CC0C0C472C2466583F
-:1017800014601DEC9AC0E02E24668F302B200C0FC5
-:101790008F1463FF1A0000006C1004C0B7C0A116BC
-:1017A000EC9615EC88D720D840B822C04005350209
-:1017B0009671957002A438040442C94B1AEC7B1947
-:1017C000EC7C29A67EC140D30F6D4A0500808800BD
-:1017D000208C220A88A272D10FC05008A53875B09B
-:1017E000E363FFD76C10069313941129200665520A
-:1017F00088C0716898052A9CF965A29816EC6F2933
-:1018000021028A1309094C6590CD8AA00A6A512ADF
-:10181000ACFD65A0C2CC5FDB30DA208C115815120C
-:10182000C0519A13C7BF9BA98E132EE20968E060CE
-:101830002F629E1DEC606FF8026000842DD2266836
-:10184000D0052F22007DF9782C629DC79064C0706E
-:101850009C108A132B200C2AA0200CBD11A6DD0A97
-:101860004F14BFA809880129D286AF88288C09792E
-:101870008B591FEC520FBF0A2FF2A368F0052822E4
-:10188000007F894729D285D4906590756000430018
-:10189000002B200C1FEC4A0CBD11A6DD29D2860FAF
-:1018A000BF0A6E96102FF2A368F00488207F890586
-:1018B00029D285659165DA2058157DC95C6001FFE4
-:1018C00000DA20C0B658157A60000C00C09063FFA3
-:1018D000B50000DA205815766551E48D138C11DBC4
-:1018E000D08DD0022A020D6D515813E39A1364A1D2
-:1018F000CEC75F8FA195A9C0510F0F479F1163FEFF
-:10190000FD00C091C0F12820062C2066288CF9A784
-:10191000CC0C0C472C24666FC6098D138DD170DE5C
-:1019200002290A00099D02648159C9D38A102B211A
-:10193000045813F38A13C0B02B24662EA2092AA0E0
-:10194000200E28141CEC298D1315EC1DC1700A778C
-:101950003685562DDC28AC2C9C12DED0A8557CD3C5
-:10196000022EDDF8D3E0DA40055B02DC305BFF8A53
-:10197000D4A028200CB455C0D02B0A882F0A800C84
-:101980008C11A6CC29C285AF3FAB9929C6851CEC2A
-:1019900012DEF0AC882D84CF28120229120378F3CE
-:1019A000022EFDF8289020D3E007880CC1700808AB
-:1019B00047289420087736657FAB891313EC10898C
-:1019C00090C0F47797491BEC0EC1CA2821048513F7
-:1019D000099E4006EE11875304881185520E880235
-:1019E0000C88029BA09FA18F2B9DA598A497A795DB
-:1019F000A603FF029FA22C200C1EEBF7AECE0CCC50
-:101A00001106CC082BC2852DE4CF2BBC202BC6851C
-:101A10002A2C748B11580D9CD2A0D10F28203DC0C8
-:101A2000E07C877F2E24670E0A4765A07B1AEBF5C2
-:101A300088201EEBE38F138EE48FF40888110A8848
-:101A4000020F8F14AFEE1FEBF098910FEE029E90F5
-:101A50001EEBEFC0801AEBE02CD285AABAB8CC28D6
-:101A6000A4CF2CD6852C21022F20720ECC02B1FFE0
-:101A70002F24722C2502C020D10F871387700707EF
-:101A80004763FD6E282138C099798B0263FE9ADD89
-:101A9000F063FE9500DA20DB308C11DD505815968E
-:101AA000D2A0D10FC0E163FF7A8B138C11DD50C03F
-:101AB000AA2E0A802A2468DA205813F1D2A0D10F66
-:101AC000C020D10F6C1006292102C0D07597102AB2
-:101AD00032047FA70A8B357FBF052D25020DD90261
-:101AE000090C4C65C18216EBB41EEBB228629EC095
-:101AF000FA78F30260018829E2266890078A2009B3
-:101B0000AA0C65A17A2A629DDFA064A1772B200C24
-:101B10000CBC11A6CC29C286C08C79830260015707
-:101B200019EBA709B90A2992A368900788200988A8
-:101B30000C65814327C2851CEBA964713A89310980
-:101B40008B140CBB016FB11D2C20669F10B1CC0C07
-:101B50000C472C24666EC60260014009FF5065F1F7
-:101B60003A8A102AAC188934C0C47F973C18EBA974
-:101B70001BEBA88F359C719B708B209D7408BB025A
-:101B80009B72C08298751BEBA40F08409B730F8853
-:101B90001198777FF70B2F2102284A0008FF022FA8
-:101BA0002502C0B4600004000000C0B07E97048F1E
-:101BB000362F25227D970488372825217C9736C02B
-:101BC000F1C0900AF9382F3C200909426490861927
-:101BD000EB7618EB7728967E00F08800A08C00F05A
-:101BE0008800A08C00F08800A08C2A629D2DE4A2C1
-:101BF0002AAC182A669D89307797388F338A321835
-:101C0000EB8007BE0B2C2104B4BB04CC1198E0C0C0
-:101C10008498E1882B9DE59AE69FE71AEB78099F67
-:101C20004006FF110FCC020A880298E2C1FC0FCCDB
-:101C3000022CE604C9B82C200C1EEB670CCA11AEAE
-:101C4000CC06AA0829A2852DC4CF09B90B29A685DF
-:101C5000CF5CC020D10FC081C0900F8938C0877978
-:101C6000880263FF7263FF6600CC57DA20DB30DC4A
-:101C7000405813FDC020D10FDA2058148D63FFE8BF
-:101C8000C0A063FE82DA20C0B658148963FFD90071
-:101C9000DB402A2C74580CFCD2A0D10F8A102B21C7
-:101CA000045813171EEB44C0D02D246663FEB10008
-:101CB0006C1006D62019EB3F1EEB4128610217EB92
-:101CC0003E08084C65805F8A300A6A5169A3572B29
-:101CD000729E6EB83F2A922668A0048C607AC9343E
-:101CE0002A729D2C4CFECAAB2B600CB64F0CBD115A
-:101CF000A7DD28D2860EBE0A78FB269C112EE2A311
-:101D00002C160068E0052F62007EF91522D285CFDF
-:101D10002560000D00DA60C0B6581465C85A60012D
-:101D20000F00DA60581462655106DC40DB308D30FC
-:101D3000DA600D6D515812D0D3A064A0F384A1C015
-:101D40005104044763FF6D00C0B02C60668931B157
-:101D5000CC0C0C472C64666FC60270960A2B61048B
-:101D60005812E7C0B02B64666550B42A3C10C0E737
-:101D7000DC20C0D1C0F002DF380F0F4264F09019B0
-:101D8000EB0A18EB0B28967E8D106DDA0500A08803
-:101D900000C08CC0A089301DEB1A77975388328C15
-:101DA000108F3302CE0BC02492E12261049DE00427
-:101DB00022118D6B9BE59FE798E61FEB1009984079
-:101DC0000688110822020FDD02C18D9DE208220261
-:101DD00092E4B4C22E600C1FEB000CE811A7882C13
-:101DE0008285AFEE0C220B2BE4CF228685D2A0D1C8
-:101DF0000F28600CD2A08C1119EAF80C8D11A9885B
-:101E0000A7DD2ED2852B84CF0ECC0B2CD685D10FFF
-:101E1000C0F00ADF387FE80263FF6C63FF600000F8
-:101E20002A6C74C0B2DC20DD405812C5C0B063FF1C
-:101E300063C020D10F0000006C10042920062A2264
-:101E40001EC0392C221D232468C0307AC107DDA0B2
-:101E5000600004000000C0D06E9738C08F2E0A804A
-:101E60002B2014C0962924060EBB022E21022B24FF
-:101E7000147E8004232502DE307AC10EC8ABDBD08D
-:101E8000DA202C0A00580B062E21020E0F4CC8FE39
-:101E90006000690068956528210208084C65805C2F
-:101EA0001AEAC61EEAC42BA29EC09A7B9B5E2BE256
-:101EB0002668B0048C207BC95329A29D1FEAC16407
-:101EC000904A9390C0C31DEAD52B21049D9608BB70
-:101ED000110CBB029B979B911CEAD2C08523E4A204
-:101EE0002BA29D2824068DFA282102B0DD2BBC30C0
-:101EF0002BA69D9DFA0C8802282502C8D2C020D1AD
-:101F00000F8EF912EAC82E2689C020D10FDA20C020
-:101F1000B65813E7C020D10F6C10062A2006941083
-:101F200068A80528ACF965825029210209094C6589
-:101F3000920ACC5FDB30DA208C1058134BC051D39F
-:101F4000A0C7AF9A3AC0D01CEA9D14EAA31EEA9C2F
-:101F50008F3A16EA99B1FB64B13128629E6F88020C
-:101F60006001ED294C332992266890078A2009AA3E
-:101F70000C65A1DC2A629DC08E64A1D42B200C0CC0
-:101F8000B7110677082972867983026001CD0CB9F2
-:101F90000A2992A36890082C220009CC0C65C1BBC9
-:101FA0002772856471B5282006288CF96481E52C98
-:101FB00020668931B1CC0C0C472C24666EC60260B9
-:101FC00001A109F85065819B2A21048CE488361E02
-:101FD000EA7D088914A9CC08084709881019EA92F3
-:101FE0000ECC029C7099718C2A1EEA9008CC020ECD
-:101FF000CC029C722E302C293013283012049910F8
-:102000000688100CEE109F740EAE0209880208EECE
-:10201000029E738C3704AA119C758938C0F4997696
-:102020008839C0C1987718EA828E359C7B9E780EDD
-:102030008E1408EE029E7A8E301CEA7177E73088A3
-:102040003289339C7C9F7D0E9C4006CC118F2B29BE
-:1020500076132D76112876120CAA0218EA68C1C9E7
-:102060000CAA022A761008FF029F7EC0AA60000117
-:10207000C0A6A4BC0CB911A6992892852DC4CF087E
-:10208000A80B289685655100C020D10F2B200C0C81
-:10209000B7110677082A72860CB90A6FA902600187
-:1020A000182992A36890082A220009AA0C65A109A0
-:1020B0002A728564A1032C203D0C2C4064C08C8CBA
-:1020C000350C8C1464C0848FE57CF37F8C360C8CCB
-:1020D0001464C0777CF374283013C0FC78F86CC0AB
-:1020E00090292467090C4765C0D719EA4718EA45C3
-:1020F0008F208C3508FF110C8C1408FF0288E49F98
-:10210000A1AC8C09CC029CA08C369FA30C8C14AC87
-:102110008809880298A218EA3DA4BC2F72852DC4B4
-:10212000CF2FFC102F76852F210229207208FF0265
-:10213000B2992924722F2502C020D10F00CC57DA82
-:1021400020DB308C105812C8C020D10FC09163FF23
-:102150008FDA20C0B658135663FFE100DA20581317
-:102160005463FFD82B21045811E61EEA152B200CCE
-:10217000C0D02D24668F3A63FE4DDA20DB30DC4080
-:10218000DD505813DDD2A0D10F2A2C748B10580BC0
-:10219000BED2A0D10F292138C08879832E8C310C72
-:1021A000FC5064CE222B2104C0C05811D5C0D01ED3
-:1021B000EA048F3A2B200C63FE0DDA2058133C639F
-:1021C000FF7ADA205BFF1CD2A0D10F002C20662BF7
-:1021D0002104B1CC0C0C472C24665811C91EE9F817
-:1021E0002B200CC0D02D24668F3A63FDDA0000004E
-:1021F0006C10089514C061C1B0D9402A203DC04080
-:102200000BAA010A64382A200629160568A8052C9D
-:10221000ACF965C33F1DE9EA6440052F120464F27E
-:10222000A02621021EE9E606064C6562E615E9E2F3
-:102230006440D98A352930039A130A990C6490CCEA
-:102240002C200C8B139C100CCC11A5CC9C112CC2F7
-:1022500086B4BB7CB3026002D78F100EFE0A2EE25A
-:10226000A368E0098620D30F0E660C6562C2881150
-:102270002882856482BA891364905EDA80D9308CB2
-:10228000201EE9E01FE9E11DE9CE8B138DD4D4B007
-:102290007FB718B88A293C10853608C6110E660229
-:1022A0009681058514A5D50F550295800418146DE7
-:1022B0008927889608CB110888140EBB02A8D82954
-:1022C0009C200F88029BA198A088929BA308881449
-:1022D000A8D80F880298A22AAC1019E9CCC0C08FE8
-:1022E000131EE9BD86118D10286285AEDD08FF0B37
-:1022F0002CD4CF2821022F66858B352A207209889D
-:1023000002ABAA2825022A2472C020D10F29529E8E
-:1023100018E9A96F980260020B28822668800829B4
-:10232000220008990C6591FC2A529DC1CE9A126434
-:10233000A1F22B200C2620060CB8110588082D824E
-:10234000860EBE0A7DC3026002052EE2A368E00885
-:102350002F22000EFF0C65F1F6288285D780DE80E3
-:102360006482009816266CF96462012C206688311C
-:102370002CCC010C0C472C24666EC6026001BC08F4
-:10238000FD5065D1B61DE9AB1CE98F19E9962A21EC
-:10239000048B2D2830102F211D0C88100BFB090AEF
-:1023A00088020988020CBB026441529B709D71989F
-:1023B00072C04D8D35D9E064D06ED730DBD0D830C7
-:1023C0007FD714273C10BCE92632168C3996E69C40
-:1023D000E78A37B4382AE6080B131464304A2A8295
-:1023E0001686799A9696978C778A7D9C982B821779
-:1023F0002C7C209A9A2A9C189B99867BB03B298C2E
-:10240000086DB9218BC996A52692162AAC18B899E1
-:102410009BA196A08BC786CD9BA22B921596A49BC1
-:10242000A386CB2CCC2026A605C0346BD4200D3B34
-:102430000C0DD8090E880A7FB705C0909988BC8812
-:10244000C0900B1A126DAA069988998B288C18C017
-:10245000D01BE97A1CE97916E96EB1FF2A211C2309
-:10246000E6130F0F4F26E6122F251D7FA906C0F099
-:10247000C08028251D05F6111AE9678F202BE61567
-:102480002CE6162DE61726E6180AFA022AE6142983
-:102490002006299CF96490F829200C8D14C0801A1C
-:1024A000E94E0C9C11AA99A5CCDA202BC285289460
-:1024B000CF0B4B0B2BC685C0B08C155811BBD2A0CF
-:1024C000D10F8A356FA546D8308BD56DA90C8A8679
-:1024D0000A8A14CBA77AB335288C10C080282467C9
-:1024E000080B4765B10BDA20DB302C12055811DEE2
-:1024F000D3A0C0C1C0D02DA4039C1463FD22863696
-:102500006461059B709D719872C04D63FEA4C0818B
-:1025100063FFC9008814CC87DA20DB308C15581192
-:10252000D2C020D10FDA20C0B658126163FFE40098
-:1025300000DA208B1058125E63FFD8009E178A12B3
-:102540002B21045810EF8E17C09029246663FE34A7
-:10255000C08063FE06DA20DB308C15DD505812E6B1
-:10256000D2A0D10FDA2058125263FFA7002B2138D6
-:10257000C0A87BAB026001048C310CFC5064CE041B
-:102580008A122B2104C0C098175810DD8E1763FDE6
-:10259000F32D21382DDCFF0D0D4F2D253865DEF78D
-:1025A00028206A7F87050826416460A3C09016E949
-:1025B000141CE9232A200723E61BB1AA0CFD0226DE
-:1025C000E61A2B200A29E61D2DE61E0CBB022BE67F
-:1025D0001C8B260A0A472BE6208B282AE53E2BE691
-:1025E000212924072820062A2064688346B44463EE
-:1025F000FEA5DB30DA208C158D142E0A80C08E28C3
-:10260000246858111FD2A0D10F2E7C4819E8ED2A5A
-:1026100032162B76129D712D761328761489960A20
-:102620002A14AA990C9902997069ED71C14663FD4B
-:102630008100000064AFB51DE8E22C20168DD20A9F
-:10264000CC0C00D10400CC1AACBC9C2963FF9D00CB
-:102650002B21046EB81E2C2066B8CC0C0C472C2401
-:1026600066C9C09E178A125810A68E17C0348F20D4
-:10267000C0D02D2466C06826240663FF2E8A122B44
-:1026800021042C20669817B1CC0C0C472C246658DA
-:10269000109C8E178716C0D02D246663FCE68D35FE
-:1026A000C08064D04AD9E0DC30DBE0DF301AE8E5F6
-:1026B000B188B4FF16E8E584C92D9DFF87C82CCCEE
-:1026C0001027D63006460127D6320A440117E8DF24
-:1026D00024D631A74727D63324F21596B794B68D62
-:1026E000C3BCBB9DB58D35299C107D83C22F211D98
-:1026F000C14663FD330000006C1006292006289CAB
-:10270000F86582BF2921022B200C09094C6590E154
-:1027100016E8AA0CBA11A6AA2DA2862C0A127DC30D
-:102720000260028C19E8A609B90A2992A3689007E9
-:102730008C2009CC0C65C27829A2856492722D6226
-:102740009E1AE89C6FD80260026E2AA22629160102
-:1027500068A0082B22000ABB0C65B25C29629DC1EF
-:102760008C6492542A21200A806099102C203CC746
-:10277000EF000F3E010B3EB1BD0FDB390BBB098FE4
-:10278000260DBD112DDC1C0D0D410EDD038E27B174
-:10279000DD0D0D410FEE0C0DBB0B2BBC1C0BB7025E
-:1027A0007EC71C2C21257BCB162D1AFC0CBA0C0DD8
-:1027B000A16000093E01073EB1780987390B770A0D
-:1027C00077EB0260020A2C2123282121B1CC0C0CCA
-:1027D0004F2C25237C8B29B0CD2D2523C855DA20FD
-:1027E000DB30581095292102CC96C0E80E9E022EAF
-:1027F0002502CC57DA20DB30DC4058111BC020D139
-:102800000F2C20668931B1CC0C0C472C24666EC687
-:10281000026001D309FD5065D1CD2F0A012E301180
-:1028200029221464E01128221B090C4400C1040071
-:10283000FA1A0A880228261B2E3010C0A0C0B094B5
-:102840001295131CE85F88302CC022088D147787FE
-:1028500004C0F10CFA38C041C0F225203CC0840805
-:1028600058010F5F010F4B3805354007BB10C0F012
-:10287000084F3808FF100FBB0228ECFEC0F0084FCD
-:1028800038842B0BA8100AFF102A21200F88020B76
-:10289000880208440218E86E8F1108440228212596
-:1028A0000A2A140828140488110A88022A21049488
-:1028B000F08B2004E41008BB1104BB02C04A04BB27
-:1028C000029BF1842A08AB110BEB0294F40A541119
-:1028D0000B44020555100D1B4094F707BB100B5518
-:1028E00002085502C08195F68433C05094F3B19428
-:1028F0008B3295F898F99BF2C080C1BC24261499BC
-:10290000FA9BF598FB853895FC843A94FD8B3B9BAC
-:10291000FE883998FF853525F6108436851324F610
-:10292000118B3784122BF612C0B064C07E893077C9
-:1029300097438D3288332E30108F111CE83109995E
-:10294000400699112CF614C0C42CF6158C2B2DF6CC
-:102950001A28F61B2BF61904A81109880208EE02A2
-:1029600019E827C18008EE0209C90229F6162EF6D9
-:1029700018C09E600001C09A2F200C18E8170CFEAA
-:1029800011A8FFA6EE2DE2852BF4CF0D9D0B2DE6B1
-:1029900085C87F8A268929A7AA9A260A990C090937
-:1029A00048292525655050C020D10F00C09A63FFEB
-:1029B000C6DA2058113F63FE38DA20C0B658113C01
-:1029C00063FE2E0068973C2B9CFD64BE24C020D182
-:1029D0000FDA20DB705810F8C0C0C0D10ADA390A0B
-:1029E000DC3865CDE063FE098A102B2104580FC442
-:1029F000C0B02B246663FE21DB402A2C745809A248
-:102A0000D2A0D10FDA20580FC963FCF76C1004C0B4
-:102A100020D10F006C1004290A801EE80E1FE80E5A
-:102A20001CE7E60C2B11ACBB2C2CFC2DB2850FCC7B
-:102A3000029ED19CD0C051C07013E80A14E8091856
-:102A4000E8072AB285A82804240A234691A986B853
-:102A5000AA2AB685A98827849F25649FD10F0000E4
-:102A60006C100AD630283010292006288CF9648290
-:102A70009B68980B2A9CF965A1B2022A02580FABF9
-:102A800089371BE7CFC89164520E2A21020A0C4CE9
-:102A900065C2588D3019E7C874D7052E212365E229
-:102AA0009E2F929E1AE7C46FF8026002532AA22654
-:102AB00068A0082C22000ACC0C65C2442A929D64AE
-:102AC000A23E9A151FE7BE8D67C1E6C8DD2B6206E0
-:102AD00018E7BC64B0052880217B8B432B200C18A1
-:102AE000E7B60CBC11A8CC29C28679EB460FBE0A0A
-:102AF0002EE2A368E0052F22007EF9372CC2859CC8
-:102B00001864C2332B212F87660B7B360B790C6F31
-:102B10009D266ED2462C203D7BC740CE5560001EC0
-:102B20002A200CC1B28C205811229A1864A2458D1B
-:102B30006763FFCFC0C063FFC5D7B063FFD300C0DA
-:102B4000E06000022E60030EDB0C6EB20EDC700C37
-:102B5000EA11AA6A2AAC20580199D7A0DA20DB70C2
-:102B6000C1C82D21205810BC8C268B279A160CBB6F
-:102B70000C7AB3348F18896399F3886298F28E6562
-:102B80009EF82D60108A189D1768D729C0D09DA97E
-:102B90002C22182B22139CAB9BAA97A58E667E73C2
-:102BA00002600097CF5860001FDA208B1658108201
-:102BB00065A13863FFBDC081C0908F18C0A29AF98B
-:102BC00099FB98FA97F563FFD2DB30DA20DC4058A6
-:102BD0001026C051D6A0C0C02BA0102CA4039B1758
-:102BE0002C1208022A02066B02DF702D60038E177A
-:102BF0009D149E100CDD11C0E0AD6D2DDC20580140
-:102C0000188C148B16ACAC2C64038A268929ABAAC9
-:102C10000A990C9A26886609094829252507880CEF
-:102C200098662F2218A7FF2F261863FE96DA20DB5E
-:102C300030DC40DD50581130D2A0D10FC0302C20F4
-:102C4000668961B1CC0C0C472C24666EC60260000C
-:102C5000D2C03009FD5065D0CA8E6764E0696470E7
-:102C600066DB608C18DF70DA202D60038E170CDDB8
-:102C7000119E10AD6D2DDC201EE7755800F923263E
-:102C800018DA208B16DC402F2213DD50B1FF2F26DF
-:102C900013580FC5D2A0D10F0028203D0848406529
-:102CA0008DE76F953EDA308DB56D990C8CA80C8C44
-:102CB00014CACF7CD32D2AAC10C090292467090DEB
-:102CC0004764DDC5600092002C1208066B022D6C73
-:102CD00020077F028E17DA209E101EE75C58007DC9
-:102CE00063FF9A00C09163FFD1000000655081DA54
-:102CF00020DB60DC40580FDCC020C0F02FA403D1E3
-:102D00000FDA20C0B658106A63FFE000006F95022A
-:102D100063FD6CDA20DB30DC40DD50C4E0580F5836
-:102D2000D2A0D10F8A152B2104580EF52324662832
-:102D30006010981763FF2100DA2058105D63FFAB25
-:102D4000C858DB30DA20580F3C2A210265AF9CC0FE
-:102D50009409A90229250263FF91DB30DC40DD5094
-:102D6000C0A32E0A802A2468DA20580F45D2A0D1A9
-:102D70000FC020D10FDA202B200C58107263FF6B8C
-:102D80006C1004282006C062288CF8658125C0508C
-:102D9000C7DF2B221BC0E12A206B29212300A104BD
-:102DA000B099292523B1AA00EC1A0BC4010A0A44E0
-:102DB0002A246B04E4390DCC030CBB012B261B64C5
-:102DC000406929200C1BE6FC0C9A110BAA082FA2C3
-:102DD000861BE6FA6FF9026000B60B9B0A2BB2A3C2
-:102DE00068B0082C22000BCC0C65C0A42BA2851D5A
-:102DF000E71E64B09B8C2B2421040DCC029CB08870
-:102E000020C0C50888110C880298B1882A0844118E
-:102E100098B48F3494B79FB5C0401EE6EF2DA285BD
-:102E20000E9E0825E4CF2DDC282DA6852921020938
-:102E3000094C68941A689820C9402A210265A00BA1
-:102E40002A221E2B221D7AB10265A079C020D10F43
-:102E50002C212365CFDE6000082E21212D21237E29
-:102E6000DBD52B221E2F221D2525027BF901C0B0A8
-:102E700064BFC413E6D02CB00728B000DA20038862
-:102E80000A28824CC0D10B8000DBA065AFE763FF4E
-:102E9000A62A2C74C0B02C0A02580E2F1CE6F49CF3
-:102EA000A08B2008BB1106BB029BA1893499A263A9
-:102EB000FF790000262468DA20DB30DC40DD505842
-:102EC000108ED2A0D10FDA202B200C580FF9C02081
-:102ED000D10F00006C1006073D14C080DC30DB40D1
-:102EE000DA20C047C02123BC3003283808084277C5
-:102EF0004001B1DD64815A1EE6AC19E6AD29E67EDB
-:102F0000D30F6DDA0500508800308CC0E0C020255A
-:102F1000A03C14E6ABB6D38FC0C0D00F87142440BA
-:102F2000220F8940941077F704C081048238C0F1E1
-:102F30000B2810C044C02204540104FD3802520181
-:102F400002FE3808DD10821C07EE100E6E020EDD48
-:102F500002242CFEC0E004FE380AEE100E88020D9A
-:102F600088028DAB1EE69B08D8020E880298B0C07E
-:102F7000E80428100E5E0184A025A125084411084C
-:102F80004402052514045511043402C0810E8E3903
-:102F900094B18FAA84109FB475660C26A11FC0F24D
-:102FA000062614600009000026A120C0F20626149F
-:102FB0000565020F770107873905E61007781008C5
-:102FC000660206550295B625A1040AE611085811B5
-:102FD00008280208660296B7C060644056649053A1
-:102FE000067E11C0F489C288C30B340B96459847FE
-:102FF000994618E6829F410459110E99021FE680F6
-:10300000020E4708D80298420E99029F40C1E00E76
-:10301000990299442FA00CB4380CF91114E66F1ED4
-:10302000E666A4FFAE992E928526F4CF0E880B2873
-:103030009685D10F2BA00C1FE6601CE6670CBE1115
-:10304000ACBBAFEE2DE28526B4CF0D3D0B2DE68552
-:10305000D10FC08005283878480263FEA263FE962F
-:103060006C1006C0C06570F18830C03008871477D6
-:103070008712C0B0C0A619E652299022C030CC9762
-:10308000C031600003C0B0C0A6C0E0C091C0D4C0D1
-:103090008225203C0B3F109712831CC070085801FA
-:1030A0000D5D01089738C0800B98380777100488A9
-:1030B00010086802087702C0800D98382D3CFE0881
-:1030C00088100D9E388D2B0AEE1008EE0207EE02D6
-:1030D0000CB8100FDD02053B400EDD029D4089203B
-:1030E000043D100899110D99022D210409A9020827
-:1030F000DD119941872A05B9100D3D020ABB110D5A
-:10310000BB02087702974428212587120828140457
-:103110008811071E4007EE100E99027566092621D8
-:103120001F062614600006002621200626140868C3
-:10313000029B47098802984629200CD2C0C0800C07
-:103140009E111BE6251FE61CAB99AFEE2DE28528EC
-:1031500094CF0DAD0B2DE685D10FDD40C0A6C0B0DC
-:103160008E51CAE0B2AAB1BB2DDC108F500E78365A
-:10317000981008770C9FD898D989538F5299119934
-:10318000DB9FDA7E8309B1CC255C10C97763FFCF62
-:1031900088108D1108E70C9751AD8DD7F078DB01C1
-:1031A000B1F79D5397528830C03008871408884083
-:1031B000648ED565BEC963FEBC0000006C1004D7E8
-:1031C00020B03A8820C0308221CAA0742B1E2972F8
-:1031D000046D080FC980C9918575B133A2527A3B3D
-:1031E0000B742B0863FFE900649FECD10FD240D130
-:1031F0000F0000006C100AD6302E3027D950DA406C
-:1032000015E5F02430269A1529160464E00264932B
-:10321000732920062A9CF865A3CE2A2102270A04D6
-:103220000A0B4C65B3978C3074C7052D212365D4E8
-:10323000A0C0A62B0A032C2200580F3664A3B9178E
-:10324000E5DE8E389A1664E3BA2F6027285021C92C
-:10325000F37E8311C2B08C202A200C580F55D7A0C2
-:10326000CDA16004A200C2B08C202A200C580F29E6
-:10327000D7A064A4862F212E8B680FBF360FB90C00
-:103280006F9D54296027D5B06E920528203D7B8F15
-:103290004CDA20DB50C1C42D211F580EEF8B269A2B
-:1032A000189A1989272AAC380B990C7A9353896399
-:1032B000C08099738F6298789F728E659E798D67B2
-:1032C0009D7B8C6695759C7A8E687E53026000B1FA
-:1032D0008B1465B050600038DBF063FFA5008A14E2
-:1032E000C9A92E60030E9B0C6EB2A5DC500CEA112E
-:1032F000AA6A2AAC285BFFB1D5A063FF93C0E06344
-:10330000FFE2DA208B18580EAC65A2B163FF9E0075
-:1033100000DA20DB308C15580E54D6A0C0C0C0D1C6
-:103320002D16042CA403DC70DA20DB60DF502D6046
-:1033300003C0E09E109D171EE5B90CDD110D6D0850
-:103340002DDC285BFF478E668F678817AF5FA8A8C4
-:1033500028640375FB01B1EE8A189E669F67892673
-:103360008829AA9909880C99268E6808084805EECC
-:103370000C28252515E5939E6865EECC63FEE600D6
-:103380000000C9432F21232B21212FFC010F0F4FB8
-:103390002F25237FBB026003142C20668961B1CCEA
-:1033A0000C0C472C24666EC60260022809FD50658D
-:1033B000D22264E1B62E602764E1B0DC70DF50DA1F
-:1033C00020DB601EE5AB2D6003C08098100CDD1182
-:1033D000AD6D2DDC285BFF22644181C0442B0A00C7
-:1033E0008C202A200C580ECB0AA70265A00FC0B073
-:1033F0002C22002A200C580EC7D7A064AFEFDA2089
-:10340000C1BCC1C82D21208F188E268929AFEE9E00
-:10341000260E990C090948292525580E8FC090C001
-:1034200050C0C288609A191EE566C0A12EE022082D
-:103430008F14778704C0810E8938C0800B93102DBC
-:10344000203C2921200CDC0104DB010929140BA8F4
-:10345000380CA5380D3D401CE57E8B2B08881007E5
-:1034600055100855020533022821250F154003BBCE
-:10347000020CBB0207551005D3100828140ADD11F1
-:103480000488110988020533022921040833029BAC
-:1034900070C0808A201BE57708AA110BAA029A71D6
-:1034A000C0A1852A9376957408931103DD020ADD85
-:1034B000029D778C63C1DC9C738B6298789A799BB0
-:1034C00072232214C0C0B1352526149C7B9D7593B0
-:1034D0007A2B621A9B7C2A621C9A7D28621D987E38
-:1034E00025621B957F2362172376102D62182D7697
-:1034F000112C62192C761264E0B98E6077E73DC01A
-:10350000FE13E53E1DE53FC1818A628B6304951180
-:103510000E9C4006CC110C5502247615085502C0AD
-:10352000802D76148D2B2B761B2A761A287619255A
-:10353000761803DD022D76166000030000C0FA2E17
-:10354000200C19E52518E51CA9E90CEE11A8EEC020
-:10355000802DE2852894CF0DFD0B2DE685DA208B9A
-:10356000198C158D14580D90D2A0D10FDC70DF503E
-:10357000DB602D6C28C0A01EE53E9A10DA205BFEB1
-:103580005563FE53002B203D0B4B4065BC826FE51D
-:1035900027DA308F556DE90C8EAA0E8E14C9E87E9D
-:1035A000F3162AAC10C090292467090F4764FC6009
-:1035B00060015F00C0FA63FF85C09163FFE8881473
-:1035C000658168DA20DB608C15580DA7C020C0909B
-:1035D00029A403D10F8A162B2104580CC9C0A02A94
-:1035E00024668E6863FDCA00002B9CF965B0FDDA85
-:1035F00020580CCE63FC220000DA20C0B6580E2CF6
-:1036000063FFBA002B200C0CBE11A7EE2DE286C181
-:10361000C27DC30260011819E4E909B90A2992A31D
-:103620006890082A220009AA0C65A10326E2856495
-:1036300060FD2C20668931B1CC0C0C472C24666FC0
-:10364000C60270960C8A162B2104580CADC0D02DE2
-:1036500024668E3077E74D1CE4E91BE4E98F32885D
-:1036600033C0A42D21040E994006991104DD1109DF
-:10367000DD029A61C19009DD029B60C0908B2B9D99
-:10368000649F66986799650CBB029B6228200C1AA0
-:10369000E4D2AA8A0C8811A7882F828529A4CF2F6B
-:1036A000FC202F86858A1465A0A6C020D10FB0FC0F
-:1036B0008B142C2523C8B7022A02066B02580CDE95
-:1036C0002A210265AEF7C0D80DAD022D250263FE9A
-:1036D000EC008E14C8E8DA20DB30580CD72A21021F
-:1036E00065AEDA07AF022F250263FED100DA20DBD8
-:1036F000308C158D14580E80D2A0D10FDA202B20DB
-:103700000C580DEB63FEB600DA202B200C580E0D82
-:1037100063FEAADA20DB308C152D12042E0A8028D5
-:103720000A00282468580CD663FAE500C020D10F9F
-:10373000DA20580DDF8914CD92DA20DB308C155851
-:103740000D4ADBA0C020C0A02AB403D10FC020D1F5
-:103750000F2A2C748B1558064CD2A0D10F000000F4
-:103760006C100E28210224160108084C6583A91F3D
-:10377000E49229F29E6F98026003AD1EE48E29E266
-:10378000266890082A220009AA0C65A39B24F29DB2
-:103790006443952A31160A4B412B240BB4BB0B0B07
-:1037A000472B240C0CB611AF66286286C1CC78C3B7
-:1037B0000260037F19E48209B90A2992A36890077D
-:1037C0008C2009CC0C65C36B276285647365293135
-:1037D00009C0D02D24668C3599139C2A88369C14F8
-:1037E000982B8E3798159E169E2C8C38C0E10C5C59
-:1037F000149C179C2D88392925042E251D28251C4D
-:103800002C3028C0822C243C2930290C0C4708C8B5
-:103810000129243D29311598189912090841089960
-:103820000C299CEC29251F7EC725921C8212282A70
-:1038300000082060991B01023E00093EB128098260
-:1038400039891B0E221102990C821C29251F821C0A
-:10385000941D951E24211F15E4880451609A10C1FF
-:10386000802B1610252014961F05054301063E00E7
-:103870000D3EB16B0DB6398B3C2D9CFC08663606AF
-:10388000441C893D2E26132E26142E26152E246B1D
-:1038900025241406D61CC05025261825261B2524B1
-:1038A000672524682832112525232525242525254B
-:1038B00025252C2925222D25202B252124252E26A2
-:1038C000252F14E46F16E46D1BE45298192D211C6A
-:1038D000C08498719B70892095759577957F967CAB
-:1038E000967E98799B7894731BE46714E4680C388F
-:1038F000400288100C064015E464016610947D9B1C
-:1039000074841D1BE444086602957B18E431851E0F
-:103910000B99029972997A0866022B121096768694
-:103920001F6FD2026001C8C0A0991A6D080AB1AA1F
-:1039300000A10400E81A7D8B0263FFEE891AC0E043
-:10394000961F1DE43E2B1610951E941D28203D2920
-:10395000761A297612C040C051C0B22D76130806DF
-:10396000408D170B8801065E380AEE101BE44A08EA
-:103970005438B0A609661188140B44102B761B042A
-:10398000EE028B1614E44308DA1406EE020D8810DA
-:103990002A761E86131AE41C04EE020D66110866D0
-:1039A000022E76160D14141EE41A0D44110BD814B1
-:1039B0000866020A44022E76182E76102476172600
-:1039C000761FC084287619287611C76F0C24400F03
-:1039D00044111CE3FB26761D26761C2676152676DA
-:1039E000148A262676242676252976222E762028E5
-:1039F00076218E1888150DB91016E4278BC70D880F
-:103A0000110E5E39ADBB851904EE022676230988B6
-:103A100002861F89102876260A04480544110505E8
-:103A2000480E551105440204EE02851E841D2E76B3
-:103A3000272820069B2D29246A2E31172B12102EA1
-:103A40002538CC83C0D02D2407C0D7090840648016
-:103A50008E9A290928416480AA64E0B42D2406C006
-:103A60009809E9362D0AA02A628501C404ADAA2D61
-:103A700021042A668508DD11883F8E3E2732100812
-:103A8000EA1800C40408E8180088110ECE5308771D
-:103A900002C08308DD029D4118E401090D4E9840E3
-:103AA00088209A4397449D4517E3FE1DE3CB058884
-:103AB0001108EE02ADBDC08007EE029E4228D4CFB1
-:103AC0002AF29D87CA2AAC18B1772AF69D1AE3B963
-:103AD00097CA28A4A268711C655060C020D10F004D
-:103AE0002D2406C080C09809E9360E893863FF731B
-:103AF000C0A063FE481BE3CB1AE3EB2AB68963FF41
-:103B0000D600000065EF54C098C0D82D240663FF8E
-:103B1000522D2406C09063FF4ACC57DA20DB308C4C
-:103B200011580C51C020D10F00DA20C0B6580CE05B
-:103B300063FFE500DA20580CDE63FFDC2A2C748B6F
-:103B400011580551D2A0D10F6C10062820068A33D7
-:103B50006F8202600161C05013E39729210216E3CE
-:103B600096699204252502D9502C20159A2814E331
-:103B7000948F2627200B0AFE0C0477092B712064F2
-:103B8000E1398E428D436FBC0260016F00E104B0E9
-:103B9000C800881A08A80808D80298272B200668A9
-:103BA000B32ECE972B221E2C221D0111027BC901A0
-:103BB000C0B064B0172CB00728B000DA2003880A20
-:103BC00028824CC0D10B8000DBA065AFE7C020D1BC
-:103BD0000F2D206464DFCA8B29C0F10BAB0C66BFCC
-:103BE000C02B200C0CBC11A6CC28C2862E0A08784B
-:103BF000EB611EE3720EBE0A2EE2A368E0052822E6
-:103C0000007E894F29C2851EE37E6490461FE38CA7
-:103C10009E90C084989128200A95930F88029892CC
-:103C20008E200FEE029E942F200788262F950A984B
-:103C3000969A972E200625240768E3432921022A15
-:103C4000C2851DE3652AAC20ADBD25D4CF2AC6852B
-:103C500063FF4E002E2065CBEDC082282465C9F697
-:103C600005E4310002002A62821BE36D2941020B48
-:103C7000AA022A668209E43129210263FF23000097
-:103C800064DFB88F422E201600F1040DEE0C00EE1A
-:103C90001AAEAE9E2963FFA38A202B3221B1AA9AC5
-:103CA000B0293221283223B4992936217989A92BC8
-:103CB00032222B362163FFA0C020D10F9F2725245D
-:103CC00015ACB82875202B2006C0C12EBCFE64E0C0
-:103CD000AB68B7772DBCFD65DEC72D2064C0F064EE
-:103CE000D0868E290EAE0C66E089C0F128205A28B5
-:103CF0008CFE08CF3865FEE863FF580000E00493AF
-:103D000010C0810AF30C038339C78F08D80308A8B1
-:103D10000108F80C080819A83303C80CA8B82875BE
-:103D200020030B472B24158310CBB700E104B0BC54
-:103D300000CC1AACAC0CDC029C27659E5EC0B20BBA
-:103D4000990209094F29250263FE50002D206A0DB2
-:103D50002D4165DF7EDA20C0B0580CA864AF18C0D2
-:103D6000F163FEEF9F2763FFD02E221F65EE3263C3
-:103D7000FF79000028221F658E2763FF6E25240629
-:103D800029210263FE1B00006C10066571332B4C69
-:103D900018C0C7293C18C0A1C08009A8380808422B
-:103DA0006481101CE3011AE3022AC67E2A5CFDD35B
-:103DB0000F6DAA0500B08800908C8940C0A00988CA
-:103DC000471FE32B080B47094C50090D5304DD1026
-:103DD000B4CC04CC100D5D029D310CBB029B30882D
-:103DE000438E2098350FEE029E328D26D850A6DDE8
-:103DF0009D268E40C0900E5E5064E0971CE3111E1D
-:103E0000E300038B0BC0F49FB19EB02D200A99B341
-:103E10000CDD029DB28F200CFF029FB48E262D2058
-:103E2000079EB68C282DB50A9CB72924072F20069B
-:103E30002B206469F339CBB61DE2E22320168DD224
-:103E40000B330C00D10400331AB48DA3C393292281
-:103E5000200C13E2E11FE2D80C2E11AFEEA32229B1
-:103E600024CF2FE285D2A00FDD0B2DE685D10F00E8
-:103E70002E200CB48C0CEB111FE2D81DE2CFAFEE5C
-:103E8000ADBB22B28529E4CF02C20B22B685D2A0F7
-:103E9000D10F00002E200C1CE2C81FE2CF0CEB114A
-:103EA000AFEEACBB22B28529E4CF02820B22B685ED
-:103EB000D2A0D10FC0D00BAD387DC80263FEEC6339
-:103EC000FEE08E40272C747BEE12DA70C0B32C3CDF
-:103ED00018DD50580A9B8940C08063FEE3066E02DD
-:103EE000022A02DB30DC40DD505800049A10DB501F
-:103EF000DA70580465881063FEF700006C100692B3
-:103F0000121EE2B98C40AE2D0C8C472E3C1804CA10
-:103F10000BD9A07DA30229ADF875C302600084C04F
-:103F2000B0C023C0A09D106D0844B89F0EB80A8D84
-:103F3000900EB70BB8770D6D36ADAA9D800D660C4F
-:103F4000D8F000808800708C879068B124B2227706
-:103F5000D3278891C0D0CB879890279C1000708879
-:103F600000F08C9D91CB6FC08108BB0375CB36638D
-:103F7000FFB4B1222EEC1863FFD485920D770C8626
-:103F8000939790A6D67D6B01B1559693959260005C
-:103F900016B3CC2D9C188810D9D078D3C729DDF85A
-:103FA00063FFC100C0238A421BE2C000CD322D4412
-:103FB000029B3092318942854379A1051EE2BC0EF5
-:103FC000550187121BE2AB897095350B9902993226
-:103FD00088420A880C98428676A6A696768F44AFC9
-:103FE000AF9F44D10F0000006C10089311D63088A9
-:103FF00030C0910863510808470598389812282165
-:1040000002293CFD08084C6581656591628A630A56
-:104010002B5065B18B0A6F142E0AFF7CA60A2C2048
-:104020005ACCC42D0A022D245A7FE0026002158961
-:104030002888261FE29F09880C65820F2E200B0F0F
-:10404000EE0B2DE0FE2EE0FF08DD110EDD021EE27C
-:1040500099AEDD1EE2991CE2990EDD010DCC37C14F
-:1040600080084837B88DB488981089601AE2557B6B
-:1040700096218B622AA0219C147BA3179D132A20D2
-:104080000C8B108C20580BCA8C148D13DBA0CEAC7B
-:104090006001C4002E200C1BE2480CEA110BAA0898
-:1040A0002BA2861FE2467BDB3B0FEF0A2FF2A368B1
-:1040B000F0052822007F892C2BA28564B0AA876294
-:1040C0008826DE700C7936097A0C6FAD1C8F279B21
-:1040D0001508FF0C77F3197E7B729D139C149B15BA
-:1040E000CF56600025C0B063FFD0D79063FFDD00DE
-:1040F000009D139C14DA20DB70580B2F8B158C1449
-:104100008D1365A06A8E6263FFCC00DA208B11DC10
-:1041100040580AD5D6A08B15C051DE70DA20DC607D
-:10412000DD405BFF768D138C14D9A02E200C1BE292
-:10413000221FE2290CEA11AFEFC0E0ABAA2BA28547
-:104140002EF4CF0B990B29A68563FF1D00DA20DC26
-:1041500060DD40DE708912282007DF50A9882824FE
-:10416000075BFF09D2A0D10F00DBE0DA20580B502B
-:104170006550EF2A20140A3A4065A0EBDB60DC4072
-:10418000DD30022A025809BCD6A064A0D584A183E0
-:10419000A00404470305479512036351C05163FE11
-:1041A0005C2C2006D30F28CCFD6480A568C704C012
-:1041B000932924062C2006C0B18D641FE2019D279F
-:1041C0009D289D298FF29D2600F10400BB1A00F066
-:1041D00004B0BE0EDD01C0F0ADBB8D652F24070D10
-:1041E0000E5E01EE11AEBB2E0AFEB0BB0B0B190E1C
-:1041F000BB36C0E20B0B470EBB372B241618E1F978
-:104200000A09450D0B422B240B29240AB4BE2E2487
-:104210000C7D88572920162FCCFDB09D0A5C520DCD
-:10422000CC362C246465FDEC0C0C4764CDE618E11B
-:10423000E48E2888820C9F0C00810400FF1AAFEEE8
-:104240009E2963FDCF1CE21163FE13001CE20B6389
-:10425000FE0C8D6563FFA500DA202B200C580B396E
-:10426000645F0FC020D10F00C020D10FC09329245C
-:1042700016C09363FFA000006C1004C06017E1CD6E
-:104280001DE1D0C3812931012A300829240A78A1EF
-:1042900008C3B27BA172D260D10FC0C16550512654
-:1042A00025022AD0202F200B290AFB2B20142E2098
-:1042B0001526241509BB010DFF0928F1202B241414
-:1042C000A8EE2EF52064A0A92B221E28221D011184
-:1042D000027B8901DB6064B0172CB00728B000DADC
-:1042E0002007880A28824CC0D10B8000DBA065AF74
-:1042F000E7DB30DC40DD50DA205800DE29210209FE
-:104300000B4CCAB2D2A0D10F00CC5A2C30087BC1C2
-:10431000372ED02064E02D022A02033B02DC40DD70
-:10432000505800D4D2A0D10F2B2014B0BB2B241492
-:104330000B0F4164F0797CB7CAC0C10C9C022C25DC
-:1043400002D2A0D10FC020D10F2E200669E2C126D3
-:1043500024062B221E2F221D29200B2820150D9903
-:10436000092A9120262415AA882895207BF14960E6
-:104370000048B0BB2B24140B0A4164A0627CB70236
-:104380002C25022B221E2C221DD30F7BC901C0B06D
-:10439000C9B62CB00728B000DA2007880A28824C5A
-:1043A000C0D10B8000DBA065AFE7C020D10F0000BB
-:1043B000262406D2A0D10F0000DB601DE18164BF7E
-:1043C0004F2CB00728B000DA2007880A28824CC09A
-:1043D000D10B8000DBA065AFE71DE17963FF310001
-:1043E00026240663FF9C00006C1004282006260A81
-:1043F000046F856364502A2920147D9724022A02C1
-:10440000DB30DC40DD50580019292102090A4CC874
-:10441000A2C020D10FC0B10B9B022B2502C020D11E
-:104420000F00022A02033B022C0A015800D1C9AA3C
-:10443000DA20DB30DC40580A0C29A011D3A07E978B
-:10444000082C0AFD0C9C012CA411C0512D2014062F
-:10445000DD022D241463FFA4DA20DB30DC40DD50C4
-:10446000C0E0580987D2A0D10F0000006C100616DA
-:10447000E1521CE152655157C0E117E14E2821027B
-:104480002D220008084C6580932B32000B695129BE
-:104490009CFD6590872A629E6EA84C2A722668A0B1
-:1044A000027AD9432A629DCBAD7CBE502B200C0CE6
-:1044B000BD11A6DD28D2862F4C0478FB160CBF0A4E
-:1044C0002FF2A368F0052822007F89072DD285D31B
-:1044D0000F65D0742A210419E17AD30F7A9B2EDA62
-:1044E00020580883600035002D21041BE1757DBB39
-:1044F00024DA20C0B658087ECA546001030B2B5042
-:104500002B240BB4BB0B0B472B240C63FFA0DA202E
-:10451000580A67600006DA20C0B6580A656550E0A0
-:10452000DC40DB302D3200022A020D6D515808D2DA
-:104530001CE123D3A064A0C8C05184A18EA00404B0
-:10454000470E0E4763FF3500002B2104C08B8931D5
-:10455000C070DF7009F950098F386EB8172C2066CB
-:10456000AECC0C0C472C24667CFB099D105808E44B
-:104570008D1027246694D11EE126B8DC9ED06550AC
-:1045800056C0D7B83AC0B1C0F00CBF380F0F42CBFD
-:10459000F119E10518E10728967EB04BD30F6DBAEB
-:1045A0000500A08800C08C2C200CC0201DE10B0C45
-:1045B000CF11A6FF2EF285ADCC27C4CF0E4E0B2E09
-:1045C000F685D10FC0800AB83878D0CD63FFC1001E
-:1045D0008E300E0E4763FEA12A2C742B0A01044D67
-:1045E000025808D72F200C12E0FC0CF911A699A252
-:1045F000FF27F4CF289285D2A008480B289685D1B2
-:104600000FC020D10F0000006C1004C060CB55DB40
-:1046100030DC40055D02022A025BFF942921020979
-:10462000084CC882D2A0D10F2B2014B0BB2B24146D
-:104630000B0C41CBC57DB7EBC0C10C9C022C2502F5
-:10464000D2A0D10F0000022A02033B02066C02C076
-:10465000D0C7F72E201428310126250228240A0F5E
-:10466000EE012E241458010E63FFA300262406D267
-:10467000A0D10F006C1006282102D62008084C6536
-:10468000809D2B200C12E0CC0CB811A2882A8286C7
-:10469000B5497A930260009719E0C909B90A2992CD
-:1046A000A36890082A620009AA0C65A08228828566
-:1046B0001CE0D46480799C80B887B14B9B819B10AF
-:1046C000655074C0A7D970280A01C0D0078D380D75
-:1046D0000D42CBDE1FE0B51EE0B62EF67ED830D3FD
-:1046E0000F6D4A0500808800908C2E3008C0A00015
-:1046F000EE322E740028600C19E0B80C8D11A2DD8A
-:10470000A988C0202CD2852284CFD2A00CBC0B2C2F
-:10471000D685D10FC0F0038F387FA0C063FFB400EF
-:10472000CC582A6C74DB30DC4058080BC020D10F09
-:10473000DA605809DF63FFE7DD402A6C74C0B0DC43
-:104740007058087F2E30088B1000EE322E7400282F
-:10475000600C19E0A10C8D11A2DDA988C0202CD21B
-:10476000852284CFD2A00CBC0B2CD685D10F0000A3
-:104770006C1004292014282006B19929241468817A
-:1047800024C0AF2C0A012B21022C24067BA004C0DC
-:10479000D02D2502022A02033B02044C02C0D0584D
-:1047A00000C0D2A0D10FC020D10F00006C1004298E
-:1047B0003101C2B429240A2A3011C28378A16C7B4A
-:1047C000A1696450472C2006C0686FC562CA572D86
-:1047D00020147CD722DA20DB30DC40DD505BFFA5E3
-:1047E000292102090E4CC8E2C020D10FC0F10F9F51
-:1047F000022F2502C020D10FDA20DB30C0C05BFFC2
-:10480000DC28201406880228241463FFC7292015F9
-:104810001BE06C2A200BC0C09C240BAA092BA120F2
-:104820002C2415AB9929A52063FF9900C020D10F36
-:10483000DA20DB30DC40DD50C0E0580891D2A0D156
-:104840000F0000006C1004CB5513E06725221F0DEC
-:10485000461106550CA32326221E25261F06440BAF
-:1048600024261E734B1DC852D240D10F280A80C087
-:104870004024261FA82828261E28261DD240D10FF6
-:10488000C020D10F244DF824261E63FFD80000005D
-:104890006C1004D620282006C0706E85026000D4FB
-:1048A0001DE04E19E04612E0442A8CFC64A1302B36
-:1048B0006102B44C0B0B4C65B0A22B600C8A600CEF
-:1048C000B8110288082E828609B90A7EC3026000E8
-:1048D0009A2992A368900509AA0C65A08E28828562
-:1048E000648088B8891BE04A94819B80655155C0DB
-:1048F000B7B8382A0A01C0C009AC380C0C4264C0F1
-:10490000421FE0291EE02B2EF67EB04AD30F6DAA7F
-:104910000500808800908CC0A029600C0C9C11A21E
-:10492000CC2BC285AD990B4B0B2BC6852860062777
-:1049300094CF6881222D6015D2A0C9D2C0E22E6426
-:1049400006D10F00C0F008AF387FB0BD63FFB100E3
-:10495000276406D2A0D10F00D2A0D10F00CC57DA25
-:1049600060DB30DC405808C0C020D10FDA60580945
-:104970005063FFE80028221E29221DD30F789901D9
-:10498000C080C1D6C1C11BE018C122AB6B6480429C
-:1049900078913F2A80000CAE0C64E0BB02AF0C643F
-:1049A000F0B52EACEC64E0AF0DAF0C64F0A92EAC0A
-:1049B000E864E0A32FACE764F09D2EACE664E097DA
-:1049C0002F800708F80BDA807B83022A8DF8D8A0A5
-:1049D00065AFBC28612308D739D97060007B00001F
-:1049E0002B600C0CB811A2882C82862A0A087CAB9A
-:1049F0007E09BA0A2AA2A368A0052C62007AC96FB0
-:104A00002A828564A0691FDFFE276504C0E3C0C455
-:104A10002E64069CA11CE02B9FA02E600A97A30C7D
-:104A2000EE029EA28F600CFF029FA42E60147AEF0C
-:104A30004627A417ADBC2F828527C4CF2FFC202F7B
-:104A4000868563FE692A6C74C0B1DC90DD4058072E
-:104A5000BC1DDFE163FEC100D9A0DA60DB30C2D04B
-:104A6000C1E0DC4009DE39DD50580805D2A0D10F85
-:104A7000DA6058090F63FEE4290A0129A4170DBF63
-:104A8000082E828527F4CF2EEC202E868564500BCD
-:104A90002A6C74DB4058017CD2A0D10FC020D10F0A
-:104AA0006C10062B221E28221D93107B8901C0B09A
-:104AB000C0C9C03BC1F20406401DDFCBC0E2C074D8
-:104AC0000747010E4E01AD2D9E11C0402E0A146401
-:104AD000B06E6D084428221D7B81652AB0007EA13E
-:104AE0003B7FA1477B51207CA14968A91768AA1484
-:104AF00073A111C09F79A10CC18B78A107C1AE2908
-:104B00000A1E29B4007CA12B2AB0070BAB0BDAB02C
-:104B10007DB3022ABDF8DBA0CAA563FFB428B0109C
-:104B200089116987BB649FB863FFDC00647FB4634D
-:104B3000FFD50000646FD0C041C1AE2AB40063FF4E
-:104B4000C62B2102CEBE2A221D2B221E7AB12A8C10
-:104B5000107CB1217AB901C0B0C9B913DF96DA204F
-:104B600028B0002CB00703880A28824CC0D10B80E3
-:104B700000DBA065AFE7D240D10F8910659FD463F9
-:104B8000FFF300006C1008C0D0C8598C30292102F6
-:104B90000C0C4760000C8E300E1E5065E19E2921E2
-:104BA00002C0C116DF85090B4C65B0908A300A6ED1
-:104BB0005168E3026000852F629E1BDF7E6EF85312
-:104BC0002BB22668B0052E22007BE94727629DB7ED
-:104BD00048CB7F97102B200CB04E0CBF11A6FF299D
-:104BE000F2869E12798B4117DF7507B70A2772A3E9
-:104BF000687004882077893029F285DF90D7906526
-:104C000090652A210419DFAE7A9B22DA205806B873
-:104C1000600029002C21041BDFAA7CBB18DA20C00D
-:104C2000B65806B3C95860014CC09063FFCCDA2077
-:104C300058089F600006DA20C0B658089D655135B7
-:104C4000DC40DB308D30DA200D6D5158070BC0D0C1
-:104C5000D3A064A120292102C05184A18CA0040406
-:104C6000470C0C4763FF3E00C09B8831DBD008F83F
-:104C700050089B3828210498116E8823282066ACA0
-:104C80008C0C0C472C24667CBB159F139E148A1039
-:104C90008B1158071B8E148F13C0D02D24668A30B9
-:104CA000C092C1C81BDF5B7FA6099BF099F12CF471
-:104CB0000827FC106550A4B83ADF70C051C08007C7
-:104CC000583808084264806718DF3819DF392986A8
-:104CD0007E6A420AD30F6DE90500A08800F08CC0FF
-:104CE000A08930B4E37F9628C0F207E90B2C940822
-:104CF0009B909F912F200C12DF380CF811A6882969
-:104D00008285A2FF2DF4CFD2A009330B238685D153
-:104D10000F22200C891218DF300C2B11A6BBA82201
-:104D20002D24CF2CB285D2A00C990B29B685D10F9A
-:104D3000C087C0900A593879809663FF8ADB30DAE1
-:104D400020C0C1C0D05BFF56292102C0D02A9CFEE2
-:104D500065AE4D2D2502C09063FE45009E142A2CA1
-:104D600074C0B1DC70DD405806F68E14C0D01BDF75
-:104D700028C1C863FF6AC020D10F00006C1006284C
-:104D8000210217DF0D08084C65824929729E6F9831
-:104D90000260025019DF082A922668A0078B200AB9
-:104DA000BB0C65B23F2A729DC0CB64A2371DDF04E5
-:104DB000C0602B3008C0F164B0712E0AFFB0B86437
-:104DC00081512DBCFE64D0F364505C2A2C74044BDA
-:104DD000025800AD0AA2020600000000001ADF0817
-:104DE0002C20076EBB0260022218DEFE13DF081BB8
-:104DF000DF36C0E229200A9AD09ED1ABCB039902BC
-:104E000099D223B08026B480B13308330293D318EB
-:104E1000DEF20CFD11A7DD2CD285A8F82684CF0C7C
-:104E2000EC0B2CD685655FA2C020D10F2B21048806
-:104E300031DE6008F85008CE386EB8102C2066B10C
-:104E4000CC0C0C472C24667CEB026001AF2E30109A
-:104E50002930112C301300993200CB3264E1452AFD
-:104E600030141EDF1A00AA3278CF050E9C092BC41D
-:104E70007F1CDF1766A0050E98092A8480B4A71846
-:104E8000DF15C76F009104AC9CDBC000AE1A00F3C5
-:104E90001A6EC1048BD00BCB0C1CDF0F08B81C069C
-:104EA0003303AC882A848B2CD03627848C03CC0126
-:104EB0000ECC022CD4365801AD63FF0B2F200C0C06
-:104EC000FB11A7BB2DB286C0987D9302600121190A
-:104ED000DEBB09F90A2992A36890082D220009DD9A
-:104EE0000C65D10C2DB285DE6064D10488312B2194
-:104EF0000408F85008CE386FB80263FEDF2C206635
-:104F0000B1CC0C0C472C24667CE30263FECE9D10D2
-:104F100060013100293108292504283014B0886443
-:104F200080A62B31092B240AC0812B30162FD423C5
-:104F30002B240BB4BC2C240C8D378B36292504DE96
-:104F4000D00D8E39DCB00B8C390ECC0264CE7808D3
-:104F50009C1101C4048F380DBE1800C4040DB8188C
-:104F600000881108FF02C08308CC0218DECC9CA187
-:104F700098A018DECB8C209EA39FA405CC110BCF4C
-:104F800053C1E09EA50CFF0208FF029FA218DE8914
-:104F90002624662C729D2684A22CCC182C769D6328
-:104FA000FE250000002D30121CDECD00DA3278DF45
-:104FB000050C9E0B2AE47F66B0050C9F0B2BF4803A
-:104FC0002A301100AA3263FEEC2E240A2B31099BF1
-:104FD0002B63FF5300CC57DA20DB30DC405807222C
-:104FE000C020D10F00DA20C0B65807B163FFE5003A
-:104FF00000DBF0DA205807AE63FFD9000058064006
-:105000001DDE70C0F126246663FE41008B20280A55
-:10501000FFB1CE23200A2C21040E0E472E24077840
-:1050200031359AD02CD50A96D319DEA62ED416C0C7
-:105030008398D1C0E309B80298D409390299D226DD
-:10504000240763FDC958062E8D102624662B2104E3
-:105050002F200C63FD86000008B81119DE6808EEE9
-:1050600002882B9ED59AD0C0EF09880298D204C935
-:10507000110E990299D4C0E49ED163FFC1000000D3
-:105080006C1004C020D10F006C100485210D381164
-:1050900014DE478622A42408660C962205330B935F
-:1050A00021743B13C862D230D10FC030BC29992182
-:1050B00099209322D230D10F233DF8932163FFE34F
-:1050C0006C100AD620941817DE3CD930B8389819DD
-:1050D0009914655256C0E1D2E02E61021DDE390EF0
-:1050E0000E4C65E1628F308E190F6F512FFCFD65FC
-:1050F000F1558EE129D0230E8F5077E66B8F181E65
-:10510000DE78B0FF0FF4110F1F146590CE18DE7516
-:105110008C60A8CCC0B119DE2728600B09CC0B0D20
-:10512000880929812028811E2A0A0009880C08BACA
-:10513000381BDE6B0CA90A2992947B9B0260008CC1
-:105140002B600C94160CBD11A7DD29D286B84879C6
-:1051500083026000D219DE1909B80A2882A39817C1
-:105160006880026000A36000A51ADE5F84180AEE62
-:1051700001CA981BDE108C192BB0008CC06EB313C3
-:105180001DDE0D0C1C520DCC0B2DC295C0A17EDB7B
-:10519000AE6000380C0C5360000900000018DE51AE
-:1051A0008C60A8CCC0B119DE0328600B09CC0B0DB4
-:1051B000880929812028811E2A0A0009880C08BA3A
-:1051C000380CA90A2992947E930263FF72DA60C0B8
-:1051D000BA58073764507360026A00001ADDF68C13
-:1051E000192AA0008CC06EA31A18DDF20C1C5208FC
-:1051F000CC0B18DE3B2BC295C0A178B30263FF3FF6
-:1052000063FFC9000C0C5363FF0989607899182962
-:10521000D285C9922B729E1DDDE76EB8232DD22652
-:10522000991369D00B60000DDA60580721600017F0
-:105230000088607D890A9A1A29729D9C129915CF5F
-:1052400095DA60C0B658071A6551F98D148C18DBD1
-:10525000D08DD0066A020D6D51580587D3A09A14DF
-:1052600064A1E182A085A1B8AF9F1905054702029C
-:10527000479518C05163FE602B6104C08B8931C013
-:10528000A009F950098A386EB81F2C6066A2CC0CB0
-:105290000C472C64667CAB119F119E1B8A15580528
-:1052A000988E1B8F11C0A02A64669F1164F0E58957
-:1052B0001388190FFD022E0A006DD9172F810300E4
-:1052C000908DAEFE0080889F9200908C008088B800
-:1052D0009900908C65514E8A10851A8B301FDDC85D
-:1052E000881229600708580A2C82942D61040ECC7C
-:1052F0000C2C86946FDB3C1CDDF4AC9C29C0800B2D
-:105300005D50A29909094729C48065D0DA2E600C46
-:10531000C0D01FDDB10CE811AFEEA7882282852D29
-:10532000E4CF02420B228685D2A0D10F8E300E0E22
-:105330004763FDA2A29C0C0C472C64077AB6CD8B68
-:10534000602E600A280AFF08E80C64810E18DDDD73
-:1053500083168213B33902330B2C34162D350AC051
-:105360002392319F30C020923308B20208E80292A3
-:10537000349832C0802864072B600CD2A01CDD96C4
-:105380000CBE11A7EE2DE285ACBB28B4CF0D9D0B52
-:105390002DE685D10F8B1888138D30B88C0D8F4773
-:1053A0000D4950B4990499100D0D5F04DD1009FFEB
-:1053B000029F800DBB029B8165508D851AB83AC053
-:1053C000F1C0800CF83808084264806B1BDD771947
-:1053D000DD7829B67E8D18B0DD6DDA0500A0880075
-:1053E000C08CC0A063FEF30082138B161DDD8828DD
-:1053F000600AC0E02EC4800D880202B20B99239F80
-:1054000020C0D298229D2122600CB2BB0C2D11A786
-:10541000DD28D28508BB0B18DD702BD685A8222E7F
-:1054200024CFD2A0D10F9E1B851A2A6C748B185BD7
-:10543000FF168E1B63FEA300C087C0900AF938795F
-:10544000809263FF86C020D10F9E1B2A6C74C0B16E
-:105450008D1858053B8E1B851A63FE7E886B821360
-:10546000891608BE110ECE0202920B9E25B4991E1B
-:10547000DD639F200E88029822C0EF04D8110E88A9
-:10548000029824C0E49E21C080D2A02B600C286426
-:10549000071CDD510CBE11A7EE2DE285ACBB28B474
-:1054A000CF0D9D0B2DE685D10F0000006C1004C0C0
-:1054B00020D10F006C10048633C071C03060000131
-:1054C000B13300310400741A0462017460F1D10F29
-:1054D0006C1004022A02033B025BFFF61CDD391B41
-:1054E000DD83C79F88B009A903098A019AB0798032
-:1054F0001EC0F00FE4311DDD300002002BD2821EF1
-:10550000DD7C2AC1020EBB022BD6820AE431D10F08
-:1055100028C102C19009880208084F28C50208E482
-:1055200031D10F006C1004C0C00CE43112DD251A1B
-:10553000DD2200020029A28218DD701BDD6E26210B
-:10554000020B990108660129A68226250206E4318C
-:1055500014DD6B15DD66236A9023261685502426FC
-:1055600015252617222C50D10F0000006C1008D6EC
-:10557000102B0A64291AB41ADD0F0D23111CDD103B
-:105580000F2511B81898130E551118DD5DAC55A8EC
-:1055900038AA332C80FF2A80FEA933288D01298068
-:1055A0000108AA112880000CAA02088811098802A3
-:1055B00008AA1C288C0828160458086814DD010A5B
-:1055C000A70224411A2A30802B120407AA2858085F
-:1055D00063B1338B13B4559A6004AC28B4662C566F
-:1055E0002B7B69E016DD3A9412C050C0D017DCF472
-:1055F0009D15D370D4102F60802E60829F169E1749
-:10560000881672891A8D128C402A607F0DCC282B47
-:105610003A200CAA28580851C0B10ABE372E354886
-:105620008F1772F91A8D128C402A60810DCC282BAD
-:105630003A200CAA28580849C0B10ABE372E354A6C
-:10564000B233B444B1556952B6B466C0508F15B880
-:1056500077D370B2FF9F156EF899D10F6C1004C00C
-:1056600021D10F006C1004270A001CDCD31FDCE4DE
-:105670001EDCE71DDCD01ADD141BDD22C02824B09F
-:10568000006D2A75AA48288080C09164806100411D
-:105690000415DCCBC03125503600361A06550105FD
-:1056A00095390C56110C66082962966E974D0D5966
-:1056B0000A29922468900812DD0602420872993B7A
-:1056C00023629512DCC8CB349F300282020E440262
-:1056D000C092993194329233AD52246295C0902495
-:1056E0004C1024669524B0002924A0AA42292480C5
-:1056F000B177B14404044224B400D10FD10FD10FCB
-:105700006C10041ADCAC2AA00058021C5BFFD50206
-:105710002A02033B025BFFD11BDCAAC9A12CB10208
-:10572000C0D40DCC020C0C4F2CB5020CE431D10FBF
-:10573000C0A00AE43118DCA00002002F828219DC2C
-:10574000B32EB10209FF022F86820EE431D10F0081
-:105750006C1004C02002E43114DC9A16DC970002BD
-:1057600000226282234102732F0603E431C020D15C
-:105770000F19DCE61ADCE52841020A2A0109880132
-:105780002A668228450208E43115DCDC12DCE125BA
-:105790004621D10F6C1004292006289CF96480A0B2
-:1057A0002A9CFD65A0968A288D262F0A087AD9049E
-:1057B0002B221FC8BD2C206464C0812E22090EAE8E
-:1057C0000C66E0782B200C1EDC7C0CBC11AECC28C7
-:1057D000C28619DC7A78F3026000AD09B90A299211
-:1057E000A36890082E220009EE0C65E09B29C28573
-:1057F0001FDC846490929F90C0E41FDC919E9128EE
-:10580000200AC0E09E930F8802989288200F880299
-:1058100098942F20079A979D962F950A2E24072853
-:10582000200629206468833328C28512DC6B288C0B
-:1058300020A2B22E24CF28C685C020D10FC020D1EF
-:105840000F2A206A0111020A2A4165AF52DA20C0EC
-:10585000B05805EA64AFE5C021D10F00649FC81FAE
-:10586000DC582D20168FF209DD0C00F10400DD1A42
-:10587000ADAD9D2912DC5928C285A2B22E24CF28B5
-:105880008C2028C685C020D10FC021D10F00000078
-:105890006C1004260A001BDC9F15DC4928206517C4
-:1058A000DC46288CFE6480940C4D110DBD082CD272
-:1058B000F52BD2F42ED2F77CB13DB4BB2BD6F47BC2
-:1058C000E9052BD2F62BD6F47CB92C2AD2F62AD6AF
-:1058D000F52AD6F406E4310002002872822AFAFF83
-:1058E000004104290A012F510200991A0A9903095B
-:1058F00088012876820FE4312624652BD2F48E5C51
-:105900002CD2F5B0EE9E5C7BCB1629D2F62FD2F7C7
-:105910000CB80C09FF0C08FF0C0F2F14C8F960001D
-:10592000320BCA0C0A2A14CEA92B5102C0C20CBBDE
-:10593000020B0B4F2B55020BE431D10F00DB30DA99
-:10594000205BFF941BDC7464AF5D0C4D11ADBD6337
-:10595000FFA8000006E4310002002F728218DC303C
-:105960002E510208FF022F76820EE431D10F000083
-:105970006C1004C03003E43116DC1015DC11000299
-:105980000024628274472118DC64875C084801287F
-:105990006682CD7319DC620C2A11AA99229283299E
-:1059A00092847291038220CC292B51020BE431C0E6
-:1059B00020D10F001FDC5B2E51020FEE012E55028D
-:1059C0000EE431B02DB17C9C5C12DC5608DD112D4B
-:1059D000561DD10F6C10061BDBF71EDBF922B00041
-:1059E0001ADC526F23721DDC39C04818DC511FDCF1
-:1059F0004FDC10D5C083F000808600508A6D4A4F7E
-:105A00000F35110D34092440800B560A296294B1D8
-:105A1000330E55092251480F44110C440A8740099E
-:105A2000A80C02883622514907883608770CA899B5
-:105A30002966949740296295874109A80C02883607
-:105A400007883608770CA899296695974103034281
-:105A5000B13808084298F0D10F1CDC3613DC372728
-:105A6000B0002332B5647057C091C0D016DC351534
-:105A7000DC33C0402AC00003884328C4006D793C51
-:105A8000004104B14400971A7780148E502FB295CC
-:105A90002DB695AFEE2EED2006EE369E5060001826
-:105AA00077A00983509D5023B69560000223B295DC
-:105AB000223D2006223622B695B455B8BBD10F0040
-:105AC00003884328C400D10F6C1004C04004E431A3
-:105AD00015DC1D000200885013DC1CCB815BFFBD70
-:105AE0001CDC1B0C2D11ADCC2BC2822AC28394501E
-:105AF0007BAB142EC28429C2850ABD0C0E990C0DF5
-:105B0000990C0929146000050BA90C092914993076
-:105B100015DBAC2A51020AE4312A2CFC58004B2B2D
-:105B200032000AA2022BBCFF9B30CCB6C8A4D2A084
-:105B3000D10F000004E4311EDBA00002002DE28240
-:105B40002FBAFF2C51020FDD012DE6820CE431D17A
-:105B50000F0000006C1004D10F0000006C1004C096
-:105B600020D10F006C100413DBFAC0D103230923EA
-:105B7000318FC0A06F340260008D19DB8F1BDB906A
-:105B800017DBF30C2811A8772672832572822CFA72
-:105B9000FF76514788502E7285255C0425768275E4
-:105BA000E9052572842576827659292E72842E760F
-:105BB000822E76830AE431000200239282002104BF
-:105BC0002FB10200D61A0C66030633012396820F0A
-:105BD000E43126728325728260000200D8A07659D3
-:105BE000220AE43100020023928200210400D21A2A
-:105BF0002FB1020C22030232012296820FE431D22D
-:105C000080D10F00D280D10FC020D10F6C1004DBE7
-:105C100030862015DB68280A00282502DA2028B003
-:105C2000002CB00705880A28824C2D0A010B800041
-:105C3000DBA065AFE61ADB610A4A0A29A2A3C7BF47
-:105C4000769101D10F2BA6A3D10F00006C1004C0D8
-:105C5000D1C7CF1BDB5B19DB5817DB560C2811A80B
-:105C60007786758574C0A076516288508E77B4555A
-:105C7000957475E903857695747659278F769F75A7
-:105C80009F740AE431000200239282B42E2FB102E5
-:105C900000E10400D61A0C66030633012396820F36
-:105CA000E431867583747639280AE4310002002EC7
-:105CB0009282B42200210424B10200DF1A0CFF03F7
-:105CC0000FEE012E968204E431D280D10FD8A07657
-:105CD00051D6D280D10F00006C1004290A801EDB3F
-:105CE0005D1FDB5D1CDB350C2B11ACBB2C2CFC2DA4
-:105CF000B2850FCC029ED19CD0C051C07013DB592D
-:105D000014DB5818DB562AB285A82804240A234637
-:105D100091A986B8AA2AB685A98827849F25649F59
-:105D2000D10F00006C100419DB8B0C2A11A9A98972
-:105D300090C484798B761BDB79ABAC2AC2832CC2EE
-:105D4000847AC1688AA02BBC30D3A064A05E0B2BE0
-:105D50000A2CB2A319DB4268C0071DDB7FD30F7D7D
-:105D6000C94AA929299D0129901F68913270A6036B
-:105D7000D3A0CA9E689210C7AF2AB6A32A2CFC5B98
-:105D8000FFB3D230D10F000013DB7503A3018C31B8
-:105D90001DDB130C8C140DCC012CB6A363FFDC00AF
-:105DA000C020D10FDA205BFFCCC020D10FC020D1A2
-:105DB0000F0000006C1004DB30C0D019DAFEDA20CE
-:105DC00028300022300708481209880A28824CDC53
-:105DD000200B80001BDAF90C4A11ABAA29A2840916
-:105DE000290B29A684D10F006C1004C04118DAF2E7
-:105DF00017DAF40C2611A727277038A866256286C3
-:105E0000007104A35500441A75414822628415DBD1
-:105E10001502320BC922882117DAF10884140744CD
-:105E200001754905C834C020D10FD10F0809471D9D
-:105E3000DB4AC0B28E201FDADF0E0E43AFEC2BC45C
-:105E4000A00FEE0A2DE6242A6284C0200A990B29AD
-:105E50006684D10FC020D10F6C1004DB30C0D01885
-:105E6000DAD5DA2025300022300708580A28824C7B
-:105E7000DC200B80008931709E121BDACF0C4A1196
-:105E8000ABAA29A28409290B29A684D10F09C952DA
-:105E900068532600910418DACAC0A12F811600AAFF
-:105EA0001A0AFF022F85161EDAC40C4D11AEDD2C26
-:105EB000D2840C2C0B2CD684D10FC0811FDAC1B830
-:105EC0009A0A0A472EF11600A10400881A08EE0269
-:105ED0002EF5161DDAB90C4C11ADCC2BC2840B2B50
-:105EE0000B2BC684D10F00006C1004DB30C0D0191E
-:105EF000DAB1DA2028300022300709880A28824CDB
-:105F0000DC200B80001CDAAC0C4B11ACBB2AB28439
-:105F10000A2A0B2AB684D10F6C1004C04118DAA6E5
-:105F200016DAA80C2711A626266038A87225228624
-:105F3000006104A35500441A7541082222840232EC
-:105F40000BD10F00C020D10F6C100415DB050249E6
-:105F5000142956112452120208430F8811C07300ED
-:105F6000810400361A008104C78F00771A0877036E
-:105F7000074401064402245612D10F006C10066E2D
-:105F800023026000AC6420A7C0A0851013DADD16E0
-:105F9000DAF4C040A6AA2BA2AE0B19416490666841
-:105FA000915D68925268933C2AA2AA283C7F288C73
-:105FB0007F0A0A4D2980012880002AACF208881146
-:105FC0000988027589462B3D0129B0002BB00108D4
-:105FD00099110B99027A9934B8332A2A00B1447284
-:105FE00049B160004A7FBF0715DADF63FFB90000DF
-:105FF000253AE863FFB10000253AE863FFA90000F5
-:10600000250A6463FFA1C05A63FF9C0000705F080B
-:106010002534FF058C142C34FE70AF0B0A8D142E22
-:106020003D012AE4012DE400DA405BFD5063FFA747
-:10603000D10FD10F6C10041ADA6219DA5F1CDACAB8
-:106040001BDACBC080C07160000D00000022A438B4
-:10605000B1AA299C107B915F26928679C2156E6247
-:1060600062C0206D080AB12200210400741A764B28
-:10607000DB63FFEE2292850D6311032514645FCF6D
-:10608000D650032D436DD9039820B4220644146DD5
-:106090004922982098219822982398249825982678
-:1060A000982798289829982A982B982C982D982EDC
-:1060B000982F222C4063FF971EDA4027E68027E6C0
-:1060C00081D10F00C02063FF830000006C1004C06A
-:1060D00062C04112DA3B1ADA3713DA522AA00023DF
-:1060E000322D19DA9F2BACFE2992AE6EA30260000E
-:1060F0008E090E402D1AC2C2CD0EDC392C251A6431
-:10610000B0895BFF9E15DA9A1ADA952B3AE80A3ABB
-:10611000015805922B211A0ABB28D3A09B50580581
-:10612000A92B52000ABB082A0A005805A815DA91C3
-:106130002D21022C3AE80C3C2804DD022D25029C7E
-:10614000505805A08B50AABBC0A15805A01CDA8AE4
-:106150002D21020C3C2806DD0213DA882D25029C35
-:10616000305805988B30AABBC0A25805982A210246
-:10617000C0B40BAA020A0A4F2A25025805ACD10F57
-:10618000242423C3CC2C251A63FF760018DA801C44
-:10619000DA7C19DA7D1BDA7B17DA4F85202E0AFDAF
-:1061A0001FDA7C2D203624F47A24F47E24F4820E27
-:1061B000DD0124F4862E0AF707552806DD02C07596
-:1061C0000EDD01050506AB5BA959C0E8AC5C24C433
-:1061D000AB0EDD0227C4AC2E0ADFA85527B4EC0EA7
-:1061E000DD0124B4EBC2E027942C0EDD0224942BB5
-:1061F0002E0A800D0D4627546C24546B0EDD022DA3
-:10620000243663FEFC0000006C10042A0A302B0ABE
-:10621000035BFF4D12DA53C390292616C3A1C0B306
-:10622000C08A2826175BFF48C03CC3B12B26161A2C
-:10623000D9E42AA02023261764A079C3A2C0B15BA9
-:10624000FF42C3A2C0B15BFF40C3C22C2616C2AF3F
-:10625000C0B12326175BFF3CC28F282616C0FE2F35
-:106260002617C2E22E26162A0AA1C0B1C0D82D26B2
-:10627000175BFF352A0AA12A2616C3A6C0B3C1920E
-:106280002926175BFF31C3C62C2616C1B32A0AA2E2
-:106290002B2617C0B35BFF2C290AA2292616C1851D
-:1062A000282617C2FB2F2616C0E72E26171DDA391F
-:1062B0002D2610D10FC3A2C0B35BFF2363FF820062
-:1062C0006C10041CDA031BD9ED18DA3317DA341614
-:1062D000DA3415DA34C0E0C0D414D9FF1FD9B9C0FC
-:1062E000288FF06D2A36DAC0D9C07C5B020FC90C4A
-:1062F0001CD9F90C9C28A8C3A6C22A36802A25845A
-:10630000A4C2A7CC2D248C2B248A2B24872E248B4B
-:10631000B1BB2E369F2C369E2C369DB1AC1CD9D7E6
-:106320001BDA22C0286D2A33DAC0D9C07C5B020F89
-:10633000C90C1CD9E80C9C28A8C3A6C22A36802BFD
-:106340002584A4C2B1BBA7CC2D248C2E248B2A2457
-:106350008A2E369F2C369E2C369DB1ACC07919D929
-:10636000D81BDA1413DA121ADA1218DA1314D9D97C
-:1063700016DA1304F42812DA1204660C040506A2D5
-:1063800052A858AA5AA3539B3029A50027848AC033
-:1063900091C0A52A848C29848B17DA0B18DA0AA7F6
-:1063A0005726361D26361E2E361F16DA0813DA0833
-:1063B000A65504330C2826C82E75002D54AC2E5437
-:1063C000AB2E54AA2326E62326E52E26E7D10F007E
-:1063D0006C100613D99417D9E224723D2232937FB0
-:1063E0002F0B6D08052832937F8F0263FFF3C0C423
-:1063F000C0B01AD973C051D94004593929A4206EAC
-:1064000044020BB502C3281ED96EDDB025E4220577
-:106410002D392DE421C0501ED9EF19D9DF18D9DF4D
-:1064200016D9E11DD9ED94102A724517D9AB6DA983
-:106430004BD450B3557A5B17DF50756B071FD9608B
-:106440008FF00F5F0C12D9A302F228AE2222D68160
-:10645000D54013D9A0746B0715D95A855005450C42
-:10646000035328B145A73FA832A93322369D2236CF
-:106470009E2436802B369F2BF48B2CF48C14D969F8
-:1064800024424DC030041414C84C6D0806B13304C6
-:106490001414C84263FFF20015D947C44000310408
-:1064A0001AD948C0D193A200DD1AC138B0DD9DA32E
-:1064B00018D95D2B824D29824E29A5202882537A36
-:1064C000871E2C54008E106FE45D12D93D2F2121C0
-:1064D0002321202F251F04330C23252023251ED103
-:1064E0000FC06218D99F88807E87D98910265400F2
-:1064F0006F94191BD9332AB1200A1A1404AA0C2A42
-:10650000B5202AB5212AB51E2AB51FD10F1BD92CBB
-:106510002AB1200A1A1403AA0C2AB5202AB5212A66
-:10652000B51E2AB51FD10F001CD9262BC1212DC1A4
-:10653000202BC51F03DD0C2DC5202DC51ED10F003E
-:106540006C100619D91F14D98612D93615D9A3C7CC
-:106550003FC0E02E56A82E56A92E56AA2E56AB2383
-:10656000262918D946DB101CD99DC0D42A42452DB6
-:1065700016012C160000B0890A880C98905BFF94D5
-:106580002C22E318D90F0C5C149C842B22E48C84FD
-:10659000B1BB0B5B140CBB0C9B852A22E50A5A1479
-:1065A0002A86062922CD0959142986072F22892FE8
-:1065B00086095BFF435BFF1423463BC1B01ED90035
-:1065C0001DD9602AE1022D463A0BAA020A0A4F2A77
-:1065D000E5025804965BFEBD5BFE96C050C0B01647
-:1065E000D8F614D8FE17D96FC0C0C73E93122C2618
-:1065F0002DC0306000440000007F9F0FB155091985
-:1066000014659FF4C0500AA9027FA7EF18D8EADAF0
-:106610005008580A28822C2B0A000B8000005104D5
-:10662000D2A0C091C7AF00991A0A99039912CE3827
-:1066300064206BD3202B20072516032C12022A621C
-:10664000827CA86318D8DC01110208580A28822C21
-:10665000DA500B8000D2A0643FD58A310A8A140434
-:10666000AA01C82A2B22010B8B1404BB017BA9456C
-:10667000DDA07A7B081DD8D22DD2000DAD0CDB3009
-:1066800019D8CD1AD91488130ADA28DC801DD951FB
-:1066900009880A28823C0DAA080B8000652F93D335
-:1066A00020C0B063FF9400007FAF34B155005004A8
-:1066B0000A091963FF42DAB07B7B081AD8C12AA203
-:1066C000000ABA0C1BD9048C310BAB280C8A141CA1
-:1066D000D941ACBB1CD94104AA012BC68163FF8FF1
-:1066E000645F60C050C0B0C7CE9C1263FF5500000D
-:1066F0006C100427221EC08008E4311BD8AF0002B2
-:10670000002AB28219D8AF003104C06100661A298C
-:1067100091020A6A022AB68209E43115D90C0C38B2
-:1067200011A8532832822432842A8CFC7841102903
-:1067300021022A368297A0096902292502D10F0079
-:106740002B21022C32850B6B022CCCFC2C36829731
-:10675000C02B2502D10F00006C1004C0E71DD89299
-:106760001CD8940D4911D7208B228A200B4B0BD2B9
-:10677000A007A80C9B72288CF4C8346F8E026000AE
-:10678000A31FD88AA298AF7B78B334C93DC081C01B
-:10679000F0028F380F0F42C9FA2CD67ED5206D4AF1
-:1067A0000500308800508C887008980878B16DD248
-:1067B000A09870D10FC0F0038F387FE0DE63FFD860
-:1067C000027B0CAFBB0B990C643047D830C0F1C0D2
-:1067D0005002F5380505426450792CD67E0B3612EE
-:1067E0002F6C100F4F366DFA0500808800208C0644
-:1067F000440CC081C05003B208237C0C03853805CB
-:10680000054264505A2CD67ED30F6D4A050020886D
-:1068100000308CD2A0A798BC889870D10FD2A0BCB1
-:10682000799970D10FD2302BAD08C0F1C0500BF563
-:1068300038050542CB542CD67E083F14260A100F8B
-:10684000660C0646366D6A0500208800B08C8270A2
-:1068500063FF2D00C05003F53875E08063FF7A00B8
-:10686000C06002863876E09F63FF9900C05003F550
-:106870003875E0C463FFBE006C1004D62068520F68
-:10688000695324DA20DB30DC405800F7D2A0D10F66
-:10689000DA20DB30DC405800F49A2424240EC02196
-:1068A00022640FC020D10F00B83BB04C2A2C748951
-:1068B000242D200E2E200FA4DDB1EE2E240FB0DDEE
-:1068C0002D240E2890072D9003A488B088B1DD2DCB
-:1068D00094032894075BFFA069511DC0E082242A1D
-:1068E000600F18D8BF2A240329600E8F202924079F
-:1068F00008FF029F209E64D10FC020D10F0000002E
-:106900006C1004942319D8B7C0B3083A110BAA022B
-:10691000992019D8299A2116D827C05028929D2548
-:1069200064A2288C1828969DD10F00006C100428B2
-:106930002066C038232406B788282466D10F0000BB
-:106940006C10060D3C111AD819D820035B0C862256
-:106950000D55118221AA8902320B928105630C9395
-:10696000820C550C792B54CB531CD8111DD80FC059
-:10697000F7A256C031C0A0043A380A0A42769343BF
-:10698000044302C9AB2CD67ED30F6DBA0500208814
-:1069900000308C8281A25272917D92818382C83EA6
-:1069A000D10FC071C06002763876F0DB63FFD5008E
-:1069B000C020BC89998199809282D10F222DF892B2
-:1069C0008163FFA219D7FA02860CA9669611D940F5
-:1069D000063612961006BB0C64A0442CD67E8A1094
-:1069E000D30F6DAA0500208800908CBC828311C053
-:1069F000E0A433240A01034E380E0E42CAEC2CD612
-:106A00007E6DBA0500208800308C821102520CA2E3
-:106A100082BC22928163FF83BC82928163FF7C00EF
-:106A2000C06002363876F0B563FFAF00C070024731
-:106A30003877F0CC63FFC6006C100414D7EBC1525A
-:106A4000A424CA3128221D73811C292102659016B5
-:106A50002A300075A912022A02033B022C3007C01B
-:106A6000D25801D5653FDCD10F2B300703BB0B0B90
-:106A7000BA0274B3022ABDF8D3A063FFC4000000B9
-:106A80006C1004292006C0706E9741292102C08F26
-:106A90002A2014C0B62B240606AA022A24147980C0
-:106AA000022725022A221E2C221D7AC10EC8ABDA2B
-:106AB00020DB302C0A00033D025BF7F96450892D7E
-:106AC00021020D0D4CC9D3C020D10F00002E9CFB1C
-:106AD00064E0962F21020F0F4C65F0A51AD7B71E60
-:106AE000D7B529A29EC08A798B712BE22668B004A3
-:106AF0008C207BC96629A29D1FD7B264905D9790B8
-:106B0000C0C31DD7C62B21049D9608BB110CBB0228
-:106B10009B919B971CD7C3C08527E4A22BA29D28DD
-:106B200024068DFA282102B0DD2BBC302BA69D9DBA
-:106B3000FA0C8802282502C8D2C020D10F8EF91283
-:106B4000D7B92E2689C020D10F283000688938DABD
-:106B500020DB30DC4058004463FF6300022A022B34
-:106B60000A065800D3220A00D10F655010293000C0
-:106B7000689924022A02033B02DC4058003BC020F3
-:106B8000D10FD270D10F00002A2C74033B02044CA9
-:106B9000025BFEF163FF2700DB30DC402A2C745BD4
-:106BA000FEEEC020D10F00006C1004C83F8926887B
-:106BB00029A399992609880C080848282525CC522C
-:106BC000C020D10FDB402A2C745BF92FD2A0D10F4B
-:106BD0006C1004D820D73082220D451105220C926A
-:106BE0008264207407420B13D771D420A3837323CC
-:106BF00002242DF8858074514CBC82C0906D08161B
-:106C000000408800708C773903D720C0918680744B
-:106C10003901D42074610263FFE2CA98C097C04171
-:106C20001BD7F2C0A00B8B0C0B4A380A0A42C9AA28
-:106C30001DD75E1CD75F2CD67EC140D30F6D4A0591
-:106C400000208800308C9780D270D10FBC8FC0E0BC
-:106C50000F4E387E90E263FFD6BC8292819280C054
-:106C6000209282D10F0000006C1006C0D71CD74EB6
-:106C70001BD7500D4911D7202E221F28221D0E4E42
-:106C80000BD280078A0C2E761F2AAC80C8346FAED8
-:106C9000026000CB2F0A801AD754A29EAA7A7EA344
-:106CA0003FC93FC0E1C05002E538050542CA552B37
-:106CB000C67EDB20D30F6D4A0500308800B08C2ED5
-:106CC000721DAE9E0EA50C645086D2802E761DC01D
-:106CD00091298403D10FC05003E53875D0D363FFE9
-:106CE000CD15D741027E0CA5EE643051C0A1250A16
-:106CF0000002A538033A020505426450922BC67E75
-:106D00000E35129510255C10054536D30F6D5A05CA
-:106D100000A08800208CC0A1A3E2C05023FA800309
-:106D2000730C03A538AF730505426450722BC67E01
-:106D3000851005450C6D5A0500208800308CD280E6
-:106D4000C0A10E9B0CAB7BAFBB2B761D2A8403D15D
-:106D50000FD280C0C1AF7D2D761D2C8403D10F00D2
-:106D6000D2302E8D08C0F1C0500EF538050542CB4B
-:106D7000592BC67E0A3F14C1600F660C064636D3F7
-:106D80000F6D6A0500208800E08C22721D63FF03EE
-:106D9000C061C05003653875D80263FF6263FF5C51
-:106DA000C05002A53875D08763FF8100C06003F62C
-:106DB0003876D0BF63FFB9006C10042A2015292053
-:106DC0001614D6FF0A990CCB9D2E200B04ED092B2F
-:106DD000D1208F2809BC36ACAA0CBB0C2BD5200ABD
-:106DE0000A472A2415CAAF8B438942B0A8009104F0
-:106DF00000881AA8FF0FBB029B278F260FB80C78BC
-:106E00003B1AC020D10F0000292102C0A20A99021A
-:106E1000292502C021D10F008B2763FFDC2BD12055
-:106E20000CAA0C0A0A472A2415ACBB2BD520C9AEE4
-:106E30008B438C288F42B0AD00F10400DD1AADCC3D
-:106E40000CBB029B27DA20B7EB580019C021D10FE9
-:106E50009F2763FFEF0000006C100428203C643083
-:106E60004705306000073E01053EB156076539050C
-:106E70004928C77FA933030641076603B1660606A2
-:106E800041A6337E871E222125291AFC732B150269
-:106E9000380C09816000063E01023EB124064239E9
-:106EA00003220AD10FD230D10FC05163FFC00000BE
-:106EB0006C100427221EC08008E4311DD6BF0002DA
-:106EC000002CD2821BD6BF003104C06100661A2B91
-:106ED000B1020C6C022CD6820BE43119D7440C3A67
-:106EE00011AA932832829780253282243284B455A5
-:106EF00025368275410A292102096902292502D114
-:106F00000F2A21022B32830A6A022B36822A25029B
-:106F1000D10F00006C100418D6A80C2711087708B0
-:106F2000267286253C04765B1315D6A405220A2218
-:106F300022A3682002742904227285D10FC020D1B7
-:106F40000F0000006C100419D6A727221EC080096C
-:106F5000770208E4311DD6980002002CD2821BD69D
-:106F600098003104C06100661A2BB1020C6C022C2F
-:106F7000D6820BE43119D71D0C3A11AA932832821C
-:106F80009780253282243284B45525368275410B90
-:106F90002A21020A6A022A2502D10F002B21022C83
-:106FA00032830B6B022C36822B2502D10F0000009E
-:106FB0006C10041BD6810C2A11ABAA29A286B43806
-:106FC000798B221BD67E19D6A50B2B0A2BB2A309CF
-:106FD000290868B00274B90D299D0129901F6E928D
-:106FE0000822A285D10FC020D10FC892C020D10F96
-:106FF000DA205BEE88C020D10F0000006C10041472
-:10700000D66E28429E19D66B6F88026000BA29920C
-:10701000266890078A2009AA0C65A0AC2A429DC068
-:10702000DC64A0A42B200C19D6650CBC11A4CC2EBA
-:10703000C28609B90A7ED30260009A2992A3689099
-:10704000078D2009DD0C65D08C25C2856450862D06
-:107050002104C0306ED80D2C2066B8CC0C0C472C07
-:10706000246665C07B1CD6E218D66B1AD66219D688
-:10707000731DD667C0E49E519D508F209357935542
-:1070800099539A569A5408FF021AD6839F5288261B
-:107090009F5A9E599D58935E9C5D935C9A5B08082D
-:1070A00048058811985FC0D81FD64C0CB911A49917
-:1070B000289285AFBF23F4CF288C402896858E2652
-:1070C0002D24069E29C020D10FCA33DA20C0B65B1A
-:1070D000FF78C72FD10FC93ADA205BFF75C72FD1D0
-:1070E0000FDBD05BFE072324662B200C63FF7500AB
-:1070F000C72FD10FC72FD10F6C1004C85B292006F2
-:1071000068941C689607C020D10FC020D10FDA20E8
-:10711000DB30DC40DD502E0A005BFE59D2A0D10FDF
-:107120002E200C18D6250CEF11A8FF29F286C08856
-:10713000798B791AD6220AEA0A2AA2A368A0048BBC
-:10714000207AB96823F2856430621BD62C290A8024
-:107150002C20682820672D21040B881104DD1108DC
-:10716000DD020DCC02C0842D4A100DCC021DD624A8
-:1071700098319D308A2B99379C340BAA02C0C09C51
-:10718000359C369A322A2C74DB4028F285C0D328ED
-:107190008C2028F6852C25042D24061FD60FDD40D3
-:1071A000AFEE2CE4CF5BFDE6D2A0D10F00DA20DBFE
-:1071B000E05BFF3FC020D10F6C100AD6302A2006BA
-:1071C00024160128ACF86583862B2122C0F22A21DF
-:1071D00024CC572AAC010A0A4F2A25247ABB026024
-:1071E000037F2C21020C0C4C65C3192E22158D3205
-:1071F000C0910EDD0C65D39088381ED5EF64836B8B
-:107200008C37C0B8C0960CB9399914B49A9A120D3B
-:10721000991199138F6718D5EAC9FB2880217F83BC
-:10722000168B142C22002A200C5BFF61D4A064A3CF
-:10723000B38F6760002800002B200C89120CBA1154
-:10724000AEAA2CA2861DD5DD7C9B3E0DBD0A2DD29B
-:10725000A368D00488207D893024A28564436427F4
-:10726000212E07F73607F90C6F9D01D7F0DA20DBE6
-:1072700070C1C42D211F5BFEF889268827DDA00977
-:10728000880C7A8B179A10600006C04063FFCC0010
-:1072900000DA208B105BFEC88D1065A267C0E09EEF
-:1072A000488C649C498B658A669B4A9A4B97458FAC
-:1072B000677F7302600120CD529D10DA20DB302CF5
-:1072C00012015BFE698D10C051D6A08FA7C0C08A85
-:1072D00068974D9A4C8869896A984E994F8E6A8A48
-:1072E00069AE7E77EB01B1AA9E6A9A698B60C0A0F5
-:1072F0000B8E1477B701C0A1C091C08493159D1760
-:107300009516C0D025203CC030085801089338C0DD
-:1073100082083310085B010535400B9D3807DD10EE
-:107320000BAB100E19402A211F07991003DD020D27
-:10733000BB020553100933020A55112921250A2AD7
-:10734000140929140499110A99020933028A2B2974
-:1073500021040BAA021BD6270899110955020855CA
-:10736000020BAA029A408920881408991109880200
-:1073700019D5A61DD62109880298418B2A934695D6
-:107380004783150DBB0285168D179B448A65896658
-:10739000AACAA97C77CB01B1AA07FB0C9C669A65A7
-:1073A00088268E29AD87972607EE0C0E0E482E25CF
-:1073B000259B672B200C87131ED5800CB911AE9925
-:1073C000289285A78828968517D584C090A7BB29C1
-:1073D000B4CF871863FE3C008C60C0E0C091C0F061
-:1073E000C034C0B82A210428203C08AA110B8B0104
-:1073F000038301039F380B9B39C03208FF100388B9
-:1074000001089E380C881407EE100FEE0203880165
-:1074100008983905BF1029211F0ABB1107881008D9
-:10742000FF020BAA0218D57809291403AA022B21FE
-:107430002583200B2B1404BB110833110FBB020B47
-:1074400099028B148F2A0B33020833028B2B647042
-:10745000868868974D984C8769886A9341994697C2
-:107460004E984FC07077C701C0719A4718D5E30B8B
-:107470007C100CEC0208F802984418D5E00CBC0211
-:1074800008CC029C402A200C295CFEC0801FD54AF3
-:107490001CD5520CAE112B2124ACAAAFEEB0BB8F81
-:1074A000132CE28528A4CFAFCC2CE6852A22152BFD
-:1074B0002524B1AA2A26156490DBC9D28F262E2254
-:1074C000090DFF082F26060FEE0C0E0E482E25255F
-:1074D0006550E4C020D10F00C07093419F4499468D
-:1074E0009A4777C70A1CD5362CC022C0810C873832
-:1074F0001CD5C40B781008E80208B8020C88029862
-:107500004063FF8000CC57DA20DB608C115BFDD636
-:10751000292102689806689403C020D10F2B221EEF
-:10752000C0A029221D2A25027B9901C0B064BFE8B2
-:1075300013D5212CB00728B000DA2003880A28824E
-:107540004CC0D10B8000DBA065AFE763FFCA000031
-:1075500068A779DA20DB30DC40DD505BFEE7D2A0A3
-:10756000D10FC16DC19D29252C60000429252CD681
-:10757000902624672F2468DA20DB308C11DD502E12
-:107580000A805BFD3FD2A0D10FC168C1A82A252C7B
-:1075900063FFDD000000C8DF8C268B29ADCC9C2664
-:1075A0000CBB0C0B0B482B25252A2C74DB602C12F2
-:1075B000015BFD87D2A0D10F2A2C748B115BF6B230
-:1075C000D2A0D10FDA205BFE3A63FF3800DA20C088
-:1075D000B15BFE8A64ABF1655F352D2124B1DD2DF1
-:1075E000252463FF1FDA202B200C5BFE5663FF145B
-:1075F00012D5858220028257C82163FFFC12D581F3
-:1076000003E83004EE3005B13093209421952263D5
-:10761000FFFC000010D57D910092019302940311AC
-:10762000D554821001EA30A21101F031C04004E4C7
-:107630001600020011D5768210234A00032202921E
-:107640001011D540C021921004E4318403830282DA
-:1076500001810000D23001230000000010D56D919F
-:107660000092019302940311D543821001EA30A2E3
-:107670001101F131C04004E41600020011D564820A
-:107680001013D4E7032202921004E431840383022E
-:107690008201810000D330013300000010D55E91DB
-:1076A00000810165104981026510448103CF1F925A
-:1076B000019302940311D531821001EA30A2110125
-:1076C000F231C04004E41600020011D550821013BC
-:1076D000D4CF032202921004E43184038302820196
-:1076E000C010910391029101810000D43001430048
-:1076F00012D500C03028374028374428374828376B
-:107700004C233D017233ED03020063FFFC000000D7
-:1077100010D542910092019302940311D54082103A
-:10772000921011D4F28310032202921011D53D124F
-:10773000D5049210C04004E41600020011D5348232
-:107740001013D4EB032202921004E4318403830269
-:107750008201810000D53001530000006C10026EE0
-:10776000322FD620056F04043F04745B2A05440CB5
-:1077700000410400331A220A006D490D73630403AB
-:10778000660CB1220F2211031314736302222C0121
-:10779000D10FC83BD10F000073630CC021D10F0083
-:1077A0000000000044495630C020D10F6C10020088
-:1077B00040046B4C07032318020219D10F0203196E
-:1077C000C020D10F6C100202EA30D10F6C1002CC35
-:1077D0002503F03160000F006F220503F1316000D6
-:1077E000056F230503F231000200D10F6C1002CCAB
-:1077F0002502F030D10F00006F220402F130D10FCA
-:107800006F230402F230D10FC020D10F6C1002227E
-:107810000A20230A006D280E2837402837442837CD
-:107820004828374C233D01030200D10F6C1002029F
-:10783000E431D10F0A0000004368656C73696F2062
-:1078400046572044454255473D3020284275696CD3
-:1078500074204D6F6E204D61722020382031373AF0
-:1078600032383A3135205053542032303130206F85
-:107870006E20636C656F70617472612E61736963F1
-:1078800064657369676E6572732E636F6D3A2F68F6
-:107890006F6D652F66656C69782F772F66775F3718
-:1078A0002E392D6977617270292C205665727369A3
-:1078B0006F6E2054337878203030372E30612E3080
-:1078C00030202D20313030373061303010070A0041
-:0478D0000BDFE8756D
-:00000001FF
index fc06fd27065eb3cade2f5e5f60f7155d3487ae80..dd6f7ee1e31258d94dfa1e038d2a272f17ecb541 100644 (file)
@@ -610,6 +610,9 @@ v9fs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
        p9_debug(P9_DEBUG_VFS, "page %p fid %lx\n",
                 page, (unsigned long)filp->private_data);
 
+       /* Update file times before taking page lock */
+       file_update_time(filp);
+
        v9inode = V9FS_I(inode);
        /* make sure the cache has finished storing the page */
        v9fs_fscache_wait_on_page_write(inode, page);
index 6e0be43ef6ef8eef90c57a4560bf671c9d756b7a..a32246b8359e50d99e658040b0e12a0bd8f4c5a1 100644 (file)
 #include <linux/slab.h>
 #include "affs.h"
 
-/* This is, of course, shamelessly stolen from fs/minix */
-
-static const int nibblemap[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
-
-static u32
-affs_count_free_bits(u32 blocksize, const void *data)
-{
-       const u32 *map;
-       u32 free;
-       u32 tmp;
-
-       map = data;
-       free = 0;
-       for (blocksize /= 4; blocksize > 0; blocksize--) {
-               tmp = *map++;
-               while (tmp) {
-                       free += nibblemap[tmp & 0xf];
-                       tmp >>= 4;
-               }
-       }
-
-       return free;
-}
-
 u32
 affs_count_free_blocks(struct super_block *sb)
 {
@@ -317,7 +293,7 @@ int affs_init_bitmap(struct super_block *sb, int *flags)
                        goto out;
                }
                pr_debug("AFFS: read bitmap block %d: %d\n", blk, bm->bm_key);
-               bm->bm_free = affs_count_free_bits(sb->s_blocksize - 4, bh->b_data + 4);
+               bm->bm_free = memweight(bh->b_data + 4, sb->s_blocksize - 4);
 
                /* Don't try read the extension if this is the last block,
                 * but we also need the right bm pointer below
@@ -367,7 +343,7 @@ int affs_init_bitmap(struct super_block *sb, int *flags)
 
        /* recalculate bitmap count for last block */
        bm--;
-       bm->bm_free = affs_count_free_bits(sb->s_blocksize - 4, bh->b_data + 4);
+       bm->bm_free = memweight(bh->b_data + 4, sb->s_blocksize - 4);
 
 out:
        affs_brelse(bh);
index adb1cd7ceb9b954af34457efc2d119fb422c6f4f..4bab807227ad938c87039ea2aa1ef8f94fe46146 100644 (file)
@@ -3342,10 +3342,22 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
 /* super.c */
 int btrfs_parse_options(struct btrfs_root *root, char *options);
 int btrfs_sync_fs(struct super_block *sb, int wait);
+
+#ifdef CONFIG_PRINTK
+__printf(2, 3)
 void btrfs_printk(struct btrfs_fs_info *fs_info, const char *fmt, ...);
+#else
+static inline __printf(2, 3)
+void btrfs_printk(struct btrfs_fs_info *fs_info, const char *fmt, ...)
+{
+}
+#endif
+
+__printf(5, 6)
 void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function,
                     unsigned int line, int errno, const char *fmt, ...);
 
+
 void __btrfs_abort_transaction(struct btrfs_trans_handle *trans,
                               struct btrfs_root *root, const char *function,
                               unsigned int line, int errno);
@@ -3386,6 +3398,7 @@ do {                                                              \
                          (errno), fmt, ##args);                \
 } while (0)
 
+__printf(5, 6)
 void __btrfs_panic(struct btrfs_fs_info *fs_info, const char *function,
                   unsigned int line, int errno, const char *fmt, ...);
 
index 502b20c56e848f4df3ede188a408d535de5b4173..62e0cafd6e250d5d1717656d3d5821e2d1bfec42 100644 (file)
@@ -1114,7 +1114,7 @@ void clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                                spin_unlock(&root->fs_info->delalloc_lock);
                                btrfs_panic(root->fs_info, -EOVERFLOW,
                                          "Can't clear %lu bytes from "
-                                         " dirty_mdatadata_bytes (%lu)",
+                                         " dirty_mdatadata_bytes (%llu)",
                                          buf->len,
                                          root->fs_info->dirty_metadata_bytes);
                        }
@@ -1614,8 +1614,6 @@ static int cleaner_kthread(void *arg)
        struct btrfs_root *root = arg;
 
        do {
-               vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE);
-
                if (!(root->fs_info->sb->s_flags & MS_RDONLY) &&
                    mutex_trylock(&root->fs_info->cleaner_mutex)) {
                        btrfs_run_delayed_iputs(root);
@@ -1647,7 +1645,6 @@ static int transaction_kthread(void *arg)
        do {
                cannot_commit = false;
                delay = HZ * 30;
-               vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE);
                mutex_lock(&root->fs_info->transaction_kthread_mutex);
 
                spin_lock(&root->fs_info->trans_lock);
index 9aa01ec2138d466f3d1726ccd6291d054c5e5c98..5caf285c6e4d0f1cb7adf82d8af911ab614a807a 100644 (file)
@@ -1379,7 +1379,7 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
        ssize_t err = 0;
        size_t count, ocount;
 
-       vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+       sb_start_write(inode->i_sb);
 
        mutex_lock(&inode->i_mutex);
 
@@ -1469,6 +1469,7 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
                        num_written = err;
        }
 out:
+       sb_end_write(inode->i_sb);
        current->backing_dev_info = NULL;
        return num_written ? num_written : err;
 }
index 48bdfd2591c2bb4c23fd141b66dd555a37ef6cc7..83baec24946d7449194e94893f92f918b14c79b5 100644 (file)
@@ -6629,6 +6629,7 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
        u64 page_start;
        u64 page_end;
 
+       sb_start_pagefault(inode->i_sb);
        ret  = btrfs_delalloc_reserve_space(inode, PAGE_CACHE_SIZE);
        if (!ret) {
                ret = file_update_time(vma->vm_file);
@@ -6718,12 +6719,15 @@ again:
        unlock_extent_cached(io_tree, page_start, page_end, &cached_state, GFP_NOFS);
 
 out_unlock:
-       if (!ret)
+       if (!ret) {
+               sb_end_pagefault(inode->i_sb);
                return VM_FAULT_LOCKED;
+       }
        unlock_page(page);
 out:
        btrfs_delalloc_release_space(inode, PAGE_CACHE_SIZE);
 out_noreserve:
+       sb_end_pagefault(inode->i_sb);
        return ret;
 }
 
index 43f0012016e3e6291cb79579a60940f52ec94a9c..bc2f6ffff3cfb004099a5d4eb84f8a9aa688d7cd 100644 (file)
@@ -195,6 +195,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        if (!inode_owner_or_capable(inode))
                return -EACCES;
 
+       ret = mnt_want_write_file(file);
+       if (ret)
+               return ret;
+
        mutex_lock(&inode->i_mutex);
 
        ip_oldflags = ip->flags;
@@ -209,10 +213,6 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
                }
        }
 
-       ret = mnt_want_write_file(file);
-       if (ret)
-               goto out_unlock;
-
        if (flags & FS_SYNC_FL)
                ip->flags |= BTRFS_INODE_SYNC;
        else
@@ -275,9 +275,9 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
                inode->i_flags = i_oldflags;
        }
 
-       mnt_drop_write_file(file);
  out_unlock:
        mutex_unlock(&inode->i_mutex);
+       mnt_drop_write_file(file);
        return ret;
 }
 
@@ -664,6 +664,10 @@ static noinline int btrfs_mksubvol(struct path *parent,
        struct dentry *dentry;
        int error;
 
+       error = mnt_want_write(parent->mnt);
+       if (error)
+               return error;
+
        mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
 
        dentry = lookup_one_len(name, parent->dentry, namelen);
@@ -699,6 +703,7 @@ out_dput:
        dput(dentry);
 out_unlock:
        mutex_unlock(&dir->i_mutex);
+       mnt_drop_write(parent->mnt);
        return error;
 }
 
index c5dbd91496790a3279b8044552f6664666b68a52..4da08652004d5dc2803dba510c0f9b015607b4a1 100644 (file)
@@ -1241,7 +1241,7 @@ static int __must_check __add_reloc_root(struct btrfs_root *root)
        if (rb_node) {
                btrfs_panic(root->fs_info, -EEXIST, "Duplicate root found "
                            "for start=%llu while inserting into relocation "
-                           "tree\n");
+                           "tree\n", node->bytenr);
                kfree(node);
                return -EEXIST;
        }
index fa61ef59cd61aadf7582ce8f73cdff298331b47f..8c6e61d6eed5b754945930fbaaaaf21d6f98d028 100644 (file)
@@ -125,6 +125,7 @@ static void btrfs_handle_error(struct btrfs_fs_info *fs_info)
        }
 }
 
+#ifdef CONFIG_PRINTK
 /*
  * __btrfs_std_error decodes expected errors from the caller and
  * invokes the approciate error response.
@@ -167,7 +168,7 @@ void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function,
        va_end(args);
 }
 
-const char *logtypes[] = {
+static const char * const logtypes[] = {
        "emergency",
        "alert",
        "critical",
@@ -185,22 +186,50 @@ void btrfs_printk(struct btrfs_fs_info *fs_info, const char *fmt, ...)
        struct va_format vaf;
        va_list args;
        const char *type = logtypes[4];
+       int kern_level;
 
        va_start(args, fmt);
 
-       if (fmt[0] == '<' && isdigit(fmt[1]) && fmt[2] == '>') {
-               memcpy(lvl, fmt, 3);
-               lvl[3] = '\0';
-               fmt += 3;
-               type = logtypes[fmt[1] - '0'];
+       kern_level = printk_get_level(fmt);
+       if (kern_level) {
+               size_t size = printk_skip_level(fmt) - fmt;
+               memcpy(lvl, fmt,  size);
+               lvl[size] = '\0';
+               fmt += size;
+               type = logtypes[kern_level - '0'];
        } else
                *lvl = '\0';
 
        vaf.fmt = fmt;
        vaf.va = &args;
+
        printk("%sBTRFS %s (device %s): %pV", lvl, type, sb->s_id, &vaf);
+
+       va_end(args);
 }
 
+#else
+
+void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function,
+                      unsigned int line, int errno, const char *fmt, ...)
+{
+       struct super_block *sb = fs_info->sb;
+
+       /*
+        * Special case: if the error is EROFS, and we're already
+        * under MS_RDONLY, then it is safe here.
+        */
+       if (errno == -EROFS && (sb->s_flags & MS_RDONLY))
+               return;
+
+       /* Don't go through full error handling during mount */
+       if (sb->s_flags & MS_BORN) {
+               save_error_info(fs_info);
+               btrfs_handle_error(fs_info);
+       }
+}
+#endif
+
 /*
  * We only mark the transaction aborted and then set the file system read-only.
  * This will prevent new transactions from starting or trying to join this
index 7ac7cdcc294e5dc561bc6916efec59dacf320884..17be3dedacbab1c47084270153ed059719984470 100644 (file)
@@ -335,6 +335,8 @@ again:
        if (!h)
                return ERR_PTR(-ENOMEM);
 
+       sb_start_intwrite(root->fs_info->sb);
+
        if (may_wait_transaction(root, type))
                wait_current_trans(root);
 
@@ -345,6 +347,7 @@ again:
        } while (ret == -EBUSY);
 
        if (ret < 0) {
+               sb_end_intwrite(root->fs_info->sb);
                kmem_cache_free(btrfs_trans_handle_cachep, h);
                return ERR_PTR(ret);
        }
@@ -548,6 +551,8 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
        btrfs_trans_release_metadata(trans, root);
        trans->block_rsv = NULL;
 
+       sb_end_intwrite(root->fs_info->sb);
+
        if (lock && !atomic_read(&root->fs_info->open_ioctl_trans) &&
            should_end_transaction(trans, root)) {
                trans->transaction->blocked = 1;
@@ -1578,6 +1583,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        put_transaction(cur_trans);
        put_transaction(cur_trans);
 
+       sb_end_intwrite(root->fs_info->sb);
+
        trace_btrfs_transaction_commit(root);
 
        btrfs_scrub_continue(root);
index c7062c896d7c20489f41ea838640a5cfa5387ca9..9f6d2e41281d69752f77d9c68772a46c855a8453 100644 (file)
@@ -2306,8 +2306,8 @@ EXPORT_SYMBOL(block_commit_write);
  * beyond EOF, then the page is guaranteed safe against truncation until we
  * unlock the page.
  *
- * Direct callers of this function should call vfs_check_frozen() so that page
- * fault does not busyloop until the fs is thawed.
+ * Direct callers of this function should protect against filesystem freezing
+ * using sb_start_write() - sb_end_write() functions.
  */
 int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
                         get_block_t get_block)
@@ -2318,6 +2318,12 @@ int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
        loff_t size;
        int ret;
 
+       /*
+        * Update file times before taking page lock. We may end up failing the
+        * fault so this update may be superfluous but who really cares...
+        */
+       file_update_time(vma->vm_file);
+
        lock_page(page);
        size = i_size_read(inode);
        if ((page->mapping != inode->i_mapping) ||
@@ -2339,18 +2345,7 @@ int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
 
        if (unlikely(ret < 0))
                goto out_unlock;
-       /*
-        * Freezing in progress? We check after the page is marked dirty and
-        * with page lock held so if the test here fails, we are sure freezing
-        * code will wait during syncing until the page fault is done - at that
-        * point page will be dirty and unlocked so freezing code will write it
-        * and writeprotect it again.
-        */
        set_page_dirty(page);
-       if (inode->i_sb->s_frozen != SB_UNFROZEN) {
-               ret = -EAGAIN;
-               goto out_unlock;
-       }
        wait_on_page_writeback(page);
        return 0;
 out_unlock:
@@ -2365,12 +2360,9 @@ int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
        int ret;
        struct super_block *sb = vma->vm_file->f_path.dentry->d_inode->i_sb;
 
-       /*
-        * This check is racy but catches the common case. The check in
-        * __block_page_mkwrite() is reliable.
-        */
-       vfs_check_frozen(sb, SB_FREEZE_WRITE);
+       sb_start_pagefault(sb);
        ret = __block_page_mkwrite(vma, vmf, get_block);
+       sb_end_pagefault(sb);
        return block_page_mkwrite_return(ret);
 }
 EXPORT_SYMBOL(block_page_mkwrite);
index c0353dfac51f819d3e8cb69644fd50a98fa04224..c994691d94454254cfcb75e796b6fcaaeab8e2b1 100644 (file)
@@ -919,7 +919,7 @@ int cachefiles_write_page(struct fscache_storage *op, struct page *page)
         * own time */
        path.mnt = cache->mnt;
        path.dentry = object->backer;
-       file = dentry_open(&path, O_RDWR, cache->cache_cred);
+       file = dentry_open(&path, O_RDWR | O_LARGEFILE, cache->cache_cred);
        if (IS_ERR(file)) {
                ret = PTR_ERR(file);
        } else {
index 8b67304e4b8079efe80eecd716fb1e98b5486842..452e71a1b75388b2dc06d3ad1665f36a1ed1176f 100644 (file)
@@ -1184,6 +1184,9 @@ static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
        loff_t size, len;
        int ret;
 
+       /* Update time before taking page lock */
+       file_update_time(vma->vm_file);
+
        size = i_size_read(inode);
        if (off + PAGE_CACHE_SIZE <= size)
                len = PAGE_CACHE_SIZE;
index 00894ff9246c2175b60177bab59b70456733ff01..f391f1e754145141235bf89d28da37d5c6aba005 100644 (file)
@@ -51,8 +51,7 @@ int ceph_init_dentry(struct dentry *dentry)
                goto out_unlock;
        }
 
-       if (dentry->d_parent == NULL ||   /* nfs fh_to_dentry */
-           ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP)
+       if (ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP)
                d_set_d_op(dentry, &ceph_dentry_ops);
        else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR)
                d_set_d_op(dentry, &ceph_snapdir_dentry_ops);
@@ -79,7 +78,7 @@ struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry)
                return NULL;
 
        spin_lock(&dentry->d_lock);
-       if (dentry->d_parent) {
+       if (!IS_ROOT(dentry)) {
                inode = dentry->d_parent->d_inode;
                ihold(inode);
        }
@@ -1154,7 +1153,7 @@ static void ceph_d_prune(struct dentry *dentry)
        dout("ceph_d_prune %p\n", dentry);
 
        /* do we have a valid parent? */
-       if (!dentry->d_parent || IS_ROOT(dentry))
+       if (IS_ROOT(dentry))
                return;
 
        /* if we are not hashed, we don't affect D_COMPLETE */
index 200bc87eceb1cc417a1caa1a73e264cde79dda78..a5a735422aa7a43e5e2d6022c81402f739a1b660 100644 (file)
@@ -10,6 +10,7 @@
 #include "super.h"
 #include "mds_client.h"
 
+#include <linux/ceph/ceph_features.h>
 #include <linux/ceph/messenger.h>
 #include <linux/ceph/decode.h>
 #include <linux/ceph/pagelist.h>
@@ -394,11 +395,7 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc,
        s->s_seq = 0;
        mutex_init(&s->s_mutex);
 
-       ceph_con_init(mdsc->fsc->client->msgr, &s->s_con);
-       s->s_con.private = s;
-       s->s_con.ops = &mds_con_ops;
-       s->s_con.peer_name.type = CEPH_ENTITY_TYPE_MDS;
-       s->s_con.peer_name.num = cpu_to_le64(mds);
+       ceph_con_init(&s->s_con, s, &mds_con_ops, &mdsc->fsc->client->msgr);
 
        spin_lock_init(&s->s_gen_ttl_lock);
        s->s_cap_gen = 0;
@@ -440,7 +437,8 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc,
        mdsc->sessions[mds] = s;
        atomic_inc(&s->s_ref);  /* one ref to sessions[], one to caller */
 
-       ceph_con_open(&s->s_con, ceph_mdsmap_get_addr(mdsc->mdsmap, mds));
+       ceph_con_open(&s->s_con, CEPH_ENTITY_TYPE_MDS, mds,
+                     ceph_mdsmap_get_addr(mdsc->mdsmap, mds));
 
        return s;
 
@@ -1472,11 +1470,6 @@ retry:
                else
                        len += 1 + temp->d_name.len;
                temp = temp->d_parent;
-               if (temp == NULL) {
-                       rcu_read_unlock();
-                       pr_err("build_path corrupt dentry %p\n", dentry);
-                       return ERR_PTR(-EINVAL);
-               }
        }
        rcu_read_unlock();
        if (len)
@@ -1513,12 +1506,6 @@ retry:
                if (pos)
                        path[--pos] = '/';
                temp = temp->d_parent;
-               if (temp == NULL) {
-                       rcu_read_unlock();
-                       pr_err("build_path corrupt dentry\n");
-                       kfree(path);
-                       return ERR_PTR(-EINVAL);
-               }
        }
        rcu_read_unlock();
        if (pos != 0 || read_seqretry(&rename_lock, seq)) {
@@ -2531,7 +2518,9 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc,
        session->s_state = CEPH_MDS_SESSION_RECONNECTING;
        session->s_seq = 0;
 
+       ceph_con_close(&session->s_con);
        ceph_con_open(&session->s_con,
+                     CEPH_ENTITY_TYPE_MDS, mds,
                      ceph_mdsmap_get_addr(mdsc->mdsmap, mds));
 
        /* replay unsafe requests */
index e5206fc765620f3b550c2457e1f271cb9d81c8c8..cbb2f54a301971cea4e7ed02b029c3e7979cbc18 100644 (file)
@@ -296,8 +296,7 @@ static int build_snap_context(struct ceph_snap_realm *realm)
        struct ceph_snap_realm *parent = realm->parent;
        struct ceph_snap_context *snapc;
        int err = 0;
-       int i;
-       int num = realm->num_prior_parent_snaps + realm->num_snaps;
+       u32 num = realm->num_prior_parent_snaps + realm->num_snaps;
 
        /*
         * build parent context, if it hasn't been built.
@@ -321,11 +320,11 @@ static int build_snap_context(struct ceph_snap_realm *realm)
            realm->cached_context->seq == realm->seq &&
            (!parent ||
             realm->cached_context->seq >= parent->cached_context->seq)) {
-               dout("build_snap_context %llx %p: %p seq %lld (%d snaps)"
+               dout("build_snap_context %llx %p: %p seq %lld (%u snaps)"
                     " (unchanged)\n",
                     realm->ino, realm, realm->cached_context,
                     realm->cached_context->seq,
-                    realm->cached_context->num_snaps);
+                    (unsigned int) realm->cached_context->num_snaps);
                return 0;
        }
 
@@ -342,6 +341,8 @@ static int build_snap_context(struct ceph_snap_realm *realm)
        num = 0;
        snapc->seq = realm->seq;
        if (parent) {
+               u32 i;
+
                /* include any of parent's snaps occurring _after_ my
                   parent became my parent */
                for (i = 0; i < parent->cached_context->num_snaps; i++)
@@ -361,8 +362,9 @@ static int build_snap_context(struct ceph_snap_realm *realm)
 
        sort(snapc->snaps, num, sizeof(u64), cmpu64_rev, NULL);
        snapc->num_snaps = num;
-       dout("build_snap_context %llx %p: %p seq %lld (%d snaps)\n",
-            realm->ino, realm, snapc, snapc->seq, snapc->num_snaps);
+       dout("build_snap_context %llx %p: %p seq %lld (%u snaps)\n",
+            realm->ino, realm, snapc, snapc->seq,
+            (unsigned int) snapc->num_snaps);
 
        if (realm->cached_context)
                ceph_put_snap_context(realm->cached_context);
@@ -402,9 +404,9 @@ static void rebuild_snap_realms(struct ceph_snap_realm *realm)
  * helper to allocate and decode an array of snapids.  free prior
  * instance, if any.
  */
-static int dup_array(u64 **dst, __le64 *src, int num)
+static int dup_array(u64 **dst, __le64 *src, u32 num)
 {
-       int i;
+       u32 i;
 
        kfree(*dst);
        if (num) {
index 7076109f014dae80a85c789c3022b6465f9b70c0..b982239f38f91dfab38fcc093e600d5b11e5c632 100644 (file)
@@ -18,6 +18,7 @@
 #include "super.h"
 #include "mds_client.h"
 
+#include <linux/ceph/ceph_features.h>
 #include <linux/ceph/decode.h>
 #include <linux/ceph/mon_client.h>
 #include <linux/ceph/auth.h>
index f4d5522cb619eba5f5a27842ea52277e9b3d5563..ebc95cc652be17bed2717d8c83f64eb39f69aed0 100644 (file)
@@ -612,9 +612,9 @@ struct ceph_snap_realm {
        u64 parent_since;   /* snapid when our current parent became so */
 
        u64 *prior_parent_snaps;      /* snaps inherited from any parents we */
-       int num_prior_parent_snaps;   /*  had prior to parent_since */
+       u32 num_prior_parent_snaps;   /*  had prior to parent_since */
        u64 *snaps;                   /* snaps specific to this realm */
-       int num_snaps;
+       u32 num_snaps;
 
        struct ceph_snap_realm *parent;
        struct list_head children;       /* list of child realms */
index 785cb3057c95a436c344da2d76ee24b86cb7954b..2c2ae5be99027af909d30a65cb73706a7c0d193a 100644 (file)
@@ -457,6 +457,7 @@ start:
                        for (i = 0; i < numattr; i++)
                                kfree(xattrs[i]);
                        kfree(xattrs);
+                       xattrs = NULL;
                        goto start;
                }
                err = -EIO;
index ffa2be57804dddcd21b89a712cc9107d027b3c3b..c3ca12c33ca2aa3c504bcc3e415595d72b96c5d4 100644 (file)
@@ -318,21 +318,20 @@ static int ecryptfs_lookup_interpose(struct dentry *dentry,
        struct vfsmount *lower_mnt;
        int rc = 0;
 
-       lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent));
-       fsstack_copy_attr_atime(dir_inode, lower_dentry->d_parent->d_inode);
-       BUG_ON(!lower_dentry->d_count);
-
        dentry_info = kmem_cache_alloc(ecryptfs_dentry_info_cache, GFP_KERNEL);
-       ecryptfs_set_dentry_private(dentry, dentry_info);
        if (!dentry_info) {
                printk(KERN_ERR "%s: Out of memory whilst attempting "
                       "to allocate ecryptfs_dentry_info struct\n",
                        __func__);
                dput(lower_dentry);
-               mntput(lower_mnt);
-               d_drop(dentry);
                return -ENOMEM;
        }
+
+       lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent));
+       fsstack_copy_attr_atime(dir_inode, lower_dentry->d_parent->d_inode);
+       BUG_ON(!lower_dentry->d_count);
+
+       ecryptfs_set_dentry_private(dentry, dentry_info);
        ecryptfs_set_dentry_lower(dentry, lower_dentry);
        ecryptfs_set_dentry_lower_mnt(dentry, lower_mnt);
 
@@ -381,12 +380,6 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
        struct dentry *lower_dir_dentry, *lower_dentry;
        int rc = 0;
 
-       if ((ecryptfs_dentry->d_name.len == 1
-            && !strcmp(ecryptfs_dentry->d_name.name, "."))
-           || (ecryptfs_dentry->d_name.len == 2
-               && !strcmp(ecryptfs_dentry->d_name.name, ".."))) {
-               goto out_d_drop;
-       }
        lower_dir_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry->d_parent);
        mutex_lock(&lower_dir_dentry->d_inode->i_mutex);
        lower_dentry = lookup_one_len(ecryptfs_dentry->d_name.name,
@@ -397,8 +390,8 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
                rc = PTR_ERR(lower_dentry);
                ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned "
                                "[%d] on lower_dentry = [%s]\n", __func__, rc,
-                               encrypted_and_encoded_name);
-               goto out_d_drop;
+                               ecryptfs_dentry->d_name.name);
+               goto out;
        }
        if (lower_dentry->d_inode)
                goto interpose;
@@ -415,7 +408,7 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
        if (rc) {
                printk(KERN_ERR "%s: Error attempting to encrypt and encode "
                       "filename; rc = [%d]\n", __func__, rc);
-               goto out_d_drop;
+               goto out;
        }
        mutex_lock(&lower_dir_dentry->d_inode->i_mutex);
        lower_dentry = lookup_one_len(encrypted_and_encoded_name,
@@ -427,14 +420,11 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
                ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned "
                                "[%d] on lower_dentry = [%s]\n", __func__, rc,
                                encrypted_and_encoded_name);
-               goto out_d_drop;
+               goto out;
        }
 interpose:
        rc = ecryptfs_lookup_interpose(ecryptfs_dentry, lower_dentry,
                                       ecryptfs_dir_inode);
-       goto out;
-out_d_drop:
-       d_drop(ecryptfs_dentry);
 out:
        kfree(encrypted_and_encoded_name);
        return ERR_PTR(rc);
index e95aeeddd25c4038ad1656621bd9af56b7a0868e..574cf4de4ec38ba2c64115ca85d78e80f0e31dd2 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -2002,17 +2002,17 @@ static void coredump_finish(struct mm_struct *mm)
 void set_dumpable(struct mm_struct *mm, int value)
 {
        switch (value) {
-       case 0:
+       case SUID_DUMPABLE_DISABLED:
                clear_bit(MMF_DUMPABLE, &mm->flags);
                smp_wmb();
                clear_bit(MMF_DUMP_SECURELY, &mm->flags);
                break;
-       case 1:
+       case SUID_DUMPABLE_ENABLED:
                set_bit(MMF_DUMPABLE, &mm->flags);
                smp_wmb();
                clear_bit(MMF_DUMP_SECURELY, &mm->flags);
                break;
-       case 2:
+       case SUID_DUMPABLE_SAFE:
                set_bit(MMF_DUMP_SECURELY, &mm->flags);
                smp_wmb();
                set_bit(MMF_DUMPABLE, &mm->flags);
@@ -2025,7 +2025,7 @@ static int __get_dumpable(unsigned long mm_flags)
        int ret;
 
        ret = mm_flags & MMF_DUMPABLE_MASK;
-       return (ret >= 2) ? 2 : ret;
+       return (ret > SUID_DUMPABLE_ENABLED) ? SUID_DUMPABLE_SAFE : ret;
 }
 
 int get_dumpable(struct mm_struct *mm)
@@ -2069,25 +2069,18 @@ static void wait_for_dump_helpers(struct file *file)
  */
 static int umh_pipe_setup(struct subprocess_info *info, struct cred *new)
 {
-       struct file *rp, *wp;
+       struct file *files[2];
        struct fdtable *fdt;
        struct coredump_params *cp = (struct coredump_params *)info->data;
        struct files_struct *cf = current->files;
+       int err = create_pipe_files(files, 0);
+       if (err)
+               return err;
 
-       wp = create_write_pipe(0);
-       if (IS_ERR(wp))
-               return PTR_ERR(wp);
-
-       rp = create_read_pipe(wp, 0);
-       if (IS_ERR(rp)) {
-               free_write_pipe(wp);
-               return PTR_ERR(rp);
-       }
-
-       cp->file = wp;
+       cp->file = files[1];
 
        sys_close(0);
-       fd_install(0, rp);
+       fd_install(0, files[0]);
        spin_lock(&cf->file_lock);
        fdt = files_fdtable(cf);
        __set_open_fd(0, fdt);
@@ -2111,6 +2104,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
        int retval = 0;
        int flag = 0;
        int ispipe;
+       bool need_nonrelative = false;
        static atomic_t core_dump_count = ATOMIC_INIT(0);
        struct coredump_params cprm = {
                .signr = signr,
@@ -2136,14 +2130,16 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
        if (!cred)
                goto fail;
        /*
-        *      We cannot trust fsuid as being the "true" uid of the
-        *      process nor do we know its entire history. We only know it
-        *      was tainted so we dump it as root in mode 2.
+        * We cannot trust fsuid as being the "true" uid of the process
+        * nor do we know its entire history. We only know it was tainted
+        * so we dump it as root in mode 2, and only into a controlled
+        * environment (pipe handler or fully qualified path).
         */
-       if (__get_dumpable(cprm.mm_flags) == 2) {
+       if (__get_dumpable(cprm.mm_flags) == SUID_DUMPABLE_SAFE) {
                /* Setuid core dump mode */
                flag = O_EXCL;          /* Stop rewrite attacks */
                cred->fsuid = GLOBAL_ROOT_UID;  /* Dump root private */
+               need_nonrelative = true;
        }
 
        retval = coredump_wait(exit_code, &core_state);
@@ -2171,15 +2167,16 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
                }
 
                if (cprm.limit == 1) {
-                       /*
+                       /* See umh_pipe_setup() which sets RLIMIT_CORE = 1.
+                        *
                         * Normally core limits are irrelevant to pipes, since
                         * we're not writing to the file system, but we use
-                        * cprm.limit of 1 here as a speacial value. Any
-                        * non-1 limit gets set to RLIM_INFINITY below, but
-                        * a limit of 0 skips the dump.  This is a consistent
-                        * way to catch recursive crashes.  We can still crash
-                        * if the core_pattern binary sets RLIM_CORE =  !1
-                        * but it runs as root, and can do lots of stupid things
+                        * cprm.limit of 1 here as a speacial value, this is a
+                        * consistent way to catch recursive crashes.
+                        * We can still crash if the core_pattern binary sets
+                        * RLIM_CORE = !1, but it runs as root, and can do
+                        * lots of stupid things.
+                        *
                         * Note that we use task_tgid_vnr here to grab the pid
                         * of the process group leader.  That way we get the
                         * right pid if a thread in a multi-threaded
@@ -2223,6 +2220,14 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
                if (cprm.limit < binfmt->min_coredump)
                        goto fail_unlock;
 
+               if (need_nonrelative && cn.corename[0] != '/') {
+                       printk(KERN_WARNING "Pid %d(%s) can only dump core "\
+                               "to fully qualified path!\n",
+                               task_tgid_vnr(current), current->comm);
+                       printk(KERN_WARNING "Skipping core dump\n");
+                       goto fail_unlock;
+               }
+
                cprm.file = filp_open(cn.corename,
                                 O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
                                 0600);
index 1c361399886249070044987d8c2a93033202f445..376aa77f3ca7f330f2c6346053f2d1e49a990ebf 100644 (file)
@@ -1444,19 +1444,9 @@ ext2_fsblk_t ext2_new_block(struct inode *inode, unsigned long goal, int *errp)
 
 #ifdef EXT2FS_DEBUG
 
-static const int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
-
-unsigned long ext2_count_free (struct buffer_head * map, unsigned int numchars)
+unsigned long ext2_count_free(struct buffer_head *map, unsigned int numchars)
 {
-       unsigned int i;
-       unsigned long sum = 0;
-
-       if (!map)
-               return (0);
-       for (i = 0; i < numchars; i++)
-               sum += nibblemap[map->b_data[i] & 0xf] +
-                       nibblemap[(map->b_data[i] >> 4) & 0xf];
-       return (sum);
+       return numchars * BITS_PER_BYTE - memweight(map->b_data, numchars);
 }
 
 #endif  /*  EXT2FS_DEBUG  */
index c13eb7b91a1100fa108a33275905d31ae35bfb02..8f370e012e613e73016856a9cab3c6257666a1a1 100644 (file)
@@ -644,6 +644,7 @@ unsigned long ext2_count_free_inodes (struct super_block * sb)
        }
        brelse(bitmap_bh);
        printk("ext2_count_free_inodes: stored = %lu, computed = %lu, %lu\n",
+               (unsigned long)
                percpu_counter_read(&EXT2_SB(sb)->s_freeinodes_counter),
                desc_count, bitmap_count);
        return desc_count;
index 264d315f6c4753d6bee17f28ea86c6497b560449..6363ac66fafa48c1c697d993a948fa1a5ff7cf78 100644 (file)
@@ -79,6 +79,7 @@ void ext2_evict_inode(struct inode * inode)
        truncate_inode_pages(&inode->i_data, 0);
 
        if (want_delete) {
+               sb_start_intwrite(inode->i_sb);
                /* set dtime */
                EXT2_I(inode)->i_dtime  = get_seconds();
                mark_inode_dirty(inode);
@@ -98,8 +99,10 @@ void ext2_evict_inode(struct inode * inode)
        if (unlikely(rsv))
                kfree(rsv);
 
-       if (want_delete)
+       if (want_delete) {
                ext2_free_inode(inode);
+               sb_end_intwrite(inode->i_sb);
+       }
 }
 
 typedef struct {
index 9f311d27b16f5a804150e5cce66ce0b1724e7cb8..af74d9e27b71b0cf569cac645e362c174c44389e 100644 (file)
@@ -42,6 +42,8 @@ static void ext2_sync_super(struct super_block *sb,
 static int ext2_remount (struct super_block * sb, int * flags, char * data);
 static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf);
 static int ext2_sync_fs(struct super_block *sb, int wait);
+static int ext2_freeze(struct super_block *sb);
+static int ext2_unfreeze(struct super_block *sb);
 
 void ext2_error(struct super_block *sb, const char *function,
                const char *fmt, ...)
@@ -305,6 +307,8 @@ static const struct super_operations ext2_sops = {
        .evict_inode    = ext2_evict_inode,
        .put_super      = ext2_put_super,
        .sync_fs        = ext2_sync_fs,
+       .freeze_fs      = ext2_freeze,
+       .unfreeze_fs    = ext2_unfreeze,
        .statfs         = ext2_statfs,
        .remount_fs     = ext2_remount,
        .show_options   = ext2_show_options,
@@ -1200,6 +1204,35 @@ static int ext2_sync_fs(struct super_block *sb, int wait)
        return 0;
 }
 
+static int ext2_freeze(struct super_block *sb)
+{
+       struct ext2_sb_info *sbi = EXT2_SB(sb);
+
+       /*
+        * Open but unlinked files present? Keep EXT2_VALID_FS flag cleared
+        * because we have unattached inodes and thus filesystem is not fully
+        * consistent.
+        */
+       if (atomic_long_read(&sb->s_remove_count)) {
+               ext2_sync_fs(sb, 1);
+               return 0;
+       }
+       /* Set EXT2_FS_VALID flag */
+       spin_lock(&sbi->s_lock);
+       sbi->s_es->s_state = cpu_to_le16(sbi->s_mount_state);
+       spin_unlock(&sbi->s_lock);
+       ext2_sync_super(sb, sbi->s_es, 1);
+
+       return 0;
+}
+
+static int ext2_unfreeze(struct super_block *sb)
+{
+       /* Just write sb to clear EXT2_VALID_FS flag */
+       ext2_write_super(sb);
+
+       return 0;
+}
 
 void ext2_write_super(struct super_block *sb)
 {
index 25cd6089211657839f8f94a68bc075313cd92e2f..90d901f0486b49f01fbfea197b5c0b9f072e280b 100644 (file)
@@ -1813,7 +1813,7 @@ ext3_fsblk_t ext3_count_free_blocks(struct super_block *sb)
        brelse(bitmap_bh);
        printk("ext3_count_free_blocks: stored = "E3FSBLK
                ", computed = "E3FSBLK", "E3FSBLK"\n",
-              le32_to_cpu(es->s_free_blocks_count),
+              (ext3_fsblk_t)le32_to_cpu(es->s_free_blocks_count),
                desc_count, bitmap_count);
        return bitmap_count;
 #else
index 909d13e265603dd24e3e1c2d143e87f7beda4202..ef9c643e8e9d5ff75703227ec9f81f0684340246 100644 (file)
 
 #ifdef EXT3FS_DEBUG
 
-static const int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
-
 unsigned long ext3_count_free (struct buffer_head * map, unsigned int numchars)
 {
-       unsigned int i;
-       unsigned long sum = 0;
-
-       if (!map)
-               return (0);
-       for (i = 0; i < numchars; i++)
-               sum += nibblemap[map->b_data[i] & 0xf] +
-                       nibblemap[(map->b_data[i] >> 4) & 0xf];
-       return (sum);
+       return numchars * BITS_PER_BYTE - memweight(map->b_data, numchars);
 }
 
 #endif  /*  EXT3FS_DEBUG  */
index a94b9c63ee5c4a32f44916d6ecb628b9a070df04..f8716eab99952401dc473deb6619295b4a830f3b 100644 (file)
 #include <linux/jbd2.h>
 #include "ext4.h"
 
-static const int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
-
 unsigned int ext4_count_free(char *bitmap, unsigned int numchars)
 {
-       unsigned int i, sum = 0;
-
-       for (i = 0; i < numchars; i++)
-               sum += nibblemap[bitmap[i] & 0xf] +
-                       nibblemap[(bitmap[i] >> 4) & 0xf];
-       return sum;
+       return numchars * BITS_PER_BYTE - memweight(bitmap, numchars);
 }
 
 int ext4_inode_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,
index 89b59cb7f9b89cc47003295db7c2d69766e62b97..6324f74e03424e9ac21ceb01f6fbee07caa546df 100644 (file)
@@ -233,6 +233,11 @@ void ext4_evict_inode(struct inode *inode)
        if (is_bad_inode(inode))
                goto no_delete;
 
+       /*
+        * Protect us against freezing - iput() caller didn't have to have any
+        * protection against it
+        */
+       sb_start_intwrite(inode->i_sb);
        handle = ext4_journal_start(inode, ext4_blocks_for_truncate(inode)+3);
        if (IS_ERR(handle)) {
                ext4_std_error(inode->i_sb, PTR_ERR(handle));
@@ -242,6 +247,7 @@ void ext4_evict_inode(struct inode *inode)
                 * cleaned up.
                 */
                ext4_orphan_del(NULL, inode);
+               sb_end_intwrite(inode->i_sb);
                goto no_delete;
        }
 
@@ -273,6 +279,7 @@ void ext4_evict_inode(struct inode *inode)
                stop_handle:
                        ext4_journal_stop(handle);
                        ext4_orphan_del(NULL, inode);
+                       sb_end_intwrite(inode->i_sb);
                        goto no_delete;
                }
        }
@@ -301,6 +308,7 @@ void ext4_evict_inode(struct inode *inode)
        else
                ext4_free_inode(handle, inode);
        ext4_journal_stop(handle);
+       sb_end_intwrite(inode->i_sb);
        return;
 no_delete:
        ext4_clear_inode(inode);        /* We must guarantee clearing of inode... */
@@ -4779,11 +4787,7 @@ int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
        get_block_t *get_block;
        int retries = 0;
 
-       /*
-        * This check is racy but catches the common case. We rely on
-        * __block_page_mkwrite() to do a reliable check.
-        */
-       vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+       sb_start_pagefault(inode->i_sb);
        /* Delalloc case is easy... */
        if (test_opt(inode->i_sb, DELALLOC) &&
            !ext4_should_journal_data(inode) &&
@@ -4851,5 +4855,6 @@ retry_alloc:
 out_ret:
        ret = block_page_mkwrite_return(ret);
 out:
+       sb_end_pagefault(inode->i_sb);
        return ret;
 }
index f99a1311e84765296b0a0a04534e0be0536915bc..fe7c63f4717e09616b92f7d3b4850ab99671cff6 100644 (file)
@@ -44,6 +44,11 @@ static int write_mmp_block(struct super_block *sb, struct buffer_head *bh)
 {
        struct mmp_struct *mmp = (struct mmp_struct *)(bh->b_data);
 
+       /*
+        * We protect against freezing so that we don't create dirty buffers
+        * on frozen filesystem.
+        */
+       sb_start_write(sb);
        ext4_mmp_csum_set(sb, mmp);
        mark_buffer_dirty(bh);
        lock_buffer(bh);
@@ -51,6 +56,7 @@ static int write_mmp_block(struct super_block *sb, struct buffer_head *bh)
        get_bh(bh);
        submit_bh(WRITE_SYNC, bh);
        wait_on_buffer(bh);
+       sb_end_write(sb);
        if (unlikely(!buffer_uptodate(bh)))
                return 1;
 
index 2d51cd9af22559394e1aa9ce303922dd64ccc083..d76ec8277d3fca660e619d097711cc0057349823 100644 (file)
@@ -331,33 +331,17 @@ static void ext4_put_nojournal(handle_t *handle)
  * journal_end calls result in the superblock being marked dirty, so
  * that sync() will call the filesystem's write_super callback if
  * appropriate.
- *
- * To avoid j_barrier hold in userspace when a user calls freeze(),
- * ext4 prevents a new handle from being started by s_frozen, which
- * is in an upper layer.
  */
 handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
 {
        journal_t *journal;
-       handle_t  *handle;
 
        trace_ext4_journal_start(sb, nblocks, _RET_IP_);
        if (sb->s_flags & MS_RDONLY)
                return ERR_PTR(-EROFS);
 
+       WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE);
        journal = EXT4_SB(sb)->s_journal;
-       handle = ext4_journal_current_handle();
-
-       /*
-        * If a handle has been started, it should be allowed to
-        * finish, otherwise deadlock could happen between freeze
-        * and others(e.g. truncate) due to the restart of the
-        * journal handle if the filesystem is forzen and active
-        * handles are not stopped.
-        */
-       if (!handle)
-               vfs_check_frozen(sb, SB_FREEZE_TRANS);
-
        if (!journal)
                return ext4_get_nojournal();
        /*
@@ -2747,6 +2731,7 @@ static int ext4_run_li_request(struct ext4_li_request *elr)
        sb = elr->lr_super;
        ngroups = EXT4_SB(sb)->s_groups_count;
 
+       sb_start_write(sb);
        for (group = elr->lr_next_group; group < ngroups; group++) {
                gdp = ext4_get_group_desc(sb, group, NULL);
                if (!gdp) {
@@ -2773,6 +2758,7 @@ static int ext4_run_li_request(struct ext4_li_request *elr)
                elr->lr_next_sched = jiffies + elr->lr_timeout;
                elr->lr_next_group = group + 1;
        }
+       sb_end_write(sb);
 
        return ret;
 }
@@ -4460,10 +4446,8 @@ int ext4_force_commit(struct super_block *sb)
                return 0;
 
        journal = EXT4_SB(sb)->s_journal;
-       if (journal) {
-               vfs_check_frozen(sb, SB_FREEZE_TRANS);
+       if (journal)
                ret = ext4_journal_force_commit(journal);
-       }
 
        return ret;
 }
@@ -4493,9 +4477,8 @@ static int ext4_sync_fs(struct super_block *sb, int wait)
  * gives us a chance to flush the journal completely and mark the fs clean.
  *
  * Note that only this function cannot bring a filesystem to be in a clean
- * state independently, because ext4 prevents a new handle from being started
- * by @sb->s_frozen, which stays in an upper layer.  It thus needs help from
- * the upper layer.
+ * state independently. It relies on upper layer to stop all data & metadata
+ * modifications.
  */
 static int ext4_freeze(struct super_block *sb)
 {
@@ -4522,7 +4505,7 @@ static int ext4_freeze(struct super_block *sb)
        EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER);
        error = ext4_commit_super(sb, 1);
 out:
-       /* we rely on s_frozen to stop further updates */
+       /* we rely on upper layer to stop further updates */
        jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
        return error;
 }
index 6eaa28c98ad1e9038dd939f2960d31034d8663a3..dc49ed2cbffa66af9407f89886d8d84cf2dc9d0f 100644 (file)
 #define FAT_MAX_UNI_CHARS      ((MSDOS_SLOTS - 1) * 13 + 1)
 #define FAT_MAX_UNI_SIZE       (FAT_MAX_UNI_CHARS * sizeof(wchar_t))
 
+static inline unsigned char fat_tolower(unsigned char c)
+{
+       return ((c >= 'A') && (c <= 'Z')) ? c+32 : c;
+}
+
 static inline loff_t fat_make_i_pos(struct super_block *sb,
                                    struct buffer_head *bh,
                                    struct msdos_dir_entry *de)
@@ -333,6 +338,124 @@ parse_long:
        return 0;
 }
 
+/**
+ * fat_parse_short - Parse MS-DOS (short) directory entry.
+ * @sb:                superblock
+ * @de:                directory entry to parse
+ * @name:      FAT_MAX_SHORT_SIZE array in which to place extracted name
+ * @dot_hidden:        Nonzero == prepend '.' to names with ATTR_HIDDEN
+ *
+ * Returns the number of characters extracted into 'name'.
+ */
+static int fat_parse_short(struct super_block *sb,
+                          const struct msdos_dir_entry *de,
+                          unsigned char *name, int dot_hidden)
+{
+       const struct msdos_sb_info *sbi = MSDOS_SB(sb);
+       int isvfat = sbi->options.isvfat;
+       int nocase = sbi->options.nocase;
+       unsigned short opt_shortname = sbi->options.shortname;
+       struct nls_table *nls_disk = sbi->nls_disk;
+       wchar_t uni_name[14];
+       unsigned char c, work[MSDOS_NAME];
+       unsigned char *ptname = name;
+       int chi, chl, i, j, k;
+       int dotoffset = 0;
+       int name_len = 0, uni_len = 0;
+
+       if (!isvfat && dot_hidden && (de->attr & ATTR_HIDDEN)) {
+               *ptname++ = '.';
+               dotoffset = 1;
+       }
+
+       memcpy(work, de->name, sizeof(work));
+       /* see namei.c, msdos_format_name */
+       if (work[0] == 0x05)
+               work[0] = 0xE5;
+
+       /* Filename */
+       for (i = 0, j = 0; i < 8;) {
+               c = work[i];
+               if (!c)
+                       break;
+               chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
+                                       &uni_name[j++], opt_shortname,
+                                       de->lcase & CASE_LOWER_BASE);
+               if (chl <= 1) {
+                       if (!isvfat)
+                               ptname[i] = nocase ? c : fat_tolower(c);
+                       i++;
+                       if (c != ' ') {
+                               name_len = i;
+                               uni_len  = j;
+                       }
+               } else {
+                       uni_len = j;
+                       if (isvfat)
+                               i += min(chl, 8-i);
+                       else {
+                               for (chi = 0; chi < chl && i < 8; chi++, i++)
+                                       ptname[i] = work[i];
+                       }
+                       if (chl)
+                               name_len = i;
+               }
+       }
+
+       i = name_len;
+       j = uni_len;
+       fat_short2uni(nls_disk, ".", 1, &uni_name[j++]);
+       if (!isvfat)
+               ptname[i] = '.';
+       i++;
+
+       /* Extension */
+       for (k = 8; k < MSDOS_NAME;) {
+               c = work[k];
+               if (!c)
+                       break;
+               chl = fat_shortname2uni(nls_disk, &work[k], MSDOS_NAME - k,
+                                       &uni_name[j++], opt_shortname,
+                                       de->lcase & CASE_LOWER_EXT);
+               if (chl <= 1) {
+                       k++;
+                       if (!isvfat)
+                               ptname[i] = nocase ? c : fat_tolower(c);
+                       i++;
+                       if (c != ' ') {
+                               name_len = i;
+                               uni_len  = j;
+                       }
+               } else {
+                       uni_len = j;
+                       if (isvfat) {
+                               int offset = min(chl, MSDOS_NAME-k);
+                               k += offset;
+                               i += offset;
+                       } else {
+                               for (chi = 0; chi < chl && k < MSDOS_NAME;
+                                    chi++, i++, k++) {
+                                               ptname[i] = work[k];
+                               }
+                       }
+                       if (chl)
+                               name_len = i;
+               }
+       }
+
+       if (name_len > 0) {
+               name_len += dotoffset;
+
+               if (sbi->options.isvfat) {
+                       uni_name[uni_len] = 0x0000;
+                       name_len = fat_uni_to_x8(sb, uni_name, name,
+                                                FAT_MAX_SHORT_SIZE);
+               }
+       }
+
+       return name_len;
+}
+
 /*
  * Return values: negative -> error, 0 -> not found, positive -> found,
  * value is the total amount of slots, including the shortname entry.
@@ -344,15 +467,11 @@ int fat_search_long(struct inode *inode, const unsigned char *name,
        struct msdos_sb_info *sbi = MSDOS_SB(sb);
        struct buffer_head *bh = NULL;
        struct msdos_dir_entry *de;
-       struct nls_table *nls_disk = sbi->nls_disk;
        unsigned char nr_slots;
-       wchar_t bufuname[14];
        wchar_t *unicode = NULL;
-       unsigned char work[MSDOS_NAME];
        unsigned char bufname[FAT_MAX_SHORT_SIZE];
-       unsigned short opt_shortname = sbi->options.shortname;
        loff_t cpos = 0;
-       int chl, i, j, last_u, err, len;
+       int err, len;
 
        err = -ENOENT;
        while (1) {
@@ -380,47 +499,16 @@ parse_record:
                                goto end_of_dir;
                }
 
-               memcpy(work, de->name, sizeof(de->name));
-               /* see namei.c, msdos_format_name */
-               if (work[0] == 0x05)
-                       work[0] = 0xE5;
-               for (i = 0, j = 0, last_u = 0; i < 8;) {
-                       if (!work[i])
-                               break;
-                       chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
-                                               &bufuname[j++], opt_shortname,
-                                               de->lcase & CASE_LOWER_BASE);
-                       if (chl <= 1) {
-                               if (work[i] != ' ')
-                                       last_u = j;
-                       } else {
-                               last_u = j;
-                       }
-                       i += chl;
-               }
-               j = last_u;
-               fat_short2uni(nls_disk, ".", 1, &bufuname[j++]);
-               for (i = 8; i < MSDOS_NAME;) {
-                       if (!work[i])
-                               break;
-                       chl = fat_shortname2uni(nls_disk, &work[i],
-                                               MSDOS_NAME - i,
-                                               &bufuname[j++], opt_shortname,
-                                               de->lcase & CASE_LOWER_EXT);
-                       if (chl <= 1) {
-                               if (work[i] != ' ')
-                                       last_u = j;
-                       } else {
-                               last_u = j;
-                       }
-                       i += chl;
-               }
-               if (!last_u)
+               /* Never prepend '.' to hidden files here.
+                * That is done only for msdos mounts (and only when
+                * 'dotsOK=yes'); if we are executing here, it is in the
+                * context of a vfat mount.
+                */
+               len = fat_parse_short(sb, de, bufname, 0);
+               if (len == 0)
                        continue;
 
                /* Compare shortname */
-               bufuname[last_u] = 0x0000;
-               len = fat_uni_to_x8(sb, bufuname, bufname, sizeof(bufname));
                if (fat_name_match(sbi, name, name_len, bufname, len))
                        goto found;
 
@@ -469,20 +557,15 @@ static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent,
        struct msdos_sb_info *sbi = MSDOS_SB(sb);
        struct buffer_head *bh;
        struct msdos_dir_entry *de;
-       struct nls_table *nls_disk = sbi->nls_disk;
        unsigned char nr_slots;
-       wchar_t bufuname[14];
        wchar_t *unicode = NULL;
-       unsigned char c, work[MSDOS_NAME];
-       unsigned char bufname[FAT_MAX_SHORT_SIZE], *ptname = bufname;
-       unsigned short opt_shortname = sbi->options.shortname;
+       unsigned char bufname[FAT_MAX_SHORT_SIZE];
        int isvfat = sbi->options.isvfat;
-       int nocase = sbi->options.nocase;
        const char *fill_name = NULL;
        unsigned long inum;
        unsigned long lpos, dummy, *furrfu = &lpos;
        loff_t cpos;
-       int chi, chl, i, i2, j, last, last_u, dotoffset = 0, fill_len = 0;
+       int short_len = 0, fill_len = 0;
        int ret = 0;
 
        lock_super(sb);
@@ -556,74 +639,10 @@ parse_record:
                }
        }
 
-       if (sbi->options.dotsOK) {
-               ptname = bufname;
-               dotoffset = 0;
-               if (de->attr & ATTR_HIDDEN) {
-                       *ptname++ = '.';
-                       dotoffset = 1;
-               }
-       }
-
-       memcpy(work, de->name, sizeof(de->name));
-       /* see namei.c, msdos_format_name */
-       if (work[0] == 0x05)
-               work[0] = 0xE5;
-       for (i = 0, j = 0, last = 0, last_u = 0; i < 8;) {
-               if (!(c = work[i]))
-                       break;
-               chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
-                                       &bufuname[j++], opt_shortname,
-                                       de->lcase & CASE_LOWER_BASE);
-               if (chl <= 1) {
-                       ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c;
-                       if (c != ' ') {
-                               last = i;
-                               last_u = j;
-                       }
-               } else {
-                       last_u = j;
-                       for (chi = 0; chi < chl && i < 8; chi++) {
-                               ptname[i] = work[i];
-                               i++; last = i;
-                       }
-               }
-       }
-       i = last;
-       j = last_u;
-       fat_short2uni(nls_disk, ".", 1, &bufuname[j++]);
-       ptname[i++] = '.';
-       for (i2 = 8; i2 < MSDOS_NAME;) {
-               if (!(c = work[i2]))
-                       break;
-               chl = fat_shortname2uni(nls_disk, &work[i2], MSDOS_NAME - i2,
-                                       &bufuname[j++], opt_shortname,
-                                       de->lcase & CASE_LOWER_EXT);
-               if (chl <= 1) {
-                       i2++;
-                       ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c;
-                       if (c != ' ') {
-                               last = i;
-                               last_u = j;
-                       }
-               } else {
-                       last_u = j;
-                       for (chi = 0; chi < chl && i2 < MSDOS_NAME; chi++) {
-                               ptname[i++] = work[i2++];
-                               last = i;
-                       }
-               }
-       }
-       if (!last)
+       short_len = fat_parse_short(sb, de, bufname, sbi->options.dotsOK);
+       if (short_len == 0)
                goto record_end;
 
-       i = last + dotoffset;
-       j = last_u;
-
-       if (isvfat) {
-               bufuname[j] = 0x0000;
-               i = fat_uni_to_x8(sb, bufuname, bufname, sizeof(bufname));
-       }
        if (nr_slots) {
                /* hack for fat_ioctl_filldir() */
                struct fat_ioctl_filldir_callback *p = dirent;
@@ -631,12 +650,12 @@ parse_record:
                p->longname = fill_name;
                p->long_len = fill_len;
                p->shortname = bufname;
-               p->short_len = i;
+               p->short_len = short_len;
                fill_name = NULL;
                fill_len = 0;
        } else {
                fill_name = bufname;
-               fill_len = i;
+               fill_len = short_len;
        }
 
 start_filldir:
index fc35c5c69136e805b41ffa6102dc1878d68f7a3f..2deeeb86f331c8ad70d56300f1b5d4d5b155fe7b 100644 (file)
@@ -217,6 +217,21 @@ static inline void fat16_towchar(wchar_t *dst, const __u8 *src, size_t len)
 #endif
 }
 
+static inline int fat_get_start(const struct msdos_sb_info *sbi,
+                               const struct msdos_dir_entry *de)
+{
+       int cluster = le16_to_cpu(de->start);
+       if (sbi->fat_bits == 32)
+               cluster |= (le16_to_cpu(de->starthi) << 16);
+       return cluster;
+}
+
+static inline void fat_set_start(struct msdos_dir_entry *de, int cluster)
+{
+       de->start   = cpu_to_le16(cluster);
+       de->starthi = cpu_to_le16(cluster >> 16);
+}
+
 static inline void fatwchar_to16(__u8 *dst, const wchar_t *src, size_t len)
 {
 #ifdef __BIG_ENDIAN
index a71fe3715ee818b016fdc956e061399d734725d0..e007b8bd8e5ec1d93f29c65f877e083d3ec10b6e 100644 (file)
@@ -43,10 +43,10 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
        if (err)
                goto out;
 
-       mutex_lock(&inode->i_mutex);
        err = mnt_want_write_file(file);
        if (err)
-               goto out_unlock_inode;
+               goto out;
+       mutex_lock(&inode->i_mutex);
 
        /*
         * ATTR_VOLUME and ATTR_DIR cannot be changed; this also
@@ -73,14 +73,14 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
        /* The root directory has no attributes */
        if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) {
                err = -EINVAL;
-               goto out_drop_write;
+               goto out_unlock_inode;
        }
 
        if (sbi->options.sys_immutable &&
            ((attr | oldattr) & ATTR_SYS) &&
            !capable(CAP_LINUX_IMMUTABLE)) {
                err = -EPERM;
-               goto out_drop_write;
+               goto out_unlock_inode;
        }
 
        /*
@@ -90,12 +90,12 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
         */
        err = security_inode_setattr(file->f_path.dentry, &ia);
        if (err)
-               goto out_drop_write;
+               goto out_unlock_inode;
 
        /* This MUST be done before doing anything irreversible... */
        err = fat_setattr(file->f_path.dentry, &ia);
        if (err)
-               goto out_drop_write;
+               goto out_unlock_inode;
 
        fsnotify_change(file->f_path.dentry, ia.ia_valid);
        if (sbi->options.sys_immutable) {
@@ -107,10 +107,9 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
 
        fat_save_attrs(inode, attr);
        mark_inode_dirty(inode);
-out_drop_write:
-       mnt_drop_write_file(file);
 out_unlock_inode:
        mutex_unlock(&inode->i_mutex);
+       mnt_drop_write_file(file);
 out:
        return err;
 }
index 0038b32cb36276d537f2ec81a46469bf3ad221b1..05e897fe9866c49fb96fa09459240bf7a550586c 100644 (file)
@@ -369,10 +369,7 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
                inode->i_op = sbi->dir_ops;
                inode->i_fop = &fat_dir_operations;
 
-               MSDOS_I(inode)->i_start = le16_to_cpu(de->start);
-               if (sbi->fat_bits == 32)
-                       MSDOS_I(inode)->i_start |= (le16_to_cpu(de->starthi) << 16);
-
+               MSDOS_I(inode)->i_start = fat_get_start(sbi, de);
                MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
                error = fat_calc_dir_size(inode);
                if (error < 0)
@@ -385,9 +382,7 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
                inode->i_mode = fat_make_mode(sbi, de->attr,
                        ((sbi->options.showexec && !is_exec(de->name + 8))
                         ? S_IRUGO|S_IWUGO : S_IRWXUGO));
-               MSDOS_I(inode)->i_start = le16_to_cpu(de->start);
-               if (sbi->fat_bits == 32)
-                       MSDOS_I(inode)->i_start |= (le16_to_cpu(de->starthi) << 16);
+               MSDOS_I(inode)->i_start = fat_get_start(sbi, de);
 
                MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
                inode->i_size = le32_to_cpu(de->size);
@@ -613,8 +608,7 @@ retry:
        else
                raw_entry->size = cpu_to_le32(inode->i_size);
        raw_entry->attr = fat_make_attrs(inode);
-       raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart);
-       raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16);
+       fat_set_start(raw_entry, MSDOS_I(inode)->i_logstart);
        fat_time_unix2fat(sbi, &inode->i_mtime, &raw_entry->time,
                          &raw_entry->date, NULL);
        if (sbi->options.isvfat) {
index 70d993a9380572bac393a45f3dcb9896db404f9e..b0e12bf9f4a1c0fc32dc42facffaf84ff75fe880 100644 (file)
@@ -246,8 +246,7 @@ static int msdos_add_entry(struct inode *dir, const unsigned char *name,
        de.ctime_cs = 0;
        de.time = time;
        de.date = date;
-       de.start = cpu_to_le16(cluster);
-       de.starthi = cpu_to_le16(cluster >> 16);
+       fat_set_start(&de, cluster);
        de.size = 0;
 
        err = fat_add_entries(dir, &de, 1, sinfo);
@@ -530,9 +529,7 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
                mark_inode_dirty(old_inode);
 
        if (update_dotdot) {
-               int start = MSDOS_I(new_dir)->i_logstart;
-               dotdot_de->start = cpu_to_le16(start);
-               dotdot_de->starthi = cpu_to_le16(start >> 16);
+               fat_set_start(dotdot_de, MSDOS_I(new_dir)->i_logstart);
                mark_buffer_dirty_inode(dotdot_bh, old_inode);
                if (IS_DIRSYNC(new_dir)) {
                        err = sync_dirty_buffer(dotdot_bh);
@@ -572,9 +569,7 @@ error_dotdot:
        corrupt = 1;
 
        if (update_dotdot) {
-               int start = MSDOS_I(old_dir)->i_logstart;
-               dotdot_de->start = cpu_to_le16(start);
-               dotdot_de->starthi = cpu_to_le16(start >> 16);
+               fat_set_start(dotdot_de, MSDOS_I(old_dir)->i_logstart);
                mark_buffer_dirty_inode(dotdot_bh, old_inode);
                corrupt |= sync_dirty_buffer(dotdot_bh);
        }
index 6cc480652433b66a52c42cbcd3bde3f19e348f30..6a6d8c0715a1c16daf2b1ac53e8d461eb2253810 100644 (file)
@@ -651,8 +651,7 @@ shortname:
        de->time = de->ctime = time;
        de->date = de->cdate = de->adate = date;
        de->ctime_cs = time_cs;
-       de->start = cpu_to_le16(cluster);
-       de->starthi = cpu_to_le16(cluster >> 16);
+       fat_set_start(de, cluster);
        de->size = 0;
 out_free:
        __putname(uname);
@@ -965,9 +964,7 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
                mark_inode_dirty(old_inode);
 
        if (update_dotdot) {
-               int start = MSDOS_I(new_dir)->i_logstart;
-               dotdot_de->start = cpu_to_le16(start);
-               dotdot_de->starthi = cpu_to_le16(start >> 16);
+               fat_set_start(dotdot_de, MSDOS_I(new_dir)->i_logstart);
                mark_buffer_dirty_inode(dotdot_bh, old_inode);
                if (IS_DIRSYNC(new_dir)) {
                        err = sync_dirty_buffer(dotdot_bh);
@@ -1009,9 +1006,7 @@ error_dotdot:
        corrupt = 1;
 
        if (update_dotdot) {
-               int start = MSDOS_I(old_dir)->i_logstart;
-               dotdot_de->start = cpu_to_le16(start);
-               dotdot_de->starthi = cpu_to_le16(start >> 16);
+               fat_set_start(dotdot_de, MSDOS_I(old_dir)->i_logstart);
                mark_buffer_dirty_inode(dotdot_bh, old_inode);
                corrupt |= sync_dirty_buffer(dotdot_bh);
        }
index 81b70e665bf000412f73aa300890a53823db36f0..887b5ba8c9b56be800d4d3f7a799fc21f1c7892c 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/signal.h>
 #include <linux/rcupdate.h>
 #include <linux/pid_namespace.h>
+#include <linux/user_namespace.h>
 
 #include <asm/poll.h>
 #include <asm/siginfo.h>
@@ -340,6 +341,31 @@ static int f_getown_ex(struct file *filp, unsigned long arg)
        return ret;
 }
 
+#ifdef CONFIG_CHECKPOINT_RESTORE
+static int f_getowner_uids(struct file *filp, unsigned long arg)
+{
+       struct user_namespace *user_ns = current_user_ns();
+       uid_t * __user dst = (void * __user)arg;
+       uid_t src[2];
+       int err;
+
+       read_lock(&filp->f_owner.lock);
+       src[0] = from_kuid(user_ns, filp->f_owner.uid);
+       src[1] = from_kuid(user_ns, filp->f_owner.euid);
+       read_unlock(&filp->f_owner.lock);
+
+       err  = put_user(src[0], &dst[0]);
+       err |= put_user(src[1], &dst[1]);
+
+       return err;
+}
+#else
+static int f_getowner_uids(struct file *filp, unsigned long arg)
+{
+       return -EINVAL;
+}
+#endif
+
 static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
                struct file *filp)
 {
@@ -396,6 +422,9 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
        case F_SETOWN_EX:
                err = f_setown_ex(filp, arg);
                break;
+       case F_GETOWNER_UIDS:
+               err = f_getowner_uids(filp, arg);
+               break;
        case F_GETSIG:
                err = filp->f_owner.signum;
                break;
index b3fc4d67a26b31243474520bfbcddacce3733ded..701985e4ccda4fc5afc05977f511bd6ca11ba3af 100644 (file)
@@ -43,7 +43,7 @@ static struct kmem_cache *filp_cachep __read_mostly;
 
 static struct percpu_counter nr_files __cacheline_aligned_in_smp;
 
-static inline void file_free_rcu(struct rcu_head *head)
+static void file_free_rcu(struct rcu_head *head)
 {
        struct file *f = container_of(head, struct file, f_u.fu_rcuhead);
 
@@ -217,7 +217,7 @@ static void drop_file_write_access(struct file *file)
                return;
        if (file_check_writeable(file) != 0)
                return;
-       mnt_drop_write(mnt);
+       __mnt_drop_write(mnt);
        file_release_write(file);
 }
 
index 8f660dd6137a1105ba648e1a1412ffc5900c1233..be3efc4f64f4b8c556e1d6de5c2ff400f05731ab 100644 (file)
@@ -52,11 +52,6 @@ struct wb_writeback_work {
        struct completion *done;        /* set if the caller waits */
 };
 
-/*
- * We don't actually have pdflush, but this one is exported though /proc...
- */
-int nr_pdflush_threads;
-
 /**
  * writeback_in_progress - determine whether there is writeback in progress
  * @bdi: the device's backing_dev_info structure.
@@ -628,8 +623,8 @@ static long writeback_sb_inodes(struct super_block *sb,
                }
 
                /*
-                * Don't bother with new inodes or inodes beeing freed, first
-                * kind does not need peridic writeout yet, and for the latter
+                * Don't bother with new inodes or inodes being freed, first
+                * kind does not need periodic writeout yet, and for the latter
                 * kind writeout is handled by the freer.
                 */
                spin_lock(&inode->i_lock);
index b321a688cde79aa0f2923f3440330d03f0e5ee1e..93d8d6c9494dbd5737b0842a3ebb55ef972e0d87 100644 (file)
@@ -944,9 +944,8 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
                return err;
 
        count = ocount;
-
+       sb_start_write(inode->i_sb);
        mutex_lock(&inode->i_mutex);
-       vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
 
        /* We can write back this queue in page reclaim */
        current->backing_dev_info = mapping->backing_dev_info;
@@ -1004,6 +1003,7 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
 out:
        current->backing_dev_info = NULL;
        mutex_unlock(&inode->i_mutex);
+       sb_end_write(inode->i_sb);
 
        return written ? written : err;
 }
index 9aa6af13823c5b75d3e8d2723d281dc79cee7a47..d1d791ef38de2188852551254a63f974a605feff 100644 (file)
@@ -373,11 +373,10 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
        loff_t size;
        int ret;
 
-       /* Wait if fs is frozen. This is racy so we check again later on
-        * and retry if the fs has been frozen after the page lock has
-        * been acquired
-        */
-       vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+       sb_start_pagefault(inode->i_sb);
+
+       /* Update file times before taking page lock */
+       file_update_time(vma->vm_file);
 
        ret = gfs2_rs_alloc(ip);
        if (ret)
@@ -462,14 +461,9 @@ out:
        gfs2_holder_uninit(&gh);
        if (ret == 0) {
                set_page_dirty(page);
-               /* This check must be post dropping of transaction lock */
-               if (inode->i_sb->s_frozen == SB_UNFROZEN) {
-                       wait_on_page_writeback(page);
-               } else {
-                       ret = -EAGAIN;
-                       unlock_page(page);
-               }
+               wait_on_page_writeback(page);
        }
+       sb_end_pagefault(inode->i_sb);
        return block_page_mkwrite_return(ret);
 }
 
index ad3e2fb763d73792d048ac0c86915be90653e1f3..adbd27875ef957e5b54846d8494865d51ff156be 100644 (file)
@@ -50,6 +50,7 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
        if (revokes)
                tr->tr_reserved += gfs2_struct2blk(sdp, revokes,
                                                   sizeof(u64));
+       sb_start_intwrite(sdp->sd_vfs);
        gfs2_holder_init(sdp->sd_trans_gl, LM_ST_SHARED, 0, &tr->tr_t_gh);
 
        error = gfs2_glock_nq(&tr->tr_t_gh);
@@ -68,6 +69,7 @@ fail_gunlock:
        gfs2_glock_dq(&tr->tr_t_gh);
 
 fail_holder_uninit:
+       sb_end_intwrite(sdp->sd_vfs);
        gfs2_holder_uninit(&tr->tr_t_gh);
        kfree(tr);
 
@@ -116,6 +118,7 @@ void gfs2_trans_end(struct gfs2_sbd *sdp)
                        gfs2_holder_uninit(&tr->tr_t_gh);
                        kfree(tr);
                }
+               sb_end_intwrite(sdp->sd_vfs);
                return;
        }
 
@@ -136,6 +139,7 @@ void gfs2_trans_end(struct gfs2_sbd *sdp)
 
        if (sdp->sd_vfs->s_flags & MS_SYNCHRONOUS)
                gfs2_log_flush(sdp, NULL);
+       sb_end_intwrite(sdp->sd_vfs);
 }
 
 /**
index 47333209801378c5cbd5baef0c948c1767a4fa2a..fdafb2d71654740776bd1b77931b955da350ecad 100644 (file)
@@ -365,7 +365,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
        u64 last_fs_block, last_fs_page;
        int err;
 
-       err = -EINVAL;
+       err = -ENOMEM;
        sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
        if (!sbi)
                goto out;
index e13e9bdb0bf57f2cdfe03fc1ad013826e1668d41..8349a899912e5c47ca26c66df1be66c684bdce4d 100644 (file)
@@ -416,8 +416,8 @@ hugetlb_vmtruncate_list(struct prio_tree_root *root, pgoff_t pgoff)
                else
                        v_offset = 0;
 
-               __unmap_hugepage_range(vma,
-                               vma->vm_start + v_offset, vma->vm_end, NULL);
+               unmap_hugepage_range(vma, vma->vm_start + v_offset,
+                                    vma->vm_end, NULL);
        }
 }
 
index 3cc50432046796df30834106966c4a295294b7fb..ac8d904b3f1624bfa945ba088d2909e740e16a6c 100644 (file)
@@ -1542,9 +1542,11 @@ void touch_atime(struct path *path)
        if (timespec_equal(&inode->i_atime, &now))
                return;
 
-       if (mnt_want_write(mnt))
+       if (!sb_start_write_trylock(inode->i_sb))
                return;
 
+       if (__mnt_want_write(mnt))
+               goto skip_update;
        /*
         * File systems can error out when updating inodes if they need to
         * allocate new space to modify an inode (such is the case for
@@ -1555,7 +1557,9 @@ void touch_atime(struct path *path)
         * of the fs read only, e.g. subvolumes in Btrfs.
         */
        update_time(inode, &now, S_ATIME);
-       mnt_drop_write(mnt);
+       __mnt_drop_write(mnt);
+skip_update:
+       sb_end_write(inode->i_sb);
 }
 EXPORT_SYMBOL(touch_atime);
 
@@ -1662,11 +1666,11 @@ int file_update_time(struct file *file)
                return 0;
 
        /* Finally allowed to write? Takes lock. */
-       if (mnt_want_write_file(file))
+       if (__mnt_want_write_file(file))
                return 0;
 
        ret = update_time(inode, &now, sync_it);
-       mnt_drop_write_file(file);
+       __mnt_drop_write_file(file);
 
        return ret;
 }
index a6fd56c68b1164928d380fdc787a988107c15a55..371bcc4b1697df808e506a6f88735209dec17dd8 100644 (file)
@@ -61,6 +61,10 @@ extern void __init mnt_init(void);
 
 extern struct lglock vfsmount_lock;
 
+extern int __mnt_want_write(struct vfsmount *);
+extern int __mnt_want_write_file(struct file *);
+extern void __mnt_drop_write(struct vfsmount *);
+extern void __mnt_drop_write_file(struct file *);
 
 /*
  * fs_struct.c
index 8392cb85bd54401d27028f0ed2932dceb4dd98bf..05d29124c6ab4150c9a5381cbc98b9a472c65b25 100644 (file)
@@ -156,12 +156,16 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
        struct nlm_rqst         *call;
        int                     status;
 
-       nlm_get_host(host);
        call = nlm_alloc_call(host);
        if (call == NULL)
                return -ENOMEM;
 
        nlmclnt_locks_init_private(fl, host);
+       if (!fl->fl_u.nfs_fl.owner) {
+               /* lockowner allocation has failed */
+               nlmclnt_release_call(call);
+               return -ENOMEM;
+       }
        /* Set up the argument struct */
        nlmclnt_setlockargs(call, fl);
 
@@ -185,9 +189,6 @@ EXPORT_SYMBOL_GPL(nlmclnt_proc);
 
 /*
  * Allocate an NLM RPC call struct
- *
- * Note: the caller must hold a reference to host. In case of failure,
- * this reference will be released.
  */
 struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
 {
@@ -199,7 +200,7 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
                        atomic_set(&call->a_count, 1);
                        locks_init_lock(&call->a_args.lock.fl);
                        locks_init_lock(&call->a_res.lock.fl);
-                       call->a_host = host;
+                       call->a_host = nlm_get_host(host);
                        return call;
                }
                if (signalled())
@@ -207,7 +208,6 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
                printk("nlm_alloc_call: failed, waiting for memory\n");
                schedule_timeout_interruptible(5*HZ);
        }
-       nlmclnt_release_host(host);
        return NULL;
 }
 
@@ -750,7 +750,7 @@ static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl
        dprintk("lockd: blocking lock attempt was interrupted by a signal.\n"
                "       Attempting to cancel lock.\n");
 
-       req = nlm_alloc_call(nlm_get_host(host));
+       req = nlm_alloc_call(host);
        if (!req)
                return -ENOMEM;
        req->a_flags = RPC_TASK_ASYNC;
index 183cc1f0af1cf6be225053e2032f47a063be25d3..6d1ee7204c889b347897b50590b620a658920020 100644 (file)
@@ -4,8 +4,10 @@
 
 #include <linux/module.h>
 #include <linux/lockd/bind.h>
+#include <net/net_namespace.h>
+
+#include "netns.h"
 
-static LIST_HEAD(grace_list);
 static DEFINE_SPINLOCK(grace_lock);
 
 /**
@@ -19,10 +21,12 @@ static DEFINE_SPINLOCK(grace_lock);
  *
  * This function is called to start a grace period.
  */
-void locks_start_grace(struct lock_manager *lm)
+void locks_start_grace(struct net *net, struct lock_manager *lm)
 {
+       struct lockd_net *ln = net_generic(net, lockd_net_id);
+
        spin_lock(&grace_lock);
-       list_add(&lm->list, &grace_list);
+       list_add(&lm->list, &ln->grace_list);
        spin_unlock(&grace_lock);
 }
 EXPORT_SYMBOL_GPL(locks_start_grace);
@@ -52,8 +56,10 @@ EXPORT_SYMBOL_GPL(locks_end_grace);
  * to answer ordinary lock requests, and when they should accept only
  * lock reclaims.
  */
-int locks_in_grace(void)
+int locks_in_grace(struct net *net)
 {
-       return !list_empty(&grace_list);
+       struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+       return !list_empty(&ln->grace_list);
 }
 EXPORT_SYMBOL_GPL(locks_in_grace);
index eb75ca7c2d6edd4782ad9f025b6115c78b3c9307..f9b22e58f78f053a05fffa2ec11feaa32bc2518a 100644 (file)
@@ -21,6 +21,8 @@
 
 #include <net/ipv6.h>
 
+#include "netns.h"
+
 #define NLMDBG_FACILITY                NLMDBG_HOSTCACHE
 #define NLM_HOST_NRHASH                32
 #define NLM_HOST_REBIND                (60 * HZ)
@@ -41,11 +43,10 @@ static struct hlist_head    nlm_client_hosts[NLM_HOST_NRHASH];
                hlist_for_each_entry_safe((host), (pos), (next), \
                                                (chain), h_hash)
 
-static unsigned long           next_gc;
 static unsigned long           nrhosts;
 static DEFINE_MUTEX(nlm_host_mutex);
 
-static void                    nlm_gc_hosts(void);
+static void                    nlm_gc_hosts(struct net *net);
 
 struct nlm_lookup_host_info {
        const int               server;         /* search for server|client */
@@ -172,6 +173,7 @@ out:
 static void nlm_destroy_host_locked(struct nlm_host *host)
 {
        struct rpc_clnt *clnt;
+       struct lockd_net *ln = net_generic(host->net, lockd_net_id);
 
        dprintk("lockd: destroy host %s\n", host->h_name);
 
@@ -188,6 +190,7 @@ static void nlm_destroy_host_locked(struct nlm_host *host)
                rpc_shutdown_client(clnt);
        kfree(host);
 
+       ln->nrhosts--;
        nrhosts--;
 }
 
@@ -228,6 +231,7 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
        struct hlist_node *pos;
        struct nlm_host *host;
        struct nsm_handle *nsm = NULL;
+       struct lockd_net *ln = net_generic(net, lockd_net_id);
 
        dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
                        (hostname ? hostname : "<none>"), version,
@@ -262,6 +266,7 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
                goto out;
 
        hlist_add_head(&host->h_hash, chain);
+       ln->nrhosts++;
        nrhosts++;
 
        dprintk("lockd: %s created host %s (%s)\n", __func__,
@@ -326,7 +331,7 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
        struct nsm_handle *nsm = NULL;
        struct sockaddr *src_sap = svc_daddr(rqstp);
        size_t src_len = rqstp->rq_daddrlen;
-       struct net *net = rqstp->rq_xprt->xpt_net;
+       struct net *net = SVC_NET(rqstp);
        struct nlm_lookup_host_info ni = {
                .server         = 1,
                .sap            = svc_addr(rqstp),
@@ -337,6 +342,7 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
                .hostname_len   = hostname_len,
                .net            = net,
        };
+       struct lockd_net *ln = net_generic(net, lockd_net_id);
 
        dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__,
                        (int)hostname_len, hostname, rqstp->rq_vers,
@@ -344,8 +350,8 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
 
        mutex_lock(&nlm_host_mutex);
 
-       if (time_after_eq(jiffies, next_gc))
-               nlm_gc_hosts();
+       if (time_after_eq(jiffies, ln->next_gc))
+               nlm_gc_hosts(net);
 
        chain = &nlm_server_hosts[nlm_hash_address(ni.sap)];
        hlist_for_each_entry(host, pos, chain, h_hash) {
@@ -382,6 +388,7 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
        memcpy(nlm_srcaddr(host), src_sap, src_len);
        host->h_srcaddrlen = src_len;
        hlist_add_head(&host->h_hash, chain);
+       ln->nrhosts++;
        nrhosts++;
 
        dprintk("lockd: %s created host %s (%s)\n",
@@ -565,6 +572,35 @@ void nlm_host_rebooted(const struct nlm_reboot *info)
        nsm_release(nsm);
 }
 
+static void nlm_complain_hosts(struct net *net)
+{
+       struct hlist_head *chain;
+       struct hlist_node *pos;
+       struct nlm_host *host;
+
+       if (net) {
+               struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+               if (ln->nrhosts == 0)
+                       return;
+               printk(KERN_WARNING "lockd: couldn't shutdown host module for net %p!\n", net);
+               dprintk("lockd: %lu hosts left in net %p:\n", ln->nrhosts, net);
+       } else {
+               if (nrhosts == 0)
+                       return;
+               printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
+               dprintk("lockd: %lu hosts left:\n", nrhosts);
+       }
+
+       for_each_host(host, pos, chain, nlm_server_hosts) {
+               if (net && host->net != net)
+                       continue;
+               dprintk("       %s (cnt %d use %d exp %ld net %p)\n",
+                       host->h_name, atomic_read(&host->h_count),
+                       host->h_inuse, host->h_expires, host->net);
+       }
+}
+
 void
 nlm_shutdown_hosts_net(struct net *net)
 {
@@ -572,11 +608,10 @@ nlm_shutdown_hosts_net(struct net *net)
        struct hlist_node *pos;
        struct nlm_host *host;
 
-       dprintk("lockd: shutting down host module\n");
        mutex_lock(&nlm_host_mutex);
 
        /* First, make all hosts eligible for gc */
-       dprintk("lockd: nuking all hosts...\n");
+       dprintk("lockd: nuking all hosts in net %p...\n", net);
        for_each_host(host, pos, chain, nlm_server_hosts) {
                if (net && host->net != net)
                        continue;
@@ -588,8 +623,10 @@ nlm_shutdown_hosts_net(struct net *net)
        }
 
        /* Then, perform a garbage collection pass */
-       nlm_gc_hosts();
+       nlm_gc_hosts(net);
        mutex_unlock(&nlm_host_mutex);
+
+       nlm_complain_hosts(net);
 }
 
 /*
@@ -599,22 +636,8 @@ nlm_shutdown_hosts_net(struct net *net)
 void
 nlm_shutdown_hosts(void)
 {
-       struct hlist_head *chain;
-       struct hlist_node *pos;
-       struct nlm_host *host;
-
+       dprintk("lockd: shutting down host module\n");
        nlm_shutdown_hosts_net(NULL);
-
-       /* complain if any hosts are left */
-       if (nrhosts != 0) {
-               printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
-               dprintk("lockd: %lu hosts left:\n", nrhosts);
-               for_each_host(host, pos, chain, nlm_server_hosts) {
-                       dprintk("       %s (cnt %d use %d exp %ld net %p)\n",
-                               host->h_name, atomic_read(&host->h_count),
-                               host->h_inuse, host->h_expires, host->net);
-               }
-       }
 }
 
 /*
@@ -623,30 +646,39 @@ nlm_shutdown_hosts(void)
  * mark & sweep for resources held by remote clients.
  */
 static void
-nlm_gc_hosts(void)
+nlm_gc_hosts(struct net *net)
 {
        struct hlist_head *chain;
        struct hlist_node *pos, *next;
        struct nlm_host *host;
 
-       dprintk("lockd: host garbage collection\n");
-       for_each_host(host, pos, chain, nlm_server_hosts)
+       dprintk("lockd: host garbage collection for net %p\n", net);
+       for_each_host(host, pos, chain, nlm_server_hosts) {
+               if (net && host->net != net)
+                       continue;
                host->h_inuse = 0;
+       }
 
        /* Mark all hosts that hold locks, blocks or shares */
-       nlmsvc_mark_resources();
+       nlmsvc_mark_resources(net);
 
        for_each_host_safe(host, pos, next, chain, nlm_server_hosts) {
+               if (net && host->net != net)
+                       continue;
                if (atomic_read(&host->h_count) || host->h_inuse
                 || time_before(jiffies, host->h_expires)) {
                        dprintk("nlm_gc_hosts skipping %s "
-                               "(cnt %d use %d exp %ld)\n",
+                               "(cnt %d use %d exp %ld net %p)\n",
                                host->h_name, atomic_read(&host->h_count),
-                               host->h_inuse, host->h_expires);
+                               host->h_inuse, host->h_expires, host->net);
                        continue;
                }
                nlm_destroy_host_locked(host);
        }
 
-       next_gc = jiffies + NLM_HOST_COLLECT;
+       if (net) {
+               struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+               ln->next_gc = jiffies + NLM_HOST_COLLECT;
+       }
 }
index ce227e0fbc5c192b0ccaaff7cc1640d33379adc1..4eee248ba96e441eb54e460130d3dde2f20d6ca1 100644 (file)
@@ -1,10 +1,17 @@
 #ifndef __LOCKD_NETNS_H__
 #define __LOCKD_NETNS_H__
 
+#include <linux/fs.h>
 #include <net/netns/generic.h>
 
 struct lockd_net {
        unsigned int nlmsvc_users;
+       unsigned long next_gc;
+       unsigned long nrhosts;
+
+       struct delayed_work grace_period_end;
+       struct lock_manager lockd_manager;
+       struct list_head grace_list;
 };
 
 extern int lockd_net_id;
index 80938fda67e0e6fde67999d3556820b87b6acd33..31a63f87b80602346cc78cfead614d35f620c9cd 100644 (file)
@@ -87,32 +87,36 @@ static unsigned long get_lockd_grace_period(void)
                return nlm_timeout * 5 * HZ;
 }
 
-static struct lock_manager lockd_manager = {
-};
-
-static void grace_ender(struct work_struct *not_used)
+static void grace_ender(struct work_struct *grace)
 {
-       locks_end_grace(&lockd_manager);
-}
+       struct delayed_work *dwork = container_of(grace, struct delayed_work,
+                                                 work);
+       struct lockd_net *ln = container_of(dwork, struct lockd_net,
+                                           grace_period_end);
 
-static DECLARE_DELAYED_WORK(grace_period_end, grace_ender);
+       locks_end_grace(&ln->lockd_manager);
+}
 
-static void set_grace_period(void)
+static void set_grace_period(struct net *net)
 {
        unsigned long grace_period = get_lockd_grace_period();
+       struct lockd_net *ln = net_generic(net, lockd_net_id);
 
-       locks_start_grace(&lockd_manager);
-       cancel_delayed_work_sync(&grace_period_end);
-       schedule_delayed_work(&grace_period_end, grace_period);
+       locks_start_grace(net, &ln->lockd_manager);
+       cancel_delayed_work_sync(&ln->grace_period_end);
+       schedule_delayed_work(&ln->grace_period_end, grace_period);
 }
 
 static void restart_grace(void)
 {
        if (nlmsvc_ops) {
-               cancel_delayed_work_sync(&grace_period_end);
-               locks_end_grace(&lockd_manager);
+               struct net *net = &init_net;
+               struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+               cancel_delayed_work_sync(&ln->grace_period_end);
+               locks_end_grace(&ln->lockd_manager);
                nlmsvc_invalidate_all();
-               set_grace_period();
+               set_grace_period(net);
        }
 }
 
@@ -137,8 +141,6 @@ lockd(void *vrqstp)
                nlm_timeout = LOCKD_DFLT_TIMEO;
        nlmsvc_timeout = nlm_timeout * HZ;
 
-       set_grace_period();
-
        /*
         * The main request loop. We don't terminate until the last
         * NFS mount or NFS daemon has gone away.
@@ -184,8 +186,6 @@ lockd(void *vrqstp)
                svc_process(rqstp);
        }
        flush_signals(current);
-       cancel_delayed_work_sync(&grace_period_end);
-       locks_end_grace(&lockd_manager);
        if (nlmsvc_ops)
                nlmsvc_invalidate_all();
        nlm_shutdown_hosts();
@@ -266,6 +266,7 @@ static int lockd_up_net(struct svc_serv *serv, struct net *net)
        error = make_socks(serv, net);
        if (error < 0)
                goto err_socks;
+       set_grace_period(net);
        dprintk("lockd_up_net: per-net data created; net=%p\n", net);
        return 0;
 
@@ -283,6 +284,8 @@ static void lockd_down_net(struct svc_serv *serv, struct net *net)
        if (ln->nlmsvc_users) {
                if (--ln->nlmsvc_users == 0) {
                        nlm_shutdown_hosts_net(net);
+                       cancel_delayed_work_sync(&ln->grace_period_end);
+                       locks_end_grace(&ln->lockd_manager);
                        svc_shutdown_net(serv, net);
                        dprintk("lockd_down_net: per-net data destroyed; net=%p\n", net);
                }
@@ -589,6 +592,10 @@ module_param(nlm_max_connections, uint, 0644);
 
 static int lockd_init_net(struct net *net)
 {
+       struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+       INIT_DELAYED_WORK(&ln->grace_period_end, grace_ender);
+       INIT_LIST_HEAD(&ln->grace_list);
        return 0;
 }
 
index 9a41fdc19511ffb85cccab8fef5d42096bd08dca..b147d1ae71fd9ec3c21ea69f847be1b2b047896d 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/time.h>
 #include <linux/lockd/lockd.h>
 #include <linux/lockd/share.h>
+#include <linux/sunrpc/svc_xprt.h>
 
 #define NLMDBG_FACILITY                NLMDBG_CLIENT
 
@@ -151,7 +152,7 @@ nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        /* Don't accept requests during grace period */
-       if (locks_in_grace()) {
+       if (locks_in_grace(SVC_NET(rqstp))) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
@@ -161,7 +162,7 @@ nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
                return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
 
        /* Try to cancel request. */
-       resp->status = nlmsvc_cancel_blocked(file, &argp->lock);
+       resp->status = nlmsvc_cancel_blocked(SVC_NET(rqstp), file, &argp->lock);
 
        dprintk("lockd: CANCEL        status %d\n", ntohl(resp->status));
        nlmsvc_release_host(host);
@@ -184,7 +185,7 @@ nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        /* Don't accept new lock requests during grace period */
-       if (locks_in_grace()) {
+       if (locks_in_grace(SVC_NET(rqstp))) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
@@ -194,7 +195,7 @@ nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
                return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
 
        /* Now try to remove the lock */
-       resp->status = nlmsvc_unlock(file, &argp->lock);
+       resp->status = nlmsvc_unlock(SVC_NET(rqstp), file, &argp->lock);
 
        dprintk("lockd: UNLOCK        status %d\n", ntohl(resp->status));
        nlmsvc_release_host(host);
@@ -256,6 +257,7 @@ static __be32 nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args
                return rpc_system_err;
 
        call = nlm_alloc_call(host);
+       nlmsvc_release_host(host);
        if (call == NULL)
                return rpc_system_err;
 
@@ -321,7 +323,7 @@ nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        /* Don't accept new lock requests during grace period */
-       if (locks_in_grace() && !argp->reclaim) {
+       if (locks_in_grace(SVC_NET(rqstp)) && !argp->reclaim) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
@@ -354,7 +356,7 @@ nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        /* Don't accept requests during grace period */
-       if (locks_in_grace()) {
+       if (locks_in_grace(SVC_NET(rqstp))) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
index e46353f41a4202ec2138998ab5449b833dc139db..fb1a2bedbe9789a8fcca5618b4637d70b25fb8ab 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/sunrpc/clnt.h>
-#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svc_xprt.h>
 #include <linux/lockd/nlm.h>
 #include <linux/lockd/lockd.h>
 #include <linux/kthread.h>
@@ -219,7 +219,6 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host,
        struct nlm_block        *block;
        struct nlm_rqst         *call = NULL;
 
-       nlm_get_host(host);
        call = nlm_alloc_call(host);
        if (call == NULL)
                return NULL;
@@ -447,11 +446,11 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
                goto out;
        }
 
-       if (locks_in_grace() && !reclaim) {
+       if (locks_in_grace(SVC_NET(rqstp)) && !reclaim) {
                ret = nlm_lck_denied_grace_period;
                goto out;
        }
-       if (reclaim && !locks_in_grace()) {
+       if (reclaim && !locks_in_grace(SVC_NET(rqstp))) {
                ret = nlm_lck_denied_grace_period;
                goto out;
        }
@@ -559,7 +558,7 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
                goto out;
        }
 
-       if (locks_in_grace()) {
+       if (locks_in_grace(SVC_NET(rqstp))) {
                ret = nlm_lck_denied_grace_period;
                goto out;
        }
@@ -603,7 +602,7 @@ out:
  * must be removed.
  */
 __be32
-nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
+nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
 {
        int     error;
 
@@ -615,7 +614,7 @@ nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
                                (long long)lock->fl.fl_end);
 
        /* First, cancel any lock that might be there */
-       nlmsvc_cancel_blocked(file, lock);
+       nlmsvc_cancel_blocked(net, file, lock);
 
        lock->fl.fl_type = F_UNLCK;
        error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
@@ -631,7 +630,7 @@ nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
  * The calling procedure must check whether the file can be closed.
  */
 __be32
-nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
+nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
 {
        struct nlm_block        *block;
        int status = 0;
@@ -643,7 +642,7 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
                                (long long)lock->fl.fl_start,
                                (long long)lock->fl.fl_end);
 
-       if (locks_in_grace())
+       if (locks_in_grace(net))
                return nlm_lck_denied_grace_period;
 
        mutex_lock(&file->f_mutex);
index d27aab11f32414ae165cec40083ef20f08de0466..3009a365e082e37764ea817d070165c3b828146b 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/time.h>
 #include <linux/lockd/lockd.h>
 #include <linux/lockd/share.h>
+#include <linux/sunrpc/svc_xprt.h>
 
 #define NLMDBG_FACILITY                NLMDBG_CLIENT
 
@@ -175,13 +176,14 @@ nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
 {
        struct nlm_host *host;
        struct nlm_file *file;
+       struct net *net = SVC_NET(rqstp);
 
        dprintk("lockd: CANCEL        called\n");
 
        resp->cookie = argp->cookie;
 
        /* Don't accept requests during grace period */
-       if (locks_in_grace()) {
+       if (locks_in_grace(net)) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
@@ -191,7 +193,7 @@ nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
                return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
 
        /* Try to cancel request. */
-       resp->status = cast_status(nlmsvc_cancel_blocked(file, &argp->lock));
+       resp->status = cast_status(nlmsvc_cancel_blocked(net, file, &argp->lock));
 
        dprintk("lockd: CANCEL        status %d\n", ntohl(resp->status));
        nlmsvc_release_host(host);
@@ -208,13 +210,14 @@ nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
 {
        struct nlm_host *host;
        struct nlm_file *file;
+       struct net *net = SVC_NET(rqstp);
 
        dprintk("lockd: UNLOCK        called\n");
 
        resp->cookie = argp->cookie;
 
        /* Don't accept new lock requests during grace period */
-       if (locks_in_grace()) {
+       if (locks_in_grace(net)) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
@@ -224,7 +227,7 @@ nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
                return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
 
        /* Now try to remove the lock */
-       resp->status = cast_status(nlmsvc_unlock(file, &argp->lock));
+       resp->status = cast_status(nlmsvc_unlock(net, file, &argp->lock));
 
        dprintk("lockd: UNLOCK        status %d\n", ntohl(resp->status));
        nlmsvc_release_host(host);
@@ -294,6 +297,7 @@ static __be32 nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args
                return rpc_system_err;
 
        call = nlm_alloc_call(host);
+       nlmsvc_release_host(host);
        if (call == NULL)
                return rpc_system_err;
 
@@ -361,7 +365,7 @@ nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        /* Don't accept new lock requests during grace period */
-       if (locks_in_grace() && !argp->reclaim) {
+       if (locks_in_grace(SVC_NET(rqstp)) && !argp->reclaim) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
@@ -394,7 +398,7 @@ nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        /* Don't accept requests during grace period */
-       if (locks_in_grace()) {
+       if (locks_in_grace(SVC_NET(rqstp))) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
index 2240d384d7877042c44ba134489b431e9de10118..0deb5f6c9dd47170ee2b19390362f2549ab7cac9 100644 (file)
@@ -309,7 +309,8 @@ nlm_release_file(struct nlm_file *file)
  * Helpers function for resource traversal
  *
  * nlmsvc_mark_host:
- *     used by the garbage collector; simply sets h_inuse.
+ *     used by the garbage collector; simply sets h_inuse only for those
+ *     hosts, which passed network check.
  *     Always returns 0.
  *
  * nlmsvc_same_host:
@@ -320,12 +321,15 @@ nlm_release_file(struct nlm_file *file)
  *     returns 1 iff the host is a client.
  *     Used by nlmsvc_invalidate_all
  */
+
 static int
-nlmsvc_mark_host(void *data, struct nlm_host *dummy)
+nlmsvc_mark_host(void *data, struct nlm_host *hint)
 {
        struct nlm_host *host = data;
 
-       host->h_inuse = 1;
+       if ((hint->net == NULL) ||
+           (host->net == hint->net))
+               host->h_inuse = 1;
        return 0;
 }
 
@@ -358,10 +362,13 @@ nlmsvc_is_client(void *data, struct nlm_host *dummy)
  * Mark all hosts that still hold resources
  */
 void
-nlmsvc_mark_resources(void)
+nlmsvc_mark_resources(struct net *net)
 {
-       dprintk("lockd: nlmsvc_mark_resources\n");
-       nlm_traverse_files(NULL, nlmsvc_mark_host, NULL);
+       struct nlm_host hint;
+
+       dprintk("lockd: nlmsvc_mark_resources for net %p\n", net);
+       hint.net = net;
+       nlm_traverse_files(&hint, nlmsvc_mark_host, NULL);
 }
 
 /*
index 82c353304f9eeccab27b01b86379441df323deeb..7e81bfc751644b0cfb70c9c4291ab6e7fb644ea3 100644 (file)
@@ -200,11 +200,7 @@ void locks_release_private(struct file_lock *fl)
                        fl->fl_ops->fl_release_private(fl);
                fl->fl_ops = NULL;
        }
-       if (fl->fl_lmops) {
-               if (fl->fl_lmops->lm_release_private)
-                       fl->fl_lmops->lm_release_private(fl);
-               fl->fl_lmops = NULL;
-       }
+       fl->fl_lmops = NULL;
 
 }
 EXPORT_SYMBOL_GPL(locks_release_private);
@@ -427,18 +423,8 @@ static void lease_break_callback(struct file_lock *fl)
        kill_fasync(&fl->fl_fasync, SIGIO, POLL_MSG);
 }
 
-static void lease_release_private_callback(struct file_lock *fl)
-{
-       if (!fl->fl_file)
-               return;
-
-       f_delown(fl->fl_file);
-       fl->fl_file->f_owner.signum = 0;
-}
-
 static const struct lock_manager_operations lease_manager_ops = {
        .lm_break = lease_break_callback,
-       .lm_release_private = lease_release_private_callback,
        .lm_change = lease_modify,
 };
 
@@ -580,12 +566,6 @@ static void locks_delete_lock(struct file_lock **thisfl_p)
        fl->fl_next = NULL;
        list_del_init(&fl->fl_link);
 
-       fasync_helper(0, fl->fl_file, 0, &fl->fl_fasync);
-       if (fl->fl_fasync != NULL) {
-               printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync);
-               fl->fl_fasync = NULL;
-       }
-
        if (fl->fl_nspid) {
                put_pid(fl->fl_nspid);
                fl->fl_nspid = NULL;
@@ -1155,8 +1135,18 @@ int lease_modify(struct file_lock **before, int arg)
                return error;
        lease_clear_pending(fl, arg);
        locks_wake_up_blocks(fl);
-       if (arg == F_UNLCK)
+       if (arg == F_UNLCK) {
+               struct file *filp = fl->fl_file;
+
+               f_delown(filp);
+               filp->f_owner.signum = 0;
+               fasync_helper(0, fl->fl_file, 0, &fl->fl_fasync);
+               if (fl->fl_fasync != NULL) {
+                       printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync);
+                       fl->fl_fasync = NULL;
+               }
                locks_delete_lock(before);
+       }
        return 0;
 }
 
index 13487ad168944e37eb8871161c3077d50b5a271c..78e2d93e5c830f33cc370d8ca98262fcfe27e883 100644 (file)
@@ -32,7 +32,8 @@ static int block_to_path(struct inode * inode, long block, int offsets[DEPTH])
        if (block < 0) {
                printk("MINIX-fs: block_to_path: block %ld < 0 on dev %s\n",
                        block, bdevname(sb->s_bdev, b));
-       } else if (block >= (minix_sb(inode->i_sb)->s_max_size/sb->s_blocksize)) {
+       } else if ((u64)block * (u64)sb->s_blocksize >=
+                       minix_sb(sb)->s_max_size) {
                if (printk_ratelimit())
                        printk("MINIX-fs: block_to_path: "
                               "block %ld too big on dev %s\n",
index 2ccc35c4dc24d95e2a5c1bf5528528fa6f22827d..1b464390dde85ecd783eb45f64aee1f89f6c3ff1 100644 (file)
@@ -650,6 +650,121 @@ static inline void put_link(struct nameidata *nd, struct path *link, void *cooki
        path_put(link);
 }
 
+int sysctl_protected_symlinks __read_mostly = 1;
+int sysctl_protected_hardlinks __read_mostly = 1;
+
+/**
+ * may_follow_link - Check symlink following for unsafe situations
+ * @link: The path of the symlink
+ *
+ * In the case of the sysctl_protected_symlinks sysctl being enabled,
+ * CAP_DAC_OVERRIDE needs to be specifically ignored if the symlink is
+ * in a sticky world-writable directory. This is to protect privileged
+ * processes from failing races against path names that may change out
+ * from under them by way of other users creating malicious symlinks.
+ * It will permit symlinks to be followed only when outside a sticky
+ * world-writable directory, or when the uid of the symlink and follower
+ * match, or when the directory owner matches the symlink's owner.
+ *
+ * Returns 0 if following the symlink is allowed, -ve on error.
+ */
+static inline int may_follow_link(struct path *link, struct nameidata *nd)
+{
+       const struct inode *inode;
+       const struct inode *parent;
+
+       if (!sysctl_protected_symlinks)
+               return 0;
+
+       /* Allowed if owner and follower match. */
+       inode = link->dentry->d_inode;
+       if (current_cred()->fsuid == inode->i_uid)
+               return 0;
+
+       /* Allowed if parent directory not sticky and world-writable. */
+       parent = nd->path.dentry->d_inode;
+       if ((parent->i_mode & (S_ISVTX|S_IWOTH)) != (S_ISVTX|S_IWOTH))
+               return 0;
+
+       /* Allowed if parent directory and link owner match. */
+       if (parent->i_uid == inode->i_uid)
+               return 0;
+
+       path_put_conditional(link, nd);
+       path_put(&nd->path);
+       audit_log_link_denied("follow_link", link);
+       return -EACCES;
+}
+
+/**
+ * safe_hardlink_source - Check for safe hardlink conditions
+ * @inode: the source inode to hardlink from
+ *
+ * Return false if at least one of the following conditions:
+ *    - inode is not a regular file
+ *    - inode is setuid
+ *    - inode is setgid and group-exec
+ *    - access failure for read and write
+ *
+ * Otherwise returns true.
+ */
+static bool safe_hardlink_source(struct inode *inode)
+{
+       umode_t mode = inode->i_mode;
+
+       /* Special files should not get pinned to the filesystem. */
+       if (!S_ISREG(mode))
+               return false;
+
+       /* Setuid files should not get pinned to the filesystem. */
+       if (mode & S_ISUID)
+               return false;
+
+       /* Executable setgid files should not get pinned to the filesystem. */
+       if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
+               return false;
+
+       /* Hardlinking to unreadable or unwritable sources is dangerous. */
+       if (inode_permission(inode, MAY_READ | MAY_WRITE))
+               return false;
+
+       return true;
+}
+
+/**
+ * may_linkat - Check permissions for creating a hardlink
+ * @link: the source to hardlink from
+ *
+ * Block hardlink when all of:
+ *  - sysctl_protected_hardlinks enabled
+ *  - fsuid does not match inode
+ *  - hardlink source is unsafe (see safe_hardlink_source() above)
+ *  - not CAP_FOWNER
+ *
+ * Returns 0 if successful, -ve on error.
+ */
+static int may_linkat(struct path *link)
+{
+       const struct cred *cred;
+       struct inode *inode;
+
+       if (!sysctl_protected_hardlinks)
+               return 0;
+
+       cred = current_cred();
+       inode = link->dentry->d_inode;
+
+       /* Source inode owner (or CAP_FOWNER) can hardlink all they like,
+        * otherwise, it must be a safe source.
+        */
+       if (cred->fsuid == inode->i_uid || safe_hardlink_source(inode) ||
+           capable(CAP_FOWNER))
+               return 0;
+
+       audit_log_link_denied("linkat", link);
+       return -EPERM;
+}
+
 static __always_inline int
 follow_link(struct path *link, struct nameidata *nd, void **p)
 {
@@ -1818,6 +1933,9 @@ static int path_lookupat(int dfd, const char *name,
                while (err > 0) {
                        void *cookie;
                        struct path link = path;
+                       err = may_follow_link(&link, nd);
+                       if (unlikely(err))
+                               break;
                        nd->flags |= LOOKUP_PARENT;
                        err = follow_link(&link, nd, &cookie);
                        if (err)
@@ -2277,7 +2395,7 @@ static int may_o_create(struct path *dir, struct dentry *dentry, umode_t mode)
 static int atomic_open(struct nameidata *nd, struct dentry *dentry,
                        struct path *path, struct file *file,
                        const struct open_flags *op,
-                       bool *want_write, bool need_lookup,
+                       bool got_write, bool need_lookup,
                        int *opened)
 {
        struct inode *dir =  nd->path.dentry->d_inode;
@@ -2300,7 +2418,7 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
        if ((open_flag & O_CREAT) && !IS_POSIXACL(dir))
                mode &= ~current_umask();
 
-       if (open_flag & O_EXCL) {
+       if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT)) {
                open_flag &= ~O_TRUNC;
                *opened |= FILE_CREATED;
        }
@@ -2314,12 +2432,9 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
         * Another problem is returing the "right" error value (e.g. for an
         * O_EXCL open we want to return EEXIST not EROFS).
         */
-       if ((open_flag & (O_CREAT | O_TRUNC)) ||
-           (open_flag & O_ACCMODE) != O_RDONLY) {
-               error = mnt_want_write(nd->path.mnt);
-               if (!error) {
-                       *want_write = true;
-               } else if (!(open_flag & O_CREAT)) {
+       if (((open_flag & (O_CREAT | O_TRUNC)) ||
+           (open_flag & O_ACCMODE) != O_RDONLY) && unlikely(!got_write)) {
+               if (!(open_flag & O_CREAT)) {
                        /*
                         * No O_CREATE -> atomicity not a requirement -> fall
                         * back to lookup + open
@@ -2327,11 +2442,11 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
                        goto no_open;
                } else if (open_flag & (O_EXCL | O_TRUNC)) {
                        /* Fall back and fail with the right error */
-                       create_error = error;
+                       create_error = -EROFS;
                        goto no_open;
                } else {
                        /* No side effects, safe to clear O_CREAT */
-                       create_error = error;
+                       create_error = -EROFS;
                        open_flag &= ~O_CREAT;
                }
        }
@@ -2438,7 +2553,7 @@ looked_up:
 static int lookup_open(struct nameidata *nd, struct path *path,
                        struct file *file,
                        const struct open_flags *op,
-                       bool *want_write, int *opened)
+                       bool got_write, int *opened)
 {
        struct dentry *dir = nd->path.dentry;
        struct inode *dir_inode = dir->d_inode;
@@ -2456,7 +2571,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
                goto out_no_open;
 
        if ((nd->flags & LOOKUP_OPEN) && dir_inode->i_op->atomic_open) {
-               return atomic_open(nd, dentry, path, file, op, want_write,
+               return atomic_open(nd, dentry, path, file, op, got_write,
                                   need_lookup, opened);
        }
 
@@ -2480,10 +2595,10 @@ static int lookup_open(struct nameidata *nd, struct path *path,
                 * a permanent write count is taken through
                 * the 'struct file' in finish_open().
                 */
-               error = mnt_want_write(nd->path.mnt);
-               if (error)
+               if (!got_write) {
+                       error = -EROFS;
                        goto out_dput;
-               *want_write = true;
+               }
                *opened |= FILE_CREATED;
                error = security_path_mknod(&nd->path, dentry, mode, 0);
                if (error)
@@ -2513,7 +2628,7 @@ static int do_last(struct nameidata *nd, struct path *path,
        struct dentry *dir = nd->path.dentry;
        int open_flag = op->open_flag;
        bool will_truncate = (open_flag & O_TRUNC) != 0;
-       bool want_write = false;
+       bool got_write = false;
        int acc_mode = op->acc_mode;
        struct inode *inode;
        bool symlink_ok = false;
@@ -2582,8 +2697,18 @@ static int do_last(struct nameidata *nd, struct path *path,
        }
 
 retry_lookup:
+       if (op->open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
+               error = mnt_want_write(nd->path.mnt);
+               if (!error)
+                       got_write = true;
+               /*
+                * do _not_ fail yet - we might not need that or fail with
+                * a different error; let lookup_open() decide; we'll be
+                * dropping this one anyway.
+                */
+       }
        mutex_lock(&dir->d_inode->i_mutex);
-       error = lookup_open(nd, path, file, op, &want_write, opened);
+       error = lookup_open(nd, path, file, op, got_write, opened);
        mutex_unlock(&dir->d_inode->i_mutex);
 
        if (error <= 0) {
@@ -2608,22 +2733,23 @@ retry_lookup:
        }
 
        /*
-        * It already exists.
+        * create/update audit record if it already exists.
         */
-       audit_inode(pathname, path->dentry);
+       if (path->dentry->d_inode)
+               audit_inode(pathname, path->dentry);
 
        /*
         * If atomic_open() acquired write access it is dropped now due to
         * possible mount and symlink following (this might be optimized away if
         * necessary...)
         */
-       if (want_write) {
+       if (got_write) {
                mnt_drop_write(nd->path.mnt);
-               want_write = false;
+               got_write = false;
        }
 
        error = -EEXIST;
-       if (open_flag & O_EXCL)
+       if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))
                goto exit_dput;
 
        error = follow_managed(path, nd->flags);
@@ -2684,7 +2810,7 @@ finish_open:
                error = mnt_want_write(nd->path.mnt);
                if (error)
                        goto out;
-               want_write = true;
+               got_write = true;
        }
 finish_open_created:
        error = may_open(&nd->path, acc_mode, open_flag);
@@ -2711,7 +2837,7 @@ opened:
                        goto exit_fput;
        }
 out:
-       if (want_write)
+       if (got_write)
                mnt_drop_write(nd->path.mnt);
        path_put(&save_parent);
        terminate_walk(nd);
@@ -2735,9 +2861,9 @@ stale_open:
        nd->inode = dir->d_inode;
        save_parent.mnt = NULL;
        save_parent.dentry = NULL;
-       if (want_write) {
+       if (got_write) {
                mnt_drop_write(nd->path.mnt);
-               want_write = false;
+               got_write = false;
        }
        retried = true;
        goto retry_lookup;
@@ -2777,6 +2903,9 @@ static struct file *path_openat(int dfd, const char *pathname,
                        error = -ELOOP;
                        break;
                }
+               error = may_follow_link(&link, nd);
+               if (unlikely(error))
+                       break;
                nd->flags |= LOOKUP_PARENT;
                nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
                error = follow_link(&link, nd, &cookie);
@@ -2846,6 +2975,7 @@ struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path
 {
        struct dentry *dentry = ERR_PTR(-EEXIST);
        struct nameidata nd;
+       int err2;
        int error = do_path_lookup(dfd, pathname, LOOKUP_PARENT, &nd);
        if (error)
                return ERR_PTR(error);
@@ -2859,16 +2989,19 @@ struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path
        nd.flags &= ~LOOKUP_PARENT;
        nd.flags |= LOOKUP_CREATE | LOOKUP_EXCL;
 
+       /* don't fail immediately if it's r/o, at least try to report other errors */
+       err2 = mnt_want_write(nd.path.mnt);
        /*
         * Do the final lookup.
         */
        mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
        dentry = lookup_hash(&nd);
        if (IS_ERR(dentry))
-               goto fail;
+               goto unlock;
 
+       error = -EEXIST;
        if (dentry->d_inode)
-               goto eexist;
+               goto fail;
        /*
         * Special case - lookup gave negative, but... we had foo/bar/
         * From the vfs_mknod() POV we just have a negative dentry -
@@ -2876,23 +3009,37 @@ struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path
         * been asking for (non-existent) directory. -ENOENT for you.
         */
        if (unlikely(!is_dir && nd.last.name[nd.last.len])) {
-               dput(dentry);
-               dentry = ERR_PTR(-ENOENT);
+               error = -ENOENT;
+               goto fail;
+       }
+       if (unlikely(err2)) {
+               error = err2;
                goto fail;
        }
        *path = nd.path;
        return dentry;
-eexist:
-       dput(dentry);
-       dentry = ERR_PTR(-EEXIST);
 fail:
+       dput(dentry);
+       dentry = ERR_PTR(error);
+unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+       if (!err2)
+               mnt_drop_write(nd.path.mnt);
 out:
        path_put(&nd.path);
        return dentry;
 }
 EXPORT_SYMBOL(kern_path_create);
 
+void done_path_create(struct path *path, struct dentry *dentry)
+{
+       dput(dentry);
+       mutex_unlock(&path->dentry->d_inode->i_mutex);
+       mnt_drop_write(path->mnt);
+       path_put(path);
+}
+EXPORT_SYMBOL(done_path_create);
+
 struct dentry *user_path_create(int dfd, const char __user *pathname, struct path *path, int is_dir)
 {
        char *tmp = getname(pathname);
@@ -2956,8 +3103,9 @@ SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode,
        struct path path;
        int error;
 
-       if (S_ISDIR(mode))
-               return -EPERM;
+       error = may_mknod(mode);
+       if (error)
+               return error;
 
        dentry = user_path_create(dfd, filename, &path, 0);
        if (IS_ERR(dentry))
@@ -2965,15 +3113,9 @@ SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode,
 
        if (!IS_POSIXACL(path.dentry->d_inode))
                mode &= ~current_umask();
-       error = may_mknod(mode);
-       if (error)
-               goto out_dput;
-       error = mnt_want_write(path.mnt);
-       if (error)
-               goto out_dput;
        error = security_path_mknod(&path, dentry, mode, dev);
        if (error)
-               goto out_drop_write;
+               goto out;
        switch (mode & S_IFMT) {
                case 0: case S_IFREG:
                        error = vfs_create(path.dentry->d_inode,dentry,mode,true);
@@ -2986,13 +3128,8 @@ SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode,
                        error = vfs_mknod(path.dentry->d_inode,dentry,mode,0);
                        break;
        }
-out_drop_write:
-       mnt_drop_write(path.mnt);
-out_dput:
-       dput(dentry);
-       mutex_unlock(&path.dentry->d_inode->i_mutex);
-       path_put(&path);
-
+out:
+       done_path_create(&path, dentry);
        return error;
 }
 
@@ -3038,19 +3175,10 @@ SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
 
        if (!IS_POSIXACL(path.dentry->d_inode))
                mode &= ~current_umask();
-       error = mnt_want_write(path.mnt);
-       if (error)
-               goto out_dput;
        error = security_path_mkdir(&path, dentry, mode);
-       if (error)
-               goto out_drop_write;
-       error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
-out_drop_write:
-       mnt_drop_write(path.mnt);
-out_dput:
-       dput(dentry);
-       mutex_unlock(&path.dentry->d_inode->i_mutex);
-       path_put(&path);
+       if (!error)
+               error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
+       done_path_create(&path, dentry);
        return error;
 }
 
@@ -3144,6 +3272,9 @@ static long do_rmdir(int dfd, const char __user *pathname)
        }
 
        nd.flags &= ~LOOKUP_PARENT;
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto exit1;
 
        mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
        dentry = lookup_hash(&nd);
@@ -3154,19 +3285,15 @@ static long do_rmdir(int dfd, const char __user *pathname)
                error = -ENOENT;
                goto exit3;
        }
-       error = mnt_want_write(nd.path.mnt);
-       if (error)
-               goto exit3;
        error = security_path_rmdir(&nd.path, dentry);
        if (error)
-               goto exit4;
+               goto exit3;
        error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
-exit4:
-       mnt_drop_write(nd.path.mnt);
 exit3:
        dput(dentry);
 exit2:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+       mnt_drop_write(nd.path.mnt);
 exit1:
        path_put(&nd.path);
        putname(name);
@@ -3233,6 +3360,9 @@ static long do_unlinkat(int dfd, const char __user *pathname)
                goto exit1;
 
        nd.flags &= ~LOOKUP_PARENT;
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto exit1;
 
        mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
        dentry = lookup_hash(&nd);
@@ -3245,21 +3375,17 @@ static long do_unlinkat(int dfd, const char __user *pathname)
                if (!inode)
                        goto slashes;
                ihold(inode);
-               error = mnt_want_write(nd.path.mnt);
-               if (error)
-                       goto exit2;
                error = security_path_unlink(&nd.path, dentry);
                if (error)
-                       goto exit3;
+                       goto exit2;
                error = vfs_unlink(nd.path.dentry->d_inode, dentry);
-exit3:
-               mnt_drop_write(nd.path.mnt);
-       exit2:
+exit2:
                dput(dentry);
        }
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
        if (inode)
                iput(inode);    /* truncate the inode here */
+       mnt_drop_write(nd.path.mnt);
 exit1:
        path_put(&nd.path);
        putname(name);
@@ -3324,19 +3450,10 @@ SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
        if (IS_ERR(dentry))
                goto out_putname;
 
-       error = mnt_want_write(path.mnt);
-       if (error)
-               goto out_dput;
        error = security_path_symlink(&path, dentry, from);
-       if (error)
-               goto out_drop_write;
-       error = vfs_symlink(path.dentry->d_inode, dentry, from);
-out_drop_write:
-       mnt_drop_write(path.mnt);
-out_dput:
-       dput(dentry);
-       mutex_unlock(&path.dentry->d_inode->i_mutex);
-       path_put(&path);
+       if (!error)
+               error = vfs_symlink(path.dentry->d_inode, dentry, from);
+       done_path_create(&path, dentry);
 out_putname:
        putname(from);
        return error;
@@ -3436,19 +3553,15 @@ SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
        error = -EXDEV;
        if (old_path.mnt != new_path.mnt)
                goto out_dput;
-       error = mnt_want_write(new_path.mnt);
-       if (error)
+       error = may_linkat(&old_path);
+       if (unlikely(error))
                goto out_dput;
        error = security_path_link(old_path.dentry, &new_path, new_dentry);
        if (error)
-               goto out_drop_write;
+               goto out_dput;
        error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry);
-out_drop_write:
-       mnt_drop_write(new_path.mnt);
 out_dput:
-       dput(new_dentry);
-       mutex_unlock(&new_path.dentry->d_inode->i_mutex);
-       path_put(&new_path);
+       done_path_create(&new_path, new_dentry);
 out:
        path_put(&old_path);
 
@@ -3644,6 +3757,10 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
        if (newnd.last_type != LAST_NORM)
                goto exit2;
 
+       error = mnt_want_write(oldnd.path.mnt);
+       if (error)
+               goto exit2;
+
        oldnd.flags &= ~LOOKUP_PARENT;
        newnd.flags &= ~LOOKUP_PARENT;
        newnd.flags |= LOOKUP_RENAME_TARGET;
@@ -3679,23 +3796,19 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
        if (new_dentry == trap)
                goto exit5;
 
-       error = mnt_want_write(oldnd.path.mnt);
-       if (error)
-               goto exit5;
        error = security_path_rename(&oldnd.path, old_dentry,
                                     &newnd.path, new_dentry);
        if (error)
-               goto exit6;
+               goto exit5;
        error = vfs_rename(old_dir->d_inode, old_dentry,
                                   new_dir->d_inode, new_dentry);
-exit6:
-       mnt_drop_write(oldnd.path.mnt);
 exit5:
        dput(new_dentry);
 exit4:
        dput(old_dentry);
 exit3:
        unlock_rename(new_dir, old_dir);
+       mnt_drop_write(oldnd.path.mnt);
 exit2:
        path_put(&newnd.path);
        putname(to);
index c53d3381b0d0043d91e2f7c47e4cafbdaaaf13d6..4d31f73e2561d4d0e19d85becc0223dda27b4710 100644 (file)
@@ -283,24 +283,22 @@ static int mnt_is_readonly(struct vfsmount *mnt)
 }
 
 /*
- * Most r/o checks on a fs are for operations that take
- * discrete amounts of time, like a write() or unlink().
- * We must keep track of when those operations start
- * (for permission checks) and when they end, so that
- * we can determine when writes are able to occur to
- * a filesystem.
+ * Most r/o & frozen checks on a fs are for operations that take discrete
+ * amounts of time, like a write() or unlink().  We must keep track of when
+ * those operations start (for permission checks) and when they end, so that we
+ * can determine when writes are able to occur to a filesystem.
  */
 /**
- * mnt_want_write - get write access to a mount
+ * __mnt_want_write - get write access to a mount without freeze protection
  * @m: the mount on which to take a write
  *
- * This tells the low-level filesystem that a write is
- * about to be performed to it, and makes sure that
- * writes are allowed before returning success.  When
- * the write operation is finished, mnt_drop_write()
- * must be called.  This is effectively a refcount.
+ * This tells the low-level filesystem that a write is about to be performed to
+ * it, and makes sure that writes are allowed (mnt it read-write) before
+ * returning success. This operation does not protect against filesystem being
+ * frozen. When the write operation is finished, __mnt_drop_write() must be
+ * called. This is effectively a refcount.
  */
-int mnt_want_write(struct vfsmount *m)
+int __mnt_want_write(struct vfsmount *m)
 {
        struct mount *mnt = real_mount(m);
        int ret = 0;
@@ -326,6 +324,27 @@ int mnt_want_write(struct vfsmount *m)
                ret = -EROFS;
        }
        preempt_enable();
+
+       return ret;
+}
+
+/**
+ * mnt_want_write - get write access to a mount
+ * @m: the mount on which to take a write
+ *
+ * This tells the low-level filesystem that a write is about to be performed to
+ * it, and makes sure that writes are allowed (mount is read-write, filesystem
+ * is not frozen) before returning success.  When the write operation is
+ * finished, mnt_drop_write() must be called.  This is effectively a refcount.
+ */
+int mnt_want_write(struct vfsmount *m)
+{
+       int ret;
+
+       sb_start_write(m->mnt_sb);
+       ret = __mnt_want_write(m);
+       if (ret)
+               sb_end_write(m->mnt_sb);
        return ret;
 }
 EXPORT_SYMBOL_GPL(mnt_want_write);
@@ -355,38 +374,76 @@ int mnt_clone_write(struct vfsmount *mnt)
 EXPORT_SYMBOL_GPL(mnt_clone_write);
 
 /**
- * mnt_want_write_file - get write access to a file's mount
+ * __mnt_want_write_file - get write access to a file's mount
  * @file: the file who's mount on which to take a write
  *
- * This is like mnt_want_write, but it takes a file and can
+ * This is like __mnt_want_write, but it takes a file and can
  * do some optimisations if the file is open for write already
  */
-int mnt_want_write_file(struct file *file)
+int __mnt_want_write_file(struct file *file)
 {
        struct inode *inode = file->f_dentry->d_inode;
+
        if (!(file->f_mode & FMODE_WRITE) || special_file(inode->i_mode))
-               return mnt_want_write(file->f_path.mnt);
+               return __mnt_want_write(file->f_path.mnt);
        else
                return mnt_clone_write(file->f_path.mnt);
 }
+
+/**
+ * mnt_want_write_file - get write access to a file's mount
+ * @file: the file who's mount on which to take a write
+ *
+ * This is like mnt_want_write, but it takes a file and can
+ * do some optimisations if the file is open for write already
+ */
+int mnt_want_write_file(struct file *file)
+{
+       int ret;
+
+       sb_start_write(file->f_path.mnt->mnt_sb);
+       ret = __mnt_want_write_file(file);
+       if (ret)
+               sb_end_write(file->f_path.mnt->mnt_sb);
+       return ret;
+}
 EXPORT_SYMBOL_GPL(mnt_want_write_file);
 
 /**
- * mnt_drop_write - give up write access to a mount
+ * __mnt_drop_write - give up write access to a mount
  * @mnt: the mount on which to give up write access
  *
  * Tells the low-level filesystem that we are done
  * performing writes to it.  Must be matched with
- * mnt_want_write() call above.
+ * __mnt_want_write() call above.
  */
-void mnt_drop_write(struct vfsmount *mnt)
+void __mnt_drop_write(struct vfsmount *mnt)
 {
        preempt_disable();
        mnt_dec_writers(real_mount(mnt));
        preempt_enable();
 }
+
+/**
+ * mnt_drop_write - give up write access to a mount
+ * @mnt: the mount on which to give up write access
+ *
+ * Tells the low-level filesystem that we are done performing writes to it and
+ * also allows filesystem to be frozen again.  Must be matched with
+ * mnt_want_write() call above.
+ */
+void mnt_drop_write(struct vfsmount *mnt)
+{
+       __mnt_drop_write(mnt);
+       sb_end_write(mnt->mnt_sb);
+}
 EXPORT_SYMBOL_GPL(mnt_drop_write);
 
+void __mnt_drop_write_file(struct file *file)
+{
+       __mnt_drop_write(file->f_path.mnt);
+}
+
 void mnt_drop_write_file(struct file *file)
 {
        mnt_drop_write(file->f_path.mnt);
index f90f4f5cd421dc7be9db2151ead1d8d458acf096..db7ad719628a655f748d30257f00d95f9191ec6e 100644 (file)
@@ -30,7 +30,7 @@ config NFS_FS
          If unsure, say N.
 
 config NFS_V2
-       bool "NFS client support for NFS version 2"
+       tristate "NFS client support for NFS version 2"
        depends on NFS_FS
        default y
        help
@@ -40,7 +40,7 @@ config NFS_V2
          If unsure, say Y.
 
 config NFS_V3
-       bool "NFS client support for NFS version 3"
+       tristate "NFS client support for NFS version 3"
        depends on NFS_FS
        default y
        help
@@ -72,7 +72,7 @@ config NFS_V3_ACL
          If unsure, say N.
 
 config NFS_V4
-       bool "NFS client support for NFS version 4"
+       tristate "NFS client support for NFS version 4"
        depends on NFS_FS
        select SUNRPC_GSS
        select KEYS
@@ -86,11 +86,18 @@ config NFS_V4
 
          If unsure, say Y.
 
+config NFS_SWAP
+       bool "Provide swap over NFS support"
+       default n
+       depends on NFS_FS
+       select SUNRPC_SWAP
+       help
+         This option enables swapon to work on files located on NFS mounts.
+
 config NFS_V4_1
        bool "NFS client support for NFSv4.1 (EXPERIMENTAL)"
-       depends on NFS_FS && NFS_V4 && EXPERIMENTAL
+       depends on NFS_V4 && EXPERIMENTAL
        select SUNRPC_BACKCHANNEL
-       select PNFS_FILE_LAYOUT
        help
          This option enables support for minor version 1 of the NFSv4 protocol
          (RFC 5661) in the kernel's NFS client.
@@ -99,15 +106,17 @@ config NFS_V4_1
 
 config PNFS_FILE_LAYOUT
        tristate
+       depends on NFS_V4_1
+       default m
 
 config PNFS_BLOCK
        tristate
-       depends on NFS_FS && NFS_V4_1 && BLK_DEV_DM
+       depends on NFS_V4_1 && BLK_DEV_DM
        default m
 
 config PNFS_OBJLAYOUT
        tristate
-       depends on NFS_FS && NFS_V4_1 && SCSI_OSD_ULD
+       depends on NFS_V4_1 && SCSI_OSD_ULD
        default m
 
 config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN
index 7ddd45d9f1707d24a7661a402d95f58aef108c03..8bf3a3f6925ab7459d93416f171b05d8cbf13cb7 100644 (file)
@@ -9,17 +9,23 @@ nfs-y                         := client.o dir.o file.o getroot.o inode.o super.o \
                           write.o namespace.o mount_clnt.o \
                           dns_resolve.o cache_lib.o
 nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
-nfs-$(CONFIG_NFS_V2)   += proc.o nfs2xdr.o
-nfs-$(CONFIG_NFS_V3)   += nfs3proc.o nfs3xdr.o
-nfs-$(CONFIG_NFS_V3_ACL)       += nfs3acl.o
-nfs-$(CONFIG_NFS_V4)   += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \
-                          delegation.o idmap.o \
-                          callback.o callback_xdr.o callback_proc.o \
-                          nfs4namespace.o
-nfs-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o
-nfs-$(CONFIG_SYSCTL) += sysctl.o
+nfs-$(CONFIG_SYSCTL)   += sysctl.o
 nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o
 
+obj-$(CONFIG_NFS_V2) += nfs2.o
+nfs2-y := nfs2super.o proc.o nfs2xdr.o
+
+obj-$(CONFIG_NFS_V3) += nfs3.o
+nfs3-y := nfs3super.o nfs3client.o nfs3proc.o nfs3xdr.o
+nfs3-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
+
+obj-$(CONFIG_NFS_V4) += nfs4.o
+nfs4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \
+         delegation.o idmap.o callback.o callback_xdr.o callback_proc.o \
+         nfs4namespace.o nfs4getroot.o nfs4client.o
+nfs4-$(CONFIG_SYSCTL)  += nfs4sysctl.o
+nfs4-$(CONFIG_NFS_V4_1)        += pnfs.o pnfs_dev.o
+
 obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o
 nfs_layout_nfsv41_files-y := nfs4filelayout.o nfs4filelayoutdev.o
 
index 7ae8a608956f60d7343fdfa9e77be7532b2db17d..dd392ed5f2e28ef48a85b37d0dc2ade6ab903d80 100644 (file)
@@ -228,6 +228,14 @@ bl_end_par_io_read(void *data, int unused)
        schedule_work(&rdata->task.u.tk_work);
 }
 
+static bool
+bl_check_alignment(u64 offset, u32 len, unsigned long blkmask)
+{
+       if ((offset & blkmask) || (len & blkmask))
+               return false;
+       return true;
+}
+
 static enum pnfs_try_status
 bl_read_pagelist(struct nfs_read_data *rdata)
 {
@@ -244,6 +252,9 @@ bl_read_pagelist(struct nfs_read_data *rdata)
        dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__,
               rdata->pages.npages, f_offset, (unsigned int)rdata->args.count);
 
+       if (!bl_check_alignment(f_offset, rdata->args.count, PAGE_CACHE_MASK))
+               goto use_mds;
+
        par = alloc_parallel(rdata);
        if (!par)
                goto use_mds;
@@ -552,7 +563,7 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
        struct bio *bio = NULL;
        struct pnfs_block_extent *be = NULL, *cow_read = NULL;
        sector_t isect, last_isect = 0, extent_length = 0;
-       struct parallel_io *par;
+       struct parallel_io *par = NULL;
        loff_t offset = wdata->args.offset;
        size_t count = wdata->args.count;
        struct page **pages = wdata->args.pages;
@@ -563,6 +574,10 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
            NFS_SERVER(header->inode)->pnfs_blksize >> PAGE_CACHE_SHIFT;
 
        dprintk("%s enter, %Zu@%lld\n", __func__, count, offset);
+       /* Check for alignment first */
+       if (!bl_check_alignment(offset, count, PAGE_CACHE_MASK))
+               goto out_mds;
+
        /* At this point, wdata->pages is a (sequential) list of nfs_pages.
         * We want to write each, and if there is an error set pnfs_error
         * to have it redone using nfs.
@@ -996,14 +1011,32 @@ bl_clear_layoutdriver(struct nfs_server *server)
        return 0;
 }
 
+static void
+bl_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
+{
+       if (!bl_check_alignment(req->wb_offset, req->wb_bytes, PAGE_CACHE_MASK))
+               nfs_pageio_reset_read_mds(pgio);
+       else
+               pnfs_generic_pg_init_read(pgio, req);
+}
+
+static void
+bl_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
+{
+       if (!bl_check_alignment(req->wb_offset, req->wb_bytes, PAGE_CACHE_MASK))
+               nfs_pageio_reset_write_mds(pgio);
+       else
+               pnfs_generic_pg_init_write(pgio, req);
+}
+
 static const struct nfs_pageio_ops bl_pg_read_ops = {
-       .pg_init = pnfs_generic_pg_init_read,
+       .pg_init = bl_pg_init_read,
        .pg_test = pnfs_generic_pg_test,
        .pg_doio = pnfs_generic_pg_readpages,
 };
 
 static const struct nfs_pageio_ops bl_pg_write_ops = {
-       .pg_init = pnfs_generic_pg_init_write,
+       .pg_init = bl_pg_init_write,
        .pg_test = pnfs_generic_pg_test,
        .pg_doio = pnfs_generic_pg_writepages,
 };
index 23ff18fe080afd0b6470e6d72df4fa9a8b9d0dab..4c8459e5bdeef904e930cff50def65f7ac91f672 100644 (file)
@@ -37,31 +37,7 @@ static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
 static DEFINE_MUTEX(nfs_callback_mutex);
 static struct svc_program nfs4_callback_program;
 
-unsigned int nfs_callback_set_tcpport;
-unsigned short nfs_callback_tcpport;
 unsigned short nfs_callback_tcpport6;
-#define NFS_CALLBACK_MAXPORTNR (65535U)
-
-static int param_set_portnr(const char *val, const struct kernel_param *kp)
-{
-       unsigned long num;
-       int ret;
-
-       if (!val)
-               return -EINVAL;
-       ret = strict_strtoul(val, 0, &num);
-       if (ret == -EINVAL || num > NFS_CALLBACK_MAXPORTNR)
-               return -EINVAL;
-       *((unsigned int *)kp->arg) = num;
-       return 0;
-}
-static struct kernel_param_ops param_ops_portnr = {
-       .set = param_set_portnr,
-       .get = param_get_uint,
-};
-#define param_check_portnr(name, p) __param_check(name, p, unsigned int);
-
-module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644);
 
 /*
  * This is the NFSv4 callback kernel thread.
@@ -265,6 +241,10 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
                ret = -ENOMEM;
                goto out_err;
        }
+       /* As there is only one thread we need to over-ride the
+        * default maximum of 80 connections
+        */
+       serv->sv_maxconn = 1024;
 
        ret = svc_bind(serv, net);
        if (ret < 0) {
index a5527c90a5aae67a67e2320ffa9d2dea1b00d4d3..b44d7b128b71973678ff265a9ea13dbeb1415c00 100644 (file)
@@ -192,7 +192,7 @@ extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args,
                                    struct cb_process_state *cps);
 extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
                                   struct cb_process_state *cps);
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt);
 extern void nfs_callback_down(int minorversion);
 extern int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation,
index e64b01d2a338274a459bc9c56c60dacf282aad84..742ff4ffced78fe9071da9c15ee22f458bfde3ca 100644 (file)
@@ -863,7 +863,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
                .drc_status = 0,
                .clp = NULL,
                .slotid = NFS4_NO_SLOT,
-               .net = rqstp->rq_xprt->xpt_net,
+               .net = SVC_NET(rqstp),
        };
        unsigned int nops = 0;
 
@@ -879,7 +879,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
                return rpc_garbage_args;
 
        if (hdr_arg.minorversion == 0) {
-               cps.clp = nfs4_find_client_ident(rqstp->rq_xprt->xpt_net, hdr_arg.cb_ident);
+               cps.clp = nfs4_find_client_ident(SVC_NET(rqstp), hdr_arg.cb_ident);
                if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp))
                        return rpc_drop_reply;
        }
index f005b5bebdc73bba4d548d134699dd4f00c471ca..9fc0d9dfc91b8a5e4226b0fcfb56bb55658f0869 100644 (file)
 #include "internal.h"
 #include "fscache.h"
 #include "pnfs.h"
+#include "nfs.h"
 #include "netns.h"
 
 #define NFSDBG_FACILITY                NFSDBG_CLIENT
 
 static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
-#ifdef CONFIG_NFS_V4
-
-/*
- * Get a unique NFSv4.0 callback identifier which will be used
- * by the V4.0 callback service to lookup the nfs_client struct
- */
-static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion)
-{
-       int ret = 0;
-       struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
-
-       if (clp->rpc_ops->version != 4 || minorversion != 0)
-               return ret;
-retry:
-       if (!idr_pre_get(&nn->cb_ident_idr, GFP_KERNEL))
-               return -ENOMEM;
-       spin_lock(&nn->nfs_client_lock);
-       ret = idr_get_new(&nn->cb_ident_idr, clp, &clp->cl_cb_ident);
-       spin_unlock(&nn->nfs_client_lock);
-       if (ret == -EAGAIN)
-               goto retry;
-       return ret;
-}
-#endif /* CONFIG_NFS_V4 */
-
-/*
- * Turn off NFSv4 uid/gid mapping when using AUTH_SYS
- */
-static bool nfs4_disable_idmapping = true;
+static DEFINE_SPINLOCK(nfs_version_lock);
+static DEFINE_MUTEX(nfs_version_mutex);
+static LIST_HEAD(nfs_versions);
 
 /*
  * RPC cruft for NFS
  */
 static const struct rpc_version *nfs_version[5] = {
-#ifdef CONFIG_NFS_V2
-       [2]                     = &nfs_version2,
-#endif
-#ifdef CONFIG_NFS_V3
-       [3]                     = &nfs_version3,
-#endif
-#ifdef CONFIG_NFS_V4
-       [4]                     = &nfs_version4,
-#endif
+       [2] = NULL,
+       [3] = NULL,
+       [4] = NULL,
 };
 
 const struct rpc_program nfs_program = {
@@ -114,32 +83,64 @@ struct rpc_stat nfs_rpcstat = {
        .program                = &nfs_program
 };
 
+static struct nfs_subversion *find_nfs_version(unsigned int version)
+{
+       struct nfs_subversion *nfs;
+       spin_lock(&nfs_version_lock);
 
-#ifdef CONFIG_NFS_V3_ACL
-static struct rpc_stat         nfsacl_rpcstat = { &nfsacl_program };
-static const struct rpc_version *nfsacl_version[] = {
-       [3]                     = &nfsacl_version3,
-};
+       list_for_each_entry(nfs, &nfs_versions, list) {
+               if (nfs->rpc_ops->version == version) {
+                       spin_unlock(&nfs_version_lock);
+                       return nfs;
+               }
+       };
 
-const struct rpc_program nfsacl_program = {
-       .name                   = "nfsacl",
-       .number                 = NFS_ACL_PROGRAM,
-       .nrvers                 = ARRAY_SIZE(nfsacl_version),
-       .version                = nfsacl_version,
-       .stats                  = &nfsacl_rpcstat,
-};
-#endif  /* CONFIG_NFS_V3_ACL */
-
-struct nfs_client_initdata {
-       unsigned long init_flags;
-       const char *hostname;
-       const struct sockaddr *addr;
-       size_t addrlen;
-       const struct nfs_rpc_ops *rpc_ops;
-       int proto;
-       u32 minorversion;
-       struct net *net;
-};
+       spin_unlock(&nfs_version_lock);
+       return ERR_PTR(-EPROTONOSUPPORT);;
+}
+
+struct nfs_subversion *get_nfs_version(unsigned int version)
+{
+       struct nfs_subversion *nfs = find_nfs_version(version);
+
+       if (IS_ERR(nfs)) {
+               mutex_lock(&nfs_version_mutex);
+               request_module("nfs%d", version);
+               nfs = find_nfs_version(version);
+               mutex_unlock(&nfs_version_mutex);
+       }
+
+       if (!IS_ERR(nfs))
+               try_module_get(nfs->owner);
+       return nfs;
+}
+
+void put_nfs_version(struct nfs_subversion *nfs)
+{
+       module_put(nfs->owner);
+}
+
+void register_nfs_version(struct nfs_subversion *nfs)
+{
+       spin_lock(&nfs_version_lock);
+
+       list_add(&nfs->list, &nfs_versions);
+       nfs_version[nfs->rpc_ops->version] = nfs->rpc_vers;
+
+       spin_unlock(&nfs_version_lock);
+}
+EXPORT_SYMBOL_GPL(register_nfs_version);
+
+void unregister_nfs_version(struct nfs_subversion *nfs)
+{
+       spin_lock(&nfs_version_lock);
+
+       nfs_version[nfs->rpc_ops->version] = NULL;
+       list_del(&nfs->list);
+
+       spin_unlock(&nfs_version_lock);
+}
+EXPORT_SYMBOL_GPL(unregister_nfs_version);
 
 /*
  * Allocate a shared client record
@@ -147,7 +148,7 @@ struct nfs_client_initdata {
  * Since these are allocated/deallocated very rarely, we don't
  * bother putting them in a slab cache...
  */
-static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
+struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
 {
        struct nfs_client *clp;
        struct rpc_cred *cred;
@@ -156,7 +157,10 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
        if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
                goto error_0;
 
-       clp->rpc_ops = cl_init->rpc_ops;
+       clp->cl_nfs_mod = cl_init->nfs_mod;
+       try_module_get(clp->cl_nfs_mod->owner);
+
+       clp->rpc_ops = clp->cl_nfs_mod->rpc_ops;
 
        atomic_set(&clp->cl_count, 1);
        clp->cl_cons_state = NFS_CS_INITING;
@@ -177,18 +181,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
        clp->cl_proto = cl_init->proto;
        clp->cl_net = get_net(cl_init->net);
 
-#ifdef CONFIG_NFS_V4
-       err = nfs_get_cb_ident_idr(clp, cl_init->minorversion);
-       if (err)
-               goto error_cleanup;
-
-       spin_lock_init(&clp->cl_lock);
-       INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
-       rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
-       clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
-       clp->cl_minorversion = cl_init->minorversion;
-       clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion];
-#endif
        cred = rpc_lookup_machine_cred("*");
        if (!IS_ERR(cred))
                clp->cl_machine_cred = cred;
@@ -197,51 +189,14 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
        return clp;
 
 error_cleanup:
+       put_nfs_version(clp->cl_nfs_mod);
        kfree(clp);
 error_0:
        return ERR_PTR(err);
 }
+EXPORT_SYMBOL_GPL(nfs_alloc_client);
 
-#ifdef CONFIG_NFS_V4
-#ifdef CONFIG_NFS_V4_1
-static void nfs4_shutdown_session(struct nfs_client *clp)
-{
-       if (nfs4_has_session(clp)) {
-               nfs4_destroy_session(clp->cl_session);
-               nfs4_destroy_clientid(clp);
-       }
-
-}
-#else /* CONFIG_NFS_V4_1 */
-static void nfs4_shutdown_session(struct nfs_client *clp)
-{
-}
-#endif /* CONFIG_NFS_V4_1 */
-
-/*
- * Destroy the NFS4 callback service
- */
-static void nfs4_destroy_callback(struct nfs_client *clp)
-{
-       if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
-               nfs_callback_down(clp->cl_mvops->minor_version);
-}
-
-static void nfs4_shutdown_client(struct nfs_client *clp)
-{
-       if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
-               nfs4_kill_renewd(clp);
-       nfs4_shutdown_session(clp);
-       nfs4_destroy_callback(clp);
-       if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
-               nfs_idmap_delete(clp);
-
-       rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
-       kfree(clp->cl_serverowner);
-       kfree(clp->cl_serverscope);
-       kfree(clp->cl_implid);
-}
-
+#if IS_ENABLED(CONFIG_NFS_V4)
 /* idr_remove_all is not needed as all id's are removed by nfs_put_client */
 void nfs_cleanup_cb_ident_idr(struct net *net)
 {
@@ -264,16 +219,7 @@ static void pnfs_init_server(struct nfs_server *server)
        rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC");
 }
 
-static void nfs4_destroy_server(struct nfs_server *server)
-{
-       nfs4_purge_state_owners(server);
-}
-
 #else
-static void nfs4_shutdown_client(struct nfs_client *clp)
-{
-}
-
 void nfs_cleanup_cb_ident_idr(struct net *net)
 {
 }
@@ -291,12 +237,10 @@ static void pnfs_init_server(struct nfs_server *server)
 /*
  * Destroy a shared client record
  */
-static void nfs_free_client(struct nfs_client *clp)
+void nfs_free_client(struct nfs_client *clp)
 {
        dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version);
 
-       nfs4_shutdown_client(clp);
-
        nfs_fscache_release_client_cookie(clp);
 
        /* -EIO all pending I/O */
@@ -307,11 +251,13 @@ static void nfs_free_client(struct nfs_client *clp)
                put_rpccred(clp->cl_machine_cred);
 
        put_net(clp->cl_net);
+       put_nfs_version(clp->cl_nfs_mod);
        kfree(clp->cl_hostname);
        kfree(clp);
 
        dprintk("<-- nfs_free_client()\n");
 }
+EXPORT_SYMBOL_GPL(nfs_free_client);
 
 /*
  * Release a reference to a shared client record
@@ -333,7 +279,7 @@ void nfs_put_client(struct nfs_client *clp)
 
                BUG_ON(!list_empty(&clp->cl_superblocks));
 
-               nfs_free_client(clp);
+               clp->rpc_ops->free_client(clp);
        }
 }
 EXPORT_SYMBOL_GPL(nfs_put_client);
@@ -412,8 +358,8 @@ static int nfs_sockaddr_cmp_ip4(const struct sockaddr *sa1,
  * Test if two socket addresses represent the same actual socket,
  * by comparing (only) relevant fields, excluding the port number.
  */
-static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
-                                    const struct sockaddr *sa2)
+int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
+                             const struct sockaddr *sa2)
 {
        if (sa1->sa_family != sa2->sa_family)
                return 0;
@@ -426,6 +372,7 @@ static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
        }
        return 0;
 }
+EXPORT_SYMBOL_GPL(nfs_sockaddr_match_ipaddr);
 #endif /* CONFIG_NFS_V4_1 */
 
 /*
@@ -447,33 +394,6 @@ static int nfs_sockaddr_cmp(const struct sockaddr *sa1,
        return 0;
 }
 
-#if defined(CONFIG_NFS_V4_1)
-/* Common match routine for v4.0 and v4.1 callback services */
-static bool nfs4_cb_match_client(const struct sockaddr *addr,
-               struct nfs_client *clp, u32 minorversion)
-{
-       struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
-
-       /* Don't match clients that failed to initialise */
-       if (!(clp->cl_cons_state == NFS_CS_READY ||
-           clp->cl_cons_state == NFS_CS_SESSION_INITING))
-               return false;
-
-       smp_rmb();
-
-       /* Match the version and minorversion */
-       if (clp->rpc_ops->version != 4 ||
-           clp->cl_minorversion != minorversion)
-               return false;
-
-       /* Match only the IP address, not the port number */
-       if (!nfs_sockaddr_match_ipaddr(addr, clap))
-               return false;
-
-       return true;
-}
-#endif /* CONFIG_NFS_V4_1 */
-
 /*
  * Find an nfs_client on the list that matches the initialisation data
  * that is supplied.
@@ -491,7 +411,7 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
                        continue;
 
                /* Different NFS versions cannot share the same nfs_client */
-               if (clp->rpc_ops != data->rpc_ops)
+               if (clp->rpc_ops != data->nfs_mod->rpc_ops)
                        continue;
 
                if (clp->cl_proto != data->proto)
@@ -519,6 +439,7 @@ int nfs_wait_client_init_complete(const struct nfs_client *clp)
        return wait_event_killable(nfs_client_active_wq,
                        nfs_client_init_is_complete(clp));
 }
+EXPORT_SYMBOL_GPL(nfs_wait_client_init_complete);
 
 /*
  * Found an existing client.  Make sure it's ready before returning.
@@ -552,7 +473,7 @@ nfs_found_client(const struct nfs_client_initdata *cl_init,
  * Look up a client by IP address and protocol version
  * - creates a new record if one doesn't yet exist
  */
-static struct nfs_client *
+struct nfs_client *
 nfs_get_client(const struct nfs_client_initdata *cl_init,
               const struct rpc_timeout *timeparms,
               const char *ip_addr,
@@ -560,9 +481,10 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
 {
        struct nfs_client *clp, *new = NULL;
        struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id);
+       const struct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops;
 
        dprintk("--> nfs_get_client(%s,v%u)\n",
-               cl_init->hostname ?: "", cl_init->rpc_ops->version);
+               cl_init->hostname ?: "", rpc_ops->version);
 
        /* see if the client already exists */
        do {
@@ -572,27 +494,27 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
                if (clp) {
                        spin_unlock(&nn->nfs_client_lock);
                        if (new)
-                               nfs_free_client(new);
+                               new->rpc_ops->free_client(new);
                        return nfs_found_client(cl_init, clp);
                }
                if (new) {
                        list_add(&new->cl_share_link, &nn->nfs_client_list);
                        spin_unlock(&nn->nfs_client_lock);
                        new->cl_flags = cl_init->init_flags;
-                       return cl_init->rpc_ops->init_client(new,
-                                               timeparms, ip_addr,
-                                               authflavour);
+                       return rpc_ops->init_client(new, timeparms, ip_addr,
+                                                   authflavour);
                }
 
                spin_unlock(&nn->nfs_client_lock);
 
-               new = nfs_alloc_client(cl_init);
+               new = rpc_ops->alloc_client(cl_init);
        } while (!IS_ERR(new));
 
        dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n",
                cl_init->hostname ?: "", PTR_ERR(new));
        return new;
 }
+EXPORT_SYMBOL_GPL(nfs_get_client);
 
 /*
  * Mark a server as ready or failed
@@ -603,11 +525,12 @@ void nfs_mark_client_ready(struct nfs_client *clp, int state)
        clp->cl_cons_state = state;
        wake_up_all(&nfs_client_active_wq);
 }
+EXPORT_SYMBOL_GPL(nfs_mark_client_ready);
 
 /*
  * Initialise the timeout values for a connection
  */
-static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
+void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
                                    unsigned int timeo, unsigned int retrans)
 {
        to->to_initval = timeo * HZ / 10;
@@ -644,13 +567,14 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
                BUG();
        }
 }
+EXPORT_SYMBOL_GPL(nfs_init_timeout_values);
 
 /*
  * Create an RPC client handle
  */
-static int nfs_create_rpc_client(struct nfs_client *clp,
-                                const struct rpc_timeout *timeparms,
-                                rpc_authflavor_t flavor)
+int nfs_create_rpc_client(struct nfs_client *clp,
+                         const struct rpc_timeout *timeparms,
+                         rpc_authflavor_t flavor)
 {
        struct rpc_clnt         *clnt = NULL;
        struct rpc_create_args args = {
@@ -683,6 +607,7 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
        clp->cl_rpcclient = clnt;
        return 0;
 }
+EXPORT_SYMBOL_GPL(nfs_create_rpc_client);
 
 /*
  * Version 2 or 3 client destruction
@@ -734,40 +659,10 @@ static int nfs_start_lockd(struct nfs_server *server)
        return 0;
 }
 
-/*
- * Initialise an NFSv3 ACL client connection
- */
-#ifdef CONFIG_NFS_V3_ACL
-static void nfs_init_server_aclclient(struct nfs_server *server)
-{
-       if (server->nfs_client->rpc_ops->version != 3)
-               goto out_noacl;
-       if (server->flags & NFS_MOUNT_NOACL)
-               goto out_noacl;
-
-       server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3);
-       if (IS_ERR(server->client_acl))
-               goto out_noacl;
-
-       /* No errors! Assume that Sun nfsacls are supported */
-       server->caps |= NFS_CAP_ACLS;
-       return;
-
-out_noacl:
-       server->caps &= ~NFS_CAP_ACLS;
-}
-#else
-static inline void nfs_init_server_aclclient(struct nfs_server *server)
-{
-       server->flags &= ~NFS_MOUNT_NOACL;
-       server->caps &= ~NFS_CAP_ACLS;
-}
-#endif
-
 /*
  * Create a general RPC client
  */
-static int nfs_init_server_rpcclient(struct nfs_server *server,
+int nfs_init_server_rpcclient(struct nfs_server *server,
                const struct rpc_timeout *timeo,
                rpc_authflavor_t pseudoflavour)
 {
@@ -799,6 +694,7 @@ static int nfs_init_server_rpcclient(struct nfs_server *server,
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(nfs_init_server_rpcclient);
 
 /**
  * nfs_init_client - Initialise an NFS2 or NFS3 client
@@ -838,18 +734,20 @@ error:
        dprintk("<-- nfs_init_client() = xerror %d\n", error);
        return ERR_PTR(error);
 }
+EXPORT_SYMBOL_GPL(nfs_init_client);
 
 /*
  * Create a version 2 or 3 client
  */
 static int nfs_init_server(struct nfs_server *server,
-                          const struct nfs_parsed_mount_data *data)
+                          const struct nfs_parsed_mount_data *data,
+                          struct nfs_subversion *nfs_mod)
 {
        struct nfs_client_initdata cl_init = {
                .hostname = data->nfs_server.hostname,
                .addr = (const struct sockaddr *)&data->nfs_server.address,
                .addrlen = data->nfs_server.addrlen,
-               .rpc_ops = NULL,
+               .nfs_mod = nfs_mod,
                .proto = data->nfs_server.protocol,
                .net = data->net,
        };
@@ -859,21 +757,6 @@ static int nfs_init_server(struct nfs_server *server,
 
        dprintk("--> nfs_init_server()\n");
 
-       switch (data->version) {
-#ifdef CONFIG_NFS_V2
-       case 2:
-               cl_init.rpc_ops = &nfs_v2_clientops;
-               break;
-#endif
-#ifdef CONFIG_NFS_V3
-       case 3:
-               cl_init.rpc_ops = &nfs_v3_clientops;
-               break;
-#endif
-       default:
-               return -EPROTONOSUPPORT;
-       }
-
        nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
                        data->timeo, data->retrans);
        if (data->flags & NFS_MOUNT_NORESVPORT)
@@ -927,8 +810,6 @@ static int nfs_init_server(struct nfs_server *server,
        server->mountd_protocol = data->mount_server.protocol;
 
        server->namelen  = data->namlen;
-       /* Create a client RPC handle for the NFSv3 ACL management interface */
-       nfs_init_server_aclclient(server);
        dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp);
        return 0;
 
@@ -975,7 +856,6 @@ static void nfs_server_set_fsinfo(struct nfs_server *server,
                server->wsize = NFS_MAX_FILE_IO_SIZE;
        server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
        server->pnfs_blksize = fsinfo->blksize;
-       set_pnfs_layoutdriver(server, mntfh, fsinfo->layouttype);
 
        server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL);
 
@@ -1001,7 +881,7 @@ static void nfs_server_set_fsinfo(struct nfs_server *server,
 /*
  * Probe filesystem information, including the FSID on v2/v3
  */
-static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr)
+int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr)
 {
        struct nfs_fsinfo fsinfo;
        struct nfs_client *clp = server->nfs_client;
@@ -1041,11 +921,12 @@ out_error:
        dprintk("nfs_probe_fsinfo: error = %d\n", -error);
        return error;
 }
+EXPORT_SYMBOL_GPL(nfs_probe_fsinfo);
 
 /*
  * Copy useful information when duplicating a server record
  */
-static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source)
+void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source)
 {
        target->flags = source->flags;
        target->rsize = source->rsize;
@@ -1057,8 +938,9 @@ static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_serve
        target->caps = source->caps;
        target->options = source->options;
 }
+EXPORT_SYMBOL_GPL(nfs_server_copy_userdata);
 
-static void nfs_server_insert_lists(struct nfs_server *server)
+void nfs_server_insert_lists(struct nfs_server *server)
 {
        struct nfs_client *clp = server->nfs_client;
        struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
@@ -1070,6 +952,7 @@ static void nfs_server_insert_lists(struct nfs_server *server)
        spin_unlock(&nn->nfs_client_lock);
 
 }
+EXPORT_SYMBOL_GPL(nfs_server_insert_lists);
 
 static void nfs_server_remove_lists(struct nfs_server *server)
 {
@@ -1092,7 +975,7 @@ static void nfs_server_remove_lists(struct nfs_server *server)
 /*
  * Allocate and initialise a server record
  */
-static struct nfs_server *nfs_alloc_server(void)
+struct nfs_server *nfs_alloc_server(void)
 {
        struct nfs_server *server;
 
@@ -1129,6 +1012,7 @@ static struct nfs_server *nfs_alloc_server(void)
 
        return server;
 }
+EXPORT_SYMBOL_GPL(nfs_alloc_server);
 
 /*
  * Free up a server record
@@ -1138,7 +1022,6 @@ void nfs_free_server(struct nfs_server *server)
        dprintk("--> nfs_free_server()\n");
 
        nfs_server_remove_lists(server);
-       unset_pnfs_layoutdriver(server);
 
        if (server->destroy != NULL)
                server->destroy(server);
@@ -1158,13 +1041,14 @@ void nfs_free_server(struct nfs_server *server)
        nfs_release_automount_timer();
        dprintk("<-- nfs_free_server()\n");
 }
+EXPORT_SYMBOL_GPL(nfs_free_server);
 
 /*
  * Create a version 2 or 3 volume record
  * - keyed on server and FSID
  */
-struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
-                                    struct nfs_fh *mntfh)
+struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info,
+                                    struct nfs_subversion *nfs_mod)
 {
        struct nfs_server *server;
        struct nfs_fattr *fattr;
@@ -1180,7 +1064,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
                goto error;
 
        /* Get a client representation */
-       error = nfs_init_server(server, data);
+       error = nfs_init_server(server, mount_info->parsed, nfs_mod);
        if (error < 0)
                goto error;
 
@@ -1189,13 +1073,13 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
        BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
 
        /* Probe the root fh to retrieve its FSID */
-       error = nfs_probe_fsinfo(server, mntfh, fattr);
+       error = nfs_probe_fsinfo(server, mount_info->mntfh, fattr);
        if (error < 0)
                goto error;
        if (server->nfs_client->rpc_ops->version == 3) {
                if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
                        server->namelen = NFS3_MAXNAMLEN;
-               if (!(data->flags & NFS_MOUNT_NORDIRPLUS))
+               if (!(mount_info->parsed->flags & NFS_MOUNT_NORDIRPLUS))
                        server->caps |= NFS_CAP_READDIRPLUS;
        } else {
                if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN)
@@ -1203,7 +1087,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
        }
 
        if (!(fattr->valid & NFS_ATTR_FATTR)) {
-               error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr);
+               error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, fattr);
                if (error < 0) {
                        dprintk("nfs_create_server: getattr error = %d\n", -error);
                        goto error;
@@ -1225,522 +1109,7 @@ error:
        nfs_free_server(server);
        return ERR_PTR(error);
 }
-
-#ifdef CONFIG_NFS_V4
-/*
- * NFSv4.0 callback thread helper
- *
- * Find a client by callback identifier
- */
-struct nfs_client *
-nfs4_find_client_ident(struct net *net, int cb_ident)
-{
-       struct nfs_client *clp;
-       struct nfs_net *nn = net_generic(net, nfs_net_id);
-
-       spin_lock(&nn->nfs_client_lock);
-       clp = idr_find(&nn->cb_ident_idr, cb_ident);
-       if (clp)
-               atomic_inc(&clp->cl_count);
-       spin_unlock(&nn->nfs_client_lock);
-       return clp;
-}
-
-#if defined(CONFIG_NFS_V4_1)
-/*
- * NFSv4.1 callback thread helper
- * For CB_COMPOUND calls, find a client by IP address, protocol version,
- * minorversion, and sessionID
- *
- * Returns NULL if no such client
- */
-struct nfs_client *
-nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
-                          struct nfs4_sessionid *sid)
-{
-       struct nfs_client *clp;
-       struct nfs_net *nn = net_generic(net, nfs_net_id);
-
-       spin_lock(&nn->nfs_client_lock);
-       list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
-               if (nfs4_cb_match_client(addr, clp, 1) == false)
-                       continue;
-
-               if (!nfs4_has_session(clp))
-                       continue;
-
-               /* Match sessionid*/
-               if (memcmp(clp->cl_session->sess_id.data,
-                   sid->data, NFS4_MAX_SESSIONID_LEN) != 0)
-                       continue;
-
-               atomic_inc(&clp->cl_count);
-               spin_unlock(&nn->nfs_client_lock);
-               return clp;
-       }
-       spin_unlock(&nn->nfs_client_lock);
-       return NULL;
-}
-
-#else /* CONFIG_NFS_V4_1 */
-
-struct nfs_client *
-nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
-                          struct nfs4_sessionid *sid)
-{
-       return NULL;
-}
-#endif /* CONFIG_NFS_V4_1 */
-
-/*
- * Initialize the NFS4 callback service
- */
-static int nfs4_init_callback(struct nfs_client *clp)
-{
-       int error;
-
-       if (clp->rpc_ops->version == 4) {
-               struct rpc_xprt *xprt;
-
-               xprt = rcu_dereference_raw(clp->cl_rpcclient->cl_xprt);
-
-               if (nfs4_has_session(clp)) {
-                       error = xprt_setup_backchannel(xprt,
-                                               NFS41_BC_MIN_CALLBACKS);
-                       if (error < 0)
-                               return error;
-               }
-
-               error = nfs_callback_up(clp->cl_mvops->minor_version, xprt);
-               if (error < 0) {
-                       dprintk("%s: failed to start callback. Error = %d\n",
-                               __func__, error);
-                       return error;
-               }
-               __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
-       }
-       return 0;
-}
-
-/*
- * Initialize the minor version specific parts of an NFS4 client record
- */
-static int nfs4_init_client_minor_version(struct nfs_client *clp)
-{
-#if defined(CONFIG_NFS_V4_1)
-       if (clp->cl_mvops->minor_version) {
-               struct nfs4_session *session = NULL;
-               /*
-                * Create the session and mark it expired.
-                * When a SEQUENCE operation encounters the expired session
-                * it will do session recovery to initialize it.
-                */
-               session = nfs4_alloc_session(clp);
-               if (!session)
-                       return -ENOMEM;
-
-               clp->cl_session = session;
-               /*
-                * The create session reply races with the server back
-                * channel probe. Mark the client NFS_CS_SESSION_INITING
-                * so that the client back channel can find the
-                * nfs_client struct
-                */
-               nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING);
-       }
-#endif /* CONFIG_NFS_V4_1 */
-
-       return nfs4_init_callback(clp);
-}
-
-/**
- * nfs4_init_client - Initialise an NFS4 client record
- *
- * @clp: nfs_client to initialise
- * @timeparms: timeout parameters for underlying RPC transport
- * @ip_addr: callback IP address in presentation format
- * @authflavor: authentication flavor for underlying RPC transport
- *
- * Returns pointer to an NFS client, or an ERR_PTR value.
- */
-struct nfs_client *nfs4_init_client(struct nfs_client *clp,
-                                   const struct rpc_timeout *timeparms,
-                                   const char *ip_addr,
-                                   rpc_authflavor_t authflavour)
-{
-       char buf[INET6_ADDRSTRLEN + 1];
-       int error;
-
-       if (clp->cl_cons_state == NFS_CS_READY) {
-               /* the client is initialised already */
-               dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp);
-               return clp;
-       }
-
-       /* Check NFS protocol revision and initialize RPC op vector */
-       clp->rpc_ops = &nfs_v4_clientops;
-
-       __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
-       error = nfs_create_rpc_client(clp, timeparms, authflavour);
-       if (error < 0)
-               goto error;
-
-       /* If no clientaddr= option was specified, find a usable cb address */
-       if (ip_addr == NULL) {
-               struct sockaddr_storage cb_addr;
-               struct sockaddr *sap = (struct sockaddr *)&cb_addr;
-
-               error = rpc_localaddr(clp->cl_rpcclient, sap, sizeof(cb_addr));
-               if (error < 0)
-                       goto error;
-               error = rpc_ntop(sap, buf, sizeof(buf));
-               if (error < 0)
-                       goto error;
-               ip_addr = (const char *)buf;
-       }
-       strlcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
-
-       error = nfs_idmap_new(clp);
-       if (error < 0) {
-               dprintk("%s: failed to create idmapper. Error = %d\n",
-                       __func__, error);
-               goto error;
-       }
-       __set_bit(NFS_CS_IDMAP, &clp->cl_res_state);
-
-       error = nfs4_init_client_minor_version(clp);
-       if (error < 0)
-               goto error;
-
-       if (!nfs4_has_session(clp))
-               nfs_mark_client_ready(clp, NFS_CS_READY);
-       return clp;
-
-error:
-       nfs_mark_client_ready(clp, error);
-       nfs_put_client(clp);
-       dprintk("<-- nfs4_init_client() = xerror %d\n", error);
-       return ERR_PTR(error);
-}
-
-/*
- * Set up an NFS4 client
- */
-static int nfs4_set_client(struct nfs_server *server,
-               const char *hostname,
-               const struct sockaddr *addr,
-               const size_t addrlen,
-               const char *ip_addr,
-               rpc_authflavor_t authflavour,
-               int proto, const struct rpc_timeout *timeparms,
-               u32 minorversion, struct net *net)
-{
-       struct nfs_client_initdata cl_init = {
-               .hostname = hostname,
-               .addr = addr,
-               .addrlen = addrlen,
-               .rpc_ops = &nfs_v4_clientops,
-               .proto = proto,
-               .minorversion = minorversion,
-               .net = net,
-       };
-       struct nfs_client *clp;
-       int error;
-
-       dprintk("--> nfs4_set_client()\n");
-
-       if (server->flags & NFS_MOUNT_NORESVPORT)
-               set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
-
-       /* Allocate or find a client reference we can use */
-       clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour);
-       if (IS_ERR(clp)) {
-               error = PTR_ERR(clp);
-               goto error;
-       }
-
-       /*
-        * Query for the lease time on clientid setup or renewal
-        *
-        * Note that this will be set on nfs_clients that were created
-        * only for the DS role and did not set this bit, but now will
-        * serve a dual role.
-        */
-       set_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state);
-
-       server->nfs_client = clp;
-       dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp);
-       return 0;
-error:
-       dprintk("<-- nfs4_set_client() = xerror %d\n", error);
-       return error;
-}
-
-/*
- * Set up a pNFS Data Server client.
- *
- * Return any existing nfs_client that matches server address,port,version
- * and minorversion.
- *
- * For a new nfs_client, use a soft mount (default), a low retrans and a
- * low timeout interval so that if a connection is lost, we retry through
- * the MDS.
- */
-struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
-               const struct sockaddr *ds_addr, int ds_addrlen,
-               int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans)
-{
-       struct nfs_client_initdata cl_init = {
-               .addr = ds_addr,
-               .addrlen = ds_addrlen,
-               .rpc_ops = &nfs_v4_clientops,
-               .proto = ds_proto,
-               .minorversion = mds_clp->cl_minorversion,
-               .net = mds_clp->cl_net,
-       };
-       struct rpc_timeout ds_timeout;
-       struct nfs_client *clp;
-
-       /*
-        * Set an authflavor equual to the MDS value. Use the MDS nfs_client
-        * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS
-        * (section 13.1 RFC 5661).
-        */
-       nfs_init_timeout_values(&ds_timeout, ds_proto, ds_timeo, ds_retrans);
-       clp = nfs_get_client(&cl_init, &ds_timeout, mds_clp->cl_ipaddr,
-                            mds_clp->cl_rpcclient->cl_auth->au_flavor);
-
-       dprintk("<-- %s %p\n", __func__, clp);
-       return clp;
-}
-EXPORT_SYMBOL_GPL(nfs4_set_ds_client);
-
-/*
- * Session has been established, and the client marked ready.
- * Set the mount rsize and wsize with negotiated fore channel
- * attributes which will be bound checked in nfs_server_set_fsinfo.
- */
-static void nfs4_session_set_rwsize(struct nfs_server *server)
-{
-#ifdef CONFIG_NFS_V4_1
-       struct nfs4_session *sess;
-       u32 server_resp_sz;
-       u32 server_rqst_sz;
-
-       if (!nfs4_has_session(server->nfs_client))
-               return;
-       sess = server->nfs_client->cl_session;
-       server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead;
-       server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead;
-
-       if (server->rsize > server_resp_sz)
-               server->rsize = server_resp_sz;
-       if (server->wsize > server_rqst_sz)
-               server->wsize = server_rqst_sz;
-#endif /* CONFIG_NFS_V4_1 */
-}
-
-static int nfs4_server_common_setup(struct nfs_server *server,
-               struct nfs_fh *mntfh)
-{
-       struct nfs_fattr *fattr;
-       int error;
-
-       BUG_ON(!server->nfs_client);
-       BUG_ON(!server->nfs_client->rpc_ops);
-       BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
-
-       /* data servers support only a subset of NFSv4.1 */
-       if (is_ds_only_client(server->nfs_client))
-               return -EPROTONOSUPPORT;
-
-       fattr = nfs_alloc_fattr();
-       if (fattr == NULL)
-               return -ENOMEM;
-
-       /* We must ensure the session is initialised first */
-       error = nfs4_init_session(server);
-       if (error < 0)
-               goto out;
-
-       /* Probe the root fh to retrieve its FSID and filehandle */
-       error = nfs4_get_rootfh(server, mntfh);
-       if (error < 0)
-               goto out;
-
-       dprintk("Server FSID: %llx:%llx\n",
-                       (unsigned long long) server->fsid.major,
-                       (unsigned long long) server->fsid.minor);
-       dprintk("Mount FH: %d\n", mntfh->size);
-
-       nfs4_session_set_rwsize(server);
-
-       error = nfs_probe_fsinfo(server, mntfh, fattr);
-       if (error < 0)
-               goto out;
-
-       if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
-               server->namelen = NFS4_MAXNAMLEN;
-
-       nfs_server_insert_lists(server);
-       server->mount_time = jiffies;
-       server->destroy = nfs4_destroy_server;
-out:
-       nfs_free_fattr(fattr);
-       return error;
-}
-
-/*
- * Create a version 4 volume record
- */
-static int nfs4_init_server(struct nfs_server *server,
-               const struct nfs_parsed_mount_data *data)
-{
-       struct rpc_timeout timeparms;
-       int error;
-
-       dprintk("--> nfs4_init_server()\n");
-
-       nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
-                       data->timeo, data->retrans);
-
-       /* Initialise the client representation from the mount data */
-       server->flags = data->flags;
-       server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK;
-       if (!(data->flags & NFS_MOUNT_NORDIRPLUS))
-                       server->caps |= NFS_CAP_READDIRPLUS;
-       server->options = data->options;
-
-       /* Get a client record */
-       error = nfs4_set_client(server,
-                       data->nfs_server.hostname,
-                       (const struct sockaddr *)&data->nfs_server.address,
-                       data->nfs_server.addrlen,
-                       data->client_address,
-                       data->auth_flavors[0],
-                       data->nfs_server.protocol,
-                       &timeparms,
-                       data->minorversion,
-                       data->net);
-       if (error < 0)
-               goto error;
-
-       /*
-        * Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower
-        * authentication.
-        */
-       if (nfs4_disable_idmapping && data->auth_flavors[0] == RPC_AUTH_UNIX)
-               server->caps |= NFS_CAP_UIDGID_NOMAP;
-
-       if (data->rsize)
-               server->rsize = nfs_block_size(data->rsize, NULL);
-       if (data->wsize)
-               server->wsize = nfs_block_size(data->wsize, NULL);
-
-       server->acregmin = data->acregmin * HZ;
-       server->acregmax = data->acregmax * HZ;
-       server->acdirmin = data->acdirmin * HZ;
-       server->acdirmax = data->acdirmax * HZ;
-
-       server->port = data->nfs_server.port;
-
-       error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]);
-
-error:
-       /* Done */
-       dprintk("<-- nfs4_init_server() = %d\n", error);
-       return error;
-}
-
-/*
- * Create a version 4 volume record
- * - keyed on server and FSID
- */
-struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
-                                     struct nfs_fh *mntfh)
-{
-       struct nfs_server *server;
-       int error;
-
-       dprintk("--> nfs4_create_server()\n");
-
-       server = nfs_alloc_server();
-       if (!server)
-               return ERR_PTR(-ENOMEM);
-
-       /* set up the general RPC client */
-       error = nfs4_init_server(server, data);
-       if (error < 0)
-               goto error;
-
-       error = nfs4_server_common_setup(server, mntfh);
-       if (error < 0)
-               goto error;
-
-       dprintk("<-- nfs4_create_server() = %p\n", server);
-       return server;
-
-error:
-       nfs_free_server(server);
-       dprintk("<-- nfs4_create_server() = error %d\n", error);
-       return ERR_PTR(error);
-}
-
-/*
- * Create an NFS4 referral server record
- */
-struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
-                                              struct nfs_fh *mntfh)
-{
-       struct nfs_client *parent_client;
-       struct nfs_server *server, *parent_server;
-       int error;
-
-       dprintk("--> nfs4_create_referral_server()\n");
-
-       server = nfs_alloc_server();
-       if (!server)
-               return ERR_PTR(-ENOMEM);
-
-       parent_server = NFS_SB(data->sb);
-       parent_client = parent_server->nfs_client;
-
-       /* Initialise the client representation from the parent server */
-       nfs_server_copy_userdata(server, parent_server);
-       server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR;
-
-       /* Get a client representation.
-        * Note: NFSv4 always uses TCP, */
-       error = nfs4_set_client(server, data->hostname,
-                               data->addr,
-                               data->addrlen,
-                               parent_client->cl_ipaddr,
-                               data->authflavor,
-                               rpc_protocol(parent_server->client),
-                               parent_server->client->cl_timeout,
-                               parent_client->cl_mvops->minor_version,
-                               parent_client->cl_net);
-       if (error < 0)
-               goto error;
-
-       error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor);
-       if (error < 0)
-               goto error;
-
-       error = nfs4_server_common_setup(server, mntfh);
-       if (error < 0)
-               goto error;
-
-       dprintk("<-- nfs_create_referral_server() = %p\n", server);
-       return server;
-
-error:
-       nfs_free_server(server);
-       dprintk("<-- nfs4_create_referral_server() = error %d\n", error);
-       return ERR_PTR(error);
-}
-
-#endif /* CONFIG_NFS_V4 */
+EXPORT_SYMBOL_GPL(nfs_create_server);
 
 /*
  * Clone an NFS2, NFS3 or NFS4 server record
@@ -1780,8 +1149,6 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
                        flavor);
        if (error < 0)
                goto out_free_server;
-       if (!IS_ERR(source->client_acl))
-               nfs_init_server_aclclient(server);
 
        /* probe the filesystem info for this server filesystem */
        error = nfs_probe_fsinfo(server, fh, fattr_fsinfo);
@@ -1812,6 +1179,7 @@ out_free_server:
        dprintk("<-- nfs_clone_server() = error %d\n", error);
        return ERR_PTR(error);
 }
+EXPORT_SYMBOL_GPL(nfs_clone_server);
 
 void nfs_clients_init(struct net *net)
 {
@@ -1819,7 +1187,7 @@ void nfs_clients_init(struct net *net)
 
        INIT_LIST_HEAD(&nn->nfs_client_list);
        INIT_LIST_HEAD(&nn->nfs_volume_list);
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
        idr_init(&nn->cb_ident_idr);
 #endif
        spin_lock_init(&nn->nfs_client_lock);
@@ -2091,7 +1459,3 @@ void nfs_fs_proc_exit(void)
 }
 
 #endif /* CONFIG_PROC_FS */
-
-module_param(nfs4_disable_idmapping, bool, 0644);
-MODULE_PARM_DESC(nfs4_disable_idmapping,
-               "Turn off NFSv4 idmapping when using 'sec=sys'");
index bd3a9601d32d9915a70e1888ab5a710671e00aac..81c5eec3cf3803a610efee85e37d0f2276cbe108 100644 (file)
@@ -47,7 +47,7 @@ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
  *
  * Returns one if inode has the indicated delegation, otherwise zero.
  */
-int nfs_have_delegation(struct inode *inode, fmode_t flags)
+int nfs4_have_delegation(struct inode *inode, fmode_t flags)
 {
        struct nfs_delegation *delegation;
        int ret = 0;
@@ -388,7 +388,7 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode)
  *
  * Returns zero on success, or a negative errno value.
  */
-int nfs_inode_return_delegation(struct inode *inode)
+int nfs4_inode_return_delegation(struct inode *inode)
 {
        struct nfs_server *server = NFS_SERVER(inode);
        struct nfs_inode *nfsi = NFS_I(inode);
@@ -417,9 +417,8 @@ static void nfs_mark_return_delegation(struct nfs_server *server,
  * @sb: sb to process
  *
  */
-void nfs_super_return_all_delegations(struct super_block *sb)
+void nfs_server_return_all_delegations(struct nfs_server *server)
 {
-       struct nfs_server *server = NFS_SB(sb);
        struct nfs_client *clp = server->nfs_client;
        struct nfs_delegation *delegation;
 
index 72709c4193fa28ac3afeba63f65e2f46e954eb23..bbc6a4dba0d8e05d1940bc8d1136b5bc9d754778 100644 (file)
@@ -8,7 +8,7 @@
 #ifndef FS_NFS_DELEGATION_H
 #define FS_NFS_DELEGATION_H
 
-#if defined(CONFIG_NFS_V4)
+#if IS_ENABLED(CONFIG_NFS_V4)
 /*
  * NFSv4 delegation
  */
@@ -33,12 +33,12 @@ enum {
 
 int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
 void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
-int nfs_inode_return_delegation(struct inode *inode);
+int nfs4_inode_return_delegation(struct inode *inode);
 int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
 void nfs_inode_return_delegation_noreclaim(struct inode *inode);
 
 struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle);
-void nfs_super_return_all_delegations(struct super_block *sb);
+void nfs_server_return_all_delegations(struct nfs_server *);
 void nfs_expire_all_delegations(struct nfs_client *clp);
 void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags);
 void nfs_expire_unreferenced_delegations(struct nfs_client *clp);
@@ -56,24 +56,13 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl);
 bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, fmode_t flags);
 
 void nfs_mark_delegation_referenced(struct nfs_delegation *delegation);
-int nfs_have_delegation(struct inode *inode, fmode_t flags);
+int nfs4_have_delegation(struct inode *inode, fmode_t flags);
 
-#else
-static inline int nfs_have_delegation(struct inode *inode, fmode_t flags)
-{
-       return 0;
-}
-
-static inline int nfs_inode_return_delegation(struct inode *inode)
-{
-       nfs_wb_all(inode);
-       return 0;
-}
 #endif
 
 static inline int nfs_have_delegated_attributes(struct inode *inode)
 {
-       return nfs_have_delegation(inode, FMODE_READ) &&
+       return NFS_PROTO(inode)->have_delegation(inode, FMODE_READ) &&
                !(NFS_I(inode)->cache_validity & NFS_INO_REVAL_FORCED);
 }
 
index a6b1c7fb8232c0a3152f79175e2047421c896859..627f108ede23327e3339d4e35ac47f6cb09490d3 100644 (file)
@@ -17,6 +17,7 @@
  *  6 Jun 1999 Cache readdir lookups in the page cache. -DaveM
  */
 
+#include <linux/module.h>
 #include <linux/time.h>
 #include <linux/errno.h>
 #include <linux/stat.h>
 static int nfs_opendir(struct inode *, struct file *);
 static int nfs_closedir(struct inode *, struct file *);
 static int nfs_readdir(struct file *, void *, filldir_t);
-static struct dentry *nfs_lookup(struct inode *, struct dentry *, unsigned int);
-static int nfs_create(struct inode *, struct dentry *, umode_t, bool);
-static int nfs_mkdir(struct inode *, struct dentry *, umode_t);
-static int nfs_rmdir(struct inode *, struct dentry *);
-static int nfs_unlink(struct inode *, struct dentry *);
-static int nfs_symlink(struct inode *, struct dentry *, const char *);
-static int nfs_link(struct dentry *, struct inode *, struct dentry *);
-static int nfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
-static int nfs_rename(struct inode *, struct dentry *,
-                     struct inode *, struct dentry *);
 static int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
 static loff_t nfs_llseek_dir(struct file *, loff_t, int);
 static void nfs_readdir_clear_array(struct page*);
@@ -69,73 +60,10 @@ const struct file_operations nfs_dir_operations = {
        .fsync          = nfs_fsync_dir,
 };
 
-const struct inode_operations nfs_dir_inode_operations = {
-       .create         = nfs_create,
-       .lookup         = nfs_lookup,
-       .link           = nfs_link,
-       .unlink         = nfs_unlink,
-       .symlink        = nfs_symlink,
-       .mkdir          = nfs_mkdir,
-       .rmdir          = nfs_rmdir,
-       .mknod          = nfs_mknod,
-       .rename         = nfs_rename,
-       .permission     = nfs_permission,
-       .getattr        = nfs_getattr,
-       .setattr        = nfs_setattr,
-};
-
 const struct address_space_operations nfs_dir_aops = {
        .freepage = nfs_readdir_clear_array,
 };
 
-#ifdef CONFIG_NFS_V3
-const struct inode_operations nfs3_dir_inode_operations = {
-       .create         = nfs_create,
-       .lookup         = nfs_lookup,
-       .link           = nfs_link,
-       .unlink         = nfs_unlink,
-       .symlink        = nfs_symlink,
-       .mkdir          = nfs_mkdir,
-       .rmdir          = nfs_rmdir,
-       .mknod          = nfs_mknod,
-       .rename         = nfs_rename,
-       .permission     = nfs_permission,
-       .getattr        = nfs_getattr,
-       .setattr        = nfs_setattr,
-       .listxattr      = nfs3_listxattr,
-       .getxattr       = nfs3_getxattr,
-       .setxattr       = nfs3_setxattr,
-       .removexattr    = nfs3_removexattr,
-};
-#endif  /* CONFIG_NFS_V3 */
-
-#ifdef CONFIG_NFS_V4
-
-static int nfs_atomic_open(struct inode *, struct dentry *,
-                          struct file *, unsigned, umode_t,
-                          int *);
-const struct inode_operations nfs4_dir_inode_operations = {
-       .create         = nfs_create,
-       .lookup         = nfs_lookup,
-       .atomic_open    = nfs_atomic_open,
-       .link           = nfs_link,
-       .unlink         = nfs_unlink,
-       .symlink        = nfs_symlink,
-       .mkdir          = nfs_mkdir,
-       .rmdir          = nfs_rmdir,
-       .mknod          = nfs_mknod,
-       .rename         = nfs_rename,
-       .permission     = nfs_permission,
-       .getattr        = nfs_getattr,
-       .setattr        = nfs_setattr,
-       .getxattr       = generic_getxattr,
-       .setxattr       = generic_setxattr,
-       .listxattr      = generic_listxattr,
-       .removexattr    = generic_removexattr,
-};
-
-#endif /* CONFIG_NFS_V4 */
-
 static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred)
 {
        struct nfs_open_dir_context *ctx;
@@ -1008,6 +936,7 @@ void nfs_force_lookup_revalidate(struct inode *dir)
 {
        NFS_I(dir)->cache_change_attribute++;
 }
+EXPORT_SYMBOL_GPL(nfs_force_lookup_revalidate);
 
 /*
  * A check for whether or not the parent directory has changed.
@@ -1128,7 +1057,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
                goto out_bad;
        }
 
-       if (nfs_have_delegation(inode, FMODE_READ))
+       if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ))
                goto out_set_verifier;
 
        /* Force a full look up iff the parent directory has changed */
@@ -1269,8 +1198,9 @@ const struct dentry_operations nfs_dentry_operations = {
        .d_automount    = nfs_d_automount,
        .d_release      = nfs_d_release,
 };
+EXPORT_SYMBOL_GPL(nfs_dentry_operations);
 
-static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags)
+struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags)
 {
        struct dentry *res;
        struct dentry *parent;
@@ -1336,8 +1266,9 @@ out:
        nfs_free_fhandle(fhandle);
        return res;
 }
+EXPORT_SYMBOL_GPL(nfs_lookup);
 
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 static int nfs4_lookup_revalidate(struct dentry *, unsigned int);
 
 const struct dentry_operations nfs4_dentry_operations = {
@@ -1347,6 +1278,7 @@ const struct dentry_operations nfs4_dentry_operations = {
        .d_automount    = nfs_d_automount,
        .d_release      = nfs_d_release,
 };
+EXPORT_SYMBOL_GPL(nfs4_dentry_operations);
 
 static fmode_t flags_to_mode(int flags)
 {
@@ -1398,9 +1330,9 @@ out:
        return err;
 }
 
-static int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
-                           struct file *file, unsigned open_flags,
-                           umode_t mode, int *opened)
+int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
+                   struct file *file, unsigned open_flags,
+                   umode_t mode, int *opened)
 {
        struct nfs_open_context *ctx;
        struct dentry *res;
@@ -1489,6 +1421,7 @@ no_open:
 
        return finish_no_open(file, res);
 }
+EXPORT_SYMBOL_GPL(nfs_atomic_open);
 
 static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
 {
@@ -1581,6 +1514,7 @@ out_error:
        dput(parent);
        return error;
 }
+EXPORT_SYMBOL_GPL(nfs_instantiate);
 
 /*
  * Following a failed create operation, we drop the dentry rather
@@ -1588,7 +1522,7 @@ out_error:
  * that the operation succeeded on the server, but an error in the
  * reply path made it appear to have failed.
  */
-static int nfs_create(struct inode *dir, struct dentry *dentry,
+int nfs_create(struct inode *dir, struct dentry *dentry,
                umode_t mode, bool excl)
 {
        struct iattr attr;
@@ -1609,11 +1543,12 @@ out_err:
        d_drop(dentry);
        return error;
 }
+EXPORT_SYMBOL_GPL(nfs_create);
 
 /*
  * See comments for nfs_proc_create regarding failed operations.
  */
-static int
+int
 nfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev)
 {
        struct iattr attr;
@@ -1636,11 +1571,12 @@ out_err:
        d_drop(dentry);
        return status;
 }
+EXPORT_SYMBOL_GPL(nfs_mknod);
 
 /*
  * See comments for nfs_proc_create regarding failed operations.
  */
-static int nfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+int nfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 {
        struct iattr attr;
        int error;
@@ -1659,6 +1595,7 @@ out_err:
        d_drop(dentry);
        return error;
 }
+EXPORT_SYMBOL_GPL(nfs_mkdir);
 
 static void nfs_dentry_handle_enoent(struct dentry *dentry)
 {
@@ -1666,7 +1603,7 @@ static void nfs_dentry_handle_enoent(struct dentry *dentry)
                d_delete(dentry);
 }
 
-static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
+int nfs_rmdir(struct inode *dir, struct dentry *dentry)
 {
        int error;
 
@@ -1682,6 +1619,7 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
 
        return error;
 }
+EXPORT_SYMBOL_GPL(nfs_rmdir);
 
 /*
  * Remove a file after making sure there are no pending writes,
@@ -1706,7 +1644,7 @@ static int nfs_safe_remove(struct dentry *dentry)
        }
 
        if (inode != NULL) {
-               nfs_inode_return_delegation(inode);
+               NFS_PROTO(inode)->return_delegation(inode);
                error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
                /* The VFS may want to delete this inode */
                if (error == 0)
@@ -1725,7 +1663,7 @@ out:
  *
  *  If sillyrename() returns 0, we do nothing, otherwise we unlink.
  */
-static int nfs_unlink(struct inode *dir, struct dentry *dentry)
+int nfs_unlink(struct inode *dir, struct dentry *dentry)
 {
        int error;
        int need_rehash = 0;
@@ -1753,6 +1691,7 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
                d_rehash(dentry);
        return error;
 }
+EXPORT_SYMBOL_GPL(nfs_unlink);
 
 /*
  * To create a symbolic link, most file systems instantiate a new inode,
@@ -1769,7 +1708,7 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
  * now have a new file handle and can instantiate an in-core NFS inode
  * and move the raw page into its mapping.
  */
-static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
 {
        struct pagevec lru_pvec;
        struct page *page;
@@ -1823,8 +1762,9 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(nfs_symlink);
 
-static int 
+int
 nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
 {
        struct inode *inode = old_dentry->d_inode;
@@ -1834,7 +1774,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
                old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
                dentry->d_parent->d_name.name, dentry->d_name.name);
 
-       nfs_inode_return_delegation(inode);
+       NFS_PROTO(inode)->return_delegation(inode);
 
        d_drop(dentry);
        error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
@@ -1844,6 +1784,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
        }
        return error;
 }
+EXPORT_SYMBOL_GPL(nfs_link);
 
 /*
  * RENAME
@@ -1869,7 +1810,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
  * If these conditions are met, we can drop the dentries before doing
  * the rename.
  */
-static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                      struct inode *new_dir, struct dentry *new_dentry)
 {
        struct inode *old_inode = old_dentry->d_inode;
@@ -1918,9 +1859,9 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                }
        }
 
-       nfs_inode_return_delegation(old_inode);
+       NFS_PROTO(old_inode)->return_delegation(old_inode);
        if (new_inode != NULL)
-               nfs_inode_return_delegation(new_inode);
+               NFS_PROTO(new_inode)->return_delegation(new_inode);
 
        error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,
                                           new_dir, &new_dentry->d_name);
@@ -1942,6 +1883,7 @@ out:
                dput(dentry);
        return error;
 }
+EXPORT_SYMBOL_GPL(nfs_rename);
 
 static DEFINE_SPINLOCK(nfs_access_lru_lock);
 static LIST_HEAD(nfs_access_lru_list);
@@ -2042,6 +1984,7 @@ void nfs_access_zap_cache(struct inode *inode)
        spin_unlock(&nfs_access_lru_lock);
        nfs_access_free_list(&head);
 }
+EXPORT_SYMBOL_GPL(nfs_access_zap_cache);
 
 static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, struct rpc_cred *cred)
 {
@@ -2202,6 +2145,7 @@ int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags)
 {
        return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags));
 }
+EXPORT_SYMBOL_GPL(nfs_may_open);
 
 int nfs_permission(struct inode *inode, int mask)
 {
@@ -2261,6 +2205,7 @@ out_notsup:
                res = generic_permission(inode, mask);
        goto out;
 }
+EXPORT_SYMBOL_GPL(nfs_permission);
 
 /*
  * Local variables:
index 48253372ab1d115def0b81f56b097db6e98a0f88..1ba385b7c90da41b4c90d5760fe13bbc7482302a 100644 (file)
@@ -115,17 +115,28 @@ static inline int put_dreq(struct nfs_direct_req *dreq)
  * @nr_segs: size of iovec array
  *
  * The presence of this routine in the address space ops vector means
- * the NFS client supports direct I/O.  However, we shunt off direct
- * read and write requests before the VFS gets them, so this method
- * should never be called.
+ * the NFS client supports direct I/O. However, for most direct IO, we
+ * shunt off direct read and write requests before the VFS gets them,
+ * so this method is only ever called for swap.
  */
 ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t pos, unsigned long nr_segs)
 {
+#ifndef CONFIG_NFS_SWAP
        dprintk("NFS: nfs_direct_IO (%s) off/no(%Ld/%lu) EINVAL\n",
                        iocb->ki_filp->f_path.dentry->d_name.name,
                        (long long) pos, nr_segs);
 
        return -EINVAL;
+#else
+       VM_BUG_ON(iocb->ki_left != PAGE_SIZE);
+       VM_BUG_ON(iocb->ki_nbytes != PAGE_SIZE);
+
+       if (rw == READ || rw == KERNEL_READ)
+               return nfs_file_direct_read(iocb, iov, nr_segs, pos,
+                               rw == READ ? true : false);
+       return nfs_file_direct_write(iocb, iov, nr_segs, pos,
+                               rw == WRITE ? true : false);
+#endif /* CONFIG_NFS_SWAP */
 }
 
 static void nfs_direct_release_pages(struct page **pages, unsigned int npages)
@@ -303,7 +314,7 @@ static const struct nfs_pgio_completion_ops nfs_direct_read_completion_ops = {
  */
 static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *desc,
                                                const struct iovec *iov,
-                                               loff_t pos)
+                                               loff_t pos, bool uio)
 {
        struct nfs_direct_req *dreq = desc->pg_dreq;
        struct nfs_open_context *ctx = dreq->ctx;
@@ -331,12 +342,20 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *de
                                          GFP_KERNEL);
                if (!pagevec)
                        break;
-               down_read(&current->mm->mmap_sem);
-               result = get_user_pages(current, current->mm, user_addr,
+               if (uio) {
+                       down_read(&current->mm->mmap_sem);
+                       result = get_user_pages(current, current->mm, user_addr,
                                        npages, 1, 0, pagevec, NULL);
-               up_read(&current->mm->mmap_sem);
-               if (result < 0)
-                       break;
+                       up_read(&current->mm->mmap_sem);
+                       if (result < 0)
+                               break;
+               } else {
+                       WARN_ON(npages != 1);
+                       result = get_kernel_page(user_addr, 1, pagevec);
+                       if (WARN_ON(result != 1))
+                               break;
+               }
+
                if ((unsigned)result < npages) {
                        bytes = result * PAGE_SIZE;
                        if (bytes <= pgbase) {
@@ -386,21 +405,21 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *de
 static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
                                              const struct iovec *iov,
                                              unsigned long nr_segs,
-                                             loff_t pos)
+                                             loff_t pos, bool uio)
 {
        struct nfs_pageio_descriptor desc;
        ssize_t result = -EINVAL;
        size_t requested_bytes = 0;
        unsigned long seg;
 
-       nfs_pageio_init_read(&desc, dreq->inode,
+       NFS_PROTO(dreq->inode)->read_pageio_init(&desc, dreq->inode,
                             &nfs_direct_read_completion_ops);
        get_dreq(dreq);
        desc.pg_dreq = dreq;
 
        for (seg = 0; seg < nr_segs; seg++) {
                const struct iovec *vec = &iov[seg];
-               result = nfs_direct_read_schedule_segment(&desc, vec, pos);
+               result = nfs_direct_read_schedule_segment(&desc, vec, pos, uio);
                if (result < 0)
                        break;
                requested_bytes += result;
@@ -426,7 +445,7 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
 }
 
 static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
-                              unsigned long nr_segs, loff_t pos)
+                              unsigned long nr_segs, loff_t pos, bool uio)
 {
        ssize_t result = -ENOMEM;
        struct inode *inode = iocb->ki_filp->f_mapping->host;
@@ -444,7 +463,7 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
        if (!is_sync_kiocb(iocb))
                dreq->iocb = iocb;
 
-       result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos);
+       result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos, uio);
        if (!result)
                result = nfs_direct_wait(dreq);
        NFS_I(inode)->read_io += result;
@@ -460,7 +479,7 @@ static void nfs_inode_dio_write_done(struct inode *inode)
        inode_dio_done(inode);
 }
 
-#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
+#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4)
 static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
 {
        struct nfs_pageio_descriptor desc;
@@ -478,7 +497,7 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
        dreq->count = 0;
        get_dreq(dreq);
 
-       nfs_pageio_init_write(&desc, dreq->inode, FLUSH_STABLE,
+       NFS_PROTO(dreq->inode)->write_pageio_init(&desc, dreq->inode, FLUSH_STABLE,
                              &nfs_direct_write_completion_ops);
        desc.pg_dreq = dreq;
 
@@ -610,7 +629,7 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode
  */
 static ssize_t nfs_direct_write_schedule_segment(struct nfs_pageio_descriptor *desc,
                                                 const struct iovec *iov,
-                                                loff_t pos)
+                                                loff_t pos, bool uio)
 {
        struct nfs_direct_req *dreq = desc->pg_dreq;
        struct nfs_open_context *ctx = dreq->ctx;
@@ -638,12 +657,19 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_pageio_descriptor *d
                if (!pagevec)
                        break;
 
-               down_read(&current->mm->mmap_sem);
-               result = get_user_pages(current, current->mm, user_addr,
-                                       npages, 0, 0, pagevec, NULL);
-               up_read(&current->mm->mmap_sem);
-               if (result < 0)
-                       break;
+               if (uio) {
+                       down_read(&current->mm->mmap_sem);
+                       result = get_user_pages(current, current->mm, user_addr,
+                                               npages, 0, 0, pagevec, NULL);
+                       up_read(&current->mm->mmap_sem);
+                       if (result < 0)
+                               break;
+               } else {
+                       WARN_ON(npages != 1);
+                       result = get_kernel_page(user_addr, 0, pagevec);
+                       if (WARN_ON(result != 1))
+                               break;
+               }
 
                if ((unsigned)result < npages) {
                        bytes = result * PAGE_SIZE;
@@ -774,7 +800,7 @@ static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops = {
 static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
                                               const struct iovec *iov,
                                               unsigned long nr_segs,
-                                              loff_t pos)
+                                              loff_t pos, bool uio)
 {
        struct nfs_pageio_descriptor desc;
        struct inode *inode = dreq->inode;
@@ -782,7 +808,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
        size_t requested_bytes = 0;
        unsigned long seg;
 
-       nfs_pageio_init_write(&desc, inode, FLUSH_COND_STABLE,
+       NFS_PROTO(inode)->write_pageio_init(&desc, inode, FLUSH_COND_STABLE,
                              &nfs_direct_write_completion_ops);
        desc.pg_dreq = dreq;
        get_dreq(dreq);
@@ -790,7 +816,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
 
        for (seg = 0; seg < nr_segs; seg++) {
                const struct iovec *vec = &iov[seg];
-               result = nfs_direct_write_schedule_segment(&desc, vec, pos);
+               result = nfs_direct_write_schedule_segment(&desc, vec, pos, uio);
                if (result < 0)
                        break;
                requested_bytes += result;
@@ -818,7 +844,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
 
 static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
                                unsigned long nr_segs, loff_t pos,
-                               size_t count)
+                               size_t count, bool uio)
 {
        ssize_t result = -ENOMEM;
        struct inode *inode = iocb->ki_filp->f_mapping->host;
@@ -836,7 +862,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
        if (!is_sync_kiocb(iocb))
                dreq->iocb = iocb;
 
-       result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos);
+       result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, uio);
        if (!result)
                result = nfs_direct_wait(dreq);
 out_release:
@@ -867,7 +893,7 @@ out:
  * cache.
  */
 ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
-                               unsigned long nr_segs, loff_t pos)
+                               unsigned long nr_segs, loff_t pos, bool uio)
 {
        ssize_t retval = -EINVAL;
        struct file *file = iocb->ki_filp;
@@ -892,7 +918,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
 
        task_io_account_read(count);
 
-       retval = nfs_direct_read(iocb, iov, nr_segs, pos);
+       retval = nfs_direct_read(iocb, iov, nr_segs, pos, uio);
        if (retval > 0)
                iocb->ki_pos = pos + retval;
 
@@ -923,7 +949,7 @@ out:
  * is no atomic O_APPEND write facility in the NFS protocol.
  */
 ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
-                               unsigned long nr_segs, loff_t pos)
+                               unsigned long nr_segs, loff_t pos, bool uio)
 {
        ssize_t retval = -EINVAL;
        struct file *file = iocb->ki_filp;
@@ -955,7 +981,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
 
        task_io_account_write(count);
 
-       retval = nfs_direct_write(iocb, iov, nr_segs, pos, count);
+       retval = nfs_direct_write(iocb, iov, nr_segs, pos, count, uio);
        if (retval > 0) {
                struct inode *inode = mapping->host;
 
index b3924b8a600021e27c89fc6c9ff910a514aec9eb..31c26c4dcc238110de0fa7374f1a2ab51a1541cd 100644 (file)
@@ -8,6 +8,7 @@
 
 #ifdef CONFIG_NFS_USE_KERNEL_DNS
 
+#include <linux/module.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/dns_resolver.h>
 #include "dns_resolve.h"
@@ -27,9 +28,11 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name, size_t namelen,
        kfree(ip_addr);
        return ret;
 }
+EXPORT_SYMBOL_GPL(nfs_dns_resolve_name);
 
 #else
 
+#include <linux/module.h>
 #include <linux/hash.h>
 #include <linux/string.h>
 #include <linux/kmod.h>
@@ -345,6 +348,7 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name,
                ret = -ESRCH;
        return ret;
 }
+EXPORT_SYMBOL_GPL(nfs_dns_resolve_name);
 
 int nfs_dns_resolver_cache_init(struct net *net)
 {
index a6708e6b438dd55f2924e5bb78c809c1575a97a9..75d6d0a3d32e2685bbd43f791b1f32775c87ce59 100644 (file)
@@ -16,6 +16,7 @@
  *  nfs regular file handling functions
  */
 
+#include <linux/module.h>
 #include <linux/time.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include "internal.h"
 #include "iostat.h"
 #include "fscache.h"
-#include "pnfs.h"
 
 #define NFSDBG_FACILITY                NFSDBG_FILE
 
 static const struct vm_operations_struct nfs_file_vm_ops;
 
-const struct inode_operations nfs_file_inode_operations = {
-       .permission     = nfs_permission,
-       .getattr        = nfs_getattr,
-       .setattr        = nfs_setattr,
-};
-
-#ifdef CONFIG_NFS_V3
-const struct inode_operations nfs3_file_inode_operations = {
-       .permission     = nfs_permission,
-       .getattr        = nfs_getattr,
-       .setattr        = nfs_setattr,
-       .listxattr      = nfs3_listxattr,
-       .getxattr       = nfs3_getxattr,
-       .setxattr       = nfs3_setxattr,
-       .removexattr    = nfs3_removexattr,
-};
-#endif  /* CONFIG_NFS_v3 */
-
 /* Hack for future NFS swap support */
 #ifndef IS_SWAPFILE
 # define IS_SWAPFILE(inode)    (0)
 #endif
 
-static int nfs_check_flags(int flags)
+int nfs_check_flags(int flags)
 {
        if ((flags & (O_APPEND | O_DIRECT)) == (O_APPEND | O_DIRECT))
                return -EINVAL;
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(nfs_check_flags);
 
 /*
  * Open file
@@ -93,7 +76,7 @@ nfs_file_open(struct inode *inode, struct file *filp)
        return res;
 }
 
-static int
+int
 nfs_file_release(struct inode *inode, struct file *filp)
 {
        dprintk("NFS: release(%s/%s)\n",
@@ -103,6 +86,7 @@ nfs_file_release(struct inode *inode, struct file *filp)
        nfs_inc_stats(inode, NFSIOS_VFSRELEASE);
        return nfs_release(inode, filp);
 }
+EXPORT_SYMBOL_GPL(nfs_file_release);
 
 /**
  * nfs_revalidate_size - Revalidate the file size
@@ -135,7 +119,7 @@ force_reval:
        return __nfs_revalidate_inode(server, inode);
 }
 
-static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
+loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
 {
        dprintk("NFS: llseek file(%s/%s, %lld, %d)\n",
                        filp->f_path.dentry->d_parent->d_name.name,
@@ -156,11 +140,12 @@ static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
 
        return generic_file_llseek(filp, offset, origin);
 }
+EXPORT_SYMBOL_GPL(nfs_file_llseek);
 
 /*
  * Flush all dirty pages, and check for write errors.
  */
-static int
+int
 nfs_file_flush(struct file *file, fl_owner_t id)
 {
        struct dentry   *dentry = file->f_path.dentry;
@@ -178,14 +163,15 @@ nfs_file_flush(struct file *file, fl_owner_t id)
         * If we're holding a write delegation, then just start the i/o
         * but don't wait for completion (or send a commit).
         */
-       if (nfs_have_delegation(inode, FMODE_WRITE))
+       if (NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE))
                return filemap_fdatawrite(file->f_mapping);
 
        /* Flush writes to the server and return any errors */
        return vfs_fsync(file, 0);
 }
+EXPORT_SYMBOL_GPL(nfs_file_flush);
 
-static ssize_t
+ssize_t
 nfs_file_read(struct kiocb *iocb, const struct iovec *iov,
                unsigned long nr_segs, loff_t pos)
 {
@@ -194,7 +180,7 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov,
        ssize_t result;
 
        if (iocb->ki_filp->f_flags & O_DIRECT)
-               return nfs_file_direct_read(iocb, iov, nr_segs, pos);
+               return nfs_file_direct_read(iocb, iov, nr_segs, pos, true);
 
        dprintk("NFS: read(%s/%s, %lu@%lu)\n",
                dentry->d_parent->d_name.name, dentry->d_name.name,
@@ -208,8 +194,9 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov,
        }
        return result;
 }
+EXPORT_SYMBOL_GPL(nfs_file_read);
 
-static ssize_t
+ssize_t
 nfs_file_splice_read(struct file *filp, loff_t *ppos,
                     struct pipe_inode_info *pipe, size_t count,
                     unsigned int flags)
@@ -230,8 +217,9 @@ nfs_file_splice_read(struct file *filp, loff_t *ppos,
        }
        return res;
 }
+EXPORT_SYMBOL_GPL(nfs_file_splice_read);
 
-static int
+int
 nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
 {
        struct dentry *dentry = file->f_path.dentry;
@@ -251,6 +239,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
        }
        return status;
 }
+EXPORT_SYMBOL_GPL(nfs_file_mmap);
 
 /*
  * Flush any dirty pages for this process, and check for write errors.
@@ -264,8 +253,8 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
  * nfs_file_write() that a write error occurred, and hence cause it to
  * fall back to doing a synchronous write.
  */
-static int
-nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+int
+nfs_file_fsync_commit(struct file *file, loff_t start, loff_t end, int datasync)
 {
        struct dentry *dentry = file->f_path.dentry;
        struct nfs_open_context *ctx = nfs_file_open_context(file);
@@ -277,9 +266,6 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
                        dentry->d_parent->d_name.name, dentry->d_name.name,
                        datasync);
 
-       ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
-       mutex_lock(&inode->i_mutex);
-
        nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
        have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
        status = nfs_commit_inode(inode, FLUSH_SYNC);
@@ -290,10 +276,21 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
                ret = xchg(&ctx->error, 0);
        if (!ret && status < 0)
                ret = status;
-       if (!ret && !datasync)
-               /* application has asked for meta-data sync */
-               ret = pnfs_layoutcommit_inode(inode, true);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(nfs_file_fsync_commit);
+
+static int
+nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+       int ret;
+       struct inode *inode = file->f_path.dentry->d_inode;
+
+       ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
+       mutex_lock(&inode->i_mutex);
+       ret = nfs_file_fsync_commit(file, start, end, datasync);
        mutex_unlock(&inode->i_mutex);
+
        return ret;
 }
 
@@ -442,7 +439,7 @@ static void nfs_invalidate_page(struct page *page, unsigned long offset)
        if (offset != 0)
                return;
        /* Cancel any unstarted writes on this page */
-       nfs_wb_page_cancel(page->mapping->host, page);
+       nfs_wb_page_cancel(page_file_mapping(page)->host, page);
 
        nfs_fscache_invalidate_page(page, page->mapping->host);
 }
@@ -459,8 +456,11 @@ static int nfs_release_page(struct page *page, gfp_t gfp)
 
        dfprintk(PAGECACHE, "NFS: release_page(%p)\n", page);
 
-       /* Only do I/O if gfp is a superset of GFP_KERNEL */
-       if (mapping && (gfp & GFP_KERNEL) == GFP_KERNEL) {
+       /* Only do I/O if gfp is a superset of GFP_KERNEL, and we're not
+        * doing this memory reclaim for a fs-related allocation.
+        */
+       if (mapping && (gfp & GFP_KERNEL) == GFP_KERNEL &&
+           !(current->flags & PF_FSTRANS)) {
                int how = FLUSH_SYNC;
 
                /* Don't let kswapd deadlock waiting for OOM RPC calls */
@@ -484,7 +484,7 @@ static int nfs_release_page(struct page *page, gfp_t gfp)
  */
 static int nfs_launder_page(struct page *page)
 {
-       struct inode *inode = page->mapping->host;
+       struct inode *inode = page_file_mapping(page)->host;
        struct nfs_inode *nfsi = NFS_I(inode);
 
        dfprintk(PAGECACHE, "NFS: launder_page(%ld, %llu)\n",
@@ -494,6 +494,20 @@ static int nfs_launder_page(struct page *page)
        return nfs_wb_page(inode, page);
 }
 
+#ifdef CONFIG_NFS_SWAP
+static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
+                                               sector_t *span)
+{
+       *span = sis->pages;
+       return xs_swapper(NFS_CLIENT(file->f_mapping->host)->cl_xprt, 1);
+}
+
+static void nfs_swap_deactivate(struct file *file)
+{
+       xs_swapper(NFS_CLIENT(file->f_mapping->host)->cl_xprt, 0);
+}
+#endif
+
 const struct address_space_operations nfs_file_aops = {
        .readpage = nfs_readpage,
        .readpages = nfs_readpages,
@@ -508,6 +522,10 @@ const struct address_space_operations nfs_file_aops = {
        .migratepage = nfs_migrate_page,
        .launder_page = nfs_launder_page,
        .error_remove_page = generic_error_remove_page,
+#ifdef CONFIG_NFS_SWAP
+       .swap_activate = nfs_swap_activate,
+       .swap_deactivate = nfs_swap_deactivate,
+#endif
 };
 
 /*
@@ -533,7 +551,7 @@ static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
        nfs_fscache_wait_on_page_write(NFS_I(dentry->d_inode), page);
 
        lock_page(page);
-       mapping = page->mapping;
+       mapping = page_file_mapping(page);
        if (mapping != dentry->d_inode->i_mapping)
                goto out_unlock;
 
@@ -572,8 +590,8 @@ static int nfs_need_sync_write(struct file *filp, struct inode *inode)
        return 0;
 }
 
-static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
-                               unsigned long nr_segs, loff_t pos)
+ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
+                      unsigned long nr_segs, loff_t pos)
 {
        struct dentry * dentry = iocb->ki_filp->f_path.dentry;
        struct inode * inode = dentry->d_inode;
@@ -582,7 +600,7 @@ static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
        size_t count = iov_length(iov, nr_segs);
 
        if (iocb->ki_filp->f_flags & O_DIRECT)
-               return nfs_file_direct_write(iocb, iov, nr_segs, pos);
+               return nfs_file_direct_write(iocb, iov, nr_segs, pos, true);
 
        dprintk("NFS: write(%s/%s, %lu@%Ld)\n",
                dentry->d_parent->d_name.name, dentry->d_name.name,
@@ -623,10 +641,11 @@ out_swapfile:
        printk(KERN_INFO "NFS: attempt to write to active swap file!\n");
        goto out;
 }
+EXPORT_SYMBOL_GPL(nfs_file_write);
 
-static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,
-                                    struct file *filp, loff_t *ppos,
-                                    size_t count, unsigned int flags)
+ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,
+                             struct file *filp, loff_t *ppos,
+                             size_t count, unsigned int flags)
 {
        struct dentry *dentry = filp->f_path.dentry;
        struct inode *inode = dentry->d_inode;
@@ -654,6 +673,7 @@ static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,
                nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written);
        return ret;
 }
+EXPORT_SYMBOL_GPL(nfs_file_splice_write);
 
 static int
 do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
@@ -670,7 +690,7 @@ do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
        }
        fl->fl_type = saved_type;
 
-       if (nfs_have_delegation(inode, FMODE_READ))
+       if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
                goto out_noconflict;
 
        if (is_local)
@@ -765,7 +785,7 @@ do_setlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
         * This makes locking act as a cache coherency point.
         */
        nfs_sync_mapping(filp->f_mapping);
-       if (!nfs_have_delegation(inode, FMODE_READ)) {
+       if (!NFS_PROTO(inode)->have_delegation(inode, FMODE_READ)) {
                if (is_time_granular(&NFS_SERVER(inode)->time_delta))
                        __nfs_revalidate_inode(NFS_SERVER(inode), inode);
                else
@@ -778,7 +798,7 @@ out:
 /*
  * Lock a (portion of) a file
  */
-static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
+int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
 {
        struct inode *inode = filp->f_mapping->host;
        int ret = -ENOLCK;
@@ -814,11 +834,12 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
 out_err:
        return ret;
 }
+EXPORT_SYMBOL_GPL(nfs_lock);
 
 /*
  * Lock a (portion of) a file
  */
-static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
+int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
 {
        struct inode *inode = filp->f_mapping->host;
        int is_local = 0;
@@ -831,6 +852,15 @@ static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
        if (!(fl->fl_flags & FL_FLOCK))
                return -ENOLCK;
 
+       /*
+        * The NFSv4 protocol doesn't support LOCK_MAND, which is not part of
+        * any standard. In principle we might be able to support LOCK_MAND
+        * on NFSv2/3 since NLMv3/4 support DOS share modes, but for now the
+        * NFS code is not set up for it.
+        */
+       if (fl->fl_type & LOCK_MAND)
+               return -EINVAL;
+
        if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FLOCK)
                is_local = 1;
 
@@ -843,18 +873,20 @@ static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
                return do_unlk(filp, cmd, fl, is_local);
        return do_setlk(filp, cmd, fl, is_local);
 }
+EXPORT_SYMBOL_GPL(nfs_flock);
 
 /*
  * There is no protocol support for leases, so we have no way to implement
  * them correctly in the face of opens by other clients.
  */
-static int nfs_setlease(struct file *file, long arg, struct file_lock **fl)
+int nfs_setlease(struct file *file, long arg, struct file_lock **fl)
 {
        dprintk("NFS: setlease(%s/%s, arg=%ld)\n",
                        file->f_path.dentry->d_parent->d_name.name,
                        file->f_path.dentry->d_name.name, arg);
        return -EINVAL;
 }
+EXPORT_SYMBOL_GPL(nfs_setlease);
 
 const struct file_operations nfs_file_operations = {
        .llseek         = nfs_file_llseek,
@@ -874,104 +906,4 @@ const struct file_operations nfs_file_operations = {
        .check_flags    = nfs_check_flags,
        .setlease       = nfs_setlease,
 };
-
-#ifdef CONFIG_NFS_V4
-static int
-nfs4_file_open(struct inode *inode, struct file *filp)
-{
-       struct nfs_open_context *ctx;
-       struct dentry *dentry = filp->f_path.dentry;
-       struct dentry *parent = NULL;
-       struct inode *dir;
-       unsigned openflags = filp->f_flags;
-       struct iattr attr;
-       int err;
-
-       BUG_ON(inode != dentry->d_inode);
-       /*
-        * If no cached dentry exists or if it's negative, NFSv4 handled the
-        * opens in ->lookup() or ->create().
-        *
-        * We only get this far for a cached positive dentry.  We skipped
-        * revalidation, so handle it here by dropping the dentry and returning
-        * -EOPENSTALE.  The VFS will retry the lookup/create/open.
-        */
-
-       dprintk("NFS: open file(%s/%s)\n",
-               dentry->d_parent->d_name.name,
-               dentry->d_name.name);
-
-       if ((openflags & O_ACCMODE) == 3)
-               openflags--;
-
-       /* We can't create new files here */
-       openflags &= ~(O_CREAT|O_EXCL);
-
-       parent = dget_parent(dentry);
-       dir = parent->d_inode;
-
-       ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
-       err = PTR_ERR(ctx);
-       if (IS_ERR(ctx))
-               goto out;
-
-       attr.ia_valid = ATTR_OPEN;
-       if (openflags & O_TRUNC) {
-               attr.ia_valid |= ATTR_SIZE;
-               attr.ia_size = 0;
-               nfs_wb_all(inode);
-       }
-
-       inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr);
-       if (IS_ERR(inode)) {
-               err = PTR_ERR(inode);
-               switch (err) {
-               case -EPERM:
-               case -EACCES:
-               case -EDQUOT:
-               case -ENOSPC:
-               case -EROFS:
-                       goto out_put_ctx;
-               default:
-                       goto out_drop;
-               }
-       }
-       iput(inode);
-       if (inode != dentry->d_inode)
-               goto out_drop;
-
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-       nfs_file_set_open_context(filp, ctx);
-       err = 0;
-
-out_put_ctx:
-       put_nfs_open_context(ctx);
-out:
-       dput(parent);
-       return err;
-
-out_drop:
-       d_drop(dentry);
-       err = -EOPENSTALE;
-       goto out_put_ctx;
-}
-
-const struct file_operations nfs4_file_operations = {
-       .llseek         = nfs_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = nfs_file_read,
-       .aio_write      = nfs_file_write,
-       .mmap           = nfs_file_mmap,
-       .open           = nfs4_file_open,
-       .flush          = nfs_file_flush,
-       .release        = nfs_file_release,
-       .fsync          = nfs_file_fsync,
-       .lock           = nfs_lock,
-       .flock          = nfs_flock,
-       .splice_read    = nfs_file_splice_read,
-       .splice_write   = nfs_file_splice_write,
-       .check_flags    = nfs_check_flags,
-       .setlease       = nfs_setlease,
-};
-#endif /* CONFIG_NFS_V4 */
+EXPORT_SYMBOL_GPL(nfs_file_operations);
index a67990f90bd7d28bdea3310514e4a88275eadc14..4654ced096a644a50dbd0421eb68c7a76db9d791 100644 (file)
 #include <linux/sunrpc/stats.h>
 #include <linux/nfs_fs.h>
 #include <linux/nfs_mount.h>
-#include <linux/nfs4_mount.h>
 #include <linux/lockd/bind.h>
 #include <linux/seq_file.h>
 #include <linux/mount.h>
-#include <linux/nfs_idmap.h>
 #include <linux/vfs.h>
 #include <linux/namei.h>
 #include <linux/security.h>
 
 #include <asm/uaccess.h>
 
-#include "nfs4_fs.h"
-#include "delegation.h"
-#include "internal.h"
-
 #define NFSDBG_FACILITY                NFSDBG_CLIENT
 
 /*
@@ -135,47 +129,3 @@ out:
        nfs_free_fattr(fsinfo.fattr);
        return ret;
 }
-
-#ifdef CONFIG_NFS_V4
-
-int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh)
-{
-       struct nfs_fsinfo fsinfo;
-       int ret = -ENOMEM;
-
-       dprintk("--> nfs4_get_rootfh()\n");
-
-       fsinfo.fattr = nfs_alloc_fattr();
-       if (fsinfo.fattr == NULL)
-               goto out;
-
-       /* Start by getting the root filehandle from the server */
-       ret = nfs4_proc_get_rootfh(server, mntfh, &fsinfo);
-       if (ret < 0) {
-               dprintk("nfs4_get_rootfh: getroot error = %d\n", -ret);
-               goto out;
-       }
-
-       if (!(fsinfo.fattr->valid & NFS_ATTR_FATTR_TYPE)
-                       || !S_ISDIR(fsinfo.fattr->mode)) {
-               printk(KERN_ERR "nfs4_get_rootfh:"
-                      " getroot encountered non-directory\n");
-               ret = -ENOTDIR;
-               goto out;
-       }
-
-       if (fsinfo.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
-               printk(KERN_ERR "nfs4_get_rootfh:"
-                      " getroot obtained referral\n");
-               ret = -EREMOTE;
-               goto out;
-       }
-
-       memcpy(&server->fsid, &fsinfo.fattr->fsid, sizeof(server->fsid));
-out:
-       nfs_free_fattr(fsinfo.fattr);
-       dprintk("<-- nfs4_get_rootfh() = %d\n", ret);
-       return ret;
-}
-
-#endif /* CONFIG_NFS_V4 */
index 864c51e4b400e5c7248bdd93eaed19772c0f23f2..b701358c39c351d0613d1db29ebcf382ca8cb0a6 100644 (file)
@@ -52,8 +52,6 @@
 
 #define NFS_UINT_MAXLEN 11
 
-/* Default cache timeout is 10 minutes */
-unsigned int nfs_idmap_cache_timeout = 600;
 static const struct cred *id_resolver_cache;
 static struct key_type key_type_id_resolver_legacy;
 
@@ -205,12 +203,18 @@ static int nfs_idmap_init_keyring(void)
        if (ret < 0)
                goto failed_put_key;
 
+       ret = register_key_type(&key_type_id_resolver_legacy);
+       if (ret < 0)
+               goto failed_reg_legacy;
+
        set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
        cred->thread_keyring = keyring;
        cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
        id_resolver_cache = cred;
        return 0;
 
+failed_reg_legacy:
+       unregister_key_type(&key_type_id_resolver);
 failed_put_key:
        key_put(keyring);
 failed_put_cred:
@@ -222,6 +226,7 @@ static void nfs_idmap_quit_keyring(void)
 {
        key_revoke(id_resolver_cache->thread_keyring);
        unregister_key_type(&key_type_id_resolver);
+       unregister_key_type(&key_type_id_resolver_legacy);
        put_cred(id_resolver_cache);
 }
 
@@ -359,7 +364,6 @@ static int nfs_idmap_lookup_id(const char *name, size_t namelen, const char *typ
 }
 
 /* idmap classic begins here */
-module_param(nfs_idmap_cache_timeout, int, 0644);
 
 enum {
        Opt_find_uid, Opt_find_gid, Opt_find_user, Opt_find_group, Opt_find_err
@@ -385,7 +389,7 @@ static const struct rpc_pipe_ops idmap_upcall_ops = {
 };
 
 static struct key_type key_type_id_resolver_legacy = {
-       .name           = "id_resolver",
+       .name           = "id_legacy",
        .instantiate    = user_instantiate,
        .match          = user_match,
        .revoke         = user_revoke,
@@ -674,6 +678,7 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons,
        if (ret < 0)
                goto out2;
 
+       BUG_ON(idmap->idmap_key_cons != NULL);
        idmap->idmap_key_cons = cons;
 
        ret = rpc_queue_upcall(idmap->idmap_pipe, msg);
@@ -687,8 +692,7 @@ out2:
 out1:
        kfree(msg);
 out0:
-       key_revoke(cons->key);
-       key_revoke(cons->authkey);
+       complete_request_key(cons, ret);
        return ret;
 }
 
@@ -722,11 +726,18 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
 {
        struct rpc_inode *rpci = RPC_I(filp->f_path.dentry->d_inode);
        struct idmap *idmap = (struct idmap *)rpci->private;
-       struct key_construction *cons = idmap->idmap_key_cons;
+       struct key_construction *cons;
        struct idmap_msg im;
        size_t namelen_in;
        int ret;
 
+       /* If instantiation is successful, anyone waiting for key construction
+        * will have been woken up and someone else may now have used
+        * idmap_key_cons - so after this point we may no longer touch it.
+        */
+       cons = ACCESS_ONCE(idmap->idmap_key_cons);
+       idmap->idmap_key_cons = NULL;
+
        if (mlen != sizeof(im)) {
                ret = -ENOSPC;
                goto out;
@@ -739,7 +750,7 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
 
        if (!(im.im_status & IDMAP_STATUS_SUCCESS)) {
                ret = mlen;
-               complete_request_key(idmap->idmap_key_cons, -ENOKEY);
+               complete_request_key(cons, -ENOKEY);
                goto out_incomplete;
        }
 
@@ -756,7 +767,7 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
        }
 
 out:
-       complete_request_key(idmap->idmap_key_cons, ret);
+       complete_request_key(cons, ret);
 out_incomplete:
        return ret;
 }
index f7296983eba60c5ea21f164600be800ea988977f..c6e895f0fbf36eee681a5cb5d8e4a92bfd8c0d35 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/lockd/bind.h>
 #include <linux/seq_file.h>
 #include <linux/mount.h>
-#include <linux/nfs_idmap.h>
 #include <linux/vfs.h>
 #include <linux/inet.h>
 #include <linux/nfs_xdr.h>
@@ -51,6 +50,7 @@
 #include "fscache.h"
 #include "dns_resolve.h"
 #include "pnfs.h"
+#include "nfs.h"
 #include "netns.h"
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
@@ -82,6 +82,7 @@ int nfs_wait_bit_killable(void *word)
        freezable_schedule();
        return 0;
 }
+EXPORT_SYMBOL_GPL(nfs_wait_bit_killable);
 
 /**
  * nfs_compat_user_ino64 - returns the user-visible inode number
@@ -106,7 +107,7 @@ u64 nfs_compat_user_ino64(u64 fileid)
        return ino;
 }
 
-static void nfs_clear_inode(struct inode *inode)
+void nfs_clear_inode(struct inode *inode)
 {
        /*
         * The following should never happen...
@@ -117,6 +118,7 @@ static void nfs_clear_inode(struct inode *inode)
        nfs_access_zap_cache(inode);
        nfs_fscache_release_inode_cookie(inode);
 }
+EXPORT_SYMBOL_GPL(nfs_clear_inode);
 
 void nfs_evict_inode(struct inode *inode)
 {
@@ -186,6 +188,7 @@ void nfs_zap_acl_cache(struct inode *inode)
        NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_ACL;
        spin_unlock(&inode->i_lock);
 }
+EXPORT_SYMBOL_GPL(nfs_zap_acl_cache);
 
 void nfs_invalidate_atime(struct inode *inode)
 {
@@ -193,6 +196,7 @@ void nfs_invalidate_atime(struct inode *inode)
        NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATIME;
        spin_unlock(&inode->i_lock);
 }
+EXPORT_SYMBOL_GPL(nfs_invalidate_atime);
 
 /*
  * Invalidate, but do not unhash, the inode.
@@ -391,6 +395,7 @@ out_no_inode:
        dprintk("nfs_fhget: iget failed with error %ld\n", PTR_ERR(inode));
        goto out;
 }
+EXPORT_SYMBOL_GPL(nfs_fhget);
 
 #define NFS_VALID_ATTRS (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE|ATTR_ATIME|ATTR_ATIME_SET|ATTR_MTIME|ATTR_MTIME_SET|ATTR_FILE|ATTR_OPEN)
 
@@ -430,7 +435,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
         * Return any delegations if we're going to change ACLs
         */
        if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
-               nfs_inode_return_delegation(inode);
+               NFS_PROTO(inode)->return_delegation(inode);
        error = NFS_PROTO(inode)->setattr(dentry, fattr, attr);
        if (error == 0)
                nfs_refresh_inode(inode, fattr);
@@ -438,6 +443,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
 out:
        return error;
 }
+EXPORT_SYMBOL_GPL(nfs_setattr);
 
 /**
  * nfs_vmtruncate - unmap mappings "freed" by truncate() syscall
@@ -496,6 +502,7 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr)
                nfs_vmtruncate(inode, attr->ia_size);
        }
 }
+EXPORT_SYMBOL_GPL(nfs_setattr_update_inode);
 
 int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
 {
@@ -535,6 +542,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
 out:
        return err;
 }
+EXPORT_SYMBOL_GPL(nfs_getattr);
 
 static void nfs_init_lock_context(struct nfs_lock_context *l_ctx)
 {
@@ -623,6 +631,7 @@ void nfs_close_context(struct nfs_open_context *ctx, int is_sync)
                return;
        nfs_revalidate_inode(server, inode);
 }
+EXPORT_SYMBOL_GPL(nfs_close_context);
 
 struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, fmode_t f_mode)
 {
@@ -649,6 +658,7 @@ struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, fmode_t f
        ctx->mdsthreshold = NULL;
        return ctx;
 }
+EXPORT_SYMBOL_GPL(alloc_nfs_open_context);
 
 struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
 {
@@ -656,6 +666,7 @@ struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
                atomic_inc(&ctx->lock_context.count);
        return ctx;
 }
+EXPORT_SYMBOL_GPL(get_nfs_open_context);
 
 static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
 {
@@ -683,6 +694,7 @@ void put_nfs_open_context(struct nfs_open_context *ctx)
 {
        __put_nfs_open_context(ctx, 0);
 }
+EXPORT_SYMBOL_GPL(put_nfs_open_context);
 
 /*
  * Ensure that mmap has a recent RPC credential for use when writing out
@@ -698,6 +710,7 @@ void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
        list_add(&ctx->list, &nfsi->open_files);
        spin_unlock(&inode->i_lock);
 }
+EXPORT_SYMBOL_GPL(nfs_file_set_open_context);
 
 /*
  * Given an inode, search for an open context with the desired characteristics
@@ -842,6 +855,7 @@ int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
                return NFS_STALE(inode) ? -ESTALE : 0;
        return __nfs_revalidate_inode(server, inode);
 }
+EXPORT_SYMBOL_GPL(nfs_revalidate_inode);
 
 static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping)
 {
@@ -883,6 +897,10 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
        struct nfs_inode *nfsi = NFS_I(inode);
        int ret = 0;
 
+       /* swapfiles are not supposed to be shared. */
+       if (IS_SWAPFILE(inode))
+               goto out;
+
        if (nfs_mapping_need_revalidate_inode(inode)) {
                ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
                if (ret < 0)
@@ -1028,6 +1046,7 @@ void nfs_fattr_init(struct nfs_fattr *fattr)
        fattr->owner_name = NULL;
        fattr->group_name = NULL;
 }
+EXPORT_SYMBOL_GPL(nfs_fattr_init);
 
 struct nfs_fattr *nfs_alloc_fattr(void)
 {
@@ -1038,6 +1057,7 @@ struct nfs_fattr *nfs_alloc_fattr(void)
                nfs_fattr_init(fattr);
        return fattr;
 }
+EXPORT_SYMBOL_GPL(nfs_alloc_fattr);
 
 struct nfs_fh *nfs_alloc_fhandle(void)
 {
@@ -1048,6 +1068,7 @@ struct nfs_fh *nfs_alloc_fhandle(void)
                fh->size = 0;
        return fh;
 }
+EXPORT_SYMBOL_GPL(nfs_alloc_fhandle);
 
 #ifdef NFS_DEBUG
 /*
@@ -1168,6 +1189,7 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
 
        return status;
 }
+EXPORT_SYMBOL_GPL(nfs_refresh_inode);
 
 static int nfs_post_op_update_inode_locked(struct inode *inode, struct nfs_fattr *fattr)
 {
@@ -1204,6 +1226,7 @@ int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)
        spin_unlock(&inode->i_lock);
        return status;
 }
+EXPORT_SYMBOL_GPL(nfs_post_op_update_inode);
 
 /**
  * nfs_post_op_update_inode_force_wcc - try to update the inode attribute cache
@@ -1255,6 +1278,7 @@ out_noforce:
        spin_unlock(&inode->i_lock);
        return status;
 }
+EXPORT_SYMBOL_GPL(nfs_post_op_update_inode_force_wcc);
 
 /*
  * Many nfs protocol calls return the new file attributes after
@@ -1457,7 +1481,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
        if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
                                || S_ISLNK(inode->i_mode)))
                invalid &= ~NFS_INO_INVALID_DATA;
-       if (!nfs_have_delegation(inode, FMODE_READ) ||
+       if (!NFS_PROTO(inode)->have_delegation(inode, FMODE_READ) ||
                        (save_cache_validity & NFS_INO_REVAL_FORCED))
                nfsi->cache_validity |= invalid;
 
@@ -1472,27 +1496,6 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
        return -ESTALE;
 }
 
-
-#ifdef CONFIG_NFS_V4
-
-/*
- * Clean out any remaining NFSv4 state that might be left over due
- * to open() calls that passed nfs_atomic_lookup, but failed to call
- * nfs_open().
- */
-void nfs4_evict_inode(struct inode *inode)
-{
-       truncate_inode_pages(&inode->i_data, 0);
-       clear_inode(inode);
-       pnfs_return_layout(inode);
-       pnfs_destroy_layout(NFS_I(inode));
-       /* If we are holding a delegation, return it! */
-       nfs_inode_return_delegation_noreclaim(inode);
-       /* First call standard NFS clear_inode() code */
-       nfs_clear_inode(inode);
-}
-#endif
-
 struct inode *nfs_alloc_inode(struct super_block *sb)
 {
        struct nfs_inode *nfsi;
@@ -1505,11 +1508,12 @@ struct inode *nfs_alloc_inode(struct super_block *sb)
        nfsi->acl_access = ERR_PTR(-EAGAIN);
        nfsi->acl_default = ERR_PTR(-EAGAIN);
 #endif
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
        nfsi->nfs4_acl = NULL;
 #endif /* CONFIG_NFS_V4 */
        return &nfsi->vfs_inode;
 }
+EXPORT_SYMBOL_GPL(nfs_alloc_inode);
 
 static void nfs_i_callback(struct rcu_head *head)
 {
@@ -1521,10 +1525,11 @@ void nfs_destroy_inode(struct inode *inode)
 {
        call_rcu(&inode->i_rcu, nfs_i_callback);
 }
+EXPORT_SYMBOL_GPL(nfs_destroy_inode);
 
 static inline void nfs4_init_once(struct nfs_inode *nfsi)
 {
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
        INIT_LIST_HEAD(&nfsi->open_states);
        nfsi->delegation = NULL;
        nfsi->delegation_state = 0;
@@ -1570,6 +1575,7 @@ static void nfs_destroy_inodecache(void)
 }
 
 struct workqueue_struct *nfsiod_workqueue;
+EXPORT_SYMBOL_GPL(nfsiod_workqueue);
 
 /*
  * start up the nfsiod workqueue
@@ -1628,81 +1634,76 @@ static int __init init_nfs_fs(void)
 {
        int err;
 
-       err = nfs_idmap_init();
-       if (err < 0)
-               goto out10;
-
        err = nfs_dns_resolver_init();
        if (err < 0)
-               goto out9;
+               goto out10;;
 
        err = register_pernet_subsys(&nfs_net_ops);
        if (err < 0)
-               goto out8;
+               goto out9;
 
        err = nfs_fscache_register();
        if (err < 0)
-               goto out7;
+               goto out8;
 
        err = nfsiod_start();
        if (err)
-               goto out6;
+               goto out7;
 
        err = nfs_fs_proc_init();
        if (err)
-               goto out5;
+               goto out6;
 
        err = nfs_init_nfspagecache();
        if (err)
-               goto out4;
+               goto out5;
 
        err = nfs_init_inodecache();
        if (err)
-               goto out3;
+               goto out4;
 
        err = nfs_init_readpagecache();
        if (err)
-               goto out2;
+               goto out3;
 
        err = nfs_init_writepagecache();
        if (err)
-               goto out1;
+               goto out2;
 
        err = nfs_init_directcache();
        if (err)
-               goto out0;
+               goto out1;
 
 #ifdef CONFIG_PROC_FS
        rpc_proc_register(&init_net, &nfs_rpcstat);
 #endif
        if ((err = register_nfs_fs()) != 0)
-               goto out;
+               goto out0;
+
        return 0;
-out:
+out0:
 #ifdef CONFIG_PROC_FS
        rpc_proc_unregister(&init_net, "nfs");
 #endif
        nfs_destroy_directcache();
-out0:
-       nfs_destroy_writepagecache();
 out1:
-       nfs_destroy_readpagecache();
+       nfs_destroy_writepagecache();
 out2:
-       nfs_destroy_inodecache();
+       nfs_destroy_readpagecache();
 out3:
-       nfs_destroy_nfspagecache();
+       nfs_destroy_inodecache();
 out4:
-       nfs_fs_proc_exit();
+       nfs_destroy_nfspagecache();
 out5:
-       nfsiod_stop();
+       nfs_fs_proc_exit();
 out6:
-       nfs_fscache_unregister();
+       nfsiod_stop();
 out7:
-       unregister_pernet_subsys(&nfs_net_ops);
+       nfs_fscache_unregister();
 out8:
-       nfs_dns_resolver_destroy();
+       unregister_pernet_subsys(&nfs_net_ops);
 out9:
-       nfs_idmap_quit();
+       nfs_dns_resolver_destroy();
 out10:
        return err;
 }
@@ -1717,7 +1718,6 @@ static void __exit exit_nfs_fs(void)
        nfs_fscache_unregister();
        unregister_pernet_subsys(&nfs_net_ops);
        nfs_dns_resolver_destroy();
-       nfs_idmap_quit();
 #ifdef CONFIG_PROC_FS
        rpc_proc_unregister(&init_net, "nfs");
 #endif
index 18f99ef7134387128507e8ffd93fff6943b241d6..31fdb03225cd0ed989424dddf361698394aa1241 100644 (file)
@@ -85,6 +85,17 @@ struct nfs_clone_mount {
  */
 #define NFS_MAX_READDIR_PAGES 8
 
+struct nfs_client_initdata {
+       unsigned long init_flags;
+       const char *hostname;
+       const struct sockaddr *addr;
+       size_t addrlen;
+       struct nfs_subversion *nfs_mod;
+       int proto;
+       u32 minorversion;
+       struct net *net;
+};
+
 /*
  * In-kernel mount arguments
  */
@@ -142,25 +153,45 @@ struct nfs_mount_request {
        struct net              *net;
 };
 
+struct nfs_mount_info {
+       void (*fill_super)(struct super_block *, struct nfs_mount_info *);
+       int (*set_security)(struct super_block *, struct dentry *, struct nfs_mount_info *);
+       struct nfs_parsed_mount_data *parsed;
+       struct nfs_clone_mount *cloned;
+       struct nfs_fh *mntfh;
+};
+
 extern int nfs_mount(struct nfs_mount_request *info);
 extern void nfs_umount(const struct nfs_mount_request *info);
 
 /* client.c */
 extern const struct rpc_program nfs_program;
 extern void nfs_clients_init(struct net *net);
+extern struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *);
+int nfs_create_rpc_client(struct nfs_client *, const struct rpc_timeout *, rpc_authflavor_t);
+struct nfs_client *nfs_get_client(const struct nfs_client_initdata *,
+                                 const struct rpc_timeout *, const char *,
+                                 rpc_authflavor_t);
+int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *, struct nfs_fattr *);
+void nfs_server_insert_lists(struct nfs_server *);
+void nfs_init_timeout_values(struct rpc_timeout *, int, unsigned int, unsigned int);
+int nfs_init_server_rpcclient(struct nfs_server *, const struct rpc_timeout *t,
+               rpc_authflavor_t);
+struct nfs_server *nfs_alloc_server(void);
+void nfs_server_copy_userdata(struct nfs_server *, struct nfs_server *);
 
 extern void nfs_cleanup_cb_ident_idr(struct net *);
 extern void nfs_put_client(struct nfs_client *);
+extern void nfs_free_client(struct nfs_client *);
 extern struct nfs_client *nfs4_find_client_ident(struct net *, int);
 extern struct nfs_client *
 nfs4_find_client_sessionid(struct net *, const struct sockaddr *,
                                struct nfs4_sessionid *);
-extern struct nfs_server *nfs_create_server(
-                                       const struct nfs_parsed_mount_data *,
-                                       struct nfs_fh *);
+extern struct nfs_server *nfs_create_server(struct nfs_mount_info *,
+                                       struct nfs_subversion *);
 extern struct nfs_server *nfs4_create_server(
-                                       const struct nfs_parsed_mount_data *,
-                                       struct nfs_fh *);
+                                       struct nfs_mount_info *,
+                                       struct nfs_subversion *);
 extern struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *,
                                                      struct nfs_fh *);
 extern void nfs_free_server(struct nfs_server *server);
@@ -188,6 +219,17 @@ static inline void nfs_fs_proc_exit(void)
 }
 #endif
 
+#ifdef CONFIG_NFS_V4_1
+int nfs_sockaddr_match_ipaddr(const struct sockaddr *, const struct sockaddr *);
+#endif
+
+/* nfs3client.c */
+#if IS_ENABLED(CONFIG_NFS_V3)
+struct nfs_server *nfs3_create_server(struct nfs_mount_info *, struct nfs_subversion *);
+struct nfs_server *nfs3_clone_server(struct nfs_server *, struct nfs_fh *,
+                                    struct nfs_fattr *, rpc_authflavor_t);
+#endif
+
 /* callback_xdr.c */
 extern struct svc_version nfs4_callback_version1;
 extern struct svc_version nfs4_callback_version4;
@@ -220,7 +262,7 @@ extern int nfs3_decode_dirent(struct xdr_stream *,
                                struct nfs_entry *, int);
 
 /* nfs4xdr.c */
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 extern int nfs4_decode_dirent(struct xdr_stream *,
                                struct nfs_entry *, int);
 #endif
@@ -230,7 +272,7 @@ extern const u32 nfs41_maxwrite_overhead;
 #endif
 
 /* nfs4proc.c */
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 extern struct rpc_procinfo nfs4_procedures[];
 #endif
 
@@ -245,25 +287,63 @@ extern struct nfs_client *nfs_init_client(struct nfs_client *clp,
 /* dir.c */
 extern int nfs_access_cache_shrinker(struct shrinker *shrink,
                                        struct shrink_control *sc);
+struct dentry *nfs_lookup(struct inode *, struct dentry *, unsigned int);
+int nfs_create(struct inode *, struct dentry *, umode_t, bool);
+int nfs_mkdir(struct inode *, struct dentry *, umode_t);
+int nfs_rmdir(struct inode *, struct dentry *);
+int nfs_unlink(struct inode *, struct dentry *);
+int nfs_symlink(struct inode *, struct dentry *, const char *);
+int nfs_link(struct dentry *, struct inode *, struct dentry *);
+int nfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
+int nfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+
+/* file.c */
+int nfs_file_fsync_commit(struct file *, loff_t, loff_t, int);
+loff_t nfs_file_llseek(struct file *, loff_t, int);
+int nfs_file_flush(struct file *, fl_owner_t);
+ssize_t nfs_file_read(struct kiocb *, const struct iovec *, unsigned long, loff_t);
+ssize_t nfs_file_splice_read(struct file *, loff_t *, struct pipe_inode_info *,
+                            size_t, unsigned int);
+int nfs_file_mmap(struct file *, struct vm_area_struct *);
+ssize_t nfs_file_write(struct kiocb *, const struct iovec *, unsigned long, loff_t);
+int nfs_file_release(struct inode *, struct file *);
+int nfs_lock(struct file *, int, struct file_lock *);
+int nfs_flock(struct file *, int, struct file_lock *);
+ssize_t nfs_file_splice_write(struct pipe_inode_info *, struct file *, loff_t *,
+                             size_t, unsigned int);
+int nfs_check_flags(int);
+int nfs_setlease(struct file *, long, struct file_lock **);
 
 /* inode.c */
 extern struct workqueue_struct *nfsiod_workqueue;
 extern struct inode *nfs_alloc_inode(struct super_block *sb);
 extern void nfs_destroy_inode(struct inode *);
 extern int nfs_write_inode(struct inode *, struct writeback_control *);
+extern void nfs_clear_inode(struct inode *);
 extern void nfs_evict_inode(struct inode *);
-#ifdef CONFIG_NFS_V4
-extern void nfs4_evict_inode(struct inode *);
-#endif
 void nfs_zap_acl_cache(struct inode *inode);
 extern int nfs_wait_bit_killable(void *word);
 
 /* super.c */
+extern const struct super_operations nfs_sops;
+extern struct file_system_type nfs_fs_type;
 extern struct file_system_type nfs_xdev_fs_type;
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 extern struct file_system_type nfs4_xdev_fs_type;
 extern struct file_system_type nfs4_referral_fs_type;
 #endif
+struct dentry *nfs_try_mount(int, const char *, struct nfs_mount_info *,
+                       struct nfs_subversion *);
+void nfs_initialise_sb(struct super_block *);
+int nfs_set_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *);
+int nfs_clone_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *);
+struct dentry *nfs_fs_mount_common(struct nfs_server *, int, const char *,
+                                  struct nfs_mount_info *, struct nfs_subversion *);
+struct dentry *nfs_fs_mount(struct file_system_type *, int, const char *, void *);
+struct dentry * nfs_xdev_mount_common(struct file_system_type *, int,
+               const char *, struct nfs_mount_info *);
+void nfs_kill_super(struct super_block *);
+void nfs_fill_super(struct super_block *, struct nfs_mount_info *);
 
 extern struct rpc_stat nfs_rpcstat;
 
@@ -284,7 +364,7 @@ struct vfsmount *nfs_do_submount(struct dentry *, struct nfs_fh *,
 /* getroot.c */
 extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *,
                                   const char *);
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *,
                                    const char *);
 
@@ -304,12 +384,23 @@ extern int nfs_initiate_read(struct rpc_clnt *clnt,
 extern void nfs_read_prepare(struct rpc_task *task, void *calldata);
 extern int nfs_generic_pagein(struct nfs_pageio_descriptor *desc,
                              struct nfs_pgio_header *hdr);
-extern void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio,
+extern void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio,
                        struct inode *inode,
                        const struct nfs_pgio_completion_ops *compl_ops);
 extern void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio);
 extern void nfs_readdata_release(struct nfs_read_data *rdata);
 
+/* super.c */
+void nfs_clone_super(struct super_block *, struct nfs_mount_info *);
+void nfs_umount_begin(struct super_block *);
+int  nfs_statfs(struct dentry *, struct kstatfs *);
+int  nfs_show_options(struct seq_file *, struct dentry *);
+int  nfs_show_devname(struct seq_file *, struct dentry *);
+int  nfs_show_path(struct seq_file *, struct dentry *);
+int  nfs_show_stats(struct seq_file *, struct dentry *);
+void nfs_put_super(struct super_block *);
+int nfs_remount(struct super_block *sb, int *flags, char *raw_data);
+
 /* write.c */
 extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
                        struct inode *inode, int ioflags,
@@ -318,7 +409,7 @@ extern struct nfs_write_header *nfs_writehdr_alloc(void);
 extern void nfs_writehdr_free(struct nfs_pgio_header *hdr);
 extern int nfs_generic_flush(struct nfs_pageio_descriptor *desc,
                             struct nfs_pgio_header *hdr);
-extern void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio,
+extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
                        struct inode *inode, int ioflags,
                        const struct nfs_pgio_completion_ops *compl_ops);
 extern void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio);
@@ -463,13 +554,14 @@ void nfs_super_set_maxbytes(struct super_block *sb, __u64 maxfilesize)
 static inline
 unsigned int nfs_page_length(struct page *page)
 {
-       loff_t i_size = i_size_read(page->mapping->host);
+       loff_t i_size = i_size_read(page_file_mapping(page)->host);
 
        if (i_size > 0) {
+               pgoff_t page_index = page_file_index(page);
                pgoff_t end_index = (i_size - 1) >> PAGE_CACHE_SHIFT;
-               if (page->index < end_index)
+               if (page_index < end_index)
                        return PAGE_CACHE_SIZE;
-               if (page->index == end_index)
+               if (page_index == end_index)
                        return ((i_size - 1) & ~PAGE_CACHE_MASK) + 1;
        }
        return 0;
index 08b9c93675da512e0ef8174a25e1e048f282b527..655925373b9161c6a6c64ad157002d10cf8064e2 100644 (file)
@@ -7,6 +7,7 @@
  * NFS namespace
  */
 
+#include <linux/module.h>
 #include <linux/dcache.h>
 #include <linux/gfp.h>
 #include <linux/mount.h>
@@ -112,6 +113,7 @@ Elong_unlock:
 Elong:
        return ERR_PTR(-ENAMETOOLONG);
 }
+EXPORT_SYMBOL_GPL(nfs_path);
 
 /*
  * nfs_d_automount - Handle crossing a mountpoint on the server
@@ -195,20 +197,7 @@ static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
                                           const char *devname,
                                           struct nfs_clone_mount *mountdata)
 {
-#ifdef CONFIG_NFS_V4
-       struct vfsmount *mnt = ERR_PTR(-EINVAL);
-       switch (server->nfs_client->rpc_ops->version) {
-               case 2:
-               case 3:
-                       mnt = vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata);
-                       break;
-               case 4:
-                       mnt = vfs_kern_mount(&nfs4_xdev_fs_type, 0, devname, mountdata);
-       }
-       return mnt;
-#else
        return vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata);
-#endif
 }
 
 /**
@@ -253,6 +242,7 @@ out:
        dprintk("<-- nfs_do_submount() = %p\n", mnt);
        return mnt;
 }
+EXPORT_SYMBOL_GPL(nfs_do_submount);
 
 struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry,
                              struct nfs_fh *fh, struct nfs_fattr *fattr)
@@ -268,3 +258,4 @@ struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry,
 
        return nfs_do_submount(dentry, fh, fattr, server->client->cl_auth->au_flavor);
 }
+EXPORT_SYMBOL_GPL(nfs_submount);
index 8a6394edb8b015375eb26016594a09e7b275261f..0539de1b8d1fe3477500b400ff2b9b6d5781b984 100644 (file)
@@ -20,7 +20,7 @@ struct nfs_net {
        wait_queue_head_t bl_wq;
        struct list_head nfs_client_list;
        struct list_head nfs_volume_list;
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
        struct idr cb_ident_idr; /* Protected by nfs_client_lock */
 #endif
        spinlock_t nfs_client_lock;
diff --git a/fs/nfs/nfs.h b/fs/nfs/nfs.h
new file mode 100644 (file)
index 0000000..43679df
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2012 Netapp, Inc. All rights reserved.
+ *
+ * Function and structures exported by the NFS module
+ * for use by NFS version-specific modules.
+ */
+#ifndef __LINUX_INTERNAL_NFS_H
+#define __LINUX_INTERNAL_NFS_H
+
+#include <linux/fs.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/nfs_xdr.h>
+
+struct nfs_subversion {
+       struct module *owner;   /* THIS_MODULE pointer */
+       struct file_system_type *nfs_fs;        /* NFS filesystem type */
+       const struct rpc_version *rpc_vers;     /* NFS version information */
+       const struct nfs_rpc_ops *rpc_ops;      /* NFS operations */
+       const struct super_operations *sops;    /* NFS Super operations */
+       const struct xattr_handler **xattr;     /* NFS xattr handlers */
+       struct list_head list;          /* List of NFS versions */
+};
+
+struct nfs_subversion *get_nfs_version(unsigned int);
+void put_nfs_version(struct nfs_subversion *);
+void register_nfs_version(struct nfs_subversion *);
+void unregister_nfs_version(struct nfs_subversion *);
+
+#endif /* __LINUX_INTERNAL_NFS_H */
diff --git a/fs/nfs/nfs2super.c b/fs/nfs/nfs2super.c
new file mode 100644 (file)
index 0000000..0a9782c
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012 Netapp, Inc. All rights reserved.
+ */
+#include <linux/module.h>
+#include <linux/nfs_fs.h>
+#include "internal.h"
+#include "nfs.h"
+
+static struct nfs_subversion nfs_v2 = {
+       .owner = THIS_MODULE,
+       .nfs_fs   = &nfs_fs_type,
+       .rpc_vers = &nfs_version2,
+       .rpc_ops  = &nfs_v2_clientops,
+       .sops     = &nfs_sops,
+};
+
+static int __init init_nfs_v2(void)
+{
+       register_nfs_version(&nfs_v2);
+       return 0;
+}
+
+static void __exit exit_nfs_v2(void)
+{
+       unregister_nfs_version(&nfs_v2);
+}
+
+MODULE_LICENSE("GPL");
+
+module_init(init_nfs_v2);
+module_exit(exit_nfs_v2);
index baf759bccd054d562d24d9adf43af18915da8267..d04f0df7be553db3aa89ce5637ff3c8ef3044d07 100644 (file)
@@ -106,19 +106,16 @@ static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
 static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result)
 {
        u32 recvd, count;
-       size_t hdrlen;
        __be32 *p;
 
        p = xdr_inline_decode(xdr, 4);
        if (unlikely(p == NULL))
                goto out_overflow;
        count = be32_to_cpup(p);
-       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
-       recvd = xdr->buf->len - hdrlen;
+       recvd = xdr_read_pages(xdr, count);
        if (unlikely(count > recvd))
                goto out_cheating;
 out:
-       xdr_read_pages(xdr, count);
        result->eof = 0;        /* NFSv2 does not pass EOF flag on the wire. */
        result->count = count;
        return count;
@@ -440,7 +437,6 @@ static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
 static int decode_path(struct xdr_stream *xdr)
 {
        u32 length, recvd;
-       size_t hdrlen;
        __be32 *p;
 
        p = xdr_inline_decode(xdr, 4);
@@ -449,12 +445,9 @@ static int decode_path(struct xdr_stream *xdr)
        length = be32_to_cpup(p);
        if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN))
                goto out_size;
-       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
-       recvd = xdr->buf->len - hdrlen;
+       recvd = xdr_read_pages(xdr, length);
        if (unlikely(length > recvd))
                goto out_cheating;
-
-       xdr_read_pages(xdr, length);
        xdr_terminate_string(xdr->buf, length);
        return 0;
 out_size:
@@ -972,22 +965,7 @@ out_overflow:
  */
 static int decode_readdirok(struct xdr_stream *xdr)
 {
-       u32 recvd, pglen;
-       size_t hdrlen;
-
-       pglen = xdr->buf->page_len;
-       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
-       recvd = xdr->buf->len - hdrlen;
-       if (unlikely(pglen > recvd))
-               goto out_cheating;
-out:
-       xdr_read_pages(xdr, pglen);
-       return pglen;
-out_cheating:
-       dprintk("NFS: server cheating in readdir result: "
-               "pglen %u > recvd %u\n", pglen, recvd);
-       pglen = recvd;
-       goto out;
+       return xdr_read_pages(xdr, xdr->buf->page_len);
 }
 
 static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req,
diff --git a/fs/nfs/nfs3client.c b/fs/nfs/nfs3client.c
new file mode 100644 (file)
index 0000000..b3fc65e
--- /dev/null
@@ -0,0 +1,65 @@
+#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include "internal.h"
+
+#ifdef CONFIG_NFS_V3_ACL
+static struct rpc_stat         nfsacl_rpcstat = { &nfsacl_program };
+static const struct rpc_version *nfsacl_version[] = {
+       [3]                     = &nfsacl_version3,
+};
+
+const struct rpc_program nfsacl_program = {
+       .name                   = "nfsacl",
+       .number                 = NFS_ACL_PROGRAM,
+       .nrvers                 = ARRAY_SIZE(nfsacl_version),
+       .version                = nfsacl_version,
+       .stats                  = &nfsacl_rpcstat,
+};
+
+/*
+ * Initialise an NFSv3 ACL client connection
+ */
+static void nfs_init_server_aclclient(struct nfs_server *server)
+{
+       if (server->flags & NFS_MOUNT_NOACL)
+               goto out_noacl;
+
+       server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3);
+       if (IS_ERR(server->client_acl))
+               goto out_noacl;
+
+       /* No errors! Assume that Sun nfsacls are supported */
+       server->caps |= NFS_CAP_ACLS;
+       return;
+
+out_noacl:
+       server->caps &= ~NFS_CAP_ACLS;
+}
+#else
+static inline void nfs_init_server_aclclient(struct nfs_server *server)
+{
+       server->flags &= ~NFS_MOUNT_NOACL;
+       server->caps &= ~NFS_CAP_ACLS;
+}
+#endif
+
+struct nfs_server *nfs3_create_server(struct nfs_mount_info *mount_info,
+                                     struct nfs_subversion *nfs_mod)
+{
+       struct nfs_server *server = nfs_create_server(mount_info, nfs_mod);
+       /* Create a client RPC handle for the NFS v3 ACL management interface */
+       if (!IS_ERR(server))
+               nfs_init_server_aclclient(server);
+       return server;
+}
+
+struct nfs_server *nfs3_clone_server(struct nfs_server *source,
+                                    struct nfs_fh *fh,
+                                    struct nfs_fattr *fattr,
+                                    rpc_authflavor_t flavor)
+{
+       struct nfs_server *server = nfs_clone_server(source, fh, fattr, flavor);
+       if (!IS_ERR(server) && !IS_ERR(source->client_acl))
+               nfs_init_server_aclclient(server);
+       return server;
+}
index 3187e24e8f78f41ffa3f78b55ccf42ff29d167f0..0952c791df36eb4f1aa58beb20732c9af67a3098 100644 (file)
@@ -877,6 +877,46 @@ nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
        return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl);
 }
 
+static int nfs3_have_delegation(struct inode *inode, fmode_t flags)
+{
+       return 0;
+}
+
+static int nfs3_return_delegation(struct inode *inode)
+{
+       nfs_wb_all(inode);
+       return 0;
+}
+
+static const struct inode_operations nfs3_dir_inode_operations = {
+       .create         = nfs_create,
+       .lookup         = nfs_lookup,
+       .link           = nfs_link,
+       .unlink         = nfs_unlink,
+       .symlink        = nfs_symlink,
+       .mkdir          = nfs_mkdir,
+       .rmdir          = nfs_rmdir,
+       .mknod          = nfs_mknod,
+       .rename         = nfs_rename,
+       .permission     = nfs_permission,
+       .getattr        = nfs_getattr,
+       .setattr        = nfs_setattr,
+       .listxattr      = nfs3_listxattr,
+       .getxattr       = nfs3_getxattr,
+       .setxattr       = nfs3_setxattr,
+       .removexattr    = nfs3_removexattr,
+};
+
+static const struct inode_operations nfs3_file_inode_operations = {
+       .permission     = nfs_permission,
+       .getattr        = nfs_getattr,
+       .setattr        = nfs_setattr,
+       .listxattr      = nfs3_listxattr,
+       .getxattr       = nfs3_getxattr,
+       .setxattr       = nfs3_setxattr,
+       .removexattr    = nfs3_removexattr,
+};
+
 const struct nfs_rpc_ops nfs_v3_clientops = {
        .version        = 3,                    /* protocol version */
        .dentry_ops     = &nfs_dentry_operations,
@@ -885,6 +925,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
        .file_ops       = &nfs_file_operations,
        .getroot        = nfs3_proc_get_root,
        .submount       = nfs_submount,
+       .try_mount      = nfs_try_mount,
        .getattr        = nfs3_proc_getattr,
        .setattr        = nfs3_proc_setattr,
        .lookup         = nfs3_proc_lookup,
@@ -910,9 +951,11 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
        .pathconf       = nfs3_proc_pathconf,
        .decode_dirent  = nfs3_decode_dirent,
        .read_setup     = nfs3_proc_read_setup,
+       .read_pageio_init = nfs_pageio_init_read,
        .read_rpc_prepare = nfs3_proc_read_rpc_prepare,
        .read_done      = nfs3_read_done,
        .write_setup    = nfs3_proc_write_setup,
+       .write_pageio_init = nfs_pageio_init_write,
        .write_rpc_prepare = nfs3_proc_write_rpc_prepare,
        .write_done     = nfs3_write_done,
        .commit_setup   = nfs3_proc_commit_setup,
@@ -921,5 +964,11 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
        .lock           = nfs3_proc_lock,
        .clear_acl_cache = nfs3_forget_cached_acls,
        .close_context  = nfs_close_context,
+       .have_delegation = nfs3_have_delegation,
+       .return_delegation = nfs3_return_delegation,
+       .alloc_client   = nfs_alloc_client,
        .init_client    = nfs_init_client,
+       .free_client    = nfs_free_client,
+       .create_server  = nfs3_create_server,
+       .clone_server   = nfs3_clone_server,
 };
diff --git a/fs/nfs/nfs3super.c b/fs/nfs/nfs3super.c
new file mode 100644 (file)
index 0000000..cc471c7
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012 Netapp, Inc. All rights reserved.
+ */
+#include <linux/module.h>
+#include <linux/nfs_fs.h>
+#include "internal.h"
+#include "nfs.h"
+
+static struct nfs_subversion nfs_v3 = {
+       .owner = THIS_MODULE,
+       .nfs_fs   = &nfs_fs_type,
+       .rpc_vers = &nfs_version3,
+       .rpc_ops  = &nfs_v3_clientops,
+       .sops     = &nfs_sops,
+};
+
+static int __init init_nfs_v3(void)
+{
+       register_nfs_version(&nfs_v3);
+       return 0;
+}
+
+static void __exit exit_nfs_v3(void)
+{
+       unregister_nfs_version(&nfs_v3);
+}
+
+MODULE_LICENSE("GPL");
+
+module_init(init_nfs_v3);
+module_exit(exit_nfs_v3);
index 902de489ec9bac793dd2e3fa65b663262879b271..6cbe89400dfcc134b9af58d6ad16ecf42bab911a 100644 (file)
@@ -246,7 +246,6 @@ static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages,
 static int decode_nfspath3(struct xdr_stream *xdr)
 {
        u32 recvd, count;
-       size_t hdrlen;
        __be32 *p;
 
        p = xdr_inline_decode(xdr, 4);
@@ -255,12 +254,9 @@ static int decode_nfspath3(struct xdr_stream *xdr)
        count = be32_to_cpup(p);
        if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN))
                goto out_nametoolong;
-       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
-       recvd = xdr->buf->len - hdrlen;
+       recvd = xdr_read_pages(xdr, count);
        if (unlikely(count > recvd))
                goto out_cheating;
-
-       xdr_read_pages(xdr, count);
        xdr_terminate_string(xdr->buf, count);
        return 0;
 
@@ -329,14 +325,14 @@ static void encode_createverf3(struct xdr_stream *xdr, const __be32 *verifier)
        memcpy(p, verifier, NFS3_CREATEVERFSIZE);
 }
 
-static int decode_writeverf3(struct xdr_stream *xdr, __be32 *verifier)
+static int decode_writeverf3(struct xdr_stream *xdr, struct nfs_write_verifier *verifier)
 {
        __be32 *p;
 
        p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE);
        if (unlikely(p == NULL))
                goto out_overflow;
-       memcpy(verifier, p, NFS3_WRITEVERFSIZE);
+       memcpy(verifier->data, p, NFS3_WRITEVERFSIZE);
        return 0;
 out_overflow:
        print_overflow_msg(__func__, xdr);
@@ -1587,7 +1583,6 @@ static int decode_read3resok(struct xdr_stream *xdr,
                             struct nfs_readres *result)
 {
        u32 eof, count, ocount, recvd;
-       size_t hdrlen;
        __be32 *p;
 
        p = xdr_inline_decode(xdr, 4 + 4 + 4);
@@ -1598,13 +1593,10 @@ static int decode_read3resok(struct xdr_stream *xdr,
        ocount = be32_to_cpup(p++);
        if (unlikely(ocount != count))
                goto out_mismatch;
-       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
-       recvd = xdr->buf->len - hdrlen;
+       recvd = xdr_read_pages(xdr, count);
        if (unlikely(count > recvd))
                goto out_cheating;
-
 out:
-       xdr_read_pages(xdr, count);
        result->eof = eof;
        result->count = count;
        return count;
@@ -1676,20 +1668,22 @@ static int decode_write3resok(struct xdr_stream *xdr,
 {
        __be32 *p;
 
-       p = xdr_inline_decode(xdr, 4 + 4 + NFS3_WRITEVERFSIZE);
+       p = xdr_inline_decode(xdr, 4 + 4);
        if (unlikely(p == NULL))
                goto out_overflow;
        result->count = be32_to_cpup(p++);
        result->verf->committed = be32_to_cpup(p++);
        if (unlikely(result->verf->committed > NFS_FILE_SYNC))
                goto out_badvalue;
-       memcpy(result->verf->verifier, p, NFS3_WRITEVERFSIZE);
+       if (decode_writeverf3(xdr, &result->verf->verifier))
+               goto out_eio;
        return result->count;
 out_badvalue:
        dprintk("NFS: bad stable_how value: %u\n", result->verf->committed);
        return -EIO;
 out_overflow:
        print_overflow_msg(__func__, xdr);
+out_eio:
        return -EIO;
 }
 
@@ -2039,22 +2033,7 @@ out_truncated:
  */
 static int decode_dirlist3(struct xdr_stream *xdr)
 {
-       u32 recvd, pglen;
-       size_t hdrlen;
-
-       pglen = xdr->buf->page_len;
-       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
-       recvd = xdr->buf->len - hdrlen;
-       if (unlikely(pglen > recvd))
-               goto out_cheating;
-out:
-       xdr_read_pages(xdr, pglen);
-       return pglen;
-out_cheating:
-       dprintk("NFS: server cheating in readdir result: "
-               "pglen %u > recvd %u\n", pglen, recvd);
-       pglen = recvd;
-       goto out;
+       return xdr_read_pages(xdr, xdr->buf->page_len);
 }
 
 static int decode_readdir3resok(struct xdr_stream *xdr,
@@ -2337,7 +2316,7 @@ static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
                goto out;
        if (status != NFS3_OK)
                goto out_status;
-       error = decode_writeverf3(xdr, result->verf->verifier);
+       error = decode_writeverf3(xdr, &result->verf->verifier);
 out:
        return error;
 out_status:
@@ -2364,7 +2343,7 @@ static inline int decode_getacl3resok(struct xdr_stream *xdr,
        if (result->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
                goto out;
 
-       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
+       hdrlen = xdr_stream_pos(xdr);
 
        acl = NULL;
        if (result->mask & NFS_ACL)
index cc5900ac61b584774de45f10c48906b3ff99de74..3b950dd81e81f82b4fd32c5b508fde181d07b875 100644 (file)
@@ -9,7 +9,7 @@
 #ifndef __LINUX_FS_NFS_NFS4_FS_H
 #define __LINUX_FS_NFS_NFS4_FS_H
 
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 
 struct idmap;
 
@@ -200,7 +200,10 @@ struct nfs4_state_maintenance_ops {
 };
 
 extern const struct dentry_operations nfs4_dentry_operations;
-extern const struct inode_operations nfs4_dir_inode_operations;
+
+/* dir.c */
+int nfs_atomic_open(struct inode *, struct dentry *, struct file *,
+                   unsigned, umode_t, int *);
 
 /* nfs4namespace.c */
 rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *);
@@ -301,6 +304,10 @@ extern const u32 nfs4_pathconf_bitmap[2];
 extern const u32 nfs4_fsinfo_bitmap[3];
 extern const u32 nfs4_fs_locations_bitmap[2];
 
+void nfs4_free_client(struct nfs_client *);
+
+struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *);
+
 /* nfs4renewd.c */
 extern void nfs4_schedule_state_renewal(struct nfs_client *);
 extern void nfs4_renewd_prepare_shutdown(struct nfs_server *);
@@ -354,6 +361,29 @@ extern void nfs4_free_lock_state(struct nfs_server *server, struct nfs4_lock_sta
 
 extern const nfs4_stateid zero_stateid;
 
+/* nfs4super.c */
+struct nfs_mount_info;
+extern struct nfs_subversion nfs_v4;
+struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *, struct nfs_subversion *);
+extern bool nfs4_disable_idmapping;
+extern unsigned short max_session_slots;
+extern unsigned short send_implementation_id;
+
+/* nfs4sysctl.c */
+#ifdef CONFIG_SYSCTL
+int nfs4_register_sysctl(void);
+void nfs4_unregister_sysctl(void);
+#else
+static inline int nfs4_register_sysctl(void)
+{
+       return 0;
+}
+
+static inline void nfs4_unregister_sysctl(void)
+{
+}
+#endif
+
 /* nfs4xdr.c */
 extern struct rpc_procinfo nfs4_procedures[];
 
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
new file mode 100644 (file)
index 0000000..cbcdfaf
--- /dev/null
@@ -0,0 +1,656 @@
+/*
+ * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+#include <linux/module.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_idmap.h>
+#include <linux/nfs_mount.h>
+#include <linux/sunrpc/auth.h>
+#include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/bc_xprt.h>
+#include "internal.h"
+#include "callback.h"
+#include "delegation.h"
+#include "pnfs.h"
+#include "netns.h"
+
+#define NFSDBG_FACILITY                NFSDBG_CLIENT
+
+/*
+ * Get a unique NFSv4.0 callback identifier which will be used
+ * by the V4.0 callback service to lookup the nfs_client struct
+ */
+static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion)
+{
+       int ret = 0;
+       struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
+
+       if (clp->rpc_ops->version != 4 || minorversion != 0)
+               return ret;
+retry:
+       if (!idr_pre_get(&nn->cb_ident_idr, GFP_KERNEL))
+               return -ENOMEM;
+       spin_lock(&nn->nfs_client_lock);
+       ret = idr_get_new(&nn->cb_ident_idr, clp, &clp->cl_cb_ident);
+       spin_unlock(&nn->nfs_client_lock);
+       if (ret == -EAGAIN)
+               goto retry;
+       return ret;
+}
+
+#ifdef CONFIG_NFS_V4_1
+static void nfs4_shutdown_session(struct nfs_client *clp)
+{
+       if (nfs4_has_session(clp)) {
+               nfs4_destroy_session(clp->cl_session);
+               nfs4_destroy_clientid(clp);
+       }
+
+}
+#else /* CONFIG_NFS_V4_1 */
+static void nfs4_shutdown_session(struct nfs_client *clp)
+{
+}
+#endif /* CONFIG_NFS_V4_1 */
+
+struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
+{
+       int err;
+       struct nfs_client *clp = nfs_alloc_client(cl_init);
+       if (IS_ERR(clp))
+               return clp;
+
+       err = nfs_get_cb_ident_idr(clp, cl_init->minorversion);
+       if (err)
+               goto error;
+
+       spin_lock_init(&clp->cl_lock);
+       INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
+       rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
+       clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
+       clp->cl_minorversion = cl_init->minorversion;
+       clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion];
+       return clp;
+
+error:
+       kfree(clp);
+       return ERR_PTR(err);
+}
+
+/*
+ * Destroy the NFS4 callback service
+ */
+static void nfs4_destroy_callback(struct nfs_client *clp)
+{
+       if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
+               nfs_callback_down(clp->cl_mvops->minor_version);
+}
+
+static void nfs4_shutdown_client(struct nfs_client *clp)
+{
+       if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
+               nfs4_kill_renewd(clp);
+       nfs4_shutdown_session(clp);
+       nfs4_destroy_callback(clp);
+       if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
+               nfs_idmap_delete(clp);
+
+       rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
+       kfree(clp->cl_serverowner);
+       kfree(clp->cl_serverscope);
+       kfree(clp->cl_implid);
+}
+
+void nfs4_free_client(struct nfs_client *clp)
+{
+       nfs4_shutdown_client(clp);
+       nfs_free_client(clp);
+}
+
+/*
+ * Initialize the NFS4 callback service
+ */
+static int nfs4_init_callback(struct nfs_client *clp)
+{
+       int error;
+
+       if (clp->rpc_ops->version == 4) {
+               struct rpc_xprt *xprt;
+
+               xprt = rcu_dereference_raw(clp->cl_rpcclient->cl_xprt);
+
+               if (nfs4_has_session(clp)) {
+                       error = xprt_setup_backchannel(xprt,
+                                               NFS41_BC_MIN_CALLBACKS);
+                       if (error < 0)
+                               return error;
+               }
+
+               error = nfs_callback_up(clp->cl_mvops->minor_version, xprt);
+               if (error < 0) {
+                       dprintk("%s: failed to start callback. Error = %d\n",
+                               __func__, error);
+                       return error;
+               }
+               __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
+       }
+       return 0;
+}
+
+/*
+ * Initialize the minor version specific parts of an NFS4 client record
+ */
+static int nfs4_init_client_minor_version(struct nfs_client *clp)
+{
+#if defined(CONFIG_NFS_V4_1)
+       if (clp->cl_mvops->minor_version) {
+               struct nfs4_session *session = NULL;
+               /*
+                * Create the session and mark it expired.
+                * When a SEQUENCE operation encounters the expired session
+                * it will do session recovery to initialize it.
+                */
+               session = nfs4_alloc_session(clp);
+               if (!session)
+                       return -ENOMEM;
+
+               clp->cl_session = session;
+               /*
+                * The create session reply races with the server back
+                * channel probe. Mark the client NFS_CS_SESSION_INITING
+                * so that the client back channel can find the
+                * nfs_client struct
+                */
+               nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING);
+       }
+#endif /* CONFIG_NFS_V4_1 */
+
+       return nfs4_init_callback(clp);
+}
+
+/**
+ * nfs4_init_client - Initialise an NFS4 client record
+ *
+ * @clp: nfs_client to initialise
+ * @timeparms: timeout parameters for underlying RPC transport
+ * @ip_addr: callback IP address in presentation format
+ * @authflavor: authentication flavor for underlying RPC transport
+ *
+ * Returns pointer to an NFS client, or an ERR_PTR value.
+ */
+struct nfs_client *nfs4_init_client(struct nfs_client *clp,
+                                   const struct rpc_timeout *timeparms,
+                                   const char *ip_addr,
+                                   rpc_authflavor_t authflavour)
+{
+       char buf[INET6_ADDRSTRLEN + 1];
+       int error;
+
+       if (clp->cl_cons_state == NFS_CS_READY) {
+               /* the client is initialised already */
+               dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp);
+               return clp;
+       }
+
+       /* Check NFS protocol revision and initialize RPC op vector */
+       clp->rpc_ops = &nfs_v4_clientops;
+
+       __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
+       error = nfs_create_rpc_client(clp, timeparms, authflavour);
+       if (error < 0)
+               goto error;
+
+       /* If no clientaddr= option was specified, find a usable cb address */
+       if (ip_addr == NULL) {
+               struct sockaddr_storage cb_addr;
+               struct sockaddr *sap = (struct sockaddr *)&cb_addr;
+
+               error = rpc_localaddr(clp->cl_rpcclient, sap, sizeof(cb_addr));
+               if (error < 0)
+                       goto error;
+               error = rpc_ntop(sap, buf, sizeof(buf));
+               if (error < 0)
+                       goto error;
+               ip_addr = (const char *)buf;
+       }
+       strlcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
+
+       error = nfs_idmap_new(clp);
+       if (error < 0) {
+               dprintk("%s: failed to create idmapper. Error = %d\n",
+                       __func__, error);
+               goto error;
+       }
+       __set_bit(NFS_CS_IDMAP, &clp->cl_res_state);
+
+       error = nfs4_init_client_minor_version(clp);
+       if (error < 0)
+               goto error;
+
+       if (!nfs4_has_session(clp))
+               nfs_mark_client_ready(clp, NFS_CS_READY);
+       return clp;
+
+error:
+       nfs_mark_client_ready(clp, error);
+       nfs_put_client(clp);
+       dprintk("<-- nfs4_init_client() = xerror %d\n", error);
+       return ERR_PTR(error);
+}
+
+static void nfs4_destroy_server(struct nfs_server *server)
+{
+       nfs_server_return_all_delegations(server);
+       unset_pnfs_layoutdriver(server);
+       nfs4_purge_state_owners(server);
+}
+
+/*
+ * NFSv4.0 callback thread helper
+ *
+ * Find a client by callback identifier
+ */
+struct nfs_client *
+nfs4_find_client_ident(struct net *net, int cb_ident)
+{
+       struct nfs_client *clp;
+       struct nfs_net *nn = net_generic(net, nfs_net_id);
+
+       spin_lock(&nn->nfs_client_lock);
+       clp = idr_find(&nn->cb_ident_idr, cb_ident);
+       if (clp)
+               atomic_inc(&clp->cl_count);
+       spin_unlock(&nn->nfs_client_lock);
+       return clp;
+}
+
+#if defined(CONFIG_NFS_V4_1)
+/* Common match routine for v4.0 and v4.1 callback services */
+static bool nfs4_cb_match_client(const struct sockaddr *addr,
+               struct nfs_client *clp, u32 minorversion)
+{
+       struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
+
+       /* Don't match clients that failed to initialise */
+       if (!(clp->cl_cons_state == NFS_CS_READY ||
+           clp->cl_cons_state == NFS_CS_SESSION_INITING))
+               return false;
+
+       smp_rmb();
+
+       /* Match the version and minorversion */
+       if (clp->rpc_ops->version != 4 ||
+           clp->cl_minorversion != minorversion)
+               return false;
+
+       /* Match only the IP address, not the port number */
+       if (!nfs_sockaddr_match_ipaddr(addr, clap))
+               return false;
+
+       return true;
+}
+
+/*
+ * NFSv4.1 callback thread helper
+ * For CB_COMPOUND calls, find a client by IP address, protocol version,
+ * minorversion, and sessionID
+ *
+ * Returns NULL if no such client
+ */
+struct nfs_client *
+nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
+                          struct nfs4_sessionid *sid)
+{
+       struct nfs_client *clp;
+       struct nfs_net *nn = net_generic(net, nfs_net_id);
+
+       spin_lock(&nn->nfs_client_lock);
+       list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
+               if (nfs4_cb_match_client(addr, clp, 1) == false)
+                       continue;
+
+               if (!nfs4_has_session(clp))
+                       continue;
+
+               /* Match sessionid*/
+               if (memcmp(clp->cl_session->sess_id.data,
+                   sid->data, NFS4_MAX_SESSIONID_LEN) != 0)
+                       continue;
+
+               atomic_inc(&clp->cl_count);
+               spin_unlock(&nn->nfs_client_lock);
+               return clp;
+       }
+       spin_unlock(&nn->nfs_client_lock);
+       return NULL;
+}
+
+#else /* CONFIG_NFS_V4_1 */
+
+struct nfs_client *
+nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
+                          struct nfs4_sessionid *sid)
+{
+       return NULL;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
+/*
+ * Set up an NFS4 client
+ */
+static int nfs4_set_client(struct nfs_server *server,
+               const char *hostname,
+               const struct sockaddr *addr,
+               const size_t addrlen,
+               const char *ip_addr,
+               rpc_authflavor_t authflavour,
+               int proto, const struct rpc_timeout *timeparms,
+               u32 minorversion, struct net *net)
+{
+       struct nfs_client_initdata cl_init = {
+               .hostname = hostname,
+               .addr = addr,
+               .addrlen = addrlen,
+               .nfs_mod = &nfs_v4,
+               .proto = proto,
+               .minorversion = minorversion,
+               .net = net,
+       };
+       struct nfs_client *clp;
+       int error;
+
+       dprintk("--> nfs4_set_client()\n");
+
+       if (server->flags & NFS_MOUNT_NORESVPORT)
+               set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
+
+       /* Allocate or find a client reference we can use */
+       clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour);
+       if (IS_ERR(clp)) {
+               error = PTR_ERR(clp);
+               goto error;
+       }
+
+       /*
+        * Query for the lease time on clientid setup or renewal
+        *
+        * Note that this will be set on nfs_clients that were created
+        * only for the DS role and did not set this bit, but now will
+        * serve a dual role.
+        */
+       set_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state);
+
+       server->nfs_client = clp;
+       dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp);
+       return 0;
+error:
+       dprintk("<-- nfs4_set_client() = xerror %d\n", error);
+       return error;
+}
+
+/*
+ * Set up a pNFS Data Server client.
+ *
+ * Return any existing nfs_client that matches server address,port,version
+ * and minorversion.
+ *
+ * For a new nfs_client, use a soft mount (default), a low retrans and a
+ * low timeout interval so that if a connection is lost, we retry through
+ * the MDS.
+ */
+struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
+               const struct sockaddr *ds_addr, int ds_addrlen,
+               int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans)
+{
+       struct nfs_client_initdata cl_init = {
+               .addr = ds_addr,
+               .addrlen = ds_addrlen,
+               .nfs_mod = &nfs_v4,
+               .proto = ds_proto,
+               .minorversion = mds_clp->cl_minorversion,
+               .net = mds_clp->cl_net,
+       };
+       struct rpc_timeout ds_timeout;
+       struct nfs_client *clp;
+
+       /*
+        * Set an authflavor equual to the MDS value. Use the MDS nfs_client
+        * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS
+        * (section 13.1 RFC 5661).
+        */
+       nfs_init_timeout_values(&ds_timeout, ds_proto, ds_timeo, ds_retrans);
+       clp = nfs_get_client(&cl_init, &ds_timeout, mds_clp->cl_ipaddr,
+                            mds_clp->cl_rpcclient->cl_auth->au_flavor);
+
+       dprintk("<-- %s %p\n", __func__, clp);
+       return clp;
+}
+EXPORT_SYMBOL_GPL(nfs4_set_ds_client);
+
+/*
+ * Session has been established, and the client marked ready.
+ * Set the mount rsize and wsize with negotiated fore channel
+ * attributes which will be bound checked in nfs_server_set_fsinfo.
+ */
+static void nfs4_session_set_rwsize(struct nfs_server *server)
+{
+#ifdef CONFIG_NFS_V4_1
+       struct nfs4_session *sess;
+       u32 server_resp_sz;
+       u32 server_rqst_sz;
+
+       if (!nfs4_has_session(server->nfs_client))
+               return;
+       sess = server->nfs_client->cl_session;
+       server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead;
+       server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead;
+
+       if (server->rsize > server_resp_sz)
+               server->rsize = server_resp_sz;
+       if (server->wsize > server_rqst_sz)
+               server->wsize = server_rqst_sz;
+#endif /* CONFIG_NFS_V4_1 */
+}
+
+static int nfs4_server_common_setup(struct nfs_server *server,
+               struct nfs_fh *mntfh)
+{
+       struct nfs_fattr *fattr;
+       int error;
+
+       BUG_ON(!server->nfs_client);
+       BUG_ON(!server->nfs_client->rpc_ops);
+       BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
+
+       /* data servers support only a subset of NFSv4.1 */
+       if (is_ds_only_client(server->nfs_client))
+               return -EPROTONOSUPPORT;
+
+       fattr = nfs_alloc_fattr();
+       if (fattr == NULL)
+               return -ENOMEM;
+
+       /* We must ensure the session is initialised first */
+       error = nfs4_init_session(server);
+       if (error < 0)
+               goto out;
+
+       /* Probe the root fh to retrieve its FSID and filehandle */
+       error = nfs4_get_rootfh(server, mntfh);
+       if (error < 0)
+               goto out;
+
+       dprintk("Server FSID: %llx:%llx\n",
+                       (unsigned long long) server->fsid.major,
+                       (unsigned long long) server->fsid.minor);
+       dprintk("Mount FH: %d\n", mntfh->size);
+
+       nfs4_session_set_rwsize(server);
+
+       error = nfs_probe_fsinfo(server, mntfh, fattr);
+       if (error < 0)
+               goto out;
+
+       if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
+               server->namelen = NFS4_MAXNAMLEN;
+
+       nfs_server_insert_lists(server);
+       server->mount_time = jiffies;
+       server->destroy = nfs4_destroy_server;
+out:
+       nfs_free_fattr(fattr);
+       return error;
+}
+
+/*
+ * Create a version 4 volume record
+ */
+static int nfs4_init_server(struct nfs_server *server,
+               const struct nfs_parsed_mount_data *data)
+{
+       struct rpc_timeout timeparms;
+       int error;
+
+       dprintk("--> nfs4_init_server()\n");
+
+       nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
+                       data->timeo, data->retrans);
+
+       /* Initialise the client representation from the mount data */
+       server->flags = data->flags;
+       server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK;
+       if (!(data->flags & NFS_MOUNT_NORDIRPLUS))
+                       server->caps |= NFS_CAP_READDIRPLUS;
+       server->options = data->options;
+
+       /* Get a client record */
+       error = nfs4_set_client(server,
+                       data->nfs_server.hostname,
+                       (const struct sockaddr *)&data->nfs_server.address,
+                       data->nfs_server.addrlen,
+                       data->client_address,
+                       data->auth_flavors[0],
+                       data->nfs_server.protocol,
+                       &timeparms,
+                       data->minorversion,
+                       data->net);
+       if (error < 0)
+               goto error;
+
+       /*
+        * Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower
+        * authentication.
+        */
+       if (nfs4_disable_idmapping && data->auth_flavors[0] == RPC_AUTH_UNIX)
+               server->caps |= NFS_CAP_UIDGID_NOMAP;
+
+       if (data->rsize)
+               server->rsize = nfs_block_size(data->rsize, NULL);
+       if (data->wsize)
+               server->wsize = nfs_block_size(data->wsize, NULL);
+
+       server->acregmin = data->acregmin * HZ;
+       server->acregmax = data->acregmax * HZ;
+       server->acdirmin = data->acdirmin * HZ;
+       server->acdirmax = data->acdirmax * HZ;
+
+       server->port = data->nfs_server.port;
+
+       error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]);
+
+error:
+       /* Done */
+       dprintk("<-- nfs4_init_server() = %d\n", error);
+       return error;
+}
+
+/*
+ * Create a version 4 volume record
+ * - keyed on server and FSID
+ */
+/*struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
+                                     struct nfs_fh *mntfh)*/
+struct nfs_server *nfs4_create_server(struct nfs_mount_info *mount_info,
+                                     struct nfs_subversion *nfs_mod)
+{
+       struct nfs_server *server;
+       int error;
+
+       dprintk("--> nfs4_create_server()\n");
+
+       server = nfs_alloc_server();
+       if (!server)
+               return ERR_PTR(-ENOMEM);
+
+       /* set up the general RPC client */
+       error = nfs4_init_server(server, mount_info->parsed);
+       if (error < 0)
+               goto error;
+
+       error = nfs4_server_common_setup(server, mount_info->mntfh);
+       if (error < 0)
+               goto error;
+
+       dprintk("<-- nfs4_create_server() = %p\n", server);
+       return server;
+
+error:
+       nfs_free_server(server);
+       dprintk("<-- nfs4_create_server() = error %d\n", error);
+       return ERR_PTR(error);
+}
+
+/*
+ * Create an NFS4 referral server record
+ */
+struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
+                                              struct nfs_fh *mntfh)
+{
+       struct nfs_client *parent_client;
+       struct nfs_server *server, *parent_server;
+       int error;
+
+       dprintk("--> nfs4_create_referral_server()\n");
+
+       server = nfs_alloc_server();
+       if (!server)
+               return ERR_PTR(-ENOMEM);
+
+       parent_server = NFS_SB(data->sb);
+       parent_client = parent_server->nfs_client;
+
+       /* Initialise the client representation from the parent server */
+       nfs_server_copy_userdata(server, parent_server);
+       server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR;
+
+       /* Get a client representation.
+        * Note: NFSv4 always uses TCP, */
+       error = nfs4_set_client(server, data->hostname,
+                               data->addr,
+                               data->addrlen,
+                               parent_client->cl_ipaddr,
+                               data->authflavor,
+                               rpc_protocol(parent_server->client),
+                               parent_server->client->cl_timeout,
+                               parent_client->cl_mvops->minor_version,
+                               parent_client->cl_net);
+       if (error < 0)
+               goto error;
+
+       error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor);
+       if (error < 0)
+               goto error;
+
+       error = nfs4_server_common_setup(server, mntfh);
+       if (error < 0)
+               goto error;
+
+       dprintk("<-- nfs_create_referral_server() = %p\n", server);
+       return server;
+
+error:
+       nfs_free_server(server);
+       dprintk("<-- nfs4_create_referral_server() = error %d\n", error);
+       return ERR_PTR(error);
+}
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
new file mode 100644 (file)
index 0000000..acb65e7
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ *  linux/fs/nfs/file.c
+ *
+ *  Copyright (C) 1992  Rick Sladkey
+ */
+#include <linux/nfs_fs.h>
+#include "internal.h"
+#include "pnfs.h"
+
+#define NFSDBG_FACILITY                NFSDBG_FILE
+
+static int
+nfs4_file_open(struct inode *inode, struct file *filp)
+{
+       struct nfs_open_context *ctx;
+       struct dentry *dentry = filp->f_path.dentry;
+       struct dentry *parent = NULL;
+       struct inode *dir;
+       unsigned openflags = filp->f_flags;
+       struct iattr attr;
+       int err;
+
+       BUG_ON(inode != dentry->d_inode);
+       /*
+        * If no cached dentry exists or if it's negative, NFSv4 handled the
+        * opens in ->lookup() or ->create().
+        *
+        * We only get this far for a cached positive dentry.  We skipped
+        * revalidation, so handle it here by dropping the dentry and returning
+        * -EOPENSTALE.  The VFS will retry the lookup/create/open.
+        */
+
+       dprintk("NFS: open file(%s/%s)\n",
+               dentry->d_parent->d_name.name,
+               dentry->d_name.name);
+
+       if ((openflags & O_ACCMODE) == 3)
+               openflags--;
+
+       /* We can't create new files here */
+       openflags &= ~(O_CREAT|O_EXCL);
+
+       parent = dget_parent(dentry);
+       dir = parent->d_inode;
+
+       ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
+       err = PTR_ERR(ctx);
+       if (IS_ERR(ctx))
+               goto out;
+
+       attr.ia_valid = ATTR_OPEN;
+       if (openflags & O_TRUNC) {
+               attr.ia_valid |= ATTR_SIZE;
+               attr.ia_size = 0;
+               nfs_wb_all(inode);
+       }
+
+       inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr);
+       if (IS_ERR(inode)) {
+               err = PTR_ERR(inode);
+               switch (err) {
+               case -EPERM:
+               case -EACCES:
+               case -EDQUOT:
+               case -ENOSPC:
+               case -EROFS:
+                       goto out_put_ctx;
+               default:
+                       goto out_drop;
+               }
+       }
+       iput(inode);
+       if (inode != dentry->d_inode)
+               goto out_drop;
+
+       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
+       nfs_file_set_open_context(filp, ctx);
+       err = 0;
+
+out_put_ctx:
+       put_nfs_open_context(ctx);
+out:
+       dput(parent);
+       return err;
+
+out_drop:
+       d_drop(dentry);
+       err = -EOPENSTALE;
+       goto out_put_ctx;
+}
+
+static int
+nfs4_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+       int ret;
+       struct inode *inode = file->f_path.dentry->d_inode;
+
+       ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
+       mutex_lock(&inode->i_mutex);
+       ret = nfs_file_fsync_commit(file, start, end, datasync);
+       if (!ret && !datasync)
+               /* application has asked for meta-data sync */
+               ret = pnfs_layoutcommit_inode(inode, true);
+       mutex_unlock(&inode->i_mutex);
+
+       return ret;
+}
+
+const struct file_operations nfs4_file_operations = {
+       .llseek         = nfs_file_llseek,
+       .read           = do_sync_read,
+       .write          = do_sync_write,
+       .aio_read       = nfs_file_read,
+       .aio_write      = nfs_file_write,
+       .mmap           = nfs_file_mmap,
+       .open           = nfs4_file_open,
+       .flush          = nfs_file_flush,
+       .release        = nfs_file_release,
+       .fsync          = nfs4_file_fsync,
+       .lock           = nfs_lock,
+       .flock          = nfs_flock,
+       .splice_read    = nfs_file_splice_read,
+       .splice_write   = nfs_file_splice_write,
+       .check_flags    = nfs_check_flags,
+       .setlease       = nfs_setlease,
+};
index e1340293872c7a70e747d051888e5ab603db905e..53f94d915bd18ce77c4cf542ebc25378c56f2add 100644 (file)
@@ -205,9 +205,9 @@ static int filelayout_async_handle_error(struct rpc_task *task,
        case -EPIPE:
                dprintk("%s DS connection error %d\n", __func__,
                        task->tk_status);
-               if (!filelayout_test_devid_invalid(devid))
-                       _pnfs_return_layout(inode);
                filelayout_mark_devid_invalid(devid);
+               clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(inode)->flags);
+               _pnfs_return_layout(inode);
                rpc_wake_up(&tbl->slot_tbl_waitq);
                nfs4_ds_disconnect(clp);
                /* fall through */
@@ -351,9 +351,9 @@ static void prepare_to_resend_writes(struct nfs_commit_data *data)
        struct nfs_page *first = nfs_list_entry(data->pages.next);
 
        data->task.tk_status = 0;
-       memcpy(data->verf.verifier, first->wb_verf.verifier,
-              sizeof(first->wb_verf.verifier));
-       data->verf.verifier[0]++; /* ensure verifier mismatch */
+       memcpy(&data->verf.verifier, &first->wb_verf,
+              sizeof(data->verf.verifier));
+       data->verf.verifier.data[0]++; /* ensure verifier mismatch */
 }
 
 static int filelayout_commit_done_cb(struct rpc_task *task,
index a1fab8da7f03c8819951af81d95e6268a71dc80f..f81231f30d949aa5d6b8a328cce91b792c656004 100644 (file)
@@ -728,7 +728,7 @@ get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, gfp_t gfp_fla
        pdev->layout_type = LAYOUT_NFSV4_1_FILES;
        pdev->pages = pages;
        pdev->pgbase = 0;
-       pdev->pglen = PAGE_SIZE * max_pages;
+       pdev->pglen = max_resp_sz;
        pdev->mincount = 0;
 
        rc = nfs4_proc_getdeviceinfo(server, pdev);
diff --git a/fs/nfs/nfs4getroot.c b/fs/nfs/nfs4getroot.c
new file mode 100644 (file)
index 0000000..6a83780
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+* Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+* Written by David Howells (dhowells@redhat.com)
+*/
+
+#include <linux/nfs_fs.h>
+#include "nfs4_fs.h"
+
+#define NFSDBG_FACILITY                NFSDBG_CLIENT
+
+int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh)
+{
+       struct nfs_fsinfo fsinfo;
+       int ret = -ENOMEM;
+
+       dprintk("--> nfs4_get_rootfh()\n");
+
+       fsinfo.fattr = nfs_alloc_fattr();
+       if (fsinfo.fattr == NULL)
+               goto out;
+
+       /* Start by getting the root filehandle from the server */
+       ret = nfs4_proc_get_rootfh(server, mntfh, &fsinfo);
+       if (ret < 0) {
+               dprintk("nfs4_get_rootfh: getroot error = %d\n", -ret);
+               goto out;
+       }
+
+       if (!(fsinfo.fattr->valid & NFS_ATTR_FATTR_TYPE)
+                       || !S_ISDIR(fsinfo.fattr->mode)) {
+               printk(KERN_ERR "nfs4_get_rootfh:"
+                      " getroot encountered non-directory\n");
+               ret = -ENOTDIR;
+               goto out;
+       }
+
+       if (fsinfo.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
+               printk(KERN_ERR "nfs4_get_rootfh:"
+                      " getroot obtained referral\n");
+               ret = -EREMOTE;
+               goto out;
+       }
+
+       memcpy(&server->fsid, &fsinfo.fattr->fsid, sizeof(server->fsid));
+out:
+       nfs_free_fattr(fsinfo.fattr);
+       dprintk("<-- nfs4_get_rootfh() = %d\n", ret);
+       return ret;
+}
index c157b2089b475c22c046a15f293236f7725a4ee7..a99a8d94872131610ab543d5a06d9f4f541cc2dc 100644 (file)
@@ -43,7 +43,6 @@
 #include <linux/printk.h>
 #include <linux/slab.h>
 #include <linux/sunrpc/clnt.h>
-#include <linux/sunrpc/gss_api.h>
 #include <linux/nfs.h>
 #include <linux/nfs4.h>
 #include <linux/nfs_fs.h>
@@ -73,8 +72,6 @@
 
 #define NFS4_MAX_LOOP_ON_RECOVER (10)
 
-static unsigned short max_session_slots = NFS4_DEF_SLOT_TABLE_SIZE;
-
 struct nfs4_opendata;
 static int _nfs4_proc_open(struct nfs4_opendata *data);
 static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
@@ -259,7 +256,12 @@ static int nfs4_wait_clnt_recover(struct nfs_client *clp)
 
        res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING,
                        nfs_wait_bit_killable, TASK_KILLABLE);
-       return res;
+       if (res)
+               return res;
+
+       if (clp->cl_cons_state < 0)
+               return clp->cl_cons_state;
+       return 0;
 }
 
 static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
@@ -294,8 +296,8 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
                case 0:
                        return 0;
                case -NFS4ERR_OPENMODE:
-                       if (inode && nfs_have_delegation(inode, FMODE_READ)) {
-                               nfs_inode_return_delegation(inode);
+                       if (inode && nfs4_have_delegation(inode, FMODE_READ)) {
+                               nfs4_inode_return_delegation(inode);
                                exception->retry = 1;
                                return 0;
                        }
@@ -1065,7 +1067,7 @@ static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmo
                return;
        }
        rcu_read_unlock();
-       nfs_inode_return_delegation(inode);
+       nfs4_inode_return_delegation(inode);
 }
 
 static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
@@ -1756,33 +1758,70 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta
 }
 
 #if defined(CONFIG_NFS_V4_1)
-static int nfs41_check_expired_stateid(struct nfs4_state *state, nfs4_stateid *stateid, unsigned int flags)
+static void nfs41_clear_delegation_stateid(struct nfs4_state *state)
 {
-       int status = NFS_OK;
        struct nfs_server *server = NFS_SERVER(state->inode);
+       nfs4_stateid *stateid = &state->stateid;
+       int status;
 
-       if (state->flags & flags) {
-               status = nfs41_test_stateid(server, stateid);
-               if (status != NFS_OK) {
+       /* If a state reset has been done, test_stateid is unneeded */
+       if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
+               return;
+
+       status = nfs41_test_stateid(server, stateid);
+       if (status != NFS_OK) {
+               /* Free the stateid unless the server explicitly
+                * informs us the stateid is unrecognized. */
+               if (status != -NFS4ERR_BAD_STATEID)
                        nfs41_free_stateid(server, stateid);
-                       state->flags &= ~flags;
-               }
+
+               clear_bit(NFS_DELEGATED_STATE, &state->flags);
+       }
+}
+
+/**
+ * nfs41_check_open_stateid - possibly free an open stateid
+ *
+ * @state: NFSv4 state for an inode
+ *
+ * Returns NFS_OK if recovery for this stateid is now finished.
+ * Otherwise a negative NFS4ERR value is returned.
+ */
+static int nfs41_check_open_stateid(struct nfs4_state *state)
+{
+       struct nfs_server *server = NFS_SERVER(state->inode);
+       nfs4_stateid *stateid = &state->stateid;
+       int status;
+
+       /* If a state reset has been done, test_stateid is unneeded */
+       if ((test_bit(NFS_O_RDONLY_STATE, &state->flags) == 0) &&
+           (test_bit(NFS_O_WRONLY_STATE, &state->flags) == 0) &&
+           (test_bit(NFS_O_RDWR_STATE, &state->flags) == 0))
+               return -NFS4ERR_BAD_STATEID;
+
+       status = nfs41_test_stateid(server, stateid);
+       if (status != NFS_OK) {
+               /* Free the stateid unless the server explicitly
+                * informs us the stateid is unrecognized. */
+               if (status != -NFS4ERR_BAD_STATEID)
+                       nfs41_free_stateid(server, stateid);
+
+               clear_bit(NFS_O_RDONLY_STATE, &state->flags);
+               clear_bit(NFS_O_WRONLY_STATE, &state->flags);
+               clear_bit(NFS_O_RDWR_STATE, &state->flags);
        }
        return status;
 }
 
 static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
 {
-       int deleg_status, open_status;
-       int deleg_flags = 1 << NFS_DELEGATED_STATE;
-       int open_flags = (1 << NFS_O_RDONLY_STATE) | (1 << NFS_O_WRONLY_STATE) | (1 << NFS_O_RDWR_STATE);
-
-       deleg_status = nfs41_check_expired_stateid(state, &state->stateid, deleg_flags);
-       open_status = nfs41_check_expired_stateid(state,  &state->open_stateid, open_flags);
+       int status;
 
-       if ((deleg_status == NFS_OK) && (open_status == NFS_OK))
-               return NFS_OK;
-       return nfs4_open_expired(sp, state);
+       nfs41_clear_delegation_stateid(state);
+       status = nfs41_check_open_stateid(state);
+       if (status != NFS_OK)
+               status = nfs4_open_expired(sp, state);
+       return status;
 }
 #endif
 
@@ -2375,11 +2414,15 @@ static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
        int i, len, status = 0;
        rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS];
 
-       len = gss_mech_list_pseudoflavors(&flav_array[0]);
-       flav_array[len] = RPC_AUTH_NULL;
-       len += 1;
+       len = rpcauth_list_flavors(flav_array, ARRAY_SIZE(flav_array));
+       BUG_ON(len < 0);
 
        for (i = 0; i < len; i++) {
+               /* AUTH_UNIX is the default flavor if none was specified,
+                * thus has already been tried. */
+               if (flav_array[i] == RPC_AUTH_UNIX)
+                       continue;
+
                status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]);
                if (status == -NFS4ERR_WRONGSEC || status == -EACCES)
                        continue;
@@ -2766,9 +2809,7 @@ static int nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
  *
  * In the case of WRITE, we also want to put the GETATTR after
  * the operation -- in this case because we want to make sure
- * we get the post-operation mtime and size.  This means that
- * we can't use xdr_encode_pages() as written: we need a variant
- * of it which would leave room in the 'tail' iovec.
+ * we get the post-operation mtime and size.
  *
  * Both of these changes to the XDR layer would in fact be quite
  * minor, but I decided to leave them for a subsequent patch.
@@ -2821,7 +2862,9 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                return PTR_ERR(ctx);
 
        sattr->ia_mode &= ~current_umask();
-       state = nfs4_do_open(dir, dentry, ctx->mode, flags, sattr, ctx->cred, NULL);
+       state = nfs4_do_open(dir, dentry, ctx->mode,
+                       flags, sattr, ctx->cred,
+                       &ctx->mdsthreshold);
        d_drop(dentry);
        if (IS_ERR(state)) {
                status = PTR_ERR(state);
@@ -3315,8 +3358,14 @@ static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, str
 
 static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
 {
+       int error;
+
        nfs_fattr_init(fsinfo->fattr);
-       return nfs4_do_fsinfo(server, fhandle, fsinfo);
+       error = nfs4_do_fsinfo(server, fhandle, fsinfo);
+       if (error == 0)
+               set_pnfs_layoutdriver(server, fhandle, fsinfo->layouttype);
+
+       return error;
 }
 
 static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
@@ -3443,7 +3492,7 @@ bool nfs4_write_need_cache_consistency_data(const struct nfs_write_data *data)
        /* Otherwise, request attributes if and only if we don't hold
         * a delegation
         */
-       return nfs_have_delegation(hdr->inode, FMODE_READ) == 0;
+       return nfs4_have_delegation(hdr->inode, FMODE_READ) == 0;
 }
 
 static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_message *msg)
@@ -3732,7 +3781,8 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
                .rpc_argp = &args,
                .rpc_resp = &res,
        };
-       int ret = -ENOMEM, npages, i, acl_len = 0;
+       int ret = -ENOMEM, npages, i;
+       size_t acl_len = 0;
 
        npages = (buflen + PAGE_SIZE - 1) >> PAGE_SHIFT;
        /* As long as we're doing a round trip to the server anyway,
@@ -3847,7 +3897,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
        i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
        if (i < 0)
                return i;
-       nfs_inode_return_delegation(inode);
+       nfs4_inode_return_delegation(inode);
        ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
 
        /*
@@ -3961,6 +4011,16 @@ static void nfs4_init_boot_verifier(const struct nfs_client *clp,
        memcpy(bootverf->data, verf, sizeof(bootverf->data));
 }
 
+/**
+ * nfs4_proc_setclientid - Negotiate client ID
+ * @clp: state data structure
+ * @program: RPC program for NFSv4 callback service
+ * @port: IP port number for NFS4 callback service
+ * @cred: RPC credential to use for this call
+ * @res: where to place the result
+ *
+ * Returns zero, a negative errno, or a negative NFS4ERR status code.
+ */
 int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
                unsigned short port, struct rpc_cred *cred,
                struct nfs4_setclientid_res *res)
@@ -3977,44 +4037,44 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
                .rpc_resp = res,
                .rpc_cred = cred,
        };
-       int loop = 0;
        int status;
 
+       /* nfs_client_id4 */
        nfs4_init_boot_verifier(clp, &sc_verifier);
-
-       for(;;) {
-               rcu_read_lock();
-               setclientid.sc_name_len = scnprintf(setclientid.sc_name,
-                               sizeof(setclientid.sc_name), "%s/%s %s %s %u",
-                               clp->cl_ipaddr,
-                               rpc_peeraddr2str(clp->cl_rpcclient,
-                                                       RPC_DISPLAY_ADDR),
-                               rpc_peeraddr2str(clp->cl_rpcclient,
-                                                       RPC_DISPLAY_PROTO),
-                               clp->cl_rpcclient->cl_auth->au_ops->au_name,
-                               clp->cl_id_uniquifier);
-               setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
+       rcu_read_lock();
+       setclientid.sc_name_len = scnprintf(setclientid.sc_name,
+                       sizeof(setclientid.sc_name), "%s/%s %s",
+                       clp->cl_ipaddr,
+                       rpc_peeraddr2str(clp->cl_rpcclient,
+                                               RPC_DISPLAY_ADDR),
+                       rpc_peeraddr2str(clp->cl_rpcclient,
+                                               RPC_DISPLAY_PROTO));
+       /* cb_client4 */
+       setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
                                sizeof(setclientid.sc_netid),
                                rpc_peeraddr2str(clp->cl_rpcclient,
                                                        RPC_DISPLAY_NETID));
-               setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr,
+       rcu_read_unlock();
+       setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr,
                                sizeof(setclientid.sc_uaddr), "%s.%u.%u",
                                clp->cl_ipaddr, port >> 8, port & 255);
-               rcu_read_unlock();
 
-               status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
-               if (status != -NFS4ERR_CLID_INUSE)
-                       break;
-               if (loop != 0) {
-                       ++clp->cl_id_uniquifier;
-                       break;
-               }
-               ++loop;
-               ssleep(clp->cl_lease_time / HZ + 1);
-       }
+       dprintk("NFS call  setclientid auth=%s, '%.*s'\n",
+               clp->cl_rpcclient->cl_auth->au_ops->au_name,
+               setclientid.sc_name_len, setclientid.sc_name);
+       status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
+       dprintk("NFS reply setclientid: %d\n", status);
        return status;
 }
 
+/**
+ * nfs4_proc_setclientid_confirm - Confirm client ID
+ * @clp: state data structure
+ * @res: result of a previous SETCLIENTID
+ * @cred: RPC credential to use for this call
+ *
+ * Returns zero, a negative errno, or a negative NFS4ERR status code.
+ */
 int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
                struct nfs4_setclientid_res *arg,
                struct rpc_cred *cred)
@@ -4029,6 +4089,9 @@ int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
        unsigned long now;
        int status;
 
+       dprintk("NFS call  setclientid_confirm auth=%s, (client ID %llx)\n",
+               clp->cl_rpcclient->cl_auth->au_ops->au_name,
+               clp->cl_clientid);
        now = jiffies;
        status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
        if (status == 0) {
@@ -4037,6 +4100,7 @@ int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
                clp->cl_last_renewal = now;
                spin_unlock(&clp->cl_lock);
        }
+       dprintk("NFS reply setclientid_confirm: %d\n", status);
        return status;
 }
 
@@ -4681,9 +4745,17 @@ out:
 }
 
 #if defined(CONFIG_NFS_V4_1)
+/**
+ * nfs41_check_expired_locks - possibly free a lock stateid
+ *
+ * @state: NFSv4 state for an inode
+ *
+ * Returns NFS_OK if recovery for this stateid is now finished.
+ * Otherwise a negative NFS4ERR value is returned.
+ */
 static int nfs41_check_expired_locks(struct nfs4_state *state)
 {
-       int status, ret = NFS_OK;
+       int status, ret = -NFS4ERR_BAD_STATEID;
        struct nfs4_lock_state *lsp;
        struct nfs_server *server = NFS_SERVER(state->inode);
 
@@ -4691,7 +4763,11 @@ static int nfs41_check_expired_locks(struct nfs4_state *state)
                if (lsp->ls_flags & NFS_LOCK_INITIALIZED) {
                        status = nfs41_test_stateid(server, &lsp->ls_stateid);
                        if (status != NFS_OK) {
-                               nfs41_free_stateid(server, &lsp->ls_stateid);
+                               /* Free the stateid unless the server
+                                * informs us the stateid is unrecognized. */
+                               if (status != -NFS4ERR_BAD_STATEID)
+                                       nfs41_free_stateid(server,
+                                                       &lsp->ls_stateid);
                                lsp->ls_flags &= ~NFS_LOCK_INITIALIZED;
                                ret = status;
                        }
@@ -4707,9 +4783,9 @@ static int nfs41_lock_expired(struct nfs4_state *state, struct file_lock *reques
 
        if (test_bit(LK_STATE_IN_USE, &state->flags))
                status = nfs41_check_expired_locks(state);
-       if (status == NFS_OK)
-               return status;
-       return nfs4_lock_expired(state, request);
+       if (status != NFS_OK)
+               status = nfs4_lock_expired(state, request);
+       return status;
 }
 #endif
 
@@ -4807,7 +4883,7 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
         * Don't rely on the VFS having checked the file open mode,
         * since it won't do this for flock() locks.
         */
-       switch (request->fl_type & (F_RDLCK|F_WRLCK|F_UNLCK)) {
+       switch (request->fl_type) {
        case F_RDLCK:
                if (!(filp->f_mode & FMODE_READ))
                        return -EBADF;
@@ -5168,6 +5244,8 @@ out:
 /*
  * nfs4_proc_exchange_id()
  *
+ * Returns zero, a negative errno, or a negative NFS4ERR status code.
+ *
  * Since the clientid has expired, all compounds using sessions
  * associated with the stale clientid will be returning
  * NFS4ERR_BADSESSION in the sequence operation, and will therefore
@@ -5192,16 +5270,14 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
                .rpc_cred = cred,
        };
 
-       dprintk("--> %s\n", __func__);
-       BUG_ON(clp == NULL);
-
        nfs4_init_boot_verifier(clp, &verifier);
-
        args.id_len = scnprintf(args.id, sizeof(args.id),
-                               "%s/%s/%u",
+                               "%s/%s",
                                clp->cl_ipaddr,
-                               clp->cl_rpcclient->cl_nodename,
-                               clp->cl_rpcclient->cl_auth->au_flavor);
+                               clp->cl_rpcclient->cl_nodename);
+       dprintk("NFS call  exchange_id auth=%s, '%.*s'\n",
+               clp->cl_rpcclient->cl_auth->au_ops->au_name,
+               args.id_len, args.id);
 
        res.server_owner = kzalloc(sizeof(struct nfs41_server_owner),
                                        GFP_NOFS);
@@ -5264,12 +5340,12 @@ out_server_scope:
        kfree(res.server_scope);
 out:
        if (clp->cl_implid != NULL)
-               dprintk("%s: Server Implementation ID: "
+               dprintk("NFS reply exchange_id: Server Implementation ID: "
                        "domain: %s, name: %s, date: %llu,%u\n",
-                       __func__, clp->cl_implid->domain, clp->cl_implid->name,
+                       clp->cl_implid->domain, clp->cl_implid->name,
                        clp->cl_implid->date.seconds,
                        clp->cl_implid->date.nseconds);
-       dprintk("<-- %s status= %d\n", __func__, status);
+       dprintk("NFS reply exchange_id: %d\n", status);
        return status;
 }
 
@@ -6570,22 +6646,36 @@ static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
                .rpc_resp = &res,
        };
 
+       dprintk("NFS call  test_stateid %p\n", stateid);
        nfs41_init_sequence(&args.seq_args, &res.seq_res, 0);
        status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 1);
-
-       if (status == NFS_OK)
-               return res.status;
-       return status;
+       if (status != NFS_OK) {
+               dprintk("NFS reply test_stateid: failed, %d\n", status);
+               return status;
+       }
+       dprintk("NFS reply test_stateid: succeeded, %d\n", -res.status);
+       return -res.status;
 }
 
+/**
+ * nfs41_test_stateid - perform a TEST_STATEID operation
+ *
+ * @server: server / transport on which to perform the operation
+ * @stateid: state ID to test
+ *
+ * Returns NFS_OK if the server recognizes that "stateid" is valid.
+ * Otherwise a negative NFS4ERR value is returned if the operation
+ * failed or the state ID is not currently valid.
+ */
 static int nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
 {
        struct nfs4_exception exception = { };
        int err;
        do {
-               err = nfs4_handle_exception(server,
-                               _nfs41_test_stateid(server, stateid),
-                               &exception);
+               err = _nfs41_test_stateid(server, stateid);
+               if (err != -NFS4ERR_DELAY)
+                       break;
+               nfs4_handle_exception(server, err, &exception);
        } while (exception.retry);
        return err;
 }
@@ -6601,19 +6691,34 @@ static int _nfs4_free_stateid(struct nfs_server *server, nfs4_stateid *stateid)
                .rpc_argp = &args,
                .rpc_resp = &res,
        };
+       int status;
 
+       dprintk("NFS call  free_stateid %p\n", stateid);
        nfs41_init_sequence(&args.seq_args, &res.seq_res, 0);
-       return nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 1);
+       status = nfs4_call_sync_sequence(server->client, server, &msg,
+                                        &args.seq_args, &res.seq_res, 1);
+       dprintk("NFS reply free_stateid: %d\n", status);
+       return status;
 }
 
+/**
+ * nfs41_free_stateid - perform a FREE_STATEID operation
+ *
+ * @server: server / transport on which to perform the operation
+ * @stateid: state ID to release
+ *
+ * Returns NFS_OK if the server freed "stateid".  Otherwise a
+ * negative NFS4ERR value is returned.
+ */
 static int nfs41_free_stateid(struct nfs_server *server, nfs4_stateid *stateid)
 {
        struct nfs4_exception exception = { };
        int err;
        do {
-               err = nfs4_handle_exception(server,
-                               _nfs4_free_stateid(server, stateid),
-                               &exception);
+               err = _nfs4_free_stateid(server, stateid);
+               if (err != -NFS4ERR_DELAY)
+                       break;
+               nfs4_handle_exception(server, err, &exception);
        } while (exception.retry);
        return err;
 }
@@ -6725,6 +6830,26 @@ const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = {
 #endif
 };
 
+const struct inode_operations nfs4_dir_inode_operations = {
+       .create         = nfs_create,
+       .lookup         = nfs_lookup,
+       .atomic_open    = nfs_atomic_open,
+       .link           = nfs_link,
+       .unlink         = nfs_unlink,
+       .symlink        = nfs_symlink,
+       .mkdir          = nfs_mkdir,
+       .rmdir          = nfs_rmdir,
+       .mknod          = nfs_mknod,
+       .rename         = nfs_rename,
+       .permission     = nfs_permission,
+       .getattr        = nfs_getattr,
+       .setattr        = nfs_setattr,
+       .getxattr       = generic_getxattr,
+       .setxattr       = generic_setxattr,
+       .listxattr      = generic_listxattr,
+       .removexattr    = generic_removexattr,
+};
+
 static const struct inode_operations nfs4_file_inode_operations = {
        .permission     = nfs_permission,
        .getattr        = nfs_getattr,
@@ -6743,6 +6868,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
        .file_ops       = &nfs4_file_operations,
        .getroot        = nfs4_proc_get_root,
        .submount       = nfs4_submount,
+       .try_mount      = nfs4_try_mount,
        .getattr        = nfs4_proc_getattr,
        .setattr        = nfs4_proc_setattr,
        .lookup         = nfs4_proc_lookup,
@@ -6769,9 +6895,11 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
        .set_capabilities = nfs4_server_capabilities,
        .decode_dirent  = nfs4_decode_dirent,
        .read_setup     = nfs4_proc_read_setup,
+       .read_pageio_init = pnfs_pageio_init_read,
        .read_rpc_prepare = nfs4_proc_read_rpc_prepare,
        .read_done      = nfs4_read_done,
        .write_setup    = nfs4_proc_write_setup,
+       .write_pageio_init = pnfs_pageio_init_write,
        .write_rpc_prepare = nfs4_proc_write_rpc_prepare,
        .write_done     = nfs4_write_done,
        .commit_setup   = nfs4_proc_commit_setup,
@@ -6781,7 +6909,13 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
        .clear_acl_cache = nfs4_zap_acl_attr,
        .close_context  = nfs4_close_context,
        .open_context   = nfs4_atomic_open,
+       .have_delegation = nfs4_have_delegation,
+       .return_delegation = nfs4_inode_return_delegation,
+       .alloc_client   = nfs4_alloc_client,
        .init_client    = nfs4_init_client,
+       .free_client    = nfs4_free_client,
+       .create_server  = nfs4_create_server,
+       .clone_server   = nfs_clone_server,
 };
 
 static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
@@ -6796,10 +6930,6 @@ const struct xattr_handler *nfs4_xattr_handlers[] = {
        NULL
 };
 
-module_param(max_session_slots, ushort, 0644);
-MODULE_PARM_DESC(max_session_slots, "Maximum number of outstanding NFSv4.1 "
-               "requests the client will negotiate");
-
 /*
  * Local variables:
  *  c-basic-offset: 8
index f38300e9f171646aeb414c704e26bb5302f380fa..55148def5540f325d3a13bea73aa713960ffcd1e 100644 (file)
@@ -1606,10 +1606,15 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
                        return -ESERVERFAULT;
                /* Lease confirmation error: retry after purging the lease */
                ssleep(1);
-       case -NFS4ERR_CLID_INUSE:
        case -NFS4ERR_STALE_CLIENTID:
                clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
                break;
+       case -NFS4ERR_CLID_INUSE:
+               pr_err("NFS: Server %s reports our clientid is in use\n",
+                       clp->cl_hostname);
+               nfs_mark_client_ready(clp, -EPERM);
+               clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
+               return -EPERM;
        case -EACCES:
                if (clp->cl_machine_cred == NULL)
                        return -EACCES;
@@ -1642,7 +1647,7 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
        return 0;
 }
 
-static int nfs4_reclaim_lease(struct nfs_client *clp)
+static int nfs4_establish_lease(struct nfs_client *clp)
 {
        struct rpc_cred *cred;
        const struct nfs4_state_recovery_ops *ops =
@@ -1655,7 +1660,41 @@ static int nfs4_reclaim_lease(struct nfs_client *clp)
        status = ops->establish_clid(clp, cred);
        put_rpccred(cred);
        if (status != 0)
+               return status;
+       pnfs_destroy_all_layouts(clp);
+       return 0;
+}
+
+/*
+ * Returns zero or a negative errno.  NFS4ERR values are converted
+ * to local errno values.
+ */
+static int nfs4_reclaim_lease(struct nfs_client *clp)
+{
+       int status;
+
+       status = nfs4_establish_lease(clp);
+       if (status < 0)
+               return nfs4_handle_reclaim_lease_error(clp, status);
+       if (test_and_clear_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state))
+               nfs4_state_start_reclaim_nograce(clp);
+       if (!test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state))
+               set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
+       clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
+       clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+       return 0;
+}
+
+static int nfs4_purge_lease(struct nfs_client *clp)
+{
+       int status;
+
+       status = nfs4_establish_lease(clp);
+       if (status < 0)
                return nfs4_handle_reclaim_lease_error(clp, status);
+       clear_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
+       set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+       nfs4_state_start_reclaim_nograce(clp);
        return 0;
 }
 
@@ -1764,6 +1803,8 @@ static int nfs4_reset_session(struct nfs_client *clp)
        struct rpc_cred *cred;
        int status;
 
+       if (!nfs4_has_session(clp))
+               return 0;
        nfs4_begin_drain_session(clp);
        cred = nfs4_get_exchange_id_cred(clp);
        status = nfs4_proc_destroy_session(clp->cl_session, cred);
@@ -1792,12 +1833,14 @@ out:
 
 static int nfs4_recall_slot(struct nfs_client *clp)
 {
-       struct nfs4_slot_table *fc_tbl = &clp->cl_session->fc_slot_table;
-       struct nfs4_channel_attrs *fc_attrs = &clp->cl_session->fc_attrs;
+       struct nfs4_slot_table *fc_tbl;
        struct nfs4_slot *new, *old;
        int i;
 
+       if (!nfs4_has_session(clp))
+               return 0;
        nfs4_begin_drain_session(clp);
+       fc_tbl = &clp->cl_session->fc_slot_table;
        new = kmalloc(fc_tbl->target_max_slots * sizeof(struct nfs4_slot),
                      GFP_NOFS);
         if (!new)
@@ -1810,11 +1853,10 @@ static int nfs4_recall_slot(struct nfs_client *clp)
        fc_tbl->slots = new;
        fc_tbl->max_slots = fc_tbl->target_max_slots;
        fc_tbl->target_max_slots = 0;
-       fc_attrs->max_reqs = fc_tbl->max_slots;
+       clp->cl_session->fc_attrs.max_reqs = fc_tbl->max_slots;
        spin_unlock(&fc_tbl->slot_tbl_lock);
 
        kfree(old);
-       nfs4_end_drain_session(clp);
        return 0;
 }
 
@@ -1823,6 +1865,8 @@ static int nfs4_bind_conn_to_session(struct nfs_client *clp)
        struct rpc_cred *cred;
        int ret;
 
+       if (!nfs4_has_session(clp))
+               return 0;
        nfs4_begin_drain_session(clp);
        cred = nfs4_get_exchange_id_cred(clp);
        ret = nfs4_proc_bind_conn_to_session(clp, cred);
@@ -1857,37 +1901,29 @@ static int nfs4_bind_conn_to_session(struct nfs_client *clp)
 static void nfs4_state_manager(struct nfs_client *clp)
 {
        int status = 0;
+       const char *section = "", *section_sep = "";
 
        /* Ensure exclusive access to NFSv4 state */
        do {
                if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) {
-                       status = nfs4_reclaim_lease(clp);
+                       section = "purge state";
+                       status = nfs4_purge_lease(clp);
                        if (status < 0)
                                goto out_error;
-                       clear_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
-                       set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+                       continue;
                }
 
-               if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) {
+               if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) {
+                       section = "lease expired";
                        /* We're going to have to re-establish a clientid */
                        status = nfs4_reclaim_lease(clp);
                        if (status < 0)
                                goto out_error;
-                       if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
-                               continue;
-                       clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
-
-                       if (test_and_clear_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH,
-                                              &clp->cl_state))
-                               nfs4_state_start_reclaim_nograce(clp);
-                       else
-                               set_bit(NFS4CLNT_RECLAIM_REBOOT,
-                                       &clp->cl_state);
-
-                       pnfs_destroy_all_layouts(clp);
+                       continue;
                }
 
                if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) {
+                       section = "check lease";
                        status = nfs4_check_lease(clp);
                        if (status < 0)
                                goto out_error;
@@ -1896,8 +1932,8 @@ static void nfs4_state_manager(struct nfs_client *clp)
                }
 
                /* Initialize or reset the session */
-               if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)
-                  && nfs4_has_session(clp)) {
+               if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) {
+                       section = "reset session";
                        status = nfs4_reset_session(clp);
                        if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
                                continue;
@@ -1907,15 +1943,26 @@ static void nfs4_state_manager(struct nfs_client *clp)
 
                /* Send BIND_CONN_TO_SESSION */
                if (test_and_clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION,
-                               &clp->cl_state) && nfs4_has_session(clp)) {
+                               &clp->cl_state)) {
+                       section = "bind conn to session";
                        status = nfs4_bind_conn_to_session(clp);
                        if (status < 0)
                                goto out_error;
                        continue;
                }
 
+               /* Recall session slots */
+               if (test_and_clear_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state)) {
+                       section = "recall slot";
+                       status = nfs4_recall_slot(clp);
+                       if (status < 0)
+                               goto out_error;
+                       continue;
+               }
+
                /* First recover reboot state... */
                if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
+                       section = "reclaim reboot";
                        status = nfs4_do_reclaim(clp,
                                clp->cl_mvops->reboot_recovery_ops);
                        if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) ||
@@ -1930,6 +1977,7 @@ static void nfs4_state_manager(struct nfs_client *clp)
 
                /* Now recover expired state... */
                if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
+                       section = "reclaim nograce";
                        status = nfs4_do_reclaim(clp,
                                clp->cl_mvops->nograce_recovery_ops);
                        if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) ||
@@ -1945,15 +1993,6 @@ static void nfs4_state_manager(struct nfs_client *clp)
                        nfs_client_return_marked_delegations(clp);
                        continue;
                }
-               /* Recall session slots */
-               if (test_and_clear_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state)
-                  && nfs4_has_session(clp)) {
-                       status = nfs4_recall_slot(clp);
-                       if (status < 0)
-                               goto out_error;
-                       continue;
-               }
-
 
                nfs4_clear_state_manager_bit(clp);
                /* Did we race with an attempt to give us more work? */
@@ -1964,8 +2003,11 @@ static void nfs4_state_manager(struct nfs_client *clp)
        } while (atomic_read(&clp->cl_count) > 1);
        return;
 out_error:
-       pr_warn_ratelimited("NFS: state manager failed on NFSv4 server %s"
-                       " with error %d\n", clp->cl_hostname, -status);
+       if (strlen(section))
+               section_sep = ": ";
+       pr_warn_ratelimited("NFS: state manager%s%s failed on NFSv4 server %s"
+                       " with error %d\n", section_sep, section,
+                       clp->cl_hostname, -status);
        nfs4_end_drain_session(clp);
        nfs4_clear_state_manager_bit(clp);
 }
diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c
new file mode 100644 (file)
index 0000000..12a31a9
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2012 Bryan Schumaker <bjschuma@netapp.com>
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/nfs_idmap.h>
+#include <linux/nfs4_mount.h>
+#include <linux/nfs_fs.h>
+#include "delegation.h"
+#include "internal.h"
+#include "nfs4_fs.h"
+#include "pnfs.h"
+#include "nfs.h"
+
+#define NFSDBG_FACILITY                NFSDBG_VFS
+
+static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc);
+static void nfs4_evict_inode(struct inode *inode);
+static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type,
+       int flags, const char *dev_name, void *raw_data);
+static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
+       int flags, const char *dev_name, void *raw_data);
+static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type,
+       int flags, const char *dev_name, void *raw_data);
+
+static struct file_system_type nfs4_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "nfs4",
+       .mount          = nfs_fs_mount,
+       .kill_sb        = nfs_kill_super,
+       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
+};
+
+static struct file_system_type nfs4_remote_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "nfs4",
+       .mount          = nfs4_remote_mount,
+       .kill_sb        = nfs_kill_super,
+       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
+};
+
+static struct file_system_type nfs4_remote_referral_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "nfs4",
+       .mount          = nfs4_remote_referral_mount,
+       .kill_sb        = nfs_kill_super,
+       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
+};
+
+struct file_system_type nfs4_referral_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "nfs4",
+       .mount          = nfs4_referral_mount,
+       .kill_sb        = nfs_kill_super,
+       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
+};
+
+static const struct super_operations nfs4_sops = {
+       .alloc_inode    = nfs_alloc_inode,
+       .destroy_inode  = nfs_destroy_inode,
+       .write_inode    = nfs4_write_inode,
+       .put_super      = nfs_put_super,
+       .statfs         = nfs_statfs,
+       .evict_inode    = nfs4_evict_inode,
+       .umount_begin   = nfs_umount_begin,
+       .show_options   = nfs_show_options,
+       .show_devname   = nfs_show_devname,
+       .show_path      = nfs_show_path,
+       .show_stats     = nfs_show_stats,
+       .remount_fs     = nfs_remount,
+};
+
+struct nfs_subversion nfs_v4 = {
+       .owner = THIS_MODULE,
+       .nfs_fs   = &nfs4_fs_type,
+       .rpc_vers = &nfs_version4,
+       .rpc_ops  = &nfs_v4_clientops,
+       .sops     = &nfs4_sops,
+       .xattr    = nfs4_xattr_handlers,
+};
+
+static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+       int ret = nfs_write_inode(inode, wbc);
+
+       if (ret >= 0 && test_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(inode)->flags)) {
+               int status;
+               bool sync = true;
+
+               if (wbc->sync_mode == WB_SYNC_NONE)
+                       sync = false;
+
+               status = pnfs_layoutcommit_inode(inode, sync);
+               if (status < 0)
+                       return status;
+       }
+       return ret;
+}
+
+/*
+ * Clean out any remaining NFSv4 state that might be left over due
+ * to open() calls that passed nfs_atomic_lookup, but failed to call
+ * nfs_open().
+ */
+static void nfs4_evict_inode(struct inode *inode)
+{
+       truncate_inode_pages(&inode->i_data, 0);
+       clear_inode(inode);
+       pnfs_return_layout(inode);
+       pnfs_destroy_layout(NFS_I(inode));
+       /* If we are holding a delegation, return it! */
+       nfs_inode_return_delegation_noreclaim(inode);
+       /* First call standard NFS clear_inode() code */
+       nfs_clear_inode(inode);
+}
+
+/*
+ * Get the superblock for the NFS4 root partition
+ */
+static struct dentry *
+nfs4_remote_mount(struct file_system_type *fs_type, int flags,
+                 const char *dev_name, void *info)
+{
+       struct nfs_mount_info *mount_info = info;
+       struct nfs_server *server;
+       struct dentry *mntroot = ERR_PTR(-ENOMEM);
+
+       mount_info->set_security = nfs_set_sb_security;
+
+       /* Get a volume representation */
+       server = nfs4_create_server(mount_info, &nfs_v4);
+       if (IS_ERR(server)) {
+               mntroot = ERR_CAST(server);
+               goto out;
+       }
+
+       mntroot = nfs_fs_mount_common(server, flags, dev_name, mount_info, &nfs_v4);
+
+out:
+       return mntroot;
+}
+
+static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
+               int flags, void *data, const char *hostname)
+{
+       struct vfsmount *root_mnt;
+       char *root_devname;
+       size_t len;
+
+       len = strlen(hostname) + 5;
+       root_devname = kmalloc(len, GFP_KERNEL);
+       if (root_devname == NULL)
+               return ERR_PTR(-ENOMEM);
+       /* Does hostname needs to be enclosed in brackets? */
+       if (strchr(hostname, ':'))
+               snprintf(root_devname, len, "[%s]:/", hostname);
+       else
+               snprintf(root_devname, len, "%s:/", hostname);
+       root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data);
+       kfree(root_devname);
+       return root_mnt;
+}
+
+struct nfs_referral_count {
+       struct list_head list;
+       const struct task_struct *task;
+       unsigned int referral_count;
+};
+
+static LIST_HEAD(nfs_referral_count_list);
+static DEFINE_SPINLOCK(nfs_referral_count_list_lock);
+
+static struct nfs_referral_count *nfs_find_referral_count(void)
+{
+       struct nfs_referral_count *p;
+
+       list_for_each_entry(p, &nfs_referral_count_list, list) {
+               if (p->task == current)
+                       return p;
+       }
+       return NULL;
+}
+
+#define NFS_MAX_NESTED_REFERRALS 2
+
+static int nfs_referral_loop_protect(void)
+{
+       struct nfs_referral_count *p, *new;
+       int ret = -ENOMEM;
+
+       new = kmalloc(sizeof(*new), GFP_KERNEL);
+       if (!new)
+               goto out;
+       new->task = current;
+       new->referral_count = 1;
+
+       ret = 0;
+       spin_lock(&nfs_referral_count_list_lock);
+       p = nfs_find_referral_count();
+       if (p != NULL) {
+               if (p->referral_count >= NFS_MAX_NESTED_REFERRALS)
+                       ret = -ELOOP;
+               else
+                       p->referral_count++;
+       } else {
+               list_add(&new->list, &nfs_referral_count_list);
+               new = NULL;
+       }
+       spin_unlock(&nfs_referral_count_list_lock);
+       kfree(new);
+out:
+       return ret;
+}
+
+static void nfs_referral_loop_unprotect(void)
+{
+       struct nfs_referral_count *p;
+
+       spin_lock(&nfs_referral_count_list_lock);
+       p = nfs_find_referral_count();
+       p->referral_count--;
+       if (p->referral_count == 0)
+               list_del(&p->list);
+       else
+               p = NULL;
+       spin_unlock(&nfs_referral_count_list_lock);
+       kfree(p);
+}
+
+static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt,
+               const char *export_path)
+{
+       struct dentry *dentry;
+       int err;
+
+       if (IS_ERR(root_mnt))
+               return ERR_CAST(root_mnt);
+
+       err = nfs_referral_loop_protect();
+       if (err) {
+               mntput(root_mnt);
+               return ERR_PTR(err);
+       }
+
+       dentry = mount_subtree(root_mnt, export_path);
+       nfs_referral_loop_unprotect();
+
+       return dentry;
+}
+
+struct dentry *nfs4_try_mount(int flags, const char *dev_name,
+                             struct nfs_mount_info *mount_info,
+                             struct nfs_subversion *nfs_mod)
+{
+       char *export_path;
+       struct vfsmount *root_mnt;
+       struct dentry *res;
+       struct nfs_parsed_mount_data *data = mount_info->parsed;
+
+       dfprintk(MOUNT, "--> nfs4_try_mount()\n");
+
+       export_path = data->nfs_server.export_path;
+       data->nfs_server.export_path = "/";
+       root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
+                       data->nfs_server.hostname);
+       data->nfs_server.export_path = export_path;
+
+       res = nfs_follow_remote_path(root_mnt, export_path);
+
+       dfprintk(MOUNT, "<-- nfs4_try_mount() = %ld%s\n",
+                       IS_ERR(res) ? PTR_ERR(res) : 0,
+                       IS_ERR(res) ? " [error]" : "");
+       return res;
+}
+
+static struct dentry *
+nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags,
+                          const char *dev_name, void *raw_data)
+{
+       struct nfs_mount_info mount_info = {
+               .fill_super = nfs_fill_super,
+               .set_security = nfs_clone_sb_security,
+               .cloned = raw_data,
+       };
+       struct nfs_server *server;
+       struct dentry *mntroot = ERR_PTR(-ENOMEM);
+
+       dprintk("--> nfs4_referral_get_sb()\n");
+
+       mount_info.mntfh = nfs_alloc_fhandle();
+       if (mount_info.cloned == NULL || mount_info.mntfh == NULL)
+               goto out;
+
+       /* create a new volume representation */
+       server = nfs4_create_referral_server(mount_info.cloned, mount_info.mntfh);
+       if (IS_ERR(server)) {
+               mntroot = ERR_CAST(server);
+               goto out;
+       }
+
+       mntroot = nfs_fs_mount_common(server, flags, dev_name, &mount_info, &nfs_v4);
+out:
+       nfs_free_fhandle(mount_info.mntfh);
+       return mntroot;
+}
+
+/*
+ * Create an NFS4 server record on referral traversal
+ */
+static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
+               int flags, const char *dev_name, void *raw_data)
+{
+       struct nfs_clone_mount *data = raw_data;
+       char *export_path;
+       struct vfsmount *root_mnt;
+       struct dentry *res;
+
+       dprintk("--> nfs4_referral_mount()\n");
+
+       export_path = data->mnt_path;
+       data->mnt_path = "/";
+
+       root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type,
+                       flags, data, data->hostname);
+       data->mnt_path = export_path;
+
+       res = nfs_follow_remote_path(root_mnt, export_path);
+       dprintk("<-- nfs4_referral_mount() = %ld%s\n",
+                       IS_ERR(res) ? PTR_ERR(res) : 0,
+                       IS_ERR(res) ? " [error]" : "");
+       return res;
+}
+
+
+static int __init init_nfs_v4(void)
+{
+       int err;
+
+       err = nfs_idmap_init();
+       if (err)
+               goto out;
+
+       err = nfs4_register_sysctl();
+       if (err)
+               goto out1;
+
+       err = register_filesystem(&nfs4_fs_type);
+       if (err < 0)
+               goto out2;
+
+       register_nfs_version(&nfs_v4);
+       return 0;
+out2:
+       nfs4_unregister_sysctl();
+out1:
+       nfs_idmap_quit();
+out:
+       return err;
+}
+
+static void __exit exit_nfs_v4(void)
+{
+       unregister_nfs_version(&nfs_v4);
+       unregister_filesystem(&nfs4_fs_type);
+       nfs4_unregister_sysctl();
+       nfs_idmap_quit();
+}
+
+MODULE_LICENSE("GPL");
+
+module_init(init_nfs_v4);
+module_exit(exit_nfs_v4);
diff --git a/fs/nfs/nfs4sysctl.c b/fs/nfs/nfs4sysctl.c
new file mode 100644 (file)
index 0000000..5729bc8
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * linux/fs/nfs/nfs4sysctl.c
+ *
+ * Sysctl interface to NFS v4 parameters
+ *
+ * Copyright (c) 2006 Trond Myklebust <Trond.Myklebust@netapp.com>
+ */
+#include <linux/sysctl.h>
+#include <linux/nfs_idmap.h>
+#include <linux/nfs_fs.h>
+
+#include "callback.h"
+
+static const int nfs_set_port_min = 0;
+static const int nfs_set_port_max = 65535;
+static struct ctl_table_header *nfs4_callback_sysctl_table;
+
+static ctl_table nfs4_cb_sysctls[] = {
+       {
+               .procname = "nfs_callback_tcpport",
+               .data = &nfs_callback_set_tcpport,
+               .maxlen = sizeof(int),
+               .mode = 0644,
+               .proc_handler = proc_dointvec_minmax,
+               .extra1 = (int *)&nfs_set_port_min,
+               .extra2 = (int *)&nfs_set_port_max,
+       },
+       {
+               .procname = "idmap_cache_timeout",
+               .data = &nfs_idmap_cache_timeout,
+               .maxlen = sizeof(int),
+               .mode = 0644,
+               .proc_handler = proc_dointvec_jiffies,
+       },
+       { }
+};
+
+static ctl_table nfs4_cb_sysctl_dir[] = {
+       {
+               .procname = "nfs",
+               .mode = 0555,
+               .child = nfs4_cb_sysctls,
+       },
+       { }
+};
+
+static ctl_table nfs4_cb_sysctl_root[] = {
+       {
+               .procname = "fs",
+               .mode = 0555,
+               .child = nfs4_cb_sysctl_dir,
+       },
+       { }
+};
+
+int nfs4_register_sysctl(void)
+{
+       nfs4_callback_sysctl_table = register_sysctl_table(nfs4_cb_sysctl_root);
+       if (nfs4_callback_sysctl_table == NULL)
+               return -ENOMEM;
+       return 0;
+}
+
+void nfs4_unregister_sysctl(void)
+{
+       unregister_sysctl_table(nfs4_callback_sysctl_table);
+       nfs4_callback_sysctl_table = NULL;
+}
index 18fae29b0301c38a09fcb30a1345375780393f5c..ca13483edd60aee67c8e1673b18250959187f8a4 100644 (file)
@@ -852,12 +852,6 @@ const u32 nfs41_maxread_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
                                    XDR_UNIT);
 #endif /* CONFIG_NFS_V4_1 */
 
-static unsigned short send_implementation_id = 1;
-
-module_param(send_implementation_id, ushort, 0644);
-MODULE_PARM_DESC(send_implementation_id,
-               "Send implementation ID with NFSv4.1 exchange_id");
-
 static const umode_t nfs_type2fmt[] = {
        [NF4BAD] = 0,
        [NF4REG] = S_IFREG,
@@ -1236,7 +1230,7 @@ static void encode_link(struct xdr_stream *xdr, const struct qstr *name, struct
 
 static inline int nfs4_lock_type(struct file_lock *fl, int block)
 {
-       if ((fl->fl_type & (F_RDLCK|F_WRLCK|F_UNLCK)) == F_RDLCK)
+       if (fl->fl_type == F_RDLCK)
                return block ? NFS4_READW_LT : NFS4_READ_LT;
        return block ? NFS4_WRITEW_LT : NFS4_WRITE_LT;
 }
@@ -3078,7 +3072,7 @@ out_overflow:
        return -EIO;
 }
 
-static inline int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen, __be32 **savep)
+static int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen, unsigned int *savep)
 {
        __be32 *p;
 
@@ -3086,7 +3080,7 @@ static inline int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen,
        if (unlikely(!p))
                goto out_overflow;
        *attrlen = be32_to_cpup(p);
-       *savep = xdr->p;
+       *savep = xdr_stream_pos(xdr);
        return 0;
 out_overflow:
        print_overflow_msg(__func__, xdr);
@@ -4068,10 +4062,10 @@ static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, str
        return status;
 }
 
-static int verify_attr_len(struct xdr_stream *xdr, __be32 *savep, uint32_t attrlen)
+static int verify_attr_len(struct xdr_stream *xdr, unsigned int savep, uint32_t attrlen)
 {
        unsigned int attrwords = XDR_QUADLEN(attrlen);
-       unsigned int nwords = xdr->p - savep;
+       unsigned int nwords = (xdr_stream_pos(xdr) - savep) >> 2;
 
        if (unlikely(attrwords != nwords)) {
                dprintk("%s: server returned incorrect attribute length: "
@@ -4158,13 +4152,18 @@ static int decode_verifier(struct xdr_stream *xdr, void *verifier)
        return decode_opaque_fixed(xdr, verifier, NFS4_VERIFIER_SIZE);
 }
 
+static int decode_write_verifier(struct xdr_stream *xdr, struct nfs_write_verifier *verifier)
+{
+       return decode_opaque_fixed(xdr, verifier->data, NFS4_VERIFIER_SIZE);
+}
+
 static int decode_commit(struct xdr_stream *xdr, struct nfs_commitres *res)
 {
        int status;
 
        status = decode_op_hdr(xdr, OP_COMMIT);
        if (!status)
-               status = decode_verifier(xdr, res->verf->verifier);
+               status = decode_write_verifier(xdr, &res->verf->verifier);
        return status;
 }
 
@@ -4193,7 +4192,7 @@ out_overflow:
 
 static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_res *res)
 {
-       __be32 *savep;
+       unsigned int savep;
        uint32_t attrlen, bitmap[3] = {0};
        int status;
 
@@ -4222,7 +4221,7 @@ xdr_error:
 
 static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat)
 {
-       __be32 *savep;
+       unsigned int savep;
        uint32_t attrlen, bitmap[3] = {0};
        int status;
 
@@ -4254,7 +4253,7 @@ xdr_error:
 
 static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf)
 {
-       __be32 *savep;
+       unsigned int savep;
        uint32_t attrlen, bitmap[3] = {0};
        int status;
 
@@ -4299,7 +4298,8 @@ out_overflow:
 static int decode_first_threshold_item4(struct xdr_stream *xdr,
                                        struct nfs4_threshold *res)
 {
-       __be32 *p, *savep;
+       __be32 *p;
+       unsigned int savep;
        uint32_t bitmap[3] = {0,}, attrlen;
        int status;
 
@@ -4503,7 +4503,7 @@ static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fat
                struct nfs_fh *fh, struct nfs4_fs_locations *fs_loc,
                const struct nfs_server *server)
 {
-       __be32 *savep;
+       unsigned int savep;
        uint32_t attrlen,
                 bitmap[3] = {0};
        int status;
@@ -4615,7 +4615,7 @@ static int decode_attr_layout_blksize(struct xdr_stream *xdr, uint32_t *bitmap,
 
 static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo)
 {
-       __be32 *savep;
+       unsigned int savep;
        uint32_t attrlen, bitmap[3];
        int status;
 
@@ -4920,9 +4920,8 @@ static int decode_putrootfh(struct xdr_stream *xdr)
 
 static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_readres *res)
 {
-       struct kvec *iov = req->rq_rcv_buf.head;
        __be32 *p;
-       uint32_t count, eof, recvd, hdrlen;
+       uint32_t count, eof, recvd;
        int status;
 
        status = decode_op_hdr(xdr, OP_READ);
@@ -4933,15 +4932,13 @@ static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_
                goto out_overflow;
        eof = be32_to_cpup(p++);
        count = be32_to_cpup(p);
-       hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base;
-       recvd = req->rq_rcv_buf.len - hdrlen;
+       recvd = xdr_read_pages(xdr, count);
        if (count > recvd) {
                dprintk("NFS: server cheating in read reply: "
                                "count %u > recvd %u\n", count, recvd);
                count = recvd;
                eof = 0;
        }
-       xdr_read_pages(xdr, count);
        res->eof = eof;
        res->count = count;
        return 0;
@@ -4952,10 +4949,6 @@ out_overflow:
 
 static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir_res *readdir)
 {
-       struct xdr_buf  *rcvbuf = &req->rq_rcv_buf;
-       struct kvec     *iov = rcvbuf->head;
-       size_t          hdrlen;
-       u32             recvd, pglen = rcvbuf->page_len;
        int             status;
        __be32          verf[2];
 
@@ -4967,22 +4960,12 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
        memcpy(verf, readdir->verifier.data, sizeof(verf));
        dprintk("%s: verifier = %08x:%08x\n",
                        __func__, verf[0], verf[1]);
-
-       hdrlen = (char *) xdr->p - (char *) iov->iov_base;
-       recvd = rcvbuf->len - hdrlen;
-       if (pglen > recvd)
-               pglen = recvd;
-       xdr_read_pages(xdr, pglen);
-
-
-       return pglen;
+       return xdr_read_pages(xdr, xdr->buf->page_len);
 }
 
 static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req)
 {
        struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
-       struct kvec *iov = rcvbuf->head;
-       size_t hdrlen;
        u32 len, recvd;
        __be32 *p;
        int status;
@@ -5000,14 +4983,12 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req)
                dprintk("nfs: server returned giant symlink!\n");
                return -ENAMETOOLONG;
        }
-       hdrlen = (char *) xdr->p - (char *) iov->iov_base;
-       recvd = req->rq_rcv_buf.len - hdrlen;
+       recvd = xdr_read_pages(xdr, len);
        if (recvd < len) {
                dprintk("NFS: server cheating in readlink reply: "
                                "count %u > recvd %u\n", len, recvd);
                return -EIO;
        }
-       xdr_read_pages(xdr, len);
        /*
         * The XDR encode routine has set things up so that
         * the link text will be copied directly into the
@@ -5063,10 +5044,10 @@ decode_restorefh(struct xdr_stream *xdr)
 static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
                         struct nfs_getaclres *res)
 {
-       __be32 *savep, *bm_p;
+       unsigned int savep;
+       __be32 *bm_p;
        uint32_t attrlen,
                 bitmap[3] = {0};
-       struct kvec *iov = req->rq_rcv_buf.head;
        int status;
        size_t page_len = xdr->buf->page_len;
 
@@ -5089,7 +5070,6 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
        if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {
-               size_t hdrlen;
 
                /* The bitmap (xdr len + bitmaps) and the attr xdr len words
                 * are stored with the acl data to handle the problem of
@@ -5098,7 +5078,6 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
 
                /* We ignore &savep and don't do consistency checks on
                 * the attr length.  Let userspace figure it out.... */
-               hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base;
                attrlen += res->acl_data_offset;
                if (attrlen > page_len) {
                        if (res->acl_flags & NFS4_ACL_LEN_REQUEST) {
@@ -5212,13 +5191,12 @@ static int decode_write(struct xdr_stream *xdr, struct nfs_writeres *res)
        if (status)
                return status;
 
-       p = xdr_inline_decode(xdr, 16);
+       p = xdr_inline_decode(xdr, 8);
        if (unlikely(!p))
                goto out_overflow;
        res->count = be32_to_cpup(p++);
        res->verf->committed = be32_to_cpup(p++);
-       memcpy(res->verf->verifier, p, NFS4_VERIFIER_SIZE);
-       return 0;
+       return decode_write_verifier(xdr, &res->verf->verifier);
 out_overflow:
        print_overflow_msg(__func__, xdr);
        return -EIO;
@@ -5599,7 +5577,7 @@ static int decode_getdevicelist(struct xdr_stream *xdr,
 {
        __be32 *p;
        int status, i;
-       struct nfs_writeverf verftemp;
+       nfs4_verifier verftemp;
 
        status = decode_op_hdr(xdr, OP_GETDEVICELIST);
        if (status)
@@ -5613,7 +5591,7 @@ static int decode_getdevicelist(struct xdr_stream *xdr,
        p += 2;
 
        /* Read verifier */
-       p = xdr_decode_opaque_fixed(p, verftemp.verifier, NFS4_VERIFIER_SIZE);
+       p = xdr_decode_opaque_fixed(p, verftemp.data, NFS4_VERIFIER_SIZE);
 
        res->num_devs = be32_to_cpup(p);
 
@@ -5707,9 +5685,7 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
        __be32 *p;
        int status;
        u32 layout_count;
-       struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
-       struct kvec *iov = rcvbuf->head;
-       u32 hdrlen, recvd;
+       u32 recvd;
 
        status = decode_op_hdr(xdr, OP_LAYOUTGET);
        if (status)
@@ -5746,8 +5722,7 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
                res->type,
                res->layoutp->len);
 
-       hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base;
-       recvd = req->rq_rcv_buf.len - hdrlen;
+       recvd = xdr_read_pages(xdr, res->layoutp->len);
        if (res->layoutp->len > recvd) {
                dprintk("NFS: server cheating in layoutget reply: "
                                "layout len %u > recvd %u\n",
@@ -5755,8 +5730,6 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
                return -EINVAL;
        }
 
-       xdr_read_pages(xdr, res->layoutp->len);
-
        if (layout_count > 1) {
                /* We only handle a length one array at the moment.  Any
                 * further entries are just ignored.  Note that this means
@@ -7103,6 +7076,7 @@ out:
 int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
                       int plus)
 {
+       unsigned int savep;
        uint32_t bitmap[3] = {0};
        uint32_t len;
        __be32 *p = xdr_inline_decode(xdr, 4);
@@ -7141,7 +7115,7 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
        if (decode_attr_bitmap(xdr, bitmap) < 0)
                goto out_overflow;
 
-       if (decode_attr_length(xdr, &len, &p) < 0)
+       if (decode_attr_length(xdr, &len, &savep) < 0)
                goto out_overflow;
 
        if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh,
index aed913c833f422bbf6a88e2726be5eec6d9bbc40..1a6732ed04a447aa14147468e15f4b7956557376 100644 (file)
@@ -54,6 +54,7 @@ void nfs_pgheader_init(struct nfs_pageio_descriptor *desc,
        if (hdr->completion_ops->init_hdr)
                hdr->completion_ops->init_hdr(hdr);
 }
+EXPORT_SYMBOL_GPL(nfs_pgheader_init);
 
 void nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos)
 {
@@ -70,7 +71,7 @@ void nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos)
 static inline struct nfs_page *
 nfs_page_alloc(void)
 {
-       struct nfs_page *p = kmem_cache_zalloc(nfs_page_cachep, GFP_KERNEL);
+       struct nfs_page *p = kmem_cache_zalloc(nfs_page_cachep, GFP_NOIO);
        if (p)
                INIT_LIST_HEAD(&p->wb_list);
        return p;
@@ -117,7 +118,7 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
         * long write-back delay. This will be adjusted in
         * update_nfs_request below if the region is not locked. */
        req->wb_page    = page;
-       req->wb_index   = page->index;
+       req->wb_index   = page_file_index(page);
        page_cache_get(page);
        req->wb_offset  = offset;
        req->wb_pgbase  = offset;
@@ -268,6 +269,7 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
        desc->pg_lseg = NULL;
        desc->pg_dreq = NULL;
 }
+EXPORT_SYMBOL_GPL(nfs_pageio_init);
 
 /**
  * nfs_can_coalesce_requests - test two requests for compatibility
@@ -409,6 +411,7 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
        } while (ret);
        return ret;
 }
+EXPORT_SYMBOL_GPL(nfs_pageio_add_request);
 
 /**
  * nfs_pageio_complete - Complete I/O on an nfs_pageio_descriptor
@@ -424,6 +427,7 @@ void nfs_pageio_complete(struct nfs_pageio_descriptor *desc)
                        break;
        }
 }
+EXPORT_SYMBOL_GPL(nfs_pageio_complete);
 
 /**
  * nfs_pageio_cond_complete - Conditional I/O completion
index bbc49caa7a82810ac497e7475997262f4da14be4..76875bfcf19ceda4a54cb0dde14466a304191cf9 100644 (file)
@@ -651,7 +651,14 @@ out_err_free:
        return NULL;
 }
 
-/* Initiates a LAYOUTRETURN(FILE) */
+/*
+ * Initiates a LAYOUTRETURN(FILE), and removes the pnfs_layout_hdr
+ * when the layout segment list is empty.
+ *
+ * Note that a pnfs_layout_hdr can exist with an empty layout segment
+ * list when LAYOUTGET has failed, or when LAYOUTGET succeeded, but the
+ * deviceid is marked invalid.
+ */
 int
 _pnfs_return_layout(struct inode *ino)
 {
@@ -660,22 +667,31 @@ _pnfs_return_layout(struct inode *ino)
        LIST_HEAD(tmp_list);
        struct nfs4_layoutreturn *lrp;
        nfs4_stateid stateid;
-       int status = 0;
+       int status = 0, empty;
 
-       dprintk("--> %s\n", __func__);
+       dprintk("NFS: %s for inode %lu\n", __func__, ino->i_ino);
 
        spin_lock(&ino->i_lock);
        lo = nfsi->layout;
-       if (!lo) {
+       if (!lo || pnfs_test_layout_returned(lo)) {
                spin_unlock(&ino->i_lock);
-               dprintk("%s: no layout to return\n", __func__);
-               return status;
+               dprintk("NFS: %s no layout to return\n", __func__);
+               goto out;
        }
        stateid = nfsi->layout->plh_stateid;
        /* Reference matched in nfs4_layoutreturn_release */
        get_layout_hdr(lo);
+       empty = list_empty(&lo->plh_segs);
        mark_matching_lsegs_invalid(lo, &tmp_list, NULL);
+       /* Don't send a LAYOUTRETURN if list was initially empty */
+       if (empty) {
+               spin_unlock(&ino->i_lock);
+               put_layout_hdr(lo);
+               dprintk("NFS: %s no layout segments to return\n", __func__);
+               goto out;
+       }
        lo->plh_block_lgets++;
+       pnfs_mark_layout_returned(lo);
        spin_unlock(&ino->i_lock);
        pnfs_free_lseg_list(&tmp_list);
 
@@ -686,6 +702,7 @@ _pnfs_return_layout(struct inode *ino)
                status = -ENOMEM;
                set_bit(NFS_LAYOUT_RW_FAILED, &lo->plh_flags);
                set_bit(NFS_LAYOUT_RO_FAILED, &lo->plh_flags);
+               pnfs_clear_layout_returned(lo);
                put_layout_hdr(lo);
                goto out;
        }
@@ -1075,6 +1092,10 @@ pnfs_update_layout(struct inode *ino,
        get_layout_hdr(lo);
        if (list_empty(&lo->plh_segs))
                first = true;
+
+       /* Enable LAYOUTRETURNs */
+       pnfs_clear_layout_returned(lo);
+
        spin_unlock(&ino->i_lock);
        if (first) {
                /* The lo must be on the clp list if there is any
@@ -1209,7 +1230,7 @@ pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *
 }
 EXPORT_SYMBOL_GPL(pnfs_generic_pg_init_write);
 
-bool
+void
 pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode,
                      const struct nfs_pgio_completion_ops *compl_ops)
 {
@@ -1217,13 +1238,12 @@ pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode,
        struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld;
 
        if (ld == NULL)
-               return false;
-       nfs_pageio_init(pgio, inode, ld->pg_read_ops, compl_ops,
-                       server->rsize, 0);
-       return true;
+               nfs_pageio_init_read(pgio, inode, compl_ops);
+       else
+               nfs_pageio_init(pgio, inode, ld->pg_read_ops, compl_ops, server->rsize, 0);
 }
 
-bool
+void
 pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode,
                       int ioflags,
                       const struct nfs_pgio_completion_ops *compl_ops)
@@ -1232,10 +1252,9 @@ pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode,
        struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld;
 
        if (ld == NULL)
-               return false;
-       nfs_pageio_init(pgio, inode, ld->pg_write_ops, compl_ops,
-                       server->wsize, ioflags);
-       return true;
+               nfs_pageio_init_write(pgio, inode, ioflags, compl_ops);
+       else
+               nfs_pageio_init(pgio, inode, ld->pg_write_ops, compl_ops, server->wsize, ioflags);
 }
 
 bool
@@ -1272,7 +1291,7 @@ int pnfs_write_done_resend_to_mds(struct inode *inode,
        LIST_HEAD(failed);
 
        /* Resend all requests through the MDS */
-       nfs_pageio_init_write_mds(&pgio, inode, FLUSH_STABLE, compl_ops);
+       nfs_pageio_init_write(&pgio, inode, FLUSH_STABLE, compl_ops);
        while (!list_empty(head)) {
                struct nfs_page *req = nfs_list_entry(head->next);
 
@@ -1388,6 +1407,7 @@ static void pnfs_writehdr_free(struct nfs_pgio_header *hdr)
        put_lseg(hdr->lseg);
        nfs_writehdr_free(hdr);
 }
+EXPORT_SYMBOL_GPL(pnfs_writehdr_free);
 
 int
 pnfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc)
@@ -1427,7 +1447,7 @@ int pnfs_read_done_resend_to_mds(struct inode *inode,
        LIST_HEAD(failed);
 
        /* Resend all requests through the MDS */
-       nfs_pageio_init_read_mds(&pgio, inode, compl_ops);
+       nfs_pageio_init_read(&pgio, inode, compl_ops);
        while (!list_empty(head)) {
                struct nfs_page *req = nfs_list_entry(head->next);
 
@@ -1542,6 +1562,7 @@ static void pnfs_readhdr_free(struct nfs_pgio_header *hdr)
        put_lseg(hdr->lseg);
        nfs_readhdr_free(hdr);
 }
+EXPORT_SYMBOL_GPL(pnfs_readhdr_free);
 
 int
 pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc)
index 64f90d845f6a95cd8752e5b4e1c9b3895f1a1e66..2c6c80503ba4851ed1e69c253293684c540b2c32 100644 (file)
@@ -64,6 +64,7 @@ enum {
        NFS_LAYOUT_ROC,                 /* some lseg had roc bit set */
        NFS_LAYOUT_DESTROYED,           /* no new use of layout allowed */
        NFS_LAYOUT_INVALID,             /* layout is being destroyed */
+       NFS_LAYOUT_RETURNED,            /* layout has already been returned */
 };
 
 enum layoutdriver_policy_flags {
@@ -178,9 +179,9 @@ extern int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp);
 void get_layout_hdr(struct pnfs_layout_hdr *lo);
 void put_lseg(struct pnfs_layout_segment *lseg);
 
-bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *, struct inode *,
+void pnfs_pageio_init_read(struct nfs_pageio_descriptor *, struct inode *,
                           const struct nfs_pgio_completion_ops *);
-bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *,
+void pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *,
                            int, const struct nfs_pgio_completion_ops *);
 
 void set_pnfs_layoutdriver(struct nfs_server *, const struct nfs_fh *, u32);
@@ -255,6 +256,24 @@ struct nfs4_deviceid_node *nfs4_insert_deviceid_node(struct nfs4_deviceid_node *
 bool nfs4_put_deviceid_node(struct nfs4_deviceid_node *);
 void nfs4_deviceid_purge_client(const struct nfs_client *);
 
+static inline void
+pnfs_mark_layout_returned(struct pnfs_layout_hdr *lo)
+{
+       set_bit(NFS_LAYOUT_RETURNED, &lo->plh_flags);
+}
+
+static inline void
+pnfs_clear_layout_returned(struct pnfs_layout_hdr *lo)
+{
+       clear_bit(NFS_LAYOUT_RETURNED, &lo->plh_flags);
+}
+
+static inline bool
+pnfs_test_layout_returned(struct pnfs_layout_hdr *lo)
+{
+       return test_bit(NFS_LAYOUT_RETURNED, &lo->plh_flags);
+}
+
 static inline int lo_fail_bit(u32 iomode)
 {
        return iomode == IOMODE_RW ?
@@ -438,16 +457,16 @@ static inline void unset_pnfs_layoutdriver(struct nfs_server *s)
 {
 }
 
-static inline bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode,
+static inline void pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode,
                                         const struct nfs_pgio_completion_ops *compl_ops)
 {
-       return false;
+       nfs_pageio_init_read(pgio, inode, compl_ops);
 }
 
-static inline bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags,
+static inline void pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags,
                                          const struct nfs_pgio_completion_ops *compl_ops)
 {
-       return false;
+       nfs_pageio_init_write(pgio, inode, ioflags, compl_ops);
 }
 
 static inline int
index 4433806e116f9a4b26f605c4ef986a1633eb3efd..50a88c3546ed9eebdee4c4755ca5b7f6d35489f0 100644 (file)
@@ -734,6 +734,38 @@ out_einval:
        return -EINVAL;
 }
 
+static int nfs_have_delegation(struct inode *inode, fmode_t flags)
+{
+       return 0;
+}
+
+static int nfs_return_delegation(struct inode *inode)
+{
+       nfs_wb_all(inode);
+       return 0;
+}
+
+static const struct inode_operations nfs_dir_inode_operations = {
+       .create         = nfs_create,
+       .lookup         = nfs_lookup,
+       .link           = nfs_link,
+       .unlink         = nfs_unlink,
+       .symlink        = nfs_symlink,
+       .mkdir          = nfs_mkdir,
+       .rmdir          = nfs_rmdir,
+       .mknod          = nfs_mknod,
+       .rename         = nfs_rename,
+       .permission     = nfs_permission,
+       .getattr        = nfs_getattr,
+       .setattr        = nfs_setattr,
+};
+
+static const struct inode_operations nfs_file_inode_operations = {
+       .permission     = nfs_permission,
+       .getattr        = nfs_getattr,
+       .setattr        = nfs_setattr,
+};
+
 const struct nfs_rpc_ops nfs_v2_clientops = {
        .version        = 2,                   /* protocol version */
        .dentry_ops     = &nfs_dentry_operations,
@@ -742,6 +774,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
        .file_ops       = &nfs_file_operations,
        .getroot        = nfs_proc_get_root,
        .submount       = nfs_submount,
+       .try_mount      = nfs_try_mount,
        .getattr        = nfs_proc_getattr,
        .setattr        = nfs_proc_setattr,
        .lookup         = nfs_proc_lookup,
@@ -767,9 +800,11 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
        .pathconf       = nfs_proc_pathconf,
        .decode_dirent  = nfs2_decode_dirent,
        .read_setup     = nfs_proc_read_setup,
+       .read_pageio_init = nfs_pageio_init_read,
        .read_rpc_prepare = nfs_proc_read_rpc_prepare,
        .read_done      = nfs_read_done,
        .write_setup    = nfs_proc_write_setup,
+       .write_pageio_init = nfs_pageio_init_write,
        .write_rpc_prepare = nfs_proc_write_rpc_prepare,
        .write_done     = nfs_write_done,
        .commit_setup   = nfs_proc_commit_setup,
@@ -777,5 +812,11 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
        .lock           = nfs_proc_lock,
        .lock_check_bounds = nfs_lock_check_bounds,
        .close_context  = nfs_close_context,
+       .have_delegation = nfs_have_delegation,
+       .return_delegation = nfs_return_delegation,
+       .alloc_client   = nfs_alloc_client,
        .init_client    = nfs_init_client,
+       .free_client    = nfs_free_client,
+       .create_server  = nfs_create_server,
+       .clone_server   = nfs_clone_server,
 };
index 86ced78362142119328ad827138c0c716668aeac..b6bdb18e892c8ea5bf04869e296b1cdafca4d4a7 100644 (file)
@@ -20,8 +20,6 @@
 #include <linux/nfs_page.h>
 #include <linux/module.h>
 
-#include "pnfs.h"
-
 #include "nfs4_fs.h"
 #include "internal.h"
 #include "iostat.h"
@@ -50,6 +48,7 @@ struct nfs_read_header *nfs_readhdr_alloc(void)
        }
        return rhdr;
 }
+EXPORT_SYMBOL_GPL(nfs_readhdr_alloc);
 
 static struct nfs_read_data *nfs_readdata_alloc(struct nfs_pgio_header *hdr,
                                                unsigned int pagecount)
@@ -82,6 +81,7 @@ void nfs_readhdr_free(struct nfs_pgio_header *hdr)
 
        kmem_cache_free(nfs_rdata_cachep, rhdr);
 }
+EXPORT_SYMBOL_GPL(nfs_readhdr_free);
 
 void nfs_readdata_release(struct nfs_read_data *rdata)
 {
@@ -98,6 +98,7 @@ void nfs_readdata_release(struct nfs_read_data *rdata)
        if (atomic_dec_and_test(&hdr->refcnt))
                hdr->completion_ops->completion(hdr);
 }
+EXPORT_SYMBOL_GPL(nfs_readdata_release);
 
 static
 int nfs_return_empty_page(struct page *page)
@@ -108,13 +109,14 @@ int nfs_return_empty_page(struct page *page)
        return 0;
 }
 
-void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio,
+void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio,
                              struct inode *inode,
                              const struct nfs_pgio_completion_ops *compl_ops)
 {
        nfs_pageio_init(pgio, inode, &nfs_pageio_read_ops, compl_ops,
                        NFS_SERVER(inode)->rsize, 0);
 }
+EXPORT_SYMBOL_GPL(nfs_pageio_init_read);
 
 void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio)
 {
@@ -123,14 +125,6 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio)
 }
 EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds);
 
-void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio,
-                         struct inode *inode,
-                         const struct nfs_pgio_completion_ops *compl_ops)
-{
-       if (!pnfs_pageio_init_read(pgio, inode, compl_ops))
-               nfs_pageio_init_read_mds(pgio, inode, compl_ops);
-}
-
 int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
                       struct page *page)
 {
@@ -149,7 +143,7 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
        if (len < PAGE_CACHE_SIZE)
                zero_user_segment(page, len, PAGE_CACHE_SIZE);
 
-       nfs_pageio_init_read(&pgio, inode, &nfs_async_read_completion_ops);
+       NFS_PROTO(inode)->read_pageio_init(&pgio, inode, &nfs_async_read_completion_ops);
        nfs_pageio_add_request(&pgio, new);
        nfs_pageio_complete(&pgio);
        NFS_I(inode)->read_io += pgio.pg_bytes_written;
@@ -407,6 +401,7 @@ int nfs_generic_pagein(struct nfs_pageio_descriptor *desc,
                return nfs_pagein_multi(desc, hdr);
        return nfs_pagein_one(desc, hdr);
 }
+EXPORT_SYMBOL_GPL(nfs_generic_pagein);
 
 static int nfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc)
 {
@@ -532,11 +527,11 @@ static const struct rpc_call_ops nfs_read_common_ops = {
 int nfs_readpage(struct file *file, struct page *page)
 {
        struct nfs_open_context *ctx;
-       struct inode *inode = page->mapping->host;
+       struct inode *inode = page_file_mapping(page)->host;
        int             error;
 
        dprintk("NFS: nfs_readpage (%p %ld@%lu)\n",
-               page, PAGE_CACHE_SIZE, page->index);
+               page, PAGE_CACHE_SIZE, page_file_index(page));
        nfs_inc_stats(inode, NFSIOS_VFSREADPAGE);
        nfs_add_stats(inode, NFSIOS_READPAGES, 1);
 
@@ -590,7 +585,7 @@ static int
 readpage_async_filler(void *data, struct page *page)
 {
        struct nfs_readdesc *desc = (struct nfs_readdesc *)data;
-       struct inode *inode = page->mapping->host;
+       struct inode *inode = page_file_mapping(page)->host;
        struct nfs_page *new;
        unsigned int len;
        int error;
@@ -652,7 +647,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
        if (ret == 0)
                goto read_complete; /* all pages were read */
 
-       nfs_pageio_init_read(&pgio, inode, &nfs_async_read_completion_ops);
+       NFS_PROTO(inode)->read_pageio_init(&pgio, inode, &nfs_async_read_completion_ops);
 
        ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc);
 
index 8b2a2977b720729f2a63a4d7bb8e5e4b7de5522e..ac6a3c55dce408afbd5348a2f9af42a7f78e660e 100644 (file)
 #include "internal.h"
 #include "fscache.h"
 #include "pnfs.h"
+#include "nfs.h"
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
 #define NFS_TEXT_DATA          1
 
-#ifdef CONFIG_NFS_V3
+#if IS_ENABLED(CONFIG_NFS_V3)
 #define NFS_DEFAULT_VERSION 3
 #else
 #define NFS_DEFAULT_VERSION 2
@@ -278,37 +279,17 @@ static match_table_t nfs_vers_tokens = {
        { Opt_vers_err, NULL }
 };
 
-struct nfs_mount_info {
-       void (*fill_super)(struct super_block *, struct nfs_mount_info *);
-       int (*set_security)(struct super_block *, struct dentry *, struct nfs_mount_info *);
-       struct nfs_parsed_mount_data *parsed;
-       struct nfs_clone_mount *cloned;
-       struct nfs_fh *mntfh;
-};
-
-static void nfs_umount_begin(struct super_block *);
-static int  nfs_statfs(struct dentry *, struct kstatfs *);
-static int  nfs_show_options(struct seq_file *, struct dentry *);
-static int  nfs_show_devname(struct seq_file *, struct dentry *);
-static int  nfs_show_path(struct seq_file *, struct dentry *);
-static int  nfs_show_stats(struct seq_file *, struct dentry *);
-static struct dentry *nfs_fs_mount_common(struct file_system_type *,
-               struct nfs_server *, int, const char *, struct nfs_mount_info *);
-static struct dentry *nfs_fs_mount(struct file_system_type *,
-               int, const char *, void *);
 static struct dentry *nfs_xdev_mount(struct file_system_type *fs_type,
                int flags, const char *dev_name, void *raw_data);
-static void nfs_put_super(struct super_block *);
-static void nfs_kill_super(struct super_block *);
-static int nfs_remount(struct super_block *sb, int *flags, char *raw_data);
 
-static struct file_system_type nfs_fs_type = {
+struct file_system_type nfs_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "nfs",
        .mount          = nfs_fs_mount,
        .kill_sb        = nfs_kill_super,
        .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
 };
+EXPORT_SYMBOL_GPL(nfs_fs_type);
 
 struct file_system_type nfs_xdev_fs_type = {
        .owner          = THIS_MODULE,
@@ -318,7 +299,7 @@ struct file_system_type nfs_xdev_fs_type = {
        .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
 };
 
-static const struct super_operations nfs_sops = {
+const struct super_operations nfs_sops = {
        .alloc_inode    = nfs_alloc_inode,
        .destroy_inode  = nfs_destroy_inode,
        .write_inode    = nfs_write_inode,
@@ -332,77 +313,12 @@ static const struct super_operations nfs_sops = {
        .show_stats     = nfs_show_stats,
        .remount_fs     = nfs_remount,
 };
+EXPORT_SYMBOL_GPL(nfs_sops);
 
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *);
 static int nfs4_validate_mount_data(void *options,
        struct nfs_parsed_mount_data *args, const char *dev_name);
-static struct dentry *nfs4_try_mount(int flags, const char *dev_name,
-       struct nfs_mount_info *mount_info);
-static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *raw_data);
-static struct dentry *nfs4_xdev_mount(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *raw_data);
-static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *raw_data);
-static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *raw_data);
-static void nfs4_kill_super(struct super_block *sb);
-
-static struct file_system_type nfs4_fs_type = {
-       .owner          = THIS_MODULE,
-       .name           = "nfs4",
-       .mount          = nfs_fs_mount,
-       .kill_sb        = nfs4_kill_super,
-       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
-};
-
-static struct file_system_type nfs4_remote_fs_type = {
-       .owner          = THIS_MODULE,
-       .name           = "nfs4",
-       .mount          = nfs4_remote_mount,
-       .kill_sb        = nfs4_kill_super,
-       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
-};
-
-struct file_system_type nfs4_xdev_fs_type = {
-       .owner          = THIS_MODULE,
-       .name           = "nfs4",
-       .mount          = nfs4_xdev_mount,
-       .kill_sb        = nfs4_kill_super,
-       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
-};
-
-static struct file_system_type nfs4_remote_referral_fs_type = {
-       .owner          = THIS_MODULE,
-       .name           = "nfs4",
-       .mount          = nfs4_remote_referral_mount,
-       .kill_sb        = nfs4_kill_super,
-       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
-};
-
-struct file_system_type nfs4_referral_fs_type = {
-       .owner          = THIS_MODULE,
-       .name           = "nfs4",
-       .mount          = nfs4_referral_mount,
-       .kill_sb        = nfs4_kill_super,
-       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
-};
-
-static const struct super_operations nfs4_sops = {
-       .alloc_inode    = nfs_alloc_inode,
-       .destroy_inode  = nfs_destroy_inode,
-       .write_inode    = nfs_write_inode,
-       .put_super      = nfs_put_super,
-       .statfs         = nfs_statfs,
-       .evict_inode    = nfs4_evict_inode,
-       .umount_begin   = nfs_umount_begin,
-       .show_options   = nfs_show_options,
-       .show_devname   = nfs_show_devname,
-       .show_path      = nfs_show_path,
-       .show_stats     = nfs_show_stats,
-       .remount_fs     = nfs_remount,
-};
 #endif
 
 static struct shrinker acl_shrinker = {
@@ -424,18 +340,9 @@ int __init register_nfs_fs(void)
        ret = nfs_register_sysctl();
        if (ret < 0)
                goto error_1;
-#ifdef CONFIG_NFS_V4
-       ret = register_filesystem(&nfs4_fs_type);
-       if (ret < 0)
-               goto error_2;
-#endif
        register_shrinker(&acl_shrinker);
        return 0;
 
-#ifdef CONFIG_NFS_V4
-error_2:
-       nfs_unregister_sysctl();
-#endif
 error_1:
        unregister_filesystem(&nfs_fs_type);
 error_0:
@@ -448,9 +355,6 @@ error_0:
 void __exit unregister_nfs_fs(void)
 {
        unregister_shrinker(&acl_shrinker);
-#ifdef CONFIG_NFS_V4
-       unregister_filesystem(&nfs4_fs_type);
-#endif
        nfs_unregister_sysctl();
        unregister_filesystem(&nfs_fs_type);
 }
@@ -462,6 +366,7 @@ void nfs_sb_active(struct super_block *sb)
        if (atomic_inc_return(&server->active) == 1)
                atomic_inc(&sb->s_active);
 }
+EXPORT_SYMBOL_GPL(nfs_sb_active);
 
 void nfs_sb_deactive(struct super_block *sb)
 {
@@ -470,11 +375,12 @@ void nfs_sb_deactive(struct super_block *sb)
        if (atomic_dec_and_test(&server->active))
                deactivate_super(sb);
 }
+EXPORT_SYMBOL_GPL(nfs_sb_deactive);
 
 /*
  * Deliver file system statistics to userspace
  */
-static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
        struct nfs_server *server = NFS_SB(dentry->d_sb);
        unsigned char blockbits;
@@ -535,6 +441,7 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
        dprintk("%s: statfs error = %d\n", __func__, -error);
        return error;
 }
+EXPORT_SYMBOL_GPL(nfs_statfs);
 
 /*
  * Map the security flavour number to a name
@@ -640,7 +547,7 @@ static void nfs_show_mountd_options(struct seq_file *m, struct nfs_server *nfss,
        nfs_show_mountd_netid(m, nfss, showdefaults);
 }
 
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 static void nfs_show_nfsv4_options(struct seq_file *m, struct nfs_server *nfss,
                                    int showdefaults)
 {
@@ -757,7 +664,7 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
 /*
  * Describe the mount options on this VFS mountpoint
  */
-static int nfs_show_options(struct seq_file *m, struct dentry *root)
+int nfs_show_options(struct seq_file *m, struct dentry *root)
 {
        struct nfs_server *nfss = NFS_SB(root->d_sb);
 
@@ -771,8 +678,9 @@ static int nfs_show_options(struct seq_file *m, struct dentry *root)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(nfs_show_options);
 
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 #ifdef CONFIG_NFS_V4_1
 static void show_sessions(struct seq_file *m, struct nfs_server *server)
 {
@@ -805,7 +713,7 @@ static void show_implementation_id(struct seq_file *m, struct nfs_server *nfss)
        }
 }
 #else
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 static void show_pnfs(struct seq_file *m, struct nfs_server *server)
 {
 }
@@ -815,7 +723,7 @@ static void show_implementation_id(struct seq_file *m, struct nfs_server *nfss)
 }
 #endif
 
-static int nfs_show_devname(struct seq_file *m, struct dentry *root)
+int nfs_show_devname(struct seq_file *m, struct dentry *root)
 {
        char *page = (char *) __get_free_page(GFP_KERNEL);
        char *devname, *dummy;
@@ -830,17 +738,19 @@ static int nfs_show_devname(struct seq_file *m, struct dentry *root)
        free_page((unsigned long)page);
        return err;
 }
+EXPORT_SYMBOL_GPL(nfs_show_devname);
 
-static int nfs_show_path(struct seq_file *m, struct dentry *dentry)
+int nfs_show_path(struct seq_file *m, struct dentry *dentry)
 {
        seq_puts(m, "/");
        return 0;
 }
+EXPORT_SYMBOL_GPL(nfs_show_path);
 
 /*
  * Present statistical information for this VFS mountpoint
  */
-static int nfs_show_stats(struct seq_file *m, struct dentry *root)
+int nfs_show_stats(struct seq_file *m, struct dentry *root)
 {
        int i, cpu;
        struct nfs_server *nfss = NFS_SB(root->d_sb);
@@ -870,7 +780,7 @@ static int nfs_show_stats(struct seq_file *m, struct dentry *root)
        seq_printf(m, ",bsize=%u", nfss->bsize);
        seq_printf(m, ",namlen=%u", nfss->namelen);
 
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
        if (nfss->nfs_client->rpc_ops->version == 4) {
                seq_printf(m, "\n\tnfsv4:\t");
                seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]);
@@ -928,12 +838,13 @@ static int nfs_show_stats(struct seq_file *m, struct dentry *root)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(nfs_show_stats);
 
 /*
  * Begin unmount by attempting to remove all automounted mountpoints we added
  * in response to xdev traversals and referrals
  */
-static void nfs_umount_begin(struct super_block *sb)
+void nfs_umount_begin(struct super_block *sb)
 {
        struct nfs_server *server;
        struct rpc_clnt *rpc;
@@ -947,6 +858,7 @@ static void nfs_umount_begin(struct super_block *sb)
        if (!IS_ERR(rpc))
                rpc_killall_tasks(rpc);
 }
+EXPORT_SYMBOL_GPL(nfs_umount_begin);
 
 static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void)
 {
@@ -1748,8 +1660,9 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args,
        return nfs_walk_authlist(args, &request);
 }
 
-static struct dentry *nfs_try_mount(int flags, const char *dev_name,
-                                   struct nfs_mount_info *mount_info)
+struct dentry *nfs_try_mount(int flags, const char *dev_name,
+                            struct nfs_mount_info *mount_info,
+                            struct nfs_subversion *nfs_mod)
 {
        int status;
        struct nfs_server *server;
@@ -1761,12 +1674,13 @@ static struct dentry *nfs_try_mount(int flags, const char *dev_name,
        }
 
        /* Get a volume representation */
-       server = nfs_create_server(mount_info->parsed, mount_info->mntfh);
+       server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
        if (IS_ERR(server))
                return ERR_CAST(server);
 
-       return nfs_fs_mount_common(&nfs_fs_type, server, flags, dev_name, mount_info);
+       return nfs_fs_mount_common(server, flags, dev_name, mount_info, nfs_mod);
 }
+EXPORT_SYMBOL_GPL(nfs_try_mount);
 
 /*
  * Split "dev_name" into "hostname:export_path".
@@ -1970,7 +1884,7 @@ static int nfs23_validate_mount_data(void *options,
                return NFS_TEXT_DATA;
        }
 
-#ifndef CONFIG_NFS_V3
+#if !IS_ENABLED(CONFIG_NFS_V3)
        if (args->version == 3)
                goto out_v3_not_compiled;
 #endif /* !CONFIG_NFS_V3 */
@@ -1990,7 +1904,7 @@ out_no_sec:
        dfprintk(MOUNT, "NFS: nfs_mount_data version supports only AUTH_SYS\n");
        return -EINVAL;
 
-#ifndef CONFIG_NFS_V3
+#if !IS_ENABLED(CONFIG_NFS_V3)
 out_v3_not_compiled:
        dfprintk(MOUNT, "NFS: NFSv3 is not compiled into kernel\n");
        return -EPROTONOSUPPORT;
@@ -2009,7 +1923,7 @@ out_invalid_fh:
        return -EINVAL;
 }
 
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 static int nfs_validate_mount_data(struct file_system_type *fs_type,
                                   void *options,
                                   struct nfs_parsed_mount_data *args,
@@ -2047,7 +1961,7 @@ static int nfs_validate_text_mount_data(void *options,
                goto out_no_address;
 
        if (args->version == 4) {
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
                port = NFS_PORT;
                max_namelen = NFS4_MAXNAMLEN;
                max_pathlen = NFS4_MAXPATHLEN;
@@ -2070,7 +1984,7 @@ static int nfs_validate_text_mount_data(void *options,
                                   &args->nfs_server.export_path,
                                   max_pathlen);
 
-#ifndef CONFIG_NFS_V4
+#if !IS_ENABLED(CONFIG_NFS_V4)
 out_v4_not_compiled:
        dfprintk(MOUNT, "NFS: NFSv4 is not compiled into kernel\n");
        return -EPROTONOSUPPORT;
@@ -2108,7 +2022,7 @@ nfs_compare_remount_data(struct nfs_server *nfss,
        return 0;
 }
 
-static int
+int
 nfs_remount(struct super_block *sb, int *flags, char *raw_data)
 {
        int error;
@@ -2169,11 +2083,12 @@ out:
        kfree(data);
        return error;
 }
+EXPORT_SYMBOL_GPL(nfs_remount);
 
 /*
  * Initialise the common bits of the superblock
  */
-static inline void nfs_initialise_sb(struct super_block *sb)
+inline void nfs_initialise_sb(struct super_block *sb)
 {
        struct nfs_server *server = NFS_SB(sb);
 
@@ -2195,18 +2110,19 @@ static inline void nfs_initialise_sb(struct super_block *sb)
 /*
  * Finish setting up an NFS2/3 superblock
  */
-static void nfs_fill_super(struct super_block *sb,
-                          struct nfs_mount_info *mount_info)
+void nfs_fill_super(struct super_block *sb, struct nfs_mount_info *mount_info)
 {
        struct nfs_parsed_mount_data *data = mount_info->parsed;
        struct nfs_server *server = NFS_SB(sb);
 
        sb->s_blocksize_bits = 0;
        sb->s_blocksize = 0;
-       if (data->bsize)
+       sb->s_xattr = server->nfs_client->cl_nfs_mod->xattr;
+       sb->s_op = server->nfs_client->cl_nfs_mod->sops;
+       if (data && data->bsize)
                sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
 
-       if (server->nfs_client->rpc_ops->version == 3) {
+       if (server->nfs_client->rpc_ops->version != 2) {
                /* The VFS shouldn't apply the umask to mode bits. We will do
                 * so ourselves when necessary.
                 */
@@ -2214,15 +2130,14 @@ static void nfs_fill_super(struct super_block *sb,
                sb->s_time_gran = 1;
        }
 
-       sb->s_op = &nfs_sops;
        nfs_initialise_sb(sb);
 }
+EXPORT_SYMBOL_GPL(nfs_fill_super);
 
 /*
- * Finish setting up a cloned NFS2/3 superblock
+ * Finish setting up a cloned NFS2/3/4 superblock
  */
-static void nfs_clone_super(struct super_block *sb,
-                           struct nfs_mount_info *mount_info)
+void nfs_clone_super(struct super_block *sb, struct nfs_mount_info *mount_info)
 {
        const struct super_block *old_sb = mount_info->cloned->sb;
        struct nfs_server *server = NFS_SB(sb);
@@ -2230,16 +2145,17 @@ static void nfs_clone_super(struct super_block *sb,
        sb->s_blocksize_bits = old_sb->s_blocksize_bits;
        sb->s_blocksize = old_sb->s_blocksize;
        sb->s_maxbytes = old_sb->s_maxbytes;
+       sb->s_xattr = old_sb->s_xattr;
+       sb->s_op = old_sb->s_op;
+       sb->s_time_gran = 1;
 
-       if (server->nfs_client->rpc_ops->version == 3) {
+       if (server->nfs_client->rpc_ops->version != 2) {
                /* The VFS shouldn't apply the umask to mode bits. We will do
                 * so ourselves when necessary.
                 */
                sb->s_flags |= MS_POSIXACL;
-               sb->s_time_gran = 1;
        }
 
-       sb->s_op = old_sb->s_op;
        nfs_initialise_sb(sb);
 }
 
@@ -2381,14 +2297,15 @@ static int nfs_bdi_register(struct nfs_server *server)
        return bdi_register_dev(&server->backing_dev_info, server->s_dev);
 }
 
-static int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot,
-                              struct nfs_mount_info *mount_info)
+int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot,
+                       struct nfs_mount_info *mount_info)
 {
        return security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts);
 }
+EXPORT_SYMBOL_GPL(nfs_set_sb_security);
 
-static int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot,
-                                struct nfs_mount_info *mount_info)
+int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot,
+                         struct nfs_mount_info *mount_info)
 {
        /* clone any lsm security options from the parent to the new sb */
        security_sb_clone_mnt_opts(mount_info->cloned->sb, s);
@@ -2396,11 +2313,12 @@ static int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot,
                return -ESTALE;
        return 0;
 }
+EXPORT_SYMBOL_GPL(nfs_clone_sb_security);
 
-static struct dentry *nfs_fs_mount_common(struct file_system_type *fs_type,
-                                         struct nfs_server *server,
-                                         int flags, const char *dev_name,
-                                         struct nfs_mount_info *mount_info)
+struct dentry *nfs_fs_mount_common(struct nfs_server *server,
+                                  int flags, const char *dev_name,
+                                  struct nfs_mount_info *mount_info,
+                                  struct nfs_subversion *nfs_mod)
 {
        struct super_block *s;
        struct dentry *mntroot = ERR_PTR(-ENOMEM);
@@ -2419,7 +2337,7 @@ static struct dentry *nfs_fs_mount_common(struct file_system_type *fs_type,
                sb_mntdata.mntflags |= MS_SYNCHRONOUS;
 
        /* Get a superblock - note that we may end up sharing one that already exists */
-       s = sget(fs_type, compare_super, nfs_set_super, flags, &sb_mntdata);
+       s = sget(nfs_mod->nfs_fs, compare_super, nfs_set_super, flags, &sb_mntdata);
        if (IS_ERR(s)) {
                mntroot = ERR_CAST(s);
                goto out_err_nosb;
@@ -2469,8 +2387,9 @@ error_splat_bdi:
        deactivate_locked_super(s);
        goto out;
 }
+EXPORT_SYMBOL_GPL(nfs_fs_mount_common);
 
-static struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
+struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *raw_data)
 {
        struct nfs_mount_info mount_info = {
@@ -2478,6 +2397,7 @@ static struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
                .set_security = nfs_set_sb_security,
        };
        struct dentry *mntroot = ERR_PTR(-ENOMEM);
+       struct nfs_subversion *nfs_mod;
        int error;
 
        mount_info.parsed = nfs_alloc_parsed_mount_data();
@@ -2494,34 +2414,38 @@ static struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
                goto out;
        }
 
-#ifdef CONFIG_NFS_V4
-       if (mount_info.parsed->version == 4)
-               mntroot = nfs4_try_mount(flags, dev_name, &mount_info);
-       else
-#endif /* CONFIG_NFS_V4 */
-               mntroot = nfs_try_mount(flags, dev_name, &mount_info);
+       nfs_mod = get_nfs_version(mount_info.parsed->version);
+       if (IS_ERR(nfs_mod)) {
+               mntroot = ERR_CAST(nfs_mod);
+               goto out;
+       }
+
+       mntroot = nfs_mod->rpc_ops->try_mount(flags, dev_name, &mount_info, nfs_mod);
 
+       put_nfs_version(nfs_mod);
 out:
        nfs_free_parsed_mount_data(mount_info.parsed);
        nfs_free_fhandle(mount_info.mntfh);
        return mntroot;
 }
+EXPORT_SYMBOL_GPL(nfs_fs_mount);
 
 /*
  * Ensure that we unregister the bdi before kill_anon_super
  * releases the device name
  */
-static void nfs_put_super(struct super_block *s)
+void nfs_put_super(struct super_block *s)
 {
        struct nfs_server *server = NFS_SB(s);
 
        bdi_unregister(&server->backing_dev_info);
 }
+EXPORT_SYMBOL_GPL(nfs_put_super);
 
 /*
  * Destroy an NFS2/3 superblock
  */
-static void nfs_kill_super(struct super_block *s)
+void nfs_kill_super(struct super_block *s)
 {
        struct nfs_server *server = NFS_SB(s);
 
@@ -2529,31 +2453,38 @@ static void nfs_kill_super(struct super_block *s)
        nfs_fscache_release_super_cookie(s);
        nfs_free_server(server);
 }
+EXPORT_SYMBOL_GPL(nfs_kill_super);
 
 /*
  * Clone an NFS2/3/4 server record on xdev traversal (FSID-change)
  */
-static struct dentry *
-nfs_xdev_mount_common(struct file_system_type *fs_type, int flags,
-               const char *dev_name, struct nfs_mount_info *mount_info)
+struct dentry *
+nfs_xdev_mount(struct file_system_type *fs_type, int flags,
+               const char *dev_name, void *raw_data)
 {
-       struct nfs_clone_mount *data = mount_info->cloned;
+       struct nfs_clone_mount *data = raw_data;
+       struct nfs_mount_info mount_info = {
+               .fill_super = nfs_clone_super,
+               .set_security = nfs_clone_sb_security,
+               .cloned = data,
+       };
        struct nfs_server *server;
        struct dentry *mntroot = ERR_PTR(-ENOMEM);
+       struct nfs_subversion *nfs_mod = NFS_SB(data->sb)->nfs_client->cl_nfs_mod;
        int error;
 
        dprintk("--> nfs_xdev_mount_common()\n");
 
-       mount_info->mntfh = data->fh;
+       mount_info.mntfh = mount_info.cloned->fh;
 
        /* create a new volume representation */
-       server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr, data->authflavor);
+       server = nfs_mod->rpc_ops->clone_server(NFS_SB(data->sb), data->fh, data->fattr, data->authflavor);
        if (IS_ERR(server)) {
                error = PTR_ERR(server);
                goto out_err;
        }
 
-       mntroot = nfs_fs_mount_common(fs_type, server, flags, dev_name, mount_info);
+       mntroot = nfs_fs_mount_common(server, flags, dev_name, &mount_info, nfs_mod);
        dprintk("<-- nfs_xdev_mount_common() = 0\n");
 out:
        return mntroot;
@@ -2563,60 +2494,7 @@ out_err:
        goto out;
 }
 
-/*
- * Clone an NFS2/3 server record on xdev traversal (FSID-change)
- */
-static struct dentry *
-nfs_xdev_mount(struct file_system_type *fs_type, int flags,
-               const char *dev_name, void *raw_data)
-{
-       struct nfs_mount_info mount_info = {
-               .fill_super = nfs_clone_super,
-               .set_security = nfs_clone_sb_security,
-               .cloned   = raw_data,
-       };
-       return nfs_xdev_mount_common(&nfs_fs_type, flags, dev_name, &mount_info);
-}
-
-#ifdef CONFIG_NFS_V4
-
-/*
- * Finish setting up a cloned NFS4 superblock
- */
-static void nfs4_clone_super(struct super_block *sb,
-                            struct nfs_mount_info *mount_info)
-{
-       const struct super_block *old_sb = mount_info->cloned->sb;
-       sb->s_blocksize_bits = old_sb->s_blocksize_bits;
-       sb->s_blocksize = old_sb->s_blocksize;
-       sb->s_maxbytes = old_sb->s_maxbytes;
-       sb->s_time_gran = 1;
-       sb->s_op = old_sb->s_op;
-       /*
-        * The VFS shouldn't apply the umask to mode bits. We will do
-        * so ourselves when necessary.
-        */
-       sb->s_flags  |= MS_POSIXACL;
-       sb->s_xattr  = old_sb->s_xattr;
-       nfs_initialise_sb(sb);
-}
-
-/*
- * Set up an NFS4 superblock
- */
-static void nfs4_fill_super(struct super_block *sb,
-                           struct nfs_mount_info *mount_info)
-{
-       sb->s_time_gran = 1;
-       sb->s_op = &nfs4_sops;
-       /*
-        * The VFS shouldn't apply the umask to mode bits. We will do
-        * so ourselves when necessary.
-        */
-       sb->s_flags  |= MS_POSIXACL;
-       sb->s_xattr = nfs4_xattr_handlers;
-       nfs_initialise_sb(sb);
-}
+#if IS_ENABLED(CONFIG_NFS_V4)
 
 static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args)
 {
@@ -2716,249 +2594,55 @@ out_no_address:
 }
 
 /*
- * Get the superblock for the NFS4 root partition
+ * NFS v4 module parameters need to stay in the
+ * NFS client for backwards compatibility
  */
-static struct dentry *
-nfs4_remote_mount(struct file_system_type *fs_type, int flags,
-                 const char *dev_name, void *info)
-{
-       struct nfs_mount_info *mount_info = info;
-       struct nfs_server *server;
-       struct dentry *mntroot = ERR_PTR(-ENOMEM);
-
-       mount_info->fill_super = nfs4_fill_super;
-       mount_info->set_security = nfs_set_sb_security;
-
-       /* Get a volume representation */
-       server = nfs4_create_server(mount_info->parsed, mount_info->mntfh);
-       if (IS_ERR(server)) {
-               mntroot = ERR_CAST(server);
-               goto out;
-       }
-
-       mntroot = nfs_fs_mount_common(fs_type, server, flags, dev_name, mount_info);
-
-out:
-       return mntroot;
-}
-
-static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
-               int flags, void *data, const char *hostname)
-{
-       struct vfsmount *root_mnt;
-       char *root_devname;
-       size_t len;
+unsigned int nfs_callback_set_tcpport;
+unsigned short nfs_callback_tcpport;
+/* Default cache timeout is 10 minutes */
+unsigned int nfs_idmap_cache_timeout = 600;
+/* Turn off NFSv4 uid/gid mapping when using AUTH_SYS */
+bool nfs4_disable_idmapping = true;
+unsigned short max_session_slots = NFS4_DEF_SLOT_TABLE_SIZE;
+unsigned short send_implementation_id = 1;
+
+EXPORT_SYMBOL_GPL(nfs_callback_set_tcpport);
+EXPORT_SYMBOL_GPL(nfs_callback_tcpport);
+EXPORT_SYMBOL_GPL(nfs_idmap_cache_timeout);
+EXPORT_SYMBOL_GPL(nfs4_disable_idmapping);
+EXPORT_SYMBOL_GPL(max_session_slots);
+EXPORT_SYMBOL_GPL(send_implementation_id);
+
+#define NFS_CALLBACK_MAXPORTNR (65535U)
+
+static int param_set_portnr(const char *val, const struct kernel_param *kp)
+{
+       unsigned long num;
+       int ret;
 
-       len = strlen(hostname) + 5;
-       root_devname = kmalloc(len, GFP_KERNEL);
-       if (root_devname == NULL)
-               return ERR_PTR(-ENOMEM);
-       /* Does hostname needs to be enclosed in brackets? */
-       if (strchr(hostname, ':'))
-               snprintf(root_devname, len, "[%s]:/", hostname);
-       else
-               snprintf(root_devname, len, "%s:/", hostname);
-       root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data);
-       kfree(root_devname);
-       return root_mnt;
+       if (!val)
+               return -EINVAL;
+       ret = strict_strtoul(val, 0, &num);
+       if (ret == -EINVAL || num > NFS_CALLBACK_MAXPORTNR)
+               return -EINVAL;
+       *((unsigned int *)kp->arg) = num;
+       return 0;
 }
-
-struct nfs_referral_count {
-       struct list_head list;
-       const struct task_struct *task;
-       unsigned int referral_count;
+static struct kernel_param_ops param_ops_portnr = {
+       .set = param_set_portnr,
+       .get = param_get_uint,
 };
-
-static LIST_HEAD(nfs_referral_count_list);
-static DEFINE_SPINLOCK(nfs_referral_count_list_lock);
-
-static struct nfs_referral_count *nfs_find_referral_count(void)
-{
-       struct nfs_referral_count *p;
-
-       list_for_each_entry(p, &nfs_referral_count_list, list) {
-               if (p->task == current)
-                       return p;
-       }
-       return NULL;
-}
-
-#define NFS_MAX_NESTED_REFERRALS 2
-
-static int nfs_referral_loop_protect(void)
-{
-       struct nfs_referral_count *p, *new;
-       int ret = -ENOMEM;
-
-       new = kmalloc(sizeof(*new), GFP_KERNEL);
-       if (!new)
-               goto out;
-       new->task = current;
-       new->referral_count = 1;
-
-       ret = 0;
-       spin_lock(&nfs_referral_count_list_lock);
-       p = nfs_find_referral_count();
-       if (p != NULL) {
-               if (p->referral_count >= NFS_MAX_NESTED_REFERRALS)
-                       ret = -ELOOP;
-               else
-                       p->referral_count++;
-       } else {
-               list_add(&new->list, &nfs_referral_count_list);
-               new = NULL;
-       }
-       spin_unlock(&nfs_referral_count_list_lock);
-       kfree(new);
-out:
-       return ret;
-}
-
-static void nfs_referral_loop_unprotect(void)
-{
-       struct nfs_referral_count *p;
-
-       spin_lock(&nfs_referral_count_list_lock);
-       p = nfs_find_referral_count();
-       p->referral_count--;
-       if (p->referral_count == 0)
-               list_del(&p->list);
-       else
-               p = NULL;
-       spin_unlock(&nfs_referral_count_list_lock);
-       kfree(p);
-}
-
-static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt,
-               const char *export_path)
-{
-       struct dentry *dentry;
-       int err;
-
-       if (IS_ERR(root_mnt))
-               return ERR_CAST(root_mnt);
-
-       err = nfs_referral_loop_protect();
-       if (err) {
-               mntput(root_mnt);
-               return ERR_PTR(err);
-       }
-
-       dentry = mount_subtree(root_mnt, export_path);
-       nfs_referral_loop_unprotect();
-
-       return dentry;
-}
-
-static struct dentry *nfs4_try_mount(int flags, const char *dev_name,
-                        struct nfs_mount_info *mount_info)
-{
-       char *export_path;
-       struct vfsmount *root_mnt;
-       struct dentry *res;
-       struct nfs_parsed_mount_data *data = mount_info->parsed;
-
-       dfprintk(MOUNT, "--> nfs4_try_mount()\n");
-
-       mount_info->fill_super = nfs4_fill_super;
-
-       export_path = data->nfs_server.export_path;
-       data->nfs_server.export_path = "/";
-       root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
-                       data->nfs_server.hostname);
-       data->nfs_server.export_path = export_path;
-
-       res = nfs_follow_remote_path(root_mnt, export_path);
-
-       dfprintk(MOUNT, "<-- nfs4_try_mount() = %ld%s\n",
-                       IS_ERR(res) ? PTR_ERR(res) : 0,
-                       IS_ERR(res) ? " [error]" : "");
-       return res;
-}
-
-static void nfs4_kill_super(struct super_block *sb)
-{
-       struct nfs_server *server = NFS_SB(sb);
-
-       dprintk("--> %s\n", __func__);
-       nfs_super_return_all_delegations(sb);
-       kill_anon_super(sb);
-       nfs_fscache_release_super_cookie(sb);
-       nfs_free_server(server);
-       dprintk("<-- %s\n", __func__);
-}
-
-/*
- * Clone an NFS4 server record on xdev traversal (FSID-change)
- */
-static struct dentry *
-nfs4_xdev_mount(struct file_system_type *fs_type, int flags,
-                const char *dev_name, void *raw_data)
-{
-       struct nfs_mount_info mount_info = {
-               .fill_super = nfs4_clone_super,
-               .set_security = nfs_clone_sb_security,
-               .cloned = raw_data,
-       };
-       return nfs_xdev_mount_common(&nfs4_fs_type, flags, dev_name, &mount_info);
-}
-
-static struct dentry *
-nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags,
-                          const char *dev_name, void *raw_data)
-{
-       struct nfs_mount_info mount_info = {
-               .fill_super = nfs4_fill_super,
-               .set_security = nfs_clone_sb_security,
-               .cloned = raw_data,
-       };
-       struct nfs_server *server;
-       struct dentry *mntroot = ERR_PTR(-ENOMEM);
-
-       dprintk("--> nfs4_referral_get_sb()\n");
-
-       mount_info.mntfh = nfs_alloc_fhandle();
-       if (mount_info.cloned == NULL || mount_info.mntfh == NULL)
-               goto out;
-
-       /* create a new volume representation */
-       server = nfs4_create_referral_server(mount_info.cloned, mount_info.mntfh);
-       if (IS_ERR(server)) {
-               mntroot = ERR_CAST(server);
-               goto out;
-       }
-
-       mntroot = nfs_fs_mount_common(&nfs4_fs_type, server, flags, dev_name, &mount_info);
-out:
-       nfs_free_fhandle(mount_info.mntfh);
-       return mntroot;
-}
-
-/*
- * Create an NFS4 server record on referral traversal
- */
-static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
-               int flags, const char *dev_name, void *raw_data)
-{
-       struct nfs_clone_mount *data = raw_data;
-       char *export_path;
-       struct vfsmount *root_mnt;
-       struct dentry *res;
-
-       dprintk("--> nfs4_referral_mount()\n");
-
-       export_path = data->mnt_path;
-       data->mnt_path = "/";
-
-       root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type,
-                       flags, data, data->hostname);
-       data->mnt_path = export_path;
-
-       res = nfs_follow_remote_path(root_mnt, export_path);
-       dprintk("<-- nfs4_referral_mount() = %ld%s\n",
-                       IS_ERR(res) ? PTR_ERR(res) : 0,
-                       IS_ERR(res) ? " [error]" : "");
-       return res;
-}
-
+#define param_check_portnr(name, p) __param_check(name, p, unsigned int);
+
+module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644);
+module_param(nfs_idmap_cache_timeout, int, 0644);
+module_param(nfs4_disable_idmapping, bool, 0644);
+MODULE_PARM_DESC(nfs4_disable_idmapping,
+               "Turn off NFSv4 idmapping when using 'sec=sys'");
+module_param(max_session_slots, ushort, 0644);
+MODULE_PARM_DESC(max_session_slots, "Maximum number of outstanding NFSv4.1 "
+               "requests the client will negotiate");
+module_param(send_implementation_id, ushort, 0644);
+MODULE_PARM_DESC(send_implementation_id,
+               "Send implementation ID with NFSv4.1 exchange_id");
 #endif /* CONFIG_NFS_V4 */
index ad4d2e787b2041d17eaacc4a1ce8097f0cc13aca..6b3f2535a3ec7ae5c840696956288c41dac84021 100644 (file)
@@ -9,37 +9,11 @@
 #include <linux/fs.h>
 #include <linux/sysctl.h>
 #include <linux/module.h>
-#include <linux/nfs4.h>
-#include <linux/nfs_idmap.h>
 #include <linux/nfs_fs.h>
 
-#include "callback.h"
-
-#ifdef CONFIG_NFS_V4
-static const int nfs_set_port_min = 0;
-static const int nfs_set_port_max = 65535;
-#endif
 static struct ctl_table_header *nfs_callback_sysctl_table;
 
 static ctl_table nfs_cb_sysctls[] = {
-#ifdef CONFIG_NFS_V4
-       {
-               .procname = "nfs_callback_tcpport",
-               .data = &nfs_callback_set_tcpport,
-               .maxlen = sizeof(int),
-               .mode = 0644,
-               .proc_handler = proc_dointvec_minmax,
-               .extra1 = (int *)&nfs_set_port_min,
-               .extra2 = (int *)&nfs_set_port_max,
-       },
-       {
-               .procname = "idmap_cache_timeout",
-               .data = &nfs_idmap_cache_timeout,
-               .maxlen = sizeof(int),
-               .mode = 0644,
-               .proc_handler = proc_dointvec_jiffies,
-       },
-#endif
        {
                .procname       = "nfs_mountpoint_timeout",
                .data           = &nfs_mountpoint_expiry_timeout,
index 3210a03342f924e886e5c7f174c9147de54911a4..13cea637eff82288f2f5508023a592e60db3fa7a 100644 (file)
@@ -501,7 +501,7 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry)
                (unsigned long long)NFS_FILEID(dentry->d_inode));
 
        /* Return delegation in anticipation of the rename */
-       nfs_inode_return_delegation(dentry->d_inode);
+       NFS_PROTO(dentry->d_inode)->return_delegation(dentry->d_inode);
 
        sdentry = NULL;
        do {
index 4d6861c0dc142a5ec41e5da405012b661aeb3bc4..5829d0ce7cfb574f28451380b153df72d3dcd18f 100644 (file)
@@ -52,7 +52,7 @@ static mempool_t *nfs_commit_mempool;
 
 struct nfs_commit_data *nfs_commitdata_alloc(void)
 {
-       struct nfs_commit_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOFS);
+       struct nfs_commit_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOIO);
 
        if (p) {
                memset(p, 0, sizeof(*p));
@@ -70,7 +70,7 @@ EXPORT_SYMBOL_GPL(nfs_commit_free);
 
 struct nfs_write_header *nfs_writehdr_alloc(void)
 {
-       struct nfs_write_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS);
+       struct nfs_write_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOIO);
 
        if (p) {
                struct nfs_pgio_header *hdr = &p->header;
@@ -84,6 +84,7 @@ struct nfs_write_header *nfs_writehdr_alloc(void)
        }
        return p;
 }
+EXPORT_SYMBOL_GPL(nfs_writehdr_alloc);
 
 static struct nfs_write_data *nfs_writedata_alloc(struct nfs_pgio_header *hdr,
                                                  unsigned int pagecount)
@@ -115,6 +116,7 @@ void nfs_writehdr_free(struct nfs_pgio_header *hdr)
        struct nfs_write_header *whdr = container_of(hdr, struct nfs_write_header, header);
        mempool_free(whdr, nfs_wdata_mempool);
 }
+EXPORT_SYMBOL_GPL(nfs_writehdr_free);
 
 void nfs_writedata_release(struct nfs_write_data *wdata)
 {
@@ -131,6 +133,7 @@ void nfs_writedata_release(struct nfs_write_data *wdata)
        if (atomic_dec_and_test(&hdr->refcnt))
                hdr->completion_ops->completion(hdr);
 }
+EXPORT_SYMBOL_GPL(nfs_writedata_release);
 
 static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error)
 {
@@ -139,25 +142,38 @@ static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error)
        set_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
 }
 
-static struct nfs_page *nfs_page_find_request_locked(struct page *page)
+static struct nfs_page *
+nfs_page_find_request_locked(struct nfs_inode *nfsi, struct page *page)
 {
        struct nfs_page *req = NULL;
 
-       if (PagePrivate(page)) {
+       if (PagePrivate(page))
                req = (struct nfs_page *)page_private(page);
-               if (req != NULL)
-                       kref_get(&req->wb_kref);
+       else if (unlikely(PageSwapCache(page))) {
+               struct nfs_page *freq, *t;
+
+               /* Linearly search the commit list for the correct req */
+               list_for_each_entry_safe(freq, t, &nfsi->commit_info.list, wb_list) {
+                       if (freq->wb_page == page) {
+                               req = freq;
+                               break;
+                       }
+               }
        }
+
+       if (req)
+               kref_get(&req->wb_kref);
+
        return req;
 }
 
 static struct nfs_page *nfs_page_find_request(struct page *page)
 {
-       struct inode *inode = page->mapping->host;
+       struct inode *inode = page_file_mapping(page)->host;
        struct nfs_page *req = NULL;
 
        spin_lock(&inode->i_lock);
-       req = nfs_page_find_request_locked(page);
+       req = nfs_page_find_request_locked(NFS_I(inode), page);
        spin_unlock(&inode->i_lock);
        return req;
 }
@@ -165,16 +181,16 @@ static struct nfs_page *nfs_page_find_request(struct page *page)
 /* Adjust the file length if we're writing beyond the end */
 static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count)
 {
-       struct inode *inode = page->mapping->host;
+       struct inode *inode = page_file_mapping(page)->host;
        loff_t end, i_size;
        pgoff_t end_index;
 
        spin_lock(&inode->i_lock);
        i_size = i_size_read(inode);
        end_index = (i_size - 1) >> PAGE_CACHE_SHIFT;
-       if (i_size > 0 && page->index < end_index)
+       if (i_size > 0 && page_file_index(page) < end_index)
                goto out;
-       end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + ((loff_t)offset+count);
+       end = page_file_offset(page) + ((loff_t)offset+count);
        if (i_size >= end)
                goto out;
        i_size_write(inode, end);
@@ -187,7 +203,7 @@ out:
 static void nfs_set_pageerror(struct page *page)
 {
        SetPageError(page);
-       nfs_zap_mapping(page->mapping->host, page->mapping);
+       nfs_zap_mapping(page_file_mapping(page)->host, page_file_mapping(page));
 }
 
 /* We can set the PG_uptodate flag if we see that a write request
@@ -228,7 +244,7 @@ static int nfs_set_page_writeback(struct page *page)
        int ret = test_set_page_writeback(page);
 
        if (!ret) {
-               struct inode *inode = page->mapping->host;
+               struct inode *inode = page_file_mapping(page)->host;
                struct nfs_server *nfss = NFS_SERVER(inode);
 
                if (atomic_long_inc_return(&nfss->writeback) >
@@ -242,7 +258,7 @@ static int nfs_set_page_writeback(struct page *page)
 
 static void nfs_end_page_writeback(struct page *page)
 {
-       struct inode *inode = page->mapping->host;
+       struct inode *inode = page_file_mapping(page)->host;
        struct nfs_server *nfss = NFS_SERVER(inode);
 
        end_page_writeback(page);
@@ -252,13 +268,13 @@ static void nfs_end_page_writeback(struct page *page)
 
 static struct nfs_page *nfs_find_and_lock_request(struct page *page, bool nonblock)
 {
-       struct inode *inode = page->mapping->host;
+       struct inode *inode = page_file_mapping(page)->host;
        struct nfs_page *req;
        int ret;
 
        spin_lock(&inode->i_lock);
        for (;;) {
-               req = nfs_page_find_request_locked(page);
+               req = nfs_page_find_request_locked(NFS_I(inode), page);
                if (req == NULL)
                        break;
                if (nfs_lock_request(req))
@@ -313,13 +329,13 @@ out:
 
 static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, struct nfs_pageio_descriptor *pgio)
 {
-       struct inode *inode = page->mapping->host;
+       struct inode *inode = page_file_mapping(page)->host;
        int ret;
 
        nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE);
        nfs_add_stats(inode, NFSIOS_WRITEPAGES, 1);
 
-       nfs_pageio_cond_complete(pgio, page->index);
+       nfs_pageio_cond_complete(pgio, page_file_index(page));
        ret = nfs_page_async_flush(pgio, page, wbc->sync_mode == WB_SYNC_NONE);
        if (ret == -EAGAIN) {
                redirty_page_for_writepage(wbc, page);
@@ -336,8 +352,10 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc
        struct nfs_pageio_descriptor pgio;
        int err;
 
-       nfs_pageio_init_write(&pgio, page->mapping->host, wb_priority(wbc),
-                             &nfs_async_write_completion_ops);
+       NFS_PROTO(page_file_mapping(page)->host)->write_pageio_init(&pgio,
+                                                         page->mapping->host,
+                                                         wb_priority(wbc),
+                                                         &nfs_async_write_completion_ops);
        err = nfs_do_writepage(page, wbc, &pgio);
        nfs_pageio_complete(&pgio);
        if (err < 0)
@@ -380,8 +398,7 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
 
        nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES);
 
-       nfs_pageio_init_write(&pgio, inode, wb_priority(wbc),
-                             &nfs_async_write_completion_ops);
+       NFS_PROTO(inode)->write_pageio_init(&pgio, inode, wb_priority(wbc), &nfs_async_write_completion_ops);
        err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio);
        nfs_pageio_complete(&pgio);
 
@@ -410,11 +427,17 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
        nfs_lock_request(req);
 
        spin_lock(&inode->i_lock);
-       if (!nfsi->npages && nfs_have_delegation(inode, FMODE_WRITE))
+       if (!nfsi->npages && NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE))
                inode->i_version++;
-       set_bit(PG_MAPPED, &req->wb_flags);
-       SetPagePrivate(req->wb_page);
-       set_page_private(req->wb_page, (unsigned long)req);
+       /*
+        * Swap-space should not get truncated. Hence no need to plug the race
+        * with invalidate/truncate.
+        */
+       if (likely(!PageSwapCache(req->wb_page))) {
+               set_bit(PG_MAPPED, &req->wb_flags);
+               SetPagePrivate(req->wb_page);
+               set_page_private(req->wb_page, (unsigned long)req);
+       }
        nfsi->npages++;
        kref_get(&req->wb_kref);
        spin_unlock(&inode->i_lock);
@@ -431,9 +454,11 @@ static void nfs_inode_remove_request(struct nfs_page *req)
        BUG_ON (!NFS_WBACK_BUSY(req));
 
        spin_lock(&inode->i_lock);
-       set_page_private(req->wb_page, 0);
-       ClearPagePrivate(req->wb_page);
-       clear_bit(PG_MAPPED, &req->wb_flags);
+       if (likely(!PageSwapCache(req->wb_page))) {
+               set_page_private(req->wb_page, 0);
+               ClearPagePrivate(req->wb_page);
+               clear_bit(PG_MAPPED, &req->wb_flags);
+       }
        nfsi->npages--;
        spin_unlock(&inode->i_lock);
        nfs_release_request(req);
@@ -445,7 +470,7 @@ nfs_mark_request_dirty(struct nfs_page *req)
        __set_page_dirty_nobuffers(req->wb_page);
 }
 
-#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
+#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4)
 /**
  * nfs_request_add_commit_list - add request to a commit list
  * @req: pointer to a struct nfs_page
@@ -470,7 +495,7 @@ nfs_request_add_commit_list(struct nfs_page *req, struct list_head *dst,
        spin_unlock(cinfo->lock);
        if (!cinfo->dreq) {
                inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
-               inc_bdi_stat(req->wb_page->mapping->backing_dev_info,
+               inc_bdi_stat(page_file_mapping(req->wb_page)->backing_dev_info,
                             BDI_RECLAIMABLE);
                __mark_inode_dirty(req->wb_context->dentry->d_inode,
                                   I_DIRTY_DATASYNC);
@@ -537,7 +562,7 @@ static void
 nfs_clear_page_commit(struct page *page)
 {
        dec_zone_page_state(page, NR_UNSTABLE_NFS);
-       dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE);
+       dec_bdi_stat(page_file_mapping(page)->backing_dev_info, BDI_RECLAIMABLE);
 }
 
 static void
@@ -620,7 +645,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr)
                        goto next;
                }
                if (test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) {
-                       memcpy(&req->wb_verf, hdr->verf, sizeof(req->wb_verf));
+                       memcpy(&req->wb_verf, &hdr->verf->verifier, sizeof(req->wb_verf));
                        nfs_mark_request_commit(req, hdr->lseg, &cinfo);
                        goto next;
                }
@@ -635,7 +660,7 @@ out:
        hdr->release(hdr);
 }
 
-#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
+#if  IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4)
 static unsigned long
 nfs_reqs_to_commit(struct nfs_commit_info *cinfo)
 {
@@ -729,7 +754,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
        spin_lock(&inode->i_lock);
 
        for (;;) {
-               req = nfs_page_find_request_locked(page);
+               req = nfs_page_find_request_locked(NFS_I(inode), page);
                if (req == NULL)
                        goto out_unlock;
 
@@ -788,7 +813,7 @@ out_err:
 static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx,
                struct page *page, unsigned int offset, unsigned int bytes)
 {
-       struct inode *inode = page->mapping->host;
+       struct inode *inode = page_file_mapping(page)->host;
        struct nfs_page *req;
 
        req = nfs_try_to_update_request(inode, page, offset, bytes);
@@ -841,7 +866,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
                nfs_release_request(req);
                if (!do_flush)
                        return 0;
-               status = nfs_wb_page(page->mapping->host, page);
+               status = nfs_wb_page(page_file_mapping(page)->host, page);
        } while (status == 0);
        return status;
 }
@@ -871,7 +896,7 @@ int nfs_updatepage(struct file *file, struct page *page,
                unsigned int offset, unsigned int count)
 {
        struct nfs_open_context *ctx = nfs_file_open_context(file);
-       struct inode    *inode = page->mapping->host;
+       struct inode    *inode = page_file_mapping(page)->host;
        int             status = 0;
 
        nfs_inc_stats(inode, NFSIOS_VFSUPDATEPAGE);
@@ -879,7 +904,7 @@ int nfs_updatepage(struct file *file, struct page *page,
        dprintk("NFS:       nfs_updatepage(%s/%s %d@%lld)\n",
                file->f_path.dentry->d_parent->d_name.name,
                file->f_path.dentry->d_name.name, count,
-               (long long)(page_offset(page) + offset));
+               (long long)(page_file_offset(page) + offset));
 
        /* If we're not using byte range locks, and we know the page
         * is up to date, it may be more efficient to extend the write
@@ -1172,6 +1197,7 @@ int nfs_generic_flush(struct nfs_pageio_descriptor *desc,
                return nfs_flush_multi(desc, hdr);
        return nfs_flush_one(desc, hdr);
 }
+EXPORT_SYMBOL_GPL(nfs_generic_flush);
 
 static int nfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc)
 {
@@ -1202,13 +1228,14 @@ static const struct nfs_pageio_ops nfs_pageio_write_ops = {
        .pg_doio = nfs_generic_pg_writepages,
 };
 
-void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio,
+void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
                               struct inode *inode, int ioflags,
                               const struct nfs_pgio_completion_ops *compl_ops)
 {
        nfs_pageio_init(pgio, inode, &nfs_pageio_write_ops, compl_ops,
                                NFS_SERVER(inode)->wsize, ioflags);
 }
+EXPORT_SYMBOL_GPL(nfs_pageio_init_write);
 
 void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio)
 {
@@ -1217,13 +1244,6 @@ void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio)
 }
 EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds);
 
-void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
-                          struct inode *inode, int ioflags,
-                          const struct nfs_pgio_completion_ops *compl_ops)
-{
-       if (!pnfs_pageio_init_write(pgio, inode, ioflags, compl_ops))
-               nfs_pageio_init_write_mds(pgio, inode, ioflags, compl_ops);
-}
 
 void nfs_write_prepare(struct rpc_task *task, void *calldata)
 {
@@ -1303,7 +1323,7 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
                return;
        nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, resp->count);
 
-#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
+#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4)
        if (resp->verf->committed < argp->stable && task->tk_status >= 0) {
                /* We tried a write call, but the server did not
                 * commit data to stable storage even though we
@@ -1363,7 +1383,7 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
 }
 
 
-#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
+#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4)
 static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait)
 {
        int ret;
@@ -1475,7 +1495,7 @@ void nfs_retry_commit(struct list_head *page_list,
                nfs_mark_request_commit(req, lseg, cinfo);
                if (!cinfo->dreq) {
                        dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
-                       dec_bdi_stat(req->wb_page->mapping->backing_dev_info,
+                       dec_bdi_stat(page_file_mapping(req->wb_page)->backing_dev_info,
                                     BDI_RECLAIMABLE);
                }
                nfs_unlock_and_release_request(req);
@@ -1547,7 +1567,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
 
                /* Okay, COMMIT succeeded, apparently. Check the verifier
                 * returned by the server against all stored verfs. */
-               if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) {
+               if (!memcmp(&req->wb_verf, &data->verf.verifier, sizeof(req->wb_verf))) {
                        /* We have a match */
                        nfs_inode_remove_request(req);
                        dprintk(" OK\n");
@@ -1677,22 +1697,9 @@ static int nfs_commit_unstable_pages(struct inode *inode, struct writeback_contr
 
 int nfs_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
-       int ret;
-
-       ret = nfs_commit_unstable_pages(inode, wbc);
-       if (ret >= 0 && test_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(inode)->flags)) {
-               int status;
-               bool sync = true;
-
-               if (wbc->sync_mode == WB_SYNC_NONE)
-                       sync = false;
-
-               status = pnfs_layoutcommit_inode(inode, sync);
-               if (status < 0)
-                       return status;
-       }
-       return ret;
+       return nfs_commit_unstable_pages(inode, wbc);
 }
+EXPORT_SYMBOL_GPL(nfs_write_inode);
 
 /*
  * flush the inode to disk.
@@ -1708,6 +1715,7 @@ int nfs_wb_all(struct inode *inode)
 
        return sync_inode(inode, &wbc);
 }
+EXPORT_SYMBOL_GPL(nfs_wb_all);
 
 int nfs_wb_page_cancel(struct inode *inode, struct page *page)
 {
@@ -1744,7 +1752,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page)
  */
 int nfs_wb_page(struct inode *inode, struct page *page)
 {
-       loff_t range_start = page_offset(page);
+       loff_t range_start = page_file_offset(page);
        loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1);
        struct writeback_control wbc = {
                .sync_mode = WB_SYNC_ALL,
index ba233499b9a5fc1b374bc7d79ad8f636f01135b0..a3946cf13fc8373d6234c64d4316505364914ca3 100644 (file)
@@ -398,7 +398,7 @@ fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc)
        int migrated, i, err;
 
        /* listsize */
-       err = get_int(mesg, &fsloc->locations_count);
+       err = get_uint(mesg, &fsloc->locations_count);
        if (err)
                return err;
        if (fsloc->locations_count > MAX_FS_LOCATIONS)
@@ -456,7 +456,7 @@ static int secinfo_parse(char **mesg, char *buf, struct svc_export *exp)
                return -EINVAL;
 
        for (f = exp->ex_flavors; f < exp->ex_flavors + listsize; f++) {
-               err = get_int(mesg, &f->pseudoflavor);
+               err = get_uint(mesg, &f->pseudoflavor);
                if (err)
                        return err;
                /*
@@ -465,7 +465,7 @@ static int secinfo_parse(char **mesg, char *buf, struct svc_export *exp)
                 * problem at export time instead of when a client fails
                 * to authenticate.
                 */
-               err = get_int(mesg, &f->flags);
+               err = get_uint(mesg, &f->flags);
                if (err)
                        return err;
                /* Only some flags are allowed to differ between flavors: */
@@ -929,7 +929,7 @@ struct svc_export *
 rqst_exp_get_by_name(struct svc_rqst *rqstp, struct path *path)
 {
        struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT);
-       struct nfsd_net *nn = net_generic(rqstp->rq_xprt->xpt_net, nfsd_net_id);
+       struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
        struct cache_detail *cd = nn->svc_export_cache;
 
        if (rqstp->rq_client == NULL)
@@ -960,7 +960,7 @@ struct svc_export *
 rqst_exp_find(struct svc_rqst *rqstp, int fsid_type, u32 *fsidv)
 {
        struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT);
-       struct nfsd_net *nn = net_generic(rqstp->rq_xprt->xpt_net, nfsd_net_id);
+       struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
        struct cache_detail *cd = nn->svc_export_cache;
 
        if (rqstp->rq_client == NULL)
index 39365636b244fbfc7aaac3a794f83f87d7ea69a6..65c2431ea32fa6638cc34b8f240b351328bad751 100644 (file)
@@ -34,6 +34,10 @@ struct nfsd_net {
 
        struct cache_detail *idtoname_cache;
        struct cache_detail *nametoid_cache;
+
+       struct lock_manager nfsd4_manager;
+       bool grace_ended;
+       time_t boot_time;
 };
 
 extern int nfsd_net_id;
index a5fd6b982f277ce648bbd528947964ea2ef63c73..cbaf4f8bb7b712be7e92db2a7bf102c1f8c2dc6f 100644 (file)
@@ -756,7 +756,6 @@ static void do_probe_callback(struct nfs4_client *clp)
  */
 void nfsd4_probe_callback(struct nfs4_client *clp)
 {
-       /* XXX: atomicity?  Also, should we be using cl_flags? */
        clp->cl_cb_state = NFSD4_CB_UNKNOWN;
        set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags);
        do_probe_callback(clp);
index dae36f1dee95e68defce943bedf01efc46d61a54..fdc91a6fc9c4e3d50bea67386c463bd50d8c4c12 100644 (file)
@@ -546,7 +546,7 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen
                .type = type,
        };
        int ret;
-       struct nfsd_net *nn = net_generic(rqstp->rq_xprt->xpt_net, nfsd_net_id);
+       struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
 
        if (namelen + 1 > sizeof(key.name))
                return nfserr_badowner;
@@ -571,7 +571,7 @@ idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name)
                .type = type,
        };
        int ret;
-       struct nfsd_net *nn = net_generic(rqstp->rq_xprt->xpt_net, nfsd_net_id);
+       struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
 
        strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
        ret = idmap_lookup(rqstp, idtoname_lookup, &key, nn->idtoname_cache, &item);
index 987e719fbae8c64f2776f914e7f4104ee04be492..c9c1c0a254170ebc1de0bc70cdcdcce6c8ff5b6d 100644 (file)
@@ -354,10 +354,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        /* Openowner is now set, so sequence id will get bumped.  Now we need
         * these checks before we do any creates: */
        status = nfserr_grace;
-       if (locks_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
+       if (locks_in_grace(SVC_NET(rqstp)) && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
                goto out;
        status = nfserr_no_grace;
-       if (!locks_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
+       if (!locks_in_grace(SVC_NET(rqstp)) && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
                goto out;
 
        switch (open->op_claim_type) {
@@ -686,7 +686,8 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        nfs4_lock_state();
        /* check stateid */
-       if ((status = nfs4_preprocess_stateid_op(cstate, &read->rd_stateid,
+       if ((status = nfs4_preprocess_stateid_op(SVC_NET(rqstp),
+                                                cstate, &read->rd_stateid,
                                                 RD_STATE, &read->rd_filp))) {
                dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
                goto out;
@@ -741,7 +742,7 @@ nfsd4_remove(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 {
        __be32 status;
 
-       if (locks_in_grace())
+       if (locks_in_grace(SVC_NET(rqstp)))
                return nfserr_grace;
        status = nfsd_unlink(rqstp, &cstate->current_fh, 0,
                             remove->rm_name, remove->rm_namelen);
@@ -760,8 +761,8 @@ nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        if (!cstate->save_fh.fh_dentry)
                return status;
-       if (locks_in_grace() && !(cstate->save_fh.fh_export->ex_flags
-                                       & NFSEXP_NOSUBTREECHECK))
+       if (locks_in_grace(SVC_NET(rqstp)) &&
+               !(cstate->save_fh.fh_export->ex_flags & NFSEXP_NOSUBTREECHECK))
                return nfserr_grace;
        status = nfsd_rename(rqstp, &cstate->save_fh, rename->rn_sname,
                             rename->rn_snamelen, &cstate->current_fh,
@@ -845,7 +846,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
                nfs4_lock_state();
-               status = nfs4_preprocess_stateid_op(cstate,
+               status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate,
                        &setattr->sa_stateid, WR_STATE, NULL);
                nfs4_unlock_state();
                if (status) {
@@ -890,7 +891,8 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                return nfserr_inval;
 
        nfs4_lock_state();
-       status = nfs4_preprocess_stateid_op(cstate, stateid, WR_STATE, &filp);
+       status = nfs4_preprocess_stateid_op(SVC_NET(rqstp),
+                                       cstate, stateid, WR_STATE, &filp);
        if (filp)
                get_file(filp);
        nfs4_unlock_state();
index 5ff0b7b9fc08f22f39cc1f2d83062baceb773bdc..43295d45cc2b553e2afda4d11d7b2d429b8676ef 100644 (file)
@@ -154,6 +154,10 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
        if (status < 0)
                return;
 
+       status = mnt_want_write_file(rec_file);
+       if (status)
+               return;
+
        dir = rec_file->f_path.dentry;
        /* lock the parent */
        mutex_lock(&dir->d_inode->i_mutex);
@@ -173,11 +177,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
                 * as well be forgiving and just succeed silently.
                 */
                goto out_put;
-       status = mnt_want_write_file(rec_file);
-       if (status)
-               goto out_put;
        status = vfs_mkdir(dir->d_inode, dentry, S_IRWXU);
-       mnt_drop_write_file(rec_file);
 out_put:
        dput(dentry);
 out_unlock:
@@ -189,6 +189,7 @@ out_unlock:
                                " (err %d); please check that %s exists"
                                " and is writeable", status,
                                user_recovery_dirname);
+       mnt_drop_write_file(rec_file);
        nfs4_reset_creds(original_cred);
 }
 
index 94effd5bc4a107086ef53515ac2bb9dc39950b88..cc894eda385a48d0ecb98f2687ef566815648d75 100644 (file)
 #include <linux/namei.h>
 #include <linux/swap.h>
 #include <linux/pagemap.h>
+#include <linux/ratelimit.h>
 #include <linux/sunrpc/svcauth_gss.h>
 #include <linux/sunrpc/clnt.h>
 #include "xdr4.h"
 #include "vfs.h"
 #include "current_stateid.h"
+#include "fault_inject.h"
+
+#include "netns.h"
 
 #define NFSDDBG_FACILITY                NFSDDBG_PROC
 
 /* Globals */
 time_t nfsd4_lease = 90;     /* default lease time */
 time_t nfsd4_grace = 90;
-static time_t boot_time;
 
 #define all_ones {{~0,~0},~0}
 static const stateid_t one_stateid = {
@@ -862,6 +865,11 @@ static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses,
        if (ret)
                /* oops; xprt is already down: */
                nfsd4_conn_lost(&conn->cn_xpt_user);
+       if (ses->se_client->cl_cb_state == NFSD4_CB_DOWN &&
+               dir & NFS4_CDFC4_BACK) {
+               /* callback channel may be back up */
+               nfsd4_probe_callback(ses->se_client);
+       }
        return nfs_ok;
 }
 
@@ -1047,12 +1055,12 @@ renew_client(struct nfs4_client *clp)
 
 /* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */
 static int
-STALE_CLIENTID(clientid_t *clid)
+STALE_CLIENTID(clientid_t *clid, struct nfsd_net *nn)
 {
-       if (clid->cl_boot == boot_time)
+       if (clid->cl_boot == nn->boot_time)
                return 0;
        dprintk("NFSD stale clientid (%08x/%08x) boot_time %08lx\n",
-               clid->cl_boot, clid->cl_id, boot_time);
+               clid->cl_boot, clid->cl_id, nn->boot_time);
        return 1;
 }
 
@@ -1215,7 +1223,7 @@ static bool groups_equal(struct group_info *g1, struct group_info *g2)
        return true;
 }
 
-static int
+static bool
 same_creds(struct svc_cred *cr1, struct svc_cred *cr2)
 {
        if ((cr1->cr_flavor != cr2->cr_flavor)
@@ -1227,14 +1235,15 @@ same_creds(struct svc_cred *cr1, struct svc_cred *cr2)
                return true;
        if (!cr1->cr_principal || !cr2->cr_principal)
                return false;
-       return 0 == strcmp(cr1->cr_principal, cr1->cr_principal);
+       return 0 == strcmp(cr1->cr_principal, cr2->cr_principal);
 }
 
 static void gen_clid(struct nfs4_client *clp)
 {
        static u32 current_clientid = 1;
+       struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
 
-       clp->cl_clientid.cl_boot = boot_time;
+       clp->cl_clientid.cl_boot = nn->boot_time;
        clp->cl_clientid.cl_id = current_clientid++; 
 }
 
@@ -2217,8 +2226,9 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
        nfs4_verifier confirm = setclientid_confirm->sc_confirm; 
        clientid_t * clid = &setclientid_confirm->sc_clientid;
        __be32 status;
+       struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
 
-       if (STALE_CLIENTID(clid))
+       if (STALE_CLIENTID(clid, nn))
                return nfserr_stale_clientid;
        nfs4_lock_state();
 
@@ -2577,8 +2587,9 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
        unsigned int strhashval;
        struct nfs4_openowner *oo = NULL;
        __be32 status;
+       struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
 
-       if (STALE_CLIENTID(&open->op_clientid))
+       if (STALE_CLIENTID(&open->op_clientid, nn))
                return nfserr_stale_clientid;
        /*
         * In case we need it later, after we've already created the
@@ -2876,7 +2887,8 @@ static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status)
  * Attempt to hand out a delegation.
  */
 static void
-nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_ol_stateid *stp)
+nfs4_open_delegation(struct net *net, struct svc_fh *fh,
+                    struct nfsd4_open *open, struct nfs4_ol_stateid *stp)
 {
        struct nfs4_delegation *dp;
        struct nfs4_openowner *oo = container_of(stp->st_stateowner, struct nfs4_openowner, oo_owner);
@@ -2897,7 +2909,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_ol_
                case NFS4_OPEN_CLAIM_NULL:
                        /* Let's not give out any delegations till everyone's
                         * had the chance to reclaim theirs.... */
-                       if (locks_in_grace())
+                       if (locks_in_grace(net))
                                goto out;
                        if (!cb_up || !(oo->oo_flags & NFS4_OO_CONFIRMED))
                                goto out;
@@ -3005,16 +3017,14 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
                        goto out;
        } else {
                status = nfs4_get_vfs_file(rqstp, fp, current_fh, open);
+               if (status)
+                       goto out;
+               status = nfsd4_truncate(rqstp, current_fh, open);
                if (status)
                        goto out;
                stp = open->op_stp;
                open->op_stp = NULL;
                init_open_stateid(stp, fp, open);
-               status = nfsd4_truncate(rqstp, current_fh, open);
-               if (status) {
-                       release_open_stateid(stp);
-                       goto out;
-               }
        }
        update_stateid(&stp->st_stid.sc_stateid);
        memcpy(&open->op_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
@@ -3033,7 +3043,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
        * Attempt to hand out a delegation. No error return, because the
        * OPEN succeeds even if we fail.
        */
-       nfs4_open_delegation(current_fh, open, stp);
+       nfs4_open_delegation(SVC_NET(rqstp), current_fh, open, stp);
 nodeleg:
        status = nfs_ok;
 
@@ -3087,12 +3097,13 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 {
        struct nfs4_client *clp;
        __be32 status;
+       struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
 
        nfs4_lock_state();
        dprintk("process_renew(%08x/%08x): starting\n", 
                        clid->cl_boot, clid->cl_id);
        status = nfserr_stale_clientid;
-       if (STALE_CLIENTID(clid))
+       if (STALE_CLIENTID(clid, nn))
                goto out;
        clp = find_confirmed_client(clid);
        status = nfserr_expired;
@@ -3111,22 +3122,19 @@ out:
        return status;
 }
 
-static struct lock_manager nfsd4_manager = {
-};
-
-static bool grace_ended;
-
 static void
-nfsd4_end_grace(void)
+nfsd4_end_grace(struct net *net)
 {
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
        /* do nothing if grace period already ended */
-       if (grace_ended)
+       if (nn->grace_ended)
                return;
 
        dprintk("NFSD: end of grace period\n");
-       grace_ended = true;
-       nfsd4_record_grace_done(&init_net, boot_time);
-       locks_end_grace(&nfsd4_manager);
+       nn->grace_ended = true;
+       nfsd4_record_grace_done(net, nn->boot_time);
+       locks_end_grace(&nn->nfsd4_manager);
        /*
         * Now that every NFSv4 client has had the chance to recover and
         * to see the (possibly new, possibly shorter) lease time, we
@@ -3149,7 +3157,7 @@ nfs4_laundromat(void)
        nfs4_lock_state();
 
        dprintk("NFSD: laundromat service - starting\n");
-       nfsd4_end_grace();
+       nfsd4_end_grace(&init_net);
        INIT_LIST_HEAD(&reaplist);
        spin_lock(&client_lock);
        list_for_each_safe(pos, next, &client_lru) {
@@ -3231,9 +3239,9 @@ static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_ol_stateid *s
 }
 
 static int
-STALE_STATEID(stateid_t *stateid)
+STALE_STATEID(stateid_t *stateid, struct nfsd_net *nn)
 {
-       if (stateid->si_opaque.so_clid.cl_boot == boot_time)
+       if (stateid->si_opaque.so_clid.cl_boot == nn->boot_time)
                return 0;
        dprintk("NFSD: stale stateid " STATEID_FMT "!\n",
                STATEID_VAL(stateid));
@@ -3273,11 +3281,11 @@ out:
 }
 
 static inline __be32
-check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags)
+check_special_stateids(struct net *net, svc_fh *current_fh, stateid_t *stateid, int flags)
 {
        if (ONE_STATEID(stateid) && (flags & RD_STATE))
                return nfs_ok;
-       else if (locks_in_grace()) {
+       else if (locks_in_grace(net)) {
                /* Answer in remaining cases depends on existence of
                 * conflicting state; so we must wait out the grace period. */
                return nfserr_grace;
@@ -3294,9 +3302,9 @@ check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags)
  * that are not able to provide mandatory locking.
  */
 static inline int
-grace_disallows_io(struct inode *inode)
+grace_disallows_io(struct net *net, struct inode *inode)
 {
-       return locks_in_grace() && mandatory_lock(inode);
+       return locks_in_grace(net) && mandatory_lock(inode);
 }
 
 /* Returns true iff a is later than b: */
@@ -3333,18 +3341,26 @@ static __be32 check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_s
        return nfserr_old_stateid;
 }
 
-__be32 nfs4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
+static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
 {
        struct nfs4_stid *s;
        struct nfs4_ol_stateid *ols;
        __be32 status;
 
-       if (STALE_STATEID(stateid))
-               return nfserr_stale_stateid;
-
+       if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
+               return nfserr_bad_stateid;
+       /* Client debugging aid. */
+       if (!same_clid(&stateid->si_opaque.so_clid, &cl->cl_clientid)) {
+               char addr_str[INET6_ADDRSTRLEN];
+               rpc_ntop((struct sockaddr *)&cl->cl_addr, addr_str,
+                                sizeof(addr_str));
+               pr_warn_ratelimited("NFSD: client %s testing state ID "
+                                       "with incorrect client ID\n", addr_str);
+               return nfserr_bad_stateid;
+       }
        s = find_stateid(cl, stateid);
        if (!s)
-                return nfserr_stale_stateid;
+               return nfserr_bad_stateid;
        status = check_stateid_generation(stateid, &s->sc_stateid, 1);
        if (status)
                return status;
@@ -3360,10 +3376,11 @@ __be32 nfs4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
 static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s)
 {
        struct nfs4_client *cl;
+       struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
 
        if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
                return nfserr_bad_stateid;
-       if (STALE_STATEID(stateid))
+       if (STALE_STATEID(stateid, nn))
                return nfserr_stale_stateid;
        cl = find_confirmed_client(&stateid->si_opaque.so_clid);
        if (!cl)
@@ -3379,7 +3396,7 @@ static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, s
 * Checks for stateid operations
 */
 __be32
-nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
+nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
                           stateid_t *stateid, int flags, struct file **filpp)
 {
        struct nfs4_stid *s;
@@ -3392,11 +3409,11 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
        if (filpp)
                *filpp = NULL;
 
-       if (grace_disallows_io(ino))
+       if (grace_disallows_io(net, ino))
                return nfserr_grace;
 
        if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
-               return check_special_stateids(current_fh, stateid, flags);
+               return check_special_stateids(net, current_fh, stateid, flags);
 
        status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s);
        if (status)
@@ -3463,7 +3480,8 @@ nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        nfs4_lock_state();
        list_for_each_entry(stateid, &test_stateid->ts_stateid_list, ts_id_list)
-               stateid->ts_id_status = nfs4_validate_stateid(cl, &stateid->ts_id_stateid);
+               stateid->ts_id_status =
+                       nfsd4_validate_stateid(cl, &stateid->ts_id_stateid);
        nfs4_unlock_state();
 
        return nfs_ok;
@@ -3750,12 +3768,19 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        nfsd4_close_open_stateid(stp);
        oo->oo_last_closed_stid = stp;
 
-       /* place unused nfs4_stateowners on so_close_lru list to be
-        * released by the laundromat service after the lease period
-        * to enable us to handle CLOSE replay
-        */
-       if (list_empty(&oo->oo_owner.so_stateids))
-               move_to_close_lru(oo);
+       if (list_empty(&oo->oo_owner.so_stateids)) {
+               if (cstate->minorversion) {
+                       release_openowner(oo);
+                       cstate->replay_owner = NULL;
+               } else {
+                       /*
+                        * In the 4.0 case we need to keep the owners around a
+                        * little while to handle CLOSE replay.
+                        */
+                       if (list_empty(&oo->oo_owner.so_stateids))
+                               move_to_close_lru(oo);
+               }
+       }
 out:
        if (!cstate->replay_owner)
                nfs4_unlock_state();
@@ -4027,6 +4052,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        bool new_state = false;
        int lkflg;
        int err;
+       struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
 
        dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n",
                (long long) lock->lk_offset,
@@ -4044,11 +4070,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        nfs4_lock_state();
 
        if (lock->lk_is_new) {
-               /*
-                * Client indicates that this is a new lockowner.
-                * Use open owner and open stateid to create lock owner and
-                * lock stateid.
-                */
                struct nfs4_ol_stateid *open_stp = NULL;
 
                if (nfsd4_has_session(cstate))
@@ -4058,7 +4079,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                                sizeof(clientid_t));
 
                status = nfserr_stale_clientid;
-               if (STALE_CLIENTID(&lock->lk_new_clientid))
+               if (STALE_CLIENTID(&lock->lk_new_clientid, nn))
                        goto out;
 
                /* validate and update open stateid and open seqid */
@@ -4075,17 +4096,13 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                        goto out;
                status = lookup_or_create_lock_state(cstate, open_stp, lock,
                                                        &lock_stp, &new_state);
-               if (status)
-                       goto out;
-       } else {
-               /* lock (lock owner + lock stateid) already exists */
+       } else
                status = nfs4_preprocess_seqid_op(cstate,
                                       lock->lk_old_lock_seqid,
                                       &lock->lk_old_lock_stateid,
                                       NFS4_LOCK_STID, &lock_stp);
-               if (status)
-                       goto out;
-       }
+       if (status)
+               goto out;
        lock_sop = lockowner(lock_stp->st_stateowner);
 
        lkflg = setlkflg(lock->lk_type);
@@ -4094,10 +4111,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                goto out;
 
        status = nfserr_grace;
-       if (locks_in_grace() && !lock->lk_reclaim)
+       if (locks_in_grace(SVC_NET(rqstp)) && !lock->lk_reclaim)
                goto out;
        status = nfserr_no_grace;
-       if (!locks_in_grace() && lock->lk_reclaim)
+       if (!locks_in_grace(SVC_NET(rqstp)) && lock->lk_reclaim)
                goto out;
 
        locks_init_lock(&file_lock);
@@ -4196,8 +4213,9 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        struct file_lock file_lock;
        struct nfs4_lockowner *lo;
        __be32 status;
+       struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
 
-       if (locks_in_grace())
+       if (locks_in_grace(SVC_NET(rqstp)))
                return nfserr_grace;
 
        if (check_lock_length(lockt->lt_offset, lockt->lt_length))
@@ -4206,7 +4224,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        nfs4_lock_state();
 
        status = nfserr_stale_clientid;
-       if (!nfsd4_has_session(cstate) && STALE_CLIENTID(&lockt->lt_clientid))
+       if (!nfsd4_has_session(cstate) && STALE_CLIENTID(&lockt->lt_clientid, nn))
                goto out;
 
        if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0)))
@@ -4355,6 +4373,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
        struct list_head matches;
        unsigned int hashval = ownerstr_hashval(clid->cl_id, owner);
        __be32 status;
+       struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
 
        dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
                clid->cl_boot, clid->cl_id);
@@ -4362,7 +4381,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
        /* XXX check for lease expiration */
 
        status = nfserr_stale_clientid;
-       if (STALE_CLIENTID(clid))
+       if (STALE_CLIENTID(clid, nn))
                return status;
 
        nfs4_lock_state();
@@ -4564,7 +4583,7 @@ void nfsd_forget_openowners(u64 num)
        printk(KERN_INFO "NFSD: Forgot %d open owners", count);
 }
 
-int nfsd_process_n_delegations(u64 num, void (*deleg_func)(struct nfs4_delegation *))
+int nfsd_process_n_delegations(u64 num, struct list_head *list)
 {
        int i, count = 0;
        struct nfs4_file *fp, *fnext;
@@ -4573,7 +4592,7 @@ int nfsd_process_n_delegations(u64 num, void (*deleg_func)(struct nfs4_delegatio
        for (i = 0; i < FILE_HASH_SIZE; i++) {
                list_for_each_entry_safe(fp, fnext, &file_hashtbl[i], fi_hash) {
                        list_for_each_entry_safe(dp, dnext, &fp->fi_delegations, dl_perfile) {
-                               deleg_func(dp);
+                               list_move(&dp->dl_recall_lru, list);
                                if (++count == num)
                                        return count;
                        }
@@ -4586,9 +4605,16 @@ int nfsd_process_n_delegations(u64 num, void (*deleg_func)(struct nfs4_delegatio
 void nfsd_forget_delegations(u64 num)
 {
        unsigned int count;
+       LIST_HEAD(victims);
+       struct nfs4_delegation *dp, *dnext;
+
+       spin_lock(&recall_lock);
+       count = nfsd_process_n_delegations(num, &victims);
+       spin_unlock(&recall_lock);
 
        nfs4_lock_state();
-       count = nfsd_process_n_delegations(num, unhash_delegation);
+       list_for_each_entry_safe(dp, dnext, &victims, dl_recall_lru)
+               unhash_delegation(dp);
        nfs4_unlock_state();
 
        printk(KERN_INFO "NFSD: Forgot %d delegations", count);
@@ -4597,12 +4623,16 @@ void nfsd_forget_delegations(u64 num)
 void nfsd_recall_delegations(u64 num)
 {
        unsigned int count;
+       LIST_HEAD(victims);
+       struct nfs4_delegation *dp, *dnext;
 
-       nfs4_lock_state();
        spin_lock(&recall_lock);
-       count = nfsd_process_n_delegations(num, nfsd_break_one_deleg);
+       count = nfsd_process_n_delegations(num, &victims);
+       list_for_each_entry_safe(dp, dnext, &victims, dl_recall_lru) {
+               list_del(&dp->dl_recall_lru);
+               nfsd_break_one_deleg(dp);
+       }
        spin_unlock(&recall_lock);
-       nfs4_unlock_state();
 
        printk(KERN_INFO "NFSD: Recalled %d delegations", count);
 }
@@ -4665,6 +4695,8 @@ set_max_delegations(void)
 int
 nfs4_state_start(void)
 {
+       struct net *net = &init_net;
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
        int ret;
 
        /*
@@ -4674,11 +4706,11 @@ nfs4_state_start(void)
         * to that instead and then do most of the rest of this on a per-net
         * basis.
         */
-       get_net(&init_net);
-       nfsd4_client_tracking_init(&init_net);
-       boot_time = get_seconds();
-       locks_start_grace(&nfsd4_manager);
-       grace_ended = false;
+       get_net(net);
+       nfsd4_client_tracking_init(net);
+       nn->boot_time = get_seconds();
+       locks_start_grace(net, &nn->nfsd4_manager);
+       nn->grace_ended = false;
        printk(KERN_INFO "NFSD: starting %ld-second grace period\n",
               nfsd4_grace);
        ret = set_callback_cred();
@@ -4700,8 +4732,8 @@ nfs4_state_start(void)
 out_free_laundry:
        destroy_workqueue(laundry_wq);
 out_recovery:
-       nfsd4_client_tracking_exit(&init_net);
-       put_net(&init_net);
+       nfsd4_client_tracking_exit(net);
+       put_net(net);
        return ret;
 }
 
@@ -4742,9 +4774,12 @@ __nfs4_state_shutdown(void)
 void
 nfs4_state_shutdown(void)
 {
+       struct net *net = &init_net;
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
        cancel_delayed_work_sync(&laundromat_work);
        destroy_workqueue(laundry_wq);
-       locks_end_grace(&nfsd4_manager);
+       locks_end_grace(&nn->nfsd4_manager);
        nfs4_lock_state();
        __nfs4_state_shutdown();
        nfs4_unlock_state();
index 4949667c84ea0c3d687a46faf0a455c410c39b6f..6322df36031f34c62f273f2d36630a472e991416 100644 (file)
@@ -2259,7 +2259,7 @@ out_acl:
        if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) {
                if ((buflen -= 4) < 0)
                        goto out_resource;
-               WRITE32(1);
+               WRITE32(0);
        }
        if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) {
                if ((buflen -= 4) < 0)
index c55298ed5772577e5afe3bd613c3e5a0df3b69dd..fa49cff5ee651b9aa0b16f9cb43804f616aedc4e 100644 (file)
@@ -673,9 +673,7 @@ static ssize_t __write_ports_addfd(char *buf)
 
        err = svc_addsock(nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT);
        if (err < 0) {
-               if (nfsd_serv->sv_nrthreads == 1)
-                       svc_shutdown_net(nfsd_serv, net);
-               svc_destroy(nfsd_serv);
+               nfsd_destroy(net);
                return err;
        }
 
@@ -744,9 +742,7 @@ out_close:
                svc_xprt_put(xprt);
        }
 out_err:
-       if (nfsd_serv->sv_nrthreads == 1)
-               svc_shutdown_net(nfsd_serv, net);
-       svc_destroy(nfsd_serv);
+       nfsd_destroy(net);
        return err;
 }
 
index 1671429ffa66fa1db509fbb212a09ad80d49efa8..2244222368ab29cf4681d0d94012055bc5861f02 100644 (file)
@@ -72,6 +72,19 @@ int          nfsd_nrthreads(void);
 int            nfsd_nrpools(void);
 int            nfsd_get_nrthreads(int n, int *);
 int            nfsd_set_nrthreads(int n, int *);
+int            nfsd_pool_stats_open(struct inode *, struct file *);
+int            nfsd_pool_stats_release(struct inode *, struct file *);
+
+static inline void nfsd_destroy(struct net *net)
+{
+       int destroy = (nfsd_serv->sv_nrthreads == 1);
+
+       if (destroy)
+               svc_shutdown_net(nfsd_serv, net);
+       svc_destroy(nfsd_serv);
+       if (destroy)
+               nfsd_serv = NULL;
+}
 
 #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
 #ifdef CONFIG_NFSD_V2_ACL
index cc793005a87cb4b5a79b7074c4861ca651085d1c..032af381b3aa4bd1298b084c57d3c4022293668c 100644 (file)
@@ -635,6 +635,7 @@ fh_put(struct svc_fh *fhp)
                fhp->fh_post_saved = 0;
 #endif
        }
+       fh_drop_write(fhp);
        if (exp) {
                exp_put(exp);
                fhp->fh_export = NULL;
index e15dc45fc5ec01c09a05d42b8ac600ed39e3892f..aad6d457b9e8ef75d410d01de3dd6bac692c96fd 100644 (file)
@@ -196,6 +196,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
        struct dentry   *dchild;
        int             type, mode;
        __be32          nfserr;
+       int             hosterr;
        dev_t           rdev = 0, wanted = new_decode_dev(attr->ia_size);
 
        dprintk("nfsd: CREATE   %s %.*s\n",
@@ -214,6 +215,12 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
        nfserr = nfserr_exist;
        if (isdotent(argp->name, argp->len))
                goto done;
+       hosterr = fh_want_write(dirfhp);
+       if (hosterr) {
+               nfserr = nfserrno(hosterr);
+               goto done;
+       }
+
        fh_lock_nested(dirfhp, I_MUTEX_PARENT);
        dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len);
        if (IS_ERR(dchild)) {
@@ -330,7 +337,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
 out_unlock:
        /* We don't really need to unlock, as fh_put does it. */
        fh_unlock(dirfhp);
-
+       fh_drop_write(dirfhp);
 done:
        fh_put(dirfhp);
        return nfsd_return_dirop(nfserr, resp);
index ee709fc8f58bc0b62a3f7ca64104630fe803b6d0..240473cb708ff8a726d2b881d9d7ec2b42339653 100644 (file)
@@ -254,8 +254,6 @@ static void nfsd_shutdown(void)
 
 static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
 {
-       /* When last nfsd thread exits we need to do some clean-up */
-       nfsd_serv = NULL;
        nfsd_shutdown();
 
        svc_rpcb_cleanup(serv, net);
@@ -332,6 +330,7 @@ static int nfsd_get_default_max_blksize(void)
 int nfsd_create_serv(void)
 {
        int error;
+       struct net *net = current->nsproxy->net_ns;
 
        WARN_ON(!mutex_is_locked(&nfsd_mutex));
        if (nfsd_serv) {
@@ -346,7 +345,7 @@ int nfsd_create_serv(void)
        if (nfsd_serv == NULL)
                return -ENOMEM;
 
-       error = svc_bind(nfsd_serv, current->nsproxy->net_ns);
+       error = svc_bind(nfsd_serv, net);
        if (error < 0) {
                svc_destroy(nfsd_serv);
                return error;
@@ -427,11 +426,7 @@ int nfsd_set_nrthreads(int n, int *nthreads)
                if (err)
                        break;
        }
-
-       if (nfsd_serv->sv_nrthreads == 1)
-               svc_shutdown_net(nfsd_serv, net);
-       svc_destroy(nfsd_serv);
-
+       nfsd_destroy(net);
        return err;
 }
 
@@ -478,9 +473,7 @@ out_shutdown:
        if (error < 0 && !nfsd_up_before)
                nfsd_shutdown();
 out_destroy:
-       if (nfsd_serv->sv_nrthreads == 1)
-               svc_shutdown_net(nfsd_serv, net);
-       svc_destroy(nfsd_serv);         /* Release server */
+       nfsd_destroy(net);              /* Release server */
 out:
        mutex_unlock(&nfsd_mutex);
        return error;
@@ -563,12 +556,13 @@ nfsd(void *vrqstp)
        nfsdstats.th_cnt --;
 
 out:
-       if (rqstp->rq_server->sv_nrthreads == 1)
-               svc_shutdown_net(rqstp->rq_server, &init_net);
+       rqstp->rq_server = NULL;
 
        /* Release the thread */
        svc_exit_thread(rqstp);
 
+       nfsd_destroy(&init_net);
+
        /* Release module */
        mutex_unlock(&nfsd_mutex);
        module_put_and_exit(0);
@@ -682,9 +676,7 @@ int nfsd_pool_stats_release(struct inode *inode, struct file *file)
 
        mutex_lock(&nfsd_mutex);
        /* this function really, really should have been called svc_put() */
-       if (nfsd_serv->sv_nrthreads == 1)
-               svc_shutdown_net(nfsd_serv, net);
-       svc_destroy(nfsd_serv);
+       nfsd_destroy(net);
        mutex_unlock(&nfsd_mutex);
        return ret;
 }
index 849091e16ea6afd43e4ddd2dbd17962fdd87ad85..e6173147f9821c816527e0bde05289d392374af7 100644 (file)
@@ -450,8 +450,10 @@ static inline struct nfs4_ol_stateid *openlockstateid(struct nfs4_stid *s)
 #define WR_STATE               0x00000020
 
 struct nfsd4_compound_state;
+struct nfsd_net;
 
-extern __be32 nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
+extern __be32 nfs4_preprocess_stateid_op(struct net *net,
+               struct nfsd4_compound_state *cstate,
                stateid_t *stateid, int flags, struct file **filp);
 extern void nfs4_lock_state(void);
 extern void nfs4_unlock_state(void);
@@ -475,7 +477,6 @@ extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname);
 extern int nfs4_client_to_reclaim(const char *name);
 extern int nfs4_has_reclaimed_state(const char *name, bool use_exchange_id);
 extern void release_session_client(struct nfsd4_session *);
-extern __be32 nfs4_validate_stateid(struct nfs4_client *, stateid_t *);
 extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *);
 
 /* nfs4recover operations */
index 4700a0a929d72baeb0c86024a1c5051e6d19cab5..a9269f142cc481ec451c681397f520c1a1cb97f7 100644 (file)
@@ -757,8 +757,16 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
         * If we get here, then the client has already done an "open",
         * and (hopefully) checked permission - so allow OWNER_OVERRIDE
         * in case a chmod has now revoked permission.
+        *
+        * Arguably we should also allow the owner override for
+        * directories, but we never have and it doesn't seem to have
+        * caused anyone a problem.  If we were to change this, note
+        * also that our filldir callbacks would need a variant of
+        * lookup_one_len that doesn't check permissions.
         */
-       err = fh_verify(rqstp, fhp, type, may_flags | NFSD_MAY_OWNER_OVERRIDE);
+       if (type == S_IFREG)
+               may_flags |= NFSD_MAY_OWNER_OVERRIDE;
+       err = fh_verify(rqstp, fhp, type, may_flags);
        if (err)
                goto out;
 
@@ -1276,6 +1284,10 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
         * If it has, the parent directory should already be locked.
         */
        if (!resfhp->fh_dentry) {
+               host_err = fh_want_write(fhp);
+               if (host_err)
+                       goto out_nfserr;
+
                /* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create */
                fh_lock_nested(fhp, I_MUTEX_PARENT);
                dchild = lookup_one_len(fname, dentry, flen);
@@ -1319,14 +1331,11 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
                goto out;
        }
 
-       host_err = fh_want_write(fhp);
-       if (host_err)
-               goto out_nfserr;
-
        /*
         * Get the dir op function pointer.
         */
        err = 0;
+       host_err = 0;
        switch (type) {
        case S_IFREG:
                host_err = vfs_create(dirp, dchild, iap->ia_mode, true);
@@ -1343,10 +1352,8 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
                host_err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev);
                break;
        }
-       if (host_err < 0) {
-               fh_drop_write(fhp);
+       if (host_err < 0)
                goto out_nfserr;
-       }
 
        err = nfsd_create_setattr(rqstp, resfhp, iap);
 
@@ -1358,7 +1365,6 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
        err2 = nfserrno(commit_metadata(fhp));
        if (err2)
                err = err2;
-       fh_drop_write(fhp);
        /*
         * Update the file handle to get the new inode info.
         */
@@ -1417,6 +1423,11 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
        err = nfserr_notdir;
        if (!dirp->i_op->lookup)
                goto out;
+
+       host_err = fh_want_write(fhp);
+       if (host_err)
+               goto out_nfserr;
+
        fh_lock_nested(fhp, I_MUTEX_PARENT);
 
        /*
@@ -1449,9 +1460,6 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
                v_atime = verifier[1]&0x7fffffff;
        }
        
-       host_err = fh_want_write(fhp);
-       if (host_err)
-               goto out_nfserr;
        if (dchild->d_inode) {
                err = 0;
 
@@ -1522,7 +1530,6 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
        if (!err)
                err = nfserrno(commit_metadata(fhp));
 
-       fh_drop_write(fhp);
        /*
         * Update the filehandle to get the new inode info.
         */
@@ -1533,6 +1540,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
        fh_unlock(fhp);
        if (dchild && !IS_ERR(dchild))
                dput(dchild);
+       fh_drop_write(fhp);
        return err;
  
  out_nfserr:
@@ -1613,6 +1621,11 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
        err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
        if (err)
                goto out;
+
+       host_err = fh_want_write(fhp);
+       if (host_err)
+               goto out_nfserr;
+
        fh_lock(fhp);
        dentry = fhp->fh_dentry;
        dnew = lookup_one_len(fname, dentry, flen);
@@ -1620,10 +1633,6 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
        if (IS_ERR(dnew))
                goto out_nfserr;
 
-       host_err = fh_want_write(fhp);
-       if (host_err)
-               goto out_nfserr;
-
        if (unlikely(path[plen] != 0)) {
                char *path_alloced = kmalloc(plen+1, GFP_KERNEL);
                if (path_alloced == NULL)
@@ -1683,6 +1692,12 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
        if (isdotent(name, len))
                goto out;
 
+       host_err = fh_want_write(tfhp);
+       if (host_err) {
+               err = nfserrno(host_err);
+               goto out;
+       }
+
        fh_lock_nested(ffhp, I_MUTEX_PARENT);
        ddir = ffhp->fh_dentry;
        dirp = ddir->d_inode;
@@ -1694,18 +1709,13 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
 
        dold = tfhp->fh_dentry;
 
-       host_err = fh_want_write(tfhp);
-       if (host_err) {
-               err = nfserrno(host_err);
-               goto out_dput;
-       }
        err = nfserr_noent;
        if (!dold->d_inode)
-               goto out_drop_write;
+               goto out_dput;
        host_err = nfsd_break_lease(dold->d_inode);
        if (host_err) {
                err = nfserrno(host_err);
-               goto out_drop_write;
+               goto out_dput;
        }
        host_err = vfs_link(dold, dirp, dnew);
        if (!host_err) {
@@ -1718,12 +1728,11 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
                else
                        err = nfserrno(host_err);
        }
-out_drop_write:
-       fh_drop_write(tfhp);
 out_dput:
        dput(dnew);
 out_unlock:
        fh_unlock(ffhp);
+       fh_drop_write(tfhp);
 out:
        return err;
 
@@ -1766,6 +1775,12 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
        if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))
                goto out;
 
+       host_err = fh_want_write(ffhp);
+       if (host_err) {
+               err = nfserrno(host_err);
+               goto out;
+       }
+
        /* cannot use fh_lock as we need deadlock protective ordering
         * so do it by hand */
        trap = lock_rename(tdentry, fdentry);
@@ -1796,17 +1811,14 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
        host_err = -EXDEV;
        if (ffhp->fh_export->ex_path.mnt != tfhp->fh_export->ex_path.mnt)
                goto out_dput_new;
-       host_err = fh_want_write(ffhp);
-       if (host_err)
-               goto out_dput_new;
 
        host_err = nfsd_break_lease(odentry->d_inode);
        if (host_err)
-               goto out_drop_write;
+               goto out_dput_new;
        if (ndentry->d_inode) {
                host_err = nfsd_break_lease(ndentry->d_inode);
                if (host_err)
-                       goto out_drop_write;
+                       goto out_dput_new;
        }
        host_err = vfs_rename(fdir, odentry, tdir, ndentry);
        if (!host_err) {
@@ -1814,8 +1826,6 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
                if (!host_err)
                        host_err = commit_metadata(ffhp);
        }
-out_drop_write:
-       fh_drop_write(ffhp);
  out_dput_new:
        dput(ndentry);
  out_dput_old:
@@ -1831,6 +1841,7 @@ out_drop_write:
        fill_post_wcc(tfhp);
        unlock_rename(tdentry, fdentry);
        ffhp->fh_locked = tfhp->fh_locked = 0;
+       fh_drop_write(ffhp);
 
 out:
        return err;
@@ -1856,6 +1867,10 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
        if (err)
                goto out;
 
+       host_err = fh_want_write(fhp);
+       if (host_err)
+               goto out_nfserr;
+
        fh_lock_nested(fhp, I_MUTEX_PARENT);
        dentry = fhp->fh_dentry;
        dirp = dentry->d_inode;
@@ -1874,21 +1889,15 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
        if (!type)
                type = rdentry->d_inode->i_mode & S_IFMT;
 
-       host_err = fh_want_write(fhp);
-       if (host_err)
-               goto out_put;
-
        host_err = nfsd_break_lease(rdentry->d_inode);
        if (host_err)
-               goto out_drop_write;
+               goto out_put;
        if (type != S_IFDIR)
                host_err = vfs_unlink(dirp, rdentry);
        else
                host_err = vfs_rmdir(dirp, rdentry);
        if (!host_err)
                host_err = commit_metadata(fhp);
-out_drop_write:
-       fh_drop_write(fhp);
 out_put:
        dput(rdentry);
 
index ec0611b2b738468fbc261b35f2190048680e67dd..359594c393d273f20b5fe3ef0a9a75379ab1edc2 100644 (file)
@@ -110,12 +110,19 @@ int nfsd_set_posix_acl(struct svc_fh *, int, struct posix_acl *);
 
 static inline int fh_want_write(struct svc_fh *fh)
 {
-       return mnt_want_write(fh->fh_export->ex_path.mnt);
+       int ret = mnt_want_write(fh->fh_export->ex_path.mnt);
+
+       if (!ret)
+               fh->fh_want_write = 1;
+       return ret;
 }
 
 static inline void fh_drop_write(struct svc_fh *fh)
 {
-       mnt_drop_write(fh->fh_export->ex_path.mnt);
+       if (fh->fh_want_write) {
+               fh->fh_want_write = 0;
+               mnt_drop_write(fh->fh_export->ex_path.mnt);
+       }
 }
 
 #endif /* LINUX_NFSD_VFS_H */
index f5fde36b9e28d09f07596d23b127005fcb26384e..fb7238100548e3e3ba1dcc0c75b362a930a22718 100644 (file)
@@ -76,15 +76,23 @@ int nilfs_palloc_freev(struct inode *, __u64 *, size_t);
 #define nilfs_clear_bit_atomic         ext2_clear_bit_atomic
 #define nilfs_find_next_zero_bit       find_next_zero_bit_le
 
-/*
- * persistent object allocator cache
+/**
+ * struct nilfs_bh_assoc - block offset and buffer head association
+ * @blkoff: block offset
+ * @bh: buffer head
  */
-
 struct nilfs_bh_assoc {
        unsigned long blkoff;
        struct buffer_head *bh;
 };
 
+/**
+ * struct nilfs_palloc_cache - persistent object allocator cache
+ * @lock: cache protecting lock
+ * @prev_desc: blockgroup descriptors cache
+ * @prev_bitmap: blockgroup bitmap cache
+ * @prev_entry: translation entries cache
+ */
 struct nilfs_palloc_cache {
        spinlock_t lock;
        struct nilfs_bh_assoc prev_desc;
index 40d9f453d31c121d29dc09279bfa57ece7d79d7d..b89e68076adc262b737c44a0b04104f56a790bd1 100644 (file)
@@ -135,6 +135,13 @@ struct nilfs_bmap {
 /* state */
 #define NILFS_BMAP_DIRTY       0x00000001
 
+/**
+ * struct nilfs_bmap_store - shadow copy of bmap state
+ * @data: cached raw block mapping of on-disk inode
+ * @last_allocated_key: cached value of last allocated key for data block
+ * @last_allocated_ptr: cached value of last allocated ptr for data block
+ * @state: cached value of state field of bmap structure
+ */
 struct nilfs_bmap_store {
        __le64 data[NILFS_BMAP_SIZE / sizeof(__le64)];
        __u64 last_allocated_key;
index 3a4dd2d8d3fc9fe47fceec0aed1a0e09837449e4..d876b565ce64863812da3d780a7e25be76c0e531 100644 (file)
 #include <linux/fs.h>
 #include <linux/backing-dev.h>
 
-
+/**
+ * struct nilfs_btnode_chkey_ctxt - change key context
+ * @oldkey: old key of block's moving content
+ * @newkey: new key for block's content
+ * @bh: buffer head of old buffer
+ * @newbh: buffer head of new buffer
+ */
 struct nilfs_btnode_chkey_ctxt {
        __u64 oldkey;
        __u64 newkey;
index dab5c4c6dfaf1d13079bc5a088cbb5f379a6cb1d..deaa3d33a0aafa7a6cc303a263288784d03e002f 100644 (file)
@@ -286,7 +286,7 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
        __u64 cno;
        void *kaddr;
        unsigned long tnicps;
-       int ret, ncps, nicps, count, i;
+       int ret, ncps, nicps, nss, count, i;
 
        if (unlikely(start == 0 || start > end)) {
                printk(KERN_ERR "%s: invalid range of checkpoint numbers: "
@@ -301,6 +301,7 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
        if (ret < 0)
                goto out_sem;
        tnicps = 0;
+       nss = 0;
 
        for (cno = start; cno < end; cno += ncps) {
                ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, end);
@@ -318,8 +319,9 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
                        cpfile, cno, cp_bh, kaddr);
                nicps = 0;
                for (i = 0; i < ncps; i++, cp = (void *)cp + cpsz) {
-                       WARN_ON(nilfs_checkpoint_snapshot(cp));
-                       if (!nilfs_checkpoint_invalid(cp)) {
+                       if (nilfs_checkpoint_snapshot(cp)) {
+                               nss++;
+                       } else if (!nilfs_checkpoint_invalid(cp)) {
                                nilfs_checkpoint_set_invalid(cp);
                                nicps++;
                        }
@@ -364,6 +366,8 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
        }
 
        brelse(header_bh);
+       if (nss > 0)
+               ret = -EBUSY;
 
  out_sem:
        up_write(&NILFS_MDT(cpfile)->mi_sem);
index b5c13f3576b935a556334692dac6df65c6bfc031..fa0f80308c2dce70d89bf6e459119c9b18543d27 100644 (file)
 #define NILFS_CNO_MIN  ((__u64)1)
 #define NILFS_CNO_MAX  (~(__u64)0)
 
+/**
+ * struct nilfs_dat_info - on-memory private data of DAT file
+ * @mi: on-memory private data of metadata file
+ * @palloc_cache: persistent object allocator cache of DAT file
+ * @shadow: shadow map of DAT file
+ */
 struct nilfs_dat_info {
        struct nilfs_mdt_info mi;
        struct nilfs_palloc_cache palloc_cache;
index a71cc412b651830184d77368349338a0bb4618f2..19ccbf9522ab3f826af531c328d73b8b4ce5ec42 100644 (file)
@@ -5,6 +5,14 @@
 
 extern const struct export_operations nilfs_export_ops;
 
+/**
+ * struct nilfs_fid - NILFS file id type
+ * @cno: checkpoint number
+ * @ino: inode number
+ * @gen: file generation (version) for NFS
+ * @parent_gen: parent generation (version) for NFS
+ * @parent_ino: parent inode number
+ */
 struct nilfs_fid {
        u64 cno;
        u64 ino;
index 62cebc8e1a1fd49ceec5684de547bd8115eedac2..a4d56ac02e6cf075b81dfaee284175d86776219d 100644 (file)
@@ -69,16 +69,18 @@ static int nilfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
        struct page *page = vmf->page;
        struct inode *inode = vma->vm_file->f_dentry->d_inode;
        struct nilfs_transaction_info ti;
-       int ret;
+       int ret = 0;
 
        if (unlikely(nilfs_near_disk_full(inode->i_sb->s_fs_info)))
                return VM_FAULT_SIGBUS; /* -ENOSPC */
 
+       sb_start_pagefault(inode->i_sb);
        lock_page(page);
        if (page->mapping != inode->i_mapping ||
            page_offset(page) >= i_size_read(inode) || !PageUptodate(page)) {
                unlock_page(page);
-               return VM_FAULT_NOPAGE; /* make the VM retry the fault */
+               ret = -EFAULT;  /* make the VM retry the fault */
+               goto out;
        }
 
        /*
@@ -112,19 +114,21 @@ static int nilfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
        ret = nilfs_transaction_begin(inode->i_sb, &ti, 1);
        /* never returns -ENOMEM, but may return -ENOSPC */
        if (unlikely(ret))
-               return VM_FAULT_SIGBUS;
+               goto out;
 
-       ret = block_page_mkwrite(vma, vmf, nilfs_get_block);
-       if (ret != VM_FAULT_LOCKED) {
+       ret = __block_page_mkwrite(vma, vmf, nilfs_get_block);
+       if (ret) {
                nilfs_transaction_abort(inode->i_sb);
-               return ret;
+               goto out;
        }
        nilfs_set_file_dirty(inode, 1 << (PAGE_SHIFT - inode->i_blkbits));
        nilfs_transaction_commit(inode->i_sb);
 
  mapped:
        wait_on_page_writeback(page);
-       return VM_FAULT_LOCKED;
+ out:
+       sb_end_pagefault(inode->i_sb);
+       return block_page_mkwrite_return(ret);
 }
 
 static const struct vm_operations_struct nilfs_file_vm_ops = {
index 5a48df79d6742caad1bdfc73d2ffa5760a3eaeba..d8e65bde083c0c46fb157d321d8ed63a6a6e0ad0 100644 (file)
 #include "alloc.h"
 #include "ifile.h"
 
-
+/**
+ * struct nilfs_ifile_info - on-memory private data of ifile
+ * @mi: on-memory private data of metadata file
+ * @palloc_cache: persistent object allocator cache of ifile
+ */
 struct nilfs_ifile_info {
        struct nilfs_mdt_info mi;
        struct nilfs_palloc_cache palloc_cache;
index 7cc64465ec2699960f045f37cd29bab8931c76ff..6e2c3db976b2a8d4b2d5bb6fdd877660fb86b0b7 100644 (file)
 #include "cpfile.h"
 #include "ifile.h"
 
+/**
+ * struct nilfs_iget_args - arguments used during comparison between inodes
+ * @ino: inode number
+ * @cno: checkpoint number
+ * @root: pointer on NILFS root object (mounted checkpoint)
+ * @for_gc: inode for GC flag
+ */
 struct nilfs_iget_args {
        u64 ino;
        __u64 cno;
index 06658caa18bd229ab42e01b876538efaf2c48882..fdb180769485f95c78a0a5b5f33d5752baf0590f 100644 (file)
@@ -182,7 +182,7 @@ static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
        if (copy_from_user(&cpmode, argp, sizeof(cpmode)))
                goto out;
 
-       down_read(&inode->i_sb->s_umount);
+       mutex_lock(&nilfs->ns_snapshot_mount_mutex);
 
        nilfs_transaction_begin(inode->i_sb, &ti, 0);
        ret = nilfs_cpfile_change_cpmode(
@@ -192,7 +192,7 @@ static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
        else
                nilfs_transaction_commit(inode->i_sb); /* never fails */
 
-       up_read(&inode->i_sb->s_umount);
+       mutex_unlock(&nilfs->ns_snapshot_mount_mutex);
 out:
        mnt_drop_write_file(filp);
        return ret;
@@ -660,8 +660,6 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
                goto out_free;
        }
 
-       vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
-
        ret = nilfs_ioctl_move_blocks(inode->i_sb, &argv[0], kbufs[0]);
        if (ret < 0)
                printk(KERN_ERR "NILFS: GC failed during preparation: "
index ab20a4baa50fa3a41ea735fe891848f8abfb581f..ab172e8549c57ce5ca20cfe4d7e215f6c41dd679 100644 (file)
 #include "nilfs.h"
 #include "page.h"
 
+/**
+ * struct nilfs_shadow_map - shadow mapping of meta data file
+ * @bmap_store: shadow copy of bmap state
+ * @frozen_data: shadowed dirty data pages
+ * @frozen_btnodes: shadowed dirty b-tree nodes' pages
+ * @frozen_buffers: list of frozen buffers
+ */
 struct nilfs_shadow_map {
        struct nilfs_bmap_store bmap_store;
        struct address_space frozen_data;
index 250add84da768e0a66118edd3e246e95bcb2e628..74cece80e9a32c302e8a7f433234d4b196914adf 100644 (file)
 #include "the_nilfs.h"
 #include "bmap.h"
 
-/*
- * nilfs inode data in memory
+/**
+ * struct nilfs_inode_info - nilfs inode data in memory
+ * @i_flags: inode flags
+ * @i_state: dynamic state flags
+ * @i_bmap: pointer on i_bmap_data
+ * @i_bmap_data: raw block mapping
+ * @i_xattr: <TODO>
+ * @i_dir_start_lookup: page index of last successful search
+ * @i_cno: checkpoint number for GC inode
+ * @i_btnode_cache: cached pages of b-tree nodes
+ * @i_dirty: list for connecting dirty files
+ * @xattr_sem: semaphore for extended attributes processing
+ * @i_bh: buffer contains disk inode
+ * @i_root: root object of the current filesystem tree
+ * @vfs_inode: VFS inode object
  */
 struct nilfs_inode_info {
        __u32 i_flags;
index 88e11fb346b6d0fd6e81fb03994fe18cac514bb7..a5752a589932d936b12cfdda27a6fb718e6cfb51 100644 (file)
@@ -189,7 +189,7 @@ int nilfs_transaction_begin(struct super_block *sb,
        if (ret > 0)
                return 0;
 
-       vfs_check_frozen(sb, SB_FREEZE_WRITE);
+       sb_start_intwrite(sb);
 
        nilfs = sb->s_fs_info;
        down_read(&nilfs->ns_segctor_sem);
@@ -205,6 +205,7 @@ int nilfs_transaction_begin(struct super_block *sb,
        current->journal_info = ti->ti_save;
        if (ti->ti_flags & NILFS_TI_DYNAMIC_ALLOC)
                kmem_cache_free(nilfs_transaction_cachep, ti);
+       sb_end_intwrite(sb);
        return ret;
 }
 
@@ -246,6 +247,7 @@ int nilfs_transaction_commit(struct super_block *sb)
                err = nilfs_construct_segment(sb);
        if (ti->ti_flags & NILFS_TI_DYNAMIC_ALLOC)
                kmem_cache_free(nilfs_transaction_cachep, ti);
+       sb_end_intwrite(sb);
        return err;
 }
 
@@ -264,6 +266,7 @@ void nilfs_transaction_abort(struct super_block *sb)
        current->journal_info = ti->ti_save;
        if (ti->ti_flags & NILFS_TI_DYNAMIC_ALLOC)
                kmem_cache_free(nilfs_transaction_cachep, ti);
+       sb_end_intwrite(sb);
 }
 
 void nilfs_relax_pressure_in_lock(struct super_block *sb)
index c5b7653a43910414007a954181d88f789b687158..3127e9f438a7c22e7e00d54288209bf0c8c02659 100644 (file)
 #include "mdt.h"
 #include "sufile.h"
 
-
+/**
+ * struct nilfs_sufile_info - on-memory private data of sufile
+ * @mi: on-memory private data of metadata file
+ * @ncleansegs: number of clean segments
+ * @allocmin: lower limit of allocatable segment range
+ * @allocmax: upper limit of allocatable segment range
+ */
 struct nilfs_sufile_info {
        struct nilfs_mdt_info mi;
        unsigned long ncleansegs;/* number of clean segments */
index d57c42f974ea3ccb78dfccccc34cbbedf9603004..6522cac6057c900fccdac3138475ea4c0aa0a830 100644 (file)
@@ -677,7 +677,6 @@ static const struct super_operations nilfs_sops = {
        .destroy_inode  = nilfs_destroy_inode,
        .dirty_inode    = nilfs_dirty_inode,
        /* .write_inode    = nilfs_write_inode, */
-       /* .put_inode      = nilfs_put_inode, */
        /* .drop_inode    = nilfs_drop_inode, */
        .evict_inode    = nilfs_evict_inode,
        .put_super      = nilfs_put_super,
@@ -685,8 +684,6 @@ static const struct super_operations nilfs_sops = {
        .sync_fs        = nilfs_sync_fs,
        .freeze_fs      = nilfs_freeze,
        .unfreeze_fs    = nilfs_unfreeze,
-       /* .write_super_lockfs */
-       /* .unlockfs */
        .statfs         = nilfs_statfs,
        .remount_fs     = nilfs_remount,
        /* .umount_begin */
@@ -948,6 +945,8 @@ static int nilfs_attach_snapshot(struct super_block *s, __u64 cno,
        struct nilfs_root *root;
        int ret;
 
+       mutex_lock(&nilfs->ns_snapshot_mount_mutex);
+
        down_read(&nilfs->ns_segctor_sem);
        ret = nilfs_cpfile_is_snapshot(nilfs->ns_cpfile, cno);
        up_read(&nilfs->ns_segctor_sem);
@@ -972,6 +971,7 @@ static int nilfs_attach_snapshot(struct super_block *s, __u64 cno,
        ret = nilfs_get_root_dentry(s, root, root_dentry);
        nilfs_put_root(root);
  out:
+       mutex_unlock(&nilfs->ns_snapshot_mount_mutex);
        return ret;
 }
 
index 501b7f8b739fc2ed8079c0bc664481933d8d9d74..41e6a04a561f36ac1bd190b80dd2c10def4cf176 100644 (file)
@@ -76,6 +76,7 @@ struct the_nilfs *alloc_nilfs(struct block_device *bdev)
        nilfs->ns_bdev = bdev;
        atomic_set(&nilfs->ns_ndirtyblks, 0);
        init_rwsem(&nilfs->ns_sem);
+       mutex_init(&nilfs->ns_snapshot_mount_mutex);
        INIT_LIST_HEAD(&nilfs->ns_dirty_files);
        INIT_LIST_HEAD(&nilfs->ns_gc_inodes);
        spin_lock_init(&nilfs->ns_inode_lock);
index 9992b11312ff5918b9509eabfdb0cab1ecbecd94..6eee4177807bf611874208c09fce9440979fbc4f 100644 (file)
@@ -47,11 +47,13 @@ enum {
  * @ns_flags: flags
  * @ns_bdev: block device
  * @ns_sem: semaphore for shared states
+ * @ns_snapshot_mount_mutex: mutex to protect snapshot mounts
  * @ns_sbh: buffer heads of on-disk super blocks
  * @ns_sbp: pointers to super block data
  * @ns_sbwtime: previous write time of super block
  * @ns_sbwcount: write count of super block
  * @ns_sbsize: size of valid data in super block
+ * @ns_mount_state: file system state
  * @ns_seg_seq: segment sequence counter
  * @ns_segnum: index number of the latest full segment.
  * @ns_nextnum: index number of the full segment index to be used next
@@ -99,6 +101,7 @@ struct the_nilfs {
 
        struct block_device    *ns_bdev;
        struct rw_semaphore     ns_sem;
+       struct mutex            ns_snapshot_mount_mutex;
 
        /*
         * used for
@@ -229,9 +232,8 @@ THE_NILFS_FNS(SB_DIRTY, sb_dirty)
  * @count: refcount of this structure
  * @nilfs: nilfs object
  * @ifile: inode file
- * @root: root inode
  * @inodes_count: number of inodes
- * @blocks_count: number of blocks (Reserved)
+ * @blocks_count: number of blocks
  */
 struct nilfs_root {
        __u64 cno;
index 7389d2d5e51d257c72f9fb0c1468c38a28b309e4..1ecf46448f858d477c040e313989cc9116315124 100644 (file)
@@ -2084,7 +2084,6 @@ static ssize_t ntfs_file_aio_write_nolock(struct kiocb *iocb,
        if (err)
                return err;
        pos = *ppos;
-       vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
        /* We can write back this queue in page reclaim. */
        current->backing_dev_info = mapping->backing_dev_info;
        written = 0;
@@ -2119,6 +2118,7 @@ static ssize_t ntfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
 
        BUG_ON(iocb->ki_pos != pos);
 
+       sb_start_write(inode->i_sb);
        mutex_lock(&inode->i_mutex);
        ret = ntfs_file_aio_write_nolock(iocb, iov, nr_segs, &iocb->ki_pos);
        mutex_unlock(&inode->i_mutex);
@@ -2127,6 +2127,7 @@ static ssize_t ntfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
                if (err < 0)
                        ret = err;
        }
+       sb_end_write(inode->i_sb);
        return ret;
 }
 
index b341492542ca9c8319cb4dc33f4cbdd088ab3242..2bc149d6a784e74ba485216d888ccd95f12e9516 100644 (file)
@@ -2660,31 +2660,14 @@ static const struct super_operations ntfs_sops = {
        .alloc_inode    = ntfs_alloc_big_inode,   /* VFS: Allocate new inode. */
        .destroy_inode  = ntfs_destroy_big_inode, /* VFS: Deallocate inode. */
 #ifdef NTFS_RW
-       //.dirty_inode  = NULL,                 /* VFS: Called from
-       //                                         __mark_inode_dirty(). */
        .write_inode    = ntfs_write_inode,     /* VFS: Write dirty inode to
                                                   disk. */
-       //.drop_inode   = NULL,                 /* VFS: Called just after the
-       //                                         inode reference count has
-       //                                         been decreased to zero.
-       //                                         NOTE: The inode lock is
-       //                                         held. See fs/inode.c::
-       //                                         generic_drop_inode(). */
-       //.delete_inode = NULL,                 /* VFS: Delete inode from disk.
-       //                                         Called when i_count becomes
-       //                                         0 and i_nlink is also 0. */
-       //.write_super  = NULL,                 /* Flush dirty super block to
-       //                                         disk. */
-       //.sync_fs      = NULL,                 /* ? */
-       //.write_super_lockfs   = NULL,         /* ? */
-       //.unlockfs     = NULL,                 /* ? */
 #endif /* NTFS_RW */
        .put_super      = ntfs_put_super,       /* Syscall: umount. */
        .statfs         = ntfs_statfs,          /* Syscall: statfs */
        .remount_fs     = ntfs_remount,         /* Syscall: mount -o remount. */
        .evict_inode    = ntfs_evict_big_inode, /* VFS: Called when an inode is
                                                   removed from memory. */
-       //.umount_begin = NULL,                 /* Forced umount. */
        .show_options   = ntfs_show_options,    /* Show mount options in
                                                   proc. */
 };
index 7602783d7f41c9a01827f16446da43e036e6a7cb..46a1f6d7510405bf14a0dbc837d2585cfd64e75d 100644 (file)
@@ -1971,6 +1971,7 @@ int ocfs2_change_file_space(struct file *file, unsigned int cmd,
 {
        struct inode *inode = file->f_path.dentry->d_inode;
        struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       int ret;
 
        if ((cmd == OCFS2_IOC_RESVSP || cmd == OCFS2_IOC_RESVSP64) &&
            !ocfs2_writes_unwritten_extents(osb))
@@ -1985,7 +1986,12 @@ int ocfs2_change_file_space(struct file *file, unsigned int cmd,
        if (!(file->f_mode & FMODE_WRITE))
                return -EBADF;
 
-       return __ocfs2_change_file_space(file, inode, file->f_pos, cmd, sr, 0);
+       ret = mnt_want_write_file(file);
+       if (ret)
+               return ret;
+       ret = __ocfs2_change_file_space(file, inode, file->f_pos, cmd, sr, 0);
+       mnt_drop_write_file(file);
+       return ret;
 }
 
 static long ocfs2_fallocate(struct file *file, int mode, loff_t offset,
@@ -2261,7 +2267,7 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb,
        if (iocb->ki_left == 0)
                return 0;
 
-       vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+       sb_start_write(inode->i_sb);
 
        appending = file->f_flags & O_APPEND ? 1 : 0;
        direct_io = file->f_flags & O_DIRECT ? 1 : 0;
@@ -2436,6 +2442,7 @@ out_sems:
                ocfs2_iocb_clear_sem_locked(iocb);
 
        mutex_unlock(&inode->i_mutex);
+       sb_end_write(inode->i_sb);
 
        if (written)
                ret = written;
index d96f7f81d8dd3257f49bb02885db296af22881cb..f20edcbfe700c83a12bb90b697535ea7a4435e98 100644 (file)
@@ -928,7 +928,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                if (get_user(new_clusters, (int __user *)arg))
                        return -EFAULT;
 
-               return ocfs2_group_extend(inode, new_clusters);
+               status = mnt_want_write_file(filp);
+               if (status)
+                       return status;
+               status = ocfs2_group_extend(inode, new_clusters);
+               mnt_drop_write_file(filp);
+               return status;
        case OCFS2_IOC_GROUP_ADD:
        case OCFS2_IOC_GROUP_ADD64:
                if (!capable(CAP_SYS_RESOURCE))
@@ -937,7 +942,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                if (copy_from_user(&input, (int __user *) arg, sizeof(input)))
                        return -EFAULT;
 
-               return ocfs2_group_add(inode, &input);
+               status = mnt_want_write_file(filp);
+               if (status)
+                       return status;
+               status = ocfs2_group_add(inode, &input);
+               mnt_drop_write_file(filp);
+               return status;
        case OCFS2_IOC_REFLINK:
                if (copy_from_user(&args, argp, sizeof(args)))
                        return -EFAULT;
index 0a42ae96dca7d4a0f662505e0e51206895ce4156..2dd36af79e262f8de2051525e1baf8585786cba8 100644 (file)
@@ -355,11 +355,14 @@ handle_t *ocfs2_start_trans(struct ocfs2_super *osb, int max_buffs)
        if (journal_current_handle())
                return jbd2_journal_start(journal, max_buffs);
 
+       sb_start_intwrite(osb->sb);
+
        down_read(&osb->journal->j_trans_barrier);
 
        handle = jbd2_journal_start(journal, max_buffs);
        if (IS_ERR(handle)) {
                up_read(&osb->journal->j_trans_barrier);
+               sb_end_intwrite(osb->sb);
 
                mlog_errno(PTR_ERR(handle));
 
@@ -388,8 +391,10 @@ int ocfs2_commit_trans(struct ocfs2_super *osb,
        if (ret < 0)
                mlog_errno(ret);
 
-       if (!nested)
+       if (!nested) {
                up_read(&journal->j_trans_barrier);
+               sb_end_intwrite(osb->sb);
+       }
 
        return ret;
 }
index 210c35237548be82f9d99966beb166cdd2ffc87d..a9f78c74d687ed5845587ac619081653f7e510b7 100644 (file)
@@ -784,14 +784,10 @@ bail:
 
 static u32 ocfs2_local_alloc_count_bits(struct ocfs2_dinode *alloc)
 {
-       int i;
-       u8 *buffer;
-       u32 count = 0;
+       u32 count;
        struct ocfs2_local_alloc *la = OCFS2_LOCAL_ALLOC(alloc);
 
-       buffer = la->la_bitmap;
-       for (i = 0; i < le16_to_cpu(la->la_size); i++)
-               count += hweight8(buffer[i]);
+       count = memweight(la->la_bitmap, le16_to_cpu(la->la_size));
 
        trace_ocfs2_local_alloc_count_bits(count);
        return count;
index 9cd41083e99123eca1c48085fb39809e6b906b40..d150372fd81da7967eb3a8f3d2a3b0d9c8c41c82 100644 (file)
@@ -136,6 +136,7 @@ static int ocfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
        sigset_t oldset;
        int ret;
 
+       sb_start_pagefault(inode->i_sb);
        ocfs2_block_signals(&oldset);
 
        /*
@@ -165,6 +166,7 @@ static int ocfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
 
 out:
        ocfs2_unblock_signals(&oldset);
+       sb_end_pagefault(inode->i_sb);
        return ret;
 }
 
index 9f32d7cbb7a3701f1f28d938b17ae4d057dc4e84..30a055049e1662eb382e3ec328918e2cd143c98f 100644 (file)
@@ -4466,20 +4466,11 @@ int ocfs2_reflink_ioctl(struct inode *inode,
                goto out_dput;
        }
 
-       error = mnt_want_write(new_path.mnt);
-       if (error) {
-               mlog_errno(error);
-               goto out_dput;
-       }
-
        error = ocfs2_vfs_reflink(old_path.dentry,
                                  new_path.dentry->d_inode,
                                  new_dentry, preserve);
-       mnt_drop_write(new_path.mnt);
 out_dput:
-       dput(new_dentry);
-       mutex_unlock(&new_path.dentry->d_inode->i_mutex);
-       path_put(&new_path);
+       done_path_create(&new_path, new_dentry);
 out:
        path_put(&old_path);
 
index 1e914b397e129f311875eb15d0c7679ace93aa3e..f3d96e7e7b19d4254924a59dfc4121aa5d8635b4 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -164,11 +164,13 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
        if (IS_APPEND(inode))
                goto out_putf;
 
+       sb_start_write(inode->i_sb);
        error = locks_verify_truncate(inode, file, length);
        if (!error)
                error = security_path_truncate(&file->f_path);
        if (!error)
                error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file);
+       sb_end_write(inode->i_sb);
 out_putf:
        fput(file);
 out:
@@ -266,7 +268,10 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
        if (!file->f_op->fallocate)
                return -EOPNOTSUPP;
 
-       return file->f_op->fallocate(file, mode, offset, len);
+       sb_start_write(inode->i_sb);
+       ret = file->f_op->fallocate(file, mode, offset, len);
+       sb_end_write(inode->i_sb);
+       return ret;
 }
 
 SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len)
@@ -620,7 +625,7 @@ static inline int __get_file_write_access(struct inode *inode,
                /*
                 * Balanced in __fput()
                 */
-               error = mnt_want_write(mnt);
+               error = __mnt_want_write(mnt);
                if (error)
                        put_write_access(inode);
        }
@@ -654,6 +659,7 @@ static int do_dentry_open(struct file *f,
        if (unlikely(f->f_flags & O_PATH))
                f->f_mode = FMODE_PATH;
 
+       path_get(&f->f_path);
        inode = f->f_path.dentry->d_inode;
        if (f->f_mode & FMODE_WRITE) {
                error = __get_file_write_access(inode, f->f_path.mnt);
@@ -739,9 +745,7 @@ int finish_open(struct file *file, struct dentry *dentry,
        int error;
        BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */
 
-       mntget(file->f_path.mnt);
-       file->f_path.dentry = dget(dentry);
-
+       file->f_path.dentry = dentry;
        error = do_dentry_open(file, open, current_cred());
        if (!error)
                *opened |= FILE_OPENED;
@@ -784,7 +788,6 @@ struct file *dentry_open(const struct path *path, int flags,
 
        f->f_flags = flags;
        f->f_path = *path;
-       path_get(&f->f_path);
        error = do_dentry_open(f, NULL, cred);
        if (!error) {
                error = open_check_o_direct(f);
index 95cbd6b227e6fb05735126a00296bb7230224429..8d85d7068c1e8a0f028a7a72dba9c04c24e3ee91 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -1016,18 +1016,16 @@ fail_inode:
        return NULL;
 }
 
-struct file *create_write_pipe(int flags)
+int create_pipe_files(struct file **res, int flags)
 {
        int err;
-       struct inode *inode;
+       struct inode *inode = get_pipe_inode();
        struct file *f;
        struct path path;
-       struct qstr name = { .name = "" };
+       static struct qstr name = { .name = "" };
 
-       err = -ENFILE;
-       inode = get_pipe_inode();
        if (!inode)
-               goto err;
+               return -ENFILE;
 
        err = -ENOMEM;
        path.dentry = d_alloc_pseudo(pipe_mnt->mnt_sb, &name);
@@ -1041,62 +1039,43 @@ struct file *create_write_pipe(int flags)
        f = alloc_file(&path, FMODE_WRITE, &write_pipefifo_fops);
        if (!f)
                goto err_dentry;
-       f->f_mapping = inode->i_mapping;
 
        f->f_flags = O_WRONLY | (flags & (O_NONBLOCK | O_DIRECT));
-       f->f_version = 0;
 
-       return f;
+       res[0] = alloc_file(&path, FMODE_READ, &read_pipefifo_fops);
+       if (!res[0])
+               goto err_file;
+
+       path_get(&path);
+       res[0]->f_flags = O_RDONLY | (flags & O_NONBLOCK);
+       res[1] = f;
+       return 0;
 
- err_dentry:
+err_file:
+       put_filp(f);
+err_dentry:
        free_pipe_info(inode);
        path_put(&path);
-       return ERR_PTR(err);
+       return err;
 
- err_inode:
+err_inode:
        free_pipe_info(inode);
        iput(inode);
- err:
-       return ERR_PTR(err);
-}
-
-void free_write_pipe(struct file *f)
-{
-       free_pipe_info(f->f_dentry->d_inode);
-       path_put(&f->f_path);
-       put_filp(f);
-}
-
-struct file *create_read_pipe(struct file *wrf, int flags)
-{
-       /* Grab pipe from the writer */
-       struct file *f = alloc_file(&wrf->f_path, FMODE_READ,
-                                   &read_pipefifo_fops);
-       if (!f)
-               return ERR_PTR(-ENFILE);
-
-       path_get(&wrf->f_path);
-       f->f_flags = O_RDONLY | (flags & O_NONBLOCK);
-
-       return f;
+       return err;
 }
 
 int do_pipe_flags(int *fd, int flags)
 {
-       struct file *fw, *fr;
+       struct file *files[2];
        int error;
        int fdw, fdr;
 
        if (flags & ~(O_CLOEXEC | O_NONBLOCK | O_DIRECT))
                return -EINVAL;
 
-       fw = create_write_pipe(flags);
-       if (IS_ERR(fw))
-               return PTR_ERR(fw);
-       fr = create_read_pipe(fw, flags);
-       error = PTR_ERR(fr);
-       if (IS_ERR(fr))
-               goto err_write_pipe;
+       error = create_pipe_files(files, flags);
+       if (error)
+               return error;
 
        error = get_unused_fd_flags(flags);
        if (error < 0)
@@ -1109,8 +1088,8 @@ int do_pipe_flags(int *fd, int flags)
        fdw = error;
 
        audit_fd_pair(fdr, fdw);
-       fd_install(fdr, fr);
-       fd_install(fdw, fw);
+       fd_install(fdr, files[0]);
+       fd_install(fdw, files[1]);
        fd[0] = fdr;
        fd[1] = fdw;
 
@@ -1119,10 +1098,8 @@ int do_pipe_flags(int *fd, int flags)
  err_fdr:
        put_unused_fd(fdr);
  err_read_pipe:
-       path_put(&fr->f_path);
-       put_filp(fr);
- err_write_pipe:
-       free_write_pipe(fw);
+       fput(files[0]);
+       fput(files[1]);
        return error;
 }
 
index 2772208338f811c6b5d5bc067c490fe8725ba3cb..1b6c84cbdb732e5684ccaa823548b8780cf1c16d 100644 (file)
@@ -695,8 +695,6 @@ static int __mem_open(struct inode *inode, struct file *file, unsigned int mode)
                mmput(mm);
        }
 
-       /* OK to pass negative loff_t, we can catch out-of-range */
-       file->f_mode |= FMODE_UNSIGNED_OFFSET;
        file->private_data = mm;
 
        return 0;
@@ -704,7 +702,12 @@ static int __mem_open(struct inode *inode, struct file *file, unsigned int mode)
 
 static int mem_open(struct inode *inode, struct file *file)
 {
-       return __mem_open(inode, file, PTRACE_MODE_ATTACH);
+       int ret = __mem_open(inode, file, PTRACE_MODE_ATTACH);
+
+       /* OK to pass negative loff_t, we can catch out-of-range */
+       file->f_mode |= FMODE_UNSIGNED_OFFSET;
+
+       return ret;
 }
 
 static ssize_t mem_rw(struct file *file, char __user *buf,
@@ -827,15 +830,16 @@ static ssize_t environ_read(struct file *file, char __user *buf,
        if (!atomic_inc_not_zero(&mm->mm_users))
                goto free;
        while (count > 0) {
-               int this_len, retval, max_len;
-
-               this_len = mm->env_end - (mm->env_start + src);
+               size_t this_len, max_len;
+               int retval;
 
-               if (this_len <= 0)
+               if (src >= (mm->env_end - mm->env_start))
                        break;
 
-               max_len = (count > PAGE_SIZE) ? PAGE_SIZE : count;
-               this_len = (this_len > max_len) ? max_len : this_len;
+               this_len = mm->env_end - (mm->env_start + src);
+
+               max_len = min_t(size_t, PAGE_SIZE, count);
+               this_len = min(max_len, this_len);
 
                retval = access_remote_vm(mm, (mm->env_start + src),
                        page, this_len, 0);
index 22e0d60e53efbe3072ade881ec69251062df83c9..76a7a697b778d7918e5db75bf5a1dd744a073a5d 100644 (file)
 #include <linux/bitops.h>
 #include "qnx4.h"
 
-static void count_bits(register const char *bmPart, register int size,
-                      int *const tf)
-{
-       char b;
-       int tot = *tf;
-
-       if (size > QNX4_BLOCK_SIZE) {
-               size = QNX4_BLOCK_SIZE;
-       }
-       do {
-               b = *bmPart++;
-               tot += 8 - hweight8(b);
-               size--;
-       } while (size != 0);
-       *tf = tot;
-}
-
 unsigned long qnx4_count_free_blocks(struct super_block *sb)
 {
        int start = le32_to_cpu(qnx4_sb(sb)->BitMap->di_first_xtnt.xtnt_blk) - 1;
@@ -44,13 +27,16 @@ unsigned long qnx4_count_free_blocks(struct super_block *sb)
        struct buffer_head *bh;
 
        while (total < size) {
+               int bytes = min(size - total, QNX4_BLOCK_SIZE);
+
                if ((bh = sb_bread(sb, start + offset)) == NULL) {
                        printk(KERN_ERR "qnx4: I/O error in counting free blocks\n");
                        break;
                }
-               count_bits(bh->b_data, size - total, &total_free);
+               total_free += bytes * BITS_PER_BYTE -
+                               memweight(bh->b_data, bytes);
                brelse(bh);
-               total += QNX4_BLOCK_SIZE;
+               total += bytes;
                offset++;
        }
 
index 7bf08fa22ec9ab122bad5438a02bd78db6f1e885..41514dd89462d73d4d1c7b8a68e431d5cabc08a9 100644 (file)
@@ -996,6 +996,8 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
        };
        ssize_t ret;
 
+       sb_start_write(inode->i_sb);
+
        pipe_lock(pipe);
 
        splice_from_pipe_begin(&sd);
@@ -1034,6 +1036,7 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
                        *ppos += ret;
                balance_dirty_pages_ratelimited_nr(mapping, nr_pages);
        }
+       sb_end_write(inode->i_sb);
 
        return ret;
 }
index c743fb3be4b8a45d8d99f9b757bfc44bbb949683..b05cf47463d0c2347e8eca1597001f96dd2a97fc 100644 (file)
 #include <linux/rculist_bl.h>
 #include <linux/cleancache.h>
 #include <linux/fsnotify.h>
+#include <linux/lockdep.h>
 #include "internal.h"
 
 
 LIST_HEAD(super_blocks);
 DEFINE_SPINLOCK(sb_lock);
 
+static char *sb_writers_name[SB_FREEZE_LEVELS] = {
+       "sb_writers",
+       "sb_pagefaults",
+       "sb_internal",
+};
+
 /*
  * One thing we have to be careful of with a per-sb shrinker is that we don't
  * drop the last active reference to the superblock from within the shrinker.
@@ -62,7 +69,7 @@ static int prune_super(struct shrinker *shrink, struct shrink_control *sc)
                return -1;
 
        if (!grab_super_passive(sb))
-               return !sc->nr_to_scan ? 0 : -1;
+               return -1;
 
        if (sb->s_op && sb->s_op->nr_cached_objects)
                fs_objects = sb->s_op->nr_cached_objects(sb);
@@ -102,6 +109,35 @@ static int prune_super(struct shrinker *shrink, struct shrink_control *sc)
        return total_objects;
 }
 
+static int init_sb_writers(struct super_block *s, struct file_system_type *type)
+{
+       int err;
+       int i;
+
+       for (i = 0; i < SB_FREEZE_LEVELS; i++) {
+               err = percpu_counter_init(&s->s_writers.counter[i], 0);
+               if (err < 0)
+                       goto err_out;
+               lockdep_init_map(&s->s_writers.lock_map[i], sb_writers_name[i],
+                                &type->s_writers_key[i], 0);
+       }
+       init_waitqueue_head(&s->s_writers.wait);
+       init_waitqueue_head(&s->s_writers.wait_unfrozen);
+       return 0;
+err_out:
+       while (--i >= 0)
+               percpu_counter_destroy(&s->s_writers.counter[i]);
+       return err;
+}
+
+static void destroy_sb_writers(struct super_block *s)
+{
+       int i;
+
+       for (i = 0; i < SB_FREEZE_LEVELS; i++)
+               percpu_counter_destroy(&s->s_writers.counter[i]);
+}
+
 /**
  *     alloc_super     -       create new superblock
  *     @type:  filesystem type superblock should belong to
@@ -117,18 +153,19 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags)
 
        if (s) {
                if (security_sb_alloc(s)) {
+                       /*
+                        * We cannot call security_sb_free() without
+                        * security_sb_alloc() succeeding. So bail out manually
+                        */
                        kfree(s);
                        s = NULL;
                        goto out;
                }
 #ifdef CONFIG_SMP
                s->s_files = alloc_percpu(struct list_head);
-               if (!s->s_files) {
-                       security_sb_free(s);
-                       kfree(s);
-                       s = NULL;
-                       goto out;
-               } else {
+               if (!s->s_files)
+                       goto err_out;
+               else {
                        int i;
 
                        for_each_possible_cpu(i)
@@ -137,6 +174,8 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags)
 #else
                INIT_LIST_HEAD(&s->s_files);
 #endif
+               if (init_sb_writers(s, type))
+                       goto err_out;
                s->s_flags = flags;
                s->s_bdi = &default_backing_dev_info;
                INIT_HLIST_NODE(&s->s_instances);
@@ -178,7 +217,6 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags)
                mutex_init(&s->s_dquot.dqio_mutex);
                mutex_init(&s->s_dquot.dqonoff_mutex);
                init_rwsem(&s->s_dquot.dqptr_sem);
-               init_waitqueue_head(&s->s_wait_unfrozen);
                s->s_maxbytes = MAX_NON_LFS;
                s->s_op = &default_op;
                s->s_time_gran = 1000000000;
@@ -190,6 +228,16 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags)
        }
 out:
        return s;
+err_out:
+       security_sb_free(s);
+#ifdef CONFIG_SMP
+       if (s->s_files)
+               free_percpu(s->s_files);
+#endif
+       destroy_sb_writers(s);
+       kfree(s);
+       s = NULL;
+       goto out;
 }
 
 /**
@@ -203,6 +251,7 @@ static inline void destroy_super(struct super_block *s)
 #ifdef CONFIG_SMP
        free_percpu(s->s_files);
 #endif
+       destroy_sb_writers(s);
        security_sb_free(s);
        WARN_ON(!list_empty(&s->s_mounts));
        kfree(s->s_subtype);
@@ -320,7 +369,7 @@ static int grab_super(struct super_block *s) __releases(sb_lock)
 
 /*
  *     grab_super_passive - acquire a passive reference
- *     @s: reference we are trying to grab
+ *     @sb: reference we are trying to grab
  *
  *     Tries to acquire a passive reference. This is used in places where we
  *     cannot take an active reference but we need to ensure that the
@@ -651,10 +700,11 @@ struct super_block *get_super_thawed(struct block_device *bdev)
 {
        while (1) {
                struct super_block *s = get_super(bdev);
-               if (!s || s->s_frozen == SB_UNFROZEN)
+               if (!s || s->s_writers.frozen == SB_UNFROZEN)
                        return s;
                up_read(&s->s_umount);
-               vfs_check_frozen(s, SB_FREEZE_WRITE);
+               wait_event(s->s_writers.wait_unfrozen,
+                          s->s_writers.frozen == SB_UNFROZEN);
                put_super(s);
        }
 }
@@ -732,7 +782,7 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
        int retval;
        int remount_ro;
 
-       if (sb->s_frozen != SB_UNFROZEN)
+       if (sb->s_writers.frozen != SB_UNFROZEN)
                return -EBUSY;
 
 #ifdef CONFIG_BLOCK
@@ -1163,6 +1213,120 @@ out:
        return ERR_PTR(error);
 }
 
+/*
+ * This is an internal function, please use sb_end_{write,pagefault,intwrite}
+ * instead.
+ */
+void __sb_end_write(struct super_block *sb, int level)
+{
+       percpu_counter_dec(&sb->s_writers.counter[level-1]);
+       /*
+        * Make sure s_writers are updated before we wake up waiters in
+        * freeze_super().
+        */
+       smp_mb();
+       if (waitqueue_active(&sb->s_writers.wait))
+               wake_up(&sb->s_writers.wait);
+       rwsem_release(&sb->s_writers.lock_map[level-1], 1, _RET_IP_);
+}
+EXPORT_SYMBOL(__sb_end_write);
+
+#ifdef CONFIG_LOCKDEP
+/*
+ * We want lockdep to tell us about possible deadlocks with freezing but
+ * it's it bit tricky to properly instrument it. Getting a freeze protection
+ * works as getting a read lock but there are subtle problems. XFS for example
+ * gets freeze protection on internal level twice in some cases, which is OK
+ * only because we already hold a freeze protection also on higher level. Due
+ * to these cases we have to tell lockdep we are doing trylock when we
+ * already hold a freeze protection for a higher freeze level.
+ */
+static void acquire_freeze_lock(struct super_block *sb, int level, bool trylock,
+                               unsigned long ip)
+{
+       int i;
+
+       if (!trylock) {
+               for (i = 0; i < level - 1; i++)
+                       if (lock_is_held(&sb->s_writers.lock_map[i])) {
+                               trylock = true;
+                               break;
+                       }
+       }
+       rwsem_acquire_read(&sb->s_writers.lock_map[level-1], 0, trylock, ip);
+}
+#endif
+
+/*
+ * This is an internal function, please use sb_start_{write,pagefault,intwrite}
+ * instead.
+ */
+int __sb_start_write(struct super_block *sb, int level, bool wait)
+{
+retry:
+       if (unlikely(sb->s_writers.frozen >= level)) {
+               if (!wait)
+                       return 0;
+               wait_event(sb->s_writers.wait_unfrozen,
+                          sb->s_writers.frozen < level);
+       }
+
+#ifdef CONFIG_LOCKDEP
+       acquire_freeze_lock(sb, level, !wait, _RET_IP_);
+#endif
+       percpu_counter_inc(&sb->s_writers.counter[level-1]);
+       /*
+        * Make sure counter is updated before we check for frozen.
+        * freeze_super() first sets frozen and then checks the counter.
+        */
+       smp_mb();
+       if (unlikely(sb->s_writers.frozen >= level)) {
+               __sb_end_write(sb, level);
+               goto retry;
+       }
+       return 1;
+}
+EXPORT_SYMBOL(__sb_start_write);
+
+/**
+ * sb_wait_write - wait until all writers to given file system finish
+ * @sb: the super for which we wait
+ * @level: type of writers we wait for (normal vs page fault)
+ *
+ * This function waits until there are no writers of given type to given file
+ * system. Caller of this function should make sure there can be no new writers
+ * of type @level before calling this function. Otherwise this function can
+ * livelock.
+ */
+static void sb_wait_write(struct super_block *sb, int level)
+{
+       s64 writers;
+
+       /*
+        * We just cycle-through lockdep here so that it does not complain
+        * about returning with lock to userspace
+        */
+       rwsem_acquire(&sb->s_writers.lock_map[level-1], 0, 0, _THIS_IP_);
+       rwsem_release(&sb->s_writers.lock_map[level-1], 1, _THIS_IP_);
+
+       do {
+               DEFINE_WAIT(wait);
+
+               /*
+                * We use a barrier in prepare_to_wait() to separate setting
+                * of frozen and checking of the counter
+                */
+               prepare_to_wait(&sb->s_writers.wait, &wait,
+                               TASK_UNINTERRUPTIBLE);
+
+               writers = percpu_counter_sum(&sb->s_writers.counter[level-1]);
+               if (writers)
+                       schedule();
+
+               finish_wait(&sb->s_writers.wait, &wait);
+       } while (writers);
+}
+
 /**
  * freeze_super - lock the filesystem and force it into a consistent state
  * @sb: the super to lock
@@ -1170,6 +1334,31 @@ out:
  * Syncs the super to make sure the filesystem is consistent and calls the fs's
  * freeze_fs.  Subsequent calls to this without first thawing the fs will return
  * -EBUSY.
+ *
+ * During this function, sb->s_writers.frozen goes through these values:
+ *
+ * SB_UNFROZEN: File system is normal, all writes progress as usual.
+ *
+ * SB_FREEZE_WRITE: The file system is in the process of being frozen.  New
+ * writes should be blocked, though page faults are still allowed. We wait for
+ * all writes to complete and then proceed to the next stage.
+ *
+ * SB_FREEZE_PAGEFAULT: Freezing continues. Now also page faults are blocked
+ * but internal fs threads can still modify the filesystem (although they
+ * should not dirty new pages or inodes), writeback can run etc. After waiting
+ * for all running page faults we sync the filesystem which will clean all
+ * dirty pages and inodes (no new dirty pages or inodes can be created when
+ * sync is running).
+ *
+ * SB_FREEZE_FS: The file system is frozen. Now all internal sources of fs
+ * modification are blocked (e.g. XFS preallocation truncation on inode
+ * reclaim). This is usually implemented by blocking new transactions for
+ * filesystems that have them and need this additional guard. After all
+ * internal writers are finished we call ->freeze_fs() to finish filesystem
+ * freezing. Then we transition to SB_FREEZE_COMPLETE state. This state is
+ * mostly auxiliary for filesystems to verify they do not modify frozen fs.
+ *
+ * sb->s_writers.frozen is protected by sb->s_umount.
  */
 int freeze_super(struct super_block *sb)
 {
@@ -1177,7 +1366,7 @@ int freeze_super(struct super_block *sb)
 
        atomic_inc(&sb->s_active);
        down_write(&sb->s_umount);
-       if (sb->s_frozen) {
+       if (sb->s_writers.frozen != SB_UNFROZEN) {
                deactivate_locked_super(sb);
                return -EBUSY;
        }
@@ -1188,33 +1377,53 @@ int freeze_super(struct super_block *sb)
        }
 
        if (sb->s_flags & MS_RDONLY) {
-               sb->s_frozen = SB_FREEZE_TRANS;
-               smp_wmb();
+               /* Nothing to do really... */
+               sb->s_writers.frozen = SB_FREEZE_COMPLETE;
                up_write(&sb->s_umount);
                return 0;
        }
 
-       sb->s_frozen = SB_FREEZE_WRITE;
+       /* From now on, no new normal writers can start */
+       sb->s_writers.frozen = SB_FREEZE_WRITE;
+       smp_wmb();
+
+       /* Release s_umount to preserve sb_start_write -> s_umount ordering */
+       up_write(&sb->s_umount);
+
+       sb_wait_write(sb, SB_FREEZE_WRITE);
+
+       /* Now we go and block page faults... */
+       down_write(&sb->s_umount);
+       sb->s_writers.frozen = SB_FREEZE_PAGEFAULT;
        smp_wmb();
 
+       sb_wait_write(sb, SB_FREEZE_PAGEFAULT);
+
+       /* All writers are done so after syncing there won't be dirty data */
        sync_filesystem(sb);
 
-       sb->s_frozen = SB_FREEZE_TRANS;
+       /* Now wait for internal filesystem counter */
+       sb->s_writers.frozen = SB_FREEZE_FS;
        smp_wmb();
+       sb_wait_write(sb, SB_FREEZE_FS);
 
-       sync_blockdev(sb->s_bdev);
        if (sb->s_op->freeze_fs) {
                ret = sb->s_op->freeze_fs(sb);
                if (ret) {
                        printk(KERN_ERR
                                "VFS:Filesystem freeze failed\n");
-                       sb->s_frozen = SB_UNFROZEN;
+                       sb->s_writers.frozen = SB_UNFROZEN;
                        smp_wmb();
-                       wake_up(&sb->s_wait_unfrozen);
+                       wake_up(&sb->s_writers.wait_unfrozen);
                        deactivate_locked_super(sb);
                        return ret;
                }
        }
+       /*
+        * This is just for debugging purposes so that fs can warn if it
+        * sees write activity when frozen is set to SB_FREEZE_COMPLETE.
+        */
+       sb->s_writers.frozen = SB_FREEZE_COMPLETE;
        up_write(&sb->s_umount);
        return 0;
 }
@@ -1231,7 +1440,7 @@ int thaw_super(struct super_block *sb)
        int error;
 
        down_write(&sb->s_umount);
-       if (sb->s_frozen == SB_UNFROZEN) {
+       if (sb->s_writers.frozen == SB_UNFROZEN) {
                up_write(&sb->s_umount);
                return -EINVAL;
        }
@@ -1244,16 +1453,15 @@ int thaw_super(struct super_block *sb)
                if (error) {
                        printk(KERN_ERR
                                "VFS:Filesystem thaw failed\n");
-                       sb->s_frozen = SB_FREEZE_TRANS;
                        up_write(&sb->s_umount);
                        return error;
                }
        }
 
 out:
-       sb->s_frozen = SB_UNFROZEN;
+       sb->s_writers.frozen = SB_UNFROZEN;
        smp_wmb();
-       wake_up(&sb->s_wait_unfrozen);
+       wake_up(&sb->s_writers.wait_unfrozen);
        deactivate_locked_super(sb);
 
        return 0;
index a4759833d62d3be4067c816c32ef32e434cad5bd..614b2b544880c002cf650b9510302ede56939921 100644 (file)
@@ -228,6 +228,8 @@ static int bin_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
        ret = 0;
        if (bb->vm_ops->page_mkwrite)
                ret = bb->vm_ops->page_mkwrite(vma, vmf);
+       else
+               file_update_time(file);
 
        sysfs_put_active(attr_sd);
        return ret;
index 1d7ac379045879b827b196f0d7a7420fe33c783d..4d45b7189e7eaa352da0567b8fe4a2d1b738c999 100644 (file)
@@ -427,6 +427,7 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
 {
        ssize_t error;
        void *kvalue = NULL;
+       void *vvalue = NULL;
        char kname[XATTR_NAME_MAX + 1];
 
        error = strncpy_from_user(kname, name, sizeof(kname));
@@ -438,9 +439,13 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
        if (size) {
                if (size > XATTR_SIZE_MAX)
                        size = XATTR_SIZE_MAX;
-               kvalue = kzalloc(size, GFP_KERNEL);
-               if (!kvalue)
-                       return -ENOMEM;
+               kvalue = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
+               if (!kvalue) {
+                       vvalue = vmalloc(size);
+                       if (!vvalue)
+                               return -ENOMEM;
+                       kvalue = vvalue;
+               }
        }
 
        error = vfs_getxattr(d, kname, kvalue, size);
@@ -452,7 +457,10 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
                   than XATTR_SIZE_MAX bytes. Not possible. */
                error = -E2BIG;
        }
-       kfree(kvalue);
+       if (vvalue)
+               vfree(vvalue);
+       else
+               kfree(kvalue);
        return error;
 }
 
index a6caa0022c9bba4f937abaf032ede0761e930a22..359fb86ed8769b6ba5259a5be6959fd40c977b46 100644 (file)
@@ -50,20 +50,6 @@ typedef struct xfs_alloc_rec_incore {
 /* btree pointer type */
 typedef __be32 xfs_alloc_ptr_t;
 
-/*
- * Minimum and maximum blocksize and sectorsize.
- * The blocksize upper limit is pretty much arbitrary.
- * The sectorsize upper limit is due to sizeof(sb_sectsize).
- */
-#define XFS_MIN_BLOCKSIZE_LOG  9       /* i.e. 512 bytes */
-#define XFS_MAX_BLOCKSIZE_LOG  16      /* i.e. 65536 bytes */
-#define XFS_MIN_BLOCKSIZE      (1 << XFS_MIN_BLOCKSIZE_LOG)
-#define XFS_MAX_BLOCKSIZE      (1 << XFS_MAX_BLOCKSIZE_LOG)
-#define XFS_MIN_SECTORSIZE_LOG 9       /* i.e. 512 bytes */
-#define XFS_MAX_SECTORSIZE_LOG 15      /* i.e. 32768 bytes */
-#define XFS_MIN_SECTORSIZE     (1 << XFS_MIN_SECTORSIZE_LOG)
-#define XFS_MAX_SECTORSIZE     (1 << XFS_MAX_SECTORSIZE_LOG)
-
 /*
  * Block numbers in the AG:
  * SB is sector 0, AGF is sector 1, AGI is sector 2, AGFL is sector 3.
index 8dad722c00410f25294f0a8fec608fcbd56e74e8..e562dd43f41fe762eb4d49c7814f1f002fe965c9 100644 (file)
@@ -123,6 +123,12 @@ xfs_setfilesize_trans_alloc(
 
        ioend->io_append_trans = tp;
 
+       /*
+        * We will pass freeze protection with a transaction.  So tell lockdep
+        * we released it.
+        */
+       rwsem_release(&ioend->io_inode->i_sb->s_writers.lock_map[SB_FREEZE_FS-1],
+                     1, _THIS_IP_);
        /*
         * We hand off the transaction to the completion thread now, so
         * clear the flag here.
@@ -179,7 +185,7 @@ xfs_finish_ioend(
        if (atomic_dec_and_test(&ioend->io_remaining)) {
                struct xfs_mount        *mp = XFS_I(ioend->io_inode)->i_mount;
 
-               if (ioend->io_type == IO_UNWRITTEN)
+               if (ioend->io_type == XFS_IO_UNWRITTEN)
                        queue_work(mp->m_unwritten_workqueue, &ioend->io_work);
                else if (ioend->io_append_trans)
                        queue_work(mp->m_data_workqueue, &ioend->io_work);
@@ -199,6 +205,15 @@ xfs_end_io(
        struct xfs_inode *ip = XFS_I(ioend->io_inode);
        int             error = 0;
 
+       if (ioend->io_append_trans) {
+               /*
+                * We've got freeze protection passed with the transaction.
+                * Tell lockdep about it.
+                */
+               rwsem_acquire_read(
+                       &ioend->io_inode->i_sb->s_writers.lock_map[SB_FREEZE_FS-1],
+                       0, 1, _THIS_IP_);
+       }
        if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
                ioend->io_error = -EIO;
                goto done;
@@ -210,7 +225,7 @@ xfs_end_io(
         * For unwritten extents we need to issue transactions to convert a
         * range to normal written extens after the data I/O has finished.
         */
-       if (ioend->io_type == IO_UNWRITTEN) {
+       if (ioend->io_type == XFS_IO_UNWRITTEN) {
                /*
                 * For buffered I/O we never preallocate a transaction when
                 * doing the unwritten extent conversion, but for direct I/O
@@ -312,7 +327,7 @@ xfs_map_blocks(
        if (XFS_FORCED_SHUTDOWN(mp))
                return -XFS_ERROR(EIO);
 
-       if (type == IO_UNWRITTEN)
+       if (type == XFS_IO_UNWRITTEN)
                bmapi_flags |= XFS_BMAPI_IGSTATE;
 
        if (!xfs_ilock_nowait(ip, XFS_ILOCK_SHARED)) {
@@ -323,10 +338,10 @@ xfs_map_blocks(
 
        ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE ||
               (ip->i_df.if_flags & XFS_IFEXTENTS));
-       ASSERT(offset <= mp->m_maxioffset);
+       ASSERT(offset <= mp->m_super->s_maxbytes);
 
-       if (offset + count > mp->m_maxioffset)
-               count = mp->m_maxioffset - offset;
+       if (offset + count > mp->m_super->s_maxbytes)
+               count = mp->m_super->s_maxbytes - offset;
        end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + count);
        offset_fsb = XFS_B_TO_FSBT(mp, offset);
        error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
@@ -336,7 +351,7 @@ xfs_map_blocks(
        if (error)
                return -XFS_ERROR(error);
 
-       if (type == IO_DELALLOC &&
+       if (type == XFS_IO_DELALLOC &&
            (!nimaps || isnullstartblock(imap->br_startblock))) {
                error = xfs_iomap_write_allocate(ip, offset, count, imap);
                if (!error)
@@ -345,7 +360,7 @@ xfs_map_blocks(
        }
 
 #ifdef DEBUG
-       if (type == IO_UNWRITTEN) {
+       if (type == XFS_IO_UNWRITTEN) {
                ASSERT(nimaps);
                ASSERT(imap->br_startblock != HOLESTARTBLOCK);
                ASSERT(imap->br_startblock != DELAYSTARTBLOCK);
@@ -634,11 +649,11 @@ xfs_check_page_type(
                bh = head = page_buffers(page);
                do {
                        if (buffer_unwritten(bh))
-                               acceptable += (type == IO_UNWRITTEN);
+                               acceptable += (type == XFS_IO_UNWRITTEN);
                        else if (buffer_delay(bh))
-                               acceptable += (type == IO_DELALLOC);
+                               acceptable += (type == XFS_IO_DELALLOC);
                        else if (buffer_dirty(bh) && buffer_mapped(bh))
-                               acceptable += (type == IO_OVERWRITE);
+                               acceptable += (type == XFS_IO_OVERWRITE);
                        else
                                break;
                } while ((bh = bh->b_this_page) != head);
@@ -721,11 +736,11 @@ xfs_convert_page(
                if (buffer_unwritten(bh) || buffer_delay(bh) ||
                    buffer_mapped(bh)) {
                        if (buffer_unwritten(bh))
-                               type = IO_UNWRITTEN;
+                               type = XFS_IO_UNWRITTEN;
                        else if (buffer_delay(bh))
-                               type = IO_DELALLOC;
+                               type = XFS_IO_DELALLOC;
                        else
-                               type = IO_OVERWRITE;
+                               type = XFS_IO_OVERWRITE;
 
                        if (!xfs_imap_valid(inode, imap, offset)) {
                                done = 1;
@@ -733,7 +748,7 @@ xfs_convert_page(
                        }
 
                        lock_buffer(bh);
-                       if (type != IO_OVERWRITE)
+                       if (type != XFS_IO_OVERWRITE)
                                xfs_map_at_offset(inode, bh, imap, offset);
                        xfs_add_to_ioend(inode, bh, offset, type,
                                         ioendp, done);
@@ -831,7 +846,7 @@ xfs_aops_discard_page(
        struct buffer_head      *bh, *head;
        loff_t                  offset = page_offset(page);
 
-       if (!xfs_check_page_type(page, IO_DELALLOC))
+       if (!xfs_check_page_type(page, XFS_IO_DELALLOC))
                goto out_invalidate;
 
        if (XFS_FORCED_SHUTDOWN(ip->i_mount))
@@ -927,11 +942,26 @@ xfs_vm_writepage(
        end_index = offset >> PAGE_CACHE_SHIFT;
        last_index = (offset - 1) >> PAGE_CACHE_SHIFT;
        if (page->index >= end_index) {
-               if ((page->index >= end_index + 1) ||
-                   !(i_size_read(inode) & (PAGE_CACHE_SIZE - 1))) {
+               unsigned offset_into_page = offset & (PAGE_CACHE_SIZE - 1);
+
+               /*
+                * Just skip the page if it is fully outside i_size, e.g. due
+                * to a truncate operation that is in progress.
+                */
+               if (page->index >= end_index + 1 || offset_into_page == 0) {
                        unlock_page(page);
                        return 0;
                }
+
+               /*
+                * The page straddles i_size.  It must be zeroed out on each
+                * and every writepage invocation because it may be mmapped.
+                * "A file is mapped in multiples of the page size.  For a file
+                * that is not a multiple of the  page size, the remaining
+                * memory is zeroed when mapped, and writes to that region are
+                * not written out to the file."
+                */
+               zero_user_segment(page, offset_into_page, PAGE_CACHE_SIZE);
        }
 
        end_offset = min_t(unsigned long long,
@@ -941,7 +971,7 @@ xfs_vm_writepage(
 
        bh = head = page_buffers(page);
        offset = page_offset(page);
-       type = IO_OVERWRITE;
+       type = XFS_IO_OVERWRITE;
 
        if (wbc->sync_mode == WB_SYNC_NONE)
                nonblocking = 1;
@@ -966,18 +996,18 @@ xfs_vm_writepage(
                }
 
                if (buffer_unwritten(bh)) {
-                       if (type != IO_UNWRITTEN) {
-                               type = IO_UNWRITTEN;
+                       if (type != XFS_IO_UNWRITTEN) {
+                               type = XFS_IO_UNWRITTEN;
                                imap_valid = 0;
                        }
                } else if (buffer_delay(bh)) {
-                       if (type != IO_DELALLOC) {
-                               type = IO_DELALLOC;
+                       if (type != XFS_IO_DELALLOC) {
+                               type = XFS_IO_DELALLOC;
                                imap_valid = 0;
                        }
                } else if (buffer_uptodate(bh)) {
-                       if (type != IO_OVERWRITE) {
-                               type = IO_OVERWRITE;
+                       if (type != XFS_IO_OVERWRITE) {
+                               type = XFS_IO_OVERWRITE;
                                imap_valid = 0;
                        }
                } else {
@@ -1013,7 +1043,7 @@ xfs_vm_writepage(
                }
                if (imap_valid) {
                        lock_buffer(bh);
-                       if (type != IO_OVERWRITE)
+                       if (type != XFS_IO_OVERWRITE)
                                xfs_map_at_offset(inode, bh, &imap, offset);
                        xfs_add_to_ioend(inode, bh, offset, type, &ioend,
                                         new_ioend);
@@ -1054,7 +1084,7 @@ xfs_vm_writepage(
                 * Reserve log space if we might write beyond the on-disk
                 * inode size.
                 */
-               if (ioend->io_type != IO_UNWRITTEN &&
+               if (ioend->io_type != XFS_IO_UNWRITTEN &&
                    xfs_ioend_is_append(ioend)) {
                        err = xfs_setfilesize_trans_alloc(ioend);
                        if (err)
@@ -1162,9 +1192,9 @@ __xfs_get_blocks(
                lockmode = xfs_ilock_map_shared(ip);
        }
 
-       ASSERT(offset <= mp->m_maxioffset);
-       if (offset + size > mp->m_maxioffset)
-               size = mp->m_maxioffset - offset;
+       ASSERT(offset <= mp->m_super->s_maxbytes);
+       if (offset + size > mp->m_super->s_maxbytes)
+               size = mp->m_super->s_maxbytes - offset;
        end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + size);
        offset_fsb = XFS_B_TO_FSBT(mp, offset);
 
@@ -1351,7 +1381,7 @@ xfs_end_io_direct_write(
        ioend->io_iocb = iocb;
        ioend->io_result = ret;
        if (private && size > 0)
-               ioend->io_type = IO_UNWRITTEN;
+               ioend->io_type = XFS_IO_UNWRITTEN;
 
        if (is_async) {
                ioend->io_isasync = 1;
@@ -1383,7 +1413,7 @@ xfs_vm_direct_IO(
                 * and converts at least on unwritten extent we will cancel
                 * the still clean transaction after the I/O has finished.
                 */
-               iocb->private = ioend = xfs_alloc_ioend(inode, IO_DIRECT);
+               iocb->private = ioend = xfs_alloc_ioend(inode, XFS_IO_DIRECT);
                if (offset + size > XFS_I(inode)->i_d.di_size) {
                        ret = xfs_setfilesize_trans_alloc(ioend);
                        if (ret)
@@ -1410,6 +1440,9 @@ out_trans_cancel:
        if (ioend->io_append_trans) {
                current_set_flags_nested(&ioend->io_append_trans->t_pflags,
                                         PF_FSTRANS);
+               rwsem_acquire_read(
+                       &inode->i_sb->s_writers.lock_map[SB_FREEZE_FS-1],
+                       0, 1, _THIS_IP_);
                xfs_trans_cancel(ioend->io_append_trans, 0);
        }
 out_destroy_ioend:
index 84eafbcb0d9dd65cecd57ef9074eb66b89bb9c08..c325abb8d61ab6bcf031073b905e440be440f8f7 100644 (file)
@@ -24,17 +24,17 @@ extern mempool_t *xfs_ioend_pool;
  * Types of I/O for bmap clustering and I/O completion tracking.
  */
 enum {
-       IO_DIRECT = 0,  /* special case for direct I/O ioends */
-       IO_DELALLOC,    /* mapping covers delalloc region */
-       IO_UNWRITTEN,   /* mapping covers allocated but uninitialized data */
-       IO_OVERWRITE,   /* mapping covers already allocated extent */
+       XFS_IO_DIRECT = 0,      /* special case for direct I/O ioends */
+       XFS_IO_DELALLOC,        /* covers delalloc region */
+       XFS_IO_UNWRITTEN,       /* covers allocated but uninitialized data */
+       XFS_IO_OVERWRITE,       /* covers already allocated extent */
 };
 
 #define XFS_IO_TYPES \
        { 0,                    "" }, \
-       { IO_DELALLOC,          "delalloc" }, \
-       { IO_UNWRITTEN,         "unwritten" }, \
-       { IO_OVERWRITE,         "overwrite" }
+       { XFS_IO_DELALLOC,              "delalloc" }, \
+       { XFS_IO_UNWRITTEN,             "unwritten" }, \
+       { XFS_IO_OVERWRITE,             "overwrite" }
 
 /*
  * xfs_ioend struct manages large extent writes for XFS.
index a17ff01b5adf900da88aacb0e430b8853443bb62..0ca1f0be62d262f8eb23873f48c81ab864995fe8 100644 (file)
@@ -893,7 +893,7 @@ STATIC int
 xfs_attr_leaf_addname(xfs_da_args_t *args)
 {
        xfs_inode_t *dp;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
        int retval, error, committed, forkoff;
 
        trace_xfs_attr_leaf_addname(args);
@@ -915,11 +915,11 @@ xfs_attr_leaf_addname(xfs_da_args_t *args)
         */
        retval = xfs_attr_leaf_lookup_int(bp, args);
        if ((args->flags & ATTR_REPLACE) && (retval == ENOATTR)) {
-               xfs_da_brelse(args->trans, bp);
+               xfs_trans_brelse(args->trans, bp);
                return(retval);
        } else if (retval == EEXIST) {
                if (args->flags & ATTR_CREATE) {        /* pure create op */
-                       xfs_da_brelse(args->trans, bp);
+                       xfs_trans_brelse(args->trans, bp);
                        return(retval);
                }
 
@@ -937,7 +937,6 @@ xfs_attr_leaf_addname(xfs_da_args_t *args)
         * if required.
         */
        retval = xfs_attr_leaf_add(bp, args);
-       xfs_da_buf_done(bp);
        if (retval == ENOSPC) {
                /*
                 * Promote the attribute list to the Btree format, then
@@ -1065,8 +1064,7 @@ xfs_attr_leaf_addname(xfs_da_args_t *args)
                         */
                        if (committed)
                                xfs_trans_ijoin(args->trans, dp, 0);
-               } else
-                       xfs_da_buf_done(bp);
+               }
 
                /*
                 * Commit the remove and start the next trans in series.
@@ -1092,7 +1090,7 @@ STATIC int
 xfs_attr_leaf_removename(xfs_da_args_t *args)
 {
        xfs_inode_t *dp;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
        int error, committed, forkoff;
 
        trace_xfs_attr_leaf_removename(args);
@@ -1111,7 +1109,7 @@ xfs_attr_leaf_removename(xfs_da_args_t *args)
        ASSERT(bp != NULL);
        error = xfs_attr_leaf_lookup_int(bp, args);
        if (error == ENOATTR) {
-               xfs_da_brelse(args->trans, bp);
+               xfs_trans_brelse(args->trans, bp);
                return(error);
        }
 
@@ -1141,8 +1139,7 @@ xfs_attr_leaf_removename(xfs_da_args_t *args)
                 */
                if (committed)
                        xfs_trans_ijoin(args->trans, dp, 0);
-       } else
-               xfs_da_buf_done(bp);
+       }
        return(0);
 }
 
@@ -1155,7 +1152,7 @@ xfs_attr_leaf_removename(xfs_da_args_t *args)
 STATIC int
 xfs_attr_leaf_get(xfs_da_args_t *args)
 {
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
        int error;
 
        args->blkno = 0;
@@ -1167,11 +1164,11 @@ xfs_attr_leaf_get(xfs_da_args_t *args)
 
        error = xfs_attr_leaf_lookup_int(bp, args);
        if (error != EEXIST)  {
-               xfs_da_brelse(args->trans, bp);
+               xfs_trans_brelse(args->trans, bp);
                return(error);
        }
        error = xfs_attr_leaf_getvalue(bp, args);
-       xfs_da_brelse(args->trans, bp);
+       xfs_trans_brelse(args->trans, bp);
        if (!error && (args->rmtblkno > 0) && !(args->flags & ATTR_KERNOVAL)) {
                error = xfs_attr_rmtval_get(args);
        }
@@ -1186,23 +1183,23 @@ xfs_attr_leaf_list(xfs_attr_list_context_t *context)
 {
        xfs_attr_leafblock_t *leaf;
        int error;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
 
        context->cursor->blkno = 0;
        error = xfs_da_read_buf(NULL, context->dp, 0, -1, &bp, XFS_ATTR_FORK);
        if (error)
                return XFS_ERROR(error);
        ASSERT(bp != NULL);
-       leaf = bp->data;
+       leaf = bp->b_addr;
        if (unlikely(leaf->hdr.info.magic != cpu_to_be16(XFS_ATTR_LEAF_MAGIC))) {
                XFS_CORRUPTION_ERROR("xfs_attr_leaf_list", XFS_ERRLEVEL_LOW,
                                     context->dp->i_mount, leaf);
-               xfs_da_brelse(NULL, bp);
+               xfs_trans_brelse(NULL, bp);
                return XFS_ERROR(EFSCORRUPTED);
        }
 
        error = xfs_attr_leaf_list_int(bp, context);
-       xfs_da_brelse(NULL, bp);
+       xfs_trans_brelse(NULL, bp);
        return XFS_ERROR(error);
 }
 
@@ -1489,7 +1486,7 @@ xfs_attr_node_removename(xfs_da_args_t *args)
        xfs_da_state_t *state;
        xfs_da_state_blk_t *blk;
        xfs_inode_t *dp;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
        int retval, error, committed, forkoff;
 
        trace_xfs_attr_node_removename(args);
@@ -1601,14 +1598,13 @@ xfs_attr_node_removename(xfs_da_args_t *args)
                 */
                ASSERT(state->path.active == 1);
                ASSERT(state->path.blk[0].bp);
-               xfs_da_buf_done(state->path.blk[0].bp);
                state->path.blk[0].bp = NULL;
 
                error = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
                                                     XFS_ATTR_FORK);
                if (error)
                        goto out;
-               ASSERT((((xfs_attr_leafblock_t *)bp->data)->hdr.info.magic) ==
+               ASSERT((((xfs_attr_leafblock_t *)bp->b_addr)->hdr.info.magic) ==
                       cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
 
                if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
@@ -1635,7 +1631,7 @@ xfs_attr_node_removename(xfs_da_args_t *args)
                        if (committed)
                                xfs_trans_ijoin(args->trans, dp, 0);
                } else
-                       xfs_da_brelse(args->trans, bp);
+                       xfs_trans_brelse(args->trans, bp);
        }
        error = 0;
 
@@ -1665,8 +1661,7 @@ xfs_attr_fillstate(xfs_da_state_t *state)
        ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
        for (blk = path->blk, level = 0; level < path->active; blk++, level++) {
                if (blk->bp) {
-                       blk->disk_blkno = xfs_da_blkno(blk->bp);
-                       xfs_da_buf_done(blk->bp);
+                       blk->disk_blkno = XFS_BUF_ADDR(blk->bp);
                        blk->bp = NULL;
                } else {
                        blk->disk_blkno = 0;
@@ -1681,8 +1676,7 @@ xfs_attr_fillstate(xfs_da_state_t *state)
        ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
        for (blk = path->blk, level = 0; level < path->active; blk++, level++) {
                if (blk->bp) {
-                       blk->disk_blkno = xfs_da_blkno(blk->bp);
-                       xfs_da_buf_done(blk->bp);
+                       blk->disk_blkno = XFS_BUF_ADDR(blk->bp);
                        blk->bp = NULL;
                } else {
                        blk->disk_blkno = 0;
@@ -1792,7 +1786,7 @@ xfs_attr_node_get(xfs_da_args_t *args)
         * If not in a transaction, we have to release all the buffers.
         */
        for (i = 0; i < state->path.active; i++) {
-               xfs_da_brelse(args->trans, state->path.blk[i].bp);
+               xfs_trans_brelse(args->trans, state->path.blk[i].bp);
                state->path.blk[i].bp = NULL;
        }
 
@@ -1808,7 +1802,7 @@ xfs_attr_node_list(xfs_attr_list_context_t *context)
        xfs_da_intnode_t *node;
        xfs_da_node_entry_t *btree;
        int error, i;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
 
        cursor = context->cursor;
        cursor->initted = 1;
@@ -1825,30 +1819,30 @@ xfs_attr_node_list(xfs_attr_list_context_t *context)
                if ((error != 0) && (error != EFSCORRUPTED))
                        return(error);
                if (bp) {
-                       node = bp->data;
+                       node = bp->b_addr;
                        switch (be16_to_cpu(node->hdr.info.magic)) {
                        case XFS_DA_NODE_MAGIC:
                                trace_xfs_attr_list_wrong_blk(context);
-                               xfs_da_brelse(NULL, bp);
+                               xfs_trans_brelse(NULL, bp);
                                bp = NULL;
                                break;
                        case XFS_ATTR_LEAF_MAGIC:
-                               leaf = bp->data;
+                               leaf = bp->b_addr;
                                if (cursor->hashval > be32_to_cpu(leaf->entries[
                                    be16_to_cpu(leaf->hdr.count)-1].hashval)) {
                                        trace_xfs_attr_list_wrong_blk(context);
-                                       xfs_da_brelse(NULL, bp);
+                                       xfs_trans_brelse(NULL, bp);
                                        bp = NULL;
                                } else if (cursor->hashval <=
                                             be32_to_cpu(leaf->entries[0].hashval)) {
                                        trace_xfs_attr_list_wrong_blk(context);
-                                       xfs_da_brelse(NULL, bp);
+                                       xfs_trans_brelse(NULL, bp);
                                        bp = NULL;
                                }
                                break;
                        default:
                                trace_xfs_attr_list_wrong_blk(context);
-                               xfs_da_brelse(NULL, bp);
+                               xfs_trans_brelse(NULL, bp);
                                bp = NULL;
                        }
                }
@@ -1873,7 +1867,7 @@ xfs_attr_node_list(xfs_attr_list_context_t *context)
                                                 context->dp->i_mount);
                                return(XFS_ERROR(EFSCORRUPTED));
                        }
-                       node = bp->data;
+                       node = bp->b_addr;
                        if (node->hdr.info.magic ==
                            cpu_to_be16(XFS_ATTR_LEAF_MAGIC))
                                break;
@@ -1883,7 +1877,7 @@ xfs_attr_node_list(xfs_attr_list_context_t *context)
                                                     XFS_ERRLEVEL_LOW,
                                                     context->dp->i_mount,
                                                     node);
-                               xfs_da_brelse(NULL, bp);
+                               xfs_trans_brelse(NULL, bp);
                                return(XFS_ERROR(EFSCORRUPTED));
                        }
                        btree = node->btree;
@@ -1898,10 +1892,10 @@ xfs_attr_node_list(xfs_attr_list_context_t *context)
                                }
                        }
                        if (i == be16_to_cpu(node->hdr.count)) {
-                               xfs_da_brelse(NULL, bp);
+                               xfs_trans_brelse(NULL, bp);
                                return(0);
                        }
-                       xfs_da_brelse(NULL, bp);
+                       xfs_trans_brelse(NULL, bp);
                }
        }
        ASSERT(bp != NULL);
@@ -1912,24 +1906,24 @@ xfs_attr_node_list(xfs_attr_list_context_t *context)
         * adding the information.
         */
        for (;;) {
-               leaf = bp->data;
+               leaf = bp->b_addr;
                if (unlikely(leaf->hdr.info.magic !=
                             cpu_to_be16(XFS_ATTR_LEAF_MAGIC))) {
                        XFS_CORRUPTION_ERROR("xfs_attr_node_list(4)",
                                             XFS_ERRLEVEL_LOW,
                                             context->dp->i_mount, leaf);
-                       xfs_da_brelse(NULL, bp);
+                       xfs_trans_brelse(NULL, bp);
                        return(XFS_ERROR(EFSCORRUPTED));
                }
                error = xfs_attr_leaf_list_int(bp, context);
                if (error) {
-                       xfs_da_brelse(NULL, bp);
+                       xfs_trans_brelse(NULL, bp);
                        return error;
                }
                if (context->seen_enough || leaf->hdr.info.forw == 0)
                        break;
                cursor->blkno = be32_to_cpu(leaf->hdr.info.forw);
-               xfs_da_brelse(NULL, bp);
+               xfs_trans_brelse(NULL, bp);
                error = xfs_da_read_buf(NULL, context->dp, cursor->blkno, -1,
                                              &bp, XFS_ATTR_FORK);
                if (error)
@@ -1941,7 +1935,7 @@ xfs_attr_node_list(xfs_attr_list_context_t *context)
                        return(XFS_ERROR(EFSCORRUPTED));
                }
        }
-       xfs_da_brelse(NULL, bp);
+       xfs_trans_brelse(NULL, bp);
        return(0);
 }
 
index 7d89d800f5173f2d5d1c34a36278cab703f92d33..d330111ca738eef9ff86b5fd28723c0b63a9b379 100644 (file)
  * Routines used for growing the Btree.
  */
 STATIC int xfs_attr_leaf_create(xfs_da_args_t *args, xfs_dablk_t which_block,
-                                   xfs_dabuf_t **bpp);
-STATIC int xfs_attr_leaf_add_work(xfs_dabuf_t *leaf_buffer, xfs_da_args_t *args,
-                                             int freemap_index);
-STATIC void xfs_attr_leaf_compact(xfs_trans_t *trans, xfs_dabuf_t *leaf_buffer);
+                               struct xfs_buf **bpp);
+STATIC int xfs_attr_leaf_add_work(struct xfs_buf *leaf_buffer,
+                                 xfs_da_args_t *args, int freemap_index);
+STATIC void xfs_attr_leaf_compact(xfs_trans_t *tp, struct xfs_buf *leaf_buffer);
 STATIC void xfs_attr_leaf_rebalance(xfs_da_state_t *state,
                                                   xfs_da_state_blk_t *blk1,
                                                   xfs_da_state_blk_t *blk2);
@@ -71,9 +71,9 @@ STATIC int xfs_attr_leaf_figure_balance(xfs_da_state_t *state,
  * Routines used for shrinking the Btree.
  */
 STATIC int xfs_attr_node_inactive(xfs_trans_t **trans, xfs_inode_t *dp,
-                                 xfs_dabuf_t *bp, int level);
+                                 struct xfs_buf *bp, int level);
 STATIC int xfs_attr_leaf_inactive(xfs_trans_t **trans, xfs_inode_t *dp,
-                                 xfs_dabuf_t *bp);
+                                 struct xfs_buf *bp);
 STATIC int xfs_attr_leaf_freextent(xfs_trans_t **trans, xfs_inode_t *dp,
                                   xfs_dablk_t blkno, int blkcnt);
 
@@ -480,7 +480,7 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
        char *tmpbuffer;
        int error, i, size;
        xfs_dablk_t blkno;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
        xfs_ifork_t *ifp;
 
        trace_xfs_attr_sf_to_leaf(args);
@@ -550,8 +550,6 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
        error = 0;
 
 out:
-       if(bp)
-               xfs_da_buf_done(bp);
        kmem_free(tmpbuffer);
        return(error);
 }
@@ -737,14 +735,16 @@ xfs_attr_shortform_list(xfs_attr_list_context_t *context)
  * a shortform attribute list.
  */
 int
-xfs_attr_shortform_allfit(xfs_dabuf_t *bp, xfs_inode_t *dp)
+xfs_attr_shortform_allfit(
+       struct xfs_buf  *bp,
+       struct xfs_inode *dp)
 {
        xfs_attr_leafblock_t *leaf;
        xfs_attr_leaf_entry_t *entry;
        xfs_attr_leaf_name_local_t *name_loc;
        int bytes, i;
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
 
        entry = &leaf->entries[0];
@@ -774,7 +774,10 @@ xfs_attr_shortform_allfit(xfs_dabuf_t *bp, xfs_inode_t *dp)
  * Convert a leaf attribute list to shortform attribute list
  */
 int
-xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args, int forkoff)
+xfs_attr_leaf_to_shortform(
+       struct xfs_buf  *bp,
+       xfs_da_args_t   *args,
+       int             forkoff)
 {
        xfs_attr_leafblock_t *leaf;
        xfs_attr_leaf_entry_t *entry;
@@ -791,10 +794,10 @@ xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args, int forkoff)
        ASSERT(tmpbuffer != NULL);
 
        ASSERT(bp != NULL);
-       memcpy(tmpbuffer, bp->data, XFS_LBSIZE(dp->i_mount));
+       memcpy(tmpbuffer, bp->b_addr, XFS_LBSIZE(dp->i_mount));
        leaf = (xfs_attr_leafblock_t *)tmpbuffer;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
-       memset(bp->data, 0, XFS_LBSIZE(dp->i_mount));
+       memset(bp->b_addr, 0, XFS_LBSIZE(dp->i_mount));
 
        /*
         * Clean out the prior contents of the attribute list.
@@ -855,7 +858,7 @@ xfs_attr_leaf_to_node(xfs_da_args_t *args)
        xfs_attr_leafblock_t *leaf;
        xfs_da_intnode_t *node;
        xfs_inode_t *dp;
-       xfs_dabuf_t *bp1, *bp2;
+       struct xfs_buf *bp1, *bp2;
        xfs_dablk_t blkno;
        int error;
 
@@ -877,10 +880,9 @@ xfs_attr_leaf_to_node(xfs_da_args_t *args)
        if (error)
                goto out;
        ASSERT(bp2 != NULL);
-       memcpy(bp2->data, bp1->data, XFS_LBSIZE(dp->i_mount));
-       xfs_da_buf_done(bp1);
+       memcpy(bp2->b_addr, bp1->b_addr, XFS_LBSIZE(dp->i_mount));
        bp1 = NULL;
-       xfs_da_log_buf(args->trans, bp2, 0, XFS_LBSIZE(dp->i_mount) - 1);
+       xfs_trans_log_buf(args->trans, bp2, 0, XFS_LBSIZE(dp->i_mount) - 1);
 
        /*
         * Set up the new root node.
@@ -888,21 +890,17 @@ xfs_attr_leaf_to_node(xfs_da_args_t *args)
        error = xfs_da_node_create(args, 0, 1, &bp1, XFS_ATTR_FORK);
        if (error)
                goto out;
-       node = bp1->data;
-       leaf = bp2->data;
+       node = bp1->b_addr;
+       leaf = bp2->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        /* both on-disk, don't endian-flip twice */
        node->btree[0].hashval =
                leaf->entries[be16_to_cpu(leaf->hdr.count)-1 ].hashval;
        node->btree[0].before = cpu_to_be32(blkno);
        node->hdr.count = cpu_to_be16(1);
-       xfs_da_log_buf(args->trans, bp1, 0, XFS_LBSIZE(dp->i_mount) - 1);
+       xfs_trans_log_buf(args->trans, bp1, 0, XFS_LBSIZE(dp->i_mount) - 1);
        error = 0;
 out:
-       if (bp1)
-               xfs_da_buf_done(bp1);
-       if (bp2)
-               xfs_da_buf_done(bp2);
        return(error);
 }
 
@@ -916,12 +914,15 @@ out:
  * or a leaf in a node attribute list.
  */
 STATIC int
-xfs_attr_leaf_create(xfs_da_args_t *args, xfs_dablk_t blkno, xfs_dabuf_t **bpp)
+xfs_attr_leaf_create(
+       xfs_da_args_t   *args,
+       xfs_dablk_t     blkno,
+       struct xfs_buf  **bpp)
 {
        xfs_attr_leafblock_t *leaf;
        xfs_attr_leaf_hdr_t *hdr;
        xfs_inode_t *dp;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
        int error;
 
        trace_xfs_attr_leaf_create(args);
@@ -933,7 +934,7 @@ xfs_attr_leaf_create(xfs_da_args_t *args, xfs_dablk_t blkno, xfs_dabuf_t **bpp)
        if (error)
                return(error);
        ASSERT(bp != NULL);
-       leaf = bp->data;
+       leaf = bp->b_addr;
        memset((char *)leaf, 0, XFS_LBSIZE(dp->i_mount));
        hdr = &leaf->hdr;
        hdr->info.magic = cpu_to_be16(XFS_ATTR_LEAF_MAGIC);
@@ -947,7 +948,7 @@ xfs_attr_leaf_create(xfs_da_args_t *args, xfs_dablk_t blkno, xfs_dabuf_t **bpp)
        hdr->freemap[0].size = cpu_to_be16(be16_to_cpu(hdr->firstused) -
                                           sizeof(xfs_attr_leaf_hdr_t));
 
-       xfs_da_log_buf(args->trans, bp, 0, XFS_LBSIZE(dp->i_mount) - 1);
+       xfs_trans_log_buf(args->trans, bp, 0, XFS_LBSIZE(dp->i_mount) - 1);
 
        *bpp = bp;
        return(0);
@@ -1014,7 +1015,9 @@ xfs_attr_leaf_split(xfs_da_state_t *state, xfs_da_state_blk_t *oldblk,
  * Add a name to the leaf attribute list structure.
  */
 int
-xfs_attr_leaf_add(xfs_dabuf_t *bp, xfs_da_args_t *args)
+xfs_attr_leaf_add(
+       struct xfs_buf          *bp,
+       struct xfs_da_args      *args)
 {
        xfs_attr_leafblock_t *leaf;
        xfs_attr_leaf_hdr_t *hdr;
@@ -1023,7 +1026,7 @@ xfs_attr_leaf_add(xfs_dabuf_t *bp, xfs_da_args_t *args)
 
        trace_xfs_attr_leaf_add(args);
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        ASSERT((args->index >= 0)
                && (args->index <= be16_to_cpu(leaf->hdr.count)));
@@ -1085,7 +1088,10 @@ xfs_attr_leaf_add(xfs_dabuf_t *bp, xfs_da_args_t *args)
  * Add a name to a leaf attribute list structure.
  */
 STATIC int
-xfs_attr_leaf_add_work(xfs_dabuf_t *bp, xfs_da_args_t *args, int mapindex)
+xfs_attr_leaf_add_work(
+       struct xfs_buf  *bp,
+       xfs_da_args_t   *args,
+       int             mapindex)
 {
        xfs_attr_leafblock_t *leaf;
        xfs_attr_leaf_hdr_t *hdr;
@@ -1096,7 +1102,7 @@ xfs_attr_leaf_add_work(xfs_dabuf_t *bp, xfs_da_args_t *args, int mapindex)
        xfs_mount_t *mp;
        int tmp, i;
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        hdr = &leaf->hdr;
        ASSERT((mapindex >= 0) && (mapindex < XFS_ATTR_LEAF_MAPSIZE));
@@ -1110,7 +1116,7 @@ xfs_attr_leaf_add_work(xfs_dabuf_t *bp, xfs_da_args_t *args, int mapindex)
                tmp  = be16_to_cpu(hdr->count) - args->index;
                tmp *= sizeof(xfs_attr_leaf_entry_t);
                memmove((char *)(entry+1), (char *)entry, tmp);
-               xfs_da_log_buf(args->trans, bp,
+               xfs_trans_log_buf(args->trans, bp,
                    XFS_DA_LOGRANGE(leaf, entry, tmp + sizeof(*entry)));
        }
        be16_add_cpu(&hdr->count, 1);
@@ -1142,7 +1148,7 @@ xfs_attr_leaf_add_work(xfs_dabuf_t *bp, xfs_da_args_t *args, int mapindex)
                        args->index2++;
                }
        }
-       xfs_da_log_buf(args->trans, bp,
+       xfs_trans_log_buf(args->trans, bp,
                          XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry)));
        ASSERT((args->index == 0) ||
               (be32_to_cpu(entry->hashval) >= be32_to_cpu((entry-1)->hashval)));
@@ -1174,7 +1180,7 @@ xfs_attr_leaf_add_work(xfs_dabuf_t *bp, xfs_da_args_t *args, int mapindex)
                args->rmtblkno = 1;
                args->rmtblkcnt = XFS_B_TO_FSB(mp, args->valuelen);
        }
-       xfs_da_log_buf(args->trans, bp,
+       xfs_trans_log_buf(args->trans, bp,
             XFS_DA_LOGRANGE(leaf, xfs_attr_leaf_name(leaf, args->index),
                                   xfs_attr_leaf_entsize(leaf, args->index)));
 
@@ -1198,7 +1204,7 @@ xfs_attr_leaf_add_work(xfs_dabuf_t *bp, xfs_da_args_t *args, int mapindex)
                }
        }
        be16_add_cpu(&hdr->usedbytes, xfs_attr_leaf_entsize(leaf, args->index));
-       xfs_da_log_buf(args->trans, bp,
+       xfs_trans_log_buf(args->trans, bp,
                XFS_DA_LOGRANGE(leaf, hdr, sizeof(*hdr)));
        return(0);
 }
@@ -1207,7 +1213,9 @@ xfs_attr_leaf_add_work(xfs_dabuf_t *bp, xfs_da_args_t *args, int mapindex)
  * Garbage collect a leaf attribute list block by copying it to a new buffer.
  */
 STATIC void
-xfs_attr_leaf_compact(xfs_trans_t *trans, xfs_dabuf_t *bp)
+xfs_attr_leaf_compact(
+       struct xfs_trans *trans,
+       struct xfs_buf  *bp)
 {
        xfs_attr_leafblock_t *leaf_s, *leaf_d;
        xfs_attr_leaf_hdr_t *hdr_s, *hdr_d;
@@ -1217,14 +1225,14 @@ xfs_attr_leaf_compact(xfs_trans_t *trans, xfs_dabuf_t *bp)
        mp = trans->t_mountp;
        tmpbuffer = kmem_alloc(XFS_LBSIZE(mp), KM_SLEEP);
        ASSERT(tmpbuffer != NULL);
-       memcpy(tmpbuffer, bp->data, XFS_LBSIZE(mp));
-       memset(bp->data, 0, XFS_LBSIZE(mp));
+       memcpy(tmpbuffer, bp->b_addr, XFS_LBSIZE(mp));
+       memset(bp->b_addr, 0, XFS_LBSIZE(mp));
 
        /*
         * Copy basic information
         */
        leaf_s = (xfs_attr_leafblock_t *)tmpbuffer;
-       leaf_d = bp->data;
+       leaf_d = bp->b_addr;
        hdr_s = &leaf_s->hdr;
        hdr_d = &leaf_d->hdr;
        hdr_d->info = hdr_s->info;      /* struct copy */
@@ -1247,7 +1255,7 @@ xfs_attr_leaf_compact(xfs_trans_t *trans, xfs_dabuf_t *bp)
         */
        xfs_attr_leaf_moveents(leaf_s, 0, leaf_d, 0,
                                be16_to_cpu(hdr_s->count), mp);
-       xfs_da_log_buf(trans, bp, 0, XFS_LBSIZE(mp) - 1);
+       xfs_trans_log_buf(trans, bp, 0, XFS_LBSIZE(mp) - 1);
 
        kmem_free(tmpbuffer);
 }
@@ -1279,8 +1287,8 @@ xfs_attr_leaf_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
         */
        ASSERT(blk1->magic == XFS_ATTR_LEAF_MAGIC);
        ASSERT(blk2->magic == XFS_ATTR_LEAF_MAGIC);
-       leaf1 = blk1->bp->data;
-       leaf2 = blk2->bp->data;
+       leaf1 = blk1->bp->b_addr;
+       leaf2 = blk2->bp->b_addr;
        ASSERT(leaf1->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        ASSERT(leaf2->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        args = state->args;
@@ -1298,8 +1306,8 @@ xfs_attr_leaf_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
                tmp_blk = blk1;
                blk1 = blk2;
                blk2 = tmp_blk;
-               leaf1 = blk1->bp->data;
-               leaf2 = blk2->bp->data;
+               leaf1 = blk1->bp->b_addr;
+               leaf2 = blk2->bp->b_addr;
                swap = 1;
        }
        hdr1 = &leaf1->hdr;
@@ -1346,8 +1354,8 @@ xfs_attr_leaf_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
                xfs_attr_leaf_moveents(leaf1, be16_to_cpu(hdr1->count) - count,
                                leaf2, 0, count, state->mp);
 
-               xfs_da_log_buf(args->trans, blk1->bp, 0, state->blocksize-1);
-               xfs_da_log_buf(args->trans, blk2->bp, 0, state->blocksize-1);
+               xfs_trans_log_buf(args->trans, blk1->bp, 0, state->blocksize-1);
+               xfs_trans_log_buf(args->trans, blk2->bp, 0, state->blocksize-1);
        } else if (count > be16_to_cpu(hdr1->count)) {
                /*
                 * I assert that since all callers pass in an empty
@@ -1378,8 +1386,8 @@ xfs_attr_leaf_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
                xfs_attr_leaf_moveents(leaf2, 0, leaf1,
                                be16_to_cpu(hdr1->count), count, state->mp);
 
-               xfs_da_log_buf(args->trans, blk1->bp, 0, state->blocksize-1);
-               xfs_da_log_buf(args->trans, blk2->bp, 0, state->blocksize-1);
+               xfs_trans_log_buf(args->trans, blk1->bp, 0, state->blocksize-1);
+               xfs_trans_log_buf(args->trans, blk2->bp, 0, state->blocksize-1);
        }
 
        /*
@@ -1448,8 +1456,8 @@ xfs_attr_leaf_figure_balance(xfs_da_state_t *state,
        /*
         * Set up environment.
         */
-       leaf1 = blk1->bp->data;
-       leaf2 = blk2->bp->data;
+       leaf1 = blk1->bp->b_addr;
+       leaf2 = blk2->bp->b_addr;
        hdr1 = &leaf1->hdr;
        hdr2 = &leaf2->hdr;
        foundit = 0;
@@ -1551,7 +1559,7 @@ xfs_attr_leaf_toosmall(xfs_da_state_t *state, int *action)
        xfs_da_blkinfo_t *info;
        int count, bytes, forward, error, retval, i;
        xfs_dablk_t blkno;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
 
        /*
         * Check for the degenerate case of the block being over 50% full.
@@ -1559,7 +1567,7 @@ xfs_attr_leaf_toosmall(xfs_da_state_t *state, int *action)
         * to coalesce with a sibling.
         */
        blk = &state->path.blk[ state->path.active-1 ];
-       info = blk->bp->data;
+       info = blk->bp->b_addr;
        ASSERT(info->magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        leaf = (xfs_attr_leafblock_t *)info;
        count = be16_to_cpu(leaf->hdr.count);
@@ -1622,13 +1630,13 @@ xfs_attr_leaf_toosmall(xfs_da_state_t *state, int *action)
                count  = be16_to_cpu(leaf->hdr.count);
                bytes  = state->blocksize - (state->blocksize>>2);
                bytes -= be16_to_cpu(leaf->hdr.usedbytes);
-               leaf = bp->data;
+               leaf = bp->b_addr;
                ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
                count += be16_to_cpu(leaf->hdr.count);
                bytes -= be16_to_cpu(leaf->hdr.usedbytes);
                bytes -= count * sizeof(xfs_attr_leaf_entry_t);
                bytes -= sizeof(xfs_attr_leaf_hdr_t);
-               xfs_da_brelse(state->args->trans, bp);
+               xfs_trans_brelse(state->args->trans, bp);
                if (bytes >= 0)
                        break;  /* fits with at least 25% to spare */
        }
@@ -1666,7 +1674,9 @@ xfs_attr_leaf_toosmall(xfs_da_state_t *state, int *action)
  * If two leaves are 37% full, when combined they will leave 25% free.
  */
 int
-xfs_attr_leaf_remove(xfs_dabuf_t *bp, xfs_da_args_t *args)
+xfs_attr_leaf_remove(
+       struct xfs_buf  *bp,
+       xfs_da_args_t   *args)
 {
        xfs_attr_leafblock_t *leaf;
        xfs_attr_leaf_hdr_t *hdr;
@@ -1676,7 +1686,7 @@ xfs_attr_leaf_remove(xfs_dabuf_t *bp, xfs_da_args_t *args)
        int tablesize, tmp, i;
        xfs_mount_t *mp;
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        hdr = &leaf->hdr;
        mp = args->trans->t_mountp;
@@ -1769,7 +1779,7 @@ xfs_attr_leaf_remove(xfs_dabuf_t *bp, xfs_da_args_t *args)
         */
        memset(xfs_attr_leaf_name(leaf, args->index), 0, entsize);
        be16_add_cpu(&hdr->usedbytes, -entsize);
-       xfs_da_log_buf(args->trans, bp,
+       xfs_trans_log_buf(args->trans, bp,
             XFS_DA_LOGRANGE(leaf, xfs_attr_leaf_name(leaf, args->index),
                                   entsize));
 
@@ -1777,7 +1787,7 @@ xfs_attr_leaf_remove(xfs_dabuf_t *bp, xfs_da_args_t *args)
                                        * sizeof(xfs_attr_leaf_entry_t);
        memmove((char *)entry, (char *)(entry+1), tmp);
        be16_add_cpu(&hdr->count, -1);
-       xfs_da_log_buf(args->trans, bp,
+       xfs_trans_log_buf(args->trans, bp,
            XFS_DA_LOGRANGE(leaf, entry, tmp + sizeof(*entry)));
        entry = &leaf->entries[be16_to_cpu(hdr->count)];
        memset((char *)entry, 0, sizeof(xfs_attr_leaf_entry_t));
@@ -1807,7 +1817,7 @@ xfs_attr_leaf_remove(xfs_dabuf_t *bp, xfs_da_args_t *args)
        } else {
                hdr->holes = 1;         /* mark as needing compaction */
        }
-       xfs_da_log_buf(args->trans, bp,
+       xfs_trans_log_buf(args->trans, bp,
                          XFS_DA_LOGRANGE(leaf, hdr, sizeof(*hdr)));
 
        /*
@@ -1840,8 +1850,8 @@ xfs_attr_leaf_unbalance(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
        mp = state->mp;
        ASSERT(drop_blk->magic == XFS_ATTR_LEAF_MAGIC);
        ASSERT(save_blk->magic == XFS_ATTR_LEAF_MAGIC);
-       drop_leaf = drop_blk->bp->data;
-       save_leaf = save_blk->bp->data;
+       drop_leaf = drop_blk->bp->b_addr;
+       save_leaf = save_blk->bp->b_addr;
        ASSERT(drop_leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        ASSERT(save_leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        drop_hdr = &drop_leaf->hdr;
@@ -1906,7 +1916,7 @@ xfs_attr_leaf_unbalance(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
                kmem_free(tmpbuffer);
        }
 
-       xfs_da_log_buf(state->args->trans, save_blk->bp, 0,
+       xfs_trans_log_buf(state->args->trans, save_blk->bp, 0,
                                           state->blocksize - 1);
 
        /*
@@ -1934,7 +1944,9 @@ xfs_attr_leaf_unbalance(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
  * Don't change the args->value unless we find the attribute.
  */
 int
-xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp, xfs_da_args_t *args)
+xfs_attr_leaf_lookup_int(
+       struct xfs_buf  *bp,
+       xfs_da_args_t   *args)
 {
        xfs_attr_leafblock_t *leaf;
        xfs_attr_leaf_entry_t *entry;
@@ -1945,7 +1957,7 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp, xfs_da_args_t *args)
 
        trace_xfs_attr_leaf_lookup(args);
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        ASSERT(be16_to_cpu(leaf->hdr.count)
                                        < (XFS_LBSIZE(args->dp->i_mount)/8));
@@ -2041,7 +2053,9 @@ xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp, xfs_da_args_t *args)
  * list structure.
  */
 int
-xfs_attr_leaf_getvalue(xfs_dabuf_t *bp, xfs_da_args_t *args)
+xfs_attr_leaf_getvalue(
+       struct xfs_buf  *bp,
+       xfs_da_args_t   *args)
 {
        int valuelen;
        xfs_attr_leafblock_t *leaf;
@@ -2049,7 +2063,7 @@ xfs_attr_leaf_getvalue(xfs_dabuf_t *bp, xfs_da_args_t *args)
        xfs_attr_leaf_name_local_t *name_loc;
        xfs_attr_leaf_name_remote_t *name_rmt;
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        ASSERT(be16_to_cpu(leaf->hdr.count)
                                        < (XFS_LBSIZE(args->dp->i_mount)/8));
@@ -2247,12 +2261,14 @@ xfs_attr_leaf_moveents(xfs_attr_leafblock_t *leaf_s, int start_s,
  * Return 0 unless leaf2 should go before leaf1.
  */
 int
-xfs_attr_leaf_order(xfs_dabuf_t *leaf1_bp, xfs_dabuf_t *leaf2_bp)
+xfs_attr_leaf_order(
+       struct xfs_buf  *leaf1_bp,
+       struct xfs_buf  *leaf2_bp)
 {
        xfs_attr_leafblock_t *leaf1, *leaf2;
 
-       leaf1 = leaf1_bp->data;
-       leaf2 = leaf2_bp->data;
+       leaf1 = leaf1_bp->b_addr;
+       leaf2 = leaf2_bp->b_addr;
        ASSERT((leaf1->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC)) &&
               (leaf2->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC)));
        if ((be16_to_cpu(leaf1->hdr.count) > 0) &&
@@ -2272,11 +2288,13 @@ xfs_attr_leaf_order(xfs_dabuf_t *leaf1_bp, xfs_dabuf_t *leaf2_bp)
  * Pick up the last hashvalue from a leaf block.
  */
 xfs_dahash_t
-xfs_attr_leaf_lasthash(xfs_dabuf_t *bp, int *count)
+xfs_attr_leaf_lasthash(
+       struct xfs_buf  *bp,
+       int             *count)
 {
        xfs_attr_leafblock_t *leaf;
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        if (count)
                *count = be16_to_cpu(leaf->hdr.count);
@@ -2337,7 +2355,9 @@ xfs_attr_leaf_newentsize(int namelen, int valuelen, int blocksize, int *local)
  * Copy out attribute list entries for attr_list(), for leaf attribute lists.
  */
 int
-xfs_attr_leaf_list_int(xfs_dabuf_t *bp, xfs_attr_list_context_t *context)
+xfs_attr_leaf_list_int(
+       struct xfs_buf          *bp,
+       xfs_attr_list_context_t *context)
 {
        attrlist_cursor_kern_t *cursor;
        xfs_attr_leafblock_t *leaf;
@@ -2345,7 +2365,7 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, xfs_attr_list_context_t *context)
        int retval, i;
 
        ASSERT(bp != NULL);
-       leaf = bp->data;
+       leaf = bp->b_addr;
        cursor = context->cursor;
        cursor->initted = 1;
 
@@ -2463,7 +2483,7 @@ xfs_attr_leaf_clearflag(xfs_da_args_t *args)
        xfs_attr_leafblock_t *leaf;
        xfs_attr_leaf_entry_t *entry;
        xfs_attr_leaf_name_remote_t *name_rmt;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
        int error;
 #ifdef DEBUG
        xfs_attr_leaf_name_local_t *name_loc;
@@ -2482,7 +2502,7 @@ xfs_attr_leaf_clearflag(xfs_da_args_t *args)
        }
        ASSERT(bp != NULL);
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        ASSERT(args->index < be16_to_cpu(leaf->hdr.count));
        ASSERT(args->index >= 0);
@@ -2505,7 +2525,7 @@ xfs_attr_leaf_clearflag(xfs_da_args_t *args)
 #endif /* DEBUG */
 
        entry->flags &= ~XFS_ATTR_INCOMPLETE;
-       xfs_da_log_buf(args->trans, bp,
+       xfs_trans_log_buf(args->trans, bp,
                         XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry)));
 
        if (args->rmtblkno) {
@@ -2513,10 +2533,9 @@ xfs_attr_leaf_clearflag(xfs_da_args_t *args)
                name_rmt = xfs_attr_leaf_name_remote(leaf, args->index);
                name_rmt->valueblk = cpu_to_be32(args->rmtblkno);
                name_rmt->valuelen = cpu_to_be32(args->valuelen);
-               xfs_da_log_buf(args->trans, bp,
+               xfs_trans_log_buf(args->trans, bp,
                         XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt)));
        }
-       xfs_da_buf_done(bp);
 
        /*
         * Commit the flag value change and start the next trans in series.
@@ -2533,7 +2552,7 @@ xfs_attr_leaf_setflag(xfs_da_args_t *args)
        xfs_attr_leafblock_t *leaf;
        xfs_attr_leaf_entry_t *entry;
        xfs_attr_leaf_name_remote_t *name_rmt;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
        int error;
 
        trace_xfs_attr_leaf_setflag(args);
@@ -2548,7 +2567,7 @@ xfs_attr_leaf_setflag(xfs_da_args_t *args)
        }
        ASSERT(bp != NULL);
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        ASSERT(args->index < be16_to_cpu(leaf->hdr.count));
        ASSERT(args->index >= 0);
@@ -2556,16 +2575,15 @@ xfs_attr_leaf_setflag(xfs_da_args_t *args)
 
        ASSERT((entry->flags & XFS_ATTR_INCOMPLETE) == 0);
        entry->flags |= XFS_ATTR_INCOMPLETE;
-       xfs_da_log_buf(args->trans, bp,
+       xfs_trans_log_buf(args->trans, bp,
                        XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry)));
        if ((entry->flags & XFS_ATTR_LOCAL) == 0) {
                name_rmt = xfs_attr_leaf_name_remote(leaf, args->index);
                name_rmt->valueblk = 0;
                name_rmt->valuelen = 0;
-               xfs_da_log_buf(args->trans, bp,
+               xfs_trans_log_buf(args->trans, bp,
                         XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt)));
        }
-       xfs_da_buf_done(bp);
 
        /*
         * Commit the flag value change and start the next trans in series.
@@ -2586,7 +2604,7 @@ xfs_attr_leaf_flipflags(xfs_da_args_t *args)
        xfs_attr_leafblock_t *leaf1, *leaf2;
        xfs_attr_leaf_entry_t *entry1, *entry2;
        xfs_attr_leaf_name_remote_t *name_rmt;
-       xfs_dabuf_t *bp1, *bp2;
+       struct xfs_buf *bp1, *bp2;
        int error;
 #ifdef DEBUG
        xfs_attr_leaf_name_local_t *name_loc;
@@ -2620,13 +2638,13 @@ xfs_attr_leaf_flipflags(xfs_da_args_t *args)
                bp2 = bp1;
        }
 
-       leaf1 = bp1->data;
+       leaf1 = bp1->b_addr;
        ASSERT(leaf1->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        ASSERT(args->index < be16_to_cpu(leaf1->hdr.count));
        ASSERT(args->index >= 0);
        entry1 = &leaf1->entries[ args->index ];
 
-       leaf2 = bp2->data;
+       leaf2 = bp2->b_addr;
        ASSERT(leaf2->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
        ASSERT(args->index2 < be16_to_cpu(leaf2->hdr.count));
        ASSERT(args->index2 >= 0);
@@ -2660,30 +2678,27 @@ xfs_attr_leaf_flipflags(xfs_da_args_t *args)
        ASSERT((entry2->flags & XFS_ATTR_INCOMPLETE) == 0);
 
        entry1->flags &= ~XFS_ATTR_INCOMPLETE;
-       xfs_da_log_buf(args->trans, bp1,
+       xfs_trans_log_buf(args->trans, bp1,
                          XFS_DA_LOGRANGE(leaf1, entry1, sizeof(*entry1)));
        if (args->rmtblkno) {
                ASSERT((entry1->flags & XFS_ATTR_LOCAL) == 0);
                name_rmt = xfs_attr_leaf_name_remote(leaf1, args->index);
                name_rmt->valueblk = cpu_to_be32(args->rmtblkno);
                name_rmt->valuelen = cpu_to_be32(args->valuelen);
-               xfs_da_log_buf(args->trans, bp1,
+               xfs_trans_log_buf(args->trans, bp1,
                         XFS_DA_LOGRANGE(leaf1, name_rmt, sizeof(*name_rmt)));
        }
 
        entry2->flags |= XFS_ATTR_INCOMPLETE;
-       xfs_da_log_buf(args->trans, bp2,
+       xfs_trans_log_buf(args->trans, bp2,
                          XFS_DA_LOGRANGE(leaf2, entry2, sizeof(*entry2)));
        if ((entry2->flags & XFS_ATTR_LOCAL) == 0) {
                name_rmt = xfs_attr_leaf_name_remote(leaf2, args->index2);
                name_rmt->valueblk = 0;
                name_rmt->valuelen = 0;
-               xfs_da_log_buf(args->trans, bp2,
+               xfs_trans_log_buf(args->trans, bp2,
                         XFS_DA_LOGRANGE(leaf2, name_rmt, sizeof(*name_rmt)));
        }
-       xfs_da_buf_done(bp1);
-       if (bp1 != bp2)
-               xfs_da_buf_done(bp2);
 
        /*
         * Commit the flag value change and start the next trans in series.
@@ -2706,7 +2721,7 @@ xfs_attr_root_inactive(xfs_trans_t **trans, xfs_inode_t *dp)
 {
        xfs_da_blkinfo_t *info;
        xfs_daddr_t blkno;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
        int error;
 
        /*
@@ -2718,20 +2733,20 @@ xfs_attr_root_inactive(xfs_trans_t **trans, xfs_inode_t *dp)
        error = xfs_da_read_buf(*trans, dp, 0, -1, &bp, XFS_ATTR_FORK);
        if (error)
                return(error);
-       blkno = xfs_da_blkno(bp);
+       blkno = XFS_BUF_ADDR(bp);
 
        /*
         * Invalidate the tree, even if the "tree" is only a single leaf block.
         * This is a depth-first traversal!
         */
-       info = bp->data;
+       info = bp->b_addr;
        if (info->magic == cpu_to_be16(XFS_DA_NODE_MAGIC)) {
                error = xfs_attr_node_inactive(trans, dp, bp, 1);
        } else if (info->magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC)) {
                error = xfs_attr_leaf_inactive(trans, dp, bp);
        } else {
                error = XFS_ERROR(EIO);
-               xfs_da_brelse(*trans, bp);
+               xfs_trans_brelse(*trans, bp);
        }
        if (error)
                return(error);
@@ -2742,7 +2757,7 @@ xfs_attr_root_inactive(xfs_trans_t **trans, xfs_inode_t *dp)
        error = xfs_da_get_buf(*trans, dp, 0, blkno, &bp, XFS_ATTR_FORK);
        if (error)
                return(error);
-       xfs_da_binval(*trans, bp);      /* remove from cache */
+       xfs_trans_binval(*trans, bp);   /* remove from cache */
        /*
         * Commit the invalidate and start the next transaction.
         */
@@ -2756,34 +2771,37 @@ xfs_attr_root_inactive(xfs_trans_t **trans, xfs_inode_t *dp)
  * We're doing a depth-first traversal in order to invalidate everything.
  */
 STATIC int
-xfs_attr_node_inactive(xfs_trans_t **trans, xfs_inode_t *dp, xfs_dabuf_t *bp,
-                                  int level)
+xfs_attr_node_inactive(
+       struct xfs_trans **trans,
+       struct xfs_inode *dp,
+       struct xfs_buf  *bp,
+       int             level)
 {
        xfs_da_blkinfo_t *info;
        xfs_da_intnode_t *node;
        xfs_dablk_t child_fsb;
        xfs_daddr_t parent_blkno, child_blkno;
        int error, count, i;
-       xfs_dabuf_t *child_bp;
+       struct xfs_buf *child_bp;
 
        /*
         * Since this code is recursive (gasp!) we must protect ourselves.
         */
        if (level > XFS_DA_NODE_MAXDEPTH) {
-               xfs_da_brelse(*trans, bp);      /* no locks for later trans */
+               xfs_trans_brelse(*trans, bp);   /* no locks for later trans */
                return(XFS_ERROR(EIO));
        }
 
-       node = bp->data;
+       node = bp->b_addr;
        ASSERT(node->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
-       parent_blkno = xfs_da_blkno(bp);        /* save for re-read later */
+       parent_blkno = XFS_BUF_ADDR(bp);        /* save for re-read later */
        count = be16_to_cpu(node->hdr.count);
        if (!count) {
-               xfs_da_brelse(*trans, bp);
+               xfs_trans_brelse(*trans, bp);
                return(0);
        }
        child_fsb = be32_to_cpu(node->btree[0].before);
-       xfs_da_brelse(*trans, bp);      /* no locks for later trans */
+       xfs_trans_brelse(*trans, bp);   /* no locks for later trans */
 
        /*
         * If this is the node level just above the leaves, simply loop
@@ -2803,12 +2821,12 @@ xfs_attr_node_inactive(xfs_trans_t **trans, xfs_inode_t *dp, xfs_dabuf_t *bp,
                        return(error);
                if (child_bp) {
                                                /* save for re-read later */
-                       child_blkno = xfs_da_blkno(child_bp);
+                       child_blkno = XFS_BUF_ADDR(child_bp);
 
                        /*
                         * Invalidate the subtree, however we have to.
                         */
-                       info = child_bp->data;
+                       info = child_bp->b_addr;
                        if (info->magic == cpu_to_be16(XFS_DA_NODE_MAGIC)) {
                                error = xfs_attr_node_inactive(trans, dp,
                                                child_bp, level+1);
@@ -2817,7 +2835,7 @@ xfs_attr_node_inactive(xfs_trans_t **trans, xfs_inode_t *dp, xfs_dabuf_t *bp,
                                                child_bp);
                        } else {
                                error = XFS_ERROR(EIO);
-                               xfs_da_brelse(*trans, child_bp);
+                               xfs_trans_brelse(*trans, child_bp);
                        }
                        if (error)
                                return(error);
@@ -2830,7 +2848,7 @@ xfs_attr_node_inactive(xfs_trans_t **trans, xfs_inode_t *dp, xfs_dabuf_t *bp,
                                &child_bp, XFS_ATTR_FORK);
                        if (error)
                                return(error);
-                       xfs_da_binval(*trans, child_bp);
+                       xfs_trans_binval(*trans, child_bp);
                }
 
                /*
@@ -2843,7 +2861,7 @@ xfs_attr_node_inactive(xfs_trans_t **trans, xfs_inode_t *dp, xfs_dabuf_t *bp,
                        if (error)
                                return(error);
                        child_fsb = be32_to_cpu(node->btree[i+1].before);
-                       xfs_da_brelse(*trans, bp);
+                       xfs_trans_brelse(*trans, bp);
                }
                /*
                 * Atomically commit the whole invalidate stuff.
@@ -2863,7 +2881,10 @@ xfs_attr_node_inactive(xfs_trans_t **trans, xfs_inode_t *dp, xfs_dabuf_t *bp,
  * caught holding something that the logging code wants to flush to disk.
  */
 STATIC int
-xfs_attr_leaf_inactive(xfs_trans_t **trans, xfs_inode_t *dp, xfs_dabuf_t *bp)
+xfs_attr_leaf_inactive(
+       struct xfs_trans **trans,
+       struct xfs_inode *dp,
+       struct xfs_buf  *bp)
 {
        xfs_attr_leafblock_t *leaf;
        xfs_attr_leaf_entry_t *entry;
@@ -2871,7 +2892,7 @@ xfs_attr_leaf_inactive(xfs_trans_t **trans, xfs_inode_t *dp, xfs_dabuf_t *bp)
        xfs_attr_inactive_list_t *list, *lp;
        int error, count, size, tmp, i;
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
 
        /*
@@ -2892,7 +2913,7 @@ xfs_attr_leaf_inactive(xfs_trans_t **trans, xfs_inode_t *dp, xfs_dabuf_t *bp)
         * If there are no "remote" values, we're done.
         */
        if (count == 0) {
-               xfs_da_brelse(*trans, bp);
+               xfs_trans_brelse(*trans, bp);
                return(0);
        }
 
@@ -2919,7 +2940,7 @@ xfs_attr_leaf_inactive(xfs_trans_t **trans, xfs_inode_t *dp, xfs_dabuf_t *bp)
                        }
                }
        }
-       xfs_da_brelse(*trans, bp);      /* unlock for trans. in freextent() */
+       xfs_trans_brelse(*trans, bp);   /* unlock for trans. in freextent() */
 
        /*
         * Invalidate each of the "remote" value extents.
index 9c7d22fdcf4d8ea0412d840a109f14c3923d3aab..dea17722945e1646a54498047c31c6918421f8ae 100644 (file)
@@ -31,7 +31,6 @@
 struct attrlist;
 struct attrlist_cursor_kern;
 struct xfs_attr_list_context;
-struct xfs_dabuf;
 struct xfs_da_args;
 struct xfs_da_state;
 struct xfs_da_state_blk;
@@ -215,7 +214,7 @@ int xfs_attr_shortform_getvalue(struct xfs_da_args *args);
 int    xfs_attr_shortform_to_leaf(struct xfs_da_args *args);
 int    xfs_attr_shortform_remove(struct xfs_da_args *args);
 int    xfs_attr_shortform_list(struct xfs_attr_list_context *context);
-int    xfs_attr_shortform_allfit(struct xfs_dabuf *bp, struct xfs_inode *dp);
+int    xfs_attr_shortform_allfit(struct xfs_buf *bp, struct xfs_inode *dp);
 int    xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes);
 
 
@@ -223,7 +222,7 @@ int xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes);
  * Internal routines when attribute fork size == XFS_LBSIZE(mp).
  */
 int    xfs_attr_leaf_to_node(struct xfs_da_args *args);
-int    xfs_attr_leaf_to_shortform(struct xfs_dabuf *bp,
+int    xfs_attr_leaf_to_shortform(struct xfs_buf *bp,
                                   struct xfs_da_args *args, int forkoff);
 int    xfs_attr_leaf_clearflag(struct xfs_da_args *args);
 int    xfs_attr_leaf_setflag(struct xfs_da_args *args);
@@ -235,14 +234,14 @@ int       xfs_attr_leaf_flipflags(xfs_da_args_t *args);
 int    xfs_attr_leaf_split(struct xfs_da_state *state,
                                   struct xfs_da_state_blk *oldblk,
                                   struct xfs_da_state_blk *newblk);
-int    xfs_attr_leaf_lookup_int(struct xfs_dabuf *leaf,
+int    xfs_attr_leaf_lookup_int(struct xfs_buf *leaf,
                                        struct xfs_da_args *args);
-int    xfs_attr_leaf_getvalue(struct xfs_dabuf *bp, struct xfs_da_args *args);
-int    xfs_attr_leaf_add(struct xfs_dabuf *leaf_buffer,
+int    xfs_attr_leaf_getvalue(struct xfs_buf *bp, struct xfs_da_args *args);
+int    xfs_attr_leaf_add(struct xfs_buf *leaf_buffer,
                                 struct xfs_da_args *args);
-int    xfs_attr_leaf_remove(struct xfs_dabuf *leaf_buffer,
+int    xfs_attr_leaf_remove(struct xfs_buf *leaf_buffer,
                                    struct xfs_da_args *args);
-int    xfs_attr_leaf_list_int(struct xfs_dabuf *bp,
+int    xfs_attr_leaf_list_int(struct xfs_buf *bp,
                                      struct xfs_attr_list_context *context);
 
 /*
@@ -257,9 +256,9 @@ int xfs_attr_root_inactive(struct xfs_trans **trans, struct xfs_inode *dp);
 /*
  * Utility routines.
  */
-xfs_dahash_t   xfs_attr_leaf_lasthash(struct xfs_dabuf *bp, int *count);
-int    xfs_attr_leaf_order(struct xfs_dabuf *leaf1_bp,
-                                  struct xfs_dabuf *leaf2_bp);
+xfs_dahash_t   xfs_attr_leaf_lasthash(struct xfs_buf *bp, int *count);
+int    xfs_attr_leaf_order(struct xfs_buf *leaf1_bp,
+                                  struct xfs_buf *leaf2_bp);
 int    xfs_attr_leaf_newentsize(int namelen, int valuelen, int blocksize,
                                        int *local);
 #endif /* __XFS_ATTR_LEAF_H__ */
index 58b815ec8c91f6ef70fef988eecfd74533bd08b0..848ffa77707b98bf272f61fc162f6e361504926e 100644 (file)
@@ -5517,7 +5517,7 @@ xfs_getbmap(
                if (xfs_get_extsz_hint(ip) ||
                    ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC|XFS_DIFLAG_APPEND)){
                        prealloced = 1;
-                       fixlen = XFS_MAXIOFFSET(mp);
+                       fixlen = mp->m_super->s_maxbytes;
                } else {
                        prealloced = 0;
                        fixlen = XFS_ISIZE(ip);
index 269b35c084dab6906267ccf66f473c98e85ce473..d7a9dd735e1e429a1787d8b0e8e1c9ddde5eff10 100644 (file)
@@ -164,14 +164,49 @@ xfs_buf_stale(
        ASSERT(atomic_read(&bp->b_hold) >= 1);
 }
 
+static int
+xfs_buf_get_maps(
+       struct xfs_buf          *bp,
+       int                     map_count)
+{
+       ASSERT(bp->b_maps == NULL);
+       bp->b_map_count = map_count;
+
+       if (map_count == 1) {
+               bp->b_maps = &bp->b_map;
+               return 0;
+       }
+
+       bp->b_maps = kmem_zalloc(map_count * sizeof(struct xfs_buf_map),
+                               KM_NOFS);
+       if (!bp->b_maps)
+               return ENOMEM;
+       return 0;
+}
+
+/*
+ *     Frees b_pages if it was allocated.
+ */
+static void
+xfs_buf_free_maps(
+       struct xfs_buf  *bp)
+{
+       if (bp->b_maps != &bp->b_map) {
+               kmem_free(bp->b_maps);
+               bp->b_maps = NULL;
+       }
+}
+
 struct xfs_buf *
-xfs_buf_alloc(
+_xfs_buf_alloc(
        struct xfs_buftarg      *target,
-       xfs_daddr_t             blkno,
-       size_t                  numblks,
+       struct xfs_buf_map      *map,
+       int                     nmaps,
        xfs_buf_flags_t         flags)
 {
        struct xfs_buf          *bp;
+       int                     error;
+       int                     i;
 
        bp = kmem_zone_zalloc(xfs_buf_zone, KM_NOFS);
        if (unlikely(!bp))
@@ -192,16 +227,28 @@ xfs_buf_alloc(
        sema_init(&bp->b_sema, 0); /* held, no waiters */
        XB_SET_OWNER(bp);
        bp->b_target = target;
+       bp->b_flags = flags;
 
        /*
         * Set length and io_length to the same value initially.
         * I/O routines should use io_length, which will be the same in
         * most cases but may be reset (e.g. XFS recovery).
         */
-       bp->b_length = numblks;
-       bp->b_io_length = numblks;
-       bp->b_flags = flags;
-       bp->b_bn = blkno;
+       error = xfs_buf_get_maps(bp, nmaps);
+       if (error)  {
+               kmem_zone_free(xfs_buf_zone, bp);
+               return NULL;
+       }
+
+       bp->b_bn = map[0].bm_bn;
+       bp->b_length = 0;
+       for (i = 0; i < nmaps; i++) {
+               bp->b_maps[i].bm_bn = map[i].bm_bn;
+               bp->b_maps[i].bm_len = map[i].bm_len;
+               bp->b_length += map[i].bm_len;
+       }
+       bp->b_io_length = bp->b_length;
+
        atomic_set(&bp->b_pin_count, 0);
        init_waitqueue_head(&bp->b_waiters);
 
@@ -280,6 +327,7 @@ xfs_buf_free(
        } else if (bp->b_flags & _XBF_KMEM)
                kmem_free(bp->b_addr);
        _xfs_buf_free_pages(bp);
+       xfs_buf_free_maps(bp);
        kmem_zone_free(xfs_buf_zone, bp);
 }
 
@@ -327,8 +375,9 @@ xfs_buf_allocate_memory(
        }
 
 use_alloc_page:
-       start = BBTOB(bp->b_bn) >> PAGE_SHIFT;
-       end = (BBTOB(bp->b_bn + bp->b_length) + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       start = BBTOB(bp->b_map.bm_bn) >> PAGE_SHIFT;
+       end = (BBTOB(bp->b_map.bm_bn + bp->b_length) + PAGE_SIZE - 1)
+                                                               >> PAGE_SHIFT;
        page_count = end - start;
        error = _xfs_buf_get_pages(bp, page_count, flags);
        if (unlikely(error))
@@ -425,8 +474,8 @@ _xfs_buf_map_pages(
 xfs_buf_t *
 _xfs_buf_find(
        struct xfs_buftarg      *btp,
-       xfs_daddr_t             blkno,
-       size_t                  numblks,
+       struct xfs_buf_map      *map,
+       int                     nmaps,
        xfs_buf_flags_t         flags,
        xfs_buf_t               *new_bp)
 {
@@ -435,7 +484,12 @@ _xfs_buf_find(
        struct rb_node          **rbp;
        struct rb_node          *parent;
        xfs_buf_t               *bp;
+       xfs_daddr_t             blkno = map[0].bm_bn;
+       int                     numblks = 0;
+       int                     i;
 
+       for (i = 0; i < nmaps; i++)
+               numblks += map[i].bm_len;
        numbytes = BBTOB(numblks);
 
        /* Check for IOs smaller than the sector size / not sector aligned */
@@ -527,31 +581,31 @@ found:
  * more hits than misses.
  */
 struct xfs_buf *
-xfs_buf_get(
-       xfs_buftarg_t           *target,
-       xfs_daddr_t             blkno,
-       size_t                  numblks,
+xfs_buf_get_map(
+       struct xfs_buftarg      *target,
+       struct xfs_buf_map      *map,
+       int                     nmaps,
        xfs_buf_flags_t         flags)
 {
        struct xfs_buf          *bp;
        struct xfs_buf          *new_bp;
        int                     error = 0;
 
-       bp = _xfs_buf_find(target, blkno, numblks, flags, NULL);
+       bp = _xfs_buf_find(target, map, nmaps, flags, NULL);
        if (likely(bp))
                goto found;
 
-       new_bp = xfs_buf_alloc(target, blkno, numblks, flags);
+       new_bp = _xfs_buf_alloc(target, map, nmaps, flags);
        if (unlikely(!new_bp))
                return NULL;
 
        error = xfs_buf_allocate_memory(new_bp, flags);
        if (error) {
-               kmem_zone_free(xfs_buf_zone, new_bp);
+               xfs_buf_free(new_bp);
                return NULL;
        }
 
-       bp = _xfs_buf_find(target, blkno, numblks, flags, new_bp);
+       bp = _xfs_buf_find(target, map, nmaps, flags, new_bp);
        if (!bp) {
                xfs_buf_free(new_bp);
                return NULL;
@@ -560,8 +614,6 @@ xfs_buf_get(
        if (bp != new_bp)
                xfs_buf_free(new_bp);
 
-       bp->b_io_length = bp->b_length;
-
 found:
        if (!bp->b_addr) {
                error = _xfs_buf_map_pages(bp, flags);
@@ -584,7 +636,7 @@ _xfs_buf_read(
        xfs_buf_flags_t         flags)
 {
        ASSERT(!(flags & XBF_WRITE));
-       ASSERT(bp->b_bn != XFS_BUF_DADDR_NULL);
+       ASSERT(bp->b_map.bm_bn != XFS_BUF_DADDR_NULL);
 
        bp->b_flags &= ~(XBF_WRITE | XBF_ASYNC | XBF_READ_AHEAD);
        bp->b_flags |= flags & (XBF_READ | XBF_ASYNC | XBF_READ_AHEAD);
@@ -596,17 +648,17 @@ _xfs_buf_read(
 }
 
 xfs_buf_t *
-xfs_buf_read(
-       xfs_buftarg_t           *target,
-       xfs_daddr_t             blkno,
-       size_t                  numblks,
+xfs_buf_read_map(
+       struct xfs_buftarg      *target,
+       struct xfs_buf_map      *map,
+       int                     nmaps,
        xfs_buf_flags_t         flags)
 {
-       xfs_buf_t               *bp;
+       struct xfs_buf          *bp;
 
        flags |= XBF_READ;
 
-       bp = xfs_buf_get(target, blkno, numblks, flags);
+       bp = xfs_buf_get_map(target, map, nmaps, flags);
        if (bp) {
                trace_xfs_buf_read(bp, flags, _RET_IP_);
 
@@ -634,15 +686,15 @@ xfs_buf_read(
  *     safe manner.
  */
 void
-xfs_buf_readahead(
-       xfs_buftarg_t           *target,
-       xfs_daddr_t             blkno,
-       size_t                  numblks)
+xfs_buf_readahead_map(
+       struct xfs_buftarg      *target,
+       struct xfs_buf_map      *map,
+       int                     nmaps)
 {
        if (bdi_read_congested(target->bt_bdi))
                return;
 
-       xfs_buf_read(target, blkno, numblks,
+       xfs_buf_read_map(target, map, nmaps,
                     XBF_TRYLOCK|XBF_ASYNC|XBF_READ_AHEAD);
 }
 
@@ -665,8 +717,10 @@ xfs_buf_read_uncached(
                return NULL;
 
        /* set up the buffer for a read IO */
-       XFS_BUF_SET_ADDR(bp, daddr);
-       XFS_BUF_READ(bp);
+       ASSERT(bp->b_map_count == 1);
+       bp->b_bn = daddr;
+       bp->b_maps[0].bm_bn = daddr;
+       bp->b_flags |= XBF_READ;
 
        xfsbdstrat(target->bt_mount, bp);
        error = xfs_buf_iowait(bp);
@@ -694,7 +748,11 @@ xfs_buf_set_empty(
        bp->b_addr = NULL;
        bp->b_length = numblks;
        bp->b_io_length = numblks;
+
+       ASSERT(bp->b_map_count == 1);
        bp->b_bn = XFS_BUF_DADDR_NULL;
+       bp->b_maps[0].bm_bn = XFS_BUF_DADDR_NULL;
+       bp->b_maps[0].bm_len = bp->b_length;
 }
 
 static inline struct page *
@@ -758,9 +816,10 @@ xfs_buf_get_uncached(
 {
        unsigned long           page_count;
        int                     error, i;
-       xfs_buf_t               *bp;
+       struct xfs_buf          *bp;
+       DEFINE_SINGLE_BUF_MAP(map, XFS_BUF_DADDR_NULL, numblks);
 
-       bp = xfs_buf_alloc(target, XFS_BUF_DADDR_NULL, numblks, 0);
+       bp = _xfs_buf_alloc(target, &map, 1, 0);
        if (unlikely(bp == NULL))
                goto fail;
 
@@ -791,6 +850,7 @@ xfs_buf_get_uncached(
                __free_page(bp->b_pages[i]);
        _xfs_buf_free_pages(bp);
  fail_free_buf:
+       xfs_buf_free_maps(bp);
        kmem_zone_free(xfs_buf_zone, bp);
  fail:
        return NULL;
@@ -1144,36 +1204,39 @@ xfs_buf_bio_end_io(
        bio_put(bio);
 }
 
-STATIC void
-_xfs_buf_ioapply(
-       xfs_buf_t               *bp)
+static void
+xfs_buf_ioapply_map(
+       struct xfs_buf  *bp,
+       int             map,
+       int             *buf_offset,
+       int             *count,
+       int             rw)
 {
-       int                     rw, map_i, total_nr_pages, nr_pages;
-       struct bio              *bio;
-       int                     offset = bp->b_offset;
-       int                     size = BBTOB(bp->b_io_length);
-       sector_t                sector = bp->b_bn;
+       int             page_index;
+       int             total_nr_pages = bp->b_page_count;
+       int             nr_pages;
+       struct bio      *bio;
+       sector_t        sector =  bp->b_maps[map].bm_bn;
+       int             size;
+       int             offset;
 
        total_nr_pages = bp->b_page_count;
-       map_i = 0;
 
-       if (bp->b_flags & XBF_WRITE) {
-               if (bp->b_flags & XBF_SYNCIO)
-                       rw = WRITE_SYNC;
-               else
-                       rw = WRITE;
-               if (bp->b_flags & XBF_FUA)
-                       rw |= REQ_FUA;
-               if (bp->b_flags & XBF_FLUSH)
-                       rw |= REQ_FLUSH;
-       } else if (bp->b_flags & XBF_READ_AHEAD) {
-               rw = READA;
-       } else {
-               rw = READ;
+       /* skip the pages in the buffer before the start offset */
+       page_index = 0;
+       offset = *buf_offset;
+       while (offset >= PAGE_SIZE) {
+               page_index++;
+               offset -= PAGE_SIZE;
        }
 
-       /* we only use the buffer cache for meta-data */
-       rw |= REQ_META;
+       /*
+        * Limit the IO size to the length of the current vector, and update the
+        * remaining IO count for the next time around.
+        */
+       size = min_t(int, BBTOB(bp->b_maps[map].bm_len), *count);
+       *count -= size;
+       *buf_offset += size;
 
 next_chunk:
        atomic_inc(&bp->b_io_remaining);
@@ -1188,13 +1251,14 @@ next_chunk:
        bio->bi_private = bp;
 
 
-       for (; size && nr_pages; nr_pages--, map_i++) {
+       for (; size && nr_pages; nr_pages--, page_index++) {
                int     rbytes, nbytes = PAGE_SIZE - offset;
 
                if (nbytes > size)
                        nbytes = size;
 
-               rbytes = bio_add_page(bio, bp->b_pages[map_i], nbytes, offset);
+               rbytes = bio_add_page(bio, bp->b_pages[page_index], nbytes,
+                                     offset);
                if (rbytes < nbytes)
                        break;
 
@@ -1216,6 +1280,54 @@ next_chunk:
                xfs_buf_ioerror(bp, EIO);
                bio_put(bio);
        }
+
+}
+
+STATIC void
+_xfs_buf_ioapply(
+       struct xfs_buf  *bp)
+{
+       struct blk_plug plug;
+       int             rw;
+       int             offset;
+       int             size;
+       int             i;
+
+       if (bp->b_flags & XBF_WRITE) {
+               if (bp->b_flags & XBF_SYNCIO)
+                       rw = WRITE_SYNC;
+               else
+                       rw = WRITE;
+               if (bp->b_flags & XBF_FUA)
+                       rw |= REQ_FUA;
+               if (bp->b_flags & XBF_FLUSH)
+                       rw |= REQ_FLUSH;
+       } else if (bp->b_flags & XBF_READ_AHEAD) {
+               rw = READA;
+       } else {
+               rw = READ;
+       }
+
+       /* we only use the buffer cache for meta-data */
+       rw |= REQ_META;
+
+       /*
+        * Walk all the vectors issuing IO on them. Set up the initial offset
+        * into the buffer and the desired IO size before we start -
+        * _xfs_buf_ioapply_vec() will modify them appropriately for each
+        * subsequent call.
+        */
+       offset = bp->b_offset;
+       size = BBTOB(bp->b_io_length);
+       blk_start_plug(&plug);
+       for (i = 0; i < bp->b_map_count; i++) {
+               xfs_buf_ioapply_map(bp, i, &offset, &size, rw);
+               if (bp->b_error)
+                       break;
+               if (size <= 0)
+                       break;  /* all done */
+       }
+       blk_finish_plug(&plug);
 }
 
 void
@@ -1557,7 +1669,7 @@ xfs_buf_cmp(
        struct xfs_buf  *bp = container_of(b, struct xfs_buf, b_list);
        xfs_daddr_t             diff;
 
-       diff = ap->b_bn - bp->b_bn;
+       diff = ap->b_map.bm_bn - bp->b_map.bm_bn;
        if (diff < 0)
                return -1;
        if (diff > 0)
index 79344c48008eedaab4aaa21dc6ed28fe328607d3..d03b73b9604e3d55d93f1fdc08201512b01449dd 100644 (file)
@@ -58,6 +58,7 @@ typedef enum {
 #define _XBF_PAGES     (1 << 20)/* backed by refcounted pages */
 #define _XBF_KMEM      (1 << 21)/* backed by heap memory */
 #define _XBF_DELWRI_Q  (1 << 22)/* buffer on a delwri queue */
+#define _XBF_COMPOUND  (1 << 23)/* compound buffer */
 
 typedef unsigned int xfs_buf_flags_t;
 
@@ -75,7 +76,8 @@ typedef unsigned int xfs_buf_flags_t;
        { XBF_UNMAPPED,         "UNMAPPED" },   /* ditto */\
        { _XBF_PAGES,           "PAGES" }, \
        { _XBF_KMEM,            "KMEM" }, \
-       { _XBF_DELWRI_Q,        "DELWRI_Q" }
+       { _XBF_DELWRI_Q,        "DELWRI_Q" }, \
+       { _XBF_COMPOUND,        "COMPOUND" }
 
 typedef struct xfs_buftarg {
        dev_t                   bt_dev;
@@ -98,6 +100,14 @@ typedef void (*xfs_buf_iodone_t)(struct xfs_buf *);
 
 #define XB_PAGES       2
 
+struct xfs_buf_map {
+       xfs_daddr_t             bm_bn;  /* block number for I/O */
+       int                     bm_len; /* size of I/O */
+};
+
+#define DEFINE_SINGLE_BUF_MAP(map, blkno, numblk) \
+       struct xfs_buf_map (map) = { .bm_bn = (blkno), .bm_len = (numblk) };
+
 typedef struct xfs_buf {
        /*
         * first cacheline holds all the fields needed for an uncontended cache
@@ -107,7 +117,7 @@ typedef struct xfs_buf {
         * fast-path on locking.
         */
        struct rb_node          b_rbnode;       /* rbtree node */
-       xfs_daddr_t             b_bn;           /* block number for I/O */
+       xfs_daddr_t             b_bn;           /* block number of buffer */
        int                     b_length;       /* size of buffer in BBs */
        atomic_t                b_hold;         /* reference count */
        atomic_t                b_lru_ref;      /* lru reclaim ref count */
@@ -127,12 +137,16 @@ typedef struct xfs_buf {
        struct xfs_trans        *b_transp;
        struct page             **b_pages;      /* array of page pointers */
        struct page             *b_page_array[XB_PAGES]; /* inline pages */
+       struct xfs_buf_map      *b_maps;        /* compound buffer map */
+       struct xfs_buf_map      b_map;          /* inline compound buffer map */
+       int                     b_map_count;
        int                     b_io_length;    /* IO size in BBs */
        atomic_t                b_pin_count;    /* pin count */
        atomic_t                b_io_remaining; /* #outstanding I/O requests */
        unsigned int            b_page_count;   /* size of page array */
        unsigned int            b_offset;       /* page offset in first page */
        unsigned short          b_error;        /* error code on I/O */
+
 #ifdef XFS_BUF_LOCK_TRACKING
        int                     b_last_holder;
 #endif
@@ -140,22 +154,78 @@ typedef struct xfs_buf {
 
 
 /* Finding and Reading Buffers */
-struct xfs_buf *_xfs_buf_find(struct xfs_buftarg *target, xfs_daddr_t blkno,
-                               size_t numblks, xfs_buf_flags_t flags,
-                               struct xfs_buf *new_bp);
-#define xfs_incore(buftarg,blkno,len,lockit) \
-       _xfs_buf_find(buftarg, blkno ,len, lockit, NULL)
-
-struct xfs_buf *xfs_buf_get(struct xfs_buftarg *target, xfs_daddr_t blkno,
-                               size_t numblks, xfs_buf_flags_t flags);
-struct xfs_buf *xfs_buf_read(struct xfs_buftarg *target, xfs_daddr_t blkno,
-                               size_t numblks, xfs_buf_flags_t flags);
-void xfs_buf_readahead(struct xfs_buftarg *target, xfs_daddr_t blkno,
-                               size_t numblks);
+struct xfs_buf *_xfs_buf_find(struct xfs_buftarg *target,
+                             struct xfs_buf_map *map, int nmaps,
+                             xfs_buf_flags_t flags, struct xfs_buf *new_bp);
+
+static inline struct xfs_buf *
+xfs_incore(
+       struct xfs_buftarg      *target,
+       xfs_daddr_t             blkno,
+       size_t                  numblks,
+       xfs_buf_flags_t         flags)
+{
+       DEFINE_SINGLE_BUF_MAP(map, blkno, numblks);
+       return _xfs_buf_find(target, &map, 1, flags, NULL);
+}
+
+struct xfs_buf *_xfs_buf_alloc(struct xfs_buftarg *target,
+                              struct xfs_buf_map *map, int nmaps,
+                              xfs_buf_flags_t flags);
+
+static inline struct xfs_buf *
+xfs_buf_alloc(
+       struct xfs_buftarg      *target,
+       xfs_daddr_t             blkno,
+       size_t                  numblks,
+       xfs_buf_flags_t         flags)
+{
+       DEFINE_SINGLE_BUF_MAP(map, blkno, numblks);
+       return _xfs_buf_alloc(target, &map, 1, flags);
+}
+
+struct xfs_buf *xfs_buf_get_map(struct xfs_buftarg *target,
+                              struct xfs_buf_map *map, int nmaps,
+                              xfs_buf_flags_t flags);
+struct xfs_buf *xfs_buf_read_map(struct xfs_buftarg *target,
+                              struct xfs_buf_map *map, int nmaps,
+                              xfs_buf_flags_t flags);
+void xfs_buf_readahead_map(struct xfs_buftarg *target,
+                              struct xfs_buf_map *map, int nmaps);
+
+static inline struct xfs_buf *
+xfs_buf_get(
+       struct xfs_buftarg      *target,
+       xfs_daddr_t             blkno,
+       size_t                  numblks,
+       xfs_buf_flags_t         flags)
+{
+       DEFINE_SINGLE_BUF_MAP(map, blkno, numblks);
+       return xfs_buf_get_map(target, &map, 1, flags);
+}
+
+static inline struct xfs_buf *
+xfs_buf_read(
+       struct xfs_buftarg      *target,
+       xfs_daddr_t             blkno,
+       size_t                  numblks,
+       xfs_buf_flags_t         flags)
+{
+       DEFINE_SINGLE_BUF_MAP(map, blkno, numblks);
+       return xfs_buf_read_map(target, &map, 1, flags);
+}
+
+static inline void
+xfs_buf_readahead(
+       struct xfs_buftarg      *target,
+       xfs_daddr_t             blkno,
+       size_t                  numblks)
+{
+       DEFINE_SINGLE_BUF_MAP(map, blkno, numblks);
+       return xfs_buf_readahead_map(target, &map, 1);
+}
 
 struct xfs_buf *xfs_buf_get_empty(struct xfs_buftarg *target, size_t numblks);
-struct xfs_buf *xfs_buf_alloc(struct xfs_buftarg *target, xfs_daddr_t blkno,
-                               size_t numblks, xfs_buf_flags_t flags);
 void xfs_buf_set_empty(struct xfs_buf *bp, size_t numblks);
 int xfs_buf_associate_memory(struct xfs_buf *bp, void *mem, size_t length);
 
@@ -232,8 +302,18 @@ void xfs_buf_stale(struct xfs_buf *bp);
 #define XFS_BUF_UNWRITE(bp)    ((bp)->b_flags &= ~XBF_WRITE)
 #define XFS_BUF_ISWRITE(bp)    ((bp)->b_flags & XBF_WRITE)
 
-#define XFS_BUF_ADDR(bp)               ((bp)->b_bn)
-#define XFS_BUF_SET_ADDR(bp, bno)      ((bp)->b_bn = (xfs_daddr_t)(bno))
+/*
+ * These macros use the IO block map rather than b_bn. b_bn is now really
+ * just for the buffer cache index for cached buffers. As IO does not use b_bn
+ * anymore, uncached buffers do not use b_bn at all and hence must modify the IO
+ * map directly. Uncached buffers are not allowed to be discontiguous, so this
+ * is safe to do.
+ *
+ * In future, uncached buffers will pass the block number directly to the io
+ * request function and hence these macros will go away at that point.
+ */
+#define XFS_BUF_ADDR(bp)               ((bp)->b_map.bm_bn)
+#define XFS_BUF_SET_ADDR(bp, bno)      ((bp)->b_map.bm_bn = (xfs_daddr_t)(bno))
 
 static inline void xfs_buf_set_ref(struct xfs_buf *bp, int lru_ref)
 {
index d9e451115f980ac1529c4e89aa89665c82661360..a8d0ed911196120a80a26bb7232f79d3a5badee1 100644 (file)
@@ -153,33 +153,25 @@ STATIC void       xfs_buf_do_callbacks(struct xfs_buf *bp);
  * If the XFS_BLI_STALE flag has been set, then log nothing.
  */
 STATIC uint
-xfs_buf_item_size(
-       struct xfs_log_item     *lip)
+xfs_buf_item_size_segment(
+       struct xfs_buf_log_item *bip,
+       struct xfs_buf_log_format *blfp)
 {
-       struct xfs_buf_log_item *bip = BUF_ITEM(lip);
        struct xfs_buf          *bp = bip->bli_buf;
        uint                    nvecs;
        int                     next_bit;
        int                     last_bit;
 
-       ASSERT(atomic_read(&bip->bli_refcount) > 0);
-       if (bip->bli_flags & XFS_BLI_STALE) {
-               /*
-                * The buffer is stale, so all we need to log
-                * is the buf log format structure with the
-                * cancel flag in it.
-                */
-               trace_xfs_buf_item_size_stale(bip);
-               ASSERT(bip->bli_format.blf_flags & XFS_BLF_CANCEL);
-               return 1;
-       }
+       last_bit = xfs_next_bit(blfp->blf_data_map, blfp->blf_map_size, 0);
+       if (last_bit == -1)
+               return 0;
+
+       /*
+        * initial count for a dirty buffer is 2 vectors - the format structure
+        * and the first dirty region.
+        */
+       nvecs = 2;
 
-       ASSERT(bip->bli_flags & XFS_BLI_LOGGED);
-       nvecs = 1;
-       last_bit = xfs_next_bit(bip->bli_format.blf_data_map,
-                                        bip->bli_format.blf_map_size, 0);
-       ASSERT(last_bit != -1);
-       nvecs++;
        while (last_bit != -1) {
                /*
                 * This takes the bit number to start looking from and
@@ -187,16 +179,15 @@ xfs_buf_item_size(
                 * if there are no more bits set or the start bit is
                 * beyond the end of the bitmap.
                 */
-               next_bit = xfs_next_bit(bip->bli_format.blf_data_map,
-                                                bip->bli_format.blf_map_size,
-                                                last_bit + 1);
+               next_bit = xfs_next_bit(blfp->blf_data_map, blfp->blf_map_size,
+                                       last_bit + 1);
                /*
                 * If we run out of bits, leave the loop,
                 * else if we find a new set of bits bump the number of vecs,
                 * else keep scanning the current set of bits.
                 */
                if (next_bit == -1) {
-                       last_bit = -1;
+                       break;
                } else if (next_bit != last_bit + 1) {
                        last_bit = next_bit;
                        nvecs++;
@@ -210,22 +201,73 @@ xfs_buf_item_size(
                }
        }
 
-       trace_xfs_buf_item_size(bip);
        return nvecs;
 }
 
 /*
- * This is called to fill in the vector of log iovecs for the
- * given log buf item.  It fills the first entry with a buf log
- * format structure, and the rest point to contiguous chunks
- * within the buffer.
+ * This returns the number of log iovecs needed to log the given buf log item.
+ *
+ * It calculates this as 1 iovec for the buf log format structure and 1 for each
+ * stretch of non-contiguous chunks to be logged.  Contiguous chunks are logged
+ * in a single iovec.
+ *
+ * Discontiguous buffers need a format structure per region that that is being
+ * logged. This makes the changes in the buffer appear to log recovery as though
+ * they came from separate buffers, just like would occur if multiple buffers
+ * were used instead of a single discontiguous buffer. This enables
+ * discontiguous buffers to be in-memory constructs, completely transparent to
+ * what ends up on disk.
+ *
+ * If the XFS_BLI_STALE flag has been set, then log nothing but the buf log
+ * format structures.
  */
-STATIC void
-xfs_buf_item_format(
-       struct xfs_log_item     *lip,
-       struct xfs_log_iovec    *vecp)
+STATIC uint
+xfs_buf_item_size(
+       struct xfs_log_item     *lip)
 {
        struct xfs_buf_log_item *bip = BUF_ITEM(lip);
+       uint                    nvecs;
+       int                     i;
+
+       ASSERT(atomic_read(&bip->bli_refcount) > 0);
+       if (bip->bli_flags & XFS_BLI_STALE) {
+               /*
+                * The buffer is stale, so all we need to log
+                * is the buf log format structure with the
+                * cancel flag in it.
+                */
+               trace_xfs_buf_item_size_stale(bip);
+               ASSERT(bip->bli_format.blf_flags & XFS_BLF_CANCEL);
+               return bip->bli_format_count;
+       }
+
+       ASSERT(bip->bli_flags & XFS_BLI_LOGGED);
+
+       /*
+        * the vector count is based on the number of buffer vectors we have
+        * dirty bits in. This will only be greater than one when we have a
+        * compound buffer with more than one segment dirty. Hence for compound
+        * buffers we need to track which segment the dirty bits correspond to,
+        * and when we move from one segment to the next increment the vector
+        * count for the extra buf log format structure that will need to be
+        * written.
+        */
+       nvecs = 0;
+       for (i = 0; i < bip->bli_format_count; i++) {
+               nvecs += xfs_buf_item_size_segment(bip, &bip->bli_formats[i]);
+       }
+
+       trace_xfs_buf_item_size(bip);
+       return nvecs;
+}
+
+static struct xfs_log_iovec *
+xfs_buf_item_format_segment(
+       struct xfs_buf_log_item *bip,
+       struct xfs_log_iovec    *vecp,
+       uint                    offset,
+       struct xfs_buf_log_format *blfp)
+{
        struct xfs_buf  *bp = bip->bli_buf;
        uint            base_size;
        uint            nvecs;
@@ -235,40 +277,22 @@ xfs_buf_item_format(
        uint            nbits;
        uint            buffer_offset;
 
-       ASSERT(atomic_read(&bip->bli_refcount) > 0);
-       ASSERT((bip->bli_flags & XFS_BLI_LOGGED) ||
-              (bip->bli_flags & XFS_BLI_STALE));
+       /* copy the flags across from the base format item */
+       blfp->blf_flags = bip->bli_format.blf_flags;
 
        /*
-        * The size of the base structure is the size of the
-        * declared structure plus the space for the extra words
-        * of the bitmap.  We subtract one from the map size, because
-        * the first element of the bitmap is accounted for in the
-        * size of the base structure.
+        * Base size is the actual size of the ondisk structure - it reflects
+        * the actual size of the dirty bitmap rather than the size of the in
+        * memory structure.
         */
-       base_size =
-               (uint)(sizeof(xfs_buf_log_format_t) +
-                      ((bip->bli_format.blf_map_size - 1) * sizeof(uint)));
-       vecp->i_addr = &bip->bli_format;
+       base_size = offsetof(struct xfs_buf_log_format, blf_data_map) +
+                       (blfp->blf_map_size * sizeof(blfp->blf_data_map[0]));
+       vecp->i_addr = blfp;
        vecp->i_len = base_size;
        vecp->i_type = XLOG_REG_TYPE_BFORMAT;
        vecp++;
        nvecs = 1;
 
-       /*
-        * If it is an inode buffer, transfer the in-memory state to the
-        * format flags and clear the in-memory state. We do not transfer
-        * this state if the inode buffer allocation has not yet been committed
-        * to the log as setting the XFS_BLI_INODE_BUF flag will prevent
-        * correct replay of the inode allocation.
-        */
-       if (bip->bli_flags & XFS_BLI_INODE_BUF) {
-               if (!((bip->bli_flags & XFS_BLI_INODE_ALLOC_BUF) &&
-                     xfs_log_item_in_current_chkpt(lip)))
-                       bip->bli_format.blf_flags |= XFS_BLF_INODE_BUF;
-               bip->bli_flags &= ~XFS_BLI_INODE_BUF;
-       }
-
        if (bip->bli_flags & XFS_BLI_STALE) {
                /*
                 * The buffer is stale, so all we need to log
@@ -276,16 +300,15 @@ xfs_buf_item_format(
                 * cancel flag in it.
                 */
                trace_xfs_buf_item_format_stale(bip);
-               ASSERT(bip->bli_format.blf_flags & XFS_BLF_CANCEL);
-               bip->bli_format.blf_size = nvecs;
-               return;
+               ASSERT(blfp->blf_flags & XFS_BLF_CANCEL);
+               blfp->blf_size = nvecs;
+               return vecp;
        }
 
        /*
         * Fill in an iovec for each set of contiguous chunks.
         */
-       first_bit = xfs_next_bit(bip->bli_format.blf_data_map,
-                                        bip->bli_format.blf_map_size, 0);
+       first_bit = xfs_next_bit(blfp->blf_data_map, blfp->blf_map_size, 0);
        ASSERT(first_bit != -1);
        last_bit = first_bit;
        nbits = 1;
@@ -296,9 +319,8 @@ xfs_buf_item_format(
                 * if there are no more bits set or the start bit is
                 * beyond the end of the bitmap.
                 */
-               next_bit = xfs_next_bit(bip->bli_format.blf_data_map,
-                                                bip->bli_format.blf_map_size,
-                                                (uint)last_bit + 1);
+               next_bit = xfs_next_bit(blfp->blf_data_map, blfp->blf_map_size,
+                                       (uint)last_bit + 1);
                /*
                 * If we run out of bits fill in the last iovec and get
                 * out of the loop.
@@ -309,14 +331,14 @@ xfs_buf_item_format(
                 * keep counting and scanning.
                 */
                if (next_bit == -1) {
-                       buffer_offset = first_bit * XFS_BLF_CHUNK;
+                       buffer_offset = offset + first_bit * XFS_BLF_CHUNK;
                        vecp->i_addr = xfs_buf_offset(bp, buffer_offset);
                        vecp->i_len = nbits * XFS_BLF_CHUNK;
                        vecp->i_type = XLOG_REG_TYPE_BCHUNK;
                        nvecs++;
                        break;
                } else if (next_bit != last_bit + 1) {
-                       buffer_offset = first_bit * XFS_BLF_CHUNK;
+                       buffer_offset = offset + first_bit * XFS_BLF_CHUNK;
                        vecp->i_addr = xfs_buf_offset(bp, buffer_offset);
                        vecp->i_len = nbits * XFS_BLF_CHUNK;
                        vecp->i_type = XLOG_REG_TYPE_BCHUNK;
@@ -325,14 +347,17 @@ xfs_buf_item_format(
                        first_bit = next_bit;
                        last_bit = next_bit;
                        nbits = 1;
-               } else if (xfs_buf_offset(bp, next_bit << XFS_BLF_SHIFT) !=
-                          (xfs_buf_offset(bp, last_bit << XFS_BLF_SHIFT) +
+               } else if (xfs_buf_offset(bp, offset +
+                                             (next_bit << XFS_BLF_SHIFT)) !=
+                          (xfs_buf_offset(bp, offset +
+                                              (last_bit << XFS_BLF_SHIFT)) +
                            XFS_BLF_CHUNK)) {
-                       buffer_offset = first_bit * XFS_BLF_CHUNK;
+                       buffer_offset = offset + first_bit * XFS_BLF_CHUNK;
                        vecp->i_addr = xfs_buf_offset(bp, buffer_offset);
                        vecp->i_len = nbits * XFS_BLF_CHUNK;
                        vecp->i_type = XLOG_REG_TYPE_BCHUNK;
-/* You would think we need to bump the nvecs here too, but we do not
+/*
+ * You would think we need to bump the nvecs here too, but we do not
  * this number is used by recovery, and it gets confused by the boundary
  * split here
  *                     nvecs++;
@@ -347,6 +372,48 @@ xfs_buf_item_format(
                }
        }
        bip->bli_format.blf_size = nvecs;
+       return vecp;
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given log buf item.  It fills the first entry with a buf log
+ * format structure, and the rest point to contiguous chunks
+ * within the buffer.
+ */
+STATIC void
+xfs_buf_item_format(
+       struct xfs_log_item     *lip,
+       struct xfs_log_iovec    *vecp)
+{
+       struct xfs_buf_log_item *bip = BUF_ITEM(lip);
+       struct xfs_buf          *bp = bip->bli_buf;
+       uint                    offset = 0;
+       int                     i;
+
+       ASSERT(atomic_read(&bip->bli_refcount) > 0);
+       ASSERT((bip->bli_flags & XFS_BLI_LOGGED) ||
+              (bip->bli_flags & XFS_BLI_STALE));
+
+       /*
+        * If it is an inode buffer, transfer the in-memory state to the
+        * format flags and clear the in-memory state. We do not transfer
+        * this state if the inode buffer allocation has not yet been committed
+        * to the log as setting the XFS_BLI_INODE_BUF flag will prevent
+        * correct replay of the inode allocation.
+        */
+       if (bip->bli_flags & XFS_BLI_INODE_BUF) {
+               if (!((bip->bli_flags & XFS_BLI_INODE_ALLOC_BUF) &&
+                     xfs_log_item_in_current_chkpt(lip)))
+                       bip->bli_format.blf_flags |= XFS_BLF_INODE_BUF;
+               bip->bli_flags &= ~XFS_BLI_INODE_BUF;
+       }
+
+       for (i = 0; i < bip->bli_format_count; i++) {
+               vecp = xfs_buf_item_format_segment(bip, vecp, offset,
+                                               &bip->bli_formats[i]);
+               offset += bp->b_maps[i].bm_len;
+       }
 
        /*
         * Check to make sure everything is consistent.
@@ -622,6 +689,35 @@ static const struct xfs_item_ops xfs_buf_item_ops = {
        .iop_committing = xfs_buf_item_committing
 };
 
+STATIC int
+xfs_buf_item_get_format(
+       struct xfs_buf_log_item *bip,
+       int                     count)
+{
+       ASSERT(bip->bli_formats == NULL);
+       bip->bli_format_count = count;
+
+       if (count == 1) {
+               bip->bli_formats = &bip->bli_format;
+               return 0;
+       }
+
+       bip->bli_formats = kmem_zalloc(count * sizeof(struct xfs_buf_log_format),
+                               KM_SLEEP);
+       if (!bip->bli_formats)
+               return ENOMEM;
+       return 0;
+}
+
+STATIC void
+xfs_buf_item_free_format(
+       struct xfs_buf_log_item *bip)
+{
+       if (bip->bli_formats != &bip->bli_format) {
+               kmem_free(bip->bli_formats);
+               bip->bli_formats = NULL;
+       }
+}
 
 /*
  * Allocate a new buf log item to go with the given buffer.
@@ -639,6 +735,8 @@ xfs_buf_item_init(
        xfs_buf_log_item_t      *bip;
        int                     chunks;
        int                     map_size;
+       int                     error;
+       int                     i;
 
        /*
         * Check to see if there is already a buf log item for
@@ -650,25 +748,33 @@ xfs_buf_item_init(
        if (lip != NULL && lip->li_type == XFS_LI_BUF)
                return;
 
-       /*
-        * chunks is the number of XFS_BLF_CHUNK size pieces
-        * the buffer can be divided into. Make sure not to
-        * truncate any pieces.  map_size is the size of the
-        * bitmap needed to describe the chunks of the buffer.
-        */
-       chunks = (int)((BBTOB(bp->b_length) + (XFS_BLF_CHUNK - 1)) >>
-                                                               XFS_BLF_SHIFT);
-       map_size = (int)((chunks + NBWORD) >> BIT_TO_WORD_SHIFT);
-
-       bip = (xfs_buf_log_item_t*)kmem_zone_zalloc(xfs_buf_item_zone,
-                                                   KM_SLEEP);
+       bip = kmem_zone_zalloc(xfs_buf_item_zone, KM_SLEEP);
        xfs_log_item_init(mp, &bip->bli_item, XFS_LI_BUF, &xfs_buf_item_ops);
        bip->bli_buf = bp;
        xfs_buf_hold(bp);
-       bip->bli_format.blf_type = XFS_LI_BUF;
-       bip->bli_format.blf_blkno = (__int64_t)XFS_BUF_ADDR(bp);
-       bip->bli_format.blf_len = (ushort)bp->b_length;
-       bip->bli_format.blf_map_size = map_size;
+
+       /*
+        * chunks is the number of XFS_BLF_CHUNK size pieces the buffer
+        * can be divided into. Make sure not to truncate any pieces.
+        * map_size is the size of the bitmap needed to describe the
+        * chunks of the buffer.
+        *
+        * Discontiguous buffer support follows the layout of the underlying
+        * buffer. This makes the implementation as simple as possible.
+        */
+       error = xfs_buf_item_get_format(bip, bp->b_map_count);
+       ASSERT(error == 0);
+
+       for (i = 0; i < bip->bli_format_count; i++) {
+               chunks = DIV_ROUND_UP(BBTOB(bp->b_maps[i].bm_len),
+                                     XFS_BLF_CHUNK);
+               map_size = DIV_ROUND_UP(chunks, NBWORD);
+
+               bip->bli_formats[i].blf_type = XFS_LI_BUF;
+               bip->bli_formats[i].blf_blkno = bp->b_maps[i].bm_bn;
+               bip->bli_formats[i].blf_len = bp->b_maps[i].bm_len;
+               bip->bli_formats[i].blf_map_size = map_size;
+       }
 
 #ifdef XFS_TRANS_DEBUG
        /*
@@ -699,10 +805,11 @@ xfs_buf_item_init(
  * item's bitmap.
  */
 void
-xfs_buf_item_log(
-       xfs_buf_log_item_t      *bip,
+xfs_buf_item_log_segment(
+       struct xfs_buf_log_item *bip,
        uint                    first,
-       uint                    last)
+       uint                    last,
+       uint                    *map)
 {
        uint            first_bit;
        uint            last_bit;
@@ -714,12 +821,6 @@ xfs_buf_item_log(
        uint            end_bit;
        uint            mask;
 
-       /*
-        * Mark the item as having some dirty data for
-        * quick reference in xfs_buf_item_dirty.
-        */
-       bip->bli_flags |= XFS_BLI_DIRTY;
-
        /*
         * Convert byte offsets to bit numbers.
         */
@@ -736,7 +837,7 @@ xfs_buf_item_log(
         * to set a bit in.
         */
        word_num = first_bit >> BIT_TO_WORD_SHIFT;
-       wordp = &(bip->bli_format.blf_data_map[word_num]);
+       wordp = &map[word_num];
 
        /*
         * Calculate the starting bit in the first word.
@@ -783,6 +884,51 @@ xfs_buf_item_log(
        xfs_buf_item_log_debug(bip, first, last);
 }
 
+/*
+ * Mark bytes first through last inclusive as dirty in the buf
+ * item's bitmap.
+ */
+void
+xfs_buf_item_log(
+       xfs_buf_log_item_t      *bip,
+       uint                    first,
+       uint                    last)
+{
+       int                     i;
+       uint                    start;
+       uint                    end;
+       struct xfs_buf          *bp = bip->bli_buf;
+
+       /*
+        * Mark the item as having some dirty data for
+        * quick reference in xfs_buf_item_dirty.
+        */
+       bip->bli_flags |= XFS_BLI_DIRTY;
+
+       /*
+        * walk each buffer segment and mark them dirty appropriately.
+        */
+       start = 0;
+       for (i = 0; i < bip->bli_format_count; i++) {
+               if (start > last)
+                       break;
+               end = start + BBTOB(bp->b_maps[i].bm_len);
+               if (first > end) {
+                       start += BBTOB(bp->b_maps[i].bm_len);
+                       continue;
+               }
+               if (first < start)
+                       first = start;
+               if (end > last)
+                       end = last;
+
+               xfs_buf_item_log_segment(bip, first, end,
+                                        &bip->bli_formats[i].blf_data_map[0]);
+
+               start += bp->b_maps[i].bm_len;
+       }
+}
+
 
 /*
  * Return 1 if the buffer has some data that has been logged (at any
@@ -804,6 +950,7 @@ xfs_buf_item_free(
        kmem_free(bip->bli_logged);
 #endif /* XFS_TRANS_DEBUG */
 
+       xfs_buf_item_free_format(bip);
        kmem_zone_free(xfs_buf_item_zone, bip);
 }
 
index b6ecd2061e7cd292b0f1c4447b13b91c65b1fa86..6850f49f4af3f60c0a5a8ef9508f7bac9af00e4c 100644 (file)
 
 extern kmem_zone_t     *xfs_buf_item_zone;
 
-/*
- * This is the structure used to lay out a buf log item in the
- * log.  The data map describes which 128 byte chunks of the buffer
- * have been logged.
- * For 6.2 and beyond, this is XFS_LI_BUF.  We use this to log everything.
- */
-typedef struct xfs_buf_log_format {
-       unsigned short  blf_type;       /* buf log item type indicator */
-       unsigned short  blf_size;       /* size of this item */
-       ushort          blf_flags;      /* misc state */
-       ushort          blf_len;        /* number of blocks in this buf */
-       __int64_t       blf_blkno;      /* starting blkno of this buf */
-       unsigned int    blf_map_size;   /* size of data bitmap in words */
-       unsigned int    blf_data_map[1];/* variable size bitmap of */
-                                       /*   regions of buffer in this item */
-} xfs_buf_log_format_t;
-
 /*
  * This flag indicates that the buffer contains on disk inodes
  * and requires special recovery handling.
@@ -60,6 +43,23 @@ typedef struct xfs_buf_log_format {
 #define        BIT_TO_WORD_SHIFT       5
 #define        NBWORD                  (NBBY * sizeof(unsigned int))
 
+/*
+ * This is the structure used to lay out a buf log item in the
+ * log.  The data map describes which 128 byte chunks of the buffer
+ * have been logged.
+ */
+#define XFS_BLF_DATAMAP_SIZE   ((XFS_MAX_BLOCKSIZE / XFS_BLF_CHUNK) / NBWORD)
+
+typedef struct xfs_buf_log_format {
+       unsigned short  blf_type;       /* buf log item type indicator */
+       unsigned short  blf_size;       /* size of this item */
+       ushort          blf_flags;      /* misc state */
+       ushort          blf_len;        /* number of blocks in this buf */
+       __int64_t       blf_blkno;      /* starting blkno of this buf */
+       unsigned int    blf_map_size;   /* used size of data bitmap in words */
+       unsigned int    blf_data_map[XFS_BLF_DATAMAP_SIZE]; /* dirty bitmap */
+} xfs_buf_log_format_t;
+
 /*
  * buf log item flags
  */
@@ -102,7 +102,9 @@ typedef struct xfs_buf_log_item {
        char                    *bli_orig;      /* original buffer copy */
        char                    *bli_logged;    /* bytes logged (bitmap) */
 #endif
-       xfs_buf_log_format_t    bli_format;     /* in-log header */
+       int                     bli_format_count;       /* count of headers */
+       struct xfs_buf_log_format *bli_formats; /* array of in-log header ptrs */
+       struct xfs_buf_log_format bli_format;   /* embedded in-log header */
 } xfs_buf_log_item_t;
 
 void   xfs_buf_item_init(struct xfs_buf *, struct xfs_mount *);
index 015b946c58081acb34e7ce49438debd5dced4aab..7bfb7dd334fc0442204ce6e483f1fc5ba4dee65f 100644 (file)
@@ -83,9 +83,9 @@ STATIC void xfs_da_node_unbalance(xfs_da_state_t *state,
 /*
  * Utility routines.
  */
-STATIC uint    xfs_da_node_lasthash(xfs_dabuf_t *bp, int *count);
-STATIC int     xfs_da_node_order(xfs_dabuf_t *node1_bp, xfs_dabuf_t *node2_bp);
-STATIC xfs_dabuf_t *xfs_da_buf_make(int nbuf, xfs_buf_t **bps);
+STATIC uint    xfs_da_node_lasthash(struct xfs_buf *bp, int *count);
+STATIC int     xfs_da_node_order(struct xfs_buf *node1_bp,
+                                 struct xfs_buf *node2_bp);
 STATIC int     xfs_da_blk_unlink(xfs_da_state_t *state,
                                  xfs_da_state_blk_t *drop_blk,
                                  xfs_da_state_blk_t *save_blk);
@@ -100,10 +100,10 @@ STATIC void       xfs_da_state_kill_altpath(xfs_da_state_t *state);
  */
 int
 xfs_da_node_create(xfs_da_args_t *args, xfs_dablk_t blkno, int level,
-                                xfs_dabuf_t **bpp, int whichfork)
+                                struct xfs_buf **bpp, int whichfork)
 {
        xfs_da_intnode_t *node;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
        int error;
        xfs_trans_t *tp;
 
@@ -114,7 +114,7 @@ xfs_da_node_create(xfs_da_args_t *args, xfs_dablk_t blkno, int level,
        if (error)
                return(error);
        ASSERT(bp != NULL);
-       node = bp->data;
+       node = bp->b_addr;
        node->hdr.info.forw = 0;
        node->hdr.info.back = 0;
        node->hdr.info.magic = cpu_to_be16(XFS_DA_NODE_MAGIC);
@@ -122,7 +122,7 @@ xfs_da_node_create(xfs_da_args_t *args, xfs_dablk_t blkno, int level,
        node->hdr.count = 0;
        node->hdr.level = cpu_to_be16(level);
 
-       xfs_da_log_buf(tp, bp,
+       xfs_trans_log_buf(tp, bp,
                XFS_DA_LOGRANGE(node, &node->hdr, sizeof(node->hdr)));
 
        *bpp = bp;
@@ -138,7 +138,7 @@ xfs_da_split(xfs_da_state_t *state)
 {
        xfs_da_state_blk_t *oldblk, *newblk, *addblk;
        xfs_da_intnode_t *node;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
        int max, action, error, i;
 
        trace_xfs_da_split(state->args);
@@ -203,7 +203,6 @@ xfs_da_split(xfs_da_state_t *state)
                case XFS_DA_NODE_MAGIC:
                        error = xfs_da_node_split(state, oldblk, newblk, addblk,
                                                         max - i, &action);
-                       xfs_da_buf_done(addblk->bp);
                        addblk->bp = NULL;
                        if (error)
                                return(error);  /* GROT: dir is inconsistent */
@@ -221,13 +220,6 @@ xfs_da_split(xfs_da_state_t *state)
                 * Update the btree to show the new hashval for this child.
                 */
                xfs_da_fixhashpath(state, &state->path);
-               /*
-                * If we won't need this block again, it's getting dropped
-                * from the active path by the loop control, so we need
-                * to mark it done now.
-                */
-               if (i > 0 || !addblk)
-                       xfs_da_buf_done(oldblk->bp);
        }
        if (!addblk)
                return(0);
@@ -239,8 +231,6 @@ xfs_da_split(xfs_da_state_t *state)
        oldblk = &state->path.blk[0];
        error = xfs_da_root_split(state, oldblk, addblk);
        if (error) {
-               xfs_da_buf_done(oldblk->bp);
-               xfs_da_buf_done(addblk->bp);
                addblk->bp = NULL;
                return(error);  /* GROT: dir is inconsistent */
        }
@@ -252,7 +242,7 @@ xfs_da_split(xfs_da_state_t *state)
         * and the original block 0 could be at any position in the list.
         */
 
-       node = oldblk->bp->data;
+       node = oldblk->bp->b_addr;
        if (node->hdr.info.forw) {
                if (be32_to_cpu(node->hdr.info.forw) == addblk->blkno) {
                        bp = addblk->bp;
@@ -260,13 +250,13 @@ xfs_da_split(xfs_da_state_t *state)
                        ASSERT(state->extravalid);
                        bp = state->extrablk.bp;
                }
-               node = bp->data;
+               node = bp->b_addr;
                node->hdr.info.back = cpu_to_be32(oldblk->blkno);
-               xfs_da_log_buf(state->args->trans, bp,
+               xfs_trans_log_buf(state->args->trans, bp,
                    XFS_DA_LOGRANGE(node, &node->hdr.info,
                    sizeof(node->hdr.info)));
        }
-       node = oldblk->bp->data;
+       node = oldblk->bp->b_addr;
        if (node->hdr.info.back) {
                if (be32_to_cpu(node->hdr.info.back) == addblk->blkno) {
                        bp = addblk->bp;
@@ -274,14 +264,12 @@ xfs_da_split(xfs_da_state_t *state)
                        ASSERT(state->extravalid);
                        bp = state->extrablk.bp;
                }
-               node = bp->data;
+               node = bp->b_addr;
                node->hdr.info.forw = cpu_to_be32(oldblk->blkno);
-               xfs_da_log_buf(state->args->trans, bp,
+               xfs_trans_log_buf(state->args->trans, bp,
                    XFS_DA_LOGRANGE(node, &node->hdr.info,
                    sizeof(node->hdr.info)));
        }
-       xfs_da_buf_done(oldblk->bp);
-       xfs_da_buf_done(addblk->bp);
        addblk->bp = NULL;
        return(0);
 }
@@ -298,7 +286,7 @@ xfs_da_root_split(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
        xfs_da_intnode_t *node, *oldroot;
        xfs_da_args_t *args;
        xfs_dablk_t blkno;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
        int error, size;
        xfs_inode_t *dp;
        xfs_trans_t *tp;
@@ -323,8 +311,8 @@ xfs_da_root_split(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
        if (error)
                return(error);
        ASSERT(bp != NULL);
-       node = bp->data;
-       oldroot = blk1->bp->data;
+       node = bp->b_addr;
+       oldroot = blk1->bp->b_addr;
        if (oldroot->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC)) {
                size = (int)((char *)&oldroot->btree[be16_to_cpu(oldroot->hdr.count)] -
                             (char *)oldroot);
@@ -335,8 +323,7 @@ xfs_da_root_split(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
                             (char *)leaf);
        }
        memcpy(node, oldroot, size);
-       xfs_da_log_buf(tp, bp, 0, size - 1);
-       xfs_da_buf_done(blk1->bp);
+       xfs_trans_log_buf(tp, bp, 0, size - 1);
        blk1->bp = bp;
        blk1->blkno = blkno;
 
@@ -348,7 +335,7 @@ xfs_da_root_split(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
                be16_to_cpu(node->hdr.level) + 1, &bp, args->whichfork);
        if (error)
                return(error);
-       node = bp->data;
+       node = bp->b_addr;
        node->btree[0].hashval = cpu_to_be32(blk1->hashval);
        node->btree[0].before = cpu_to_be32(blk1->blkno);
        node->btree[1].hashval = cpu_to_be32(blk2->hashval);
@@ -365,10 +352,9 @@ xfs_da_root_split(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
 #endif
 
        /* Header is already logged by xfs_da_node_create */
-       xfs_da_log_buf(tp, bp,
+       xfs_trans_log_buf(tp, bp,
                XFS_DA_LOGRANGE(node, node->btree,
                        sizeof(xfs_da_node_entry_t) * 2));
-       xfs_da_buf_done(bp);
 
        return(0);
 }
@@ -389,7 +375,7 @@ xfs_da_node_split(xfs_da_state_t *state, xfs_da_state_blk_t *oldblk,
 
        trace_xfs_da_node_split(state->args);
 
-       node = oldblk->bp->data;
+       node = oldblk->bp->b_addr;
        ASSERT(node->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
 
        /*
@@ -436,7 +422,7 @@ xfs_da_node_split(xfs_da_state_t *state, xfs_da_state_blk_t *oldblk,
         *
         * If we had double-split op below us, then add the extra block too.
         */
-       node = oldblk->bp->data;
+       node = oldblk->bp->b_addr;
        if (oldblk->index <= be16_to_cpu(node->hdr.count)) {
                oldblk->index++;
                xfs_da_node_add(state, oldblk, addblk);
@@ -477,8 +463,8 @@ xfs_da_node_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
 
        trace_xfs_da_node_rebalance(state->args);
 
-       node1 = blk1->bp->data;
-       node2 = blk2->bp->data;
+       node1 = blk1->bp->b_addr;
+       node2 = blk2->bp->b_addr;
        /*
         * Figure out how many entries need to move, and in which direction.
         * Swap the nodes around if that makes it simpler.
@@ -532,7 +518,7 @@ xfs_da_node_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
                btree_d = &node1->btree[be16_to_cpu(node1->hdr.count)];
                memcpy(btree_d, btree_s, tmp);
                be16_add_cpu(&node1->hdr.count, count);
-               xfs_da_log_buf(tp, blk1->bp,
+               xfs_trans_log_buf(tp, blk1->bp,
                        XFS_DA_LOGRANGE(node1, btree_d, tmp));
 
                /*
@@ -549,9 +535,9 @@ xfs_da_node_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
        /*
         * Log header of node 1 and all current bits of node 2.
         */
-       xfs_da_log_buf(tp, blk1->bp,
+       xfs_trans_log_buf(tp, blk1->bp,
                XFS_DA_LOGRANGE(node1, &node1->hdr, sizeof(node1->hdr)));
-       xfs_da_log_buf(tp, blk2->bp,
+       xfs_trans_log_buf(tp, blk2->bp,
                XFS_DA_LOGRANGE(node2, &node2->hdr,
                        sizeof(node2->hdr) +
                        sizeof(node2->btree[0]) * be16_to_cpu(node2->hdr.count)));
@@ -560,8 +546,8 @@ xfs_da_node_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
         * Record the last hashval from each block for upward propagation.
         * (note: don't use the swapped node pointers)
         */
-       node1 = blk1->bp->data;
-       node2 = blk2->bp->data;
+       node1 = blk1->bp->b_addr;
+       node2 = blk2->bp->b_addr;
        blk1->hashval = be32_to_cpu(node1->btree[be16_to_cpu(node1->hdr.count)-1].hashval);
        blk2->hashval = be32_to_cpu(node2->btree[be16_to_cpu(node2->hdr.count)-1].hashval);
 
@@ -587,7 +573,7 @@ xfs_da_node_add(xfs_da_state_t *state, xfs_da_state_blk_t *oldblk,
 
        trace_xfs_da_node_add(state->args);
 
-       node = oldblk->bp->data;
+       node = oldblk->bp->b_addr;
        ASSERT(node->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
        ASSERT((oldblk->index >= 0) && (oldblk->index <= be16_to_cpu(node->hdr.count)));
        ASSERT(newblk->blkno != 0);
@@ -606,10 +592,10 @@ xfs_da_node_add(xfs_da_state_t *state, xfs_da_state_blk_t *oldblk,
        }
        btree->hashval = cpu_to_be32(newblk->hashval);
        btree->before = cpu_to_be32(newblk->blkno);
-       xfs_da_log_buf(state->args->trans, oldblk->bp,
+       xfs_trans_log_buf(state->args->trans, oldblk->bp,
                XFS_DA_LOGRANGE(node, btree, tmp + sizeof(*btree)));
        be16_add_cpu(&node->hdr.count, 1);
-       xfs_da_log_buf(state->args->trans, oldblk->bp,
+       xfs_trans_log_buf(state->args->trans, oldblk->bp,
                XFS_DA_LOGRANGE(node, &node->hdr, sizeof(node->hdr)));
 
        /*
@@ -735,7 +721,7 @@ xfs_da_root_join(xfs_da_state_t *state, xfs_da_state_blk_t *root_blk)
        xfs_da_intnode_t *oldroot;
        xfs_da_args_t *args;
        xfs_dablk_t child;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
        int error;
 
        trace_xfs_da_root_join(state->args);
@@ -743,7 +729,7 @@ xfs_da_root_join(xfs_da_state_t *state, xfs_da_state_blk_t *root_blk)
        args = state->args;
        ASSERT(args != NULL);
        ASSERT(root_blk->magic == XFS_DA_NODE_MAGIC);
-       oldroot = root_blk->bp->data;
+       oldroot = root_blk->bp->b_addr;
        ASSERT(oldroot->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
        ASSERT(!oldroot->hdr.info.forw);
        ASSERT(!oldroot->hdr.info.back);
@@ -765,11 +751,11 @@ xfs_da_root_join(xfs_da_state_t *state, xfs_da_state_blk_t *root_blk)
        if (error)
                return(error);
        ASSERT(bp != NULL);
-       xfs_da_blkinfo_onlychild_validate(bp->data,
+       xfs_da_blkinfo_onlychild_validate(bp->b_addr,
                                        be16_to_cpu(oldroot->hdr.level));
 
-       memcpy(root_blk->bp->data, bp->data, state->blocksize);
-       xfs_da_log_buf(args->trans, root_blk->bp, 0, state->blocksize - 1);
+       memcpy(root_blk->bp->b_addr, bp->b_addr, state->blocksize);
+       xfs_trans_log_buf(args->trans, root_blk->bp, 0, state->blocksize - 1);
        error = xfs_da_shrink_inode(args, child, bp);
        return(error);
 }
@@ -791,7 +777,7 @@ xfs_da_node_toosmall(xfs_da_state_t *state, int *action)
        xfs_da_blkinfo_t *info;
        int count, forward, error, retval, i;
        xfs_dablk_t blkno;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
 
        /*
         * Check for the degenerate case of the block being over 50% full.
@@ -799,7 +785,7 @@ xfs_da_node_toosmall(xfs_da_state_t *state, int *action)
         * to coalesce with a sibling.
         */
        blk = &state->path.blk[ state->path.active-1 ];
-       info = blk->bp->data;
+       info = blk->bp->b_addr;
        ASSERT(info->magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
        node = (xfs_da_intnode_t *)info;
        count = be16_to_cpu(node->hdr.count);
@@ -859,10 +845,10 @@ xfs_da_node_toosmall(xfs_da_state_t *state, int *action)
                count  = state->node_ents;
                count -= state->node_ents >> 2;
                count -= be16_to_cpu(node->hdr.count);
-               node = bp->data;
+               node = bp->b_addr;
                ASSERT(node->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
                count -= be16_to_cpu(node->hdr.count);
-               xfs_da_brelse(state->args->trans, bp);
+               xfs_trans_brelse(state->args->trans, bp);
                if (count >= 0)
                        break;  /* fits with at least 25% to spare */
        }
@@ -934,14 +920,14 @@ xfs_da_fixhashpath(xfs_da_state_t *state, xfs_da_state_path_t *path)
                break;
        }
        for (blk--, level--; level >= 0; blk--, level--) {
-               node = blk->bp->data;
+               node = blk->bp->b_addr;
                ASSERT(node->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
                btree = &node->btree[ blk->index ];
                if (be32_to_cpu(btree->hashval) == lasthash)
                        break;
                blk->hashval = lasthash;
                btree->hashval = cpu_to_be32(lasthash);
-               xfs_da_log_buf(state->args->trans, blk->bp,
+               xfs_trans_log_buf(state->args->trans, blk->bp,
                                  XFS_DA_LOGRANGE(node, btree, sizeof(*btree)));
 
                lasthash = be32_to_cpu(node->btree[be16_to_cpu(node->hdr.count)-1].hashval);
@@ -960,7 +946,7 @@ xfs_da_node_remove(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk)
 
        trace_xfs_da_node_remove(state->args);
 
-       node = drop_blk->bp->data;
+       node = drop_blk->bp->b_addr;
        ASSERT(drop_blk->index < be16_to_cpu(node->hdr.count));
        ASSERT(drop_blk->index >= 0);
 
@@ -972,15 +958,15 @@ xfs_da_node_remove(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk)
                tmp  = be16_to_cpu(node->hdr.count) - drop_blk->index - 1;
                tmp *= (uint)sizeof(xfs_da_node_entry_t);
                memmove(btree, btree + 1, tmp);
-               xfs_da_log_buf(state->args->trans, drop_blk->bp,
+               xfs_trans_log_buf(state->args->trans, drop_blk->bp,
                    XFS_DA_LOGRANGE(node, btree, tmp));
                btree = &node->btree[be16_to_cpu(node->hdr.count)-1];
        }
        memset((char *)btree, 0, sizeof(xfs_da_node_entry_t));
-       xfs_da_log_buf(state->args->trans, drop_blk->bp,
+       xfs_trans_log_buf(state->args->trans, drop_blk->bp,
            XFS_DA_LOGRANGE(node, btree, sizeof(*btree)));
        be16_add_cpu(&node->hdr.count, -1);
-       xfs_da_log_buf(state->args->trans, drop_blk->bp,
+       xfs_trans_log_buf(state->args->trans, drop_blk->bp,
            XFS_DA_LOGRANGE(node, &node->hdr, sizeof(node->hdr)));
 
        /*
@@ -1005,8 +991,8 @@ xfs_da_node_unbalance(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
 
        trace_xfs_da_node_unbalance(state->args);
 
-       drop_node = drop_blk->bp->data;
-       save_node = save_blk->bp->data;
+       drop_node = drop_blk->bp->b_addr;
+       save_node = save_blk->bp->b_addr;
        ASSERT(drop_node->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
        ASSERT(save_node->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
        tp = state->args->trans;
@@ -1023,13 +1009,13 @@ xfs_da_node_unbalance(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
                tmp = be16_to_cpu(save_node->hdr.count) * (uint)sizeof(xfs_da_node_entry_t);
                memmove(btree, &save_node->btree[0], tmp);
                btree = &save_node->btree[0];
-               xfs_da_log_buf(tp, save_blk->bp,
+               xfs_trans_log_buf(tp, save_blk->bp,
                        XFS_DA_LOGRANGE(save_node, btree,
                                (be16_to_cpu(save_node->hdr.count) + be16_to_cpu(drop_node->hdr.count)) *
                                sizeof(xfs_da_node_entry_t)));
        } else {
                btree = &save_node->btree[be16_to_cpu(save_node->hdr.count)];
-               xfs_da_log_buf(tp, save_blk->bp,
+               xfs_trans_log_buf(tp, save_blk->bp,
                        XFS_DA_LOGRANGE(save_node, btree,
                                be16_to_cpu(drop_node->hdr.count) *
                                sizeof(xfs_da_node_entry_t)));
@@ -1042,7 +1028,7 @@ xfs_da_node_unbalance(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
        memcpy(btree, &drop_node->btree[0], tmp);
        be16_add_cpu(&save_node->hdr.count, be16_to_cpu(drop_node->hdr.count));
 
-       xfs_da_log_buf(tp, save_blk->bp,
+       xfs_trans_log_buf(tp, save_blk->bp,
                XFS_DA_LOGRANGE(save_node, &save_node->hdr,
                        sizeof(save_node->hdr)));
 
@@ -1100,7 +1086,7 @@ xfs_da_node_lookup_int(xfs_da_state_t *state, int *result)
                        state->path.active--;
                        return(error);
                }
-               curr = blk->bp->data;
+               curr = blk->bp->b_addr;
                blk->magic = be16_to_cpu(curr->magic);
                ASSERT(blk->magic == XFS_DA_NODE_MAGIC ||
                       blk->magic == XFS_DIR2_LEAFN_MAGIC ||
@@ -1110,7 +1096,7 @@ xfs_da_node_lookup_int(xfs_da_state_t *state, int *result)
                 * Search an intermediate node for a match.
                 */
                if (blk->magic == XFS_DA_NODE_MAGIC) {
-                       node = blk->bp->data;
+                       node = blk->bp->b_addr;
                        max = be16_to_cpu(node->hdr.count);
                        blk->hashval = be32_to_cpu(node->btree[max-1].hashval);
 
@@ -1216,15 +1202,15 @@ xfs_da_blk_link(xfs_da_state_t *state, xfs_da_state_blk_t *old_blk,
        xfs_da_blkinfo_t *old_info, *new_info, *tmp_info;
        xfs_da_args_t *args;
        int before=0, error;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
 
        /*
         * Set up environment.
         */
        args = state->args;
        ASSERT(args != NULL);
-       old_info = old_blk->bp->data;
-       new_info = new_blk->bp->data;
+       old_info = old_blk->bp->b_addr;
+       new_info = new_blk->bp->b_addr;
        ASSERT(old_blk->magic == XFS_DA_NODE_MAGIC ||
               old_blk->magic == XFS_DIR2_LEAFN_MAGIC ||
               old_blk->magic == XFS_ATTR_LEAF_MAGIC);
@@ -1261,12 +1247,11 @@ xfs_da_blk_link(xfs_da_state_t *state, xfs_da_state_blk_t *old_blk,
                        if (error)
                                return(error);
                        ASSERT(bp != NULL);
-                       tmp_info = bp->data;
+                       tmp_info = bp->b_addr;
                        ASSERT(be16_to_cpu(tmp_info->magic) == be16_to_cpu(old_info->magic));
                        ASSERT(be32_to_cpu(tmp_info->forw) == old_blk->blkno);
                        tmp_info->forw = cpu_to_be32(new_blk->blkno);
-                       xfs_da_log_buf(args->trans, bp, 0, sizeof(*tmp_info)-1);
-                       xfs_da_buf_done(bp);
+                       xfs_trans_log_buf(args->trans, bp, 0, sizeof(*tmp_info)-1);
                }
                old_info->back = cpu_to_be32(new_blk->blkno);
        } else {
@@ -1283,18 +1268,17 @@ xfs_da_blk_link(xfs_da_state_t *state, xfs_da_state_blk_t *old_blk,
                        if (error)
                                return(error);
                        ASSERT(bp != NULL);
-                       tmp_info = bp->data;
+                       tmp_info = bp->b_addr;
                        ASSERT(tmp_info->magic == old_info->magic);
                        ASSERT(be32_to_cpu(tmp_info->back) == old_blk->blkno);
                        tmp_info->back = cpu_to_be32(new_blk->blkno);
-                       xfs_da_log_buf(args->trans, bp, 0, sizeof(*tmp_info)-1);
-                       xfs_da_buf_done(bp);
+                       xfs_trans_log_buf(args->trans, bp, 0, sizeof(*tmp_info)-1);
                }
                old_info->forw = cpu_to_be32(new_blk->blkno);
        }
 
-       xfs_da_log_buf(args->trans, old_blk->bp, 0, sizeof(*tmp_info) - 1);
-       xfs_da_log_buf(args->trans, new_blk->bp, 0, sizeof(*tmp_info) - 1);
+       xfs_trans_log_buf(args->trans, old_blk->bp, 0, sizeof(*tmp_info) - 1);
+       xfs_trans_log_buf(args->trans, new_blk->bp, 0, sizeof(*tmp_info) - 1);
        return(0);
 }
 
@@ -1302,12 +1286,14 @@ xfs_da_blk_link(xfs_da_state_t *state, xfs_da_state_blk_t *old_blk,
  * Compare two intermediate nodes for "order".
  */
 STATIC int
-xfs_da_node_order(xfs_dabuf_t *node1_bp, xfs_dabuf_t *node2_bp)
+xfs_da_node_order(
+       struct xfs_buf  *node1_bp,
+       struct xfs_buf  *node2_bp)
 {
        xfs_da_intnode_t *node1, *node2;
 
-       node1 = node1_bp->data;
-       node2 = node2_bp->data;
+       node1 = node1_bp->b_addr;
+       node2 = node2_bp->b_addr;
        ASSERT(node1->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC) &&
               node2->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
        if ((be16_to_cpu(node1->hdr.count) > 0) && (be16_to_cpu(node2->hdr.count) > 0) &&
@@ -1324,11 +1310,13 @@ xfs_da_node_order(xfs_dabuf_t *node1_bp, xfs_dabuf_t *node2_bp)
  * Pick up the last hashvalue from an intermediate node.
  */
 STATIC uint
-xfs_da_node_lasthash(xfs_dabuf_t *bp, int *count)
+xfs_da_node_lasthash(
+       struct xfs_buf  *bp,
+       int             *count)
 {
        xfs_da_intnode_t *node;
 
-       node = bp->data;
+       node = bp->b_addr;
        ASSERT(node->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
        if (count)
                *count = be16_to_cpu(node->hdr.count);
@@ -1346,7 +1334,7 @@ xfs_da_blk_unlink(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
 {
        xfs_da_blkinfo_t *drop_info, *save_info, *tmp_info;
        xfs_da_args_t *args;
-       xfs_dabuf_t *bp;
+       struct xfs_buf *bp;
        int error;
 
        /*
@@ -1354,8 +1342,8 @@ xfs_da_blk_unlink(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
         */
        args = state->args;
        ASSERT(args != NULL);
-       save_info = save_blk->bp->data;
-       drop_info = drop_blk->bp->data;
+       save_info = save_blk->bp->b_addr;
+       drop_info = drop_blk->bp->b_addr;
        ASSERT(save_blk->magic == XFS_DA_NODE_MAGIC ||
               save_blk->magic == XFS_DIR2_LEAFN_MAGIC ||
               save_blk->magic == XFS_ATTR_LEAF_MAGIC);
@@ -1380,13 +1368,12 @@ xfs_da_blk_unlink(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
                        if (error)
                                return(error);
                        ASSERT(bp != NULL);
-                       tmp_info = bp->data;
+                       tmp_info = bp->b_addr;
                        ASSERT(tmp_info->magic == save_info->magic);
                        ASSERT(be32_to_cpu(tmp_info->forw) == drop_blk->blkno);
                        tmp_info->forw = cpu_to_be32(save_blk->blkno);
-                       xfs_da_log_buf(args->trans, bp, 0,
+                       xfs_trans_log_buf(args->trans, bp, 0,
                                                    sizeof(*tmp_info) - 1);
-                       xfs_da_buf_done(bp);
                }
        } else {
                trace_xfs_da_unlink_forward(args);
@@ -1398,17 +1385,16 @@ xfs_da_blk_unlink(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
                        if (error)
                                return(error);
                        ASSERT(bp != NULL);
-                       tmp_info = bp->data;
+                       tmp_info = bp->b_addr;
                        ASSERT(tmp_info->magic == save_info->magic);
                        ASSERT(be32_to_cpu(tmp_info->back) == drop_blk->blkno);
                        tmp_info->back = cpu_to_be32(save_blk->blkno);
-                       xfs_da_log_buf(args->trans, bp, 0,
+                       xfs_trans_log_buf(args->trans, bp, 0,
                                                    sizeof(*tmp_info) - 1);
-                       xfs_da_buf_done(bp);
                }
        }
 
-       xfs_da_log_buf(args->trans, save_blk->bp, 0, sizeof(*save_info) - 1);
+       xfs_trans_log_buf(args->trans, save_blk->bp, 0, sizeof(*save_info) - 1);
        return(0);
 }
 
@@ -1443,7 +1429,7 @@ xfs_da_path_shift(xfs_da_state_t *state, xfs_da_state_path_t *path,
        level = (path->active-1) - 1;   /* skip bottom layer in path */
        for (blk = &path->blk[level]; level >= 0; blk--, level--) {
                ASSERT(blk->bp != NULL);
-               node = blk->bp->data;
+               node = blk->bp->b_addr;
                ASSERT(node->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
                if (forward && (blk->index < be16_to_cpu(node->hdr.count)-1)) {
                        blk->index++;
@@ -1471,7 +1457,7 @@ xfs_da_path_shift(xfs_da_state_t *state, xfs_da_state_path_t *path,
                 * (if it's dirty, trans won't actually let go)
                 */
                if (release)
-                       xfs_da_brelse(args->trans, blk->bp);
+                       xfs_trans_brelse(args->trans, blk->bp);
 
                /*
                 * Read the next child block.
@@ -1482,7 +1468,7 @@ xfs_da_path_shift(xfs_da_state_t *state, xfs_da_state_path_t *path,
                if (error)
                        return(error);
                ASSERT(blk->bp != NULL);
-               info = blk->bp->data;
+               info = blk->bp->b_addr;
                ASSERT(info->magic == cpu_to_be16(XFS_DA_NODE_MAGIC) ||
                       info->magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC) ||
                       info->magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
@@ -1702,11 +1688,13 @@ xfs_da_grow_inode(
  * a bmap btree split to do that.
  */
 STATIC int
-xfs_da_swap_lastblock(xfs_da_args_t *args, xfs_dablk_t *dead_blknop,
-                     xfs_dabuf_t **dead_bufp)
+xfs_da_swap_lastblock(
+       xfs_da_args_t   *args,
+       xfs_dablk_t     *dead_blknop,
+       struct xfs_buf  **dead_bufp)
 {
        xfs_dablk_t dead_blkno, last_blkno, sib_blkno, par_blkno;
-       xfs_dabuf_t *dead_buf, *last_buf, *sib_buf, *par_buf;
+       struct xfs_buf *dead_buf, *last_buf, *sib_buf, *par_buf;
        xfs_fileoff_t lastoff;
        xfs_inode_t *ip;
        xfs_trans_t *tp;
@@ -1744,9 +1732,9 @@ xfs_da_swap_lastblock(xfs_da_args_t *args, xfs_dablk_t *dead_blknop,
        /*
         * Copy the last block into the dead buffer and log it.
         */
-       memcpy(dead_buf->data, last_buf->data, mp->m_dirblksize);
-       xfs_da_log_buf(tp, dead_buf, 0, mp->m_dirblksize - 1);
-       dead_info = dead_buf->data;
+       memcpy(dead_buf->b_addr, last_buf->b_addr, mp->m_dirblksize);
+       xfs_trans_log_buf(tp, dead_buf, 0, mp->m_dirblksize - 1);
+       dead_info = dead_buf->b_addr;
        /*
         * Get values from the moved block.
         */
@@ -1767,7 +1755,7 @@ xfs_da_swap_lastblock(xfs_da_args_t *args, xfs_dablk_t *dead_blknop,
        if ((sib_blkno = be32_to_cpu(dead_info->back))) {
                if ((error = xfs_da_read_buf(tp, ip, sib_blkno, -1, &sib_buf, w)))
                        goto done;
-               sib_info = sib_buf->data;
+               sib_info = sib_buf->b_addr;
                if (unlikely(
                    be32_to_cpu(sib_info->forw) != last_blkno ||
                    sib_info->magic != dead_info->magic)) {
@@ -1777,10 +1765,9 @@ xfs_da_swap_lastblock(xfs_da_args_t *args, xfs_dablk_t *dead_blknop,
                        goto done;
                }
                sib_info->forw = cpu_to_be32(dead_blkno);
-               xfs_da_log_buf(tp, sib_buf,
+               xfs_trans_log_buf(tp, sib_buf,
                        XFS_DA_LOGRANGE(sib_info, &sib_info->forw,
                                        sizeof(sib_info->forw)));
-               xfs_da_buf_done(sib_buf);
                sib_buf = NULL;
        }
        /*
@@ -1789,7 +1776,7 @@ xfs_da_swap_lastblock(xfs_da_args_t *args, xfs_dablk_t *dead_blknop,
        if ((sib_blkno = be32_to_cpu(dead_info->forw))) {
                if ((error = xfs_da_read_buf(tp, ip, sib_blkno, -1, &sib_buf, w)))
                        goto done;
-               sib_info = sib_buf->data;
+               sib_info = sib_buf->b_addr;
                if (unlikely(
                       be32_to_cpu(sib_info->back) != last_blkno ||
                       sib_info->magic != dead_info->magic)) {
@@ -1799,10 +1786,9 @@ xfs_da_swap_lastblock(xfs_da_args_t *args, xfs_dablk_t *dead_blknop,
                        goto done;
                }
                sib_info->back = cpu_to_be32(dead_blkno);
-               xfs_da_log_buf(tp, sib_buf,
+               xfs_trans_log_buf(tp, sib_buf,
                        XFS_DA_LOGRANGE(sib_info, &sib_info->back,
                                        sizeof(sib_info->back)));
-               xfs_da_buf_done(sib_buf);
                sib_buf = NULL;
        }
        par_blkno = mp->m_dirleafblk;
@@ -1813,7 +1799,7 @@ xfs_da_swap_lastblock(xfs_da_args_t *args, xfs_dablk_t *dead_blknop,
        for (;;) {
                if ((error = xfs_da_read_buf(tp, ip, par_blkno, -1, &par_buf, w)))
                        goto done;
-               par_node = par_buf->data;
+               par_node = par_buf->b_addr;
                if (unlikely(par_node->hdr.info.magic !=
                    cpu_to_be16(XFS_DA_NODE_MAGIC) ||
                    (level >= 0 && level != be16_to_cpu(par_node->hdr.level) + 1))) {
@@ -1837,7 +1823,7 @@ xfs_da_swap_lastblock(xfs_da_args_t *args, xfs_dablk_t *dead_blknop,
                par_blkno = be32_to_cpu(par_node->btree[entno].before);
                if (level == dead_level + 1)
                        break;
-               xfs_da_brelse(tp, par_buf);
+               xfs_trans_brelse(tp, par_buf);
                par_buf = NULL;
        }
        /*
@@ -1853,7 +1839,7 @@ xfs_da_swap_lastblock(xfs_da_args_t *args, xfs_dablk_t *dead_blknop,
                if (entno < be16_to_cpu(par_node->hdr.count))
                        break;
                par_blkno = be32_to_cpu(par_node->hdr.info.forw);
-               xfs_da_brelse(tp, par_buf);
+               xfs_trans_brelse(tp, par_buf);
                par_buf = NULL;
                if (unlikely(par_blkno == 0)) {
                        XFS_ERROR_REPORT("xfs_da_swap_lastblock(6)",
@@ -1863,7 +1849,7 @@ xfs_da_swap_lastblock(xfs_da_args_t *args, xfs_dablk_t *dead_blknop,
                }
                if ((error = xfs_da_read_buf(tp, ip, par_blkno, -1, &par_buf, w)))
                        goto done;
-               par_node = par_buf->data;
+               par_node = par_buf->b_addr;
                if (unlikely(
                    be16_to_cpu(par_node->hdr.level) != level ||
                    par_node->hdr.info.magic != cpu_to_be16(XFS_DA_NODE_MAGIC))) {
@@ -1878,20 +1864,18 @@ xfs_da_swap_lastblock(xfs_da_args_t *args, xfs_dablk_t *dead_blknop,
         * Update the parent entry pointing to the moved block.
         */
        par_node->btree[entno].before = cpu_to_be32(dead_blkno);
-       xfs_da_log_buf(tp, par_buf,
+       xfs_trans_log_buf(tp, par_buf,
                XFS_DA_LOGRANGE(par_node, &par_node->btree[entno].before,
                                sizeof(par_node->btree[entno].before)));
-       xfs_da_buf_done(par_buf);
-       xfs_da_buf_done(dead_buf);
        *dead_blknop = last_blkno;
        *dead_bufp = last_buf;
        return 0;
 done:
        if (par_buf)
-               xfs_da_brelse(tp, par_buf);
+               xfs_trans_brelse(tp, par_buf);
        if (sib_buf)
-               xfs_da_brelse(tp, sib_buf);
-       xfs_da_brelse(tp, last_buf);
+               xfs_trans_brelse(tp, sib_buf);
+       xfs_trans_brelse(tp, last_buf);
        return error;
 }
 
@@ -1899,8 +1883,10 @@ done:
  * Remove a btree block from a directory or attribute.
  */
 int
-xfs_da_shrink_inode(xfs_da_args_t *args, xfs_dablk_t dead_blkno,
-                   xfs_dabuf_t *dead_buf)
+xfs_da_shrink_inode(
+       xfs_da_args_t   *args,
+       xfs_dablk_t     dead_blkno,
+       struct xfs_buf  *dead_buf)
 {
        xfs_inode_t *dp;
        int done, error, w, count;
@@ -1935,7 +1921,7 @@ xfs_da_shrink_inode(xfs_da_args_t *args, xfs_dablk_t dead_blkno,
                        break;
                }
        }
-       xfs_da_binval(tp, dead_buf);
+       xfs_trans_binval(tp, dead_buf);
        return error;
 }
 
@@ -1967,35 +1953,75 @@ xfs_da_map_covers_blocks(
 }
 
 /*
- * Make a dabuf.
- * Used for get_buf, read_buf, read_bufr, and reada_buf.
+ * Convert a struct xfs_bmbt_irec to a struct xfs_buf_map.
+ *
+ * For the single map case, it is assumed that the caller has provided a pointer
+ * to a valid xfs_buf_map.  For the multiple map case, this function will
+ * allocate the xfs_buf_map to hold all the maps and replace the caller's single
+ * map pointer with the allocated map.
  */
-STATIC int
-xfs_da_do_buf(
-       xfs_trans_t     *trans,
-       xfs_inode_t     *dp,
-       xfs_dablk_t     bno,
-       xfs_daddr_t     *mappedbnop,
-       xfs_dabuf_t     **bpp,
-       int             whichfork,
-       int             caller)
+static int
+xfs_buf_map_from_irec(
+       struct xfs_mount        *mp,
+       struct xfs_buf_map      **mapp,
+       unsigned int            *nmaps,
+       struct xfs_bmbt_irec    *irecs,
+       unsigned int            nirecs)
 {
-       xfs_buf_t       *bp = NULL;
-       xfs_buf_t       **bplist;
-       int             error=0;
-       int             i;
-       xfs_bmbt_irec_t map;
-       xfs_bmbt_irec_t *mapp;
-       xfs_daddr_t     mappedbno;
-       xfs_mount_t     *mp;
-       int             nbplist=0;
-       int             nfsb;
-       int             nmap;
-       xfs_dabuf_t     *rbp;
+       struct xfs_buf_map      *map;
+       int                     i;
+
+       ASSERT(*nmaps == 1);
+       ASSERT(nirecs >= 1);
+
+       if (nirecs > 1) {
+               map = kmem_zalloc(nirecs * sizeof(struct xfs_buf_map), KM_SLEEP);
+               if (!map)
+                       return ENOMEM;
+               *mapp = map;
+       }
+
+       *nmaps = nirecs;
+       map = *mapp;
+       for (i = 0; i < *nmaps; i++) {
+               ASSERT(irecs[i].br_startblock != DELAYSTARTBLOCK &&
+                      irecs[i].br_startblock != HOLESTARTBLOCK);
+               map[i].bm_bn = XFS_FSB_TO_DADDR(mp, irecs[i].br_startblock);
+               map[i].bm_len = XFS_FSB_TO_BB(mp, irecs[i].br_blockcount);
+       }
+       return 0;
+}
+
+/*
+ * Map the block we are given ready for reading. There are three possible return
+ * values:
+ *     -1 - will be returned if we land in a hole and mappedbno == -2 so the
+ *          caller knows not to execute a subsequent read.
+ *      0 - if we mapped the block successfully
+ *     >0 - positive error number if there was an error.
+ */
+static int
+xfs_dabuf_map(
+       struct xfs_trans        *trans,
+       struct xfs_inode        *dp,
+       xfs_dablk_t             bno,
+       xfs_daddr_t             mappedbno,
+       int                     whichfork,
+       struct xfs_buf_map      **map,
+       int                     *nmaps)
+{
+       struct xfs_mount        *mp = dp->i_mount;
+       int                     nfsb;
+       int                     error = 0;
+       struct xfs_bmbt_irec    irec;
+       struct xfs_bmbt_irec    *irecs = &irec;
+       int                     nirecs;
+
+       ASSERT(map && *map);
+       ASSERT(*nmaps == 1);
 
-       mp = dp->i_mount;
        nfsb = (whichfork == XFS_DATA_FORK) ? mp->m_dirblkfsbs : 1;
-       mappedbno = *mappedbnop;
+
        /*
         * Caller doesn't have a mapping.  -2 means don't complain
         * if we land in a hole.
@@ -2004,112 +2030,150 @@ xfs_da_do_buf(
                /*
                 * Optimize the one-block case.
                 */
-               if (nfsb == 1)
-                       mapp = &map;
-               else
-                       mapp = kmem_alloc(sizeof(*mapp) * nfsb, KM_SLEEP);
+               if (nfsb != 1)
+                       irecs = kmem_zalloc(sizeof(irec) * nfsb, KM_SLEEP);
 
-               nmap = nfsb;
-               error = xfs_bmapi_read(dp, (xfs_fileoff_t)bno, nfsb, mapp,
-                                      &nmap, xfs_bmapi_aflag(whichfork));
+               nirecs = nfsb;
+               error = xfs_bmapi_read(dp, (xfs_fileoff_t)bno, nfsb, irecs,
+                                      &nirecs, xfs_bmapi_aflag(whichfork));
                if (error)
-                       goto exit0;
+                       goto out;
        } else {
-               map.br_startblock = XFS_DADDR_TO_FSB(mp, mappedbno);
-               map.br_startoff = (xfs_fileoff_t)bno;
-               map.br_blockcount = nfsb;
-               mapp = &map;
-               nmap = 1;
+               irecs->br_startblock = XFS_DADDR_TO_FSB(mp, mappedbno);
+               irecs->br_startoff = (xfs_fileoff_t)bno;
+               irecs->br_blockcount = nfsb;
+               irecs->br_state = 0;
+               nirecs = 1;
        }
-       if (!xfs_da_map_covers_blocks(nmap, mapp, bno, nfsb)) {
-               error = mappedbno == -2 ? 0 : XFS_ERROR(EFSCORRUPTED);
+
+       if (!xfs_da_map_covers_blocks(nirecs, irecs, bno, nfsb)) {
+               error = mappedbno == -2 ? -1 : XFS_ERROR(EFSCORRUPTED);
                if (unlikely(error == EFSCORRUPTED)) {
                        if (xfs_error_level >= XFS_ERRLEVEL_LOW) {
+                               int i;
                                xfs_alert(mp, "%s: bno %lld dir: inode %lld",
                                        __func__, (long long)bno,
                                        (long long)dp->i_ino);
-                               for (i = 0; i < nmap; i++) {
+                               for (i = 0; i < *nmaps; i++) {
                                        xfs_alert(mp,
 "[%02d] br_startoff %lld br_startblock %lld br_blockcount %lld br_state %d",
                                                i,
-                                               (long long)mapp[i].br_startoff,
-                                               (long long)mapp[i].br_startblock,
-                                               (long long)mapp[i].br_blockcount,
-                                               mapp[i].br_state);
+                                               (long long)irecs[i].br_startoff,
+                                               (long long)irecs[i].br_startblock,
+                                               (long long)irecs[i].br_blockcount,
+                                               irecs[i].br_state);
                                }
                        }
                        XFS_ERROR_REPORT("xfs_da_do_buf(1)",
                                         XFS_ERRLEVEL_LOW, mp);
                }
-               goto exit0;
+               goto out;
        }
-       if (caller != 3 && nmap > 1) {
-               bplist = kmem_alloc(sizeof(*bplist) * nmap, KM_SLEEP);
-               nbplist = 0;
-       } else
-               bplist = NULL;
-       /*
-        * Turn the mapping(s) into buffer(s).
-        */
-       for (i = 0; i < nmap; i++) {
-               int     nmapped;
-
-               mappedbno = XFS_FSB_TO_DADDR(mp, mapp[i].br_startblock);
-               if (i == 0)
-                       *mappedbnop = mappedbno;
-               nmapped = (int)XFS_FSB_TO_BB(mp, mapp[i].br_blockcount);
-               switch (caller) {
-               case 0:
-                       bp = xfs_trans_get_buf(trans, mp->m_ddev_targp,
-                               mappedbno, nmapped, 0);
-                       error = bp ? bp->b_error : XFS_ERROR(EIO);
-                       break;
-               case 1:
-               case 2:
-                       bp = NULL;
-                       error = xfs_trans_read_buf(mp, trans, mp->m_ddev_targp,
-                               mappedbno, nmapped, 0, &bp);
-                       break;
-               case 3:
-                       xfs_buf_readahead(mp->m_ddev_targp, mappedbno, nmapped);
+       error = xfs_buf_map_from_irec(mp, map, nmaps, irecs, nirecs);
+out:
+       if (irecs != &irec)
+               kmem_free(irecs);
+       return error;
+}
+
+/*
+ * Get a buffer for the dir/attr block.
+ */
+int
+xfs_da_get_buf(
+       struct xfs_trans        *trans,
+       struct xfs_inode        *dp,
+       xfs_dablk_t             bno,
+       xfs_daddr_t             mappedbno,
+       struct xfs_buf          **bpp,
+       int                     whichfork)
+{
+       struct xfs_buf          *bp;
+       struct xfs_buf_map      map;
+       struct xfs_buf_map      *mapp;
+       int                     nmap;
+       int                     error;
+
+       *bpp = NULL;
+       mapp = &map;
+       nmap = 1;
+       error = xfs_dabuf_map(trans, dp, bno, mappedbno, whichfork,
+                               &mapp, &nmap);
+       if (error) {
+               /* mapping a hole is not an error, but we don't continue */
+               if (error == -1)
                        error = 0;
-                       bp = NULL;
-                       break;
-               }
-               if (error) {
-                       if (bp)
-                               xfs_trans_brelse(trans, bp);
-                       goto exit1;
-               }
-               if (!bp)
-                       continue;
-               if (caller == 1) {
-                       if (whichfork == XFS_ATTR_FORK)
-                               xfs_buf_set_ref(bp, XFS_ATTR_BTREE_REF);
-                       else
-                               xfs_buf_set_ref(bp, XFS_DIR_BTREE_REF);
-               }
-               if (bplist) {
-                       bplist[nbplist++] = bp;
-               }
+               goto out_free;
        }
-       /*
-        * Build a dabuf structure.
-        */
-       if (bplist) {
-               rbp = xfs_da_buf_make(nbplist, bplist);
-       } else if (bp)
-               rbp = xfs_da_buf_make(1, &bp);
+
+       bp = xfs_trans_get_buf_map(trans, dp->i_mount->m_ddev_targp,
+                                   mapp, nmap, 0);
+       error = bp ? bp->b_error : XFS_ERROR(EIO);
+       if (error) {
+               xfs_trans_brelse(trans, bp);
+               goto out_free;
+       }
+
+       *bpp = bp;
+
+out_free:
+       if (mapp != &map)
+               kmem_free(mapp);
+
+       return error;
+}
+
+/*
+ * Get a buffer for the dir/attr block, fill in the contents.
+ */
+int
+xfs_da_read_buf(
+       struct xfs_trans        *trans,
+       struct xfs_inode        *dp,
+       xfs_dablk_t             bno,
+       xfs_daddr_t             mappedbno,
+       struct xfs_buf          **bpp,
+       int                     whichfork)
+{
+       struct xfs_buf          *bp;
+       struct xfs_buf_map      map;
+       struct xfs_buf_map      *mapp;
+       int                     nmap;
+       int                     error;
+
+       *bpp = NULL;
+       mapp = &map;
+       nmap = 1;
+       error = xfs_dabuf_map(trans, dp, bno, mappedbno, whichfork,
+                               &mapp, &nmap);
+       if (error) {
+               /* mapping a hole is not an error, but we don't continue */
+               if (error == -1)
+                       error = 0;
+               goto out_free;
+       }
+
+       error = xfs_trans_read_buf_map(dp->i_mount, trans,
+                                       dp->i_mount->m_ddev_targp,
+                                       mapp, nmap, 0, &bp);
+       if (error)
+               goto out_free;
+
+       if (whichfork == XFS_ATTR_FORK)
+               xfs_buf_set_ref(bp, XFS_ATTR_BTREE_REF);
        else
-               rbp = NULL;
+               xfs_buf_set_ref(bp, XFS_DIR_BTREE_REF);
+
        /*
-        * For read_buf, check the magic number.
+        * This verification code will be moved to a CRC verification callback
+        * function so just leave it here unchanged until then.
         */
-       if (caller == 1) {
-               xfs_dir2_data_hdr_t     *hdr = rbp->data;
-               xfs_dir2_free_t         *free = rbp->data;
-               xfs_da_blkinfo_t        *info = rbp->data;
+       {
+               xfs_dir2_data_hdr_t     *hdr = bp->b_addr;
+               xfs_dir2_free_t         *free = bp->b_addr;
+               xfs_da_blkinfo_t        *info = bp->b_addr;
                uint                    magic, magic1;
+               struct xfs_mount        *mp = dp->i_mount;
 
                magic = be16_to_cpu(info->magic);
                magic1 = be32_to_cpu(hdr->magic);
@@ -2123,66 +2187,20 @@ xfs_da_do_buf(
                                   (free->hdr.magic != cpu_to_be32(XFS_DIR2_FREE_MAGIC)),
                                mp, XFS_ERRTAG_DA_READ_BUF,
                                XFS_RANDOM_DA_READ_BUF))) {
-                       trace_xfs_da_btree_corrupt(rbp->bps[0], _RET_IP_);
+                       trace_xfs_da_btree_corrupt(bp, _RET_IP_);
                        XFS_CORRUPTION_ERROR("xfs_da_do_buf(2)",
                                             XFS_ERRLEVEL_LOW, mp, info);
                        error = XFS_ERROR(EFSCORRUPTED);
-                       xfs_da_brelse(trans, rbp);
-                       nbplist = 0;
-                       goto exit1;
+                       xfs_trans_brelse(trans, bp);
+                       goto out_free;
                }
        }
-       if (bplist) {
-               kmem_free(bplist);
-       }
-       if (mapp != &map) {
-               kmem_free(mapp);
-       }
-       if (bpp)
-               *bpp = rbp;
-       return 0;
-exit1:
-       if (bplist) {
-               for (i = 0; i < nbplist; i++)
-                       xfs_trans_brelse(trans, bplist[i]);
-               kmem_free(bplist);
-       }
-exit0:
+       *bpp = bp;
+out_free:
        if (mapp != &map)
                kmem_free(mapp);
-       if (bpp)
-               *bpp = NULL;
-       return error;
-}
-
-/*
- * Get a buffer for the dir/attr block.
- */
-int
-xfs_da_get_buf(
-       xfs_trans_t     *trans,
-       xfs_inode_t     *dp,
-       xfs_dablk_t     bno,
-       xfs_daddr_t             mappedbno,
-       xfs_dabuf_t     **bpp,
-       int             whichfork)
-{
-       return xfs_da_do_buf(trans, dp, bno, &mappedbno, bpp, whichfork, 0);
-}
 
-/*
- * Get a buffer for the dir/attr block, fill in the contents.
- */
-int
-xfs_da_read_buf(
-       xfs_trans_t     *trans,
-       xfs_inode_t     *dp,
-       xfs_dablk_t     bno,
-       xfs_daddr_t             mappedbno,
-       xfs_dabuf_t     **bpp,
-       int             whichfork)
-{
-       return xfs_da_do_buf(trans, dp, bno, &mappedbno, bpp, whichfork, 1);
+       return error;
 }
 
 /*
@@ -2190,22 +2208,41 @@ xfs_da_read_buf(
  */
 xfs_daddr_t
 xfs_da_reada_buf(
-       xfs_trans_t     *trans,
-       xfs_inode_t     *dp,
-       xfs_dablk_t     bno,
-       int             whichfork)
+       struct xfs_trans        *trans,
+       struct xfs_inode        *dp,
+       xfs_dablk_t             bno,
+       int                     whichfork)
 {
-       xfs_daddr_t             rval;
+       xfs_daddr_t             mappedbno = -1;
+       struct xfs_buf_map      map;
+       struct xfs_buf_map      *mapp;
+       int                     nmap;
+       int                     error;
+
+       mapp = &map;
+       nmap = 1;
+       error = xfs_dabuf_map(trans, dp, bno, -1, whichfork,
+                               &mapp, &nmap);
+       if (error) {
+               /* mapping a hole is not an error, but we don't continue */
+               if (error == -1)
+                       error = 0;
+               goto out_free;
+       }
 
-       rval = -1;
-       if (xfs_da_do_buf(trans, dp, bno, &rval, NULL, whichfork, 3))
+       mappedbno = mapp[0].bm_bn;
+       xfs_buf_readahead_map(dp->i_mount->m_ddev_targp, mapp, nmap);
+
+out_free:
+       if (mapp != &map)
+               kmem_free(mapp);
+
+       if (error)
                return -1;
-       else
-               return rval;
+       return mappedbno;
 }
 
 kmem_zone_t *xfs_da_state_zone;        /* anchor for state struct zone */
-kmem_zone_t *xfs_dabuf_zone;           /* dabuf zone */
 
 /*
  * Allocate a dir-state structure.
@@ -2225,13 +2262,8 @@ xfs_da_state_kill_altpath(xfs_da_state_t *state)
 {
        int     i;
 
-       for (i = 0; i < state->altpath.active; i++) {
-               if (state->altpath.blk[i].bp) {
-                       if (state->altpath.blk[i].bp != state->path.blk[i].bp)
-                               xfs_da_buf_done(state->altpath.blk[i].bp);
-                       state->altpath.blk[i].bp = NULL;
-               }
-       }
+       for (i = 0; i < state->altpath.active; i++)
+               state->altpath.blk[i].bp = NULL;
        state->altpath.active = 0;
 }
 
@@ -2241,204 +2273,9 @@ xfs_da_state_kill_altpath(xfs_da_state_t *state)
 void
 xfs_da_state_free(xfs_da_state_t *state)
 {
-       int     i;
-
        xfs_da_state_kill_altpath(state);
-       for (i = 0; i < state->path.active; i++) {
-               if (state->path.blk[i].bp)
-                       xfs_da_buf_done(state->path.blk[i].bp);
-       }
-       if (state->extravalid && state->extrablk.bp)
-               xfs_da_buf_done(state->extrablk.bp);
 #ifdef DEBUG
        memset((char *)state, 0, sizeof(*state));
 #endif /* DEBUG */
        kmem_zone_free(xfs_da_state_zone, state);
 }
-
-/*
- * Create a dabuf.
- */
-/* ARGSUSED */
-STATIC xfs_dabuf_t *
-xfs_da_buf_make(int nbuf, xfs_buf_t **bps)
-{
-       xfs_buf_t       *bp;
-       xfs_dabuf_t     *dabuf;
-       int             i;
-       int             off;
-
-       if (nbuf == 1)
-               dabuf = kmem_zone_alloc(xfs_dabuf_zone, KM_NOFS);
-       else
-               dabuf = kmem_alloc(XFS_DA_BUF_SIZE(nbuf), KM_NOFS);
-       dabuf->dirty = 0;
-       if (nbuf == 1) {
-               dabuf->nbuf = 1;
-               bp = bps[0];
-               dabuf->bbcount = bp->b_length;
-               dabuf->data = bp->b_addr;
-               dabuf->bps[0] = bp;
-       } else {
-               dabuf->nbuf = nbuf;
-               for (i = 0, dabuf->bbcount = 0; i < nbuf; i++) {
-                       dabuf->bps[i] = bp = bps[i];
-                       dabuf->bbcount += bp->b_length;
-               }
-               dabuf->data = kmem_alloc(BBTOB(dabuf->bbcount), KM_SLEEP);
-               for (i = off = 0; i < nbuf; i++, off += BBTOB(bp->b_length)) {
-                       bp = bps[i];
-                       memcpy((char *)dabuf->data + off, bp->b_addr,
-                               BBTOB(bp->b_length));
-               }
-       }
-       return dabuf;
-}
-
-/*
- * Un-dirty a dabuf.
- */
-STATIC void
-xfs_da_buf_clean(xfs_dabuf_t *dabuf)
-{
-       xfs_buf_t       *bp;
-       int             i;
-       int             off;
-
-       if (dabuf->dirty) {
-               ASSERT(dabuf->nbuf > 1);
-               dabuf->dirty = 0;
-               for (i = off = 0; i < dabuf->nbuf;
-                               i++, off += BBTOB(bp->b_length)) {
-                       bp = dabuf->bps[i];
-                       memcpy(bp->b_addr, dabuf->data + off,
-                                               BBTOB(bp->b_length));
-               }
-       }
-}
-
-/*
- * Release a dabuf.
- */
-void
-xfs_da_buf_done(xfs_dabuf_t *dabuf)
-{
-       ASSERT(dabuf);
-       ASSERT(dabuf->nbuf && dabuf->data && dabuf->bbcount && dabuf->bps[0]);
-       if (dabuf->dirty)
-               xfs_da_buf_clean(dabuf);
-       if (dabuf->nbuf > 1) {
-               kmem_free(dabuf->data);
-               kmem_free(dabuf);
-       } else {
-               kmem_zone_free(xfs_dabuf_zone, dabuf);
-       }
-}
-
-/*
- * Log transaction from a dabuf.
- */
-void
-xfs_da_log_buf(xfs_trans_t *tp, xfs_dabuf_t *dabuf, uint first, uint last)
-{
-       xfs_buf_t       *bp;
-       uint            f;
-       int             i;
-       uint            l;
-       int             off;
-
-       ASSERT(dabuf->nbuf && dabuf->data && dabuf->bbcount && dabuf->bps[0]);
-       if (dabuf->nbuf == 1) {
-               ASSERT(dabuf->data == dabuf->bps[0]->b_addr);
-               xfs_trans_log_buf(tp, dabuf->bps[0], first, last);
-               return;
-       }
-       dabuf->dirty = 1;
-       ASSERT(first <= last);
-       for (i = off = 0; i < dabuf->nbuf; i++, off += BBTOB(bp->b_length)) {
-               bp = dabuf->bps[i];
-               f = off;
-               l = f + BBTOB(bp->b_length) - 1;
-               if (f < first)
-                       f = first;
-               if (l > last)
-                       l = last;
-               if (f <= l)
-                       xfs_trans_log_buf(tp, bp, f - off, l - off);
-               /*
-                * B_DONE is set by xfs_trans_log buf.
-                * If we don't set it on a new buffer (get not read)
-                * then if we don't put anything in the buffer it won't
-                * be set, and at commit it it released into the cache,
-                * and then a read will fail.
-                */
-               else if (!(XFS_BUF_ISDONE(bp)))
-                 XFS_BUF_DONE(bp);
-       }
-       ASSERT(last < off);
-}
-
-/*
- * Release dabuf from a transaction.
- * Have to free up the dabuf before the buffers are released,
- * since the synchronization on the dabuf is really the lock on the buffer.
- */
-void
-xfs_da_brelse(xfs_trans_t *tp, xfs_dabuf_t *dabuf)
-{
-       xfs_buf_t       *bp;
-       xfs_buf_t       **bplist;
-       int             i;
-       int             nbuf;
-
-       ASSERT(dabuf->nbuf && dabuf->data && dabuf->bbcount && dabuf->bps[0]);
-       if ((nbuf = dabuf->nbuf) == 1) {
-               bplist = &bp;
-               bp = dabuf->bps[0];
-       } else {
-               bplist = kmem_alloc(nbuf * sizeof(*bplist), KM_SLEEP);
-               memcpy(bplist, dabuf->bps, nbuf * sizeof(*bplist));
-       }
-       xfs_da_buf_done(dabuf);
-       for (i = 0; i < nbuf; i++)
-               xfs_trans_brelse(tp, bplist[i]);
-       if (bplist != &bp)
-               kmem_free(bplist);
-}
-
-/*
- * Invalidate dabuf from a transaction.
- */
-void
-xfs_da_binval(xfs_trans_t *tp, xfs_dabuf_t *dabuf)
-{
-       xfs_buf_t       *bp;
-       xfs_buf_t       **bplist;
-       int             i;
-       int             nbuf;
-
-       ASSERT(dabuf->nbuf && dabuf->data && dabuf->bbcount && dabuf->bps[0]);
-       if ((nbuf = dabuf->nbuf) == 1) {
-               bplist = &bp;
-               bp = dabuf->bps[0];
-       } else {
-               bplist = kmem_alloc(nbuf * sizeof(*bplist), KM_SLEEP);
-               memcpy(bplist, dabuf->bps, nbuf * sizeof(*bplist));
-       }
-       xfs_da_buf_done(dabuf);
-       for (i = 0; i < nbuf; i++)
-               xfs_trans_binval(tp, bplist[i]);
-       if (bplist != &bp)
-               kmem_free(bplist);
-}
-
-/*
- * Get the first daddr from a dabuf.
- */
-xfs_daddr_t
-xfs_da_blkno(xfs_dabuf_t *dabuf)
-{
-       ASSERT(dabuf->nbuf);
-       ASSERT(dabuf->data);
-       return XFS_BUF_ADDR(dabuf->bps[0]);
-}
index dbf7c074ae730c66de16aee0eccc128f1e96b5fc..132adafb041ecf2c6483047f63940fbccd118d15 100644 (file)
@@ -32,7 +32,7 @@ struct zone;
 /*
  * This structure is common to both leaf nodes and non-leaf nodes in the Btree.
  *
- * Is is used to manage a doubly linked list of all blocks at the same
+ * It is used to manage a doubly linked list of all blocks at the same
  * level in the Btree, and to identify which type of block this is.
  */
 #define XFS_DA_NODE_MAGIC      0xfebe  /* magic number: non-leaf blocks */
@@ -132,24 +132,6 @@ typedef struct xfs_da_args {
        { XFS_DA_OP_OKNOENT,    "OKNOENT" }, \
        { XFS_DA_OP_CILOOKUP,   "CILOOKUP" }
 
-/*
- * Structure to describe buffer(s) for a block.
- * This is needed in the directory version 2 format case, when
- * multiple non-contiguous fsblocks might be needed to cover one
- * logical directory block.
- * If the buffer count is 1 then the data pointer points to the
- * same place as the b_addr field for the buffer, else to kmem_alloced memory.
- */
-typedef struct xfs_dabuf {
-       int             nbuf;           /* number of buffer pointers present */
-       short           dirty;          /* data needs to be copied back */
-       short           bbcount;        /* how large is data in bbs */
-       void            *data;          /* pointer for buffers' data */
-       struct xfs_buf  *bps[1];        /* actually nbuf of these */
-} xfs_dabuf_t;
-#define        XFS_DA_BUF_SIZE(n)      \
-       (sizeof(xfs_dabuf_t) + sizeof(struct xfs_buf *) * ((n) - 1))
-
 /*
  * Storage for holding state during Btree searches and split/join ops.
  *
@@ -158,7 +140,7 @@ typedef struct xfs_dabuf {
  * which is slightly more than enough.
  */
 typedef struct xfs_da_state_blk {
-       xfs_dabuf_t     *bp;            /* buffer containing block */
+       struct xfs_buf  *bp;            /* buffer containing block */
        xfs_dablk_t     blkno;          /* filesystem blkno of buffer */
        xfs_daddr_t     disk_blkno;     /* on-disk blkno (in BBs) of buffer */
        int             index;          /* relevant index into block */
@@ -211,7 +193,7 @@ struct xfs_nameops {
  * Routines used for growing the Btree.
  */
 int    xfs_da_node_create(xfs_da_args_t *args, xfs_dablk_t blkno, int level,
-                                        xfs_dabuf_t **bpp, int whichfork);
+                                        struct xfs_buf **bpp, int whichfork);
 int    xfs_da_split(xfs_da_state_t *state);
 
 /*
@@ -241,14 +223,14 @@ int       xfs_da_grow_inode_int(struct xfs_da_args *args, xfs_fileoff_t *bno,
                              int count);
 int    xfs_da_get_buf(struct xfs_trans *trans, struct xfs_inode *dp,
                              xfs_dablk_t bno, xfs_daddr_t mappedbno,
-                             xfs_dabuf_t **bp, int whichfork);
+                             struct xfs_buf **bp, int whichfork);
 int    xfs_da_read_buf(struct xfs_trans *trans, struct xfs_inode *dp,
                               xfs_dablk_t bno, xfs_daddr_t mappedbno,
-                              xfs_dabuf_t **bpp, int whichfork);
+                              struct xfs_buf **bpp, int whichfork);
 xfs_daddr_t    xfs_da_reada_buf(struct xfs_trans *trans, struct xfs_inode *dp,
                        xfs_dablk_t bno, int whichfork);
 int    xfs_da_shrink_inode(xfs_da_args_t *args, xfs_dablk_t dead_blkno,
-                                         xfs_dabuf_t *dead_buf);
+                                         struct xfs_buf *dead_buf);
 
 uint xfs_da_hashname(const __uint8_t *name_string, int name_length);
 enum xfs_dacmp xfs_da_compname(struct xfs_da_args *args,
@@ -258,15 +240,7 @@ enum xfs_dacmp xfs_da_compname(struct xfs_da_args *args,
 xfs_da_state_t *xfs_da_state_alloc(void);
 void xfs_da_state_free(xfs_da_state_t *state);
 
-void xfs_da_buf_done(xfs_dabuf_t *dabuf);
-void xfs_da_log_buf(struct xfs_trans *tp, xfs_dabuf_t *dabuf, uint first,
-                          uint last);
-void xfs_da_brelse(struct xfs_trans *tp, xfs_dabuf_t *dabuf);
-void xfs_da_binval(struct xfs_trans *tp, xfs_dabuf_t *dabuf);
-xfs_daddr_t xfs_da_blkno(xfs_dabuf_t *dabuf);
-
 extern struct kmem_zone *xfs_da_state_zone;
-extern struct kmem_zone *xfs_dabuf_zone;
 extern const struct xfs_nameops xfs_default_nameops;
 
 #endif /* __XFS_DA_BTREE_H__ */
index a3721633abc8744a1d0187de3382cdd973efdb15..1d9643b3dce656123cb591379bb07ee235143f11 100644 (file)
@@ -33,7 +33,7 @@ typedef struct xfs_timestamp {
  * variable size the leftover area split into a data and an attribute fork.
  * The format of the data and attribute fork depends on the format of the
  * inode as indicated by di_format and di_aformat.  To access the data and
- * attribute use the XFS_DFORK_PTR, XFS_DFORK_DPTR, and XFS_DFORK_PTR macros
+ * attribute use the XFS_DFORK_DPTR, XFS_DFORK_APTR, and XFS_DFORK_PTR macros
  * below.
  *
  * There is a very similar struct icdinode in xfs_inode which matches the
index 67a250c36d414d3be83a50f4acb51f8069f147f4..b26a50f9921db60e746d43946b644415ba24ca6a 100644 (file)
@@ -592,7 +592,7 @@ int
 xfs_dir2_shrink_inode(
        xfs_da_args_t   *args,
        xfs_dir2_db_t   db,
-       xfs_dabuf_t     *bp)
+       struct xfs_buf  *bp)
 {
        xfs_fileoff_t   bno;            /* directory file offset */
        xfs_dablk_t     da;             /* directory file offset */
@@ -634,7 +634,7 @@ xfs_dir2_shrink_inode(
        /*
         * Invalidate the buffer from the transaction.
         */
-       xfs_da_binval(tp, bp);
+       xfs_trans_binval(tp, bp);
        /*
         * If it's not a data block, we're done.
         */
index 586732f2d80de076faf539e87aba8d1444ff172d..e93ca8f054f404245b2927b93cdf486387b432c1 100644 (file)
 /*
  * Local function prototypes.
  */
-static void xfs_dir2_block_log_leaf(xfs_trans_t *tp, xfs_dabuf_t *bp, int first,
-                                   int last);
-static void xfs_dir2_block_log_tail(xfs_trans_t *tp, xfs_dabuf_t *bp);
-static int xfs_dir2_block_lookup_int(xfs_da_args_t *args, xfs_dabuf_t **bpp,
+static void xfs_dir2_block_log_leaf(xfs_trans_t *tp, struct xfs_buf *bp,
+                                   int first, int last);
+static void xfs_dir2_block_log_tail(xfs_trans_t *tp, struct xfs_buf *bp);
+static int xfs_dir2_block_lookup_int(xfs_da_args_t *args, struct xfs_buf **bpp,
                                     int *entno);
 static int xfs_dir2_block_sort(const void *a, const void *b);
 
@@ -66,7 +66,7 @@ xfs_dir2_block_addname(
        xfs_dir2_data_free_t    *bf;            /* bestfree table in block */
        xfs_dir2_data_hdr_t     *hdr;           /* block header */
        xfs_dir2_leaf_entry_t   *blp;           /* block leaf entries */
-       xfs_dabuf_t             *bp;            /* buffer for block */
+       struct xfs_buf          *bp;            /* buffer for block */
        xfs_dir2_block_tail_t   *btp;           /* block tail */
        int                     compact;        /* need to compact leaf ents */
        xfs_dir2_data_entry_t   *dep;           /* block data entry */
@@ -102,14 +102,14 @@ xfs_dir2_block_addname(
                return error;
        }
        ASSERT(bp != NULL);
-       hdr = bp->data;
+       hdr = bp->b_addr;
        /*
         * Check the magic number, corrupted if wrong.
         */
        if (unlikely(hdr->magic != cpu_to_be32(XFS_DIR2_BLOCK_MAGIC))) {
                XFS_CORRUPTION_ERROR("xfs_dir2_block_addname",
                                     XFS_ERRLEVEL_LOW, mp, hdr);
-               xfs_da_brelse(tp, bp);
+               xfs_trans_brelse(tp, bp);
                return XFS_ERROR(EFSCORRUPTED);
        }
        len = xfs_dir2_data_entsize(args->namelen);
@@ -212,7 +212,7 @@ xfs_dir2_block_addname(
         * If this isn't a real add, we're done with the buffer.
         */
        if (args->op_flags & XFS_DA_OP_JUSTCHECK)
-               xfs_da_brelse(tp, bp);
+               xfs_trans_brelse(tp, bp);
        /*
         * If we don't have space for the new entry & leaf ...
         */
@@ -228,7 +228,6 @@ xfs_dir2_block_addname(
                 * Then add the new entry in that format.
                 */
                error = xfs_dir2_block_to_leaf(args, bp);
-               xfs_da_buf_done(bp);
                if (error)
                        return error;
                return xfs_dir2_leaf_addname(args);
@@ -422,7 +421,6 @@ xfs_dir2_block_addname(
        xfs_dir2_block_log_tail(tp, bp);
        xfs_dir2_data_log_entry(tp, bp, dep);
        xfs_dir2_data_check(dp, bp);
-       xfs_da_buf_done(bp);
        return 0;
 }
 
@@ -437,7 +435,7 @@ xfs_dir2_block_getdents(
        filldir_t               filldir)
 {
        xfs_dir2_data_hdr_t     *hdr;           /* block header */
-       xfs_dabuf_t             *bp;            /* buffer for block */
+       struct xfs_buf          *bp;            /* buffer for block */
        xfs_dir2_block_tail_t   *btp;           /* block tail */
        xfs_dir2_data_entry_t   *dep;           /* block data entry */
        xfs_dir2_data_unused_t  *dup;           /* block unused entry */
@@ -469,7 +467,7 @@ xfs_dir2_block_getdents(
         * We'll skip entries before this.
         */
        wantoff = xfs_dir2_dataptr_to_off(mp, *offset);
-       hdr = bp->data;
+       hdr = bp->b_addr;
        xfs_dir2_data_check(dp, bp);
        /*
         * Set up values for the loop.
@@ -514,7 +512,7 @@ xfs_dir2_block_getdents(
                            cook & 0x7fffffff, be64_to_cpu(dep->inumber),
                            DT_UNKNOWN)) {
                        *offset = cook & 0x7fffffff;
-                       xfs_da_brelse(NULL, bp);
+                       xfs_trans_brelse(NULL, bp);
                        return 0;
                }
        }
@@ -525,7 +523,7 @@ xfs_dir2_block_getdents(
         */
        *offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0) &
                        0x7fffffff;
-       xfs_da_brelse(NULL, bp);
+       xfs_trans_brelse(NULL, bp);
        return 0;
 }
 
@@ -535,17 +533,17 @@ xfs_dir2_block_getdents(
 static void
 xfs_dir2_block_log_leaf(
        xfs_trans_t             *tp,            /* transaction structure */
-       xfs_dabuf_t             *bp,            /* block buffer */
+       struct xfs_buf          *bp,            /* block buffer */
        int                     first,          /* index of first logged leaf */
        int                     last)           /* index of last logged leaf */
 {
-       xfs_dir2_data_hdr_t     *hdr = bp->data;
+       xfs_dir2_data_hdr_t     *hdr = bp->b_addr;
        xfs_dir2_leaf_entry_t   *blp;
        xfs_dir2_block_tail_t   *btp;
 
        btp = xfs_dir2_block_tail_p(tp->t_mountp, hdr);
        blp = xfs_dir2_block_leaf_p(btp);
-       xfs_da_log_buf(tp, bp, (uint)((char *)&blp[first] - (char *)hdr),
+       xfs_trans_log_buf(tp, bp, (uint)((char *)&blp[first] - (char *)hdr),
                (uint)((char *)&blp[last + 1] - (char *)hdr - 1));
 }
 
@@ -555,13 +553,13 @@ xfs_dir2_block_log_leaf(
 static void
 xfs_dir2_block_log_tail(
        xfs_trans_t             *tp,            /* transaction structure */
-       xfs_dabuf_t             *bp)            /* block buffer */
+       struct xfs_buf          *bp)            /* block buffer */
 {
-       xfs_dir2_data_hdr_t     *hdr = bp->data;
+       xfs_dir2_data_hdr_t     *hdr = bp->b_addr;
        xfs_dir2_block_tail_t   *btp;
 
        btp = xfs_dir2_block_tail_p(tp->t_mountp, hdr);
-       xfs_da_log_buf(tp, bp, (uint)((char *)btp - (char *)hdr),
+       xfs_trans_log_buf(tp, bp, (uint)((char *)btp - (char *)hdr),
                (uint)((char *)(btp + 1) - (char *)hdr - 1));
 }
 
@@ -575,7 +573,7 @@ xfs_dir2_block_lookup(
 {
        xfs_dir2_data_hdr_t     *hdr;           /* block header */
        xfs_dir2_leaf_entry_t   *blp;           /* block leaf entries */
-       xfs_dabuf_t             *bp;            /* block buffer */
+       struct xfs_buf          *bp;            /* block buffer */
        xfs_dir2_block_tail_t   *btp;           /* block tail */
        xfs_dir2_data_entry_t   *dep;           /* block data entry */
        xfs_inode_t             *dp;            /* incore inode */
@@ -593,7 +591,7 @@ xfs_dir2_block_lookup(
                return error;
        dp = args->dp;
        mp = dp->i_mount;
-       hdr = bp->data;
+       hdr = bp->b_addr;
        xfs_dir2_data_check(dp, bp);
        btp = xfs_dir2_block_tail_p(mp, hdr);
        blp = xfs_dir2_block_leaf_p(btp);
@@ -607,7 +605,7 @@ xfs_dir2_block_lookup(
         */
        args->inumber = be64_to_cpu(dep->inumber);
        error = xfs_dir_cilookup_result(args, dep->name, dep->namelen);
-       xfs_da_brelse(args->trans, bp);
+       xfs_trans_brelse(args->trans, bp);
        return XFS_ERROR(error);
 }
 
@@ -617,13 +615,13 @@ xfs_dir2_block_lookup(
 static int                                     /* error */
 xfs_dir2_block_lookup_int(
        xfs_da_args_t           *args,          /* dir lookup arguments */
-       xfs_dabuf_t             **bpp,          /* returned block buffer */
+       struct xfs_buf          **bpp,          /* returned block buffer */
        int                     *entno)         /* returned entry number */
 {
        xfs_dir2_dataptr_t      addr;           /* data entry address */
        xfs_dir2_data_hdr_t     *hdr;           /* block header */
        xfs_dir2_leaf_entry_t   *blp;           /* block leaf entries */
-       xfs_dabuf_t             *bp;            /* block buffer */
+       struct xfs_buf          *bp;            /* block buffer */
        xfs_dir2_block_tail_t   *btp;           /* block tail */
        xfs_dir2_data_entry_t   *dep;           /* block data entry */
        xfs_inode_t             *dp;            /* incore inode */
@@ -647,7 +645,7 @@ xfs_dir2_block_lookup_int(
                return error;
        }
        ASSERT(bp != NULL);
-       hdr = bp->data;
+       hdr = bp->b_addr;
        xfs_dir2_data_check(dp, bp);
        btp = xfs_dir2_block_tail_p(mp, hdr);
        blp = xfs_dir2_block_leaf_p(btp);
@@ -666,7 +664,7 @@ xfs_dir2_block_lookup_int(
                        high = mid - 1;
                if (low > high) {
                        ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
-                       xfs_da_brelse(tp, bp);
+                       xfs_trans_brelse(tp, bp);
                        return XFS_ERROR(ENOENT);
                }
        }
@@ -714,7 +712,7 @@ xfs_dir2_block_lookup_int(
        /*
         * No match, release the buffer and return ENOENT.
         */
-       xfs_da_brelse(tp, bp);
+       xfs_trans_brelse(tp, bp);
        return XFS_ERROR(ENOENT);
 }
 
@@ -728,7 +726,7 @@ xfs_dir2_block_removename(
 {
        xfs_dir2_data_hdr_t     *hdr;           /* block header */
        xfs_dir2_leaf_entry_t   *blp;           /* block leaf pointer */
-       xfs_dabuf_t             *bp;            /* block buffer */
+       struct xfs_buf          *bp;            /* block buffer */
        xfs_dir2_block_tail_t   *btp;           /* block tail */
        xfs_dir2_data_entry_t   *dep;           /* block data entry */
        xfs_inode_t             *dp;            /* incore inode */
@@ -753,7 +751,7 @@ xfs_dir2_block_removename(
        dp = args->dp;
        tp = args->trans;
        mp = dp->i_mount;
-       hdr = bp->data;
+       hdr = bp->b_addr;
        btp = xfs_dir2_block_tail_p(mp, hdr);
        blp = xfs_dir2_block_leaf_p(btp);
        /*
@@ -790,10 +788,9 @@ xfs_dir2_block_removename(
         * See if the size as a shortform is good enough.
         */
        size = xfs_dir2_block_sfsize(dp, hdr, &sfh);
-       if (size > XFS_IFORK_DSIZE(dp)) {
-               xfs_da_buf_done(bp);
+       if (size > XFS_IFORK_DSIZE(dp))
                return 0;
-       }
+
        /*
         * If it works, do the conversion.
         */
@@ -810,7 +807,7 @@ xfs_dir2_block_replace(
 {
        xfs_dir2_data_hdr_t     *hdr;           /* block header */
        xfs_dir2_leaf_entry_t   *blp;           /* block leaf entries */
-       xfs_dabuf_t             *bp;            /* block buffer */
+       struct xfs_buf          *bp;            /* block buffer */
        xfs_dir2_block_tail_t   *btp;           /* block tail */
        xfs_dir2_data_entry_t   *dep;           /* block data entry */
        xfs_inode_t             *dp;            /* incore inode */
@@ -829,7 +826,7 @@ xfs_dir2_block_replace(
        }
        dp = args->dp;
        mp = dp->i_mount;
-       hdr = bp->data;
+       hdr = bp->b_addr;
        btp = xfs_dir2_block_tail_p(mp, hdr);
        blp = xfs_dir2_block_leaf_p(btp);
        /*
@@ -844,7 +841,6 @@ xfs_dir2_block_replace(
        dep->inumber = cpu_to_be64(args->inumber);
        xfs_dir2_data_log_entry(args->trans, bp, dep);
        xfs_dir2_data_check(dp, bp);
-       xfs_da_buf_done(bp);
        return 0;
 }
 
@@ -871,8 +867,8 @@ xfs_dir2_block_sort(
 int                                            /* error */
 xfs_dir2_leaf_to_block(
        xfs_da_args_t           *args,          /* operation arguments */
-       xfs_dabuf_t             *lbp,           /* leaf buffer */
-       xfs_dabuf_t             *dbp)           /* data buffer */
+       struct xfs_buf          *lbp,           /* leaf buffer */
+       struct xfs_buf          *dbp)           /* data buffer */
 {
        __be16                  *bestsp;        /* leaf bests table */
        xfs_dir2_data_hdr_t     *hdr;           /* block header */
@@ -898,7 +894,7 @@ xfs_dir2_leaf_to_block(
        dp = args->dp;
        tp = args->trans;
        mp = dp->i_mount;
-       leaf = lbp->data;
+       leaf = lbp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAF1_MAGIC));
        ltp = xfs_dir2_leaf_tail_p(mp, leaf);
        /*
@@ -914,11 +910,9 @@ xfs_dir2_leaf_to_block(
                        if ((error =
                            xfs_dir2_leaf_trim_data(args, lbp,
                                    (xfs_dir2_db_t)(be32_to_cpu(ltp->bestcount) - 1))))
-                               goto out;
-               } else {
-                       error = 0;
-                       goto out;
-               }
+                               return error;
+               } else
+                       return 0;
        }
        /*
         * Read the data block if we don't already have it, give up if it fails.
@@ -926,9 +920,9 @@ xfs_dir2_leaf_to_block(
        if (dbp == NULL &&
            (error = xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, &dbp,
                    XFS_DATA_FORK))) {
-               goto out;
+               return error;
        }
-       hdr = dbp->data;
+       hdr = dbp->b_addr;
        ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC));
        /*
         * Size of the "leaf" area in the block.
@@ -944,10 +938,9 @@ xfs_dir2_leaf_to_block(
         * If it's not free or is too short we can't do it.
         */
        if (be16_to_cpu(dup->freetag) != XFS_DIR2_DATA_FREE_TAG ||
-           be16_to_cpu(dup->length) < size) {
-               error = 0;
-               goto out;
-       }
+           be16_to_cpu(dup->length) < size)
+               return 0;
+
        /*
         * Start converting it to block form.
         */
@@ -989,25 +982,17 @@ xfs_dir2_leaf_to_block(
         * Pitch the old leaf block.
         */
        error = xfs_da_shrink_inode(args, mp->m_dirleafblk, lbp);
-       lbp = NULL;
-       if (error) {
-               goto out;
-       }
+       if (error)
+               return error;
+
        /*
         * Now see if the resulting block can be shrunken to shortform.
         */
        size = xfs_dir2_block_sfsize(dp, hdr, &sfh);
-       if (size > XFS_IFORK_DSIZE(dp)) {
-               error = 0;
-               goto out;
-       }
+       if (size > XFS_IFORK_DSIZE(dp))
+               return 0;
+
        return xfs_dir2_block_to_sf(args, dbp, size, &sfh);
-out:
-       if (lbp)
-               xfs_da_buf_done(lbp);
-       if (dbp)
-               xfs_da_buf_done(dbp);
-       return error;
 }
 
 /*
@@ -1020,7 +1005,7 @@ xfs_dir2_sf_to_block(
        xfs_dir2_db_t           blkno;          /* dir-relative block # (0) */
        xfs_dir2_data_hdr_t     *hdr;           /* block header */
        xfs_dir2_leaf_entry_t   *blp;           /* block leaf entries */
-       xfs_dabuf_t             *bp;            /* block buffer */
+       struct xfs_buf          *bp;            /* block buffer */
        xfs_dir2_block_tail_t   *btp;           /* block tail pointer */
        xfs_dir2_data_entry_t   *dep;           /* data entry pointer */
        xfs_inode_t             *dp;            /* incore directory inode */
@@ -1088,7 +1073,7 @@ xfs_dir2_sf_to_block(
                kmem_free(sfp);
                return error;
        }
-       hdr = bp->data;
+       hdr = bp->b_addr;
        hdr->magic = cpu_to_be32(XFS_DIR2_BLOCK_MAGIC);
        /*
         * Compute size of block "tail" area.
@@ -1217,6 +1202,5 @@ xfs_dir2_sf_to_block(
        xfs_dir2_block_log_leaf(tp, bp, 0, be32_to_cpu(btp->count) - 1);
        xfs_dir2_block_log_tail(tp, bp);
        xfs_dir2_data_check(dp, bp);
-       xfs_da_buf_done(bp);
        return 0;
 }
index 2046988e9eb20dcf46832288f74f73f8f966f31c..44ffd4d6bc91af0f0f738bcf38f1371b2185d684 100644 (file)
@@ -42,8 +42,8 @@ xfs_dir2_data_freefind(xfs_dir2_data_hdr_t *hdr, xfs_dir2_data_unused_t *dup);
  */
 void
 xfs_dir2_data_check(
-       xfs_inode_t             *dp,            /* incore inode pointer */
-       xfs_dabuf_t             *bp)            /* data block's buffer */
+       struct xfs_inode        *dp,            /* incore inode pointer */
+       struct xfs_buf          *bp)            /* data block's buffer */
 {
        xfs_dir2_dataptr_t      addr;           /* addr for leaf lookup */
        xfs_dir2_data_free_t    *bf;            /* bestfree table */
@@ -65,7 +65,7 @@ xfs_dir2_data_check(
        struct xfs_name         name;
 
        mp = dp->i_mount;
-       hdr = bp->data;
+       hdr = bp->b_addr;
        bf = hdr->bestfree;
        p = (char *)(hdr + 1);
 
@@ -389,9 +389,9 @@ int                                         /* error */
 xfs_dir2_data_init(
        xfs_da_args_t           *args,          /* directory operation args */
        xfs_dir2_db_t           blkno,          /* logical dir block number */
-       xfs_dabuf_t             **bpp)          /* output block buffer */
+       struct xfs_buf          **bpp)          /* output block buffer */
 {
-       xfs_dabuf_t             *bp;            /* block buffer */
+       struct xfs_buf          *bp;            /* block buffer */
        xfs_dir2_data_hdr_t     *hdr;           /* data block header */
        xfs_inode_t             *dp;            /* incore directory inode */
        xfs_dir2_data_unused_t  *dup;           /* unused entry pointer */
@@ -417,7 +417,7 @@ xfs_dir2_data_init(
        /*
         * Initialize the header.
         */
-       hdr = bp->data;
+       hdr = bp->b_addr;
        hdr->magic = cpu_to_be32(XFS_DIR2_DATA_MAGIC);
        hdr->bestfree[0].offset = cpu_to_be16(sizeof(*hdr));
        for (i = 1; i < XFS_DIR2_DATA_FD_COUNT; i++) {
@@ -449,16 +449,16 @@ xfs_dir2_data_init(
  */
 void
 xfs_dir2_data_log_entry(
-       xfs_trans_t             *tp,            /* transaction pointer */
-       xfs_dabuf_t             *bp,            /* block buffer */
+       struct xfs_trans        *tp,
+       struct xfs_buf          *bp,
        xfs_dir2_data_entry_t   *dep)           /* data entry pointer */
 {
-       xfs_dir2_data_hdr_t     *hdr = bp->data;
+       xfs_dir2_data_hdr_t     *hdr = bp->b_addr;
 
        ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
               hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC));
 
-       xfs_da_log_buf(tp, bp, (uint)((char *)dep - (char *)hdr),
+       xfs_trans_log_buf(tp, bp, (uint)((char *)dep - (char *)hdr),
                (uint)((char *)(xfs_dir2_data_entry_tag_p(dep) + 1) -
                       (char *)hdr - 1));
 }
@@ -468,15 +468,15 @@ xfs_dir2_data_log_entry(
  */
 void
 xfs_dir2_data_log_header(
-       xfs_trans_t             *tp,            /* transaction pointer */
-       xfs_dabuf_t             *bp)            /* block buffer */
+       struct xfs_trans        *tp,
+       struct xfs_buf          *bp)
 {
-       xfs_dir2_data_hdr_t     *hdr = bp->data;
+       xfs_dir2_data_hdr_t     *hdr = bp->b_addr;
 
        ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
               hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC));
 
-       xfs_da_log_buf(tp, bp, 0, sizeof(*hdr) - 1);
+       xfs_trans_log_buf(tp, bp, 0, sizeof(*hdr) - 1);
 }
 
 /*
@@ -484,11 +484,11 @@ xfs_dir2_data_log_header(
  */
 void
 xfs_dir2_data_log_unused(
-       xfs_trans_t             *tp,            /* transaction pointer */
-       xfs_dabuf_t             *bp,            /* block buffer */
+       struct xfs_trans        *tp,
+       struct xfs_buf          *bp,
        xfs_dir2_data_unused_t  *dup)           /* data unused pointer */
 {
-       xfs_dir2_data_hdr_t     *hdr = bp->data;
+       xfs_dir2_data_hdr_t     *hdr = bp->b_addr;
 
        ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
               hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC));
@@ -496,13 +496,13 @@ xfs_dir2_data_log_unused(
        /*
         * Log the first part of the unused entry.
         */
-       xfs_da_log_buf(tp, bp, (uint)((char *)dup - (char *)hdr),
+       xfs_trans_log_buf(tp, bp, (uint)((char *)dup - (char *)hdr),
                (uint)((char *)&dup->length + sizeof(dup->length) -
                       1 - (char *)hdr));
        /*
         * Log the end (tag) of the unused entry.
         */
-       xfs_da_log_buf(tp, bp,
+       xfs_trans_log_buf(tp, bp,
                (uint)((char *)xfs_dir2_data_unused_tag_p(dup) - (char *)hdr),
                (uint)((char *)xfs_dir2_data_unused_tag_p(dup) - (char *)hdr +
                       sizeof(xfs_dir2_data_off_t) - 1));
@@ -514,8 +514,8 @@ xfs_dir2_data_log_unused(
  */
 void
 xfs_dir2_data_make_free(
-       xfs_trans_t             *tp,            /* transaction pointer */
-       xfs_dabuf_t             *bp,            /* block buffer */
+       struct xfs_trans        *tp,
+       struct xfs_buf          *bp,
        xfs_dir2_data_aoff_t    offset,         /* starting byte offset */
        xfs_dir2_data_aoff_t    len,            /* length in bytes */
        int                     *needlogp,      /* out: log header */
@@ -531,7 +531,7 @@ xfs_dir2_data_make_free(
        xfs_dir2_data_unused_t  *prevdup;       /* unused entry before us */
 
        mp = tp->t_mountp;
-       hdr = bp->data;
+       hdr = bp->b_addr;
 
        /*
         * Figure out where the end of the data area is.
@@ -696,8 +696,8 @@ xfs_dir2_data_make_free(
  */
 void
 xfs_dir2_data_use_free(
-       xfs_trans_t             *tp,            /* transaction pointer */
-       xfs_dabuf_t             *bp,            /* data block buffer */
+       struct xfs_trans        *tp,
+       struct xfs_buf          *bp,
        xfs_dir2_data_unused_t  *dup,           /* unused entry */
        xfs_dir2_data_aoff_t    offset,         /* starting offset to use */
        xfs_dir2_data_aoff_t    len,            /* length to use */
@@ -713,7 +713,7 @@ xfs_dir2_data_use_free(
        xfs_dir2_data_unused_t  *newdup2;       /* another new unused entry */
        int                     oldlen;         /* old unused entry's length */
 
-       hdr = bp->data;
+       hdr = bp->b_addr;
        ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
               hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC));
        ASSERT(be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG);
index 397ffbcbab1dbc66fec4e65a418c20bd96808950..0b296253bd018d450f5773d6df3bdbcc03d55db5 100644 (file)
  * Local function declarations.
  */
 #ifdef DEBUG
-static void xfs_dir2_leaf_check(xfs_inode_t *dp, xfs_dabuf_t *bp);
+static void xfs_dir2_leaf_check(struct xfs_inode *dp, struct xfs_buf *bp);
 #else
 #define        xfs_dir2_leaf_check(dp, bp)
 #endif
-static int xfs_dir2_leaf_lookup_int(xfs_da_args_t *args, xfs_dabuf_t **lbpp,
-                                   int *indexp, xfs_dabuf_t **dbpp);
-static void xfs_dir2_leaf_log_bests(struct xfs_trans *tp, struct xfs_dabuf *bp,
+static int xfs_dir2_leaf_lookup_int(xfs_da_args_t *args, struct xfs_buf **lbpp,
+                                   int *indexp, struct xfs_buf **dbpp);
+static void xfs_dir2_leaf_log_bests(struct xfs_trans *tp, struct xfs_buf *bp,
                                    int first, int last);
-static void xfs_dir2_leaf_log_tail(struct xfs_trans *tp, struct xfs_dabuf *bp);
+static void xfs_dir2_leaf_log_tail(struct xfs_trans *tp, struct xfs_buf *bp);
 
 
 /*
@@ -55,7 +55,7 @@ static void xfs_dir2_leaf_log_tail(struct xfs_trans *tp, struct xfs_dabuf *bp);
 int                                            /* error */
 xfs_dir2_block_to_leaf(
        xfs_da_args_t           *args,          /* operation arguments */
-       xfs_dabuf_t             *dbp)           /* input block's buffer */
+       struct xfs_buf          *dbp)           /* input block's buffer */
 {
        __be16                  *bestsp;        /* leaf's bestsp entries */
        xfs_dablk_t             blkno;          /* leaf block's bno */
@@ -64,7 +64,7 @@ xfs_dir2_block_to_leaf(
        xfs_dir2_block_tail_t   *btp;           /* block's tail */
        xfs_inode_t             *dp;            /* incore directory inode */
        int                     error;          /* error return code */
-       xfs_dabuf_t             *lbp;           /* leaf block's buffer */
+       struct xfs_buf          *lbp;           /* leaf block's buffer */
        xfs_dir2_db_t           ldb;            /* leaf block's bno */
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
        xfs_dir2_leaf_tail_t    *ltp;           /* leaf's tail */
@@ -95,8 +95,8 @@ xfs_dir2_block_to_leaf(
                return error;
        }
        ASSERT(lbp != NULL);
-       leaf = lbp->data;
-       hdr = dbp->data;
+       leaf = lbp->b_addr;
+       hdr = dbp->b_addr;
        xfs_dir2_data_check(dp, dbp);
        btp = xfs_dir2_block_tail_p(mp, hdr);
        blp = xfs_dir2_block_leaf_p(btp);
@@ -143,7 +143,6 @@ xfs_dir2_block_to_leaf(
        xfs_dir2_leaf_check(dp, lbp);
        xfs_dir2_data_check(dp, dbp);
        xfs_dir2_leaf_log_bests(tp, lbp, 0, 0);
-       xfs_da_buf_done(lbp);
        return 0;
 }
 
@@ -282,7 +281,7 @@ xfs_dir2_leaf_addname(
        __be16                  *bestsp;        /* freespace table in leaf */
        int                     compact;        /* need to compact leaves */
        xfs_dir2_data_hdr_t     *hdr;           /* data block header */
-       xfs_dabuf_t             *dbp;           /* data block buffer */
+       struct xfs_buf          *dbp;           /* data block buffer */
        xfs_dir2_data_entry_t   *dep;           /* data block entry */
        xfs_inode_t             *dp;            /* incore directory inode */
        xfs_dir2_data_unused_t  *dup;           /* data unused entry */
@@ -291,7 +290,7 @@ xfs_dir2_leaf_addname(
        int                     highstale;      /* index of next stale leaf */
        int                     i;              /* temporary, index */
        int                     index;          /* leaf table position */
-       xfs_dabuf_t             *lbp;           /* leaf's buffer */
+       struct xfs_buf          *lbp;           /* leaf's buffer */
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
        int                     length;         /* length of new entry */
        xfs_dir2_leaf_entry_t   *lep;           /* leaf entry table pointer */
@@ -328,7 +327,7 @@ xfs_dir2_leaf_addname(
         * But if there are dup hash values the index is of the first of those.
         */
        index = xfs_dir2_leaf_search_hash(args, lbp);
-       leaf = lbp->data;
+       leaf = lbp->b_addr;
        ltp = xfs_dir2_leaf_tail_p(mp, leaf);
        bestsp = xfs_dir2_leaf_bests_p(ltp);
        length = xfs_dir2_data_entsize(args->namelen);
@@ -402,14 +401,13 @@ xfs_dir2_leaf_addname(
                 */
                if ((args->op_flags & XFS_DA_OP_JUSTCHECK) ||
                                                        args->total == 0) {
-                       xfs_da_brelse(tp, lbp);
+                       xfs_trans_brelse(tp, lbp);
                        return XFS_ERROR(ENOSPC);
                }
                /*
                 * Convert to node form.
                 */
                error = xfs_dir2_leaf_to_node(args, lbp);
-               xfs_da_buf_done(lbp);
                if (error)
                        return error;
                /*
@@ -427,7 +425,7 @@ xfs_dir2_leaf_addname(
         * a new data block.
         */
        if (args->op_flags & XFS_DA_OP_JUSTCHECK) {
-               xfs_da_brelse(tp, lbp);
+               xfs_trans_brelse(tp, lbp);
                return use_block == -1 ? XFS_ERROR(ENOSPC) : 0;
        }
        /*
@@ -435,7 +433,7 @@ xfs_dir2_leaf_addname(
         * changed anything.
         */
        if (args->total == 0 && use_block == -1) {
-               xfs_da_brelse(tp, lbp);
+               xfs_trans_brelse(tp, lbp);
                return XFS_ERROR(ENOSPC);
        }
        /*
@@ -466,14 +464,14 @@ xfs_dir2_leaf_addname(
                 */
                if ((error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE,
                                &use_block))) {
-                       xfs_da_brelse(tp, lbp);
+                       xfs_trans_brelse(tp, lbp);
                        return error;
                }
                /*
                 * Initialize the block.
                 */
                if ((error = xfs_dir2_data_init(args, use_block, &dbp))) {
-                       xfs_da_brelse(tp, lbp);
+                       xfs_trans_brelse(tp, lbp);
                        return error;
                }
                /*
@@ -493,7 +491,7 @@ xfs_dir2_leaf_addname(
                 */
                else
                        xfs_dir2_leaf_log_bests(tp, lbp, use_block, use_block);
-               hdr = dbp->data;
+               hdr = dbp->b_addr;
                bestsp[use_block] = hdr->bestfree[0].length;
                grown = 1;
        }
@@ -505,10 +503,10 @@ xfs_dir2_leaf_addname(
                if ((error =
                    xfs_da_read_buf(tp, dp, xfs_dir2_db_to_da(mp, use_block),
                            -1, &dbp, XFS_DATA_FORK))) {
-                       xfs_da_brelse(tp, lbp);
+                       xfs_trans_brelse(tp, lbp);
                        return error;
                }
-               hdr = dbp->data;
+               hdr = dbp->b_addr;
                grown = 0;
        }
        xfs_dir2_data_check(dp, dbp);
@@ -570,9 +568,7 @@ xfs_dir2_leaf_addname(
        xfs_dir2_leaf_log_header(tp, lbp);
        xfs_dir2_leaf_log_ents(tp, lbp, lfloglow, lfloghigh);
        xfs_dir2_leaf_check(dp, lbp);
-       xfs_da_buf_done(lbp);
        xfs_dir2_data_check(dp, dbp);
-       xfs_da_buf_done(dbp);
        return 0;
 }
 
@@ -583,8 +579,8 @@ xfs_dir2_leaf_addname(
  */
 STATIC void
 xfs_dir2_leaf_check(
-       xfs_inode_t             *dp,            /* incore directory inode */
-       xfs_dabuf_t             *bp)            /* leaf's buffer */
+       struct xfs_inode        *dp,            /* incore directory inode */
+       struct xfs_buf          *bp)            /* leaf's buffer */
 {
        int                     i;              /* leaf index */
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
@@ -592,7 +588,7 @@ xfs_dir2_leaf_check(
        xfs_mount_t             *mp;            /* filesystem mount point */
        int                     stale;          /* count of stale leaves */
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        mp = dp->i_mount;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAF1_MAGIC));
        /*
@@ -628,14 +624,14 @@ xfs_dir2_leaf_check(
 void
 xfs_dir2_leaf_compact(
        xfs_da_args_t   *args,          /* operation arguments */
-       xfs_dabuf_t     *bp)            /* leaf buffer */
+       struct xfs_buf  *bp)            /* leaf buffer */
 {
        int             from;           /* source leaf index */
        xfs_dir2_leaf_t *leaf;          /* leaf structure */
        int             loglow;         /* first leaf entry to log */
        int             to;             /* target leaf index */
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        if (!leaf->hdr.stale) {
                return;
        }
@@ -677,7 +673,7 @@ xfs_dir2_leaf_compact(
  */
 void
 xfs_dir2_leaf_compact_x1(
-       xfs_dabuf_t     *bp,            /* leaf buffer */
+       struct xfs_buf  *bp,            /* leaf buffer */
        int             *indexp,        /* insertion index */
        int             *lowstalep,     /* out: stale entry before us */
        int             *highstalep,    /* out: stale entry after us */
@@ -693,7 +689,7 @@ xfs_dir2_leaf_compact_x1(
        int             newindex=0;     /* new insertion index */
        int             to;             /* destination copy index */
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(be16_to_cpu(leaf->hdr.stale) > 1);
        index = *indexp;
 
@@ -763,6 +759,218 @@ xfs_dir2_leaf_compact_x1(
        *highstalep = highstale;
 }
 
+struct xfs_dir2_leaf_map_info {
+       xfs_extlen_t    map_blocks;     /* number of fsbs in map */
+       xfs_dablk_t     map_off;        /* last mapped file offset */
+       int             map_size;       /* total entries in *map */
+       int             map_valid;      /* valid entries in *map */
+       int             nmap;           /* mappings to ask xfs_bmapi */
+       xfs_dir2_db_t   curdb;          /* db for current block */
+       int             ra_current;     /* number of read-ahead blks */
+       int             ra_index;       /* *map index for read-ahead */
+       int             ra_offset;      /* map entry offset for ra */
+       int             ra_want;        /* readahead count wanted */
+       struct xfs_bmbt_irec map[];     /* map vector for blocks */
+};
+
+STATIC int
+xfs_dir2_leaf_readbuf(
+       struct xfs_inode        *dp,
+       size_t                  bufsize,
+       struct xfs_dir2_leaf_map_info *mip,
+       xfs_dir2_off_t          *curoff,
+       struct xfs_buf          **bpp)
+{
+       struct xfs_mount        *mp = dp->i_mount;
+       struct xfs_buf          *bp = *bpp;
+       struct xfs_bmbt_irec    *map = mip->map;
+       int                     error = 0;
+       int                     length;
+       int                     i;
+       int                     j;
+
+       /*
+        * If we have a buffer, we need to release it and
+        * take it out of the mapping.
+        */
+
+       if (bp) {
+               xfs_trans_brelse(NULL, bp);
+               bp = NULL;
+               mip->map_blocks -= mp->m_dirblkfsbs;
+               /*
+                * Loop to get rid of the extents for the
+                * directory block.
+                */
+               for (i = mp->m_dirblkfsbs; i > 0; ) {
+                       j = min_t(int, map->br_blockcount, i);
+                       map->br_blockcount -= j;
+                       map->br_startblock += j;
+                       map->br_startoff += j;
+                       /*
+                        * If mapping is done, pitch it from
+                        * the table.
+                        */
+                       if (!map->br_blockcount && --mip->map_valid)
+                               memmove(&map[0], &map[1],
+                                       sizeof(map[0]) * mip->map_valid);
+                       i -= j;
+               }
+       }
+
+       /*
+        * Recalculate the readahead blocks wanted.
+        */
+       mip->ra_want = howmany(bufsize + mp->m_dirblksize,
+                              mp->m_sb.sb_blocksize) - 1;
+       ASSERT(mip->ra_want >= 0);
+
+       /*
+        * If we don't have as many as we want, and we haven't
+        * run out of data blocks, get some more mappings.
+        */
+       if (1 + mip->ra_want > mip->map_blocks &&
+           mip->map_off < xfs_dir2_byte_to_da(mp, XFS_DIR2_LEAF_OFFSET)) {
+               /*
+                * Get more bmaps, fill in after the ones
+                * we already have in the table.
+                */
+               mip->nmap = mip->map_size - mip->map_valid;
+               error = xfs_bmapi_read(dp, mip->map_off,
+                               xfs_dir2_byte_to_da(mp, XFS_DIR2_LEAF_OFFSET) -
+                                                               mip->map_off,
+                               &map[mip->map_valid], &mip->nmap, 0);
+
+               /*
+                * Don't know if we should ignore this or try to return an
+                * error.  The trouble with returning errors is that readdir
+                * will just stop without actually passing the error through.
+                */
+               if (error)
+                       goto out;       /* XXX */
+
+               /*
+                * If we got all the mappings we asked for, set the final map
+                * offset based on the last bmap value received.  Otherwise,
+                * we've reached the end.
+                */
+               if (mip->nmap == mip->map_size - mip->map_valid) {
+                       i = mip->map_valid + mip->nmap - 1;
+                       mip->map_off = map[i].br_startoff + map[i].br_blockcount;
+               } else
+                       mip->map_off = xfs_dir2_byte_to_da(mp,
+                                                       XFS_DIR2_LEAF_OFFSET);
+
+               /*
+                * Look for holes in the mapping, and eliminate them.  Count up
+                * the valid blocks.
+                */
+               for (i = mip->map_valid; i < mip->map_valid + mip->nmap; ) {
+                       if (map[i].br_startblock == HOLESTARTBLOCK) {
+                               mip->nmap--;
+                               length = mip->map_valid + mip->nmap - i;
+                               if (length)
+                                       memmove(&map[i], &map[i + 1],
+                                               sizeof(map[i]) * length);
+                       } else {
+                               mip->map_blocks += map[i].br_blockcount;
+                               i++;
+                       }
+               }
+               mip->map_valid += mip->nmap;
+       }
+
+       /*
+        * No valid mappings, so no more data blocks.
+        */
+       if (!mip->map_valid) {
+               *curoff = xfs_dir2_da_to_byte(mp, mip->map_off);
+               goto out;
+       }
+
+       /*
+        * Read the directory block starting at the first mapping.
+        */
+       mip->curdb = xfs_dir2_da_to_db(mp, map->br_startoff);
+       error = xfs_da_read_buf(NULL, dp, map->br_startoff,
+                       map->br_blockcount >= mp->m_dirblkfsbs ?
+                           XFS_FSB_TO_DADDR(mp, map->br_startblock) : -1,
+                       &bp, XFS_DATA_FORK);
+
+       /*
+        * Should just skip over the data block instead of giving up.
+        */
+       if (error)
+               goto out;       /* XXX */
+
+       /*
+        * Adjust the current amount of read-ahead: we just read a block that
+        * was previously ra.
+        */
+       if (mip->ra_current)
+               mip->ra_current -= mp->m_dirblkfsbs;
+
+       /*
+        * Do we need more readahead?
+        */
+       for (mip->ra_index = mip->ra_offset = i = 0;
+            mip->ra_want > mip->ra_current && i < mip->map_blocks;
+            i += mp->m_dirblkfsbs) {
+               ASSERT(mip->ra_index < mip->map_valid);
+               /*
+                * Read-ahead a contiguous directory block.
+                */
+               if (i > mip->ra_current &&
+                   map[mip->ra_index].br_blockcount >= mp->m_dirblkfsbs) {
+                       xfs_buf_readahead(mp->m_ddev_targp,
+                               XFS_FSB_TO_DADDR(mp,
+                                       map[mip->ra_index].br_startblock +
+                                                       mip->ra_offset),
+                               (int)BTOBB(mp->m_dirblksize));
+                       mip->ra_current = i;
+               }
+
+               /*
+                * Read-ahead a non-contiguous directory block.  This doesn't
+                * use our mapping, but this is a very rare case.
+                */
+               else if (i > mip->ra_current) {
+                       xfs_da_reada_buf(NULL, dp,
+                                       map[mip->ra_index].br_startoff +
+                                                       mip->ra_offset,
+                                       XFS_DATA_FORK);
+                       mip->ra_current = i;
+               }
+
+               /*
+                * Advance offset through the mapping table.
+                */
+               for (j = 0; j < mp->m_dirblkfsbs; j++) {
+                       /*
+                        * The rest of this extent but not more than a dir
+                        * block.
+                        */
+                       length = min_t(int, mp->m_dirblkfsbs,
+                                       map[mip->ra_index].br_blockcount -
+                                                       mip->ra_offset);
+                       j += length;
+                       mip->ra_offset += length;
+
+                       /*
+                        * Advance to the next mapping if this one is used up.
+                        */
+                       if (mip->ra_offset == map[mip->ra_index].br_blockcount) {
+                               mip->ra_offset = 0;
+                               mip->ra_index++;
+                       }
+               }
+       }
+
+out:
+       *bpp = bp;
+       return error;
+}
+
 /*
  * Getdents (readdir) for leaf and node directories.
  * This reads the data blocks only, so is the same for both forms.
@@ -775,30 +983,18 @@ xfs_dir2_leaf_getdents(
        xfs_off_t               *offset,
        filldir_t               filldir)
 {
-       xfs_dabuf_t             *bp;            /* data block buffer */
-       int                     byteoff;        /* offset in current block */
-       xfs_dir2_db_t           curdb;          /* db for current block */
-       xfs_dir2_off_t          curoff;         /* current overall offset */
+       struct xfs_buf          *bp = NULL;     /* data block buffer */
        xfs_dir2_data_hdr_t     *hdr;           /* data block header */
        xfs_dir2_data_entry_t   *dep;           /* data entry */
        xfs_dir2_data_unused_t  *dup;           /* unused entry */
        int                     error = 0;      /* error return value */
-       int                     i;              /* temporary loop index */
-       int                     j;              /* temporary loop index */
        int                     length;         /* temporary length value */
-       xfs_bmbt_irec_t         *map;           /* map vector for blocks */
-       xfs_extlen_t            map_blocks;     /* number of fsbs in map */
-       xfs_dablk_t             map_off;        /* last mapped file offset */
-       int                     map_size;       /* total entries in *map */
-       int                     map_valid;      /* valid entries in *map */
        xfs_mount_t             *mp;            /* filesystem mount point */
+       int                     byteoff;        /* offset in current block */
+       xfs_dir2_off_t          curoff;         /* current overall offset */
        xfs_dir2_off_t          newoff;         /* new curoff after new blk */
-       int                     nmap;           /* mappings to ask xfs_bmapi */
        char                    *ptr = NULL;    /* pointer to current data */
-       int                     ra_current;     /* number of read-ahead blks */
-       int                     ra_index;       /* *map index for read-ahead */
-       int                     ra_offset;      /* map entry offset for ra */
-       int                     ra_want;        /* readahead count wanted */
+       struct xfs_dir2_leaf_map_info *map_info;
 
        /*
         * If the offset is at or past the largest allowed value,
@@ -814,10 +1010,12 @@ xfs_dir2_leaf_getdents(
         * buffer size, the directory block size, and the filesystem
         * block size.
         */
-       map_size = howmany(bufsize + mp->m_dirblksize, mp->m_sb.sb_blocksize);
-       map = kmem_alloc(map_size * sizeof(*map), KM_SLEEP);
-       map_valid = ra_index = ra_offset = ra_current = map_blocks = 0;
-       bp = NULL;
+       length = howmany(bufsize + mp->m_dirblksize,
+                                    mp->m_sb.sb_blocksize);
+       map_info = kmem_zalloc(offsetof(struct xfs_dir2_leaf_map_info, map) +
+                               (length * sizeof(struct xfs_bmbt_irec)),
+                              KM_SLEEP);
+       map_info->map_size = length;
 
        /*
         * Inside the loop we keep the main offset value as a byte offset
@@ -829,7 +1027,9 @@ xfs_dir2_leaf_getdents(
         * Force this conversion through db so we truncate the offset
         * down to get the start of the data block.
         */
-       map_off = xfs_dir2_db_to_da(mp, xfs_dir2_byte_to_db(mp, curoff));
+       map_info->map_off = xfs_dir2_db_to_da(mp,
+                                             xfs_dir2_byte_to_db(mp, curoff));
+
        /*
         * Loop over directory entries until we reach the end offset.
         * Get more blocks and readahead as necessary.
@@ -839,191 +1039,17 @@ xfs_dir2_leaf_getdents(
                 * If we have no buffer, or we're off the end of the
                 * current buffer, need to get another one.
                 */
-               if (!bp || ptr >= (char *)bp->data + mp->m_dirblksize) {
-                       /*
-                        * If we have a buffer, we need to release it and
-                        * take it out of the mapping.
-                        */
-                       if (bp) {
-                               xfs_da_brelse(NULL, bp);
-                               bp = NULL;
-                               map_blocks -= mp->m_dirblkfsbs;
-                               /*
-                                * Loop to get rid of the extents for the
-                                * directory block.
-                                */
-                               for (i = mp->m_dirblkfsbs; i > 0; ) {
-                                       j = MIN((int)map->br_blockcount, i);
-                                       map->br_blockcount -= j;
-                                       map->br_startblock += j;
-                                       map->br_startoff += j;
-                                       /*
-                                        * If mapping is done, pitch it from
-                                        * the table.
-                                        */
-                                       if (!map->br_blockcount && --map_valid)
-                                               memmove(&map[0], &map[1],
-                                                       sizeof(map[0]) *
-                                                       map_valid);
-                                       i -= j;
-                               }
-                       }
-                       /*
-                        * Recalculate the readahead blocks wanted.
-                        */
-                       ra_want = howmany(bufsize + mp->m_dirblksize,
-                                         mp->m_sb.sb_blocksize) - 1;
-                       ASSERT(ra_want >= 0);
+               if (!bp || ptr >= (char *)bp->b_addr + mp->m_dirblksize) {
 
-                       /*
-                        * If we don't have as many as we want, and we haven't
-                        * run out of data blocks, get some more mappings.
-                        */
-                       if (1 + ra_want > map_blocks &&
-                           map_off <
-                           xfs_dir2_byte_to_da(mp, XFS_DIR2_LEAF_OFFSET)) {
-                               /*
-                                * Get more bmaps, fill in after the ones
-                                * we already have in the table.
-                                */
-                               nmap = map_size - map_valid;
-                               error = xfs_bmapi_read(dp, map_off,
-                                       xfs_dir2_byte_to_da(mp,
-                                               XFS_DIR2_LEAF_OFFSET) - map_off,
-                                       &map[map_valid], &nmap, 0);
-                               /*
-                                * Don't know if we should ignore this or
-                                * try to return an error.
-                                * The trouble with returning errors
-                                * is that readdir will just stop without
-                                * actually passing the error through.
-                                */
-                               if (error)
-                                       break;  /* XXX */
-                               /*
-                                * If we got all the mappings we asked for,
-                                * set the final map offset based on the
-                                * last bmap value received.
-                                * Otherwise, we've reached the end.
-                                */
-                               if (nmap == map_size - map_valid)
-                                       map_off =
-                                       map[map_valid + nmap - 1].br_startoff +
-                                       map[map_valid + nmap - 1].br_blockcount;
-                               else
-                                       map_off =
-                                               xfs_dir2_byte_to_da(mp,
-                                                       XFS_DIR2_LEAF_OFFSET);
-                               /*
-                                * Look for holes in the mapping, and
-                                * eliminate them.  Count up the valid blocks.
-                                */
-                               for (i = map_valid; i < map_valid + nmap; ) {
-                                       if (map[i].br_startblock ==
-                                           HOLESTARTBLOCK) {
-                                               nmap--;
-                                               length = map_valid + nmap - i;
-                                               if (length)
-                                                       memmove(&map[i],
-                                                               &map[i + 1],
-                                                               sizeof(map[i]) *
-                                                               length);
-                                       } else {
-                                               map_blocks +=
-                                                       map[i].br_blockcount;
-                                               i++;
-                                       }
-                               }
-                               map_valid += nmap;
-                       }
-                       /*
-                        * No valid mappings, so no more data blocks.
-                        */
-                       if (!map_valid) {
-                               curoff = xfs_dir2_da_to_byte(mp, map_off);
+                       error = xfs_dir2_leaf_readbuf(dp, bufsize, map_info,
+                                                     &curoff, &bp);
+                       if (error || !map_info->map_valid)
                                break;
-                       }
-                       /*
-                        * Read the directory block starting at the first
-                        * mapping.
-                        */
-                       curdb = xfs_dir2_da_to_db(mp, map->br_startoff);
-                       error = xfs_da_read_buf(NULL, dp, map->br_startoff,
-                               map->br_blockcount >= mp->m_dirblkfsbs ?
-                                   XFS_FSB_TO_DADDR(mp, map->br_startblock) :
-                                   -1,
-                               &bp, XFS_DATA_FORK);
-                       /*
-                        * Should just skip over the data block instead
-                        * of giving up.
-                        */
-                       if (error)
-                               break;  /* XXX */
-                       /*
-                        * Adjust the current amount of read-ahead: we just
-                        * read a block that was previously ra.
-                        */
-                       if (ra_current)
-                               ra_current -= mp->m_dirblkfsbs;
-                       /*
-                        * Do we need more readahead?
-                        */
-                       for (ra_index = ra_offset = i = 0;
-                            ra_want > ra_current && i < map_blocks;
-                            i += mp->m_dirblkfsbs) {
-                               ASSERT(ra_index < map_valid);
-                               /*
-                                * Read-ahead a contiguous directory block.
-                                */
-                               if (i > ra_current &&
-                                   map[ra_index].br_blockcount >=
-                                   mp->m_dirblkfsbs) {
-                                       xfs_buf_readahead(mp->m_ddev_targp,
-                                               XFS_FSB_TO_DADDR(mp,
-                                                  map[ra_index].br_startblock +
-                                                  ra_offset),
-                                               (int)BTOBB(mp->m_dirblksize));
-                                       ra_current = i;
-                               }
-                               /*
-                                * Read-ahead a non-contiguous directory block.
-                                * This doesn't use our mapping, but this
-                                * is a very rare case.
-                                */
-                               else if (i > ra_current) {
-                                       (void)xfs_da_reada_buf(NULL, dp,
-                                               map[ra_index].br_startoff +
-                                               ra_offset, XFS_DATA_FORK);
-                                       ra_current = i;
-                               }
-                               /*
-                                * Advance offset through the mapping table.
-                                */
-                               for (j = 0; j < mp->m_dirblkfsbs; j++) {
-                                       /*
-                                        * The rest of this extent but not
-                                        * more than a dir block.
-                                        */
-                                       length = MIN(mp->m_dirblkfsbs,
-                                               (int)(map[ra_index].br_blockcount -
-                                               ra_offset));
-                                       j += length;
-                                       ra_offset += length;
-                                       /*
-                                        * Advance to the next mapping if
-                                        * this one is used up.
-                                        */
-                                       if (ra_offset ==
-                                           map[ra_index].br_blockcount) {
-                                               ra_offset = 0;
-                                               ra_index++;
-                                       }
-                               }
-                       }
+
                        /*
                         * Having done a read, we need to set a new offset.
                         */
-                       newoff = xfs_dir2_db_off_to_byte(mp, curdb, 0);
+                       newoff = xfs_dir2_db_off_to_byte(mp, map_info->curdb, 0);
                        /*
                         * Start of the current block.
                         */
@@ -1034,8 +1060,8 @@ xfs_dir2_leaf_getdents(
                         */
                        else if (curoff > newoff)
                                ASSERT(xfs_dir2_byte_to_db(mp, curoff) ==
-                                      curdb);
-                       hdr = bp->data;
+                                      map_info->curdb);
+                       hdr = bp->b_addr;
                        xfs_dir2_data_check(dp, bp);
                        /*
                         * Find our position in the block.
@@ -1117,9 +1143,9 @@ xfs_dir2_leaf_getdents(
                *offset = XFS_DIR2_MAX_DATAPTR & 0x7fffffff;
        else
                *offset = xfs_dir2_byte_to_dataptr(mp, curoff) & 0x7fffffff;
-       kmem_free(map);
+       kmem_free(map_info);
        if (bp)
-               xfs_da_brelse(NULL, bp);
+               xfs_trans_brelse(NULL, bp);
        return error;
 }
 
@@ -1130,10 +1156,10 @@ int
 xfs_dir2_leaf_init(
        xfs_da_args_t           *args,          /* operation arguments */
        xfs_dir2_db_t           bno,            /* directory block number */
-       xfs_dabuf_t             **bpp,          /* out: leaf buffer */
+       struct xfs_buf          **bpp,          /* out: leaf buffer */
        int                     magic)          /* magic number for block */
 {
-       xfs_dabuf_t             *bp;            /* leaf buffer */
+       struct xfs_buf          *bp;            /* leaf buffer */
        xfs_inode_t             *dp;            /* incore directory inode */
        int                     error;          /* error return code */
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
@@ -1156,7 +1182,7 @@ xfs_dir2_leaf_init(
                return error;
        }
        ASSERT(bp != NULL);
-       leaf = bp->data;
+       leaf = bp->b_addr;
        /*
         * Initialize the header.
         */
@@ -1186,7 +1212,7 @@ xfs_dir2_leaf_init(
 static void
 xfs_dir2_leaf_log_bests(
        xfs_trans_t             *tp,            /* transaction pointer */
-       xfs_dabuf_t             *bp,            /* leaf buffer */
+       struct xfs_buf          *bp,            /* leaf buffer */
        int                     first,          /* first entry to log */
        int                     last)           /* last entry to log */
 {
@@ -1195,12 +1221,12 @@ xfs_dir2_leaf_log_bests(
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
        xfs_dir2_leaf_tail_t    *ltp;           /* leaf tail structure */
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAF1_MAGIC));
        ltp = xfs_dir2_leaf_tail_p(tp->t_mountp, leaf);
        firstb = xfs_dir2_leaf_bests_p(ltp) + first;
        lastb = xfs_dir2_leaf_bests_p(ltp) + last;
-       xfs_da_log_buf(tp, bp, (uint)((char *)firstb - (char *)leaf),
+       xfs_trans_log_buf(tp, bp, (uint)((char *)firstb - (char *)leaf),
                (uint)((char *)lastb - (char *)leaf + sizeof(*lastb) - 1));
 }
 
@@ -1210,7 +1236,7 @@ xfs_dir2_leaf_log_bests(
 void
 xfs_dir2_leaf_log_ents(
        xfs_trans_t             *tp,            /* transaction pointer */
-       xfs_dabuf_t             *bp,            /* leaf buffer */
+       struct xfs_buf          *bp,            /* leaf buffer */
        int                     first,          /* first entry to log */
        int                     last)           /* last entry to log */
 {
@@ -1218,12 +1244,12 @@ xfs_dir2_leaf_log_ents(
        xfs_dir2_leaf_entry_t   *lastlep;       /* pointer to last entry */
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAF1_MAGIC) ||
               leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
        firstlep = &leaf->ents[first];
        lastlep = &leaf->ents[last];
-       xfs_da_log_buf(tp, bp, (uint)((char *)firstlep - (char *)leaf),
+       xfs_trans_log_buf(tp, bp, (uint)((char *)firstlep - (char *)leaf),
                (uint)((char *)lastlep - (char *)leaf + sizeof(*lastlep) - 1));
 }
 
@@ -1232,15 +1258,15 @@ xfs_dir2_leaf_log_ents(
  */
 void
 xfs_dir2_leaf_log_header(
-       xfs_trans_t             *tp,            /* transaction pointer */
-       xfs_dabuf_t             *bp)            /* leaf buffer */
+       struct xfs_trans        *tp,
+       struct xfs_buf          *bp)
 {
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAF1_MAGIC) ||
               leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
-       xfs_da_log_buf(tp, bp, (uint)((char *)&leaf->hdr - (char *)leaf),
+       xfs_trans_log_buf(tp, bp, (uint)((char *)&leaf->hdr - (char *)leaf),
                (uint)(sizeof(leaf->hdr) - 1));
 }
 
@@ -1249,18 +1275,18 @@ xfs_dir2_leaf_log_header(
  */
 STATIC void
 xfs_dir2_leaf_log_tail(
-       xfs_trans_t             *tp,            /* transaction pointer */
-       xfs_dabuf_t             *bp)            /* leaf buffer */
+       struct xfs_trans        *tp,
+       struct xfs_buf          *bp)
 {
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
        xfs_dir2_leaf_tail_t    *ltp;           /* leaf tail structure */
        xfs_mount_t             *mp;            /* filesystem mount point */
 
        mp = tp->t_mountp;
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAF1_MAGIC));
        ltp = xfs_dir2_leaf_tail_p(mp, leaf);
-       xfs_da_log_buf(tp, bp, (uint)((char *)ltp - (char *)leaf),
+       xfs_trans_log_buf(tp, bp, (uint)((char *)ltp - (char *)leaf),
                (uint)(mp->m_dirblksize - 1));
 }
 
@@ -1273,12 +1299,12 @@ int
 xfs_dir2_leaf_lookup(
        xfs_da_args_t           *args)          /* operation arguments */
 {
-       xfs_dabuf_t             *dbp;           /* data block buffer */
+       struct xfs_buf          *dbp;           /* data block buffer */
        xfs_dir2_data_entry_t   *dep;           /* data block entry */
        xfs_inode_t             *dp;            /* incore directory inode */
        int                     error;          /* error return code */
        int                     index;          /* found entry index */
-       xfs_dabuf_t             *lbp;           /* leaf buffer */
+       struct xfs_buf          *lbp;           /* leaf buffer */
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
        xfs_dir2_leaf_entry_t   *lep;           /* leaf entry */
        xfs_trans_t             *tp;            /* transaction pointer */
@@ -1294,7 +1320,7 @@ xfs_dir2_leaf_lookup(
        tp = args->trans;
        dp = args->dp;
        xfs_dir2_leaf_check(dp, lbp);
-       leaf = lbp->data;
+       leaf = lbp->b_addr;
        /*
         * Get to the leaf entry and contained data entry address.
         */
@@ -1303,15 +1329,15 @@ xfs_dir2_leaf_lookup(
         * Point to the data entry.
         */
        dep = (xfs_dir2_data_entry_t *)
-             ((char *)dbp->data +
+             ((char *)dbp->b_addr +
               xfs_dir2_dataptr_to_off(dp->i_mount, be32_to_cpu(lep->address)));
        /*
         * Return the found inode number & CI name if appropriate
         */
        args->inumber = be64_to_cpu(dep->inumber);
        error = xfs_dir_cilookup_result(args, dep->name, dep->namelen);
-       xfs_da_brelse(tp, dbp);
-       xfs_da_brelse(tp, lbp);
+       xfs_trans_brelse(tp, dbp);
+       xfs_trans_brelse(tp, lbp);
        return XFS_ERROR(error);
 }
 
@@ -1324,17 +1350,17 @@ xfs_dir2_leaf_lookup(
 static int                                     /* error */
 xfs_dir2_leaf_lookup_int(
        xfs_da_args_t           *args,          /* operation arguments */
-       xfs_dabuf_t             **lbpp,         /* out: leaf buffer */
+       struct xfs_buf          **lbpp,         /* out: leaf buffer */
        int                     *indexp,        /* out: index in leaf block */
-       xfs_dabuf_t             **dbpp)         /* out: data buffer */
+       struct xfs_buf          **dbpp)         /* out: data buffer */
 {
        xfs_dir2_db_t           curdb = -1;     /* current data block number */
-       xfs_dabuf_t             *dbp = NULL;    /* data buffer */
+       struct xfs_buf          *dbp = NULL;    /* data buffer */
        xfs_dir2_data_entry_t   *dep;           /* data entry */
        xfs_inode_t             *dp;            /* incore directory inode */
        int                     error;          /* error return code */
        int                     index;          /* index in leaf block */
-       xfs_dabuf_t             *lbp;           /* leaf buffer */
+       struct xfs_buf          *lbp;           /* leaf buffer */
        xfs_dir2_leaf_entry_t   *lep;           /* leaf entry */
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
        xfs_mount_t             *mp;            /* filesystem mount point */
@@ -1354,7 +1380,7 @@ xfs_dir2_leaf_lookup_int(
        if (error)
                return error;
        *lbpp = lbp;
-       leaf = lbp->data;
+       leaf = lbp->b_addr;
        xfs_dir2_leaf_check(dp, lbp);
        /*
         * Look for the first leaf entry with our hash value.
@@ -1382,12 +1408,12 @@ xfs_dir2_leaf_lookup_int(
                 */
                if (newdb != curdb) {
                        if (dbp)
-                               xfs_da_brelse(tp, dbp);
+                               xfs_trans_brelse(tp, dbp);
                        error = xfs_da_read_buf(tp, dp,
                                                xfs_dir2_db_to_da(mp, newdb),
                                                -1, &dbp, XFS_DATA_FORK);
                        if (error) {
-                               xfs_da_brelse(tp, lbp);
+                               xfs_trans_brelse(tp, lbp);
                                return error;
                        }
                        xfs_dir2_data_check(dp, dbp);
@@ -1396,7 +1422,7 @@ xfs_dir2_leaf_lookup_int(
                /*
                 * Point to the data entry.
                 */
-               dep = (xfs_dir2_data_entry_t *)((char *)dbp->data +
+               dep = (xfs_dir2_data_entry_t *)((char *)dbp->b_addr +
                        xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
                /*
                 * Compare name and if it's an exact match, return the index
@@ -1424,12 +1450,12 @@ xfs_dir2_leaf_lookup_int(
        if (args->cmpresult == XFS_CMP_CASE) {
                ASSERT(cidb != -1);
                if (cidb != curdb) {
-                       xfs_da_brelse(tp, dbp);
+                       xfs_trans_brelse(tp, dbp);
                        error = xfs_da_read_buf(tp, dp,
                                                xfs_dir2_db_to_da(mp, cidb),
                                                -1, &dbp, XFS_DATA_FORK);
                        if (error) {
-                               xfs_da_brelse(tp, lbp);
+                               xfs_trans_brelse(tp, lbp);
                                return error;
                        }
                }
@@ -1441,8 +1467,8 @@ xfs_dir2_leaf_lookup_int(
         */
        ASSERT(cidb == -1);
        if (dbp)
-               xfs_da_brelse(tp, dbp);
-       xfs_da_brelse(tp, lbp);
+               xfs_trans_brelse(tp, dbp);
+       xfs_trans_brelse(tp, lbp);
        return XFS_ERROR(ENOENT);
 }
 
@@ -1456,13 +1482,13 @@ xfs_dir2_leaf_removename(
        __be16                  *bestsp;        /* leaf block best freespace */
        xfs_dir2_data_hdr_t     *hdr;           /* data block header */
        xfs_dir2_db_t           db;             /* data block number */
-       xfs_dabuf_t             *dbp;           /* data block buffer */
+       struct xfs_buf          *dbp;           /* data block buffer */
        xfs_dir2_data_entry_t   *dep;           /* data entry structure */
        xfs_inode_t             *dp;            /* incore directory inode */
        int                     error;          /* error return code */
        xfs_dir2_db_t           i;              /* temporary data block # */
        int                     index;          /* index into leaf entries */
-       xfs_dabuf_t             *lbp;           /* leaf buffer */
+       struct xfs_buf          *lbp;           /* leaf buffer */
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
        xfs_dir2_leaf_entry_t   *lep;           /* leaf entry */
        xfs_dir2_leaf_tail_t    *ltp;           /* leaf tail structure */
@@ -1483,8 +1509,8 @@ xfs_dir2_leaf_removename(
        dp = args->dp;
        tp = args->trans;
        mp = dp->i_mount;
-       leaf = lbp->data;
-       hdr = dbp->data;
+       leaf = lbp->b_addr;
+       hdr = dbp->b_addr;
        xfs_dir2_data_check(dp, dbp);
        /*
         * Point to the leaf entry, use that to point to the data entry.
@@ -1541,12 +1567,9 @@ xfs_dir2_leaf_removename(
                         * Just go on, returning success, leaving the
                         * empty block in place.
                         */
-                       if (error == ENOSPC && args->total == 0) {
-                               xfs_da_buf_done(dbp);
+                       if (error == ENOSPC && args->total == 0)
                                error = 0;
-                       }
                        xfs_dir2_leaf_check(dp, lbp);
-                       xfs_da_buf_done(lbp);
                        return error;
                }
                dbp = NULL;
@@ -1577,10 +1600,9 @@ xfs_dir2_leaf_removename(
        /*
         * If the data block was not the first one, drop it.
         */
-       else if (db != mp->m_dirdatablk && dbp != NULL) {
-               xfs_da_buf_done(dbp);
+       else if (db != mp->m_dirdatablk)
                dbp = NULL;
-       }
+
        xfs_dir2_leaf_check(dp, lbp);
        /*
         * See if we can convert to block form.
@@ -1595,12 +1617,12 @@ int                                             /* error */
 xfs_dir2_leaf_replace(
        xfs_da_args_t           *args)          /* operation arguments */
 {
-       xfs_dabuf_t             *dbp;           /* data block buffer */
+       struct xfs_buf          *dbp;           /* data block buffer */
        xfs_dir2_data_entry_t   *dep;           /* data block entry */
        xfs_inode_t             *dp;            /* incore directory inode */
        int                     error;          /* error return code */
        int                     index;          /* index of leaf entry */
-       xfs_dabuf_t             *lbp;           /* leaf buffer */
+       struct xfs_buf          *lbp;           /* leaf buffer */
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
        xfs_dir2_leaf_entry_t   *lep;           /* leaf entry */
        xfs_trans_t             *tp;            /* transaction pointer */
@@ -1614,7 +1636,7 @@ xfs_dir2_leaf_replace(
                return error;
        }
        dp = args->dp;
-       leaf = lbp->data;
+       leaf = lbp->b_addr;
        /*
         * Point to the leaf entry, get data address from it.
         */
@@ -1623,7 +1645,7 @@ xfs_dir2_leaf_replace(
         * Point to the data entry.
         */
        dep = (xfs_dir2_data_entry_t *)
-             ((char *)dbp->data +
+             ((char *)dbp->b_addr +
               xfs_dir2_dataptr_to_off(dp->i_mount, be32_to_cpu(lep->address)));
        ASSERT(args->inumber != be64_to_cpu(dep->inumber));
        /*
@@ -1632,9 +1654,8 @@ xfs_dir2_leaf_replace(
        dep->inumber = cpu_to_be64(args->inumber);
        tp = args->trans;
        xfs_dir2_data_log_entry(tp, dbp, dep);
-       xfs_da_buf_done(dbp);
        xfs_dir2_leaf_check(dp, lbp);
-       xfs_da_brelse(tp, lbp);
+       xfs_trans_brelse(tp, lbp);
        return 0;
 }
 
@@ -1646,7 +1667,7 @@ xfs_dir2_leaf_replace(
 int                                            /* index value */
 xfs_dir2_leaf_search_hash(
        xfs_da_args_t           *args,          /* operation arguments */
-       xfs_dabuf_t             *lbp)           /* leaf buffer */
+       struct xfs_buf          *lbp)           /* leaf buffer */
 {
        xfs_dahash_t            hash=0;         /* hash from this entry */
        xfs_dahash_t            hashwant;       /* hash value looking for */
@@ -1656,7 +1677,7 @@ xfs_dir2_leaf_search_hash(
        xfs_dir2_leaf_entry_t   *lep;           /* leaf entry */
        int                     mid=0;          /* current leaf index */
 
-       leaf = lbp->data;
+       leaf = lbp->b_addr;
 #ifndef __KERNEL__
        if (!leaf->hdr.count)
                return 0;
@@ -1699,11 +1720,11 @@ xfs_dir2_leaf_search_hash(
 int                                            /* error */
 xfs_dir2_leaf_trim_data(
        xfs_da_args_t           *args,          /* operation arguments */
-       xfs_dabuf_t             *lbp,           /* leaf buffer */
+       struct xfs_buf          *lbp,           /* leaf buffer */
        xfs_dir2_db_t           db)             /* data block number */
 {
        __be16                  *bestsp;        /* leaf bests table */
-       xfs_dabuf_t             *dbp;           /* data block buffer */
+       struct xfs_buf          *dbp;           /* data block buffer */
        xfs_inode_t             *dp;            /* incore directory inode */
        int                     error;          /* error return value */
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
@@ -1722,12 +1743,12 @@ xfs_dir2_leaf_trim_data(
                return error;
        }
 
-       leaf = lbp->data;
+       leaf = lbp->b_addr;
        ltp = xfs_dir2_leaf_tail_p(mp, leaf);
 
 #ifdef DEBUG
 {
-       struct xfs_dir2_data_hdr *hdr = dbp->data;
+       struct xfs_dir2_data_hdr *hdr = dbp->b_addr;
 
        ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC));
        ASSERT(be16_to_cpu(hdr->bestfree[0].length) ==
@@ -1741,7 +1762,7 @@ xfs_dir2_leaf_trim_data(
         */
        if ((error = xfs_dir2_shrink_inode(args, db, dbp))) {
                ASSERT(error != ENOSPC);
-               xfs_da_brelse(tp, dbp);
+               xfs_trans_brelse(tp, dbp);
                return error;
        }
        /*
@@ -1781,10 +1802,10 @@ xfs_dir2_node_to_leaf(
        xfs_da_args_t           *args;          /* operation arguments */
        xfs_inode_t             *dp;            /* incore directory inode */
        int                     error;          /* error return code */
-       xfs_dabuf_t             *fbp;           /* buffer for freespace block */
+       struct xfs_buf          *fbp;           /* buffer for freespace block */
        xfs_fileoff_t           fo;             /* freespace file offset */
        xfs_dir2_free_t         *free;          /* freespace structure */
-       xfs_dabuf_t             *lbp;           /* buffer for leaf block */
+       struct xfs_buf          *lbp;           /* buffer for leaf block */
        xfs_dir2_leaf_tail_t    *ltp;           /* tail of leaf structure */
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
        xfs_mount_t             *mp;            /* filesystem mount point */
@@ -1838,7 +1859,7 @@ xfs_dir2_node_to_leaf(
        if (XFS_FSB_TO_B(mp, fo) > XFS_DIR2_LEAF_OFFSET + mp->m_dirblksize)
                return 0;
        lbp = state->path.blk[0].bp;
-       leaf = lbp->data;
+       leaf = lbp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
        /*
         * Read the freespace block.
@@ -1847,7 +1868,7 @@ xfs_dir2_node_to_leaf(
                        XFS_DATA_FORK))) {
                return error;
        }
-       free = fbp->data;
+       free = fbp->b_addr;
        ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
        ASSERT(!free->hdr.firstdb);
 
@@ -1857,7 +1878,7 @@ xfs_dir2_node_to_leaf(
         */
        if (xfs_dir2_leaf_size(&leaf->hdr, be32_to_cpu(free->hdr.nvalid)) >
                        mp->m_dirblksize) {
-               xfs_da_brelse(tp, fbp);
+               xfs_trans_brelse(tp, fbp);
                return 0;
        }
 
index b0f26780449d5ebabca4d4b3b448a40728615e56..6c70524066051381f823d1043b857b5421a6490e 100644 (file)
 /*
  * Function declarations.
  */
-static void xfs_dir2_free_log_header(xfs_trans_t *tp, xfs_dabuf_t *bp);
-static int xfs_dir2_leafn_add(xfs_dabuf_t *bp, xfs_da_args_t *args, int index);
+static int xfs_dir2_leafn_add(struct xfs_buf *bp, xfs_da_args_t *args,
+                             int index);
 #ifdef DEBUG
-static void xfs_dir2_leafn_check(xfs_inode_t *dp, xfs_dabuf_t *bp);
+static void xfs_dir2_leafn_check(struct xfs_inode *dp, struct xfs_buf *bp);
 #else
 #define        xfs_dir2_leafn_check(dp, bp)
 #endif
-static void xfs_dir2_leafn_moveents(xfs_da_args_t *args, xfs_dabuf_t *bp_s,
-                                   int start_s, xfs_dabuf_t *bp_d, int start_d,
-                                   int count);
+static void xfs_dir2_leafn_moveents(xfs_da_args_t *args, struct xfs_buf *bp_s,
+                                   int start_s, struct xfs_buf *bp_d,
+                                   int start_d, int count);
 static void xfs_dir2_leafn_rebalance(xfs_da_state_t *state,
                                     xfs_da_state_blk_t *blk1,
                                     xfs_da_state_blk_t *blk2);
-static int xfs_dir2_leafn_remove(xfs_da_args_t *args, xfs_dabuf_t *bp,
+static int xfs_dir2_leafn_remove(xfs_da_args_t *args, struct xfs_buf *bp,
                                 int index, xfs_da_state_blk_t *dblk,
                                 int *rval);
 static int xfs_dir2_node_addname_int(xfs_da_args_t *args,
@@ -60,16 +60,16 @@ static int xfs_dir2_node_addname_int(xfs_da_args_t *args,
  */
 STATIC void
 xfs_dir2_free_log_bests(
-       xfs_trans_t             *tp,            /* transaction pointer */
-       xfs_dabuf_t             *bp,            /* freespace buffer */
+       struct xfs_trans        *tp,
+       struct xfs_buf          *bp,
        int                     first,          /* first entry to log */
        int                     last)           /* last entry to log */
 {
        xfs_dir2_free_t         *free;          /* freespace structure */
 
-       free = bp->data;
+       free = bp->b_addr;
        ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
-       xfs_da_log_buf(tp, bp,
+       xfs_trans_log_buf(tp, bp,
                (uint)((char *)&free->bests[first] - (char *)free),
                (uint)((char *)&free->bests[last] - (char *)free +
                       sizeof(free->bests[0]) - 1));
@@ -80,14 +80,14 @@ xfs_dir2_free_log_bests(
  */
 static void
 xfs_dir2_free_log_header(
-       xfs_trans_t             *tp,            /* transaction pointer */
-       xfs_dabuf_t             *bp)            /* freespace buffer */
+       struct xfs_trans        *tp,
+       struct xfs_buf          *bp)
 {
        xfs_dir2_free_t         *free;          /* freespace structure */
 
-       free = bp->data;
+       free = bp->b_addr;
        ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
-       xfs_da_log_buf(tp, bp, (uint)((char *)&free->hdr - (char *)free),
+       xfs_trans_log_buf(tp, bp, (uint)((char *)&free->hdr - (char *)free),
                (uint)(sizeof(xfs_dir2_free_hdr_t) - 1));
 }
 
@@ -99,11 +99,11 @@ xfs_dir2_free_log_header(
 int                                            /* error */
 xfs_dir2_leaf_to_node(
        xfs_da_args_t           *args,          /* operation arguments */
-       xfs_dabuf_t             *lbp)           /* leaf buffer */
+       struct xfs_buf          *lbp)           /* leaf buffer */
 {
        xfs_inode_t             *dp;            /* incore directory inode */
        int                     error;          /* error return value */
-       xfs_dabuf_t             *fbp;           /* freespace buffer */
+       struct xfs_buf          *fbp;           /* freespace buffer */
        xfs_dir2_db_t           fdb;            /* freespace block number */
        xfs_dir2_free_t         *free;          /* freespace structure */
        __be16                  *from;          /* pointer to freespace entry */
@@ -136,8 +136,8 @@ xfs_dir2_leaf_to_node(
                return error;
        }
        ASSERT(fbp != NULL);
-       free = fbp->data;
-       leaf = lbp->data;
+       free = fbp->b_addr;
+       leaf = lbp->b_addr;
        ltp = xfs_dir2_leaf_tail_p(mp, leaf);
        /*
         * Initialize the freespace block header.
@@ -164,7 +164,6 @@ xfs_dir2_leaf_to_node(
        xfs_dir2_leaf_log_header(tp, lbp);
        xfs_dir2_free_log_header(tp, fbp);
        xfs_dir2_free_log_bests(tp, fbp, 0, be32_to_cpu(free->hdr.nvalid) - 1);
-       xfs_da_buf_done(fbp);
        xfs_dir2_leafn_check(dp, lbp);
        return 0;
 }
@@ -175,7 +174,7 @@ xfs_dir2_leaf_to_node(
  */
 static int                                     /* error */
 xfs_dir2_leafn_add(
-       xfs_dabuf_t             *bp,            /* leaf buffer */
+       struct xfs_buf          *bp,            /* leaf buffer */
        xfs_da_args_t           *args,          /* operation arguments */
        int                     index)          /* insertion pt for new entry */
 {
@@ -195,7 +194,7 @@ xfs_dir2_leafn_add(
        dp = args->dp;
        mp = dp->i_mount;
        tp = args->trans;
-       leaf = bp->data;
+       leaf = bp->b_addr;
 
        /*
         * Quick check just to make sure we are not going to index
@@ -261,15 +260,15 @@ xfs_dir2_leafn_add(
  */
 void
 xfs_dir2_leafn_check(
-       xfs_inode_t     *dp,                    /* incore directory inode */
-       xfs_dabuf_t     *bp)                    /* leaf buffer */
+       struct xfs_inode *dp,
+       struct xfs_buf  *bp)
 {
        int             i;                      /* leaf index */
        xfs_dir2_leaf_t *leaf;                  /* leaf structure */
        xfs_mount_t     *mp;                    /* filesystem mount point */
        int             stale;                  /* count of stale leaves */
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        mp = dp->i_mount;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
        ASSERT(be16_to_cpu(leaf->hdr.count) <= xfs_dir2_max_leaf_ents(mp));
@@ -291,12 +290,12 @@ xfs_dir2_leafn_check(
  */
 xfs_dahash_t                                   /* hash value */
 xfs_dir2_leafn_lasthash(
-       xfs_dabuf_t     *bp,                    /* leaf buffer */
+       struct xfs_buf  *bp,                    /* leaf buffer */
        int             *count)                 /* count of entries in leaf */
 {
        xfs_dir2_leaf_t *leaf;                  /* leaf structure */
 
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
        if (count)
                *count = be16_to_cpu(leaf->hdr.count);
@@ -311,12 +310,12 @@ xfs_dir2_leafn_lasthash(
  */
 STATIC int
 xfs_dir2_leafn_lookup_for_addname(
-       xfs_dabuf_t             *bp,            /* leaf buffer */
+       struct xfs_buf          *bp,            /* leaf buffer */
        xfs_da_args_t           *args,          /* operation arguments */
        int                     *indexp,        /* out: leaf entry index */
        xfs_da_state_t          *state)         /* state to fill in */
 {
-       xfs_dabuf_t             *curbp = NULL;  /* current data/free buffer */
+       struct xfs_buf          *curbp = NULL;  /* current data/free buffer */
        xfs_dir2_db_t           curdb = -1;     /* current data block number */
        xfs_dir2_db_t           curfdb = -1;    /* current free block number */
        xfs_inode_t             *dp;            /* incore directory inode */
@@ -335,7 +334,7 @@ xfs_dir2_leafn_lookup_for_addname(
        dp = args->dp;
        tp = args->trans;
        mp = dp->i_mount;
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
 #ifdef __KERNEL__
        ASSERT(be16_to_cpu(leaf->hdr.count) > 0);
@@ -352,7 +351,7 @@ xfs_dir2_leafn_lookup_for_addname(
                /* If so, it's a free block buffer, get the block number. */
                curbp = state->extrablk.bp;
                curfdb = state->extrablk.blkno;
-               free = curbp->data;
+               free = curbp->b_addr;
                ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
        }
        length = xfs_dir2_data_entsize(args->namelen);
@@ -394,7 +393,7 @@ xfs_dir2_leafn_lookup_for_addname(
                                 * If we had one before, drop it.
                                 */
                                if (curbp)
-                                       xfs_da_brelse(tp, curbp);
+                                       xfs_trans_brelse(tp, curbp);
                                /*
                                 * Read the free block.
                                 */
@@ -403,7 +402,7 @@ xfs_dir2_leafn_lookup_for_addname(
                                                -1, &curbp, XFS_DATA_FORK);
                                if (error)
                                        return error;
-                               free = curbp->data;
+                               free = curbp->b_addr;
                                ASSERT(be32_to_cpu(free->hdr.magic) ==
                                        XFS_DIR2_FREE_MAGIC);
                                ASSERT((be32_to_cpu(free->hdr.firstdb) %
@@ -424,7 +423,7 @@ xfs_dir2_leafn_lookup_for_addname(
                                XFS_ERROR_REPORT("xfs_dir2_leafn_lookup_int",
                                                        XFS_ERRLEVEL_LOW, mp);
                                if (curfdb != newfdb)
-                                       xfs_da_brelse(tp, curbp);
+                                       xfs_trans_brelse(tp, curbp);
                                return XFS_ERROR(EFSCORRUPTED);
                        }
                        curfdb = newfdb;
@@ -459,12 +458,12 @@ out:
  */
 STATIC int
 xfs_dir2_leafn_lookup_for_entry(
-       xfs_dabuf_t             *bp,            /* leaf buffer */
+       struct xfs_buf          *bp,            /* leaf buffer */
        xfs_da_args_t           *args,          /* operation arguments */
        int                     *indexp,        /* out: leaf entry index */
        xfs_da_state_t          *state)         /* state to fill in */
 {
-       xfs_dabuf_t             *curbp = NULL;  /* current data/free buffer */
+       struct xfs_buf          *curbp = NULL;  /* current data/free buffer */
        xfs_dir2_db_t           curdb = -1;     /* current data block number */
        xfs_dir2_data_entry_t   *dep;           /* data block entry */
        xfs_inode_t             *dp;            /* incore directory inode */
@@ -480,7 +479,7 @@ xfs_dir2_leafn_lookup_for_entry(
        dp = args->dp;
        tp = args->trans;
        mp = dp->i_mount;
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
 #ifdef __KERNEL__
        ASSERT(be16_to_cpu(leaf->hdr.count) > 0);
@@ -525,7 +524,7 @@ xfs_dir2_leafn_lookup_for_entry(
                         */
                        if (curbp && (args->cmpresult == XFS_CMP_DIFFERENT ||
                                                curdb != state->extrablk.blkno))
-                               xfs_da_brelse(tp, curbp);
+                               xfs_trans_brelse(tp, curbp);
                        /*
                         * If needing the block that is saved with a CI match,
                         * use it otherwise read in the new data block.
@@ -547,7 +546,7 @@ xfs_dir2_leafn_lookup_for_entry(
                /*
                 * Point to the data entry.
                 */
-               dep = (xfs_dir2_data_entry_t *)((char *)curbp->data +
+               dep = (xfs_dir2_data_entry_t *)((char *)curbp->b_addr +
                        xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
                /*
                 * Compare the entry and if it's an exact match, return
@@ -559,7 +558,7 @@ xfs_dir2_leafn_lookup_for_entry(
                        /* If there is a CI match block, drop it */
                        if (args->cmpresult != XFS_CMP_DIFFERENT &&
                                                curdb != state->extrablk.blkno)
-                               xfs_da_brelse(tp, state->extrablk.bp);
+                               xfs_trans_brelse(tp, state->extrablk.bp);
                        args->cmpresult = cmp;
                        args->inumber = be64_to_cpu(dep->inumber);
                        *indexp = index;
@@ -567,7 +566,7 @@ xfs_dir2_leafn_lookup_for_entry(
                        state->extrablk.bp = curbp;
                        state->extrablk.blkno = curdb;
                        state->extrablk.index = (int)((char *)dep -
-                                                       (char *)curbp->data);
+                                                       (char *)curbp->b_addr);
                        state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
                        if (cmp == XFS_CMP_EXACT)
                                return XFS_ERROR(EEXIST);
@@ -586,7 +585,7 @@ xfs_dir2_leafn_lookup_for_entry(
                } else {
                        /* If the curbp is not the CI match block, drop it */
                        if (state->extrablk.bp != curbp)
-                               xfs_da_brelse(tp, curbp);
+                               xfs_trans_brelse(tp, curbp);
                }
        } else {
                state->extravalid = 0;
@@ -602,7 +601,7 @@ xfs_dir2_leafn_lookup_for_entry(
  */
 int
 xfs_dir2_leafn_lookup_int(
-       xfs_dabuf_t             *bp,            /* leaf buffer */
+       struct xfs_buf          *bp,            /* leaf buffer */
        xfs_da_args_t           *args,          /* operation arguments */
        int                     *indexp,        /* out: leaf entry index */
        xfs_da_state_t          *state)         /* state to fill in */
@@ -620,9 +619,9 @@ xfs_dir2_leafn_lookup_int(
 static void
 xfs_dir2_leafn_moveents(
        xfs_da_args_t   *args,                  /* operation arguments */
-       xfs_dabuf_t     *bp_s,                  /* source leaf buffer */
+       struct xfs_buf  *bp_s,                  /* source leaf buffer */
        int             start_s,                /* source leaf index */
-       xfs_dabuf_t     *bp_d,                  /* destination leaf buffer */
+       struct xfs_buf  *bp_d,                  /* destination leaf buffer */
        int             start_d,                /* destination leaf index */
        int             count)                  /* count of leaves to copy */
 {
@@ -640,8 +639,8 @@ xfs_dir2_leafn_moveents(
                return;
        }
        tp = args->trans;
-       leaf_s = bp_s->data;
-       leaf_d = bp_d->data;
+       leaf_s = bp_s->b_addr;
+       leaf_d = bp_d->b_addr;
        /*
         * If the destination index is not the end of the current
         * destination leaf entries, open up a hole in the destination
@@ -702,14 +701,14 @@ xfs_dir2_leafn_moveents(
  */
 int                                            /* sort order */
 xfs_dir2_leafn_order(
-       xfs_dabuf_t     *leaf1_bp,              /* leaf1 buffer */
-       xfs_dabuf_t     *leaf2_bp)              /* leaf2 buffer */
+       struct xfs_buf  *leaf1_bp,              /* leaf1 buffer */
+       struct xfs_buf  *leaf2_bp)              /* leaf2 buffer */
 {
        xfs_dir2_leaf_t *leaf1;                 /* leaf1 structure */
        xfs_dir2_leaf_t *leaf2;                 /* leaf2 structure */
 
-       leaf1 = leaf1_bp->data;
-       leaf2 = leaf2_bp->data;
+       leaf1 = leaf1_bp->b_addr;
+       leaf2 = leaf2_bp->b_addr;
        ASSERT(leaf1->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
        ASSERT(leaf2->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
        if (be16_to_cpu(leaf1->hdr.count) > 0 &&
@@ -757,8 +756,8 @@ xfs_dir2_leafn_rebalance(
                blk1 = blk2;
                blk2 = tmp;
        }
-       leaf1 = blk1->bp->data;
-       leaf2 = blk2->bp->data;
+       leaf1 = blk1->bp->b_addr;
+       leaf2 = blk2->bp->b_addr;
        oldsum = be16_to_cpu(leaf1->hdr.count) + be16_to_cpu(leaf2->hdr.count);
 #ifdef DEBUG
        oldstale = be16_to_cpu(leaf1->hdr.stale) + be16_to_cpu(leaf2->hdr.stale);
@@ -834,14 +833,14 @@ xfs_dir2_leafn_rebalance(
 static int                                     /* error */
 xfs_dir2_leafn_remove(
        xfs_da_args_t           *args,          /* operation arguments */
-       xfs_dabuf_t             *bp,            /* leaf buffer */
+       struct xfs_buf          *bp,            /* leaf buffer */
        int                     index,          /* leaf entry index */
        xfs_da_state_blk_t      *dblk,          /* data block */
        int                     *rval)          /* resulting block needs join */
 {
        xfs_dir2_data_hdr_t     *hdr;           /* data block header */
        xfs_dir2_db_t           db;             /* data block number */
-       xfs_dabuf_t             *dbp;           /* data block buffer */
+       struct xfs_buf          *dbp;           /* data block buffer */
        xfs_dir2_data_entry_t   *dep;           /* data block entry */
        xfs_inode_t             *dp;            /* incore directory inode */
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
@@ -858,7 +857,7 @@ xfs_dir2_leafn_remove(
        dp = args->dp;
        tp = args->trans;
        mp = dp->i_mount;
-       leaf = bp->data;
+       leaf = bp->b_addr;
        ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
        /*
         * Point to the entry we're removing.
@@ -884,7 +883,7 @@ xfs_dir2_leafn_remove(
         * in the data block in case it changes.
         */
        dbp = dblk->bp;
-       hdr = dbp->data;
+       hdr = dbp->b_addr;
        dep = (xfs_dir2_data_entry_t *)((char *)hdr + off);
        longest = be16_to_cpu(hdr->bestfree[0].length);
        needlog = needscan = 0;
@@ -905,7 +904,7 @@ xfs_dir2_leafn_remove(
         */
        if (longest < be16_to_cpu(hdr->bestfree[0].length)) {
                int             error;          /* error return value */
-               xfs_dabuf_t     *fbp;           /* freeblock buffer */
+               struct xfs_buf  *fbp;           /* freeblock buffer */
                xfs_dir2_db_t   fdb;            /* freeblock block number */
                int             findex;         /* index in freeblock entries */
                xfs_dir2_free_t *free;          /* freeblock structure */
@@ -920,7 +919,7 @@ xfs_dir2_leafn_remove(
                                -1, &fbp, XFS_DATA_FORK))) {
                        return error;
                }
-               free = fbp->data;
+               free = fbp->b_addr;
                ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
                ASSERT(be32_to_cpu(free->hdr.firstdb) ==
                       xfs_dir2_free_max_bests(mp) *
@@ -948,9 +947,7 @@ xfs_dir2_leafn_remove(
                         * In this case just drop the buffer and some one else
                         * will eventually get rid of the empty block.
                         */
-                       else if (error == ENOSPC && args->total == 0)
-                               xfs_da_buf_done(dbp);
-                       else
+                       else if (!(error == ENOSPC && args->total == 0))
                                return error;
                }
                /*
@@ -1018,11 +1015,6 @@ xfs_dir2_leafn_remove(
                 */
                if (logfree)
                        xfs_dir2_free_log_bests(tp, fbp, findex, findex);
-               /*
-                * Drop the buffer if we still have it.
-                */
-               if (fbp)
-                       xfs_da_buf_done(fbp);
        }
        xfs_dir2_leafn_check(dp, bp);
        /*
@@ -1114,7 +1106,7 @@ xfs_dir2_leafn_toosmall(
 {
        xfs_da_state_blk_t      *blk;           /* leaf block */
        xfs_dablk_t             blkno;          /* leaf block number */
-       xfs_dabuf_t             *bp;            /* leaf buffer */
+       struct xfs_buf          *bp;            /* leaf buffer */
        int                     bytes;          /* bytes in use */
        int                     count;          /* leaf live entry count */
        int                     error;          /* error return value */
@@ -1130,7 +1122,7 @@ xfs_dir2_leafn_toosmall(
         * to coalesce with a sibling.
         */
        blk = &state->path.blk[state->path.active - 1];
-       info = blk->bp->data;
+       info = blk->bp->b_addr;
        ASSERT(info->magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
        leaf = (xfs_dir2_leaf_t *)info;
        count = be16_to_cpu(leaf->hdr.count) - be16_to_cpu(leaf->hdr.stale);
@@ -1189,7 +1181,7 @@ xfs_dir2_leafn_toosmall(
                leaf = (xfs_dir2_leaf_t *)info;
                count = be16_to_cpu(leaf->hdr.count) - be16_to_cpu(leaf->hdr.stale);
                bytes = state->blocksize - (state->blocksize >> 2);
-               leaf = bp->data;
+               leaf = bp->b_addr;
                ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
                count += be16_to_cpu(leaf->hdr.count) - be16_to_cpu(leaf->hdr.stale);
                bytes -= count * (uint)sizeof(leaf->ents[0]);
@@ -1198,7 +1190,7 @@ xfs_dir2_leafn_toosmall(
                 */
                if (bytes >= 0)
                        break;
-               xfs_da_brelse(state->args->trans, bp);
+               xfs_trans_brelse(state->args->trans, bp);
        }
        /*
         * Didn't like either block, give up.
@@ -1207,11 +1199,7 @@ xfs_dir2_leafn_toosmall(
                *action = 0;
                return 0;
        }
-       /*
-        * Done with the sibling leaf block here, drop the dabuf
-        * so path_shift can get it.
-        */
-       xfs_da_buf_done(bp);
+
        /*
         * Make altpath point to the block we want to keep (the lower
         * numbered block) and path point to the block we want to drop.
@@ -1247,8 +1235,8 @@ xfs_dir2_leafn_unbalance(
        args = state->args;
        ASSERT(drop_blk->magic == XFS_DIR2_LEAFN_MAGIC);
        ASSERT(save_blk->magic == XFS_DIR2_LEAFN_MAGIC);
-       drop_leaf = drop_blk->bp->data;
-       save_leaf = save_blk->bp->data;
+       drop_leaf = drop_blk->bp->b_addr;
+       save_leaf = save_blk->bp->b_addr;
        ASSERT(drop_leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
        ASSERT(save_leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
        /*
@@ -1356,13 +1344,13 @@ xfs_dir2_node_addname_int(
 {
        xfs_dir2_data_hdr_t     *hdr;           /* data block header */
        xfs_dir2_db_t           dbno;           /* data block number */
-       xfs_dabuf_t             *dbp;           /* data block buffer */
+       struct xfs_buf          *dbp;           /* data block buffer */
        xfs_dir2_data_entry_t   *dep;           /* data entry pointer */
        xfs_inode_t             *dp;            /* incore directory inode */
        xfs_dir2_data_unused_t  *dup;           /* data unused entry pointer */
        int                     error;          /* error return value */
        xfs_dir2_db_t           fbno;           /* freespace block number */
-       xfs_dabuf_t             *fbp;           /* freespace buffer */
+       struct xfs_buf          *fbp;           /* freespace buffer */
        int                     findex;         /* freespace entry index */
        xfs_dir2_free_t         *free=NULL;     /* freespace block structure */
        xfs_dir2_db_t           ifbno;          /* initial freespace block no */
@@ -1390,7 +1378,7 @@ xfs_dir2_node_addname_int(
                 * Remember initial freespace block number.
                 */
                ifbno = fblk->blkno;
-               free = fbp->data;
+               free = fbp->b_addr;
                ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
                findex = fblk->index;
                /*
@@ -1474,7 +1462,7 @@ xfs_dir2_node_addname_int(
                        if (unlikely(fbp == NULL)) {
                                continue;
                        }
-                       free = fbp->data;
+                       free = fbp->b_addr;
                        ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
                        findex = 0;
                }
@@ -1492,7 +1480,7 @@ xfs_dir2_node_addname_int(
                                /*
                                 * Drop the block.
                                 */
-                               xfs_da_brelse(tp, fbp);
+                               xfs_trans_brelse(tp, fbp);
                                fbp = NULL;
                                if (fblk && fblk->bp)
                                        fblk->bp = NULL;
@@ -1507,36 +1495,23 @@ xfs_dir2_node_addname_int(
                /*
                 * Not allowed to allocate, return failure.
                 */
-               if ((args->op_flags & XFS_DA_OP_JUSTCHECK) ||
-                                                       args->total == 0) {
-                       /*
-                        * Drop the freespace buffer unless it came from our
-                        * caller.
-                        */
-                       if ((fblk == NULL || fblk->bp == NULL) && fbp != NULL)
-                               xfs_da_buf_done(fbp);
+               if ((args->op_flags & XFS_DA_OP_JUSTCHECK) || args->total == 0)
                        return XFS_ERROR(ENOSPC);
-               }
+
                /*
                 * Allocate and initialize the new data block.
                 */
                if (unlikely((error = xfs_dir2_grow_inode(args,
                                                         XFS_DIR2_DATA_SPACE,
                                                         &dbno)) ||
-                   (error = xfs_dir2_data_init(args, dbno, &dbp)))) {
-                       /*
-                        * Drop the freespace buffer unless it came from our
-                        * caller.
-                        */
-                       if ((fblk == NULL || fblk->bp == NULL) && fbp != NULL)
-                               xfs_da_buf_done(fbp);
+                   (error = xfs_dir2_data_init(args, dbno, &dbp))))
                        return error;
-               }
+
                /*
                 * If (somehow) we have a freespace block, get rid of it.
                 */
                if (fbp)
-                       xfs_da_brelse(tp, fbp);
+                       xfs_trans_brelse(tp, fbp);
                if (fblk && fblk->bp)
                        fblk->bp = NULL;
 
@@ -1547,10 +1522,9 @@ xfs_dir2_node_addname_int(
                fbno = xfs_dir2_db_to_fdb(mp, dbno);
                if (unlikely(error = xfs_da_read_buf(tp, dp,
                                xfs_dir2_db_to_da(mp, fbno), -2, &fbp,
-                               XFS_DATA_FORK))) {
-                       xfs_da_buf_done(dbp);
+                               XFS_DATA_FORK)))
                        return error;
-               }
+
                /*
                 * If there wasn't a freespace block, the read will
                 * return a NULL fbp.  Allocate and initialize a new one.
@@ -1598,7 +1572,7 @@ xfs_dir2_node_addname_int(
                         * Initialize the new block to be empty, and remember
                         * its first slot as our empty slot.
                         */
-                       free = fbp->data;
+                       free = fbp->b_addr;
                        free->hdr.magic = cpu_to_be32(XFS_DIR2_FREE_MAGIC);
                        free->hdr.firstdb = cpu_to_be32(
                                (fbno - XFS_DIR2_FREE_FIRSTDB(mp)) *
@@ -1606,7 +1580,7 @@ xfs_dir2_node_addname_int(
                        free->hdr.nvalid = 0;
                        free->hdr.nused = 0;
                } else {
-                       free = fbp->data;
+                       free = fbp->b_addr;
                        ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
                }
 
@@ -1639,7 +1613,7 @@ xfs_dir2_node_addname_int(
                 * We haven't allocated the data entry yet so this will
                 * change again.
                 */
-               hdr = dbp->data;
+               hdr = dbp->b_addr;
                free->bests[findex] = hdr->bestfree[0].length;
                logfree = 1;
        }
@@ -1650,22 +1624,17 @@ xfs_dir2_node_addname_int(
                /*
                 * If just checking, we succeeded.
                 */
-               if (args->op_flags & XFS_DA_OP_JUSTCHECK) {
-                       if ((fblk == NULL || fblk->bp == NULL) && fbp != NULL)
-                               xfs_da_buf_done(fbp);
+               if (args->op_flags & XFS_DA_OP_JUSTCHECK)
                        return 0;
-               }
+
                /*
                 * Read the data block in.
                 */
-               if (unlikely(
-                   error = xfs_da_read_buf(tp, dp, xfs_dir2_db_to_da(mp, dbno),
-                               -1, &dbp, XFS_DATA_FORK))) {
-                       if ((fblk == NULL || fblk->bp == NULL) && fbp != NULL)
-                               xfs_da_buf_done(fbp);
+               error = xfs_da_read_buf(tp, dp, xfs_dir2_db_to_da(mp, dbno),
+                               -1, &dbp, XFS_DATA_FORK);
+               if (error)
                        return error;
-               }
-               hdr = dbp->data;
+               hdr = dbp->b_addr;
                logfree = 0;
        }
        ASSERT(be16_to_cpu(hdr->bestfree[0].length) >= length);
@@ -1713,17 +1682,11 @@ xfs_dir2_node_addname_int(
         */
        if (logfree)
                xfs_dir2_free_log_bests(tp, fbp, findex, findex);
-       /*
-        * If the caller didn't hand us the freespace block, drop it.
-        */
-       if ((fblk == NULL || fblk->bp == NULL) && fbp != NULL)
-               xfs_da_buf_done(fbp);
        /*
         * Return the data block and offset in args, then drop the data block.
         */
        args->blkno = (xfs_dablk_t)dbno;
        args->index = be16_to_cpu(*tagp);
-       xfs_da_buf_done(dbp);
        return 0;
 }
 
@@ -1761,22 +1724,23 @@ xfs_dir2_node_lookup(
                /* If a CI match, dup the actual name and return EEXIST */
                xfs_dir2_data_entry_t   *dep;
 
-               dep = (xfs_dir2_data_entry_t *)((char *)state->extrablk.bp->
-                                               data + state->extrablk.index);
+               dep = (xfs_dir2_data_entry_t *)
+                       ((char *)state->extrablk.bp->b_addr +
+                                                state->extrablk.index);
                rval = xfs_dir_cilookup_result(args, dep->name, dep->namelen);
        }
        /*
         * Release the btree blocks and leaf block.
         */
        for (i = 0; i < state->path.active; i++) {
-               xfs_da_brelse(args->trans, state->path.blk[i].bp);
+               xfs_trans_brelse(args->trans, state->path.blk[i].bp);
                state->path.blk[i].bp = NULL;
        }
        /*
         * Release the data block if we have it.
         */
        if (state->extravalid && state->extrablk.bp) {
-               xfs_da_brelse(args->trans, state->extrablk.bp);
+               xfs_trans_brelse(args->trans, state->extrablk.bp);
                state->extrablk.bp = NULL;
        }
        xfs_da_state_free(state);
@@ -1893,13 +1857,13 @@ xfs_dir2_node_replace(
                 */
                blk = &state->path.blk[state->path.active - 1];
                ASSERT(blk->magic == XFS_DIR2_LEAFN_MAGIC);
-               leaf = blk->bp->data;
+               leaf = blk->bp->b_addr;
                lep = &leaf->ents[blk->index];
                ASSERT(state->extravalid);
                /*
                 * Point to the data entry.
                 */
-               hdr = state->extrablk.bp->data;
+               hdr = state->extrablk.bp->b_addr;
                ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC));
                dep = (xfs_dir2_data_entry_t *)
                      ((char *)hdr +
@@ -1916,14 +1880,14 @@ xfs_dir2_node_replace(
         * Didn't find it, and we're holding a data block.  Drop it.
         */
        else if (state->extravalid) {
-               xfs_da_brelse(args->trans, state->extrablk.bp);
+               xfs_trans_brelse(args->trans, state->extrablk.bp);
                state->extrablk.bp = NULL;
        }
        /*
         * Release all the buffers in the cursor.
         */
        for (i = 0; i < state->path.active; i++) {
-               xfs_da_brelse(args->trans, state->path.blk[i].bp);
+               xfs_trans_brelse(args->trans, state->path.blk[i].bp);
                state->path.blk[i].bp = NULL;
        }
        xfs_da_state_free(state);
@@ -1940,7 +1904,7 @@ xfs_dir2_node_trim_free(
        xfs_fileoff_t           fo,             /* free block number */
        int                     *rvalp)         /* out: did something */
 {
-       xfs_dabuf_t             *bp;            /* freespace buffer */
+       struct xfs_buf          *bp;            /* freespace buffer */
        xfs_inode_t             *dp;            /* incore directory inode */
        int                     error;          /* error return code */
        xfs_dir2_free_t         *free;          /* freespace structure */
@@ -1965,13 +1929,13 @@ xfs_dir2_node_trim_free(
        if (bp == NULL) {
                return 0;
        }
-       free = bp->data;
+       free = bp->b_addr;
        ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
        /*
         * If there are used entries, there's nothing to do.
         */
        if (be32_to_cpu(free->hdr.nused) > 0) {
-               xfs_da_brelse(tp, bp);
+               xfs_trans_brelse(tp, bp);
                *rvalp = 0;
                return 0;
        }
@@ -1987,7 +1951,7 @@ xfs_dir2_node_trim_free(
                 * pieces.  This is the last block of an extent.
                 */
                ASSERT(error != ENOSPC);
-               xfs_da_brelse(tp, bp);
+               xfs_trans_brelse(tp, bp);
                return error;
        }
        /*
index 067f403ecf8a41c4367c07d46cb6bd0c86b75cca..3523d3e15aa8ad3fd3f86aaf65f322ad13c956bf 100644 (file)
@@ -25,7 +25,7 @@ extern int xfs_dir2_isleaf(struct xfs_trans *tp, struct xfs_inode *dp, int *r);
 extern int xfs_dir2_grow_inode(struct xfs_da_args *args, int space,
                                xfs_dir2_db_t *dbp);
 extern int xfs_dir2_shrink_inode(struct xfs_da_args *args, xfs_dir2_db_t db,
-                               struct xfs_dabuf *bp);
+                               struct xfs_buf *bp);
 extern int xfs_dir_cilookup_result(struct xfs_da_args *args,
                                const unsigned char *name, int len);
 
@@ -37,11 +37,11 @@ extern int xfs_dir2_block_lookup(struct xfs_da_args *args);
 extern int xfs_dir2_block_removename(struct xfs_da_args *args);
 extern int xfs_dir2_block_replace(struct xfs_da_args *args);
 extern int xfs_dir2_leaf_to_block(struct xfs_da_args *args,
-               struct xfs_dabuf *lbp, struct xfs_dabuf *dbp);
+               struct xfs_buf *lbp, struct xfs_buf *dbp);
 
 /* xfs_dir2_data.c */
 #ifdef DEBUG
-extern void xfs_dir2_data_check(struct xfs_inode *dp, struct xfs_dabuf *bp);
+extern void xfs_dir2_data_check(struct xfs_inode *dp, struct xfs_buf *bp);
 #else
 #define        xfs_dir2_data_check(dp,bp)
 #endif
@@ -51,43 +51,43 @@ xfs_dir2_data_freeinsert(struct xfs_dir2_data_hdr *hdr,
 extern void xfs_dir2_data_freescan(struct xfs_mount *mp,
                struct xfs_dir2_data_hdr *hdr, int *loghead);
 extern int xfs_dir2_data_init(struct xfs_da_args *args, xfs_dir2_db_t blkno,
-               struct xfs_dabuf **bpp);
-extern void xfs_dir2_data_log_entry(struct xfs_trans *tp, struct xfs_dabuf *bp,
+               struct xfs_buf **bpp);
+extern void xfs_dir2_data_log_entry(struct xfs_trans *tp, struct xfs_buf *bp,
                struct xfs_dir2_data_entry *dep);
 extern void xfs_dir2_data_log_header(struct xfs_trans *tp,
-               struct xfs_dabuf *bp);
-extern void xfs_dir2_data_log_unused(struct xfs_trans *tp, struct xfs_dabuf *bp,
+               struct xfs_buf *bp);
+extern void xfs_dir2_data_log_unused(struct xfs_trans *tp, struct xfs_buf *bp,
                struct xfs_dir2_data_unused *dup);
-extern void xfs_dir2_data_make_free(struct xfs_trans *tp, struct xfs_dabuf *bp,
+extern void xfs_dir2_data_make_free(struct xfs_trans *tp, struct xfs_buf *bp,
                xfs_dir2_data_aoff_t offset, xfs_dir2_data_aoff_t len,
                int *needlogp, int *needscanp);
-extern void xfs_dir2_data_use_free(struct xfs_trans *tp, struct xfs_dabuf *bp,
+extern void xfs_dir2_data_use_free(struct xfs_trans *tp, struct xfs_buf *bp,
                struct xfs_dir2_data_unused *dup, xfs_dir2_data_aoff_t offset,
                xfs_dir2_data_aoff_t len, int *needlogp, int *needscanp);
 
 /* xfs_dir2_leaf.c */
 extern int xfs_dir2_block_to_leaf(struct xfs_da_args *args,
-               struct xfs_dabuf *dbp);
+               struct xfs_buf *dbp);
 extern int xfs_dir2_leaf_addname(struct xfs_da_args *args);
 extern void xfs_dir2_leaf_compact(struct xfs_da_args *args,
-               struct xfs_dabuf *bp);
-extern void xfs_dir2_leaf_compact_x1(struct xfs_dabuf *bp, int *indexp,
+               struct xfs_buf *bp);
+extern void xfs_dir2_leaf_compact_x1(struct xfs_buf *bp, int *indexp,
                int *lowstalep, int *highstalep, int *lowlogp, int *highlogp);
 extern int xfs_dir2_leaf_getdents(struct xfs_inode *dp, void *dirent,
                size_t bufsize, xfs_off_t *offset, filldir_t filldir);
 extern int xfs_dir2_leaf_init(struct xfs_da_args *args, xfs_dir2_db_t bno,
-               struct xfs_dabuf **bpp, int magic);
-extern void xfs_dir2_leaf_log_ents(struct xfs_trans *tp, struct xfs_dabuf *bp,
+               struct xfs_buf **bpp, int magic);
+extern void xfs_dir2_leaf_log_ents(struct xfs_trans *tp, struct xfs_buf *bp,
                int first, int last);
 extern void xfs_dir2_leaf_log_header(struct xfs_trans *tp,
-               struct xfs_dabuf *bp);
+               struct xfs_buf *bp);
 extern int xfs_dir2_leaf_lookup(struct xfs_da_args *args);
 extern int xfs_dir2_leaf_removename(struct xfs_da_args *args);
 extern int xfs_dir2_leaf_replace(struct xfs_da_args *args);
 extern int xfs_dir2_leaf_search_hash(struct xfs_da_args *args,
-               struct xfs_dabuf *lbp);
+               struct xfs_buf *lbp);
 extern int xfs_dir2_leaf_trim_data(struct xfs_da_args *args,
-               struct xfs_dabuf *lbp, xfs_dir2_db_t db);
+               struct xfs_buf *lbp, xfs_dir2_db_t db);
 extern struct xfs_dir2_leaf_entry *
 xfs_dir2_leaf_find_entry(struct xfs_dir2_leaf *leaf, int index, int compact,
                int lowstale, int highstale,
@@ -96,13 +96,13 @@ extern int xfs_dir2_node_to_leaf(struct xfs_da_state *state);
 
 /* xfs_dir2_node.c */
 extern int xfs_dir2_leaf_to_node(struct xfs_da_args *args,
-               struct xfs_dabuf *lbp);
-extern xfs_dahash_t xfs_dir2_leafn_lasthash(struct xfs_dabuf *bp, int *count);
-extern int xfs_dir2_leafn_lookup_int(struct xfs_dabuf *bp,
+               struct xfs_buf *lbp);
+extern xfs_dahash_t xfs_dir2_leafn_lasthash(struct xfs_buf *bp, int *count);
+extern int xfs_dir2_leafn_lookup_int(struct xfs_buf *bp,
                struct xfs_da_args *args, int *indexp,
                struct xfs_da_state *state);
-extern int xfs_dir2_leafn_order(struct xfs_dabuf *leaf1_bp,
-               struct xfs_dabuf *leaf2_bp);
+extern int xfs_dir2_leafn_order(struct xfs_buf *leaf1_bp,
+               struct xfs_buf *leaf2_bp);
 extern int xfs_dir2_leafn_split(struct xfs_da_state *state,
        struct xfs_da_state_blk *oldblk, struct xfs_da_state_blk *newblk);
 extern int xfs_dir2_leafn_toosmall(struct xfs_da_state *state, int *action);
@@ -122,7 +122,7 @@ extern xfs_ino_t xfs_dir2_sfe_get_ino(struct xfs_dir2_sf_hdr *sfp,
                struct xfs_dir2_sf_entry *sfep);
 extern int xfs_dir2_block_sfsize(struct xfs_inode *dp,
                struct xfs_dir2_data_hdr *block, struct xfs_dir2_sf_hdr *sfhp);
-extern int xfs_dir2_block_to_sf(struct xfs_da_args *args, struct xfs_dabuf *bp,
+extern int xfs_dir2_block_to_sf(struct xfs_da_args *args, struct xfs_buf *bp,
                int size, xfs_dir2_sf_hdr_t *sfhp);
 extern int xfs_dir2_sf_addname(struct xfs_da_args *args);
 extern int xfs_dir2_sf_create(struct xfs_da_args *args, xfs_ino_t pino);
index 19bf0c5e38f456e815a11806098ee675a9ed6134..1b9fc3ec7e4b393f573b1861b0b12db315872d32 100644 (file)
@@ -222,7 +222,7 @@ xfs_dir2_block_sfsize(
 int                                            /* error */
 xfs_dir2_block_to_sf(
        xfs_da_args_t           *args,          /* operation arguments */
-       xfs_dabuf_t             *bp,            /* block buffer */
+       struct xfs_buf          *bp,
        int                     size,           /* shortform directory size */
        xfs_dir2_sf_hdr_t       *sfhp)          /* shortform directory hdr */
 {
@@ -249,7 +249,7 @@ xfs_dir2_block_to_sf(
         * and add local data.
         */
        hdr = kmem_alloc(mp->m_dirblksize, KM_SLEEP);
-       memcpy(hdr, bp->data, mp->m_dirblksize);
+       memcpy(hdr, bp->b_addr, mp->m_dirblksize);
        logflags = XFS_ILOG_CORE;
        if ((error = xfs_dir2_shrink_inode(args, mp->m_dirdatablk, bp))) {
                ASSERT(error != ENOSPC);
index 9f7ec15a65222e2fe318e0ab81ac9cca0a664b4a..56afcdb2377d57c03bce9dc40b78c991184735a1 100644 (file)
@@ -236,7 +236,6 @@ xfs_file_aio_read(
        ssize_t                 ret = 0;
        int                     ioflags = 0;
        xfs_fsize_t             n;
-       unsigned long           seg;
 
        XFS_STATS_INC(xs_read_calls);
 
@@ -247,19 +246,9 @@ xfs_file_aio_read(
        if (file->f_mode & FMODE_NOCMTIME)
                ioflags |= IO_INVIS;
 
-       /* START copy & waste from filemap.c */
-       for (seg = 0; seg < nr_segs; seg++) {
-               const struct iovec *iv = &iovp[seg];
-
-               /*
-                * If any segment has a negative length, or the cumulative
-                * length ever wraps negative then return -EINVAL.
-                */
-               size += iv->iov_len;
-               if (unlikely((ssize_t)(size|iv->iov_len) < 0))
-                       return XFS_ERROR(-EINVAL);
-       }
-       /* END copy & waste from filemap.c */
+       ret = generic_segment_checks(iovp, &nr_segs, &size, VERIFY_WRITE);
+       if (ret < 0)
+               return ret;
 
        if (unlikely(ioflags & IO_ISDIRECT)) {
                xfs_buftarg_t   *target =
@@ -273,7 +262,7 @@ xfs_file_aio_read(
                }
        }
 
-       n = XFS_MAXIOFFSET(mp) - iocb->ki_pos;
+       n = mp->m_super->s_maxbytes - iocb->ki_pos;
        if (n <= 0 || size == 0)
                return 0;
 
@@ -781,10 +770,12 @@ xfs_file_aio_write(
        if (ocount == 0)
                return 0;
 
-       xfs_wait_for_freeze(ip->i_mount, SB_FREEZE_WRITE);
+       sb_start_write(inode->i_sb);
 
-       if (XFS_FORCED_SHUTDOWN(ip->i_mount))
-               return -EIO;
+       if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
+               ret = -EIO;
+               goto out;
+       }
 
        if (unlikely(file->f_flags & O_DIRECT))
                ret = xfs_file_dio_aio_write(iocb, iovp, nr_segs, pos, ocount);
@@ -803,6 +794,8 @@ xfs_file_aio_write(
                        ret = err;
        }
 
+out:
+       sb_end_write(inode->i_sb);
        return ret;
 }
 
index 177a21a7ac490983a5a5ca424ff07ff75bb61000..21e37b55f7e596c6d29c0c0125a755f7a8ada7f6 100644 (file)
@@ -442,14 +442,13 @@ xfs_ialloc_next_ag(
  * Select an allocation group to look for a free inode in, based on the parent
  * inode and then mode.  Return the allocation group buffer.
  */
-STATIC xfs_buf_t *                     /* allocation group buffer */
+STATIC xfs_agnumber_t
 xfs_ialloc_ag_select(
        xfs_trans_t     *tp,            /* transaction pointer */
        xfs_ino_t       parent,         /* parent directory inode number */
        umode_t         mode,           /* bits set to indicate file type */
        int             okalloc)        /* ok to allocate more space */
 {
-       xfs_buf_t       *agbp;          /* allocation group header buffer */
        xfs_agnumber_t  agcount;        /* number of ag's in the filesystem */
        xfs_agnumber_t  agno;           /* current ag number */
        int             flags;          /* alloc buffer locking flags */
@@ -459,6 +458,7 @@ xfs_ialloc_ag_select(
        int             needspace;      /* file mode implies space allocated */
        xfs_perag_t     *pag;           /* per allocation group data */
        xfs_agnumber_t  pagno;          /* parent (starting) ag number */
+       int             error;
 
        /*
         * Files of these types need at least one block if length > 0
@@ -474,7 +474,9 @@ xfs_ialloc_ag_select(
                if (pagno >= agcount)
                        pagno = 0;
        }
+
        ASSERT(pagno < agcount);
+
        /*
         * Loop through allocation groups, looking for one with a little
         * free space in it.  Note we don't look for free inodes, exactly.
@@ -486,51 +488,45 @@ xfs_ialloc_ag_select(
        flags = XFS_ALLOC_FLAG_TRYLOCK;
        for (;;) {
                pag = xfs_perag_get(mp, agno);
+               if (!pag->pagi_inodeok) {
+                       xfs_ialloc_next_ag(mp);
+                       goto nextag;
+               }
+
                if (!pag->pagi_init) {
-                       if (xfs_ialloc_read_agi(mp, tp, agno, &agbp)) {
-                               agbp = NULL;
+                       error = xfs_ialloc_pagi_init(mp, tp, agno);
+                       if (error)
                                goto nextag;
-                       }
-               } else
-                       agbp = NULL;
+               }
 
-               if (!pag->pagi_inodeok) {
-                       xfs_ialloc_next_ag(mp);
-                       goto unlock_nextag;
+               if (pag->pagi_freecount) {
+                       xfs_perag_put(pag);
+                       return agno;
                }
 
-               /*
-                * Is there enough free space for the file plus a block
-                * of inodes (if we need to allocate some)?
-                */
-               ineed = pag->pagi_freecount ? 0 : XFS_IALLOC_BLOCKS(mp);
-               if (ineed && !pag->pagf_init) {
-                       if (agbp == NULL &&
-                           xfs_ialloc_read_agi(mp, tp, agno, &agbp)) {
-                               agbp = NULL;
+               if (!okalloc)
+                       goto nextag;
+
+               if (!pag->pagf_init) {
+                       error = xfs_alloc_pagf_init(mp, tp, agno, flags);
+                       if (error)
                                goto nextag;
-                       }
-                       (void)xfs_alloc_pagf_init(mp, tp, agno, flags);
                }
-               if (!ineed || pag->pagf_init) {
-                       if (ineed && !(longest = pag->pagf_longest))
-                               longest = pag->pagf_flcount > 0;
-                       if (!ineed ||
-                           (pag->pagf_freeblks >= needspace + ineed &&
-                            longest >= ineed &&
-                            okalloc)) {
-                               if (agbp == NULL &&
-                                   xfs_ialloc_read_agi(mp, tp, agno, &agbp)) {
-                                       agbp = NULL;
-                                       goto nextag;
-                               }
-                               xfs_perag_put(pag);
-                               return agbp;
-                       }
+
+               /*
+                * Is there enough free space for the file plus a block of
+                * inodes? (if we need to allocate some)?
+                */
+               ineed = XFS_IALLOC_BLOCKS(mp);
+               longest = pag->pagf_longest;
+               if (!longest)
+                       longest = pag->pagf_flcount > 0;
+
+               if (pag->pagf_freeblks >= needspace + ineed &&
+                   longest >= ineed) {
+                       xfs_perag_put(pag);
+                       return agno;
                }
-unlock_nextag:
-               if (agbp)
-                       xfs_trans_brelse(tp, agbp);
 nextag:
                xfs_perag_put(pag);
                /*
@@ -538,13 +534,13 @@ nextag:
                 * down.
                 */
                if (XFS_FORCED_SHUTDOWN(mp))
-                       return NULL;
+                       return NULLAGNUMBER;
                agno++;
                if (agno >= agcount)
                        agno = 0;
                if (agno == pagno) {
                        if (flags == 0)
-                               return NULL;
+                               return NULLAGNUMBER;
                        flags = 0;
                }
        }
@@ -607,195 +603,39 @@ xfs_ialloc_get_rec(
 }
 
 /*
- * Visible inode allocation functions.
- */
-/*
- * Find a free (set) bit in the inode bitmask.
- */
-static inline int xfs_ialloc_find_free(xfs_inofree_t *fp)
-{
-       return xfs_lowbit64(*fp);
-}
-
-/*
- * Allocate an inode on disk.
- * Mode is used to tell whether the new inode will need space, and whether
- * it is a directory.
- *
- * The arguments IO_agbp and alloc_done are defined to work within
- * the constraint of one allocation per transaction.
- * xfs_dialloc() is designed to be called twice if it has to do an
- * allocation to make more free inodes.  On the first call,
- * IO_agbp should be set to NULL. If an inode is available,
- * i.e., xfs_dialloc() did not need to do an allocation, an inode
- * number is returned.  In this case, IO_agbp would be set to the
- * current ag_buf and alloc_done set to false.
- * If an allocation needed to be done, xfs_dialloc would return
- * the current ag_buf in IO_agbp and set alloc_done to true.
- * The caller should then commit the current transaction, allocate a new
- * transaction, and call xfs_dialloc() again, passing in the previous
- * value of IO_agbp.  IO_agbp should be held across the transactions.
- * Since the agbp is locked across the two calls, the second call is
- * guaranteed to have a free inode available.
+ * Allocate an inode.
  *
- * Once we successfully pick an inode its number is returned and the
- * on-disk data structures are updated.  The inode itself is not read
- * in, since doing so would break ordering constraints with xfs_reclaim.
+ * The caller selected an AG for us, and made sure that free inodes are
+ * available.
  */
-int
-xfs_dialloc(
-       xfs_trans_t     *tp,            /* transaction pointer */
-       xfs_ino_t       parent,         /* parent inode (directory) */
-       umode_t         mode,           /* mode bits for new inode */
-       int             okalloc,        /* ok to allocate more space */
-       xfs_buf_t       **IO_agbp,      /* in/out ag header's buffer */
-       boolean_t       *alloc_done,    /* true if we needed to replenish
-                                          inode freelist */
-       xfs_ino_t       *inop)          /* inode number allocated */
+STATIC int
+xfs_dialloc_ag(
+       struct xfs_trans        *tp,
+       struct xfs_buf          *agbp,
+       xfs_ino_t               parent,
+       xfs_ino_t               *inop)
 {
-       xfs_agnumber_t  agcount;        /* number of allocation groups */
-       xfs_buf_t       *agbp;          /* allocation group header's buffer */
-       xfs_agnumber_t  agno;           /* allocation group number */
-       xfs_agi_t       *agi;           /* allocation group header structure */
-       xfs_btree_cur_t *cur;           /* inode allocation btree cursor */
-       int             error;          /* error return value */
-       int             i;              /* result code */
-       int             ialloced;       /* inode allocation status */
-       int             noroom = 0;     /* no space for inode blk allocation */
-       xfs_ino_t       ino;            /* fs-relative inode to be returned */
-       /* REFERENCED */
-       int             j;              /* result code */
-       xfs_mount_t     *mp;            /* file system mount structure */
-       int             offset;         /* index of inode in chunk */
-       xfs_agino_t     pagino;         /* parent's AG relative inode # */
-       xfs_agnumber_t  pagno;          /* parent's AG number */
-       xfs_inobt_rec_incore_t rec;     /* inode allocation record */
-       xfs_agnumber_t  tagno;          /* testing allocation group number */
-       xfs_btree_cur_t *tcur;          /* temp cursor */
-       xfs_inobt_rec_incore_t trec;    /* temp inode allocation record */
-       struct xfs_perag *pag;
-
-
-       if (*IO_agbp == NULL) {
-               /*
-                * We do not have an agbp, so select an initial allocation
-                * group for inode allocation.
-                */
-               agbp = xfs_ialloc_ag_select(tp, parent, mode, okalloc);
-               /*
-                * Couldn't find an allocation group satisfying the
-                * criteria, give up.
-                */
-               if (!agbp) {
-                       *inop = NULLFSINO;
-                       return 0;
-               }
-               agi = XFS_BUF_TO_AGI(agbp);
-               ASSERT(agi->agi_magicnum == cpu_to_be32(XFS_AGI_MAGIC));
-       } else {
-               /*
-                * Continue where we left off before.  In this case, we
-                * know that the allocation group has free inodes.
-                */
-               agbp = *IO_agbp;
-               agi = XFS_BUF_TO_AGI(agbp);
-               ASSERT(agi->agi_magicnum == cpu_to_be32(XFS_AGI_MAGIC));
-               ASSERT(be32_to_cpu(agi->agi_freecount) > 0);
-       }
-       mp = tp->t_mountp;
-       agcount = mp->m_sb.sb_agcount;
-       agno = be32_to_cpu(agi->agi_seqno);
-       tagno = agno;
-       pagno = XFS_INO_TO_AGNO(mp, parent);
-       pagino = XFS_INO_TO_AGINO(mp, parent);
-
-       /*
-        * If we have already hit the ceiling of inode blocks then clear
-        * okalloc so we scan all available agi structures for a free
-        * inode.
-        */
-
-       if (mp->m_maxicount &&
-           mp->m_sb.sb_icount + XFS_IALLOC_INODES(mp) > mp->m_maxicount) {
-               noroom = 1;
-               okalloc = 0;
-       }
+       struct xfs_mount        *mp = tp->t_mountp;
+       struct xfs_agi          *agi = XFS_BUF_TO_AGI(agbp);
+       xfs_agnumber_t          agno = be32_to_cpu(agi->agi_seqno);
+       xfs_agnumber_t          pagno = XFS_INO_TO_AGNO(mp, parent);
+       xfs_agino_t             pagino = XFS_INO_TO_AGINO(mp, parent);
+       struct xfs_perag        *pag;
+       struct xfs_btree_cur    *cur, *tcur;
+       struct xfs_inobt_rec_incore rec, trec;
+       xfs_ino_t               ino;
+       int                     error;
+       int                     offset;
+       int                     i, j;
 
-       /*
-        * Loop until we find an allocation group that either has free inodes
-        * or in which we can allocate some inodes.  Iterate through the
-        * allocation groups upward, wrapping at the end.
-        */
-       *alloc_done = B_FALSE;
-       while (!agi->agi_freecount) {
-               /*
-                * Don't do anything if we're not supposed to allocate
-                * any blocks, just go on to the next ag.
-                */
-               if (okalloc) {
-                       /*
-                        * Try to allocate some new inodes in the allocation
-                        * group.
-                        */
-                       if ((error = xfs_ialloc_ag_alloc(tp, agbp, &ialloced))) {
-                               xfs_trans_brelse(tp, agbp);
-                               if (error == ENOSPC) {
-                                       *inop = NULLFSINO;
-                                       return 0;
-                               } else
-                                       return error;
-                       }
-                       if (ialloced) {
-                               /*
-                                * We successfully allocated some inodes, return
-                                * the current context to the caller so that it
-                                * can commit the current transaction and call
-                                * us again where we left off.
-                                */
-                               ASSERT(be32_to_cpu(agi->agi_freecount) > 0);
-                               *alloc_done = B_TRUE;
-                               *IO_agbp = agbp;
-                               *inop = NULLFSINO;
-                               return 0;
-                       }
-               }
-               /*
-                * If it failed, give up on this ag.
-                */
-               xfs_trans_brelse(tp, agbp);
-               /*
-                * Go on to the next ag: get its ag header.
-                */
-nextag:
-               if (++tagno == agcount)
-                       tagno = 0;
-               if (tagno == agno) {
-                       *inop = NULLFSINO;
-                       return noroom ? ENOSPC : 0;
-               }
-               pag = xfs_perag_get(mp, tagno);
-               if (pag->pagi_inodeok == 0) {
-                       xfs_perag_put(pag);
-                       goto nextag;
-               }
-               error = xfs_ialloc_read_agi(mp, tp, tagno, &agbp);
-               xfs_perag_put(pag);
-               if (error)
-                       goto nextag;
-               agi = XFS_BUF_TO_AGI(agbp);
-               ASSERT(agi->agi_magicnum == cpu_to_be32(XFS_AGI_MAGIC));
-       }
-       /*
-        * Here with an allocation group that has a free inode.
-        * Reset agno since we may have chosen a new ag in the
-        * loop above.
-        */
-       agno = tagno;
-       *IO_agbp = NULL;
        pag = xfs_perag_get(mp, agno);
 
+       ASSERT(pag->pagi_init);
+       ASSERT(pag->pagi_inodeok);
+       ASSERT(pag->pagi_freecount > 0);
+
  restart_pagno:
-       cur = xfs_inobt_init_cursor(mp, tp, agbp, be32_to_cpu(agi->agi_seqno));
+       cur = xfs_inobt_init_cursor(mp, tp, agbp, agno);
        /*
         * If pagino is 0 (this is the root inode allocation) use newino.
         * This must work because we've just allocated some.
@@ -995,7 +835,7 @@ newino:
        }
 
 alloc_inode:
-       offset = xfs_ialloc_find_free(&rec.ir_free);
+       offset = xfs_lowbit64(rec.ir_free);
        ASSERT(offset >= 0);
        ASSERT(offset < XFS_INODES_PER_CHUNK);
        ASSERT((XFS_AGINO_TO_OFFSET(mp, rec.ir_startino) %
@@ -1027,6 +867,164 @@ error0:
        return error;
 }
 
+/*
+ * Allocate an inode on disk.
+ *
+ * Mode is used to tell whether the new inode will need space, and whether it
+ * is a directory.
+ *
+ * This function is designed to be called twice if it has to do an allocation
+ * to make more free inodes.  On the first call, *IO_agbp should be set to NULL.
+ * If an inode is available without having to performn an allocation, an inode
+ * number is returned.  In this case, *IO_agbp would be NULL.  If an allocation
+ * needes to be done, xfs_dialloc would return the current AGI buffer in
+ * *IO_agbp.  The caller should then commit the current transaction, allocate a
+ * new transaction, and call xfs_dialloc() again, passing in the previous value
+ * of *IO_agbp.  IO_agbp should be held across the transactions. Since the AGI
+ * buffer is locked across the two calls, the second call is guaranteed to have
+ * a free inode available.
+ *
+ * Once we successfully pick an inode its number is returned and the on-disk
+ * data structures are updated.  The inode itself is not read in, since doing so
+ * would break ordering constraints with xfs_reclaim.
+ */
+int
+xfs_dialloc(
+       struct xfs_trans        *tp,
+       xfs_ino_t               parent,
+       umode_t                 mode,
+       int                     okalloc,
+       struct xfs_buf          **IO_agbp,
+       xfs_ino_t               *inop)
+{
+       struct xfs_mount        *mp = tp->t_mountp;
+       struct xfs_buf          *agbp;
+       xfs_agnumber_t          agno;
+       int                     error;
+       int                     ialloced;
+       int                     noroom = 0;
+       xfs_agnumber_t          start_agno;
+       struct xfs_perag        *pag;
+
+       if (*IO_agbp) {
+               /*
+                * If the caller passes in a pointer to the AGI buffer,
+                * continue where we left off before.  In this case, we
+                * know that the allocation group has free inodes.
+                */
+               agbp = *IO_agbp;
+               goto out_alloc;
+       }
+
+       /*
+        * We do not have an agbp, so select an initial allocation
+        * group for inode allocation.
+        */
+       start_agno = xfs_ialloc_ag_select(tp, parent, mode, okalloc);
+       if (start_agno == NULLAGNUMBER) {
+               *inop = NULLFSINO;
+               return 0;
+       }
+
+       /*
+        * If we have already hit the ceiling of inode blocks then clear
+        * okalloc so we scan all available agi structures for a free
+        * inode.
+        */
+       if (mp->m_maxicount &&
+           mp->m_sb.sb_icount + XFS_IALLOC_INODES(mp) > mp->m_maxicount) {
+               noroom = 1;
+               okalloc = 0;
+       }
+
+       /*
+        * Loop until we find an allocation group that either has free inodes
+        * or in which we can allocate some inodes.  Iterate through the
+        * allocation groups upward, wrapping at the end.
+        */
+       agno = start_agno;
+       for (;;) {
+               pag = xfs_perag_get(mp, agno);
+               if (!pag->pagi_inodeok) {
+                       xfs_ialloc_next_ag(mp);
+                       goto nextag;
+               }
+
+               if (!pag->pagi_init) {
+                       error = xfs_ialloc_pagi_init(mp, tp, agno);
+                       if (error)
+                               goto out_error;
+               }
+
+               /*
+                * Do a first racy fast path check if this AG is usable.
+                */
+               if (!pag->pagi_freecount && !okalloc)
+                       goto nextag;
+
+               error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
+               if (error)
+                       goto out_error;
+
+               /*
+                * Once the AGI has been read in we have to recheck
+                * pagi_freecount with the AGI buffer lock held.
+                */
+               if (pag->pagi_freecount) {
+                       xfs_perag_put(pag);
+                       goto out_alloc;
+               }
+
+               if (!okalloc) {
+                       xfs_trans_brelse(tp, agbp);
+                       goto nextag;
+               }
+
+               error = xfs_ialloc_ag_alloc(tp, agbp, &ialloced);
+               if (error) {
+                       xfs_trans_brelse(tp, agbp);
+
+                       if (error != ENOSPC)
+                               goto out_error;
+
+                       xfs_perag_put(pag);
+                       *inop = NULLFSINO;
+                       return 0;
+               }
+
+               if (ialloced) {
+                       /*
+                        * We successfully allocated some inodes, return
+                        * the current context to the caller so that it
+                        * can commit the current transaction and call
+                        * us again where we left off.
+                        */
+                       ASSERT(pag->pagi_freecount > 0);
+                       xfs_perag_put(pag);
+
+                       *IO_agbp = agbp;
+                       *inop = NULLFSINO;
+                       return 0;
+               }
+
+nextag:
+               xfs_perag_put(pag);
+               if (++agno == mp->m_sb.sb_agcount)
+                       agno = 0;
+               if (agno == start_agno) {
+                       *inop = NULLFSINO;
+                       return noroom ? ENOSPC : 0;
+               }
+       }
+
+out_alloc:
+       *IO_agbp = NULL;
+       return xfs_dialloc_ag(tp, agbp, parent, inop);
+out_error:
+       xfs_perag_put(pag);
+       return XFS_ERROR(error);
+}
+
 /*
  * Free disk inode.  Carefully avoids touching the incore inode, all
  * manipulations incore are the caller's responsibility.
index 65ac57c8063c78cc066f996db1ddbcce7c6e8a9f..1fd6ea4e9c91cdb09af5c80ff293ad818894a2d1 100644 (file)
@@ -75,8 +75,6 @@ xfs_dialloc(
        umode_t         mode,           /* mode bits for new inode */
        int             okalloc,        /* ok to allocate more space */
        struct xfs_buf  **agbp,         /* buf for a.g. inode header */
-       boolean_t       *alloc_done,    /* an allocation was done to replenish
-                                          the free inodes */
        xfs_ino_t       *inop);         /* inode number allocated */
 
 /*
index 1bb4365e8c25470d8686996401d7996ce1d3b244..784a803383ec01d670366fc6a334dcc9442176ca 100644 (file)
 #include "xfs_trace.h"
 
 
-/*
- * Define xfs inode iolock lockdep classes. We need to ensure that all active
- * inodes are considered the same for lockdep purposes, including inodes that
- * are recycled through the XFS_IRECLAIMABLE state. This is the the only way to
- * guarantee the locks are considered the same when there are multiple lock
- * initialisation siteÑ•. Also, define a reclaimable inode class so it is
- * obvious in lockdep reports which class the report is against.
- */
-static struct lock_class_key xfs_iolock_active;
-struct lock_class_key xfs_iolock_reclaimable;
-
 /*
  * Allocate and initialise an xfs_inode.
  */
@@ -80,8 +69,6 @@ xfs_inode_alloc(
        ASSERT(ip->i_ino == 0);
 
        mrlock_init(&ip->i_iolock, MRLOCK_BARRIER, "xfsio", ip->i_ino);
-       lockdep_set_class_and_name(&ip->i_iolock.mr_lock,
-                       &xfs_iolock_active, "xfs_iolock_active");
 
        /* initialise the xfs inode */
        ip->i_ino = ino;
@@ -250,8 +237,6 @@ xfs_iget_cache_hit(
 
                ASSERT(!rwsem_is_locked(&ip->i_iolock.mr_lock));
                mrlock_init(&ip->i_iolock, MRLOCK_BARRIER, "xfsio", ip->i_ino);
-               lockdep_set_class_and_name(&ip->i_iolock.mr_lock,
-                               &xfs_iolock_active, "xfs_iolock_active");
 
                spin_unlock(&ip->i_flags_lock);
                spin_unlock(&pag->pag_ici_lock);
index a59eea09930ae3a729cae134f1bd26084e2fbc4c..2778258fcfa239e07dbc79edf6cb4750b7e13dd8 100644 (file)
@@ -132,23 +132,28 @@ xfs_inobp_check(
 #endif
 
 /*
- * Find the buffer associated with the given inode map
- * We do basic validation checks on the buffer once it has been
- * retrieved from disk.
+ * This routine is called to map an inode to the buffer containing the on-disk
+ * version of the inode.  It returns a pointer to the buffer containing the
+ * on-disk inode in the bpp parameter, and in the dipp parameter it returns a
+ * pointer to the on-disk inode within that buffer.
+ *
+ * If a non-zero error is returned, then the contents of bpp and dipp are
+ * undefined.
  */
-STATIC int
+int
 xfs_imap_to_bp(
-       xfs_mount_t     *mp,
-       xfs_trans_t     *tp,
-       struct xfs_imap *imap,
-       xfs_buf_t       **bpp,
-       uint            buf_flags,
-       uint            iget_flags)
+       struct xfs_mount        *mp,
+       struct xfs_trans        *tp,
+       struct xfs_imap         *imap,
+       struct xfs_dinode       **dipp,
+       struct xfs_buf          **bpp,
+       uint                    buf_flags,
+       uint                    iget_flags)
 {
-       int             error;
-       int             i;
-       int             ni;
-       xfs_buf_t       *bp;
+       struct xfs_buf          *bp;
+       int                     error;
+       int                     i;
+       int                     ni;
 
        buf_flags |= XBF_UNMAPPED;
        error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, imap->im_blkno,
@@ -189,8 +194,8 @@ xfs_imap_to_bp(
                                xfs_trans_brelse(tp, bp);
                                return XFS_ERROR(EINVAL);
                        }
-                       XFS_CORRUPTION_ERROR("xfs_imap_to_bp",
-                                               XFS_ERRLEVEL_HIGH, mp, dip);
+                       XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_HIGH,
+                                            mp, dip);
 #ifdef DEBUG
                        xfs_emerg(mp,
                                "bad inode magic/vsn daddr %lld #%d (magic=%x)",
@@ -204,96 +209,9 @@ xfs_imap_to_bp(
        }
 
        xfs_inobp_check(mp, bp);
-       *bpp = bp;
-       return 0;
-}
-
-/*
- * This routine is called to map an inode number within a file
- * system to the buffer containing the on-disk version of the
- * inode.  It returns a pointer to the buffer containing the
- * on-disk inode in the bpp parameter, and in the dip parameter
- * it returns a pointer to the on-disk inode within that buffer.
- *
- * If a non-zero error is returned, then the contents of bpp and
- * dipp are undefined.
- *
- * Use xfs_imap() to determine the size and location of the
- * buffer to read from disk.
- */
-int
-xfs_inotobp(
-       xfs_mount_t     *mp,
-       xfs_trans_t     *tp,
-       xfs_ino_t       ino,
-       xfs_dinode_t    **dipp,
-       xfs_buf_t       **bpp,
-       int             *offset,
-       uint            imap_flags)
-{
-       struct xfs_imap imap;
-       xfs_buf_t       *bp;
-       int             error;
-
-       imap.im_blkno = 0;
-       error = xfs_imap(mp, tp, ino, &imap, imap_flags);
-       if (error)
-               return error;
-
-       error = xfs_imap_to_bp(mp, tp, &imap, &bp, 0, imap_flags);
-       if (error)
-               return error;
-
-       *dipp = (xfs_dinode_t *)xfs_buf_offset(bp, imap.im_boffset);
-       *bpp = bp;
-       *offset = imap.im_boffset;
-       return 0;
-}
-
-
-/*
- * This routine is called to map an inode to the buffer containing
- * the on-disk version of the inode.  It returns a pointer to the
- * buffer containing the on-disk inode in the bpp parameter, and in
- * the dip parameter it returns a pointer to the on-disk inode within
- * that buffer.
- *
- * If a non-zero error is returned, then the contents of bpp and
- * dipp are undefined.
- *
- * The inode is expected to already been mapped to its buffer and read
- * in once, thus we can use the mapping information stored in the inode
- * rather than calling xfs_imap().  This allows us to avoid the overhead
- * of looking at the inode btree for small block file systems
- * (see xfs_imap()).
- */
-int
-xfs_itobp(
-       xfs_mount_t     *mp,
-       xfs_trans_t     *tp,
-       xfs_inode_t     *ip,
-       xfs_dinode_t    **dipp,
-       xfs_buf_t       **bpp,
-       uint            buf_flags)
-{
-       xfs_buf_t       *bp;
-       int             error;
-
-       ASSERT(ip->i_imap.im_blkno != 0);
-
-       error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &bp, buf_flags, 0);
-       if (error)
-               return error;
 
-       if (!bp) {
-               ASSERT(buf_flags & XBF_TRYLOCK);
-               ASSERT(tp == NULL);
-               *bpp = NULL;
-               return EAGAIN;
-       }
-
-       *dipp = (xfs_dinode_t *)xfs_buf_offset(bp, ip->i_imap.im_boffset);
        *bpp = bp;
+       *dipp = (struct xfs_dinode *)xfs_buf_offset(bp, imap->im_boffset);
        return 0;
 }
 
@@ -796,10 +714,9 @@ xfs_iread(
        /*
         * Get pointers to the on-disk inode and the buffer containing it.
         */
-       error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &bp, 0, iget_flags);
+       error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &bp, 0, iget_flags);
        if (error)
                return error;
-       dip = (xfs_dinode_t *)xfs_buf_offset(bp, ip->i_imap.im_boffset);
 
        /*
         * If we got something that isn't an inode it means someone
@@ -876,7 +793,7 @@ xfs_iread(
        /*
         * Use xfs_trans_brelse() to release the buffer containing the
         * on-disk inode, because it was acquired with xfs_trans_read_buf()
-        * in xfs_itobp() above.  If tp is NULL, this is just a normal
+        * in xfs_imap_to_bp() above.  If tp is NULL, this is just a normal
         * brelse().  If we're within a transaction, then xfs_trans_brelse()
         * will only release the buffer if it is not dirty within the
         * transaction.  It will be OK to release the buffer in this case,
@@ -970,7 +887,6 @@ xfs_ialloc(
        prid_t          prid,
        int             okalloc,
        xfs_buf_t       **ialloc_context,
-       boolean_t       *call_again,
        xfs_inode_t     **ipp)
 {
        xfs_ino_t       ino;
@@ -985,10 +901,10 @@ xfs_ialloc(
         * the on-disk inode to be allocated.
         */
        error = xfs_dialloc(tp, pip ? pip->i_ino : 0, mode, okalloc,
-                           ialloc_context, call_again, &ino);
+                           ialloc_context, &ino);
        if (error)
                return error;
-       if (*call_again || ino == NULLFSINO) {
+       if (*ialloc_context || ino == NULLFSINO) {
                *ipp = NULL;
                return 0;
        }
@@ -1207,7 +1123,9 @@ xfs_itruncate_extents(
        int                     error = 0;
        int                     done = 0;
 
-       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_IOLOCK_EXCL));
+       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
+       ASSERT(!atomic_read(&VFS_I(ip)->i_count) ||
+              xfs_isilocked(ip, XFS_IOLOCK_EXCL));
        ASSERT(new_size <= XFS_ISIZE(ip));
        ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
        ASSERT(ip->i_itemp != NULL);
@@ -1226,7 +1144,7 @@ xfs_itruncate_extents(
         * then there is nothing to do.
         */
        first_unmap_block = XFS_B_TO_FSB(mp, (xfs_ufsize_t)new_size);
-       last_block = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp));
+       last_block = XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes);
        if (first_unmap_block == last_block)
                return 0;
 
@@ -1355,7 +1273,8 @@ xfs_iunlink(
                 * Here we put the head pointer into our next pointer,
                 * and then we fall through to point the head at us.
                 */
-               error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0);
+               error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp,
+                                      0, 0);
                if (error)
                        return error;
 
@@ -1429,16 +1348,16 @@ xfs_iunlink_remove(
 
        if (be32_to_cpu(agi->agi_unlinked[bucket_index]) == agino) {
                /*
-                * We're at the head of the list.  Get the inode's
-                * on-disk buffer to see if there is anyone after us
-                * on the list.  Only modify our next pointer if it
-                * is not already NULLAGINO.  This saves us the overhead
-                * of dealing with the buffer when there is no need to
-                * change it.
+                * We're at the head of the list.  Get the inode's on-disk
+                * buffer to see if there is anyone after us on the list.
+                * Only modify our next pointer if it is not already NULLAGINO.
+                * This saves us the overhead of dealing with the buffer when
+                * there is no need to change it.
                 */
-               error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0);
+               error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp,
+                                      0, 0);
                if (error) {
-                       xfs_warn(mp, "%s: xfs_itobp() returned error %d.",
+                       xfs_warn(mp, "%s: xfs_imap_to_bp returned error %d.",
                                __func__, error);
                        return error;
                }
@@ -1472,34 +1391,45 @@ xfs_iunlink_remove(
                next_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]);
                last_ibp = NULL;
                while (next_agino != agino) {
-                       /*
-                        * If the last inode wasn't the one pointing to
-                        * us, then release its buffer since we're not
-                        * going to do anything with it.
-                        */
-                       if (last_ibp != NULL) {
+                       struct xfs_imap imap;
+
+                       if (last_ibp)
                                xfs_trans_brelse(tp, last_ibp);
-                       }
+
+                       imap.im_blkno = 0;
                        next_ino = XFS_AGINO_TO_INO(mp, agno, next_agino);
-                       error = xfs_inotobp(mp, tp, next_ino, &last_dip,
-                                           &last_ibp, &last_offset, 0);
+
+                       error = xfs_imap(mp, tp, next_ino, &imap, 0);
+                       if (error) {
+                               xfs_warn(mp,
+       "%s: xfs_imap returned error %d.",
+                                        __func__, error);
+                               return error;
+                       }
+
+                       error = xfs_imap_to_bp(mp, tp, &imap, &last_dip,
+                                              &last_ibp, 0, 0);
                        if (error) {
                                xfs_warn(mp,
-                                       "%s: xfs_inotobp() returned error %d.",
+       "%s: xfs_imap_to_bp returned error %d.",
                                        __func__, error);
                                return error;
                        }
+
+                       last_offset = imap.im_boffset;
                        next_agino = be32_to_cpu(last_dip->di_next_unlinked);
                        ASSERT(next_agino != NULLAGINO);
                        ASSERT(next_agino != 0);
                }
+
                /*
-                * Now last_ibp points to the buffer previous to us on
-                * the unlinked list.  Pull us from the list.
+                * Now last_ibp points to the buffer previous to us on the
+                * unlinked list.  Pull us from the list.
                 */
-               error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0);
+               error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp,
+                                      0, 0);
                if (error) {
-                       xfs_warn(mp, "%s: xfs_itobp(2) returned error %d.",
+                       xfs_warn(mp, "%s: xfs_imap_to_bp(2) returned error %d.",
                                __func__, error);
                        return error;
                }
@@ -1749,7 +1679,8 @@ xfs_ifree(
 
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 
-       error = xfs_itobp(ip->i_mount, tp, ip, &dip, &ibp, 0);
+       error = xfs_imap_to_bp(ip->i_mount, tp, &ip->i_imap, &dip, &ibp,
+                              0, 0);
        if (error)
                return error;
 
@@ -2428,7 +2359,7 @@ xfs_iflush(
        /*
         * For stale inodes we cannot rely on the backing buffer remaining
         * stale in cache for the remaining life of the stale inode and so
-        * xfs_itobp() below may give us a buffer that no longer contains
+        * xfs_imap_to_bp() below may give us a buffer that no longer contains
         * inodes below. We have to check this after ensuring the inode is
         * unpinned so that it is safe to reclaim the stale inode after the
         * flush call.
@@ -2454,7 +2385,8 @@ xfs_iflush(
        /*
         * Get the buffer containing the on-disk inode.
         */
-       error = xfs_itobp(mp, NULL, ip, &dip, &bp, XBF_TRYLOCK);
+       error = xfs_imap_to_bp(mp, NULL, &ip->i_imap, &dip, &bp, XBF_TRYLOCK,
+                              0);
        if (error || !bp) {
                xfs_ifunlock(ip);
                return error;
index 1efff36a75b6961150ec8fb342d6fd47994eb79a..94b32f906e7903a32cc1e1474f3d4a6fed267f93 100644 (file)
@@ -487,8 +487,6 @@ static inline int xfs_isiflocked(struct xfs_inode *ip)
 #define XFS_IOLOCK_DEP(flags)  (((flags) & XFS_IOLOCK_DEP_MASK) >> XFS_IOLOCK_SHIFT)
 #define XFS_ILOCK_DEP(flags)   (((flags) & XFS_ILOCK_DEP_MASK) >> XFS_ILOCK_SHIFT)
 
-extern struct lock_class_key xfs_iolock_reclaimable;
-
 /*
  * For multiple groups support: if S_ISGID bit is set in the parent
  * directory, group of new file is set to that of the parent, and
@@ -517,7 +515,7 @@ void                xfs_inode_free(struct xfs_inode *ip);
  */
 int            xfs_ialloc(struct xfs_trans *, xfs_inode_t *, umode_t,
                           xfs_nlink_t, xfs_dev_t, prid_t, int,
-                          struct xfs_buf **, boolean_t *, xfs_inode_t **);
+                          struct xfs_buf **, xfs_inode_t **);
 
 uint           xfs_ip2xflags(struct xfs_inode *);
 uint           xfs_dic2xflags(struct xfs_dinode *);
@@ -557,12 +555,9 @@ do { \
 #define XFS_IGET_UNTRUSTED     0x2
 #define XFS_IGET_DONTCACHE     0x4
 
-int            xfs_inotobp(struct xfs_mount *, struct xfs_trans *,
-                           xfs_ino_t, struct xfs_dinode **,
-                           struct xfs_buf **, int *, uint);
-int            xfs_itobp(struct xfs_mount *, struct xfs_trans *,
-                         struct xfs_inode *, struct xfs_dinode **,
-                         struct xfs_buf **, uint);
+int            xfs_imap_to_bp(struct xfs_mount *, struct xfs_trans *,
+                              struct xfs_imap *, struct xfs_dinode **,
+                              struct xfs_buf **, uint, uint);
 int            xfs_iread(struct xfs_mount *, struct xfs_trans *,
                          struct xfs_inode *, uint);
 void           xfs_dinode_to_disk(struct xfs_dinode *,
index 1f1535d25a9bb726b96742fa82c59dec74183f51..0e0232c3b6d98039eb2b73fb556faec1b9069016 100644 (file)
@@ -364,9 +364,15 @@ xfs_fssetdm_by_handle(
        if (copy_from_user(&dmhreq, arg, sizeof(xfs_fsop_setdm_handlereq_t)))
                return -XFS_ERROR(EFAULT);
 
+       error = mnt_want_write_file(parfilp);
+       if (error)
+               return error;
+
        dentry = xfs_handlereq_to_dentry(parfilp, &dmhreq.hreq);
-       if (IS_ERR(dentry))
+       if (IS_ERR(dentry)) {
+               mnt_drop_write_file(parfilp);
                return PTR_ERR(dentry);
+       }
 
        if (IS_IMMUTABLE(dentry->d_inode) || IS_APPEND(dentry->d_inode)) {
                error = -XFS_ERROR(EPERM);
@@ -382,6 +388,7 @@ xfs_fssetdm_by_handle(
                                 fsd.fsd_dmstate);
 
  out:
+       mnt_drop_write_file(parfilp);
        dput(dentry);
        return error;
 }
@@ -634,7 +641,11 @@ xfs_ioc_space(
        if (ioflags & IO_INVIS)
                attr_flags |= XFS_ATTR_DMI;
 
+       error = mnt_want_write_file(filp);
+       if (error)
+               return error;
        error = xfs_change_file_space(ip, cmd, bf, filp->f_pos, attr_flags);
+       mnt_drop_write_file(filp);
        return -error;
 }
 
@@ -1163,6 +1174,7 @@ xfs_ioc_fssetxattr(
 {
        struct fsxattr          fa;
        unsigned int            mask;
+       int error;
 
        if (copy_from_user(&fa, arg, sizeof(fa)))
                return -EFAULT;
@@ -1171,7 +1183,12 @@ xfs_ioc_fssetxattr(
        if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
                mask |= FSX_NONBLOCK;
 
-       return -xfs_ioctl_setattr(ip, &fa, mask);
+       error = mnt_want_write_file(filp);
+       if (error)
+               return error;
+       error = xfs_ioctl_setattr(ip, &fa, mask);
+       mnt_drop_write_file(filp);
+       return -error;
 }
 
 STATIC int
@@ -1196,6 +1213,7 @@ xfs_ioc_setxflags(
        struct fsxattr          fa;
        unsigned int            flags;
        unsigned int            mask;
+       int error;
 
        if (copy_from_user(&flags, arg, sizeof(flags)))
                return -EFAULT;
@@ -1210,7 +1228,12 @@ xfs_ioc_setxflags(
                mask |= FSX_NONBLOCK;
        fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip));
 
-       return -xfs_ioctl_setattr(ip, &fa, mask);
+       error = mnt_want_write_file(filp);
+       if (error)
+               return error;
+       error = xfs_ioctl_setattr(ip, &fa, mask);
+       mnt_drop_write_file(filp);
+       return -error;
 }
 
 STATIC int
@@ -1385,8 +1408,13 @@ xfs_file_ioctl(
                if (copy_from_user(&dmi, arg, sizeof(dmi)))
                        return -XFS_ERROR(EFAULT);
 
+               error = mnt_want_write_file(filp);
+               if (error)
+                       return error;
+
                error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
                                dmi.fsd_dmstate);
+               mnt_drop_write_file(filp);
                return -error;
        }
 
@@ -1434,7 +1462,11 @@ xfs_file_ioctl(
 
                if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
                        return -XFS_ERROR(EFAULT);
+               error = mnt_want_write_file(filp);
+               if (error)
+                       return error;
                error = xfs_swapext(&sxp);
+               mnt_drop_write_file(filp);
                return -error;
        }
 
@@ -1463,9 +1495,14 @@ xfs_file_ioctl(
                if (copy_from_user(&inout, arg, sizeof(inout)))
                        return -XFS_ERROR(EFAULT);
 
+               error = mnt_want_write_file(filp);
+               if (error)
+                       return error;
+
                /* input parameter is passed in resblks field of structure */
                in = inout.resblks;
                error = xfs_reserve_blocks(mp, &in, &inout);
+               mnt_drop_write_file(filp);
                if (error)
                        return -error;
 
@@ -1496,7 +1533,11 @@ xfs_file_ioctl(
                if (copy_from_user(&in, arg, sizeof(in)))
                        return -XFS_ERROR(EFAULT);
 
+               error = mnt_want_write_file(filp);
+               if (error)
+                       return error;
                error = xfs_growfs_data(mp, &in);
+               mnt_drop_write_file(filp);
                return -error;
        }
 
@@ -1506,7 +1547,11 @@ xfs_file_ioctl(
                if (copy_from_user(&in, arg, sizeof(in)))
                        return -XFS_ERROR(EFAULT);
 
+               error = mnt_want_write_file(filp);
+               if (error)
+                       return error;
                error = xfs_growfs_log(mp, &in);
+               mnt_drop_write_file(filp);
                return -error;
        }
 
@@ -1516,7 +1561,11 @@ xfs_file_ioctl(
                if (copy_from_user(&in, arg, sizeof(in)))
                        return -XFS_ERROR(EFAULT);
 
+               error = mnt_want_write_file(filp);
+               if (error)
+                       return error;
                error = xfs_growfs_rt(mp, &in);
+               mnt_drop_write_file(filp);
                return -error;
        }
 
index c4f2da0d2bf5ef1ec4f04aafe1a58034d5255375..1244274a56741404b36e0afc4cfc7d5d7f043ac7 100644 (file)
@@ -600,7 +600,11 @@ xfs_file_compat_ioctl(
 
                if (xfs_compat_growfs_data_copyin(&in, arg))
                        return -XFS_ERROR(EFAULT);
+               error = mnt_want_write_file(filp);
+               if (error)
+                       return error;
                error = xfs_growfs_data(mp, &in);
+               mnt_drop_write_file(filp);
                return -error;
        }
        case XFS_IOC_FSGROWFSRT_32: {
@@ -608,7 +612,11 @@ xfs_file_compat_ioctl(
 
                if (xfs_compat_growfs_rt_copyin(&in, arg))
                        return -XFS_ERROR(EFAULT);
+               error = mnt_want_write_file(filp);
+               if (error)
+                       return error;
                error = xfs_growfs_rt(mp, &in);
+               mnt_drop_write_file(filp);
                return -error;
        }
 #endif
@@ -627,7 +635,11 @@ xfs_file_compat_ioctl(
                                   offsetof(struct xfs_swapext, sx_stat)) ||
                    xfs_ioctl32_bstat_copyin(&sxp.sx_stat, &sxu->sx_stat))
                        return -XFS_ERROR(EFAULT);
+               error = mnt_want_write_file(filp);
+               if (error)
+                       return error;
                error = xfs_swapext(&sxp);
+               mnt_drop_write_file(filp);
                return -error;
        }
        case XFS_IOC_FSBULKSTAT_32:
index aadfce6681ee0d501b5c7df3c65abb712b2333bb..973dff6ad93526292871d3452d758e86b3f077ac 100644 (file)
@@ -285,7 +285,7 @@ xfs_iomap_eof_want_preallocate(
         * do any speculative allocation.
         */
        start_fsb = XFS_B_TO_FSBT(mp, ((xfs_ufsize_t)(offset + count - 1)));
-       count_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp));
+       count_fsb = XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes);
        while (count_fsb > 0) {
                imaps = nimaps;
                firstblock = NULLFSBLOCK;
@@ -416,8 +416,8 @@ retry:
         * Make sure preallocation does not create extents beyond the range we
         * actually support in this filesystem.
         */
-       if (last_fsb > XFS_B_TO_FSB(mp, mp->m_maxioffset))
-               last_fsb = XFS_B_TO_FSB(mp, mp->m_maxioffset);
+       if (last_fsb > XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes))
+               last_fsb = XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes);
 
        ASSERT(last_fsb > offset_fsb);
 
@@ -680,9 +680,9 @@ xfs_iomap_write_unwritten(
                 * the same inode that we complete here and might deadlock
                 * on the iolock.
                 */
-               xfs_wait_for_freeze(mp, SB_FREEZE_TRANS);
+               sb_start_intwrite(mp->m_super);
                tp = _xfs_trans_alloc(mp, XFS_TRANS_STRAT_WRITE, KM_NOFS);
-               tp->t_flags |= XFS_TRANS_RESERVE;
+               tp->t_flags |= XFS_TRANS_RESERVE | XFS_TRANS_FREEZE_PROT;
                error = xfs_trans_reserve(tp, resblks,
                                XFS_WRITE_LOG_RES(mp), 0,
                                XFS_TRANS_PERM_LOG_RES,
index 9c4340f5c3e0ff92bb84505155c3ba4f9a4bac54..4e00cf091d2ccac6b0a9e65113a67b2b5169f21f 100644 (file)
@@ -897,6 +897,47 @@ xfs_vn_setattr(
        return -xfs_setattr_nonsize(XFS_I(dentry->d_inode), iattr, 0);
 }
 
+STATIC int
+xfs_vn_update_time(
+       struct inode            *inode,
+       struct timespec         *now,
+       int                     flags)
+{
+       struct xfs_inode        *ip = XFS_I(inode);
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp;
+       int                     error;
+
+       trace_xfs_update_time(ip);
+
+       tp = xfs_trans_alloc(mp, XFS_TRANS_FSYNC_TS);
+       error = xfs_trans_reserve(tp, 0, XFS_FSYNC_TS_LOG_RES(mp), 0, 0, 0);
+       if (error) {
+               xfs_trans_cancel(tp, 0);
+               return -error;
+       }
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       if (flags & S_CTIME) {
+               inode->i_ctime = *now;
+               ip->i_d.di_ctime.t_sec = (__int32_t)now->tv_sec;
+               ip->i_d.di_ctime.t_nsec = (__int32_t)now->tv_nsec;
+       }
+       if (flags & S_MTIME) {
+               inode->i_mtime = *now;
+               ip->i_d.di_mtime.t_sec = (__int32_t)now->tv_sec;
+               ip->i_d.di_mtime.t_nsec = (__int32_t)now->tv_nsec;
+       }
+       if (flags & S_ATIME) {
+               inode->i_atime = *now;
+               ip->i_d.di_atime.t_sec = (__int32_t)now->tv_sec;
+               ip->i_d.di_atime.t_nsec = (__int32_t)now->tv_nsec;
+       }
+       xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+       xfs_trans_log_inode(tp, ip, XFS_ILOG_TIMESTAMP);
+       return -xfs_trans_commit(tp, 0);
+}
+
 #define XFS_FIEMAP_FLAGS       (FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)
 
 /*
@@ -991,6 +1032,7 @@ static const struct inode_operations xfs_inode_operations = {
        .removexattr            = generic_removexattr,
        .listxattr              = xfs_vn_listxattr,
        .fiemap                 = xfs_vn_fiemap,
+       .update_time            = xfs_vn_update_time,
 };
 
 static const struct inode_operations xfs_dir_inode_operations = {
@@ -1016,6 +1058,7 @@ static const struct inode_operations xfs_dir_inode_operations = {
        .getxattr               = generic_getxattr,
        .removexattr            = generic_removexattr,
        .listxattr              = xfs_vn_listxattr,
+       .update_time            = xfs_vn_update_time,
 };
 
 static const struct inode_operations xfs_dir_ci_inode_operations = {
@@ -1041,6 +1084,7 @@ static const struct inode_operations xfs_dir_ci_inode_operations = {
        .getxattr               = generic_getxattr,
        .removexattr            = generic_removexattr,
        .listxattr              = xfs_vn_listxattr,
+       .update_time            = xfs_vn_update_time,
 };
 
 static const struct inode_operations xfs_symlink_inode_operations = {
@@ -1054,6 +1098,7 @@ static const struct inode_operations xfs_symlink_inode_operations = {
        .getxattr               = generic_getxattr,
        .removexattr            = generic_removexattr,
        .listxattr              = xfs_vn_listxattr,
+       .update_time            = xfs_vn_update_time,
 };
 
 STATIC void
index eff577a9b67ffe0d47f7d1834b5776d76122ba40..01d10a66e30243518d293f2c72a29b6135f7977b 100644 (file)
@@ -555,7 +555,7 @@ xfs_bulkstat_single(
 
        /*
         * note that requesting valid inode numbers which are not allocated
-        * to inodes will most likely cause xfs_itobp to generate warning
+        * to inodes will most likely cause xfs_imap_to_bp to generate warning
         * messages about bad magic numbers. This is ok. The fact that
         * the inode isn't actually an inode is handled by the
         * error check below. Done this way to make the usual case faster
index d90d4a388609af9da0cd40eb786a1fdd225a8d62..7f4f9370d0e7438df3820fab9b0026b37292623b 100644 (file)
@@ -45,51 +45,85 @@ xlog_commit_record(
        struct xlog_in_core     **iclog,
        xfs_lsn_t               *commitlsnp);
 
-STATIC xlog_t *  xlog_alloc_log(xfs_mount_t    *mp,
-                               xfs_buftarg_t   *log_target,
-                               xfs_daddr_t     blk_offset,
-                               int             num_bblks);
+STATIC struct xlog *
+xlog_alloc_log(
+       struct xfs_mount        *mp,
+       struct xfs_buftarg      *log_target,
+       xfs_daddr_t             blk_offset,
+       int                     num_bblks);
 STATIC int
 xlog_space_left(
        struct xlog             *log,
        atomic64_t              *head);
-STATIC int      xlog_sync(xlog_t *log, xlog_in_core_t *iclog);
-STATIC void     xlog_dealloc_log(xlog_t *log);
+STATIC int
+xlog_sync(
+       struct xlog             *log,
+       struct xlog_in_core     *iclog);
+STATIC void
+xlog_dealloc_log(
+       struct xlog             *log);
 
 /* local state machine functions */
 STATIC void xlog_state_done_syncing(xlog_in_core_t *iclog, int);
-STATIC void xlog_state_do_callback(xlog_t *log,int aborted, xlog_in_core_t *iclog);
-STATIC int  xlog_state_get_iclog_space(xlog_t          *log,
-                                      int              len,
-                                      xlog_in_core_t   **iclog,
-                                      xlog_ticket_t    *ticket,
-                                      int              *continued_write,
-                                      int              *logoffsetp);
-STATIC int  xlog_state_release_iclog(xlog_t            *log,
-                                    xlog_in_core_t     *iclog);
-STATIC void xlog_state_switch_iclogs(xlog_t            *log,
-                                    xlog_in_core_t *iclog,
-                                    int                eventual_size);
-STATIC void xlog_state_want_sync(xlog_t        *log, xlog_in_core_t *iclog);
+STATIC void
+xlog_state_do_callback(
+       struct xlog             *log,
+       int                     aborted,
+       struct xlog_in_core     *iclog);
+STATIC int
+xlog_state_get_iclog_space(
+       struct xlog             *log,
+       int                     len,
+       struct xlog_in_core     **iclog,
+       struct xlog_ticket      *ticket,
+       int                     *continued_write,
+       int                     *logoffsetp);
+STATIC int
+xlog_state_release_iclog(
+       struct xlog             *log,
+       struct xlog_in_core     *iclog);
+STATIC void
+xlog_state_switch_iclogs(
+       struct xlog             *log,
+       struct xlog_in_core     *iclog,
+       int                     eventual_size);
+STATIC void
+xlog_state_want_sync(
+       struct xlog             *log,
+       struct xlog_in_core     *iclog);
 
 STATIC void
 xlog_grant_push_ail(
-       struct xlog     *log,
-       int             need_bytes);
-STATIC void xlog_regrant_reserve_log_space(xlog_t       *log,
-                                          xlog_ticket_t *ticket);
-STATIC void xlog_ungrant_log_space(xlog_t       *log,
-                                  xlog_ticket_t *ticket);
+       struct xlog             *log,
+       int                     need_bytes);
+STATIC void
+xlog_regrant_reserve_log_space(
+       struct xlog             *log,
+       struct xlog_ticket      *ticket);
+STATIC void
+xlog_ungrant_log_space(
+       struct xlog             *log,
+       struct xlog_ticket      *ticket);
 
 #if defined(DEBUG)
-STATIC void    xlog_verify_dest_ptr(xlog_t *log, char *ptr);
+STATIC void
+xlog_verify_dest_ptr(
+       struct xlog             *log,
+       char                    *ptr);
 STATIC void
 xlog_verify_grant_tail(
-       struct xlog     *log);
-STATIC void    xlog_verify_iclog(xlog_t *log, xlog_in_core_t *iclog,
-                                 int count, boolean_t syncing);
-STATIC void    xlog_verify_tail_lsn(xlog_t *log, xlog_in_core_t *iclog,
-                                    xfs_lsn_t tail_lsn);
+       struct xlog *log);
+STATIC void
+xlog_verify_iclog(
+       struct xlog             *log,
+       struct xlog_in_core     *iclog,
+       int                     count,
+       boolean_t               syncing);
+STATIC void
+xlog_verify_tail_lsn(
+       struct xlog             *log,
+       struct xlog_in_core     *iclog,
+       xfs_lsn_t               tail_lsn);
 #else
 #define xlog_verify_dest_ptr(a,b)
 #define xlog_verify_grant_tail(a)
@@ -97,7 +131,9 @@ STATIC void  xlog_verify_tail_lsn(xlog_t *log, xlog_in_core_t *iclog,
 #define xlog_verify_tail_lsn(a,b,c)
 #endif
 
-STATIC int     xlog_iclogs_empty(xlog_t *log);
+STATIC int
+xlog_iclogs_empty(
+       struct xlog             *log);
 
 static void
 xlog_grant_sub_space(
@@ -684,7 +720,7 @@ xfs_log_mount_finish(xfs_mount_t *mp)
 int
 xfs_log_unmount_write(xfs_mount_t *mp)
 {
-       xlog_t           *log = mp->m_log;
+       struct xlog      *log = mp->m_log;
        xlog_in_core_t   *iclog;
 #ifdef DEBUG
        xlog_in_core_t   *first_iclog;
@@ -893,7 +929,7 @@ int
 xfs_log_need_covered(xfs_mount_t *mp)
 {
        int             needed = 0;
-       xlog_t          *log = mp->m_log;
+       struct xlog     *log = mp->m_log;
 
        if (!xfs_fs_writable(mp))
                return 0;
@@ -1024,9 +1060,9 @@ xlog_space_left(
 void
 xlog_iodone(xfs_buf_t *bp)
 {
-       xlog_in_core_t  *iclog = bp->b_fspriv;
-       xlog_t          *l = iclog->ic_log;
-       int             aborted = 0;
+       struct xlog_in_core     *iclog = bp->b_fspriv;
+       struct xlog             *l = iclog->ic_log;
+       int                     aborted = 0;
 
        /*
         * Race to shutdown the filesystem if we see an error.
@@ -1067,8 +1103,9 @@ xlog_iodone(xfs_buf_t *bp)
  */
 
 STATIC void
-xlog_get_iclog_buffer_size(xfs_mount_t *mp,
-                          xlog_t       *log)
+xlog_get_iclog_buffer_size(
+       struct xfs_mount        *mp,
+       struct xlog             *log)
 {
        int size;
        int xhdrs;
@@ -1129,13 +1166,14 @@ done:
  * Its primary purpose is to fill in enough, so recovery can occur.  However,
  * some other stuff may be filled in too.
  */
-STATIC xlog_t *
-xlog_alloc_log(xfs_mount_t     *mp,
-              xfs_buftarg_t    *log_target,
-              xfs_daddr_t      blk_offset,
-              int              num_bblks)
+STATIC struct xlog *
+xlog_alloc_log(
+       struct xfs_mount        *mp,
+       struct xfs_buftarg      *log_target,
+       xfs_daddr_t             blk_offset,
+       int                     num_bblks)
 {
-       xlog_t                  *log;
+       struct xlog             *log;
        xlog_rec_header_t       *head;
        xlog_in_core_t          **iclogp;
        xlog_in_core_t          *iclog, *prev_iclog=NULL;
@@ -1144,7 +1182,7 @@ xlog_alloc_log(xfs_mount_t        *mp,
        int                     error = ENOMEM;
        uint                    log2_size = 0;
 
-       log = kmem_zalloc(sizeof(xlog_t), KM_MAYFAIL);
+       log = kmem_zalloc(sizeof(struct xlog), KM_MAYFAIL);
        if (!log) {
                xfs_warn(mp, "Log allocation failed: No memory!");
                goto out;
@@ -1434,8 +1472,9 @@ xlog_bdstrat(
  */
 
 STATIC int
-xlog_sync(xlog_t               *log,
-         xlog_in_core_t        *iclog)
+xlog_sync(
+       struct xlog             *log,
+       struct xlog_in_core     *iclog)
 {
        xfs_caddr_t     dptr;           /* pointer to byte sized element */
        xfs_buf_t       *bp;
@@ -1584,7 +1623,8 @@ xlog_sync(xlog_t          *log,
  * Deallocate a log structure
  */
 STATIC void
-xlog_dealloc_log(xlog_t *log)
+xlog_dealloc_log(
+       struct xlog     *log)
 {
        xlog_in_core_t  *iclog, *next_iclog;
        int             i;
@@ -1616,10 +1656,11 @@ xlog_dealloc_log(xlog_t *log)
  */
 /* ARGSUSED */
 static inline void
-xlog_state_finish_copy(xlog_t          *log,
-                      xlog_in_core_t   *iclog,
-                      int              record_cnt,
-                      int              copy_bytes)
+xlog_state_finish_copy(
+       struct xlog             *log,
+       struct xlog_in_core     *iclog,
+       int                     record_cnt,
+       int                     copy_bytes)
 {
        spin_lock(&log->l_icloglock);
 
@@ -2142,7 +2183,8 @@ xlog_write(
  * State Change: DIRTY -> ACTIVE
  */
 STATIC void
-xlog_state_clean_log(xlog_t *log)
+xlog_state_clean_log(
+       struct xlog *log)
 {
        xlog_in_core_t  *iclog;
        int changed = 0;
@@ -2222,7 +2264,7 @@ xlog_state_clean_log(xlog_t *log)
 
 STATIC xfs_lsn_t
 xlog_get_lowest_lsn(
-       xlog_t          *log)
+       struct xlog     *log)
 {
        xlog_in_core_t  *lsn_log;
        xfs_lsn_t       lowest_lsn, lsn;
@@ -2245,9 +2287,9 @@ xlog_get_lowest_lsn(
 
 STATIC void
 xlog_state_do_callback(
-       xlog_t          *log,
-       int             aborted,
-       xlog_in_core_t  *ciclog)
+       struct xlog             *log,
+       int                     aborted,
+       struct xlog_in_core     *ciclog)
 {
        xlog_in_core_t     *iclog;
        xlog_in_core_t     *first_iclog;        /* used to know when we've
@@ -2467,7 +2509,7 @@ xlog_state_done_syncing(
        xlog_in_core_t  *iclog,
        int             aborted)
 {
-       xlog_t             *log = iclog->ic_log;
+       struct xlog        *log = iclog->ic_log;
 
        spin_lock(&log->l_icloglock);
 
@@ -2521,12 +2563,13 @@ xlog_state_done_syncing(
  *             is copied.
  */
 STATIC int
-xlog_state_get_iclog_space(xlog_t        *log,
-                          int            len,
-                          xlog_in_core_t **iclogp,
-                          xlog_ticket_t  *ticket,
-                          int            *continued_write,
-                          int            *logoffsetp)
+xlog_state_get_iclog_space(
+       struct xlog             *log,
+       int                     len,
+       struct xlog_in_core     **iclogp,
+       struct xlog_ticket      *ticket,
+       int                     *continued_write,
+       int                     *logoffsetp)
 {
        int               log_offset;
        xlog_rec_header_t *head;
@@ -2631,8 +2674,9 @@ restart:
  * move grant reservation head forward.
  */
 STATIC void
-xlog_regrant_reserve_log_space(xlog_t       *log,
-                              xlog_ticket_t *ticket)
+xlog_regrant_reserve_log_space(
+       struct xlog             *log,
+       struct xlog_ticket      *ticket)
 {
        trace_xfs_log_regrant_reserve_enter(log, ticket);
 
@@ -2677,8 +2721,9 @@ xlog_regrant_reserve_log_space(xlog_t          *log,
  * in the current reservation field.
  */
 STATIC void
-xlog_ungrant_log_space(xlog_t       *log,
-                      xlog_ticket_t *ticket)
+xlog_ungrant_log_space(
+       struct xlog             *log,
+       struct xlog_ticket      *ticket)
 {
        int     bytes;
 
@@ -2717,8 +2762,8 @@ xlog_ungrant_log_space(xlog_t          *log,
  */
 STATIC int
 xlog_state_release_iclog(
-       xlog_t          *log,
-       xlog_in_core_t  *iclog)
+       struct xlog             *log,
+       struct xlog_in_core     *iclog)
 {
        int             sync = 0;       /* do we sync? */
 
@@ -2768,9 +2813,10 @@ xlog_state_release_iclog(
  * that every data block.  We have run out of space in this log record.
  */
 STATIC void
-xlog_state_switch_iclogs(xlog_t                *log,
-                        xlog_in_core_t *iclog,
-                        int            eventual_size)
+xlog_state_switch_iclogs(
+       struct xlog             *log,
+       struct xlog_in_core     *iclog,
+       int                     eventual_size)
 {
        ASSERT(iclog->ic_state == XLOG_STATE_ACTIVE);
        if (!eventual_size)
@@ -3114,7 +3160,9 @@ xfs_log_force_lsn(
  * disk.
  */
 STATIC void
-xlog_state_want_sync(xlog_t *log, xlog_in_core_t *iclog)
+xlog_state_want_sync(
+       struct xlog             *log,
+       struct xlog_in_core     *iclog)
 {
        assert_spin_locked(&log->l_icloglock);
 
@@ -3158,7 +3206,7 @@ xfs_log_ticket_get(
 /*
  * Allocate and initialise a new log ticket.
  */
-xlog_ticket_t *
+struct xlog_ticket *
 xlog_ticket_alloc(
        struct xlog     *log,
        int             unit_bytes,
@@ -3346,9 +3394,10 @@ xlog_verify_grant_tail(
 
 /* check if it will fit */
 STATIC void
-xlog_verify_tail_lsn(xlog_t        *log,
-                    xlog_in_core_t *iclog,
-                    xfs_lsn_t      tail_lsn)
+xlog_verify_tail_lsn(
+       struct xlog             *log,
+       struct xlog_in_core     *iclog,
+       xfs_lsn_t               tail_lsn)
 {
     int blocks;
 
@@ -3385,10 +3434,11 @@ xlog_verify_tail_lsn(xlog_t         *log,
  *     the cycle numbers agree with the current cycle number.
  */
 STATIC void
-xlog_verify_iclog(xlog_t        *log,
-                 xlog_in_core_t *iclog,
-                 int            count,
-                 boolean_t      syncing)
+xlog_verify_iclog(
+       struct xlog             *log,
+       struct xlog_in_core     *iclog,
+       int                     count,
+       boolean_t               syncing)
 {
        xlog_op_header_t        *ophead;
        xlog_in_core_t          *icptr;
@@ -3482,7 +3532,7 @@ xlog_verify_iclog(xlog_t   *log,
  */
 STATIC int
 xlog_state_ioerror(
-       xlog_t  *log)
+       struct xlog     *log)
 {
        xlog_in_core_t  *iclog, *ic;
 
@@ -3527,7 +3577,7 @@ xfs_log_force_umount(
        struct xfs_mount        *mp,
        int                     logerror)
 {
-       xlog_t          *log;
+       struct xlog     *log;
        int             retval;
 
        log = mp->m_log;
@@ -3634,7 +3684,8 @@ xfs_log_force_umount(
 }
 
 STATIC int
-xlog_iclogs_empty(xlog_t *log)
+xlog_iclogs_empty(
+       struct xlog     *log)
 {
        xlog_in_core_t  *iclog;
 
index 72eba2201b1449aee4d52f5c0863549c9aedf7e9..18a801d76a4234e550427a83b6d6a65b7092d670 100644 (file)
@@ -487,7 +487,7 @@ struct xlog_grant_head {
  * overflow 31 bits worth of byte offset, so using a byte number will mean
  * that round off problems won't occur when releasing partial reservations.
  */
-typedef struct xlog {
+struct xlog {
        /* The following fields don't need locking */
        struct xfs_mount        *l_mp;          /* mount point */
        struct xfs_ail          *l_ailp;        /* AIL log is working with */
@@ -540,7 +540,7 @@ typedef struct xlog {
        char                    *l_iclog_bak[XLOG_MAX_ICLOGS];
 #endif
 
-} xlog_t;
+};
 
 #define XLOG_BUF_CANCEL_BUCKET(log, blkno) \
        ((log)->l_buf_cancel_table + ((__uint64_t)blkno % XLOG_BC_TABLE_SIZE))
@@ -548,9 +548,17 @@ typedef struct xlog {
 #define XLOG_FORCED_SHUTDOWN(log)      ((log)->l_flags & XLOG_IO_ERROR)
 
 /* common routines */
-extern int      xlog_recover(xlog_t *log);
-extern int      xlog_recover_finish(xlog_t *log);
-extern void     xlog_pack_data(xlog_t *log, xlog_in_core_t *iclog, int);
+extern int
+xlog_recover(
+       struct xlog             *log);
+extern int
+xlog_recover_finish(
+       struct xlog             *log);
+extern void
+xlog_pack_data(
+       struct xlog             *log,
+       struct xlog_in_core     *iclog,
+       int);
 
 extern kmem_zone_t *xfs_log_ticket_zone;
 struct xlog_ticket *
index a7be98abd6a90327ae9a0993ef65f2bc7f005e1c..5da3ace352bffe6ca32d16ae76fc131291914a00 100644 (file)
 #include "xfs_utils.h"
 #include "xfs_trace.h"
 
-STATIC int     xlog_find_zeroed(xlog_t *, xfs_daddr_t *);
-STATIC int     xlog_clear_stale_blocks(xlog_t *, xfs_lsn_t);
+STATIC int
+xlog_find_zeroed(
+       struct xlog     *,
+       xfs_daddr_t     *);
+STATIC int
+xlog_clear_stale_blocks(
+       struct xlog     *,
+       xfs_lsn_t);
 #if defined(DEBUG)
-STATIC void    xlog_recover_check_summary(xlog_t *);
+STATIC void
+xlog_recover_check_summary(
+       struct xlog *);
 #else
 #define        xlog_recover_check_summary(log)
 #endif
@@ -74,7 +82,7 @@ struct xfs_buf_cancel {
 
 static inline int
 xlog_buf_bbcount_valid(
-       xlog_t          *log,
+       struct xlog     *log,
        int             bbcount)
 {
        return bbcount > 0 && bbcount <= log->l_logBBsize;
@@ -87,7 +95,7 @@ xlog_buf_bbcount_valid(
  */
 STATIC xfs_buf_t *
 xlog_get_bp(
-       xlog_t          *log,
+       struct xlog     *log,
        int             nbblks)
 {
        struct xfs_buf  *bp;
@@ -138,10 +146,10 @@ xlog_put_bp(
  */
 STATIC xfs_caddr_t
 xlog_align(
-       xlog_t          *log,
+       struct xlog     *log,
        xfs_daddr_t     blk_no,
        int             nbblks,
-       xfs_buf_t       *bp)
+       struct xfs_buf  *bp)
 {
        xfs_daddr_t     offset = blk_no & ((xfs_daddr_t)log->l_sectBBsize - 1);
 
@@ -155,10 +163,10 @@ xlog_align(
  */
 STATIC int
 xlog_bread_noalign(
-       xlog_t          *log,
+       struct xlog     *log,
        xfs_daddr_t     blk_no,
        int             nbblks,
-       xfs_buf_t       *bp)
+       struct xfs_buf  *bp)
 {
        int             error;
 
@@ -189,10 +197,10 @@ xlog_bread_noalign(
 
 STATIC int
 xlog_bread(
-       xlog_t          *log,
+       struct xlog     *log,
        xfs_daddr_t     blk_no,
        int             nbblks,
-       xfs_buf_t       *bp,
+       struct xfs_buf  *bp,
        xfs_caddr_t     *offset)
 {
        int             error;
@@ -211,10 +219,10 @@ xlog_bread(
  */
 STATIC int
 xlog_bread_offset(
-       xlog_t          *log,
+       struct xlog     *log,
        xfs_daddr_t     blk_no,         /* block to read from */
        int             nbblks,         /* blocks to read */
-       xfs_buf_t       *bp,
+       struct xfs_buf  *bp,
        xfs_caddr_t     offset)
 {
        xfs_caddr_t     orig_offset = bp->b_addr;
@@ -241,10 +249,10 @@ xlog_bread_offset(
  */
 STATIC int
 xlog_bwrite(
-       xlog_t          *log,
+       struct xlog     *log,
        xfs_daddr_t     blk_no,
        int             nbblks,
-       xfs_buf_t       *bp)
+       struct xfs_buf  *bp)
 {
        int             error;
 
@@ -378,8 +386,8 @@ xlog_recover_iodone(
  */
 STATIC int
 xlog_find_cycle_start(
-       xlog_t          *log,
-       xfs_buf_t       *bp,
+       struct xlog     *log,
+       struct xfs_buf  *bp,
        xfs_daddr_t     first_blk,
        xfs_daddr_t     *last_blk,
        uint            cycle)
@@ -421,7 +429,7 @@ xlog_find_cycle_start(
  */
 STATIC int
 xlog_find_verify_cycle(
-       xlog_t          *log,
+       struct xlog     *log,
        xfs_daddr_t     start_blk,
        int             nbblks,
        uint            stop_on_cycle_no,
@@ -490,7 +498,7 @@ out:
  */
 STATIC int
 xlog_find_verify_log_record(
-       xlog_t                  *log,
+       struct xlog             *log,
        xfs_daddr_t             start_blk,
        xfs_daddr_t             *last_blk,
        int                     extra_bblks)
@@ -600,7 +608,7 @@ out:
  */
 STATIC int
 xlog_find_head(
-       xlog_t          *log,
+       struct xlog     *log,
        xfs_daddr_t     *return_head_blk)
 {
        xfs_buf_t       *bp;
@@ -871,7 +879,7 @@ validate_head:
  */
 STATIC int
 xlog_find_tail(
-       xlog_t                  *log,
+       struct xlog             *log,
        xfs_daddr_t             *head_blk,
        xfs_daddr_t             *tail_blk)
 {
@@ -1080,7 +1088,7 @@ done:
  */
 STATIC int
 xlog_find_zeroed(
-       xlog_t          *log,
+       struct xlog     *log,
        xfs_daddr_t     *blk_no)
 {
        xfs_buf_t       *bp;
@@ -1183,7 +1191,7 @@ bp_err:
  */
 STATIC void
 xlog_add_record(
-       xlog_t                  *log,
+       struct xlog             *log,
        xfs_caddr_t             buf,
        int                     cycle,
        int                     block,
@@ -1205,7 +1213,7 @@ xlog_add_record(
 
 STATIC int
 xlog_write_log_records(
-       xlog_t          *log,
+       struct xlog     *log,
        int             cycle,
        int             start_block,
        int             blocks,
@@ -1305,7 +1313,7 @@ xlog_write_log_records(
  */
 STATIC int
 xlog_clear_stale_blocks(
-       xlog_t          *log,
+       struct xlog     *log,
        xfs_lsn_t       tail_lsn)
 {
        int             tail_cycle, head_cycle;
@@ -2050,11 +2058,11 @@ xfs_qm_dqcheck(
  */
 STATIC void
 xlog_recover_do_dquot_buffer(
-       xfs_mount_t             *mp,
-       xlog_t                  *log,
-       xlog_recover_item_t     *item,
-       xfs_buf_t               *bp,
-       xfs_buf_log_format_t    *buf_f)
+       struct xfs_mount                *mp,
+       struct xlog                     *log,
+       struct xlog_recover_item        *item,
+       struct xfs_buf                  *bp,
+       struct xfs_buf_log_format       *buf_f)
 {
        uint                    type;
 
@@ -2108,9 +2116,9 @@ xlog_recover_do_dquot_buffer(
  */
 STATIC int
 xlog_recover_buffer_pass2(
-       xlog_t                  *log,
-       struct list_head        *buffer_list,
-       xlog_recover_item_t     *item)
+       struct xlog                     *log,
+       struct list_head                *buffer_list,
+       struct xlog_recover_item        *item)
 {
        xfs_buf_log_format_t    *buf_f = item->ri_buf[0].i_addr;
        xfs_mount_t             *mp = log->l_mp;
@@ -2189,9 +2197,9 @@ xlog_recover_buffer_pass2(
 
 STATIC int
 xlog_recover_inode_pass2(
-       xlog_t                  *log,
-       struct list_head        *buffer_list,
-       xlog_recover_item_t     *item)
+       struct xlog                     *log,
+       struct list_head                *buffer_list,
+       struct xlog_recover_item        *item)
 {
        xfs_inode_log_format_t  *in_f;
        xfs_mount_t             *mp = log->l_mp;
@@ -2452,14 +2460,14 @@ error:
 }
 
 /*
- * Recover QUOTAOFF records. We simply make a note of it in the xlog_t
+ * Recover QUOTAOFF records. We simply make a note of it in the xlog
  * structure, so that we know not to do any dquot item or dquot buffer recovery,
  * of that type.
  */
 STATIC int
 xlog_recover_quotaoff_pass1(
-       xlog_t                  *log,
-       xlog_recover_item_t     *item)
+       struct xlog                     *log,
+       struct xlog_recover_item        *item)
 {
        xfs_qoff_logformat_t    *qoff_f = item->ri_buf[0].i_addr;
        ASSERT(qoff_f);
@@ -2483,9 +2491,9 @@ xlog_recover_quotaoff_pass1(
  */
 STATIC int
 xlog_recover_dquot_pass2(
-       xlog_t                  *log,
-       struct list_head        *buffer_list,
-       xlog_recover_item_t     *item)
+       struct xlog                     *log,
+       struct list_head                *buffer_list,
+       struct xlog_recover_item        *item)
 {
        xfs_mount_t             *mp = log->l_mp;
        xfs_buf_t               *bp;
@@ -2578,9 +2586,9 @@ xlog_recover_dquot_pass2(
  */
 STATIC int
 xlog_recover_efi_pass2(
-       xlog_t                  *log,
-       xlog_recover_item_t     *item,
-       xfs_lsn_t               lsn)
+       struct xlog                     *log,
+       struct xlog_recover_item        *item,
+       xfs_lsn_t                       lsn)
 {
        int                     error;
        xfs_mount_t             *mp = log->l_mp;
@@ -2616,8 +2624,8 @@ xlog_recover_efi_pass2(
  */
 STATIC int
 xlog_recover_efd_pass2(
-       xlog_t                  *log,
-       xlog_recover_item_t     *item)
+       struct xlog                     *log,
+       struct xlog_recover_item        *item)
 {
        xfs_efd_log_format_t    *efd_formatp;
        xfs_efi_log_item_t      *efip = NULL;
@@ -2812,9 +2820,9 @@ xlog_recover_unmount_trans(
  */
 STATIC int
 xlog_recover_process_data(
-       xlog_t                  *log,
+       struct xlog             *log,
        struct hlist_head       rhash[],
-       xlog_rec_header_t       *rhead,
+       struct xlog_rec_header  *rhead,
        xfs_caddr_t             dp,
        int                     pass)
 {
@@ -2986,7 +2994,7 @@ abort_error:
  */
 STATIC int
 xlog_recover_process_efis(
-       xlog_t                  *log)
+       struct xlog     *log)
 {
        xfs_log_item_t          *lip;
        xfs_efi_log_item_t      *efip;
@@ -3098,7 +3106,7 @@ xlog_recover_process_one_iunlink(
        /*
         * Get the on disk inode to find the next inode in the bucket.
         */
-       error = xfs_itobp(mp, NULL, ip, &dip, &ibp, 0);
+       error = xfs_imap_to_bp(mp, NULL, &ip->i_imap, &dip, &ibp, 0, 0);
        if (error)
                goto fail_iput;
 
@@ -3147,7 +3155,7 @@ xlog_recover_process_one_iunlink(
  */
 STATIC void
 xlog_recover_process_iunlinks(
-       xlog_t          *log)
+       struct xlog     *log)
 {
        xfs_mount_t     *mp;
        xfs_agnumber_t  agno;
@@ -3209,9 +3217,9 @@ xlog_recover_process_iunlinks(
 #ifdef DEBUG
 STATIC void
 xlog_pack_data_checksum(
-       xlog_t          *log,
-       xlog_in_core_t  *iclog,
-       int             size)
+       struct xlog             *log,
+       struct xlog_in_core     *iclog,
+       int                     size)
 {
        int             i;
        __be32          *up;
@@ -3234,8 +3242,8 @@ xlog_pack_data_checksum(
  */
 void
 xlog_pack_data(
-       xlog_t                  *log,
-       xlog_in_core_t          *iclog,
+       struct xlog             *log,
+       struct xlog_in_core     *iclog,
        int                     roundoff)
 {
        int                     i, j, k;
@@ -3274,9 +3282,9 @@ xlog_pack_data(
 
 STATIC void
 xlog_unpack_data(
-       xlog_rec_header_t       *rhead,
+       struct xlog_rec_header  *rhead,
        xfs_caddr_t             dp,
-       xlog_t                  *log)
+       struct xlog             *log)
 {
        int                     i, j, k;
 
@@ -3299,8 +3307,8 @@ xlog_unpack_data(
 
 STATIC int
 xlog_valid_rec_header(
-       xlog_t                  *log,
-       xlog_rec_header_t       *rhead,
+       struct xlog             *log,
+       struct xlog_rec_header  *rhead,
        xfs_daddr_t             blkno)
 {
        int                     hlen;
@@ -3343,7 +3351,7 @@ xlog_valid_rec_header(
  */
 STATIC int
 xlog_do_recovery_pass(
-       xlog_t                  *log,
+       struct xlog             *log,
        xfs_daddr_t             head_blk,
        xfs_daddr_t             tail_blk,
        int                     pass)
@@ -3595,7 +3603,7 @@ xlog_do_recovery_pass(
  */
 STATIC int
 xlog_do_log_recovery(
-       xlog_t          *log,
+       struct xlog     *log,
        xfs_daddr_t     head_blk,
        xfs_daddr_t     tail_blk)
 {
@@ -3646,7 +3654,7 @@ xlog_do_log_recovery(
  */
 STATIC int
 xlog_do_recover(
-       xlog_t          *log,
+       struct xlog     *log,
        xfs_daddr_t     head_blk,
        xfs_daddr_t     tail_blk)
 {
@@ -3721,7 +3729,7 @@ xlog_do_recover(
  */
 int
 xlog_recover(
-       xlog_t          *log)
+       struct xlog     *log)
 {
        xfs_daddr_t     head_blk, tail_blk;
        int             error;
@@ -3767,7 +3775,7 @@ xlog_recover(
  */
 int
 xlog_recover_finish(
-       xlog_t          *log)
+       struct xlog     *log)
 {
        /*
         * Now we're ready to do the transactions needed for the
@@ -3814,7 +3822,7 @@ xlog_recover_finish(
  */
 void
 xlog_recover_check_summary(
-       xlog_t          *log)
+       struct xlog     *log)
 {
        xfs_mount_t     *mp;
        xfs_agf_t       *agfp;
index 536021fb3d4e338a0e694ac069191c0b0d4d0cf0..29c2f83d4147c6824f8e8b67f5a9b4968c80c92b 100644 (file)
@@ -1200,8 +1200,6 @@ xfs_mountfs(
 
        xfs_set_maxicount(mp);
 
-       mp->m_maxioffset = xfs_max_file_offset(sbp->sb_blocklog);
-
        error = xfs_uuid_mount(mp);
        if (error)
                goto out;
@@ -1531,6 +1529,15 @@ xfs_unmountfs(
        xfs_ail_push_all_sync(mp->m_ail);
        xfs_wait_buftarg(mp->m_ddev_targp);
 
+       /*
+        * The superblock buffer is uncached and xfsaild_push() will lock and
+        * set the XBF_ASYNC flag on the buffer. We cannot do xfs_buf_iowait()
+        * here but a lock on the superblock buffer will block until iodone()
+        * has completed.
+        */
+       xfs_buf_lock(mp->m_sb_bp);
+       xfs_buf_unlock(mp->m_sb_bp);
+
        xfs_log_unmount_write(mp);
        xfs_log_unmount(mp);
        xfs_uuid_unmount(mp);
@@ -1544,7 +1551,7 @@ xfs_unmountfs(
 int
 xfs_fs_writable(xfs_mount_t *mp)
 {
-       return !(xfs_test_for_freeze(mp) || XFS_FORCED_SHUTDOWN(mp) ||
+       return !(mp->m_super->s_writers.frozen || XFS_FORCED_SHUTDOWN(mp) ||
                (mp->m_flags & XFS_MOUNT_RDONLY));
 }
 
index 90c1fc9eaea4d9c7be48c5ef219e7598f6e47f54..05a05a7b611963f03d0249a97cb5edbc50c152a6 100644 (file)
@@ -176,7 +176,6 @@ typedef struct xfs_mount {
        uint                    m_qflags;       /* quota status flags */
        xfs_trans_reservations_t m_reservations;/* precomputed res values */
        __uint64_t              m_maxicount;    /* maximum inode count */
-       __uint64_t              m_maxioffset;   /* maximum inode offset */
        __uint64_t              m_resblks;      /* total reserved blocks */
        __uint64_t              m_resblks_avail;/* available reserved blocks */
        __uint64_t              m_resblks_save; /* reserved blks @ remount,ro */
@@ -297,8 +296,6 @@ xfs_preferred_iosize(xfs_mount_t *mp)
                        PAGE_CACHE_SIZE));
 }
 
-#define XFS_MAXIOFFSET(mp)     ((mp)->m_maxioffset)
-
 #define XFS_LAST_UNMOUNT_WAS_CLEAN(mp) \
                                ((mp)->m_flags & XFS_MOUNT_WAS_CLEAN)
 #define XFS_FORCED_SHUTDOWN(mp)        ((mp)->m_flags & XFS_MOUNT_FS_SHUTDOWN)
@@ -314,9 +311,6 @@ void xfs_do_force_shutdown(struct xfs_mount *mp, int flags, char *fname,
 #define SHUTDOWN_REMOTE_REQ    0x0010  /* shutdown came from remote cell */
 #define SHUTDOWN_DEVICE_REQ    0x0020  /* failed all paths to the device */
 
-#define xfs_test_for_freeze(mp)                ((mp)->m_super->s_frozen)
-#define xfs_wait_for_freeze(mp,l)      vfs_check_frozen((mp)->m_super, (l))
-
 /*
  * Flags for xfs_mountfs
  */
index 249db1987764586c37c3b6d75a2fd1c78cae3c48..2e86fa0cfc0d660374b2d8860f7c30e059b94e2b 100644 (file)
@@ -940,7 +940,7 @@ xfs_qm_dqiterate(
        map = kmem_alloc(XFS_DQITER_MAP_SIZE * sizeof(*map), KM_SLEEP);
 
        lblkno = 0;
-       maxlblkcnt = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp));
+       maxlblkcnt = XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes);
        do {
                nmaps = XFS_DQITER_MAP_SIZE;
                /*
index 0d9de41a7151568621cfad89a1a95a97b2ba8b4a..bdaf4cb9f4a2d19fc8b781f232f61d88c529ac2a 100644 (file)
@@ -868,67 +868,14 @@ xfs_fs_inode_init_once(
                     "xfsino", ip->i_ino);
 }
 
-/*
- * This is called by the VFS when dirtying inode metadata.  This can happen
- * for a few reasons, but we only care about timestamp updates, given that
- * we handled the rest ourselves.  In theory no other calls should happen,
- * but for example generic_write_end() keeps dirtying the inode after
- * updating i_size.  Thus we check that the flags are exactly I_DIRTY_SYNC,
- * and skip this call otherwise.
- *
- * We'll hopefull get a different method just for updating timestamps soon,
- * at which point this hack can go away, and maybe we'll also get real
- * error handling here.
- */
-STATIC void
-xfs_fs_dirty_inode(
-       struct inode            *inode,
-       int                     flags)
-{
-       struct xfs_inode        *ip = XFS_I(inode);
-       struct xfs_mount        *mp = ip->i_mount;
-       struct xfs_trans        *tp;
-       int                     error;
-
-       if (flags != I_DIRTY_SYNC)
-               return;
-
-       trace_xfs_dirty_inode(ip);
-
-       tp = xfs_trans_alloc(mp, XFS_TRANS_FSYNC_TS);
-       error = xfs_trans_reserve(tp, 0, XFS_FSYNC_TS_LOG_RES(mp), 0, 0, 0);
-       if (error) {
-               xfs_trans_cancel(tp, 0);
-               goto trouble;
-       }
-       xfs_ilock(ip, XFS_ILOCK_EXCL);
-       /*
-        * Grab all the latest timestamps from the Linux inode.
-        */
-       ip->i_d.di_atime.t_sec = (__int32_t)inode->i_atime.tv_sec;
-       ip->i_d.di_atime.t_nsec = (__int32_t)inode->i_atime.tv_nsec;
-       ip->i_d.di_ctime.t_sec = (__int32_t)inode->i_ctime.tv_sec;
-       ip->i_d.di_ctime.t_nsec = (__int32_t)inode->i_ctime.tv_nsec;
-       ip->i_d.di_mtime.t_sec = (__int32_t)inode->i_mtime.tv_sec;
-       ip->i_d.di_mtime.t_nsec = (__int32_t)inode->i_mtime.tv_nsec;
-
-       xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
-       xfs_trans_log_inode(tp, ip, XFS_ILOG_TIMESTAMP);
-       error = xfs_trans_commit(tp, 0);
-       if (error)
-               goto trouble;
-       return;
-
-trouble:
-       xfs_warn(mp, "failed to update timestamps for inode 0x%llx", ip->i_ino);
-}
-
 STATIC void
 xfs_fs_evict_inode(
        struct inode            *inode)
 {
        xfs_inode_t             *ip = XFS_I(inode);
 
+       ASSERT(!rwsem_is_locked(&ip->i_iolock.mr_lock));
+
        trace_xfs_evict_inode(ip);
 
        truncate_inode_pages(&inode->i_data, 0);
@@ -937,22 +884,6 @@ xfs_fs_evict_inode(
        XFS_STATS_INC(vn_remove);
        XFS_STATS_DEC(vn_active);
 
-       /*
-        * The iolock is used by the file system to coordinate reads,
-        * writes, and block truncates.  Up to this point the lock
-        * protected concurrent accesses by users of the inode.  But
-        * from here forward we're doing some final processing of the
-        * inode because we're done with it, and although we reuse the
-        * iolock for protection it is really a distinct lock class
-        * (in the lockdep sense) from before.  To keep lockdep happy
-        * (and basically indicate what we are doing), we explicitly
-        * re-init the iolock here.
-        */
-       ASSERT(!rwsem_is_locked(&ip->i_iolock.mr_lock));
-       mrlock_init(&ip->i_iolock, MRLOCK_BARRIER, "xfsio", ip->i_ino);
-       lockdep_set_class_and_name(&ip->i_iolock.mr_lock,
-                       &xfs_iolock_reclaimable, "xfs_iolock_reclaimable");
-
        xfs_inactive(ip);
 }
 
@@ -1436,7 +1367,6 @@ xfs_fs_free_cached_objects(
 static const struct super_operations xfs_super_operations = {
        .alloc_inode            = xfs_fs_alloc_inode,
        .destroy_inode          = xfs_fs_destroy_inode,
-       .dirty_inode            = xfs_fs_dirty_inode,
        .evict_inode            = xfs_fs_evict_inode,
        .drop_inode             = xfs_fs_drop_inode,
        .put_super              = xfs_fs_put_super,
@@ -1491,13 +1421,9 @@ xfs_init_zones(void)
        if (!xfs_da_state_zone)
                goto out_destroy_btree_cur_zone;
 
-       xfs_dabuf_zone = kmem_zone_init(sizeof(xfs_dabuf_t), "xfs_dabuf");
-       if (!xfs_dabuf_zone)
-               goto out_destroy_da_state_zone;
-
        xfs_ifork_zone = kmem_zone_init(sizeof(xfs_ifork_t), "xfs_ifork");
        if (!xfs_ifork_zone)
-               goto out_destroy_dabuf_zone;
+               goto out_destroy_da_state_zone;
 
        xfs_trans_zone = kmem_zone_init(sizeof(xfs_trans_t), "xfs_trans");
        if (!xfs_trans_zone)
@@ -1514,9 +1440,8 @@ xfs_init_zones(void)
         * size possible under XFS.  This wastes a little bit of memory,
         * but it is much faster.
         */
-       xfs_buf_item_zone = kmem_zone_init((sizeof(xfs_buf_log_item_t) +
-                               (((XFS_MAX_BLOCKSIZE / XFS_BLF_CHUNK) /
-                                 NBWORD) * sizeof(int))), "xfs_buf_item");
+       xfs_buf_item_zone = kmem_zone_init(sizeof(struct xfs_buf_log_item),
+                                          "xfs_buf_item");
        if (!xfs_buf_item_zone)
                goto out_destroy_log_item_desc_zone;
 
@@ -1561,8 +1486,6 @@ xfs_init_zones(void)
        kmem_zone_destroy(xfs_trans_zone);
  out_destroy_ifork_zone:
        kmem_zone_destroy(xfs_ifork_zone);
- out_destroy_dabuf_zone:
-       kmem_zone_destroy(xfs_dabuf_zone);
  out_destroy_da_state_zone:
        kmem_zone_destroy(xfs_da_state_zone);
  out_destroy_btree_cur_zone:
@@ -1590,7 +1513,6 @@ xfs_destroy_zones(void)
        kmem_zone_destroy(xfs_log_item_desc_zone);
        kmem_zone_destroy(xfs_trans_zone);
        kmem_zone_destroy(xfs_ifork_zone);
-       kmem_zone_destroy(xfs_dabuf_zone);
        kmem_zone_destroy(xfs_da_state_zone);
        kmem_zone_destroy(xfs_btree_cur_zone);
        kmem_zone_destroy(xfs_bmap_free_item_zone);
index 1e9ee064dbb28c7cb491d4c41dce53007eebc7e9..96548176db80c1ee4557190e5d21d32d8c347de3 100644 (file)
@@ -359,6 +359,15 @@ xfs_quiesce_attr(
         * added an item to the AIL, thus flush it again.
         */
        xfs_ail_push_all_sync(mp->m_ail);
+
+       /*
+        * The superblock buffer is uncached and xfsaild_push() will lock and
+        * set the XBF_ASYNC flag on the buffer. We cannot do xfs_buf_iowait()
+        * here but a lock on the superblock buffer will block until iodone()
+        * has completed.
+        */
+       xfs_buf_lock(mp->m_sb_bp);
+       xfs_buf_unlock(mp->m_sb_bp);
 }
 
 static void
@@ -394,7 +403,7 @@ xfs_sync_worker(
        if (!(mp->m_super->s_flags & MS_ACTIVE) &&
            !(mp->m_flags & XFS_MOUNT_RDONLY)) {
                /* dgc: errors ignored here */
-               if (mp->m_super->s_frozen == SB_UNFROZEN &&
+               if (mp->m_super->s_writers.frozen == SB_UNFROZEN &&
                    xfs_log_need_covered(mp))
                        error = xfs_fs_log_dummy(mp);
                else
@@ -712,8 +721,8 @@ restart:
         * Note that xfs_iflush will never block on the inode buffer lock, as
         * xfs_ifree_cluster() can lock the inode buffer before it locks the
         * ip->i_lock, and we are doing the exact opposite here.  As a result,
-        * doing a blocking xfs_itobp() to get the cluster buffer would result
-        * in an ABBA deadlock with xfs_ifree_cluster().
+        * doing a blocking xfs_imap_to_bp() to get the cluster buffer would
+        * result in an ABBA deadlock with xfs_ifree_cluster().
         *
         * As xfs_ifree_cluser() must gather all inodes that are active in the
         * cache to mark them stale, if we hit this case we don't actually want
index caf5dabfd55347b292664e86654f04bbe62d7a98..e5795dd6013ad2b34d5a208d1cf7ad448c937387 100644 (file)
@@ -578,8 +578,8 @@ DEFINE_INODE_EVENT(xfs_ioctl_setattr);
 DEFINE_INODE_EVENT(xfs_dir_fsync);
 DEFINE_INODE_EVENT(xfs_file_fsync);
 DEFINE_INODE_EVENT(xfs_destroy_inode);
-DEFINE_INODE_EVENT(xfs_dirty_inode);
 DEFINE_INODE_EVENT(xfs_evict_inode);
+DEFINE_INODE_EVENT(xfs_update_time);
 
 DEFINE_INODE_EVENT(xfs_dquot_dqalloc);
 DEFINE_INODE_EVENT(xfs_dquot_dqdetach);
index fdf324508c5ee467c6055f0866e1be88c387942d..06ed520a767f99f06c43cd7cb30055d009b87ebd 100644 (file)
@@ -576,8 +576,12 @@ xfs_trans_alloc(
        xfs_mount_t     *mp,
        uint            type)
 {
-       xfs_wait_for_freeze(mp, SB_FREEZE_TRANS);
-       return _xfs_trans_alloc(mp, type, KM_SLEEP);
+       xfs_trans_t     *tp;
+
+       sb_start_intwrite(mp->m_super);
+       tp = _xfs_trans_alloc(mp, type, KM_SLEEP);
+       tp->t_flags |= XFS_TRANS_FREEZE_PROT;
+       return tp;
 }
 
 xfs_trans_t *
@@ -588,6 +592,7 @@ _xfs_trans_alloc(
 {
        xfs_trans_t     *tp;
 
+       WARN_ON(mp->m_super->s_writers.frozen == SB_FREEZE_COMPLETE);
        atomic_inc(&mp->m_active_trans);
 
        tp = kmem_zone_zalloc(xfs_trans_zone, memflags);
@@ -611,6 +616,8 @@ xfs_trans_free(
        xfs_extent_busy_clear(tp->t_mountp, &tp->t_busy, false);
 
        atomic_dec(&tp->t_mountp->m_active_trans);
+       if (tp->t_flags & XFS_TRANS_FREEZE_PROT)
+               sb_end_intwrite(tp->t_mountp->m_super);
        xfs_trans_free_dqinfo(tp);
        kmem_zone_free(xfs_trans_zone, tp);
 }
@@ -643,7 +650,11 @@ xfs_trans_dup(
        ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
        ASSERT(tp->t_ticket != NULL);
 
-       ntp->t_flags = XFS_TRANS_PERM_LOG_RES | (tp->t_flags & XFS_TRANS_RESERVE);
+       ntp->t_flags = XFS_TRANS_PERM_LOG_RES |
+                      (tp->t_flags & XFS_TRANS_RESERVE) |
+                      (tp->t_flags & XFS_TRANS_FREEZE_PROT);
+       /* We gave our writer reference to the new transaction */
+       tp->t_flags &= ~XFS_TRANS_FREEZE_PROT;
        ntp->t_ticket = xfs_log_ticket_get(tp->t_ticket);
        ntp->t_blk_res = tp->t_blk_res - tp->t_blk_res_used;
        tp->t_blk_res = tp->t_blk_res_used;
index 7c37b533aa8e5c169f0ef98643f96958df3788df..db056544cbb5ecaa2360d9ac1319f171fbbe1e27 100644 (file)
@@ -179,6 +179,8 @@ struct xfs_log_item_desc {
 #define        XFS_TRANS_SYNC          0x08    /* make commit synchronous */
 #define XFS_TRANS_DQ_DIRTY     0x10    /* at least one dquot in trx dirty */
 #define XFS_TRANS_RESERVE      0x20    /* OK to use reserved data blocks */
+#define XFS_TRANS_FREEZE_PROT  0x40    /* Transaction has elevated writer
+                                          count in superblock */
 
 /*
  * Values for call flags parameter.
@@ -448,11 +450,51 @@ xfs_trans_t       *xfs_trans_dup(xfs_trans_t *);
 int            xfs_trans_reserve(xfs_trans_t *, uint, uint, uint,
                                  uint, uint);
 void           xfs_trans_mod_sb(xfs_trans_t *, uint, int64_t);
-struct xfs_buf *xfs_trans_get_buf(xfs_trans_t *, struct xfs_buftarg *, xfs_daddr_t,
-                                  int, uint);
-int            xfs_trans_read_buf(struct xfs_mount *, xfs_trans_t *,
-                                  struct xfs_buftarg *, xfs_daddr_t, int, uint,
-                                  struct xfs_buf **);
+
+struct xfs_buf *xfs_trans_get_buf_map(struct xfs_trans *tp,
+                                      struct xfs_buftarg *target,
+                                      struct xfs_buf_map *map, int nmaps,
+                                      uint flags);
+
+static inline struct xfs_buf *
+xfs_trans_get_buf(
+       struct xfs_trans        *tp,
+       struct xfs_buftarg      *target,
+       xfs_daddr_t             blkno,
+       int                     numblks,
+       uint                    flags)
+{
+       struct xfs_buf_map      map = {
+               .bm_bn = blkno,
+               .bm_len = numblks,
+       };
+       return xfs_trans_get_buf_map(tp, target, &map, 1, flags);
+}
+
+int            xfs_trans_read_buf_map(struct xfs_mount *mp,
+                                      struct xfs_trans *tp,
+                                      struct xfs_buftarg *target,
+                                      struct xfs_buf_map *map, int nmaps,
+                                      xfs_buf_flags_t flags,
+                                      struct xfs_buf **bpp);
+
+static inline int
+xfs_trans_read_buf(
+       struct xfs_mount        *mp,
+       struct xfs_trans        *tp,
+       struct xfs_buftarg      *target,
+       xfs_daddr_t             blkno,
+       int                     numblks,
+       xfs_buf_flags_t         flags,
+       struct xfs_buf          **bpp)
+{
+       struct xfs_buf_map      map = {
+               .bm_bn = blkno,
+               .bm_len = numblks,
+       };
+       return xfs_trans_read_buf_map(mp, tp, target, &map, 1, flags, bpp);
+}
+
 struct xfs_buf *xfs_trans_getsb(xfs_trans_t *, struct xfs_mount *, int);
 
 void           xfs_trans_brelse(xfs_trans_t *, struct xfs_buf *);
index 9c514483e59924eecb6e64c95bb198af2de5e606..6011ee6613396f9b325418fde28e07c2147f0e5e 100644 (file)
@@ -383,6 +383,12 @@ xfsaild_push(
        }
 
        spin_lock(&ailp->xa_lock);
+
+       /* barrier matches the xa_target update in xfs_ail_push() */
+       smp_rmb();
+       target = ailp->xa_target;
+       ailp->xa_target_prev = target;
+
        lip = xfs_trans_ail_cursor_first(ailp, &cur, ailp->xa_last_pushed_lsn);
        if (!lip) {
                /*
@@ -397,7 +403,6 @@ xfsaild_push(
        XFS_STATS_INC(xs_push_ail);
 
        lsn = lip->li_lsn;
-       target = ailp->xa_target;
        while ((XFS_LSN_CMP(lip->li_lsn, target) <= 0)) {
                int     lock_result;
 
@@ -527,8 +532,32 @@ xfsaild(
                        __set_current_state(TASK_KILLABLE);
                else
                        __set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(tout ?
-                                msecs_to_jiffies(tout) : MAX_SCHEDULE_TIMEOUT);
+
+               spin_lock(&ailp->xa_lock);
+
+               /*
+                * Idle if the AIL is empty and we are not racing with a target
+                * update. We check the AIL after we set the task to a sleep
+                * state to guarantee that we either catch an xa_target update
+                * or that a wake_up resets the state to TASK_RUNNING.
+                * Otherwise, we run the risk of sleeping indefinitely.
+                *
+                * The barrier matches the xa_target update in xfs_ail_push().
+                */
+               smp_rmb();
+               if (!xfs_ail_min(ailp) &&
+                   ailp->xa_target == ailp->xa_target_prev) {
+                       spin_unlock(&ailp->xa_lock);
+                       schedule();
+                       tout = 0;
+                       continue;
+               }
+               spin_unlock(&ailp->xa_lock);
+
+               if (tout)
+                       schedule_timeout(msecs_to_jiffies(tout));
+
+               __set_current_state(TASK_RUNNING);
 
                try_to_freeze();
 
index 21c5a5e3700d066d05b19fa2ac1a47f19e952eb1..6311b99c267f69fe3ac1e7cc4d72a7681e96415d 100644 (file)
@@ -41,20 +41,26 @@ STATIC struct xfs_buf *
 xfs_trans_buf_item_match(
        struct xfs_trans        *tp,
        struct xfs_buftarg      *target,
-       xfs_daddr_t             blkno,
-       int                     len)
+       struct xfs_buf_map      *map,
+       int                     nmaps)
 {
        struct xfs_log_item_desc *lidp;
        struct xfs_buf_log_item *blip;
+       int                     len = 0;
+       int                     i;
+
+       for (i = 0; i < nmaps; i++)
+               len += map[i].bm_len;
 
-       len = BBTOB(len);
        list_for_each_entry(lidp, &tp->t_items, lid_trans) {
                blip = (struct xfs_buf_log_item *)lidp->lid_item;
                if (blip->bli_item.li_type == XFS_LI_BUF &&
                    blip->bli_buf->b_target == target &&
-                   XFS_BUF_ADDR(blip->bli_buf) == blkno &&
-                   BBTOB(blip->bli_buf->b_length) == len)
+                   XFS_BUF_ADDR(blip->bli_buf) == map[0].bm_bn &&
+                   blip->bli_buf->b_length == len) {
+                       ASSERT(blip->bli_buf->b_map_count == nmaps);
                        return blip->bli_buf;
+               }
        }
 
        return NULL;
@@ -128,21 +134,19 @@ xfs_trans_bjoin(
  * If the transaction pointer is NULL, make this just a normal
  * get_buf() call.
  */
-xfs_buf_t *
-xfs_trans_get_buf(xfs_trans_t  *tp,
-                 xfs_buftarg_t *target_dev,
-                 xfs_daddr_t   blkno,
-                 int           len,
-                 uint          flags)
+struct xfs_buf *
+xfs_trans_get_buf_map(
+       struct xfs_trans        *tp,
+       struct xfs_buftarg      *target,
+       struct xfs_buf_map      *map,
+       int                     nmaps,
+       xfs_buf_flags_t         flags)
 {
        xfs_buf_t               *bp;
        xfs_buf_log_item_t      *bip;
 
-       /*
-        * Default to a normal get_buf() call if the tp is NULL.
-        */
-       if (tp == NULL)
-               return xfs_buf_get(target_dev, blkno, len, flags);
+       if (!tp)
+               return xfs_buf_get_map(target, map, nmaps, flags);
 
        /*
         * If we find the buffer in the cache with this transaction
@@ -150,7 +154,7 @@ xfs_trans_get_buf(xfs_trans_t       *tp,
         * have it locked.  In this case we just increment the lock
         * recursion count and return the buffer to the caller.
         */
-       bp = xfs_trans_buf_item_match(tp, target_dev, blkno, len);
+       bp = xfs_trans_buf_item_match(tp, target, map, nmaps);
        if (bp != NULL) {
                ASSERT(xfs_buf_islocked(bp));
                if (XFS_FORCED_SHUTDOWN(tp->t_mountp)) {
@@ -167,7 +171,7 @@ xfs_trans_get_buf(xfs_trans_t       *tp,
                return (bp);
        }
 
-       bp = xfs_buf_get(target_dev, blkno, len, flags);
+       bp = xfs_buf_get_map(target, map, nmaps, flags);
        if (bp == NULL) {
                return NULL;
        }
@@ -246,26 +250,22 @@ int       xfs_error_mod = 33;
  * read_buf() call.
  */
 int
-xfs_trans_read_buf(
-       xfs_mount_t     *mp,
-       xfs_trans_t     *tp,
-       xfs_buftarg_t   *target,
-       xfs_daddr_t     blkno,
-       int             len,
-       uint            flags,
-       xfs_buf_t       **bpp)
+xfs_trans_read_buf_map(
+       struct xfs_mount        *mp,
+       struct xfs_trans        *tp,
+       struct xfs_buftarg      *target,
+       struct xfs_buf_map      *map,
+       int                     nmaps,
+       xfs_buf_flags_t         flags,
+       struct xfs_buf          **bpp)
 {
        xfs_buf_t               *bp;
        xfs_buf_log_item_t      *bip;
        int                     error;
 
        *bpp = NULL;
-
-       /*
-        * Default to a normal get_buf() call if the tp is NULL.
-        */
-       if (tp == NULL) {
-               bp = xfs_buf_read(target, blkno, len, flags);
+       if (!tp) {
+               bp = xfs_buf_read_map(target, map, nmaps, flags);
                if (!bp)
                        return (flags & XBF_TRYLOCK) ?
                                        EAGAIN : XFS_ERROR(ENOMEM);
@@ -303,7 +303,7 @@ xfs_trans_read_buf(
         * If the buffer is not yet read in, then we read it in, increment
         * the lock recursion count, and return it to the caller.
         */
-       bp = xfs_trans_buf_item_match(tp, target, blkno, len);
+       bp = xfs_trans_buf_item_match(tp, target, map, nmaps);
        if (bp != NULL) {
                ASSERT(xfs_buf_islocked(bp));
                ASSERT(bp->b_transp == tp);
@@ -349,7 +349,7 @@ xfs_trans_read_buf(
                return 0;
        }
 
-       bp = xfs_buf_read(target, blkno, len, flags);
+       bp = xfs_buf_read_map(target, map, nmaps, flags);
        if (bp == NULL) {
                *bpp = NULL;
                return (flags & XBF_TRYLOCK) ?
index fb62377d1cbc73305ab84afc360afb6eb24c9d9c..53b7c9b0f8f7a6fa3c96a3f73298c2862eb3d107 100644 (file)
@@ -67,6 +67,7 @@ struct xfs_ail {
        struct task_struct      *xa_task;
        struct list_head        xa_ail;
        xfs_lsn_t               xa_target;
+       xfs_lsn_t               xa_target_prev;
        struct list_head        xa_cursors;
        spinlock_t              xa_lock;
        xfs_lsn_t               xa_last_pushed_lsn;
index 398cf681d025d9c5de3cb189d212acd0224acb7b..7a41874f4c209b7a405486ce123be374efa9c148 100644 (file)
@@ -132,6 +132,20 @@ typedef __uint64_t xfs_filblks_t;  /* number of blocks in a file */
 #define        MAXEXTNUM       ((xfs_extnum_t)0x7fffffff)      /* signed int */
 #define        MAXAEXTNUM      ((xfs_aextnum_t)0x7fff)         /* signed short */
 
+/*
+ * Minimum and maximum blocksize and sectorsize.
+ * The blocksize upper limit is pretty much arbitrary.
+ * The sectorsize upper limit is due to sizeof(sb_sectsize).
+ */
+#define XFS_MIN_BLOCKSIZE_LOG  9       /* i.e. 512 bytes */
+#define XFS_MAX_BLOCKSIZE_LOG  16      /* i.e. 65536 bytes */
+#define XFS_MIN_BLOCKSIZE      (1 << XFS_MIN_BLOCKSIZE_LOG)
+#define XFS_MAX_BLOCKSIZE      (1 << XFS_MAX_BLOCKSIZE_LOG)
+#define XFS_MIN_SECTORSIZE_LOG 9       /* i.e. 512 bytes */
+#define XFS_MAX_SECTORSIZE_LOG 15      /* i.e. 32768 bytes */
+#define XFS_MIN_SECTORSIZE     (1 << XFS_MIN_SECTORSIZE_LOG)
+#define XFS_MAX_SECTORSIZE     (1 << XFS_MAX_SECTORSIZE_LOG)
+
 /*
  * Min numbers of data/attr fork btree root pointers.
  */
index 4e5b9ad5cb97c733332f7d95b321949f1b168996..0025c78ac03cc7fbf44479e080766e5925494b25 100644 (file)
@@ -65,7 +65,6 @@ xfs_dir_ialloc(
        xfs_trans_t     *ntp;
        xfs_inode_t     *ip;
        xfs_buf_t       *ialloc_context = NULL;
-       boolean_t       call_again = B_FALSE;
        int             code;
        uint            log_res;
        uint            log_count;
@@ -91,7 +90,7 @@ xfs_dir_ialloc(
         * the inode(s) that we've just allocated.
         */
        code = xfs_ialloc(tp, dp, mode, nlink, rdev, prid, okalloc,
-                         &ialloc_context, &call_again, &ip);
+                         &ialloc_context, &ip);
 
        /*
         * Return an error if we were unable to allocate a new inode.
@@ -102,19 +101,18 @@ xfs_dir_ialloc(
                *ipp = NULL;
                return code;
        }
-       if (!call_again && (ip == NULL)) {
+       if (!ialloc_context && !ip) {
                *ipp = NULL;
                return XFS_ERROR(ENOSPC);
        }
 
        /*
-        * If call_again is set, then we were unable to get an
+        * If the AGI buffer is non-NULL, then we were unable to get an
         * inode in one operation.  We need to commit the current
         * transaction and call xfs_ialloc() again.  It is guaranteed
         * to succeed the second time.
         */
-       if (call_again) {
-
+       if (ialloc_context) {
                /*
                 * Normally, xfs_trans_commit releases all the locks.
                 * We call bhold to hang on to the ialloc_context across
@@ -195,7 +193,7 @@ xfs_dir_ialloc(
                 * this call should always succeed.
                 */
                code = xfs_ialloc(tp, dp, mode, nlink, rdev, prid,
-                                 okalloc, &ialloc_context, &call_again, &ip);
+                                 okalloc, &ialloc_context, &ip);
 
                /*
                 * If we get an error at this point, return to the caller
@@ -206,12 +204,11 @@ xfs_dir_ialloc(
                        *ipp = NULL;
                        return code;
                }
-               ASSERT ((!call_again) && (ip != NULL));
+               ASSERT(!ialloc_context && ip);
 
        } else {
-               if (committed != NULL) {
+               if (committed != NULL)
                        *committed = 0;
-               }
        }
 
        *ipp = ip;
index b6a82d817a82b67cf8f3791bd1d6509dcd8fd95e..2a5c637344b4f310cd28586eec082d38176cbfcb 100644 (file)
@@ -145,11 +145,6 @@ xfs_readlink(
        return error;
 }
 
-/*
- * Flags for xfs_free_eofblocks
- */
-#define XFS_FREE_EOF_TRYLOCK   (1<<0)
-
 /*
  * This is called by xfs_inactive to free any blocks beyond eof
  * when the link count isn't zero and by xfs_dm_punch_hole() when
@@ -159,7 +154,7 @@ STATIC int
 xfs_free_eofblocks(
        xfs_mount_t     *mp,
        xfs_inode_t     *ip,
-       int             flags)
+       bool            need_iolock)
 {
        xfs_trans_t     *tp;
        int             error;
@@ -174,7 +169,7 @@ xfs_free_eofblocks(
         * of the file.  If not, then there is nothing to do.
         */
        end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_ISIZE(ip));
-       last_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp));
+       last_fsb = XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes);
        if (last_fsb <= end_fsb)
                return 0;
        map_len = last_fsb - end_fsb;
@@ -201,13 +196,11 @@ xfs_free_eofblocks(
                 */
                tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
 
-               if (flags & XFS_FREE_EOF_TRYLOCK) {
+               if (need_iolock) {
                        if (!xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)) {
                                xfs_trans_cancel(tp, 0);
                                return 0;
                        }
-               } else {
-                       xfs_ilock(ip, XFS_IOLOCK_EXCL);
                }
 
                error = xfs_trans_reserve(tp, 0,
@@ -217,7 +210,8 @@ xfs_free_eofblocks(
                if (error) {
                        ASSERT(XFS_FORCED_SHUTDOWN(mp));
                        xfs_trans_cancel(tp, 0);
-                       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+                       if (need_iolock)
+                               xfs_iunlock(ip, XFS_IOLOCK_EXCL);
                        return error;
                }
 
@@ -244,7 +238,10 @@ xfs_free_eofblocks(
                        error = xfs_trans_commit(tp,
                                                XFS_TRANS_RELEASE_LOG_RES);
                }
-               xfs_iunlock(ip, XFS_IOLOCK_EXCL|XFS_ILOCK_EXCL);
+
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+               if (need_iolock)
+                       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
        }
        return error;
 }
@@ -282,23 +279,15 @@ xfs_inactive_symlink_rmt(
         * free them all in one bunmapi call.
         */
        ASSERT(ip->i_d.di_nextents > 0 && ip->i_d.di_nextents <= 2);
-       if ((error = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0,
-                       XFS_TRANS_PERM_LOG_RES, XFS_ITRUNCATE_LOG_COUNT))) {
-               ASSERT(XFS_FORCED_SHUTDOWN(mp));
-               xfs_trans_cancel(tp, 0);
-               *tpp = NULL;
-               return error;
-       }
+
        /*
         * Lock the inode, fix the size, and join it to the transaction.
         * Hold it so in the normal path, we still have it locked for
         * the second transaction.  In the error paths we need it
         * held so the cancel won't rele it, see below.
         */
-       xfs_ilock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
        size = (int)ip->i_d.di_size;
        ip->i_d.di_size = 0;
-       xfs_trans_ijoin(tp, ip, 0);
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
        /*
         * Find the block(s) so we can inval and unmap them.
@@ -385,114 +374,14 @@ xfs_inactive_symlink_rmt(
                ASSERT(XFS_FORCED_SHUTDOWN(mp));
                goto error0;
        }
-       /*
-        * Return with the inode locked but not joined to the transaction.
-        */
+
+       xfs_trans_ijoin(tp, ip, 0);
        *tpp = tp;
        return 0;
 
  error1:
        xfs_bmap_cancel(&free_list);
  error0:
-       /*
-        * Have to come here with the inode locked and either
-        * (held and in the transaction) or (not in the transaction).
-        * If the inode isn't held then cancel would iput it, but
-        * that's wrong since this is inactive and the vnode ref
-        * count is 0 already.
-        * Cancel won't do anything to the inode if held, but it still
-        * needs to be locked until the cancel is done, if it was
-        * joined to the transaction.
-        */
-       xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
-       xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
-       *tpp = NULL;
-       return error;
-
-}
-
-STATIC int
-xfs_inactive_symlink_local(
-       xfs_inode_t     *ip,
-       xfs_trans_t     **tpp)
-{
-       int             error;
-
-       ASSERT(ip->i_d.di_size <= XFS_IFORK_DSIZE(ip));
-       /*
-        * We're freeing a symlink which fit into
-        * the inode.  Just free the memory used
-        * to hold the old symlink.
-        */
-       error = xfs_trans_reserve(*tpp, 0,
-                                 XFS_ITRUNCATE_LOG_RES(ip->i_mount),
-                                 0, XFS_TRANS_PERM_LOG_RES,
-                                 XFS_ITRUNCATE_LOG_COUNT);
-
-       if (error) {
-               xfs_trans_cancel(*tpp, 0);
-               *tpp = NULL;
-               return error;
-       }
-       xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
-
-       /*
-        * Zero length symlinks _can_ exist.
-        */
-       if (ip->i_df.if_bytes > 0) {
-               xfs_idata_realloc(ip,
-                                 -(ip->i_df.if_bytes),
-                                 XFS_DATA_FORK);
-               ASSERT(ip->i_df.if_bytes == 0);
-       }
-       return 0;
-}
-
-STATIC int
-xfs_inactive_attrs(
-       xfs_inode_t     *ip,
-       xfs_trans_t     **tpp)
-{
-       xfs_trans_t     *tp;
-       int             error;
-       xfs_mount_t     *mp;
-
-       ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
-       tp = *tpp;
-       mp = ip->i_mount;
-       ASSERT(ip->i_d.di_forkoff != 0);
-       error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
-       xfs_iunlock(ip, XFS_ILOCK_EXCL);
-       if (error)
-               goto error_unlock;
-
-       error = xfs_attr_inactive(ip);
-       if (error)
-               goto error_unlock;
-
-       tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
-       error = xfs_trans_reserve(tp, 0,
-                                 XFS_IFREE_LOG_RES(mp),
-                                 0, XFS_TRANS_PERM_LOG_RES,
-                                 XFS_INACTIVE_LOG_COUNT);
-       if (error)
-               goto error_cancel;
-
-       xfs_ilock(ip, XFS_ILOCK_EXCL);
-       xfs_trans_ijoin(tp, ip, 0);
-       xfs_idestroy_fork(ip, XFS_ATTR_FORK);
-
-       ASSERT(ip->i_d.di_anextents == 0);
-
-       *tpp = tp;
-       return 0;
-
-error_cancel:
-       ASSERT(XFS_FORCED_SHUTDOWN(mp));
-       xfs_trans_cancel(tp, 0);
-error_unlock:
-       *tpp = NULL;
-       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
        return error;
 }
 
@@ -574,8 +463,7 @@ xfs_release(
                if (xfs_iflags_test(ip, XFS_IDIRTY_RELEASE))
                        return 0;
 
-               error = xfs_free_eofblocks(mp, ip,
-                                          XFS_FREE_EOF_TRYLOCK);
+               error = xfs_free_eofblocks(mp, ip, true);
                if (error)
                        return error;
 
@@ -604,7 +492,7 @@ xfs_inactive(
        xfs_trans_t     *tp;
        xfs_mount_t     *mp;
        int             error;
-       int             truncate;
+       int             truncate = 0;
 
        /*
         * If the inode is already free, then there can be nothing
@@ -616,17 +504,6 @@ xfs_inactive(
                return VN_INACTIVE_CACHE;
        }
 
-       /*
-        * Only do a truncate if it's a regular file with
-        * some actual space in it.  It's OK to look at the
-        * inode's fields without the lock because we're the
-        * only one with a reference to the inode.
-        */
-       truncate = ((ip->i_d.di_nlink == 0) &&
-           ((ip->i_d.di_size != 0) || XFS_ISIZE(ip) != 0 ||
-            (ip->i_d.di_nextents > 0) || (ip->i_delayed_blks > 0)) &&
-           S_ISREG(ip->i_d.di_mode));
-
        mp = ip->i_mount;
 
        error = 0;
@@ -643,99 +520,100 @@ xfs_inactive(
                    (!(ip->i_d.di_flags &
                                (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) ||
                     ip->i_delayed_blks != 0))) {
-                       error = xfs_free_eofblocks(mp, ip, 0);
+                       error = xfs_free_eofblocks(mp, ip, false);
                        if (error)
                                return VN_INACTIVE_CACHE;
                }
                goto out;
        }
 
-       ASSERT(ip->i_d.di_nlink == 0);
+       if (S_ISREG(ip->i_d.di_mode) &&
+           (ip->i_d.di_size != 0 || XFS_ISIZE(ip) != 0 ||
+            ip->i_d.di_nextents > 0 || ip->i_delayed_blks > 0))
+               truncate = 1;
 
        error = xfs_qm_dqattach(ip, 0);
        if (error)
                return VN_INACTIVE_CACHE;
 
        tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
-       if (truncate) {
-               xfs_ilock(ip, XFS_IOLOCK_EXCL);
-
-               error = xfs_trans_reserve(tp, 0,
-                                         XFS_ITRUNCATE_LOG_RES(mp),
-                                         0, XFS_TRANS_PERM_LOG_RES,
-                                         XFS_ITRUNCATE_LOG_COUNT);
-               if (error) {
-                       /* Don't call itruncate_cleanup */
-                       ASSERT(XFS_FORCED_SHUTDOWN(mp));
-                       xfs_trans_cancel(tp, 0);
-                       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
-                       return VN_INACTIVE_CACHE;
-               }
+       error = xfs_trans_reserve(tp, 0,
+                       (truncate || S_ISLNK(ip->i_d.di_mode)) ?
+                               XFS_ITRUNCATE_LOG_RES(mp) :
+                               XFS_IFREE_LOG_RES(mp),
+                       0,
+                       XFS_TRANS_PERM_LOG_RES,
+                       XFS_ITRUNCATE_LOG_COUNT);
+       if (error) {
+               ASSERT(XFS_FORCED_SHUTDOWN(mp));
+               xfs_trans_cancel(tp, 0);
+               return VN_INACTIVE_CACHE;
+       }
 
-               xfs_ilock(ip, XFS_ILOCK_EXCL);
-               xfs_trans_ijoin(tp, ip, 0);
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
 
+       if (S_ISLNK(ip->i_d.di_mode)) {
+               /*
+                * Zero length symlinks _can_ exist.
+                */
+               if (ip->i_d.di_size > XFS_IFORK_DSIZE(ip)) {
+                       error = xfs_inactive_symlink_rmt(ip, &tp);
+                       if (error)
+                               goto out_cancel;
+               } else if (ip->i_df.if_bytes > 0) {
+                       xfs_idata_realloc(ip, -(ip->i_df.if_bytes),
+                                         XFS_DATA_FORK);
+                       ASSERT(ip->i_df.if_bytes == 0);
+               }
+       } else if (truncate) {
                ip->i_d.di_size = 0;
                xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 
                error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, 0);
-               if (error) {
-                       xfs_trans_cancel(tp,
-                               XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
-                       xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
-                       return VN_INACTIVE_CACHE;
-               }
+               if (error)
+                       goto out_cancel;
 
                ASSERT(ip->i_d.di_nextents == 0);
-       } else if (S_ISLNK(ip->i_d.di_mode)) {
+       }
 
-               /*
-                * If we get an error while cleaning up a
-                * symlink we bail out.
-                */
-               error = (ip->i_d.di_size > XFS_IFORK_DSIZE(ip)) ?
-                       xfs_inactive_symlink_rmt(ip, &tp) :
-                       xfs_inactive_symlink_local(ip, &tp);
+       /*
+        * If there are attributes associated with the file then blow them away
+        * now.  The code calls a routine that recursively deconstructs the
+        * attribute fork.  We need to just commit the current transaction
+        * because we can't use it for xfs_attr_inactive().
+        */
+       if (ip->i_d.di_anextents > 0) {
+               ASSERT(ip->i_d.di_forkoff != 0);
 
-               if (error) {
-                       ASSERT(tp == NULL);
-                       return VN_INACTIVE_CACHE;
-               }
+               error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
+               if (error)
+                       goto out_unlock;
 
-               xfs_trans_ijoin(tp, ip, 0);
-       } else {
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+               error = xfs_attr_inactive(ip);
+               if (error)
+                       goto out;
+
+               tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
                error = xfs_trans_reserve(tp, 0,
                                          XFS_IFREE_LOG_RES(mp),
                                          0, XFS_TRANS_PERM_LOG_RES,
                                          XFS_INACTIVE_LOG_COUNT);
                if (error) {
-                       ASSERT(XFS_FORCED_SHUTDOWN(mp));
                        xfs_trans_cancel(tp, 0);
-                       return VN_INACTIVE_CACHE;
+                       goto out;
                }
 
-               xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+               xfs_ilock(ip, XFS_ILOCK_EXCL);
                xfs_trans_ijoin(tp, ip, 0);
        }
 
-       /*
-        * If there are attributes associated with the file
-        * then blow them away now.  The code calls a routine
-        * that recursively deconstructs the attribute fork.
-        * We need to just commit the current transaction
-        * because we can't use it for xfs_attr_inactive().
-        */
-       if (ip->i_d.di_anextents > 0) {
-               error = xfs_inactive_attrs(ip, &tp);
-               /*
-                * If we got an error, the transaction is already
-                * cancelled, and the inode is unlocked. Just get out.
-                */
-                if (error)
-                        return VN_INACTIVE_CACHE;
-       } else if (ip->i_afp) {
+       if (ip->i_afp)
                xfs_idestroy_fork(ip, XFS_ATTR_FORK);
-       }
+
+       ASSERT(ip->i_d.di_anextents == 0);
 
        /*
         * Free the inode.
@@ -779,10 +657,13 @@ xfs_inactive(
         * Release the dquots held by inode, if any.
         */
        xfs_qm_dqdetach(ip);
-       xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
-
- out:
+out_unlock:
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+out:
        return VN_INACTIVE_CACHE;
+out_cancel:
+       xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
+       goto out_unlock;
 }
 
 /*
@@ -2262,10 +2143,10 @@ xfs_change_file_space(
 
        llen = bf->l_len > 0 ? bf->l_len - 1 : bf->l_len;
 
-       if (   (bf->l_start < 0)
-           || (bf->l_start > XFS_MAXIOFFSET(mp))
-           || (bf->l_start + llen < 0)
-           || (bf->l_start + llen > XFS_MAXIOFFSET(mp)))
+       if (bf->l_start < 0 ||
+           bf->l_start > mp->m_super->s_maxbytes ||
+           bf->l_start + llen < 0 ||
+           bf->l_start + llen > mp->m_super->s_maxbytes)
                return XFS_ERROR(EINVAL);
 
        bf->l_whence = 0;
index abfb2682de7f33b0dce686447849e9c49b85c0a5..2be8a2dbc868f86f84e90c0e79a755c1f51e672b 100644 (file)
@@ -29,6 +29,7 @@ dma_mark_declared_memory_occupied(struct device *dev,
 #else
 #define dma_alloc_from_coherent(dev, size, handle, ret) (0)
 #define dma_release_from_coherent(dev, order, vaddr) (0)
+#define dma_mmap_from_coherent(dev, vma, vaddr, order, ret) (0)
 #endif
 
 #endif
index 2e248d8924dc34c38fa0ed91fdaa619060c9a603..de8bf89940f8dc3a7bc27a877067d3885cf646fa 100644 (file)
@@ -176,4 +176,59 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
 #define dma_map_sg(d, s, n, r) dma_map_sg_attrs(d, s, n, r, NULL)
 #define dma_unmap_sg(d, s, n, r) dma_unmap_sg_attrs(d, s, n, r, NULL)
 
+extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
+                          void *cpu_addr, dma_addr_t dma_addr, size_t size);
+
+/**
+ * dma_mmap_attrs - map a coherent DMA allocation into user space
+ * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
+ * @vma: vm_area_struct describing requested user mapping
+ * @cpu_addr: kernel CPU-view address returned from dma_alloc_attrs
+ * @handle: device-view address returned from dma_alloc_attrs
+ * @size: size of memory originally requested in dma_alloc_attrs
+ * @attrs: attributes of mapping properties requested in dma_alloc_attrs
+ *
+ * Map a coherent DMA buffer previously allocated by dma_alloc_attrs
+ * into user space.  The coherent DMA buffer must not be freed by the
+ * driver until the user space mapping has been released.
+ */
+static inline int
+dma_mmap_attrs(struct device *dev, struct vm_area_struct *vma, void *cpu_addr,
+              dma_addr_t dma_addr, size_t size, struct dma_attrs *attrs)
+{
+       struct dma_map_ops *ops = get_dma_ops(dev);
+       BUG_ON(!ops);
+       if (ops->mmap)
+               return ops->mmap(dev, vma, cpu_addr, dma_addr, size, attrs);
+       return dma_common_mmap(dev, vma, cpu_addr, dma_addr, size);
+}
+
+#define dma_mmap_coherent(d, v, c, h, s) dma_mmap_attrs(d, v, c, h, s, NULL)
+
+static inline int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma,
+                     void *cpu_addr, dma_addr_t dma_addr, size_t size)
+{
+       DEFINE_DMA_ATTRS(attrs);
+       dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
+       return dma_mmap_attrs(dev, vma, cpu_addr, dma_addr, size, &attrs);
+}
+
+int
+dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
+                      void *cpu_addr, dma_addr_t dma_addr, size_t size);
+
+static inline int
+dma_get_sgtable_attrs(struct device *dev, struct sg_table *sgt, void *cpu_addr,
+                     dma_addr_t dma_addr, size_t size, struct dma_attrs *attrs)
+{
+       struct dma_map_ops *ops = get_dma_ops(dev);
+       BUG_ON(!ops);
+       if (ops->get_sgtable)
+               return ops->get_sgtable(dev, sgt, cpu_addr, dma_addr, size,
+                                       attrs);
+       return dma_common_get_sgtable(dev, sgt, cpu_addr, dma_addr, size);
+}
+
+#define dma_get_sgtable(d, t, v, h, s) dma_get_sgtable_attrs(d, t, v, h, s, NULL)
+
 #endif
index 9e5b0356e2bbd0950c6399929e69da831928d9ee..a48937d4a5ea5715fafe813baf4f586a0b747355 100644 (file)
 #define F_GETOWN_EX    16
 #endif
 
+#ifndef F_GETOWNER_UIDS
+#define F_GETOWNER_UIDS        17
+#endif
+
 #define F_OWNER_TID    0
 #define F_OWNER_PID    1
 #define F_OWNER_PGRP   2
index 68733587e700bb00b776114e4a19530425194143..c20b001815303a670347a3e1c6b2227ea0f53383 100644 (file)
@@ -107,11 +107,6 @@ struct drm_exynos_vidi_connection {
        uint64_t edid;
 };
 
-struct drm_exynos_plane_set_zpos {
-       __u32 plane_id;
-       __s32 zpos;
-};
-
 /* memory type definitions. */
 enum e_drm_exynos_gem_mem_type {
        /* Physically Continuous memory and used as default. */
@@ -164,7 +159,6 @@ struct drm_exynos_g2d_exec {
 #define DRM_EXYNOS_GEM_MMAP            0x02
 /* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */
 #define DRM_EXYNOS_GEM_GET             0x04
-#define DRM_EXYNOS_PLANE_SET_ZPOS      0x06
 #define DRM_EXYNOS_VIDI_CONNECTION     0x07
 
 /* G2D */
@@ -184,9 +178,6 @@ struct drm_exynos_g2d_exec {
 #define DRM_IOCTL_EXYNOS_GEM_GET       DRM_IOWR(DRM_COMMAND_BASE + \
                DRM_EXYNOS_GEM_GET,     struct drm_exynos_gem_info)
 
-#define DRM_IOCTL_EXYNOS_PLANE_SET_ZPOS        DRM_IOWR(DRM_COMMAND_BASE + \
-               DRM_EXYNOS_PLANE_SET_ZPOS, struct drm_exynos_plane_set_zpos)
-
 #define DRM_IOCTL_EXYNOS_VIDI_CONNECTION       DRM_IOWR(DRM_COMMAND_BASE + \
                DRM_EXYNOS_VIDI_CONNECTION, struct drm_exynos_vidi_connection)
 
index 9547daddf8130f7db081edb28dad839c7c652268..d823d603dadd854c15f81b33c71e3a1159a52666 100644 (file)
@@ -195,6 +195,7 @@ header-y += in_route.h
 header-y += sock_diag.h
 header-y += inet_diag.h
 header-y += unix_diag.h
+header-y += packet_diag.h
 header-y += inotify.h
 header-y += input.h
 header-y += ioctl.h
@@ -386,6 +387,7 @@ header-y += utime.h
 header-y += utsname.h
 header-y += uuid.h
 header-y += uvcvideo.h
+header-y += v4l2-common.h
 header-y += v4l2-dv-timings.h
 header-y += v4l2-mediabus.h
 header-y += v4l2-subdev.h
index b2b4d2ad7103d9fa9d5710b21b522ff78371fd46..3ad510b25283ee1e5285929b702d739fb0afc31a 100644 (file)
@@ -190,6 +190,8 @@ extern bool wmi_has_guid(const char *guid);
 
 extern long acpi_video_get_capabilities(acpi_handle graphics_dev_handle);
 extern long acpi_is_video_device(struct acpi_device *device);
+extern void acpi_video_dmi_promote_vendor(void);
+extern void acpi_video_dmi_demote_vendor(void);
 extern int acpi_video_backlight_support(void);
 extern int acpi_video_display_switch_support(void);
 
@@ -205,6 +207,14 @@ static inline long acpi_is_video_device(struct acpi_device *device)
        return 0;
 }
 
+static inline void acpi_video_dmi_promote_vendor(void)
+{
+}
+
+static inline void acpi_video_dmi_demote_vendor(void)
+{
+}
+
 static inline int acpi_video_backlight_support(void)
 {
        return 0;
index b1a520ec8b59dd0975bc4e738ce2398ca19dc363..31ff6dba4872a96c6bbfa5a78bbe27a782affc41 100644 (file)
@@ -126,22 +126,20 @@ struct kiocb {
        struct eventfd_ctx      *ki_eventfd;
 };
 
-#define is_sync_kiocb(iocb)    ((iocb)->ki_key == KIOCB_SYNC_KEY)
-#define init_sync_kiocb(x, filp)                       \
-       do {                                            \
-               struct task_struct *tsk = current;      \
-               (x)->ki_flags = 0;                      \
-               (x)->ki_users = 1;                      \
-               (x)->ki_key = KIOCB_SYNC_KEY;           \
-               (x)->ki_filp = (filp);                  \
-               (x)->ki_ctx = NULL;                     \
-               (x)->ki_cancel = NULL;                  \
-               (x)->ki_retry = NULL;                   \
-               (x)->ki_dtor = NULL;                    \
-               (x)->ki_obj.tsk = tsk;                  \
-               (x)->ki_user_data = 0;                  \
-               (x)->private = NULL;                    \
-       } while (0)
+static inline bool is_sync_kiocb(struct kiocb *kiocb)
+{
+       return kiocb->ki_key == KIOCB_SYNC_KEY;
+}
+
+static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp)
+{
+       *kiocb = (struct kiocb) {
+                       .ki_users = 1,
+                       .ki_key = KIOCB_SYNC_KEY,
+                       .ki_filp = filp,
+                       .ki_obj.tsk = current,
+               };
+}
 
 #define AIO_RING_MAGIC                 0xa10a10a1
 #define AIO_RING_COMPAT_FEATURES       1
@@ -161,8 +159,6 @@ struct aio_ring {
        struct io_event         io_events[0];
 }; /* 128 bytes + ring size */
 
-#define aio_ring_avail(info, ring)     (((ring)->head + (info)->nr - 1 - (ring)->tail) % (info)->nr)
-
 #define AIO_RING_PAGES 8
 struct aio_ring_info {
        unsigned long           mmap_base;
@@ -177,6 +173,12 @@ struct aio_ring_info {
        struct page             *internal_pages[AIO_RING_PAGES];
 };
 
+static inline unsigned aio_ring_avail(struct aio_ring_info *info,
+                                       struct aio_ring *ring)
+{
+       return (ring->head + info->nr - 1 - ring->tail) % info->nr;
+}
+
 struct kioctx {
        atomic_t                users;
        int                     dead;
index 02549017212a2113c42a36354d5da37be1e23eec..2a5f64a11b77aee8d56100d60df81a7600511a6e 100644 (file)
@@ -21,8 +21,9 @@
 #include <linux/dmaengine.h>
 #include <linux/interrupt.h>
 
-struct pl08x_lli;
 struct pl08x_driver_data;
+struct pl08x_phy_chan;
+struct pl08x_txd;
 
 /* Bitmasks for selecting AHB ports for DMA transfers */
 enum {
@@ -46,169 +47,28 @@ enum {
  * devices with static assignments
  * @muxval: a number usually used to poke into some mux regiser to
  * mux in the signal to this channel
- * @cctl_opt: default options for the channel control register
+ * @cctl_memcpy: options for the channel control register for memcpy
+ *  *** not used for slave channels ***
  * @addr: source/target address in physical memory for this DMA channel,
  * can be the address of a FIFO register for burst requests for example.
  * This can be left undefined if the PrimeCell API is used for configuring
  * this.
- * @circular_buffer: whether the buffer passed in is circular and
- * shall simply be looped round round (like a record baby round
- * round round round)
  * @single: the device connected to this channel will request single DMA
  * transfers, not bursts. (Bursts are default.)
  * @periph_buses: the device connected to this channel is accessible via
  * these buses (use PL08X_AHB1 | PL08X_AHB2).
  */
 struct pl08x_channel_data {
-       char *bus_id;
+       const char *bus_id;
        int min_signal;
        int max_signal;
        u32 muxval;
-       u32 cctl;
+       u32 cctl_memcpy;
        dma_addr_t addr;
-       bool circular_buffer;
        bool single;
        u8 periph_buses;
 };
 
-/**
- * Struct pl08x_bus_data - information of source or destination
- * busses for a transfer
- * @addr: current address
- * @maxwidth: the maximum width of a transfer on this bus
- * @buswidth: the width of this bus in bytes: 1, 2 or 4
- */
-struct pl08x_bus_data {
-       dma_addr_t addr;
-       u8 maxwidth;
-       u8 buswidth;
-};
-
-/**
- * struct pl08x_phy_chan - holder for the physical channels
- * @id: physical index to this channel
- * @lock: a lock to use when altering an instance of this struct
- * @signal: the physical signal (aka channel) serving this physical channel
- * right now
- * @serving: the virtual channel currently being served by this physical
- * channel
- * @locked: channel unavailable for the system, e.g. dedicated to secure
- * world
- */
-struct pl08x_phy_chan {
-       unsigned int id;
-       void __iomem *base;
-       spinlock_t lock;
-       int signal;
-       struct pl08x_dma_chan *serving;
-       bool locked;
-};
-
-/**
- * struct pl08x_sg - structure containing data per sg
- * @src_addr: src address of sg
- * @dst_addr: dst address of sg
- * @len: transfer len in bytes
- * @node: node for txd's dsg_list
- */
-struct pl08x_sg {
-       dma_addr_t src_addr;
-       dma_addr_t dst_addr;
-       size_t len;
-       struct list_head node;
-};
-
-/**
- * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor
- * @tx: async tx descriptor
- * @node: node for txd list for channels
- * @dsg_list: list of children sg's
- * @direction: direction of transfer
- * @llis_bus: DMA memory address (physical) start for the LLIs
- * @llis_va: virtual memory address start for the LLIs
- * @cctl: control reg values for current txd
- * @ccfg: config reg values for current txd
- */
-struct pl08x_txd {
-       struct dma_async_tx_descriptor tx;
-       struct list_head node;
-       struct list_head dsg_list;
-       enum dma_transfer_direction direction;
-       dma_addr_t llis_bus;
-       struct pl08x_lli *llis_va;
-       /* Default cctl value for LLIs */
-       u32 cctl;
-       /*
-        * Settings to be put into the physical channel when we
-        * trigger this txd.  Other registers are in llis_va[0].
-        */
-       u32 ccfg;
-};
-
-/**
- * struct pl08x_dma_chan_state - holds the PL08x specific virtual channel
- * states
- * @PL08X_CHAN_IDLE: the channel is idle
- * @PL08X_CHAN_RUNNING: the channel has allocated a physical transport
- * channel and is running a transfer on it
- * @PL08X_CHAN_PAUSED: the channel has allocated a physical transport
- * channel, but the transfer is currently paused
- * @PL08X_CHAN_WAITING: the channel is waiting for a physical transport
- * channel to become available (only pertains to memcpy channels)
- */
-enum pl08x_dma_chan_state {
-       PL08X_CHAN_IDLE,
-       PL08X_CHAN_RUNNING,
-       PL08X_CHAN_PAUSED,
-       PL08X_CHAN_WAITING,
-};
-
-/**
- * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel
- * @chan: wrappped abstract channel
- * @phychan: the physical channel utilized by this channel, if there is one
- * @phychan_hold: if non-zero, hold on to the physical channel even if we
- * have no pending entries
- * @tasklet: tasklet scheduled by the IRQ to handle actual work etc
- * @name: name of channel
- * @cd: channel platform data
- * @runtime_addr: address for RX/TX according to the runtime config
- * @runtime_direction: current direction of this channel according to
- * runtime config
- * @pend_list: queued transactions pending on this channel
- * @at: active transaction on this channel
- * @lock: a lock for this channel data
- * @host: a pointer to the host (internal use)
- * @state: whether the channel is idle, paused, running etc
- * @slave: whether this channel is a device (slave) or for memcpy
- * @device_fc: Flow Controller Settings for ccfg register. Only valid for slave
- * channels. Fill with 'true' if peripheral should be flow controller. Direction
- * will be selected at Runtime.
- * @waiting: a TX descriptor on this channel which is waiting for a physical
- * channel to become available
- */
-struct pl08x_dma_chan {
-       struct dma_chan chan;
-       struct pl08x_phy_chan *phychan;
-       int phychan_hold;
-       struct tasklet_struct tasklet;
-       char *name;
-       const struct pl08x_channel_data *cd;
-       dma_addr_t src_addr;
-       dma_addr_t dst_addr;
-       u32 src_cctl;
-       u32 dst_cctl;
-       enum dma_transfer_direction runtime_direction;
-       struct list_head pend_list;
-       struct pl08x_txd *at;
-       spinlock_t lock;
-       struct pl08x_driver_data *host;
-       enum pl08x_dma_chan_state state;
-       bool slave;
-       bool device_fc;
-       struct pl08x_txd *waiting;
-};
-
 /**
  * struct pl08x_platform_data - the platform configuration for the PL08x
  * PrimeCells.
@@ -229,8 +89,8 @@ struct pl08x_platform_data {
        const struct pl08x_channel_data *slave_channels;
        unsigned int num_slave_channels;
        struct pl08x_channel_data memcpy_channel;
-       int (*get_signal)(struct pl08x_dma_chan *);
-       void (*put_signal)(struct pl08x_dma_chan *);
+       int (*get_signal)(const struct pl08x_channel_data *);
+       void (*put_signal)(const struct pl08x_channel_data *, int);
        u8 lli_buses;
        u8 mem_buses;
 };
index 22f292a917a3f6c13eb4353c50ec9cf95acffa3f..36abf2aa7e680e24afe8c52b87ccf8f88707ee17 100644 (file)
 #define AUDIT_LAST_KERN_ANOM_MSG    1799
 #define AUDIT_ANOM_PROMISCUOUS      1700 /* Device changed promiscuous mode */
 #define AUDIT_ANOM_ABEND            1701 /* Process ended abnormally */
+#define AUDIT_ANOM_LINK                    1702 /* Suspicious use of file links */
 #define AUDIT_INTEGRITY_DATA       1800 /* Data integrity verification */
 #define AUDIT_INTEGRITY_METADATA    1801 /* Metadata integrity verification */
 #define AUDIT_INTEGRITY_STATUS     1802 /* Integrity enable status */
@@ -687,6 +688,8 @@ extern void             audit_log_d_path(struct audit_buffer *ab,
                                             const struct path *path);
 extern void                audit_log_key(struct audit_buffer *ab,
                                          char *key);
+extern void                audit_log_link_denied(const char *operation,
+                                                 struct path *link);
 extern void                audit_log_lost(const char *message);
 #ifdef CONFIG_SECURITY
 extern void                audit_log_secctx(struct audit_buffer *ab, u32 secid);
@@ -716,6 +719,7 @@ extern int audit_enabled;
 #define audit_log_untrustedstring(a,s) do { ; } while (0)
 #define audit_log_d_path(b, p, d) do { ; } while (0)
 #define audit_log_key(b, k) do { ; } while (0)
+#define audit_log_link_denied(o, l) do { ; } while (0)
 #define audit_log_secctx(b,s) do { ; } while (0)
 #define audit_enabled 0
 #endif
index b1038bd686acfc46caf251aad8ae765e9970ceb3..c97c6b9cd38ee34e501735758de2b710decceae0 100644 (file)
 
 #include <linux/percpu_counter.h>
 #include <linux/log2.h>
-#include <linux/proportions.h>
+#include <linux/flex_proportions.h>
 #include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/writeback.h>
 #include <linux/atomic.h>
+#include <linux/sysctl.h>
 
 struct page;
 struct device;
@@ -89,7 +90,7 @@ struct backing_dev_info {
        unsigned long dirty_ratelimit;
        unsigned long balanced_dirty_ratelimit;
 
-       struct prop_local_percpu completions;
+       struct fprop_local_percpu completions;
        int dirty_exceeded;
 
        unsigned int min_ratio;
@@ -304,6 +305,8 @@ void clear_bdi_congested(struct backing_dev_info *bdi, int sync);
 void set_bdi_congested(struct backing_dev_info *bdi, int sync);
 long congestion_wait(int sync, long timeout);
 long wait_iff_congested(struct zone *zone, int sync, long timeout);
+int pdflush_proc_obsolete(struct ctl_table *table, int write,
+               void __user *buffer, size_t *lenp, loff_t *ppos);
 
 static inline bool bdi_cap_writeback_dirty(struct backing_dev_info *bdi)
 {
index fcb06fb284eb145d61824caf43ea12402b44b735..3fb8bbafe5e7f70f69614e2730aefc914fb0083d 100644 (file)
 #define  BCMA_CC_CHIPST_4313_OTP_PRESENT       2
 #define  BCMA_CC_CHIPST_4331_SPROM_PRESENT     2
 #define  BCMA_CC_CHIPST_4331_OTP_PRESENT       4
+#define  BCMA_CC_CHIPST_43228_ILP_DIV_EN       0x00000001
+#define  BCMA_CC_CHIPST_43228_OTP_PRESENT      0x00000002
+#define  BCMA_CC_CHIPST_43228_SERDES_REFCLK_PADSEL     0x00000004
+#define  BCMA_CC_CHIPST_43228_SDIO_MODE                0x00000008
+#define  BCMA_CC_CHIPST_43228_SDIO_OTP_PRESENT 0x00000010
+#define  BCMA_CC_CHIPST_43228_SDIO_RESET       0x00000020
 #define  BCMA_CC_CHIPST_4706_PKG_OPTION                BIT(0) /* 0: full-featured package 1: low-cost package */
 #define  BCMA_CC_CHIPST_4706_SFLASH_PRESENT    BIT(1) /* 0: parallel, 1: serial flash is present */
 #define  BCMA_CC_CHIPST_4706_SFLASH_TYPE       BIT(2) /* 0: 8b-p/ST-s flash, 1: 16b-p/Atmal-s flash */
index 0edb65dd8eddd35de6b0c383a27d889ec00f2193..7b7ac9ccec7af5900b469166e7bdda9aa578b4d2 100644 (file)
@@ -160,6 +160,7 @@ enum rq_flag_bits {
        __REQ_FLUSH_SEQ,        /* request for flush sequence */
        __REQ_IO_STAT,          /* account I/O stat */
        __REQ_MIXED_MERGE,      /* merge of different types, fail separately */
+       __REQ_KERNEL,           /* direct IO to kernel pages */
        __REQ_NR_BITS,          /* stops here */
 };
 
@@ -201,5 +202,6 @@ enum rq_flag_bits {
 #define REQ_IO_STAT            (1 << __REQ_IO_STAT)
 #define REQ_MIXED_MERGE                (1 << __REQ_MIXED_MERGE)
 #define REQ_SECURE             (1 << __REQ_SECURE)
+#define REQ_KERNEL             (1 << __REQ_KERNEL)
 
 #endif /* __LINUX_BLK_TYPES_H */
index 07954b05b86cc3ac9c4f72aa97584b551914631e..4e72a9d48232d513b5b2fe44d973de8dfeb1e9e4 100644 (file)
@@ -46,16 +46,23 @@ struct blkcg_gq;
 struct request;
 typedef void (rq_end_io_fn)(struct request *, int);
 
+#define BLK_RL_SYNCFULL                (1U << 0)
+#define BLK_RL_ASYNCFULL       (1U << 1)
+
 struct request_list {
+       struct request_queue    *q;     /* the queue this rl belongs to */
+#ifdef CONFIG_BLK_CGROUP
+       struct blkcg_gq         *blkg;  /* blkg this request pool belongs to */
+#endif
        /*
         * count[], starved[], and wait[] are indexed by
         * BLK_RW_SYNC/BLK_RW_ASYNC
         */
-       int count[2];
-       int starved[2];
-       int elvpriv;
-       mempool_t *rq_pool;
-       wait_queue_head_t wait[2];
+       int                     count[2];
+       int                     starved[2];
+       mempool_t               *rq_pool;
+       wait_queue_head_t       wait[2];
+       unsigned int            flags;
 };
 
 /*
@@ -138,6 +145,7 @@ struct request {
        struct hd_struct *part;
        unsigned long start_time;
 #ifdef CONFIG_BLK_CGROUP
+       struct request_list *rl;                /* rl this rq is alloced from */
        unsigned long long start_time_ns;
        unsigned long long io_start_time_ns;    /* when passed to hardware */
 #endif
@@ -282,11 +290,16 @@ struct request_queue {
        struct list_head        queue_head;
        struct request          *last_merge;
        struct elevator_queue   *elevator;
+       int                     nr_rqs[2];      /* # allocated [a]sync rqs */
+       int                     nr_rqs_elvpriv; /* # allocated rqs w/ elvpriv */
 
        /*
-        * the queue request freelist, one for reads and one for writes
+        * If blkcg is not used, @q->root_rl serves all requests.  If blkcg
+        * is used, root blkg allocates from @q->root_rl and all other
+        * blkgs from their own blkg->rl.  Which one to use should be
+        * determined using bio_request_list().
         */
-       struct request_list     rq;
+       struct request_list     root_rl;
 
        request_fn_proc         *request_fn;
        make_request_fn         *make_request_fn;
@@ -561,27 +574,25 @@ static inline bool rq_is_sync(struct request *rq)
        return rw_is_sync(rq->cmd_flags);
 }
 
-static inline int blk_queue_full(struct request_queue *q, int sync)
+static inline bool blk_rl_full(struct request_list *rl, bool sync)
 {
-       if (sync)
-               return test_bit(QUEUE_FLAG_SYNCFULL, &q->queue_flags);
-       return test_bit(QUEUE_FLAG_ASYNCFULL, &q->queue_flags);
+       unsigned int flag = sync ? BLK_RL_SYNCFULL : BLK_RL_ASYNCFULL;
+
+       return rl->flags & flag;
 }
 
-static inline void blk_set_queue_full(struct request_queue *q, int sync)
+static inline void blk_set_rl_full(struct request_list *rl, bool sync)
 {
-       if (sync)
-               queue_flag_set(QUEUE_FLAG_SYNCFULL, q);
-       else
-               queue_flag_set(QUEUE_FLAG_ASYNCFULL, q);
+       unsigned int flag = sync ? BLK_RL_SYNCFULL : BLK_RL_ASYNCFULL;
+
+       rl->flags |= flag;
 }
 
-static inline void blk_clear_queue_full(struct request_queue *q, int sync)
+static inline void blk_clear_rl_full(struct request_list *rl, bool sync)
 {
-       if (sync)
-               queue_flag_clear(QUEUE_FLAG_SYNCFULL, q);
-       else
-               queue_flag_clear(QUEUE_FLAG_ASYNCFULL, q);
+       unsigned int flag = sync ? BLK_RL_SYNCFULL : BLK_RL_ASYNCFULL;
+
+       rl->flags &= ~flag;
 }
 
 
@@ -911,11 +922,15 @@ struct blk_plug {
 };
 #define BLK_MAX_REQUEST_COUNT 16
 
+struct blk_plug_cb;
+typedef void (*blk_plug_cb_fn)(struct blk_plug_cb *, bool);
 struct blk_plug_cb {
        struct list_head list;
-       void (*callback)(struct blk_plug_cb *);
+       blk_plug_cb_fn callback;
+       void *data;
 };
-
+extern struct blk_plug_cb *blk_check_plugged(blk_plug_cb_fn unplug,
+                                            void *data, int size);
 extern void blk_start_plug(struct blk_plug *);
 extern void blk_finish_plug(struct blk_plug *);
 extern void blk_flush_plug_list(struct blk_plug *, bool);
index faf8a45af210053c535537c85cb25b1af228c7e0..a8519446c111ab02fb71e79e6b31fea1df666223 100644 (file)
@@ -40,6 +40,7 @@ struct blkpg_ioctl_arg {
 /* The subfunctions (for the op field) */
 #define BLKPG_ADD_PARTITION    1
 #define BLKPG_DEL_PARTITION    2
+#define BLKPG_RESIZE_PARTITION 3
 
 /* Sizes of name fields. Unused at present. */
 #define BLKPG_DEVNAMELTH       64
index f55ab8cdc10630f75131f96324278fd56cc00e81..4d0fb3df2f4adaa584e13e963b7bc6ffa02a4e26 100644 (file)
@@ -67,7 +67,6 @@ void bsg_job_done(struct bsg_job *job, int result,
 int bsg_setup_queue(struct device *dev, struct request_queue *q, char *name,
                    bsg_job_fn *job_fn, int dd_job_size);
 void bsg_request_fn(struct request_queue *q);
-void bsg_remove_queue(struct request_queue *q);
 void bsg_goose_queue(struct request_queue *q);
 
 #endif
diff --git a/include/linux/ceph/ceph_features.h b/include/linux/ceph/ceph_features.h
new file mode 100644 (file)
index 0000000..dad579b
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __CEPH_FEATURES
+#define __CEPH_FEATURES
+
+/*
+ * feature bits
+ */
+#define CEPH_FEATURE_UID            (1<<0)
+#define CEPH_FEATURE_NOSRCADDR      (1<<1)
+#define CEPH_FEATURE_MONCLOCKCHECK  (1<<2)
+#define CEPH_FEATURE_FLOCK          (1<<3)
+#define CEPH_FEATURE_SUBSCRIBE2     (1<<4)
+#define CEPH_FEATURE_MONNAMES       (1<<5)
+#define CEPH_FEATURE_RECONNECT_SEQ  (1<<6)
+#define CEPH_FEATURE_DIRLAYOUTHASH  (1<<7)
+/* bits 8-17 defined by user-space; not supported yet here */
+#define CEPH_FEATURE_CRUSH_TUNABLES (1<<18)
+
+/*
+ * Features supported.
+ */
+#define CEPH_FEATURES_SUPPORTED_DEFAULT  \
+       (CEPH_FEATURE_NOSRCADDR |        \
+        CEPH_FEATURE_CRUSH_TUNABLES)
+
+#define CEPH_FEATURES_REQUIRED_DEFAULT   \
+       (CEPH_FEATURE_NOSRCADDR)
+#endif
index e81ab30d4896329e29d47bea33d92e5634da6a89..d021610efd65168bf8fc5e071416d13e6faa3d0a 100644 (file)
 /* arbitrary limit on max # of monitors (cluster of 3 is typical) */
 #define CEPH_MAX_MON   31
 
-
-/*
- * feature bits
- */
-#define CEPH_FEATURE_UID            (1<<0)
-#define CEPH_FEATURE_NOSRCADDR      (1<<1)
-#define CEPH_FEATURE_MONCLOCKCHECK  (1<<2)
-#define CEPH_FEATURE_FLOCK          (1<<3)
-#define CEPH_FEATURE_SUBSCRIBE2     (1<<4)
-#define CEPH_FEATURE_MONNAMES       (1<<5)
-#define CEPH_FEATURE_RECONNECT_SEQ  (1<<6)
-#define CEPH_FEATURE_DIRLAYOUTHASH  (1<<7)
-
-
 /*
  * ceph_file_layout - describe data layout for a file/inode
  */
index d8615dee5808d3f55c93a38c6fdb66113f09a691..4bbf2db45f461bf8be35159a3a0b91671191992e 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef __CEPH_DECODE_H
 #define __CEPH_DECODE_H
 
+#include <linux/err.h>
 #include <linux/bug.h>
 #include <linux/time.h>
 #include <asm/unaligned.h>
@@ -84,6 +85,52 @@ static inline int ceph_has_room(void **p, void *end, size_t n)
                ceph_decode_copy(p, pv, n);                     \
        } while (0)
 
+/*
+ * Allocate a buffer big enough to hold the wire-encoded string, and
+ * decode the string into it.  The resulting string will always be
+ * terminated with '\0'.  If successful, *p will be advanced
+ * past the decoded data.  Also, if lenp is not a null pointer, the
+ * length (not including the terminating '\0') will be recorded in
+ * *lenp.  Note that a zero-length string is a valid return value.
+ *
+ * Returns a pointer to the newly-allocated string buffer, or a
+ * pointer-coded errno if an error occurs.  Neither *p nor *lenp
+ * will have been updated if an error is returned.
+ *
+ * There are two possible failures:
+ *   - converting the string would require accessing memory at or
+ *     beyond the "end" pointer provided (-E
+ *   - memory could not be allocated for the result
+ */
+static inline char *ceph_extract_encoded_string(void **p, void *end,
+                                               size_t *lenp, gfp_t gfp)
+{
+       u32 len;
+       void *sp = *p;
+       char *buf;
+
+       ceph_decode_32_safe(&sp, end, len, bad);
+       if (!ceph_has_room(&sp, end, len))
+               goto bad;
+
+       buf = kmalloc(len + 1, gfp);
+       if (!buf)
+               return ERR_PTR(-ENOMEM);
+
+       if (len)
+               memcpy(buf, sp, len);
+       buf[len] = '\0';
+
+       *p = (char *) *p + sizeof (u32) + len;
+       if (lenp)
+               *lenp = (size_t) len;
+
+       return buf;
+
+bad:
+       return ERR_PTR(-ERANGE);
+}
+
 /*
  * struct ceph_timespec <-> struct timespec
  */
@@ -151,7 +198,7 @@ static inline void ceph_encode_filepath(void **p, void *end,
                                        u64 ino, const char *path)
 {
        u32 len = path ? strlen(path) : 0;
-       BUG_ON(*p + sizeof(ino) + sizeof(len) + len > end);
+       BUG_ON(*p + 1 + sizeof(ino) + sizeof(len) + len > end);
        ceph_encode_8(p, 1);
        ceph_encode_64(p, ino);
        ceph_encode_32(p, len);
index e71d683982a6f651a83a22fd7ef12bf7af6f655f..42624789b06f62caf30437da5b104d92f3c3a6e3 100644 (file)
 #include "osd_client.h"
 #include "ceph_fs.h"
 
-/*
- * Supported features
- */
-#define CEPH_FEATURE_SUPPORTED_DEFAULT CEPH_FEATURE_NOSRCADDR
-#define CEPH_FEATURE_REQUIRED_DEFAULT  CEPH_FEATURE_NOSRCADDR
-
 /*
  * mount options
  */
@@ -132,7 +126,7 @@ struct ceph_client {
        u32 supported_features;
        u32 required_features;
 
-       struct ceph_messenger *msgr;   /* messenger instance */
+       struct ceph_messenger msgr;   /* messenger instance */
        struct ceph_mon_client monc;
        struct ceph_osd_client osdc;
 
@@ -160,7 +154,7 @@ struct ceph_client {
 struct ceph_snap_context {
        atomic_t nref;
        u64 seq;
-       int num_snaps;
+       u32 num_snaps;
        u64 snaps[];
 };
 
index 44c87e731e9d4c5fff13cbf851694cd41356fb04..189ae063763403a95c28e667ea094bab5c74ded8 100644 (file)
@@ -31,9 +31,6 @@ struct ceph_connection_operations {
        int (*verify_authorizer_reply) (struct ceph_connection *con, int len);
        int (*invalidate_authorizer)(struct ceph_connection *con);
 
-       /* protocol version mismatch */
-       void (*bad_proto) (struct ceph_connection *con);
-
        /* there was some error on the socket (disconnect, whatever) */
        void (*fault) (struct ceph_connection *con);
 
@@ -53,6 +50,7 @@ struct ceph_messenger {
        struct ceph_entity_inst inst;    /* my name+address */
        struct ceph_entity_addr my_enc_addr;
 
+       atomic_t stopping;
        bool nocrc;
 
        /*
@@ -80,7 +78,10 @@ struct ceph_msg {
        unsigned nr_pages;              /* size of page array */
        unsigned page_alignment;        /* io offset in first page */
        struct ceph_pagelist *pagelist; /* instead of pages */
+
+       struct ceph_connection *con;
        struct list_head list_head;
+
        struct kref kref;
        struct bio  *bio;               /* instead of pages/pagelist */
        struct bio  *bio_iter;          /* bio iterator */
@@ -105,23 +106,6 @@ struct ceph_msg_pos {
 #define BASE_DELAY_INTERVAL    (HZ/2)
 #define MAX_DELAY_INTERVAL     (5 * 60 * HZ)
 
-/*
- * ceph_connection state bit flags
- */
-#define LOSSYTX         0  /* we can close channel or drop messages on errors */
-#define CONNECTING     1
-#define NEGOTIATING    2
-#define KEEPALIVE_PENDING      3
-#define WRITE_PENDING  4  /* we have data ready to send */
-#define STANDBY                8  /* no outgoing messages, socket closed.  we keep
-                           * the ceph_connection around to maintain shared
-                           * state with the peer. */
-#define CLOSED         10 /* we've closed the connection */
-#define SOCK_CLOSED    11 /* socket state changed to closed */
-#define OPENING         13 /* open connection w/ (possibly new) peer */
-#define DEAD            14 /* dead, about to kfree */
-#define BACKOFF         15
-
 /*
  * A single connection with another host.
  *
@@ -131,18 +115,22 @@ struct ceph_msg_pos {
  */
 struct ceph_connection {
        void *private;
-       atomic_t nref;
 
        const struct ceph_connection_operations *ops;
 
        struct ceph_messenger *msgr;
+
+       atomic_t sock_state;
        struct socket *sock;
-       unsigned long state;    /* connection state (see flags above) */
+       struct ceph_entity_addr peer_addr; /* peer address */
+       struct ceph_entity_addr peer_addr_for_me;
+
+       unsigned long flags;
+       unsigned long state;
        const char *error_msg;  /* error message, if any */
 
-       struct ceph_entity_addr peer_addr; /* peer address */
        struct ceph_entity_name peer_name; /* peer name */
-       struct ceph_entity_addr peer_addr_for_me;
+
        unsigned peer_features;
        u32 connect_seq;      /* identify the most recent connection
                                 attempt for this connection, client */
@@ -207,24 +195,26 @@ extern int ceph_msgr_init(void);
 extern void ceph_msgr_exit(void);
 extern void ceph_msgr_flush(void);
 
-extern struct ceph_messenger *ceph_messenger_create(
-       struct ceph_entity_addr *myaddr,
-       u32 features, u32 required);
-extern void ceph_messenger_destroy(struct ceph_messenger *);
+extern void ceph_messenger_init(struct ceph_messenger *msgr,
+                       struct ceph_entity_addr *myaddr,
+                       u32 supported_features,
+                       u32 required_features,
+                       bool nocrc);
 
-extern void ceph_con_init(struct ceph_messenger *msgr,
-                         struct ceph_connection *con);
+extern void ceph_con_init(struct ceph_connection *con, void *private,
+                       const struct ceph_connection_operations *ops,
+                       struct ceph_messenger *msgr);
 extern void ceph_con_open(struct ceph_connection *con,
+                         __u8 entity_type, __u64 entity_num,
                          struct ceph_entity_addr *addr);
 extern bool ceph_con_opened(struct ceph_connection *con);
 extern void ceph_con_close(struct ceph_connection *con);
 extern void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg);
-extern void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg);
-extern void ceph_con_revoke_message(struct ceph_connection *con,
-                                 struct ceph_msg *msg);
+
+extern void ceph_msg_revoke(struct ceph_msg *msg);
+extern void ceph_msg_revoke_incoming(struct ceph_msg *msg);
+
 extern void ceph_con_keepalive(struct ceph_connection *con);
-extern struct ceph_connection *ceph_con_get(struct ceph_connection *con);
-extern void ceph_con_put(struct ceph_connection *con);
 
 extern struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags,
                                     bool can_fail);
index 545f85917780ab3cd65a314553f7011b655dbb50..2113e3850a4e0c7e088b22b07092baf2616654a5 100644 (file)
@@ -70,7 +70,7 @@ struct ceph_mon_client {
        bool hunting;
        int cur_mon;                       /* last monitor i contacted */
        unsigned long sub_sent, sub_renew_after;
-       struct ceph_connection *con;
+       struct ceph_connection con;
        bool have_fsid;
 
        /* pending generic requests */
index a362605f93682a8ffe213fc8a3bc7d06f4cc3187..09fa96b43436cba01cda946a30e48d17991fbae8 100644 (file)
 struct ceph_msgpool {
        const char *name;
        mempool_t *pool;
+       int type;               /* preallocated message type */
        int front_len;          /* preallocated payload size */
 };
 
-extern int ceph_msgpool_init(struct ceph_msgpool *pool,
+extern int ceph_msgpool_init(struct ceph_msgpool *pool, int type,
                             int front_len, int size, bool blocking,
                             const char *name);
 extern void ceph_msgpool_destroy(struct ceph_msgpool *pool);
index 0bd390ce98b2a9e9fd5fa69a697bb3d02f252194..dfae957398c333e4f4a62c969350eac9fae368b9 100644 (file)
@@ -31,7 +31,7 @@ SUBSYS(cpuacct)
 
 /* */
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
 SUBSYS(mem_cgroup)
 #endif
 
@@ -72,3 +72,9 @@ SUBSYS(net_prio)
 #endif
 
 /* */
+
+#ifdef CONFIG_CGROUP_HUGETLB
+SUBSYS(hugetlb)
+#endif
+
+/* */
index 2fd6a4234531577e128cdc1d5bfabacbed7babfb..b3ac22d0fc1fdc4be337e32e7991c5b0b45d9541 100644 (file)
@@ -84,6 +84,43 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb);
 
 #endif
 
+/**
+ * clk_prepare - prepare a clock source
+ * @clk: clock source
+ *
+ * This prepares the clock source for use.
+ *
+ * Must not be called from within atomic context.
+ */
+#ifdef CONFIG_HAVE_CLK_PREPARE
+int clk_prepare(struct clk *clk);
+#else
+static inline int clk_prepare(struct clk *clk)
+{
+       might_sleep();
+       return 0;
+}
+#endif
+
+/**
+ * clk_unprepare - undo preparation of a clock source
+ * @clk: clock source
+ *
+ * This undoes a previously prepared clock.  The caller must balance
+ * the number of prepare and unprepare calls.
+ *
+ * Must not be called from within atomic context.
+ */
+#ifdef CONFIG_HAVE_CLK_PREPARE
+void clk_unprepare(struct clk *clk);
+#else
+static inline void clk_unprepare(struct clk *clk)
+{
+       might_sleep();
+}
+#endif
+
+#ifdef CONFIG_HAVE_CLK
 /**
  * clk_get - lookup and obtain a reference to a clock producer.
  * @dev: device for clock "consumer"
@@ -121,24 +158,6 @@ struct clk *clk_get(struct device *dev, const char *id);
  */
 struct clk *devm_clk_get(struct device *dev, const char *id);
 
-/**
- * clk_prepare - prepare a clock source
- * @clk: clock source
- *
- * This prepares the clock source for use.
- *
- * Must not be called from within atomic context.
- */
-#ifdef CONFIG_HAVE_CLK_PREPARE
-int clk_prepare(struct clk *clk);
-#else
-static inline int clk_prepare(struct clk *clk)
-{
-       might_sleep();
-       return 0;
-}
-#endif
-
 /**
  * clk_enable - inform the system when the clock source should be running.
  * @clk: clock source
@@ -167,47 +186,6 @@ int clk_enable(struct clk *clk);
  */
 void clk_disable(struct clk *clk);
 
-
-/**
- * clk_unprepare - undo preparation of a clock source
- * @clk: clock source
- *
- * This undoes a previously prepared clock.  The caller must balance
- * the number of prepare and unprepare calls.
- *
- * Must not be called from within atomic context.
- */
-#ifdef CONFIG_HAVE_CLK_PREPARE
-void clk_unprepare(struct clk *clk);
-#else
-static inline void clk_unprepare(struct clk *clk)
-{
-       might_sleep();
-}
-#endif
-
-/* clk_prepare_enable helps cases using clk_enable in non-atomic context. */
-static inline int clk_prepare_enable(struct clk *clk)
-{
-       int ret;
-
-       ret = clk_prepare(clk);
-       if (ret)
-               return ret;
-       ret = clk_enable(clk);
-       if (ret)
-               clk_unprepare(clk);
-
-       return ret;
-}
-
-/* clk_disable_unprepare helps cases using clk_disable in non-atomic context. */
-static inline void clk_disable_unprepare(struct clk *clk)
-{
-       clk_disable(clk);
-       clk_unprepare(clk);
-}
-
 /**
  * clk_get_rate - obtain the current clock rate (in Hz) for a clock source.
  *               This is only valid once the clock source has been enabled.
@@ -298,6 +276,78 @@ struct clk *clk_get_parent(struct clk *clk);
  */
 struct clk *clk_get_sys(const char *dev_id, const char *con_id);
 
+#else /* !CONFIG_HAVE_CLK */
+
+static inline struct clk *clk_get(struct device *dev, const char *id)
+{
+       return NULL;
+}
+
+static inline struct clk *devm_clk_get(struct device *dev, const char *id)
+{
+       return NULL;
+}
+
+static inline void clk_put(struct clk *clk) {}
+
+static inline void devm_clk_put(struct device *dev, struct clk *clk) {}
+
+static inline int clk_enable(struct clk *clk)
+{
+       return 0;
+}
+
+static inline void clk_disable(struct clk *clk) {}
+
+static inline unsigned long clk_get_rate(struct clk *clk)
+{
+       return 0;
+}
+
+static inline int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+       return 0;
+}
+
+static inline long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+       return 0;
+}
+
+static inline int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+       return 0;
+}
+
+static inline struct clk *clk_get_parent(struct clk *clk)
+{
+       return NULL;
+}
+
+#endif
+
+/* clk_prepare_enable helps cases using clk_enable in non-atomic context. */
+static inline int clk_prepare_enable(struct clk *clk)
+{
+       int ret;
+
+       ret = clk_prepare(clk);
+       if (ret)
+               return ret;
+       ret = clk_enable(clk);
+       if (ret)
+               clk_unprepare(clk);
+
+       return ret;
+}
+
+/* clk_disable_unprepare helps cases using clk_disable in non-atomic context. */
+static inline void clk_disable_unprepare(struct clk *clk)
+{
+       clk_disable(clk);
+       clk_unprepare(clk);
+}
+
 /**
  * clk_add_alias - add a new clock alias
  * @alias: name for clock alias
index 51a90b7f2d606a3dcb9bb582cd209add0c429351..133ddcf83397036a5c20ed6431db3b789eeab37a 100644 (file)
@@ -58,7 +58,7 @@ static inline bool compaction_deferred(struct zone *zone, int order)
        if (++zone->compact_considered > defer_limit)
                zone->compact_considered = defer_limit;
 
-       return zone->compact_considered < (1UL << zone->compact_defer_shift);
+       return zone->compact_considered < defer_limit;
 }
 
 #else
@@ -85,7 +85,7 @@ static inline void defer_compaction(struct zone *zone, int order)
 
 static inline bool compaction_deferred(struct zone *zone, int order)
 {
-       return 1;
+       return true;
 }
 
 #endif /* CONFIG_COMPACTION */
index 4e890394ef996e709c490439be23f0c6fe24292f..09b28b7369d77e1ebedfe9a03017860df4d09fc3 100644 (file)
@@ -265,9 +265,9 @@ long compat_sys_shmat(int first, int second, compat_uptr_t third, int version,
 #else
 long compat_sys_semctl(int semid, int semnum, int cmd, int arg);
 long compat_sys_msgsnd(int msqid, struct compat_msgbuf __user *msgp,
-               size_t msgsz, int msgflg);
+               compat_ssize_t msgsz, int msgflg);
 long compat_sys_msgrcv(int msqid, struct compat_msgbuf __user *msgp,
-               size_t msgsz, long msgtyp, int msgflg);
+               compat_ssize_t msgsz, long msgtyp, int msgflg);
 long compat_sys_shmat(int shmid, compat_uptr_t shmaddr, int shmflg);
 #endif
 long compat_sys_msgctl(int first, int second, void __user *uptr);
index 7c4750811b966e7d865484c2cf7020199c628164..25baa287cff7287d8e6cc6f5b94255ef4cf86f9e 100644 (file)
@@ -154,6 +154,14 @@ struct crush_map {
        __s32 max_buckets;
        __u32 max_rules;
        __s32 max_devices;
+
+       /* choose local retries before re-descent */
+       __u32 choose_local_tries;
+       /* choose local attempts using a fallback permutation before
+        * re-descent */
+       __u32 choose_local_fallback_tries;
+       /* choose attempts before giving up */ 
+       __u32 choose_total_tries;
 };
 
 
index 98f34b886f955db4e9c7ecd63d892bafd30e6f1f..38d27a10aa5d9f922a14dd5746b9e29f34bce633 100644 (file)
@@ -66,14 +66,13 @@ typedef int (*dm_request_endio_fn) (struct dm_target *ti,
                                    struct request *clone, int error,
                                    union map_info *map_context);
 
-typedef void (*dm_flush_fn) (struct dm_target *ti);
 typedef void (*dm_presuspend_fn) (struct dm_target *ti);
 typedef void (*dm_postsuspend_fn) (struct dm_target *ti);
 typedef int (*dm_preresume_fn) (struct dm_target *ti);
 typedef void (*dm_resume_fn) (struct dm_target *ti);
 
 typedef int (*dm_status_fn) (struct dm_target *ti, status_type_t status_type,
-                            char *result, unsigned int maxlen);
+                            unsigned status_flags, char *result, unsigned maxlen);
 
 typedef int (*dm_message_fn) (struct dm_target *ti, unsigned argc, char **argv);
 
@@ -139,7 +138,6 @@ struct target_type {
        dm_map_request_fn map_rq;
        dm_endio_fn end_io;
        dm_request_endio_fn rq_end_io;
-       dm_flush_fn flush;
        dm_presuspend_fn presuspend;
        dm_postsuspend_fn postsuspend;
        dm_preresume_fn preresume;
@@ -188,8 +186,8 @@ struct dm_target {
        sector_t begin;
        sector_t len;
 
-       /* Always a power of 2 */
-       sector_t split_io;
+       /* If non-zero, maximum size of I/O submitted to a target. */
+       uint32_t max_io_len;
 
        /*
         * A number of zero-length barrier requests that will be submitted
@@ -213,16 +211,28 @@ struct dm_target {
        /* Used to provide an error string from the ctr */
        char *error;
 
+       /*
+        * Set if this target needs to receive flushes regardless of
+        * whether or not its underlying devices have support.
+        */
+       bool flush_supported:1;
+
        /*
         * Set if this target needs to receive discards regardless of
         * whether or not its underlying devices have support.
         */
-       unsigned discards_supported:1;
+       bool discards_supported:1;
+
+       /*
+        * Set if the target required discard request to be split
+        * on max_io_len boundary.
+        */
+       bool split_discard_requests:1;
 
        /*
         * Set if this target does not return zeroes on discarded blocks.
         */
-       unsigned discard_zeroes_data_unsupported:1;
+       bool discard_zeroes_data_unsupported:1;
 };
 
 /* Each target can link one of these into the table */
@@ -359,6 +369,11 @@ void dm_table_add_target_callbacks(struct dm_table *t, struct dm_target_callback
  */
 int dm_table_complete(struct dm_table *t);
 
+/*
+ * Target may require that it is never sent I/O larger than len.
+ */
+int __must_check dm_set_target_max_io_len(struct dm_target *ti, sector_t len);
+
 /*
  * Table reference counting.
  */
index 75fd5573516e15f04bcc864140620977405362f6..91e3a360f61103451986008553dd91567e68fe13 100644 (file)
@@ -267,9 +267,9 @@ enum {
 #define DM_DEV_SET_GEOMETRY    _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
 
 #define DM_VERSION_MAJOR       4
-#define DM_VERSION_MINOR       22
+#define DM_VERSION_MINOR       23
 #define DM_VERSION_PATCHLEVEL  0
-#define DM_VERSION_EXTRA       "-ioctl (2011-10-19)"
+#define DM_VERSION_EXTRA       "-ioctl (2012-07-25)"
 
 /* Status bits */
 #define DM_READONLY_FLAG       (1 << 0) /* In/Out */
@@ -307,6 +307,8 @@ enum {
 
 /*
  * Set this to suspend without flushing queued ios.
+ * Also disables flushing uncommitted changes in the thin target before
+ * generating statistics for DM_TABLE_STATUS and DM_DEV_WAIT.
  */
 #define DM_NOFLUSH_FLAG                (1 << 11) /* In */
 
index 547ab568d3ae83f940e503c6999719f37a73547e..f83f793223ff7491f59c44b3c3bd6d7440b51084 100644 (file)
@@ -15,6 +15,8 @@ enum dma_attr {
        DMA_ATTR_WEAK_ORDERING,
        DMA_ATTR_WRITE_COMBINE,
        DMA_ATTR_NON_CONSISTENT,
+       DMA_ATTR_NO_KERNEL_MAPPING,
+       DMA_ATTR_SKIP_CPU_SYNC,
        DMA_ATTR_MAX,
 };
 
index dfc099e56a66187ba9d223eea924da5650e462bf..94af418585135a6fdaabb987e284caad4a8b1e99 100644 (file)
@@ -18,6 +18,9 @@ struct dma_map_ops {
        int (*mmap)(struct device *, struct vm_area_struct *,
                          void *, dma_addr_t, size_t, struct dma_attrs *attrs);
 
+       int (*get_sgtable)(struct device *dev, struct sg_table *sgt, void *,
+                          dma_addr_t, size_t, struct dma_attrs *attrs);
+
        dma_addr_t (*map_page)(struct device *dev, struct page *page,
                               unsigned long offset, size_t size,
                               enum dma_data_direction dir,
index 91ba3bae42ee53b7dac7c891284cee2b32ecb32a..bab9f8473dc1c7673c7e900e73691d38ee35e737 100644 (file)
 #define _LINUX_EDAC_H_
 
 #include <linux/atomic.h>
+#include <linux/device.h>
 #include <linux/kobject.h>
 #include <linux/completion.h>
 #include <linux/workqueue.h>
+#include <linux/debugfs.h>
 
 struct device;
 
@@ -49,7 +51,19 @@ static inline void opstate_init(void)
 #define EDAC_MC_LABEL_LEN      31
 #define MC_PROC_NAME_MAX_LEN   7
 
-/* memory devices */
+/**
+ * enum dev_type - describe the type of memory DRAM chips used at the stick
+ * @DEV_UNKNOWN:       Can't be determined, or MC doesn't support detect it
+ * @DEV_X1:            1 bit for data
+ * @DEV_X2:            2 bits for data
+ * @DEV_X4:            4 bits for data
+ * @DEV_X8:            8 bits for data
+ * @DEV_X16:           16 bits for data
+ * @DEV_X32:           32 bits for data
+ * @DEV_X64:           64 bits for data
+ *
+ * Typical values are x4 and x8.
+ */
 enum dev_type {
        DEV_UNKNOWN = 0,
        DEV_X1,
@@ -167,18 +181,30 @@ enum mem_type {
 #define MEM_FLAG_DDR3           BIT(MEM_DDR3)
 #define MEM_FLAG_RDDR3          BIT(MEM_RDDR3)
 
-/* chipset Error Detection and Correction capabilities and mode */
+/**
+ * enum edac-type - Error Detection and Correction capabilities and mode
+ * @EDAC_UNKNOWN:      Unknown if ECC is available
+ * @EDAC_NONE:         Doesn't support ECC
+ * @EDAC_RESERVED:     Reserved ECC type
+ * @EDAC_PARITY:       Detects parity errors
+ * @EDAC_EC:           Error Checking - no correction
+ * @EDAC_SECDED:       Single bit error correction, Double detection
+ * @EDAC_S2ECD2ED:     Chipkill x2 devices - do these exist?
+ * @EDAC_S4ECD4ED:     Chipkill x4 devices
+ * @EDAC_S8ECD8ED:     Chipkill x8 devices
+ * @EDAC_S16ECD16ED:   Chipkill x16 devices
+ */
 enum edac_type {
-       EDAC_UNKNOWN = 0,       /* Unknown if ECC is available */
-       EDAC_NONE,              /* Doesn't support ECC */
-       EDAC_RESERVED,          /* Reserved ECC type */
-       EDAC_PARITY,            /* Detects parity errors */
-       EDAC_EC,                /* Error Checking - no correction */
-       EDAC_SECDED,            /* Single bit error correction, Double detection */
-       EDAC_S2ECD2ED,          /* Chipkill x2 devices - do these exist? */
-       EDAC_S4ECD4ED,          /* Chipkill x4 devices */
-       EDAC_S8ECD8ED,          /* Chipkill x8 devices */
-       EDAC_S16ECD16ED,        /* Chipkill x16 devices */
+       EDAC_UNKNOWN =  0,
+       EDAC_NONE,
+       EDAC_RESERVED,
+       EDAC_PARITY,
+       EDAC_EC,
+       EDAC_SECDED,
+       EDAC_S2ECD2ED,
+       EDAC_S4ECD4ED,
+       EDAC_S8ECD8ED,
+       EDAC_S16ECD16ED,
 };
 
 #define EDAC_FLAG_UNKNOWN      BIT(EDAC_UNKNOWN)
@@ -191,18 +217,30 @@ enum edac_type {
 #define EDAC_FLAG_S8ECD8ED     BIT(EDAC_S8ECD8ED)
 #define EDAC_FLAG_S16ECD16ED   BIT(EDAC_S16ECD16ED)
 
-/* scrubbing capabilities */
+/**
+ * enum scrub_type - scrubbing capabilities
+ * @SCRUB_UNKNOWN              Unknown if scrubber is available
+ * @SCRUB_NONE:                        No scrubber
+ * @SCRUB_SW_PROG:             SW progressive (sequential) scrubbing
+ * @SCRUB_SW_SRC:              Software scrub only errors
+ * @SCRUB_SW_PROG_SRC:         Progressive software scrub from an error
+ * @SCRUB_SW_TUNABLE:          Software scrub frequency is tunable
+ * @SCRUB_HW_PROG:             HW progressive (sequential) scrubbing
+ * @SCRUB_HW_SRC:              Hardware scrub only errors
+ * @SCRUB_HW_PROG_SRC:         Progressive hardware scrub from an error
+ * SCRUB_HW_TUNABLE:           Hardware scrub frequency is tunable
+ */
 enum scrub_type {
-       SCRUB_UNKNOWN = 0,      /* Unknown if scrubber is available */
-       SCRUB_NONE,             /* No scrubber */
-       SCRUB_SW_PROG,          /* SW progressive (sequential) scrubbing */
-       SCRUB_SW_SRC,           /* Software scrub only errors */
-       SCRUB_SW_PROG_SRC,      /* Progressive software scrub from an error */
-       SCRUB_SW_TUNABLE,       /* Software scrub frequency is tunable */
-       SCRUB_HW_PROG,          /* HW progressive (sequential) scrubbing */
-       SCRUB_HW_SRC,           /* Hardware scrub only errors */
-       SCRUB_HW_PROG_SRC,      /* Progressive hardware scrub from an error */
-       SCRUB_HW_TUNABLE        /* Hardware scrub frequency is tunable */
+       SCRUB_UNKNOWN = 0,
+       SCRUB_NONE,
+       SCRUB_SW_PROG,
+       SCRUB_SW_SRC,
+       SCRUB_SW_PROG_SRC,
+       SCRUB_SW_TUNABLE,
+       SCRUB_HW_PROG,
+       SCRUB_HW_SRC,
+       SCRUB_HW_PROG_SRC,
+       SCRUB_HW_TUNABLE
 };
 
 #define SCRUB_FLAG_SW_PROG     BIT(SCRUB_SW_PROG)
@@ -374,23 +412,21 @@ struct edac_mc_layer {
 #define EDAC_MAX_LAYERS                3
 
 /**
- * EDAC_DIMM_PTR - Macro responsible to find a pointer inside a pointer array
+ * EDAC_DIMM_OFF - Macro responsible to get a pointer offset inside a pointer array
  *                for the element given by [layer0,layer1,layer2] position
  *
  * @layers:    a struct edac_mc_layer array, describing how many elements
  *             were allocated for each layer
- * @var:       name of the var where we want to get the pointer
- *             (like mci->dimms)
  * @n_layers:  Number of layers at the @layers array
  * @layer0:    layer0 position
  * @layer1:    layer1 position. Unused if n_layers < 2
  * @layer2:    layer2 position. Unused if n_layers < 3
  *
- * For 1 layer, this macro returns &var[layer0]
+ * For 1 layer, this macro returns &var[layer0] - &var
  * For 2 layers, this macro is similar to allocate a bi-dimensional array
- *             and to return "&var[layer0][layer1]"
+ *             and to return "&var[layer0][layer1] - &var"
  * For 3 layers, this macro is similar to allocate a tri-dimensional array
- *             and to return "&var[layer0][layer1][layer2]"
+ *             and to return "&var[layer0][layer1][layer2] - &var"
  *
  * A loop could be used here to make it more generic, but, as we only have
  * 3 layers, this is a little faster.
@@ -398,23 +434,52 @@ struct edac_mc_layer {
  * a NULL is returned, causing an OOPS during the memory allocation routine,
  * with would point to the developer that he's doing something wrong.
  */
-#define EDAC_DIMM_PTR(layers, var, nlayers, layer0, layer1, layer2) ({ \
-       typeof(var) __p;                                                \
+#define EDAC_DIMM_OFF(layers, nlayers, layer0, layer1, layer2) ({              \
+       int __i;                                                        \
        if ((nlayers) == 1)                                             \
-               __p = &var[layer0];                                     \
+               __i = layer0;                                           \
        else if ((nlayers) == 2)                                        \
-               __p = &var[(layer1) + ((layers[1]).size * (layer0))];   \
+               __i = (layer1) + ((layers[1]).size * (layer0));         \
        else if ((nlayers) == 3)                                        \
-               __p = &var[(layer2) + ((layers[2]).size * ((layer1) +   \
-                           ((layers[1]).size * (layer0))))];           \
+               __i = (layer2) + ((layers[2]).size * ((layer1) +        \
+                           ((layers[1]).size * (layer0))))           \
        else                                                            \
+               __i = -EINVAL;                                          \
+       __i;                                                            \
+})
+
+/**
+ * EDAC_DIMM_PTR - Macro responsible to get a pointer inside a pointer array
+ *                for the element given by [layer0,layer1,layer2] position
+ *
+ * @layers:    a struct edac_mc_layer array, describing how many elements
+ *             were allocated for each layer
+ * @var:       name of the var where we want to get the pointer
+ *             (like mci->dimms)
+ * @n_layers:  Number of layers at the @layers array
+ * @layer0:    layer0 position
+ * @layer1:    layer1 position. Unused if n_layers < 2
+ * @layer2:    layer2 position. Unused if n_layers < 3
+ *
+ * For 1 layer, this macro returns &var[layer0]
+ * For 2 layers, this macro is similar to allocate a bi-dimensional array
+ *             and to return "&var[layer0][layer1]"
+ * For 3 layers, this macro is similar to allocate a tri-dimensional array
+ *             and to return "&var[layer0][layer1][layer2]"
+ */
+#define EDAC_DIMM_PTR(layers, var, nlayers, layer0, layer1, layer2) ({ \
+       typeof(*var) __p;                                               \
+       int ___i = EDAC_DIMM_OFF(layers, nlayers, layer0, layer1, layer2);      \
+       if (___i < 0)                                                   \
                __p = NULL;                                             \
+       else                                                            \
+               __p = (var)[___i];                                      \
        __p;                                                            \
 })
 
-
-/* FIXME: add the proper per-location error counts */
 struct dimm_info {
+       struct device dev;
+
        char label[EDAC_MC_LABEL_LEN + 1];      /* DIMM label on motherboard */
 
        /* Memory location data */
@@ -456,6 +521,8 @@ struct rank_info {
 };
 
 struct csrow_info {
+       struct device dev;
+
        /* Used only by edac_mc_find_csrow_by_page() */
        unsigned long first_page;       /* first page number in csrow */
        unsigned long last_page;        /* last page number in csrow */
@@ -469,44 +536,26 @@ struct csrow_info {
 
        struct mem_ctl_info *mci;       /* the parent */
 
-       struct kobject kobj;    /* sysfs kobject for this csrow */
-
        /* channel information for this csrow */
        u32 nr_channels;
-       struct rank_info *channels;
+       struct rank_info **channels;
 };
 
-struct mcidev_sysfs_group {
-       const char *name;                               /* group name */
-       const struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */
-};
-
-struct mcidev_sysfs_group_kobj {
-       struct list_head list;          /* list for all instances within a mc */
-
-       struct kobject kobj;            /* kobj for the group */
-
-       const struct mcidev_sysfs_group *grp;   /* group description table */
-       struct mem_ctl_info *mci;       /* the parent */
-};
-
-/* mcidev_sysfs_attribute structure
- *     used for driver sysfs attributes and in mem_ctl_info
- *     sysfs top level entries
+/*
+ * struct errcount_attribute - used to store the several error counts
  */
-struct mcidev_sysfs_attribute {
-       /* It should use either attr or grp */
-       struct attribute attr;
-       const struct mcidev_sysfs_group *grp;   /* Points to a group of attributes */
-
-       /* Ops for show/store values at the attribute - not used on group */
-        ssize_t (*show)(struct mem_ctl_info *,char *);
-        ssize_t (*store)(struct mem_ctl_info *, const char *,size_t);
+struct errcount_attribute_data {
+       int n_layers;
+       int pos[EDAC_MAX_LAYERS];
+       int layer0, layer1, layer2;
 };
 
 /* MEMORY controller information structure
  */
 struct mem_ctl_info {
+       struct device                   dev;
+       struct bus_type                 bus;
+
        struct list_head link;  /* for global list of mem_ctl_info structs */
 
        struct module *owner;   /* Module owner of this control struct */
@@ -548,10 +597,18 @@ struct mem_ctl_info {
        unsigned long (*ctl_page_to_phys) (struct mem_ctl_info * mci,
                                           unsigned long page);
        int mc_idx;
-       struct csrow_info *csrows;
+       struct csrow_info **csrows;
        unsigned nr_csrows, num_cschannel;
 
-       /* Memory Controller hierarchy */
+       /*
+        * Memory Controller hierarchy
+        *
+        * There are basically two types of memory controller: the ones that
+        * sees memory sticks ("dimms"), and the ones that sees memory ranks.
+        * All old memory controllers enumerate memories per rank, but most
+        * of the recent drivers enumerate memories per DIMM, instead.
+        * When the memory controller is per rank, mem_is_per_rank is true.
+        */
        unsigned n_layers;
        struct edac_mc_layer *layers;
        bool mem_is_per_rank;
@@ -560,14 +617,14 @@ struct mem_ctl_info {
         * DIMM info. Will eventually remove the entire csrows_info some day
         */
        unsigned tot_dimms;
-       struct dimm_info *dimms;
+       struct dimm_info **dimms;
 
        /*
         * FIXME - what about controllers on other busses? - IDs must be
         * unique.  dev pointer should be sufficiently unique, but
         * BUS:SLOT.FUNC numbers may not be unique.
         */
-       struct device *dev;
+       struct device *pdev;
        const char *mod_name;
        const char *mod_ver;
        const char *ctl_name;
@@ -586,12 +643,6 @@ struct mem_ctl_info {
 
        struct completion complete;
 
-       /* edac sysfs device control */
-       struct kobject edac_mci_kobj;
-
-       /* list for all grp instances within a mc */
-       struct list_head grp_kobj_list;
-
        /* Additional top controller level attributes, but specified
         * by the low level driver.
         *
@@ -609,6 +660,13 @@ struct mem_ctl_info {
 
        /* the internal state of this controller instance */
        int op_state;
+
+#ifdef CONFIG_EDAC_DEBUG
+       struct dentry *debugfs;
+       u8 fake_inject_layer[EDAC_MAX_LAYERS];
+       u32 fake_inject_ue;
+       u16 fake_inject_count;
+#endif
 };
 
 #endif
index 7edcf10317182a32c4ffeef84b5c6dd1d7ed091c..db04ec5121cb770d6c571a59234997c60527e539 100644 (file)
@@ -152,7 +152,7 @@ static inline void fw_card_put(struct fw_card *card)
 struct fw_attribute_group {
        struct attribute_group *groups[2];
        struct attribute_group group;
-       struct attribute *attrs[12];
+       struct attribute *attrs[13];
 };
 
 enum fw_device_state {
@@ -321,7 +321,7 @@ struct fw_transaction {
 
 struct fw_address_handler {
        u64 offset;
-       size_t length;
+       u64 length;
        fw_address_callback_t address_callback;
        void *callback_data;
        struct list_head link;
diff --git a/include/linux/flex_proportions.h b/include/linux/flex_proportions.h
new file mode 100644 (file)
index 0000000..4ebc49f
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Floating proportions with flexible aging period
+ *
+ *  Copyright (C) 2011, SUSE, Jan Kara <jack@suse.cz>
+ */
+
+#ifndef _LINUX_FLEX_PROPORTIONS_H
+#define _LINUX_FLEX_PROPORTIONS_H
+
+#include <linux/percpu_counter.h>
+#include <linux/spinlock.h>
+#include <linux/seqlock.h>
+
+/*
+ * When maximum proportion of some event type is specified, this is the
+ * precision with which we allow limitting. Note that this creates an upper
+ * bound on the number of events per period like
+ *   ULLONG_MAX >> FPROP_FRAC_SHIFT.
+ */
+#define FPROP_FRAC_SHIFT 10
+#define FPROP_FRAC_BASE (1UL << FPROP_FRAC_SHIFT)
+
+/*
+ * ---- Global proportion definitions ----
+ */
+struct fprop_global {
+       /* Number of events in the current period */
+       struct percpu_counter events;
+       /* Current period */
+       unsigned int period;
+       /* Synchronization with period transitions */
+       seqcount_t sequence;
+};
+
+int fprop_global_init(struct fprop_global *p);
+void fprop_global_destroy(struct fprop_global *p);
+bool fprop_new_period(struct fprop_global *p, int periods);
+
+/*
+ *  ---- SINGLE ----
+ */
+struct fprop_local_single {
+       /* the local events counter */
+       unsigned long events;
+       /* Period in which we last updated events */
+       unsigned int period;
+       raw_spinlock_t lock;    /* Protect period and numerator */
+};
+
+#define INIT_FPROP_LOCAL_SINGLE(name)                  \
+{      .lock = __RAW_SPIN_LOCK_UNLOCKED(name.lock),    \
+}
+
+int fprop_local_init_single(struct fprop_local_single *pl);
+void fprop_local_destroy_single(struct fprop_local_single *pl);
+void __fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl);
+void fprop_fraction_single(struct fprop_global *p,
+       struct fprop_local_single *pl, unsigned long *numerator,
+       unsigned long *denominator);
+
+static inline
+void fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       __fprop_inc_single(p, pl);
+       local_irq_restore(flags);
+}
+
+/*
+ * ---- PERCPU ----
+ */
+struct fprop_local_percpu {
+       /* the local events counter */
+       struct percpu_counter events;
+       /* Period in which we last updated events */
+       unsigned int period;
+       raw_spinlock_t lock;    /* Protect period and numerator */
+};
+
+int fprop_local_init_percpu(struct fprop_local_percpu *pl);
+void fprop_local_destroy_percpu(struct fprop_local_percpu *pl);
+void __fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl);
+void __fprop_inc_percpu_max(struct fprop_global *p, struct fprop_local_percpu *pl,
+                           int max_frac);
+void fprop_fraction_percpu(struct fprop_global *p,
+       struct fprop_local_percpu *pl, unsigned long *numerator,
+       unsigned long *denominator);
+
+static inline
+void fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       __fprop_inc_percpu(p, pl);
+       local_irq_restore(flags);
+}
+
+#endif
index 8fabb037a48db40e8a490fc74c15ce907ad9059a..38dba16c417646ff28f9ea78dea28a268d65d125 100644 (file)
@@ -165,6 +165,8 @@ struct inodes_stat_t {
 #define READ                   0
 #define WRITE                  RW_MASK
 #define READA                  RWA_MASK
+#define KERNEL_READ            (READ|REQ_KERNEL)
+#define KERNEL_WRITE           (WRITE|REQ_KERNEL)
 
 #define READ_SYNC              (READ | REQ_SYNC)
 #define WRITE_SYNC             (WRITE | REQ_SYNC | REQ_NOIDLE)
@@ -412,6 +414,7 @@ struct inodes_stat_t {
 #include <linux/shrinker.h>
 #include <linux/migrate_mode.h>
 #include <linux/uidgid.h>
+#include <linux/lockdep.h>
 
 #include <asm/byteorder.h>
 
@@ -427,6 +430,7 @@ struct kstatfs;
 struct vm_area_struct;
 struct vfsmount;
 struct cred;
+struct swap_info_struct;
 
 extern void __init inode_init(void);
 extern void __init inode_init_early(void);
@@ -437,6 +441,8 @@ extern unsigned long get_max_files(void);
 extern int sysctl_nr_open;
 extern struct inodes_stat_t inodes_stat;
 extern int leases_enable, lease_break_time;
+extern int sysctl_protected_symlinks;
+extern int sysctl_protected_hardlinks;
 
 struct buffer_head;
 typedef int (get_block_t)(struct inode *inode, sector_t iblock,
@@ -636,6 +642,11 @@ struct address_space_operations {
        int (*is_partially_uptodate) (struct page *, read_descriptor_t *,
                                        unsigned long);
        int (*error_remove_page)(struct address_space *, struct page *);
+
+       /* swapfile support */
+       int (*swap_activate)(struct swap_info_struct *sis, struct file *file,
+                               sector_t *span);
+       void (*swap_deactivate)(struct file *file);
 };
 
 extern const struct address_space_operations empty_aops;
@@ -1154,7 +1165,6 @@ struct lock_manager_operations {
        int (*lm_compare_owner)(struct file_lock *, struct file_lock *);
        void (*lm_notify)(struct file_lock *);  /* unblock callback */
        int (*lm_grant)(struct file_lock *, struct file_lock *, int);
-       void (*lm_release_private)(struct file_lock *);
        void (*lm_break)(struct file_lock *);
        int (*lm_change)(struct file_lock **, int);
 };
@@ -1163,9 +1173,10 @@ struct lock_manager {
        struct list_head list;
 };
 
-void locks_start_grace(struct lock_manager *);
+struct net;
+void locks_start_grace(struct net *, struct lock_manager *);
 void locks_end_grace(struct lock_manager *);
-int locks_in_grace(void);
+int locks_in_grace(struct net *);
 
 /* that will die - we need it for nfs_lock_info */
 #include <linux/nfs_fs_i.h>
@@ -1437,6 +1448,8 @@ extern void f_delown(struct file *filp);
 extern pid_t f_getown(struct file *filp);
 extern int send_sigurg(struct fown_struct *fown);
 
+struct mm_struct;
+
 /*
  *     Umount options
  */
@@ -1450,6 +1463,31 @@ extern int send_sigurg(struct fown_struct *fown);
 extern struct list_head super_blocks;
 extern spinlock_t sb_lock;
 
+/* Possible states of 'frozen' field */
+enum {
+       SB_UNFROZEN = 0,                /* FS is unfrozen */
+       SB_FREEZE_WRITE = 1,            /* Writes, dir ops, ioctls frozen */
+       SB_FREEZE_PAGEFAULT = 2,        /* Page faults stopped as well */
+       SB_FREEZE_FS = 3,               /* For internal FS use (e.g. to stop
+                                        * internal threads if needed) */
+       SB_FREEZE_COMPLETE = 4,         /* ->freeze_fs finished successfully */
+};
+
+#define SB_FREEZE_LEVELS (SB_FREEZE_COMPLETE - 1)
+
+struct sb_writers {
+       /* Counters for counting writers at each level */
+       struct percpu_counter   counter[SB_FREEZE_LEVELS];
+       wait_queue_head_t       wait;           /* queue for waiting for
+                                                  writers / faults to finish */
+       int                     frozen;         /* Is sb frozen? */
+       wait_queue_head_t       wait_unfrozen;  /* queue for waiting for
+                                                  sb to be thawed */
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       struct lockdep_map      lock_map[SB_FREEZE_LEVELS];
+#endif
+};
+
 struct super_block {
        struct list_head        s_list;         /* Keep this first */
        dev_t                   s_dev;          /* search index; _not_ kdev_t */
@@ -1497,8 +1535,7 @@ struct super_block {
        struct hlist_node       s_instances;
        struct quota_info       s_dquot;        /* Diskquota specific options */
 
-       int                     s_frozen;
-       wait_queue_head_t       s_wait_unfrozen;
+       struct sb_writers       s_writers;
 
        char s_id[32];                          /* Informational name */
        u8 s_uuid[16];                          /* UUID */
@@ -1553,14 +1590,117 @@ extern struct timespec current_fs_time(struct super_block *sb);
 /*
  * Snapshotting support.
  */
-enum {
-       SB_UNFROZEN = 0,
-       SB_FREEZE_WRITE = 1,
-       SB_FREEZE_TRANS = 2,
-};
 
-#define vfs_check_frozen(sb, level) \
-       wait_event((sb)->s_wait_unfrozen, ((sb)->s_frozen < (level)))
+void __sb_end_write(struct super_block *sb, int level);
+int __sb_start_write(struct super_block *sb, int level, bool wait);
+
+/**
+ * sb_end_write - drop write access to a superblock
+ * @sb: the super we wrote to
+ *
+ * Decrement number of writers to the filesystem. Wake up possible waiters
+ * wanting to freeze the filesystem.
+ */
+static inline void sb_end_write(struct super_block *sb)
+{
+       __sb_end_write(sb, SB_FREEZE_WRITE);
+}
+
+/**
+ * sb_end_pagefault - drop write access to a superblock from a page fault
+ * @sb: the super we wrote to
+ *
+ * Decrement number of processes handling write page fault to the filesystem.
+ * Wake up possible waiters wanting to freeze the filesystem.
+ */
+static inline void sb_end_pagefault(struct super_block *sb)
+{
+       __sb_end_write(sb, SB_FREEZE_PAGEFAULT);
+}
+
+/**
+ * sb_end_intwrite - drop write access to a superblock for internal fs purposes
+ * @sb: the super we wrote to
+ *
+ * Decrement fs-internal number of writers to the filesystem.  Wake up possible
+ * waiters wanting to freeze the filesystem.
+ */
+static inline void sb_end_intwrite(struct super_block *sb)
+{
+       __sb_end_write(sb, SB_FREEZE_FS);
+}
+
+/**
+ * sb_start_write - get write access to a superblock
+ * @sb: the super we write to
+ *
+ * When a process wants to write data or metadata to a file system (i.e. dirty
+ * a page or an inode), it should embed the operation in a sb_start_write() -
+ * sb_end_write() pair to get exclusion against file system freezing. This
+ * function increments number of writers preventing freezing. If the file
+ * system is already frozen, the function waits until the file system is
+ * thawed.
+ *
+ * Since freeze protection behaves as a lock, users have to preserve
+ * ordering of freeze protection and other filesystem locks. Generally,
+ * freeze protection should be the outermost lock. In particular, we have:
+ *
+ * sb_start_write
+ *   -> i_mutex                        (write path, truncate, directory ops, ...)
+ *   -> s_umount               (freeze_super, thaw_super)
+ */
+static inline void sb_start_write(struct super_block *sb)
+{
+       __sb_start_write(sb, SB_FREEZE_WRITE, true);
+}
+
+static inline int sb_start_write_trylock(struct super_block *sb)
+{
+       return __sb_start_write(sb, SB_FREEZE_WRITE, false);
+}
+
+/**
+ * sb_start_pagefault - get write access to a superblock from a page fault
+ * @sb: the super we write to
+ *
+ * When a process starts handling write page fault, it should embed the
+ * operation into sb_start_pagefault() - sb_end_pagefault() pair to get
+ * exclusion against file system freezing. This is needed since the page fault
+ * is going to dirty a page. This function increments number of running page
+ * faults preventing freezing. If the file system is already frozen, the
+ * function waits until the file system is thawed.
+ *
+ * Since page fault freeze protection behaves as a lock, users have to preserve
+ * ordering of freeze protection and other filesystem locks. It is advised to
+ * put sb_start_pagefault() close to mmap_sem in lock ordering. Page fault
+ * handling code implies lock dependency:
+ *
+ * mmap_sem
+ *   -> sb_start_pagefault
+ */
+static inline void sb_start_pagefault(struct super_block *sb)
+{
+       __sb_start_write(sb, SB_FREEZE_PAGEFAULT, true);
+}
+
+/*
+ * sb_start_intwrite - get write access to a superblock for internal fs purposes
+ * @sb: the super we write to
+ *
+ * This is the third level of protection against filesystem freezing. It is
+ * free for use by a filesystem. The only requirement is that it must rank
+ * below sb_start_pagefault.
+ *
+ * For example filesystem can call sb_start_intwrite() when starting a
+ * transaction which somewhat eases handling of freezing for internal sources
+ * of filesystem changes (internal fs threads, discarding preallocation on file
+ * close, etc.).
+ */
+static inline void sb_start_intwrite(struct super_block *sb)
+{
+       __sb_start_write(sb, SB_FREEZE_FS, true);
+}
+
 
 extern bool inode_owner_or_capable(const struct inode *inode);
 
@@ -1884,6 +2024,7 @@ struct file_system_type {
        struct lock_class_key s_lock_key;
        struct lock_class_key s_umount_key;
        struct lock_class_key s_vfs_rename_key;
+       struct lock_class_key s_writers_key[SB_FREEZE_LEVELS];
 
        struct lock_class_key i_lock_key;
        struct lock_class_key i_mutex_key;
@@ -2326,9 +2467,6 @@ static inline void i_readcount_inc(struct inode *inode)
 }
 #endif
 extern int do_pipe_flags(int *, int);
-extern struct file *create_read_pipe(struct file *f, int flags);
-extern struct file *create_write_pipe(int flags);
-extern void free_write_pipe(struct file *);
 
 extern int kernel_read(struct file *, loff_t, char *, unsigned long);
 extern struct file * open_exec(const char *);
index ae0aaa9d42faacf9d250ae509a93947fc1d06424..4f440b3e89fe7fd7e47ead6c6ed911f173205431 100644 (file)
@@ -97,7 +97,13 @@ struct partition_meta_info {
 
 struct hd_struct {
        sector_t start_sect;
+       /*
+        * nr_sects is protected by sequence counter. One might extend a
+        * partition while IO is happening to it and update of nr_sects
+        * can be non-atomic on 32bit machines with 64bit sector_t.
+        */
        sector_t nr_sects;
+       seqcount_t nr_sects_seq;
        sector_t alignment_offset;
        unsigned int discard_alignment;
        struct device __dev;
@@ -647,6 +653,57 @@ static inline void hd_struct_put(struct hd_struct *part)
                __delete_partition(part);
 }
 
+/*
+ * Any access of part->nr_sects which is not protected by partition
+ * bd_mutex or gendisk bdev bd_mutex, should be done using this
+ * accessor function.
+ *
+ * Code written along the lines of i_size_read() and i_size_write().
+ * CONFIG_PREEMPT case optimizes the case of UP kernel with preemption
+ * on.
+ */
+static inline sector_t part_nr_sects_read(struct hd_struct *part)
+{
+#if BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_SMP)
+       sector_t nr_sects;
+       unsigned seq;
+       do {
+               seq = read_seqcount_begin(&part->nr_sects_seq);
+               nr_sects = part->nr_sects;
+       } while (read_seqcount_retry(&part->nr_sects_seq, seq));
+       return nr_sects;
+#elif BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_PREEMPT)
+       sector_t nr_sects;
+
+       preempt_disable();
+       nr_sects = part->nr_sects;
+       preempt_enable();
+       return nr_sects;
+#else
+       return part->nr_sects;
+#endif
+}
+
+/*
+ * Should be called with mutex lock held (typically bd_mutex) of partition
+ * to provide mutual exlusion among writers otherwise seqcount might be
+ * left in wrong state leaving the readers spinning infinitely.
+ */
+static inline void part_nr_sects_write(struct hd_struct *part, sector_t size)
+{
+#if BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_SMP)
+       write_seqcount_begin(&part->nr_sects_seq);
+       part->nr_sects = size;
+       write_seqcount_end(&part->nr_sects_seq);
+#elif BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_PREEMPT)
+       preempt_disable();
+       part->nr_sects = size;
+       preempt_enable();
+#else
+       part->nr_sects = size;
+#endif
+}
+
 #else /* CONFIG_BLOCK */
 
 static inline void printk_all_partitions(void) { }
index 1e49be49d3247cb74cc798aa2772a085102851a5..4883f393f50a8049cc83298068b5751621a7a8dc 100644 (file)
@@ -23,6 +23,7 @@ struct vm_area_struct;
 #define ___GFP_REPEAT          0x400u
 #define ___GFP_NOFAIL          0x800u
 #define ___GFP_NORETRY         0x1000u
+#define ___GFP_MEMALLOC                0x2000u
 #define ___GFP_COMP            0x4000u
 #define ___GFP_ZERO            0x8000u
 #define ___GFP_NOMEMALLOC      0x10000u
@@ -76,9 +77,14 @@ struct vm_area_struct;
 #define __GFP_REPEAT   ((__force gfp_t)___GFP_REPEAT)  /* See above */
 #define __GFP_NOFAIL   ((__force gfp_t)___GFP_NOFAIL)  /* See above */
 #define __GFP_NORETRY  ((__force gfp_t)___GFP_NORETRY) /* See above */
+#define __GFP_MEMALLOC ((__force gfp_t)___GFP_MEMALLOC)/* Allow access to emergency reserves */
 #define __GFP_COMP     ((__force gfp_t)___GFP_COMP)    /* Add compound page metadata */
 #define __GFP_ZERO     ((__force gfp_t)___GFP_ZERO)    /* Return zeroed page on success */
-#define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC) /* Don't use emergency reserves */
+#define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC) /* Don't use emergency reserves.
+                                                        * This takes precedence over the
+                                                        * __GFP_MEMALLOC flag if both are
+                                                        * set
+                                                        */
 #define __GFP_HARDWALL   ((__force gfp_t)___GFP_HARDWALL) /* Enforce hardwall cpuset memory allocs */
 #define __GFP_THISNODE ((__force gfp_t)___GFP_THISNODE)/* No fallback, no policies */
 #define __GFP_RECLAIMABLE ((__force gfp_t)___GFP_RECLAIMABLE) /* Page is reclaimable */
@@ -129,7 +135,7 @@ struct vm_area_struct;
 /* Control page allocator reclaim behavior */
 #define GFP_RECLAIM_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_FS|\
                        __GFP_NOWARN|__GFP_REPEAT|__GFP_NOFAIL|\
-                       __GFP_NORETRY|__GFP_NOMEMALLOC)
+                       __GFP_NORETRY|__GFP_MEMALLOC|__GFP_NOMEMALLOC)
 
 /* Control slab gfp mask during early boot */
 #define GFP_BOOT_MASK (__GFP_BITS_MASK & ~(__GFP_WAIT|__GFP_IO|__GFP_FS))
@@ -379,6 +385,9 @@ void drain_local_pages(void *dummy);
  */
 extern gfp_t gfp_allowed_mask;
 
+/* Returns true if the gfp_mask allows use of ALLOC_NO_WATERMARK */
+bool gfp_pfmemalloc_allowed(gfp_t gfp_mask);
+
 extern void pm_restrict_gfp_mask(void);
 extern void pm_restore_gfp_mask(void);
 
index b80506bdd733ee181f202ddb6322529d42299d1b..24df9e70406ffb94fb98faf91c174e236155a639 100644 (file)
@@ -67,4 +67,14 @@ static inline unsigned long hash_ptr(const void *ptr, unsigned int bits)
 {
        return hash_long((unsigned long)ptr, bits);
 }
+
+static inline u32 hash32_ptr(const void *ptr)
+{
+       unsigned long val = (unsigned long)ptr;
+
+#if BITS_PER_LONG == 64
+       val ^= (val >> 32);
+#endif
+       return (u32)val;
+}
 #endif /* _LINUX_HASH_H */
index 774fa47b3b5b2a19f810a166682e1a0912827f3c..ef788b5b4a3504b069818e33145b2b3e578235fb 100644 (file)
@@ -39,10 +39,17 @@ extern unsigned long totalhigh_pages;
 
 void kmap_flush_unused(void);
 
+struct page *kmap_to_page(void *addr);
+
 #else /* CONFIG_HIGHMEM */
 
 static inline unsigned int nr_free_highpages(void) { return 0; }
 
+static inline struct page *kmap_to_page(void *addr)
+{
+       return virt_to_page(addr);
+}
+
 #define totalhigh_pages 0UL
 
 #ifndef ARCH_HAS_KMAP
index d5d6bbe2259e527b34d63abc4a93d64c469e8dd6..225164842ab62797216cd13c82c510d6847e13c4 100644 (file)
@@ -4,9 +4,11 @@
 #include <linux/mm_types.h>
 #include <linux/fs.h>
 #include <linux/hugetlb_inline.h>
+#include <linux/cgroup.h>
 
 struct ctl_table;
 struct user_struct;
+struct mmu_gather;
 
 #ifdef CONFIG_HUGETLB_PAGE
 
@@ -20,6 +22,11 @@ struct hugepage_subpool {
        long max_hpages, used_hpages;
 };
 
+extern spinlock_t hugetlb_lock;
+extern int hugetlb_max_hstate __read_mostly;
+#define for_each_hstate(h) \
+       for ((h) = hstates; (h) < &hstates[hugetlb_max_hstate]; (h)++)
+
 struct hugepage_subpool *hugepage_new_subpool(long nr_blocks);
 void hugepage_put_subpool(struct hugepage_subpool *spool);
 
@@ -40,9 +47,14 @@ int follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *,
                        struct page **, struct vm_area_struct **,
                        unsigned long *, int *, int, unsigned int flags);
 void unmap_hugepage_range(struct vm_area_struct *,
-                       unsigned long, unsigned long, struct page *);
-void __unmap_hugepage_range(struct vm_area_struct *,
-                       unsigned long, unsigned long, struct page *);
+                         unsigned long, unsigned long, struct page *);
+void __unmap_hugepage_range_final(struct mmu_gather *tlb,
+                         struct vm_area_struct *vma,
+                         unsigned long start, unsigned long end,
+                         struct page *ref_page);
+void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+                               unsigned long start, unsigned long end,
+                               struct page *ref_page);
 int hugetlb_prefault(struct address_space *, struct vm_area_struct *);
 void hugetlb_report_meminfo(struct seq_file *);
 int hugetlb_report_node_meminfo(int, char *);
@@ -98,7 +110,6 @@ static inline unsigned long hugetlb_total_pages(void)
 #define follow_huge_addr(mm, addr, write)      ERR_PTR(-EINVAL)
 #define copy_hugetlb_page_range(src, dst, vma) ({ BUG(); 0; })
 #define hugetlb_prefault(mapping, vma)         ({ BUG(); 0; })
-#define unmap_hugepage_range(vma, start, end, page)    BUG()
 static inline void hugetlb_report_meminfo(struct seq_file *m)
 {
 }
@@ -112,13 +123,31 @@ static inline void hugetlb_report_meminfo(struct seq_file *m)
 #define hugetlb_free_pgd_range(tlb, addr, end, floor, ceiling) ({BUG(); 0; })
 #define hugetlb_fault(mm, vma, addr, flags)    ({ BUG(); 0; })
 #define huge_pte_offset(mm, address)   0
-#define dequeue_hwpoisoned_huge_page(page)     0
+static inline int dequeue_hwpoisoned_huge_page(struct page *page)
+{
+       return 0;
+}
+
 static inline void copy_huge_page(struct page *dst, struct page *src)
 {
 }
 
 #define hugetlb_change_protection(vma, address, end, newprot)
 
+static inline void __unmap_hugepage_range_final(struct mmu_gather *tlb,
+                       struct vm_area_struct *vma, unsigned long start,
+                       unsigned long end, struct page *ref_page)
+{
+       BUG();
+}
+
+static inline void __unmap_hugepage_range(struct mmu_gather *tlb,
+                       struct vm_area_struct *vma, unsigned long start,
+                       unsigned long end, struct page *ref_page)
+{
+       BUG();
+}
+
 #endif /* !CONFIG_HUGETLB_PAGE */
 
 #define HUGETLB_ANON_FILE "anon_hugepage"
@@ -199,10 +228,15 @@ struct hstate {
        unsigned long resv_huge_pages;
        unsigned long surplus_huge_pages;
        unsigned long nr_overcommit_huge_pages;
+       struct list_head hugepage_activelist;
        struct list_head hugepage_freelists[MAX_NUMNODES];
        unsigned int nr_huge_pages_node[MAX_NUMNODES];
        unsigned int free_huge_pages_node[MAX_NUMNODES];
        unsigned int surplus_huge_pages_node[MAX_NUMNODES];
+#ifdef CONFIG_CGROUP_HUGETLB
+       /* cgroup control files */
+       struct cftype cgroup_files[5];
+#endif
        char name[HSTATE_NAME_LEN];
 };
 
@@ -302,6 +336,11 @@ static inline unsigned hstate_index_to_shift(unsigned index)
        return hstates[index].order + PAGE_SHIFT;
 }
 
+static inline int hstate_index(struct hstate *h)
+{
+       return h - hstates;
+}
+
 #else
 struct hstate {};
 #define alloc_huge_page_node(h, nid) NULL
@@ -320,6 +359,7 @@ static inline unsigned int pages_per_huge_page(struct hstate *h)
        return 1;
 }
 #define hstate_index_to_shift(index) 0
+#define hstate_index(h) 0
 #endif
 
 #endif /* _LINUX_HUGETLB_H */
diff --git a/include/linux/hugetlb_cgroup.h b/include/linux/hugetlb_cgroup.h
new file mode 100644 (file)
index 0000000..d73878c
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright IBM Corporation, 2012
+ * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef _LINUX_HUGETLB_CGROUP_H
+#define _LINUX_HUGETLB_CGROUP_H
+
+#include <linux/res_counter.h>
+
+struct hugetlb_cgroup;
+/*
+ * Minimum page order trackable by hugetlb cgroup.
+ * At least 3 pages are necessary for all the tracking information.
+ */
+#define HUGETLB_CGROUP_MIN_ORDER       2
+
+#ifdef CONFIG_CGROUP_HUGETLB
+
+static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page)
+{
+       VM_BUG_ON(!PageHuge(page));
+
+       if (compound_order(page) < HUGETLB_CGROUP_MIN_ORDER)
+               return NULL;
+       return (struct hugetlb_cgroup *)page[2].lru.next;
+}
+
+static inline
+int set_hugetlb_cgroup(struct page *page, struct hugetlb_cgroup *h_cg)
+{
+       VM_BUG_ON(!PageHuge(page));
+
+       if (compound_order(page) < HUGETLB_CGROUP_MIN_ORDER)
+               return -1;
+       page[2].lru.next = (void *)h_cg;
+       return 0;
+}
+
+static inline bool hugetlb_cgroup_disabled(void)
+{
+       if (hugetlb_subsys.disabled)
+               return true;
+       return false;
+}
+
+extern int hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,
+                                       struct hugetlb_cgroup **ptr);
+extern void hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages,
+                                        struct hugetlb_cgroup *h_cg,
+                                        struct page *page);
+extern void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages,
+                                        struct page *page);
+extern void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages,
+                                          struct hugetlb_cgroup *h_cg);
+extern int hugetlb_cgroup_file_init(int idx) __init;
+extern void hugetlb_cgroup_migrate(struct page *oldhpage,
+                                  struct page *newhpage);
+
+#else
+static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page)
+{
+       return NULL;
+}
+
+static inline
+int set_hugetlb_cgroup(struct page *page, struct hugetlb_cgroup *h_cg)
+{
+       return 0;
+}
+
+static inline bool hugetlb_cgroup_disabled(void)
+{
+       return true;
+}
+
+static inline int
+hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,
+                            struct hugetlb_cgroup **ptr)
+{
+       return 0;
+}
+
+static inline void
+hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages,
+                            struct hugetlb_cgroup *h_cg,
+                            struct page *page)
+{
+       return;
+}
+
+static inline void
+hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, struct page *page)
+{
+       return;
+}
+
+static inline void
+hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages,
+                              struct hugetlb_cgroup *h_cg)
+{
+       return;
+}
+
+static inline int __init hugetlb_cgroup_file_init(int idx)
+{
+       return 0;
+}
+
+static inline void hugetlb_cgroup_migrate(struct page *oldhpage,
+                                         struct page *newhpage)
+{
+       return;
+}
+
+#endif  /* CONFIG_MEM_RES_CTLR_HUGETLB */
+#endif
index 4d5e57ff66144f48fce709bcf5d151b96f8f1530..1c06b5c7c308468204e53883398c11d29d12539a 100644 (file)
@@ -12,7 +12,8 @@
 #define _LINUX_I2C_OCORES_H
 
 struct ocores_i2c_platform_data {
-       u32 regstep;   /* distance between registers */
+       u32 reg_shift; /* register offset shift value */
+       u32 reg_io_width; /* register io read/write width */
        u32 clock_khz; /* input clock in kHz */
        u8 num_devices; /* number of devices in the devices list */
        struct i2c_board_info const *devices; /* devices connected to the bus */
index 1d0fe4877b1fa0fdd4c9e2c36a4f154874de8cc1..5970266930a26f0517208b03336646232067dc1a 100644 (file)
@@ -68,6 +68,9 @@ extern int i2c_master_recv(const struct i2c_client *client, char *buf,
  */
 extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
                        int num);
+/* Unlocked flavor */
+extern int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+                         int num);
 
 /* This is the very generalized SMBus access routine. You probably do not
    want to use this, though; one of the functions below may be much easier,
index 555382660bc40e00eb4876eae585f3f9a5e6eea6..7ea898c55a601e04d11231a9425d9b29460089b0 100644 (file)
@@ -555,6 +555,8 @@ struct twl4030_clock_init_data {
 struct twl4030_bci_platform_data {
        int *battery_tmp_tbl;
        unsigned int tblsize;
+       int     bb_uvolt;       /* voltage to charge backup battery */
+       int     bb_uamp;        /* current for backup battery charging */
 };
 
 /* TWL4030_GPIO_MAX (18) GPIOs, with interrupts */
index f0e69c6e82083c33eb799cbba0eb2ba789699a56..9adcc29f084af485a8b674dd6c6b3b51532ce68b 100644 (file)
@@ -92,6 +92,7 @@
 #define ARPHRD_PHONET  820             /* PhoNet media type            */
 #define ARPHRD_PHONET_PIPE 821         /* PhoNet pipe header           */
 #define ARPHRD_CAIF    822             /* CAIF media type              */
+#define ARPHRD_IP6GRE  823             /* GRE over IPv6                */
 
 #define ARPHRD_VOID      0xFFFF        /* Void type, nothing is known */
 #define ARPHRD_NONE      0xFFFE        /* zero header length */
index 6960fc1841a7adc3bd42ff5b9a2221b78f2b9eb2..33fcc20b58812d032405d0916aadd947613948df 100644 (file)
@@ -67,6 +67,9 @@ struct team_port {
        struct netpoll *np;
 #endif
 
+       s32 priority; /* lower number ~ higher priority */
+       u16 queue_id;
+       struct list_head qom_list; /* node in queue override mapping list */
        long mode_priv[0];
 };
 
@@ -130,6 +133,7 @@ enum team_option_type {
        TEAM_OPTION_TYPE_STRING,
        TEAM_OPTION_TYPE_BINARY,
        TEAM_OPTION_TYPE_BOOL,
+       TEAM_OPTION_TYPE_S32,
 };
 
 struct team_option_inst_info {
@@ -146,6 +150,7 @@ struct team_gsetter_ctx {
                        u32 len;
                } bin_val;
                bool bool_val;
+               s32 s32_val;
        } data;
        struct team_option_inst_info *info;
 };
@@ -197,6 +202,8 @@ struct team {
 
        const struct team_mode *mode;
        struct team_mode_ops ops;
+       bool queue_override_enabled;
+       struct list_head *qom_lists; /* array of queue override mapping lists */
        long mode_priv[TEAM_MODE_PRIV_LONGS];
 };
 
index 5efff60b6f56906112b5c71dffbdf47b2b22cc8d..8c5035ac31421aa1bee89a34c342de34ff63131a 100644 (file)
@@ -75,6 +75,9 @@ enum {
        IFLA_GRE_TTL,
        IFLA_GRE_TOS,
        IFLA_GRE_PMTUDISC,
+       IFLA_GRE_ENCAP_LIMIT,
+       IFLA_GRE_FLOWINFO,
+       IFLA_GRE_FLAGS,
        __IFLA_GRE_MAX,
 };
 
index 67f9ddacb70c327e6576b47d0c103cddb752dd18..d032780d0ce50849c5441aae24025b2cc2f80cc3 100644 (file)
@@ -104,9 +104,14 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev)
 #define IN_DEV_ANDCONF(in_dev, attr) \
        (IPV4_DEVCONF_ALL(dev_net(in_dev->dev), attr) && \
         IN_DEV_CONF_GET((in_dev), attr))
-#define IN_DEV_ORCONF(in_dev, attr) \
-       (IPV4_DEVCONF_ALL(dev_net(in_dev->dev), attr) || \
+
+#define IN_DEV_NET_ORCONF(in_dev, net, attr) \
+       (IPV4_DEVCONF_ALL(net, attr) || \
         IN_DEV_CONF_GET((in_dev), attr))
+
+#define IN_DEV_ORCONF(in_dev, attr) \
+       IN_DEV_NET_ORCONF(in_dev, dev_net(in_dev->dev), attr)
+
 #define IN_DEV_MAXCONF(in_dev, attr) \
        (max(IPV4_DEVCONF_ALL(dev_net(in_dev->dev), attr), \
             IN_DEV_CONF_GET((in_dev), attr)))
@@ -133,6 +138,8 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev)
                                        IN_DEV_ORCONF((in_dev), \
                                                      PROMOTE_SECONDARIES)
 #define IN_DEV_ROUTE_LOCALNET(in_dev)  IN_DEV_ORCONF(in_dev, ROUTE_LOCALNET)
+#define IN_DEV_NET_ROUTE_LOCALNET(in_dev, net) \
+       IN_DEV_NET_ORCONF(in_dev, net, ROUTE_LOCALNET)
 
 #define IN_DEV_RX_REDIRECTS(in_dev) \
        ((IN_DEV_FORWARD(in_dev) && \
diff --git a/include/linux/input/edt-ft5x06.h b/include/linux/input/edt-ft5x06.h
new file mode 100644 (file)
index 0000000..8a1e0d1
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef _EDT_FT5X06_H
+#define _EDT_FT5X06_H
+
+/*
+ * Copyright (c) 2012 Simon Budig, <simon.budig@kernelconcepts.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.
+ */
+
+struct edt_ft5x06_platform_data {
+       int irq_pin;
+       int reset_pin;
+
+       /* startup defaults for operational parameters */
+       bool use_parameters;
+       u8 gain;
+       u8 threshold;
+       u8 offset;
+       u8 report_rate;
+};
+
+#endif /* _EDT_FT5X06_H */
index e68a8e53bb59acf87c2dd07259f77ce491d5ec1e..c5f856a040b9252f116a3c58744176a2feba0b19 100644 (file)
@@ -42,7 +42,6 @@
  *
  * IRQF_DISABLED - keep irqs disabled when calling the action handler.
  *                 DEPRECATED. This flag is a NOOP and scheduled to be removed
- * IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
  * IRQF_SHARED - allow sharing the irq among several devices
  * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
  * IRQF_TIMER - Flag to mark this interrupt as timer interrupt
@@ -61,7 +60,6 @@
  *                resume time.
  */
 #define IRQF_DISABLED          0x00000020
-#define IRQF_SAMPLE_RANDOM     0x00000040
 #define IRQF_SHARED            0x00000080
 #define IRQF_PROBE_SHARED      0x00000100
 #define __IRQF_TIMER           0x00000200
index bf22b03179022da13565642026213601e879fa88..48af63c9a48d25f5f6d9e2681e6eeeec5a4fef8c 100644 (file)
@@ -31,4 +31,21 @@ struct ip6_tnl_parm {
        struct in6_addr raddr;  /* remote tunnel end-point address */
 };
 
+struct ip6_tnl_parm2 {
+       char name[IFNAMSIZ];    /* name of tunnel device */
+       int link;               /* ifindex of underlying L2 interface */
+       __u8 proto;             /* tunnel protocol */
+       __u8 encap_limit;       /* encapsulation limit for tunnel */
+       __u8 hop_limit;         /* hop limit for tunnel */
+       __be32 flowinfo;        /* traffic class and flowlabel for tunnel */
+       __u32 flags;            /* tunnel flags */
+       struct in6_addr laddr;  /* local tunnel end-point address */
+       struct in6_addr raddr;  /* remote tunnel end-point address */
+
+       __be16                  i_flags;
+       __be16                  o_flags;
+       __be32                  i_key;
+       __be32                  o_key;
+};
+
 #endif
index f1e2527006bd92f4780584b874c829bef49523e5..9a323d12de1c1700b10988013f38f6d8b1ca4615 100644 (file)
@@ -39,7 +39,6 @@ struct module;
  */
 struct irq_desc {
        struct irq_data         irq_data;
-       struct timer_rand_state *timer_rand_state;
        unsigned int __percpu   *kstat_irqs;
        irq_flow_handler_t      handle_irq;
 #ifdef CONFIG_IRQ_PREFLOW_FASTEOI
index 5abb533eb8eb2a68a1645423ed0b8c6e2d0e94fa..0d5b17bf5e5131dfd03dc176a8c7fa4f9cbec9b1 100644 (file)
@@ -112,6 +112,11 @@ struct irq_domain {
 };
 
 #ifdef CONFIG_IRQ_DOMAIN
+struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
+                                        unsigned int size,
+                                        unsigned int first_irq,
+                                        const struct irq_domain_ops *ops,
+                                        void *host_data);
 struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
                                         unsigned int size,
                                         unsigned int first_irq,
@@ -144,16 +149,31 @@ static inline struct irq_domain *irq_domain_add_legacy_isa(
 
 extern void irq_domain_remove(struct irq_domain *host);
 
+extern int irq_domain_associate_many(struct irq_domain *domain,
+                                    unsigned int irq_base,
+                                    irq_hw_number_t hwirq_base, int count);
+static inline int irq_domain_associate(struct irq_domain *domain, unsigned int irq,
+                                       irq_hw_number_t hwirq)
+{
+       return irq_domain_associate_many(domain, irq, hwirq, 1);
+}
+
 extern unsigned int irq_create_mapping(struct irq_domain *host,
                                       irq_hw_number_t hwirq);
 extern void irq_dispose_mapping(unsigned int virq);
 extern unsigned int irq_find_mapping(struct irq_domain *host,
                                     irq_hw_number_t hwirq);
 extern unsigned int irq_create_direct_mapping(struct irq_domain *host);
-extern void irq_radix_revmap_insert(struct irq_domain *host, unsigned int virq,
-                                   irq_hw_number_t hwirq);
-extern unsigned int irq_radix_revmap_lookup(struct irq_domain *host,
-                                           irq_hw_number_t hwirq);
+extern int irq_create_strict_mappings(struct irq_domain *domain,
+                                     unsigned int irq_base,
+                                     irq_hw_number_t hwirq_base, int count);
+
+static inline int irq_create_identity_mapping(struct irq_domain *host,
+                                             irq_hw_number_t hwirq)
+{
+       return irq_create_strict_mappings(host, hwirq, hwirq, 1);
+}
+
 extern unsigned int irq_linear_revmap(struct irq_domain *host,
                                      irq_hw_number_t hwirq);
 
index 265e2c3cbd1cd74d2b3efd416aa9301c90ae90ad..aded9b1ac5ca6a524fc53f360ca3c0c983c669d7 100644 (file)
@@ -303,7 +303,13 @@ extern void jiffies_to_timespec(const unsigned long jiffies,
 extern unsigned long timeval_to_jiffies(const struct timeval *value);
 extern void jiffies_to_timeval(const unsigned long jiffies,
                               struct timeval *value);
+
 extern clock_t jiffies_to_clock_t(unsigned long x);
+static inline clock_t jiffies_delta_to_clock_t(long delta)
+{
+       return jiffies_to_clock_t(max(0L, delta));
+}
+
 extern unsigned long clock_t_to_jiffies(unsigned long x);
 extern u64 jiffies_64_to_clock_t(u64 x);
 extern u64 nsec_to_clock_t(u64 x);
diff --git a/include/linux/kern_levels.h b/include/linux/kern_levels.h
new file mode 100644 (file)
index 0000000..866caaa
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef __KERN_LEVELS_H__
+#define __KERN_LEVELS_H__
+
+#define KERN_SOH       "\001"          /* ASCII Start Of Header */
+#define KERN_SOH_ASCII '\001'
+
+#define KERN_EMERG     KERN_SOH "0"    /* system is unusable */
+#define KERN_ALERT     KERN_SOH "1"    /* action must be taken immediately */
+#define KERN_CRIT      KERN_SOH "2"    /* critical conditions */
+#define KERN_ERR       KERN_SOH "3"    /* error conditions */
+#define KERN_WARNING   KERN_SOH "4"    /* warning conditions */
+#define KERN_NOTICE    KERN_SOH "5"    /* normal but significant condition */
+#define KERN_INFO      KERN_SOH "6"    /* informational */
+#define KERN_DEBUG     KERN_SOH "7"    /* debug-level messages */
+
+#define KERN_DEFAULT   KERN_SOH "d"    /* the default kernel loglevel */
+
+/*
+ * Annotation for a "continued" line of log printout (only done after a
+ * line that had no enclosing \n). Only to be used by core/arch code
+ * during early bootup (a continued line is not SMP-safe otherwise).
+ */
+#define KERN_CONT      ""
+
+#endif
index 39e3c082c49d489a32ab781e080b28236661877f..f0c651cda7b0b7a367b41160ed88ea487838a72b 100644 (file)
@@ -13,6 +13,7 @@
 #define _LINUX_KEY_TYPE_H
 
 #include <linux/key.h>
+#include <linux/errno.h>
 
 #ifdef CONFIG_KEYS
 
diff --git a/include/linux/libfdt.h b/include/linux/libfdt.h
new file mode 100644 (file)
index 0000000..4c0306c
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _INCLUDE_LIBFDT_H_
+#define _INCLUDE_LIBFDT_H_
+
+#include <linux/libfdt_env.h>
+#include "../../scripts/dtc/libfdt/fdt.h"
+#include "../../scripts/dtc/libfdt/libfdt.h"
+
+#endif /* _INCLUDE_LIBFDT_H_ */
diff --git a/include/linux/libfdt_env.h b/include/linux/libfdt_env.h
new file mode 100644 (file)
index 0000000..01508c7
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _LIBFDT_ENV_H
+#define _LIBFDT_ENV_H
+
+#include <linux/string.h>
+
+#include <asm/byteorder.h>
+
+#define fdt32_to_cpu(x) be32_to_cpu(x)
+#define cpu_to_fdt32(x) cpu_to_be32(x)
+#define fdt64_to_cpu(x) be64_to_cpu(x)
+#define cpu_to_fdt64(x) cpu_to_be64(x)
+
+#endif /* _LIBFDT_ENV_H */
index f04ce6ac6d04fd84f65c533cb34b5748d2d6665f..f5a051a79273c7f3fcdb1a18f4c9e2522e68cc85 100644 (file)
@@ -262,11 +262,11 @@ typedef int         (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref);
 __be32           nlmsvc_lock(struct svc_rqst *, struct nlm_file *,
                              struct nlm_host *, struct nlm_lock *, int,
                              struct nlm_cookie *, int);
-__be32           nlmsvc_unlock(struct nlm_file *, struct nlm_lock *);
+__be32           nlmsvc_unlock(struct net *net, struct nlm_file *, struct nlm_lock *);
 __be32           nlmsvc_testlock(struct svc_rqst *, struct nlm_file *,
                        struct nlm_host *, struct nlm_lock *,
                        struct nlm_lock *, struct nlm_cookie *);
-__be32           nlmsvc_cancel_blocked(struct nlm_file *, struct nlm_lock *);
+__be32           nlmsvc_cancel_blocked(struct net *net, struct nlm_file *, struct nlm_lock *);
 unsigned long    nlmsvc_retry_blocked(void);
 void             nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *,
                                        nlm_host_match_fn_t match);
@@ -279,7 +279,7 @@ void                  nlmsvc_release_call(struct nlm_rqst *);
 __be32           nlm_lookup_file(struct svc_rqst *, struct nlm_file **,
                                        struct nfs_fh *);
 void             nlm_release_file(struct nlm_file *);
-void             nlmsvc_mark_resources(void);
+void             nlmsvc_mark_resources(struct net *);
 void             nlmsvc_free_host_resources(struct nlm_host *);
 void             nlmsvc_invalidate_all(void);
 
diff --git a/include/linux/lp855x.h b/include/linux/lp855x.h
deleted file mode 100644 (file)
index 781a490..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * LP855x Backlight Driver
- *
- *                     Copyright (C) 2011 Texas Instruments
- *
- * 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 _LP855X_H
-#define _LP855X_H
-
-#define BL_CTL_SHFT    (0)
-#define BRT_MODE_SHFT  (1)
-#define BRT_MODE_MASK  (0x06)
-
-/* Enable backlight. Only valid when BRT_MODE=10(I2C only) */
-#define ENABLE_BL      (1)
-#define DISABLE_BL     (0)
-
-#define I2C_CONFIG(id) id ## _I2C_CONFIG
-#define PWM_CONFIG(id) id ## _PWM_CONFIG
-
-/* DEVICE CONTROL register - LP8550 */
-#define LP8550_PWM_CONFIG      (LP8550_PWM_ONLY << BRT_MODE_SHFT)
-#define LP8550_I2C_CONFIG      ((ENABLE_BL << BL_CTL_SHFT) | \
-                               (LP8550_I2C_ONLY << BRT_MODE_SHFT))
-
-/* DEVICE CONTROL register - LP8551 */
-#define LP8551_PWM_CONFIG      LP8550_PWM_CONFIG
-#define LP8551_I2C_CONFIG      LP8550_I2C_CONFIG
-
-/* DEVICE CONTROL register - LP8552 */
-#define LP8552_PWM_CONFIG      LP8550_PWM_CONFIG
-#define LP8552_I2C_CONFIG      LP8550_I2C_CONFIG
-
-/* DEVICE CONTROL register - LP8553 */
-#define LP8553_PWM_CONFIG      LP8550_PWM_CONFIG
-#define LP8553_I2C_CONFIG      LP8550_I2C_CONFIG
-
-/* DEVICE CONTROL register - LP8556 */
-#define LP8556_PWM_CONFIG      (LP8556_PWM_ONLY << BRT_MODE_SHFT)
-#define LP8556_COMB1_CONFIG    (LP8556_COMBINED1 << BRT_MODE_SHFT)
-#define LP8556_I2C_CONFIG      ((ENABLE_BL << BL_CTL_SHFT) | \
-                               (LP8556_I2C_ONLY << BRT_MODE_SHFT))
-#define LP8556_COMB2_CONFIG    (LP8556_COMBINED2 << BRT_MODE_SHFT)
-
-/* ROM area boundary */
-#define EEPROM_START   (0xA0)
-#define EEPROM_END     (0xA7)
-#define EPROM_START    (0xA0)
-#define EPROM_END      (0xAF)
-
-enum lp855x_chip_id {
-       LP8550,
-       LP8551,
-       LP8552,
-       LP8553,
-       LP8556,
-};
-
-enum lp855x_brightness_ctrl_mode {
-       PWM_BASED = 1,
-       REGISTER_BASED,
-};
-
-enum lp8550_brighntess_source {
-       LP8550_PWM_ONLY,
-       LP8550_I2C_ONLY = 2,
-};
-
-enum lp8551_brighntess_source {
-       LP8551_PWM_ONLY = LP8550_PWM_ONLY,
-       LP8551_I2C_ONLY = LP8550_I2C_ONLY,
-};
-
-enum lp8552_brighntess_source {
-       LP8552_PWM_ONLY = LP8550_PWM_ONLY,
-       LP8552_I2C_ONLY = LP8550_I2C_ONLY,
-};
-
-enum lp8553_brighntess_source {
-       LP8553_PWM_ONLY = LP8550_PWM_ONLY,
-       LP8553_I2C_ONLY = LP8550_I2C_ONLY,
-};
-
-enum lp8556_brightness_source {
-       LP8556_PWM_ONLY,
-       LP8556_COMBINED1,       /* pwm + i2c before the shaper block */
-       LP8556_I2C_ONLY,
-       LP8556_COMBINED2,       /* pwm + i2c after the shaper block */
-};
-
-struct lp855x_pwm_data {
-       void (*pwm_set_intensity) (int brightness, int max_brightness);
-       int (*pwm_get_intensity) (int max_brightness);
-};
-
-struct lp855x_rom_data {
-       u8 addr;
-       u8 val;
-};
-
-/**
- * struct lp855x_platform_data
- * @name : Backlight driver name. If it is not defined, default name is set.
- * @mode : brightness control by pwm or lp855x register
- * @device_control : value of DEVICE CONTROL register
- * @initial_brightness : initial value of backlight brightness
- * @pwm_data : platform specific pwm generation functions.
-               Only valid when mode is PWM_BASED.
- * @load_new_rom_data :
-       0 : use default configuration data
-       1 : update values of eeprom or eprom registers on loading driver
- * @size_program : total size of lp855x_rom_data
- * @rom_data : list of new eeprom/eprom registers
- */
-struct lp855x_platform_data {
-       char *name;
-       enum lp855x_brightness_ctrl_mode mode;
-       u8 device_control;
-       int initial_brightness;
-       struct lp855x_pwm_data pwm_data;
-       u8 load_new_rom_data;
-       int size_program;
-       struct lp855x_rom_data *rom_data;
-};
-
-#endif
diff --git a/include/linux/lp8727.h b/include/linux/lp8727.h
deleted file mode 100644 (file)
index ea98c61..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * LP8727 Micro/Mini USB IC with integrated charger
- *
- *                     Copyright (C) 2011 Texas Instruments
- *                     Copyright (C) 2011 National Semiconductor
- *
- * 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 _LP8727_H
-#define _LP8727_H
-
-enum lp8727_eoc_level {
-       EOC_5P,
-       EOC_10P,
-       EOC_16P,
-       EOC_20P,
-       EOC_25P,
-       EOC_33P,
-       EOC_50P,
-};
-
-enum lp8727_ichg {
-       ICHG_90mA,
-       ICHG_100mA,
-       ICHG_400mA,
-       ICHG_450mA,
-       ICHG_500mA,
-       ICHG_600mA,
-       ICHG_700mA,
-       ICHG_800mA,
-       ICHG_900mA,
-       ICHG_1000mA,
-};
-
-/**
- * struct lp8727_chg_param
- * @eoc_level : end of charge level setting
- * @ichg : charging current
- */
-struct lp8727_chg_param {
-       enum lp8727_eoc_level eoc_level;
-       enum lp8727_ichg ichg;
-};
-
-/**
- * struct lp8727_platform_data
- * @get_batt_present : check battery status - exists or not
- * @get_batt_level : get battery voltage (mV)
- * @get_batt_capacity : get battery capacity (%)
- * @get_batt_temp : get battery temperature
- * @ac, @usb : charging parameters each charger type
- */
-struct lp8727_platform_data {
-       u8 (*get_batt_present)(void);
-       u16 (*get_batt_level)(void);
-       u8 (*get_batt_capacity)(void);
-       u8 (*get_batt_temp)(void);
-       struct lp8727_chg_param ac;
-       struct lp8727_chg_param usb;
-};
-
-#endif
index 83e7ba90d6e5d5eb1b652ec1b7ce21dc47a4a9a9..8d9489fdab2e4910eec7c30dfeea9ce0f08481cd 100644 (file)
@@ -38,7 +38,7 @@ struct mem_cgroup_reclaim_cookie {
        unsigned int generation;
 };
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
 /*
  * All "charge" functions with gfp_mask should use GFP_KERNEL or
  * (gfp_mask & GFP_RECLAIM_MASK). In current implementatin, memcg doesn't
@@ -72,8 +72,6 @@ extern void mem_cgroup_uncharge_end(void);
 extern void mem_cgroup_uncharge_page(struct page *page);
 extern void mem_cgroup_uncharge_cache_page(struct page *page);
 
-extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
-                                    int order);
 bool __mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
                                  struct mem_cgroup *memcg);
 int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg);
@@ -100,9 +98,9 @@ int mm_match_cgroup(const struct mm_struct *mm, const struct mem_cgroup *cgroup)
 
 extern struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *memcg);
 
-extern int
-mem_cgroup_prepare_migration(struct page *page,
-       struct page *newpage, struct mem_cgroup **memcgp, gfp_t gfp_mask);
+extern void
+mem_cgroup_prepare_migration(struct page *page, struct page *newpage,
+                            struct mem_cgroup **memcgp);
 extern void mem_cgroup_end_migration(struct mem_cgroup *memcg,
        struct page *oldpage, struct page *newpage, bool migration_ok);
 
@@ -124,7 +122,7 @@ extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg,
 extern void mem_cgroup_replace_page_cache(struct page *oldpage,
                                        struct page *newpage);
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
 extern int do_swap_account;
 #endif
 
@@ -182,7 +180,6 @@ static inline void mem_cgroup_dec_page_stat(struct page *page,
 unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
                                                gfp_t gfp_mask,
                                                unsigned long *total_scanned);
-u64 mem_cgroup_get_limit(struct mem_cgroup *memcg);
 
 void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx);
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
@@ -193,7 +190,7 @@ void mem_cgroup_split_huge_fixup(struct page *head);
 bool mem_cgroup_bad_page_check(struct page *page);
 void mem_cgroup_print_bad_page(struct page *page);
 #endif
-#else /* CONFIG_CGROUP_MEM_RES_CTLR */
+#else /* CONFIG_MEMCG */
 struct mem_cgroup;
 
 static inline int mem_cgroup_newpage_charge(struct page *page,
@@ -279,11 +276,10 @@ static inline struct cgroup_subsys_state
        return NULL;
 }
 
-static inline int
+static inline void
 mem_cgroup_prepare_migration(struct page *page, struct page *newpage,
-       struct mem_cgroup **memcgp, gfp_t gfp_mask)
+                            struct mem_cgroup **memcgp)
 {
-       return 0;
 }
 
 static inline void mem_cgroup_end_migration(struct mem_cgroup *memcg,
@@ -366,12 +362,6 @@ unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
        return 0;
 }
 
-static inline
-u64 mem_cgroup_get_limit(struct mem_cgroup *memcg)
-{
-       return 0;
-}
-
 static inline void mem_cgroup_split_huge_fixup(struct page *head)
 {
 }
@@ -384,9 +374,9 @@ static inline void mem_cgroup_replace_page_cache(struct page *oldpage,
                                struct page *newpage)
 {
 }
-#endif /* CONFIG_CGROUP_MEM_RES_CTLR */
+#endif /* CONFIG_MEMCG */
 
-#if !defined(CONFIG_CGROUP_MEM_RES_CTLR) || !defined(CONFIG_DEBUG_VM)
+#if !defined(CONFIG_MEMCG) || !defined(CONFIG_DEBUG_VM)
 static inline bool
 mem_cgroup_bad_page_check(struct page *page)
 {
@@ -406,7 +396,7 @@ enum {
 };
 
 struct sock;
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
 void sock_update_memcg(struct sock *sk);
 void sock_release_memcg(struct sock *sk);
 #else
@@ -416,6 +406,6 @@ static inline void sock_update_memcg(struct sock *sk)
 static inline void sock_release_memcg(struct sock *sk)
 {
 }
-#endif /* CONFIG_CGROUP_MEM_RES_CTLR_KMEM */
+#endif /* CONFIG_MEMCG_KMEM */
 #endif /* _LINUX_MEMCONTROL_H */
 
index 4aa42732e47f34ca29392180fbee0fc73a086cd2..95b738c7abff9ac1e45a5e78e1daf1437b2283fd 100644 (file)
@@ -215,7 +215,7 @@ extern struct zonelist *huge_zonelist(struct vm_area_struct *vma,
 extern bool init_nodemask_of_mempolicy(nodemask_t *mask);
 extern bool mempolicy_nodemask_intersects(struct task_struct *tsk,
                                const nodemask_t *mask);
-extern unsigned slab_node(struct mempolicy *policy);
+extern unsigned slab_node(void);
 
 extern enum zone_type policy_zone;
 
index 7c08052e332111a7aa14d3f4bd7f0390b2a390c9..39ed62ab5b8a38ef3aafa3729767dc7d7c7587ef 100644 (file)
@@ -26,7 +26,8 @@ typedef struct mempool_s {
 extern mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn,
                        mempool_free_t *free_fn, void *pool_data);
 extern mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn,
-                       mempool_free_t *free_fn, void *pool_data, int nid);
+                       mempool_free_t *free_fn, void *pool_data,
+                       gfp_t gfp_mask, int nid);
 
 extern int mempool_resize(mempool_t *pool, int new_min_nr, gfp_t gfp_mask);
 extern void mempool_destroy(mempool_t *pool);
diff --git a/include/linux/mfd/88pm80x.h b/include/linux/mfd/88pm80x.h
new file mode 100644 (file)
index 0000000..a0ca0dc
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * Marvell 88PM80x Interface
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ * Qiao Zhou <zhouqiao@marvell.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 __LINUX_MFD_88PM80X_H
+#define __LINUX_MFD_88PM80X_H
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/atomic.h>
+
+#define PM80X_VERSION_MASK             (0xFF)  /* 80X chip ID mask */
+enum {
+       CHIP_INVALID = 0,
+       CHIP_PM800,
+       CHIP_PM805,
+       CHIP_MAX,
+};
+
+enum {
+       PM800_ID_BUCK1 = 0,
+       PM800_ID_BUCK2,
+       PM800_ID_BUCK3,
+       PM800_ID_BUCK4,
+       PM800_ID_BUCK5,
+
+       PM800_ID_LDO1,
+       PM800_ID_LDO2,
+       PM800_ID_LDO3,
+       PM800_ID_LDO4,
+       PM800_ID_LDO5,
+       PM800_ID_LDO6,
+       PM800_ID_LDO7,
+       PM800_ID_LDO8,
+       PM800_ID_LDO9,
+       PM800_ID_LDO10,
+       PM800_ID_LDO11,
+       PM800_ID_LDO12,
+       PM800_ID_LDO13,
+       PM800_ID_LDO14,
+       PM800_ID_LDO15,
+       PM800_ID_LDO16,
+       PM800_ID_LDO17,
+       PM800_ID_LDO18,
+       PM800_ID_LDO19,
+
+       PM800_ID_RG_MAX,
+};
+#define PM800_MAX_REGULATOR    PM800_ID_RG_MAX /* 5 Bucks, 19 LDOs */
+#define PM800_NUM_BUCK (5)     /*5 Bucks */
+#define PM800_NUM_LDO (19)     /*19 Bucks */
+
+/* page 0 basic: slave adder 0x60 */
+
+#define PM800_STATUS_1                 (0x01)
+#define PM800_ONKEY_STS1               (1 << 0)
+#define PM800_EXTON_STS1               (1 << 1)
+#define PM800_CHG_STS1                 (1 << 2)
+#define PM800_BAT_STS1                 (1 << 3)
+#define PM800_VBUS_STS1                        (1 << 4)
+#define PM800_LDO_PGOOD_STS1   (1 << 5)
+#define PM800_BUCK_PGOOD_STS1  (1 << 6)
+
+#define PM800_STATUS_2                 (0x02)
+#define PM800_RTC_ALARM_STS2   (1 << 0)
+
+/* Wakeup Registers */
+#define PM800_WAKEUP1          (0x0D)
+
+#define PM800_WAKEUP2          (0x0E)
+#define PM800_WAKEUP2_INV_INT          (1 << 0)
+#define PM800_WAKEUP2_INT_CLEAR                (1 << 1)
+#define PM800_WAKEUP2_INT_MASK         (1 << 2)
+
+#define PM800_POWER_UP_LOG     (0x10)
+
+/* Referance and low power registers */
+#define PM800_LOW_POWER1               (0x20)
+#define PM800_LOW_POWER2               (0x21)
+#define PM800_LOW_POWER_CONFIG3        (0x22)
+#define PM800_LOW_POWER_CONFIG4        (0x23)
+
+/* GPIO register */
+#define PM800_GPIO_0_1_CNTRL           (0x30)
+#define PM800_GPIO0_VAL                                (1 << 0)
+#define PM800_GPIO0_GPIO_MODE(x)       (x << 1)
+#define PM800_GPIO1_VAL                                (1 << 4)
+#define PM800_GPIO1_GPIO_MODE(x)       (x << 5)
+
+#define PM800_GPIO_2_3_CNTRL           (0x31)
+#define PM800_GPIO2_VAL                                (1 << 0)
+#define PM800_GPIO2_GPIO_MODE(x)       (x << 1)
+#define PM800_GPIO3_VAL                                (1 << 4)
+#define PM800_GPIO3_GPIO_MODE(x)       (x << 5)
+#define PM800_GPIO3_MODE_MASK          0x1F
+#define PM800_GPIO3_HEADSET_MODE       PM800_GPIO3_GPIO_MODE(6)
+
+#define PM800_GPIO_4_CNTRL                     (0x32)
+#define PM800_GPIO4_VAL                                (1 << 0)
+#define PM800_GPIO4_GPIO_MODE(x)       (x << 1)
+
+#define PM800_HEADSET_CNTRL            (0x38)
+#define PM800_HEADSET_DET_EN           (1 << 7)
+#define PM800_HSDET_SLP                        (1 << 1)
+/* PWM register */
+#define PM800_PWM1             (0x40)
+#define PM800_PWM2             (0x41)
+#define PM800_PWM3             (0x42)
+#define PM800_PWM4             (0x43)
+
+/* RTC Registers */
+#define PM800_RTC_CONTROL              (0xD0)
+#define PM800_RTC_MISC1                        (0xE1)
+#define PM800_RTC_MISC2                        (0xE2)
+#define PM800_RTC_MISC3                        (0xE3)
+#define PM800_RTC_MISC4                        (0xE4)
+#define PM800_RTC_MISC5                        (0xE7)
+/* bit definitions of RTC Register 1 (0xD0) */
+#define PM800_ALARM1_EN                        (1 << 0)
+#define PM800_ALARM_WAKEUP             (1 << 4)
+#define PM800_ALARM                    (1 << 5)
+#define PM800_RTC1_USE_XO              (1 << 7)
+
+/* Regulator Control Registers: BUCK1,BUCK5,LDO1 have DVC */
+
+/* buck registers */
+#define PM800_SLEEP_BUCK1      (0x30)
+
+/* BUCK Sleep Mode Register 1: BUCK[1..4] */
+#define PM800_BUCK_SLP1                (0x5A)
+#define PM800_BUCK1_SLP1_SHIFT 0
+#define PM800_BUCK1_SLP1_MASK  (0x3 << PM800_BUCK1_SLP1_SHIFT)
+
+/* page 2 GPADC: slave adder 0x02 */
+#define PM800_GPADC_MEAS_EN1           (0x01)
+#define PM800_MEAS_EN1_VBAT         (1 << 2)
+#define PM800_GPADC_MEAS_EN2           (0x02)
+#define PM800_MEAS_EN2_RFTMP        (1 << 0)
+#define PM800_MEAS_GP0_EN                      (1 << 2)
+#define PM800_MEAS_GP1_EN                      (1 << 3)
+#define PM800_MEAS_GP2_EN                      (1 << 4)
+#define PM800_MEAS_GP3_EN                      (1 << 5)
+#define PM800_MEAS_GP4_EN                      (1 << 6)
+
+#define PM800_GPADC_MISC_CONFIG1       (0x05)
+#define PM800_GPADC_MISC_CONFIG2       (0x06)
+#define PM800_GPADC_MISC_GPFSM_EN      (1 << 0)
+#define PM800_GPADC_SLOW_MODE(x)       (x << 3)
+
+#define PM800_GPADC_MISC_CONFIG3               (0x09)
+#define PM800_GPADC_MISC_CONFIG4               (0x0A)
+
+#define PM800_GPADC_PREBIAS1                   (0x0F)
+#define PM800_GPADC0_GP_PREBIAS_TIME(x)        (x << 0)
+#define PM800_GPADC_PREBIAS2                   (0x10)
+
+#define PM800_GP_BIAS_ENA1                             (0x14)
+#define PM800_GPADC_GP_BIAS_EN0                        (1 << 0)
+#define PM800_GPADC_GP_BIAS_EN1                        (1 << 1)
+#define PM800_GPADC_GP_BIAS_EN2                        (1 << 2)
+#define PM800_GPADC_GP_BIAS_EN3                        (1 << 3)
+
+#define PM800_GP_BIAS_OUT1             (0x15)
+#define PM800_BIAS_OUT_GP0             (1 << 0)
+#define PM800_BIAS_OUT_GP1             (1 << 1)
+#define PM800_BIAS_OUT_GP2             (1 << 2)
+#define PM800_BIAS_OUT_GP3             (1 << 3)
+
+#define PM800_GPADC0_LOW_TH            0x20
+#define PM800_GPADC1_LOW_TH            0x21
+#define PM800_GPADC2_LOW_TH            0x22
+#define PM800_GPADC3_LOW_TH            0x23
+#define PM800_GPADC4_LOW_TH            0x24
+
+#define PM800_GPADC0_UPP_TH            0x30
+#define PM800_GPADC1_UPP_TH            0x31
+#define PM800_GPADC2_UPP_TH            0x32
+#define PM800_GPADC3_UPP_TH            0x33
+#define PM800_GPADC4_UPP_TH            0x34
+
+#define PM800_VBBAT_MEAS1              0x40
+#define PM800_VBBAT_MEAS2              0x41
+#define PM800_VBAT_MEAS1               0x42
+#define PM800_VBAT_MEAS2               0x43
+#define PM800_VSYS_MEAS1               0x44
+#define PM800_VSYS_MEAS2               0x45
+#define PM800_VCHG_MEAS1               0x46
+#define PM800_VCHG_MEAS2               0x47
+#define PM800_TINT_MEAS1               0x50
+#define PM800_TINT_MEAS2               0x51
+#define PM800_PMOD_MEAS1               0x52
+#define PM800_PMOD_MEAS2               0x53
+
+#define PM800_GPADC0_MEAS1             0x54
+#define PM800_GPADC0_MEAS2             0x55
+#define PM800_GPADC1_MEAS1             0x56
+#define PM800_GPADC1_MEAS2             0x57
+#define PM800_GPADC2_MEAS1             0x58
+#define PM800_GPADC2_MEAS2             0x59
+#define PM800_GPADC3_MEAS1             0x5A
+#define PM800_GPADC3_MEAS2             0x5B
+#define PM800_GPADC4_MEAS1             0x5C
+#define PM800_GPADC4_MEAS2             0x5D
+
+#define PM800_GPADC4_AVG1              0xA8
+#define PM800_GPADC4_AVG2              0xA9
+
+/* 88PM805 Registers */
+#define PM805_MAIN_POWERUP             (0x01)
+#define PM805_INT_STATUS0              (0x02)  /* for ena/dis all interrupts */
+
+#define PM805_STATUS0_INT_CLEAR                (1 << 0)
+#define PM805_STATUS0_INV_INT          (1 << 1)
+#define PM800_STATUS0_INT_MASK         (1 << 2)
+
+#define PM805_INT_STATUS1              (0x03)
+
+#define PM805_INT1_HP1_SHRT            (1 << 0)
+#define PM805_INT1_HP2_SHRT            (1 << 1)
+#define PM805_INT1_MIC_CONFLICT                (1 << 2)
+#define PM805_INT1_CLIP_FAULT          (1 << 3)
+#define PM805_INT1_LDO_OFF                     (1 << 4)
+#define PM805_INT1_SRC_DPLL_LOCK       (1 << 5)
+
+#define PM805_INT_STATUS2              (0x04)
+
+#define PM805_INT2_MIC_DET                     (1 << 0)
+#define PM805_INT2_SHRT_BTN_DET                (1 << 1)
+#define PM805_INT2_VOLM_BTN_DET                (1 << 2)
+#define PM805_INT2_VOLP_BTN_DET                (1 << 3)
+#define PM805_INT2_RAW_PLL_FAULT       (1 << 4)
+#define PM805_INT2_FINE_PLL_FAULT      (1 << 5)
+
+#define PM805_INT_MASK1                        (0x05)
+#define PM805_INT_MASK2                        (0x06)
+#define PM805_SHRT_BTN_DET             (1 << 1)
+
+/* number of status and int reg in a row */
+#define PM805_INT_REG_NUM              (2)
+
+#define PM805_MIC_DET1                 (0x07)
+#define PM805_MIC_DET_EN_MIC_DET (1 << 0)
+#define PM805_MIC_DET2                 (0x08)
+#define PM805_MIC_DET_STATUS1  (0x09)
+
+#define PM805_MIC_DET_STATUS3  (0x0A)
+#define PM805_AUTO_SEQ_STATUS1 (0x0B)
+#define PM805_AUTO_SEQ_STATUS2 (0x0C)
+
+#define PM805_ADC_SETTING1             (0x10)
+#define PM805_ADC_SETTING2             (0x11)
+#define PM805_ADC_SETTING3             (0x11)
+#define PM805_ADC_GAIN1                        (0x12)
+#define PM805_ADC_GAIN2                        (0x13)
+#define PM805_DMIC_SETTING             (0x15)
+#define PM805_DWS_SETTING              (0x16)
+#define PM805_MIC_CONFLICT_STS (0x17)
+
+#define PM805_PDM_SETTING1             (0x20)
+#define PM805_PDM_SETTING2             (0x21)
+#define PM805_PDM_SETTING3             (0x22)
+#define PM805_PDM_CONTROL1             (0x23)
+#define PM805_PDM_CONTROL2             (0x24)
+#define PM805_PDM_CONTROL3             (0x25)
+
+#define PM805_HEADPHONE_SETTING                        (0x26)
+#define PM805_HEADPHONE_GAIN_A2A               (0x27)
+#define PM805_HEADPHONE_SHORT_STATE            (0x28)
+#define PM805_EARPHONE_SETTING                 (0x29)
+#define PM805_AUTO_SEQ_SETTING                 (0x2A)
+
+struct pm80x_rtc_pdata {
+       int             vrtc;
+       int             rtc_wakeup;
+};
+
+struct pm80x_subchip {
+       struct i2c_client *power_page;  /* chip client for power page */
+       struct i2c_client *gpadc_page;  /* chip client for gpadc page */
+       struct regmap *regmap_power;
+       struct regmap *regmap_gpadc;
+       unsigned short power_page_addr; /* power page I2C address */
+       unsigned short gpadc_page_addr; /* gpadc page I2C address */
+};
+
+struct pm80x_chip {
+       struct pm80x_subchip *subchip;
+       struct device *dev;
+       struct i2c_client *client;
+       struct i2c_client *companion;
+       struct regmap *regmap;
+       struct regmap_irq_chip *regmap_irq_chip;
+       struct regmap_irq_chip_data *irq_data;
+       unsigned char version;
+       int id;
+       int irq;
+       int irq_mode;
+       unsigned long wu_flag;
+       spinlock_t lock;
+};
+
+struct pm80x_platform_data {
+       struct pm80x_rtc_pdata *rtc;
+       unsigned short power_page_addr; /* power page I2C address */
+       unsigned short gpadc_page_addr; /* gpadc page I2C address */
+       int irq_mode;           /* Clear interrupt by read/write(0/1) */
+       int batt_det;           /* enable/disable */
+       int (*plat_config)(struct pm80x_chip *chip,
+                               struct pm80x_platform_data *pdata);
+};
+
+extern const struct dev_pm_ops pm80x_pm_ops;
+extern const struct regmap_config pm80x_regmap_config;
+
+static inline int pm80x_request_irq(struct pm80x_chip *pm80x, int irq,
+                                    irq_handler_t handler, unsigned long flags,
+                                    const char *name, void *data)
+{
+       if (!pm80x->irq_data)
+               return -EINVAL;
+       return request_threaded_irq(regmap_irq_get_virq(pm80x->irq_data, irq),
+                                   NULL, handler, flags, name, data);
+}
+
+static inline void pm80x_free_irq(struct pm80x_chip *pm80x, int irq, void *data)
+{
+       if (!pm80x->irq_data)
+               return;
+       free_irq(regmap_irq_get_virq(pm80x->irq_data, irq), data);
+}
+
+#ifdef CONFIG_PM
+static inline int pm80x_dev_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+       int irq = platform_get_irq(pdev, 0);
+
+       if (device_may_wakeup(dev))
+               set_bit((1 << irq), &chip->wu_flag);
+
+       return 0;
+}
+
+static inline int pm80x_dev_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+       int irq = platform_get_irq(pdev, 0);
+
+       if (device_may_wakeup(dev))
+               clear_bit((1 << irq), &chip->wu_flag);
+
+       return 0;
+}
+#endif
+
+extern int pm80x_init(struct i2c_client *client,
+                            const struct i2c_device_id *id) __devinit;
+extern int pm80x_deinit(struct i2c_client *client);
+#endif /* __LINUX_MFD_88PM80X_H */
index 84d071ade1d89df5dd9faf6e6de1c7f3a0f88bcf..7b24943779fa7258904f2b93dbd484856b8fe74d 100644 (file)
@@ -136,6 +136,7 @@ enum {
        PM8607_ID_LDO13,
        PM8607_ID_LDO14,
        PM8607_ID_LDO15,
+       PM8606_ID_PREG,
 
        PM8607_ID_RG_MAX,
 };
index bc9b84b60ec6e53a35b0c6b7e7fe103f5c4e8986..3764cb6759e31e05a39c8eb99034f48d323462b4 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <linux/atomic.h>
 #include <linux/mutex.h>
+#include <linux/irqdomain.h>
 
 struct device;
 
@@ -227,6 +228,7 @@ enum ab8500_version {
  * @irq_lock: genirq bus lock
  * @transfer_ongoing: 0 if no transfer ongoing
  * @irq: irq line
+ * @irq_domain: irq domain
  * @version: chip version id (e.g. ab8500 or ab9540)
  * @chip_id: chip revision id
  * @write: register write
@@ -247,6 +249,7 @@ struct ab8500 {
        atomic_t        transfer_ongoing;
        int             irq_base;
        int             irq;
+       struct irq_domain  *domain;
        enum ab8500_version version;
        u8              chip_id;
 
@@ -338,4 +341,6 @@ static inline int is_ab8500_2p0(struct ab8500 *ab)
        return (is_ab8500(ab) && (ab->chip_id == AB8500_CUT2P0));
 }
 
+int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq);
+
 #endif /* MFD_AB8500_H */
diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h
new file mode 100644 (file)
index 0000000..dd231ac
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Arizona MFD internals
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM_ARIZONA_CORE_H
+#define _WM_ARIZONA_CORE_H
+
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/arizona/pdata.h>
+
+#define ARIZONA_MAX_CORE_SUPPLIES 3
+
+enum arizona_type {
+       WM5102 = 1,
+       WM5110 = 2,
+};
+
+#define ARIZONA_IRQ_GP1                    0
+#define ARIZONA_IRQ_GP2                    1
+#define ARIZONA_IRQ_GP3                    2
+#define ARIZONA_IRQ_GP4                    3
+#define ARIZONA_IRQ_GP5_FALL               4
+#define ARIZONA_IRQ_GP5_RISE               5
+#define ARIZONA_IRQ_JD_FALL                6
+#define ARIZONA_IRQ_JD_RISE                7
+#define ARIZONA_IRQ_DSP1_RAM_RDY           8
+#define ARIZONA_IRQ_DSP2_RAM_RDY           9
+#define ARIZONA_IRQ_DSP3_RAM_RDY          10
+#define ARIZONA_IRQ_DSP4_RAM_RDY          11
+#define ARIZONA_IRQ_DSP_IRQ1              12
+#define ARIZONA_IRQ_DSP_IRQ2              13
+#define ARIZONA_IRQ_DSP_IRQ3              14
+#define ARIZONA_IRQ_DSP_IRQ4              15
+#define ARIZONA_IRQ_DSP_IRQ5              16
+#define ARIZONA_IRQ_DSP_IRQ6              17
+#define ARIZONA_IRQ_DSP_IRQ7              18
+#define ARIZONA_IRQ_DSP_IRQ8              19
+#define ARIZONA_IRQ_SPK_SHUTDOWN_WARN     20
+#define ARIZONA_IRQ_SPK_SHUTDOWN          21
+#define ARIZONA_IRQ_MICDET                22
+#define ARIZONA_IRQ_HPDET                 23
+#define ARIZONA_IRQ_WSEQ_DONE             24
+#define ARIZONA_IRQ_DRC2_SIG_DET          25
+#define ARIZONA_IRQ_DRC1_SIG_DET          26
+#define ARIZONA_IRQ_ASRC2_LOCK            27
+#define ARIZONA_IRQ_ASRC1_LOCK            28
+#define ARIZONA_IRQ_UNDERCLOCKED          29
+#define ARIZONA_IRQ_OVERCLOCKED           30
+#define ARIZONA_IRQ_FLL2_LOCK             31
+#define ARIZONA_IRQ_FLL1_LOCK             32
+#define ARIZONA_IRQ_CLKGEN_ERR            33
+#define ARIZONA_IRQ_CLKGEN_ERR_ASYNC      34
+#define ARIZONA_IRQ_ASRC_CFG_ERR          35
+#define ARIZONA_IRQ_AIF3_ERR              36
+#define ARIZONA_IRQ_AIF2_ERR              37
+#define ARIZONA_IRQ_AIF1_ERR              38
+#define ARIZONA_IRQ_CTRLIF_ERR            39
+#define ARIZONA_IRQ_MIXER_DROPPED_SAMPLES 40
+#define ARIZONA_IRQ_ASYNC_CLK_ENA_LOW     41
+#define ARIZONA_IRQ_SYSCLK_ENA_LOW        42
+#define ARIZONA_IRQ_ISRC1_CFG_ERR         43
+#define ARIZONA_IRQ_ISRC2_CFG_ERR         44
+#define ARIZONA_IRQ_BOOT_DONE             45
+#define ARIZONA_IRQ_DCS_DAC_DONE          46
+#define ARIZONA_IRQ_DCS_HP_DONE           47
+#define ARIZONA_IRQ_FLL2_CLOCK_OK         48
+#define ARIZONA_IRQ_FLL1_CLOCK_OK         49
+
+#define ARIZONA_NUM_IRQ                   50
+
+struct arizona {
+       struct regmap *regmap;
+       struct device *dev;
+
+       enum arizona_type type;
+       unsigned int rev;
+
+       int num_core_supplies;
+       struct regulator_bulk_data core_supplies[ARIZONA_MAX_CORE_SUPPLIES];
+       struct regulator *dcvdd;
+
+       struct arizona_pdata pdata;
+
+       int irq;
+       struct irq_domain *virq;
+       struct regmap_irq_chip_data *aod_irq_chip;
+       struct regmap_irq_chip_data *irq_chip;
+
+       struct mutex clk_lock;
+       int clk32k_ref;
+};
+
+int arizona_clk32k_enable(struct arizona *arizona);
+int arizona_clk32k_disable(struct arizona *arizona);
+
+int arizona_request_irq(struct arizona *arizona, int irq, char *name,
+                       irq_handler_t handler, void *data);
+void arizona_free_irq(struct arizona *arizona, int irq, void *data);
+int arizona_set_irq_wake(struct arizona *arizona, int irq, int on);
+
+int wm5102_patch(struct arizona *arizona);
+int wm5110_patch(struct arizona *arizona);
+
+#endif
diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h
new file mode 100644 (file)
index 0000000..7ab4429
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Platform data for Arizona devices
+ *
+ * Copyright 2012 Wolfson Microelectronics. PLC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _ARIZONA_PDATA_H
+#define _ARIZONA_PDATA_H
+
+#define ARIZONA_GPN_DIR                          0x8000  /* GPN_DIR */
+#define ARIZONA_GPN_DIR_MASK                     0x8000  /* GPN_DIR */
+#define ARIZONA_GPN_DIR_SHIFT                        15  /* GPN_DIR */
+#define ARIZONA_GPN_DIR_WIDTH                         1  /* GPN_DIR */
+#define ARIZONA_GPN_PU                           0x4000  /* GPN_PU */
+#define ARIZONA_GPN_PU_MASK                      0x4000  /* GPN_PU */
+#define ARIZONA_GPN_PU_SHIFT                         14  /* GPN_PU */
+#define ARIZONA_GPN_PU_WIDTH                          1  /* GPN_PU */
+#define ARIZONA_GPN_PD                           0x2000  /* GPN_PD */
+#define ARIZONA_GPN_PD_MASK                      0x2000  /* GPN_PD */
+#define ARIZONA_GPN_PD_SHIFT                         13  /* GPN_PD */
+#define ARIZONA_GPN_PD_WIDTH                          1  /* GPN_PD */
+#define ARIZONA_GPN_LVL                          0x0800  /* GPN_LVL */
+#define ARIZONA_GPN_LVL_MASK                     0x0800  /* GPN_LVL */
+#define ARIZONA_GPN_LVL_SHIFT                        11  /* GPN_LVL */
+#define ARIZONA_GPN_LVL_WIDTH                         1  /* GPN_LVL */
+#define ARIZONA_GPN_POL                          0x0400  /* GPN_POL */
+#define ARIZONA_GPN_POL_MASK                     0x0400  /* GPN_POL */
+#define ARIZONA_GPN_POL_SHIFT                        10  /* GPN_POL */
+#define ARIZONA_GPN_POL_WIDTH                         1  /* GPN_POL */
+#define ARIZONA_GPN_OP_CFG                       0x0200  /* GPN_OP_CFG */
+#define ARIZONA_GPN_OP_CFG_MASK                  0x0200  /* GPN_OP_CFG */
+#define ARIZONA_GPN_OP_CFG_SHIFT                      9  /* GPN_OP_CFG */
+#define ARIZONA_GPN_OP_CFG_WIDTH                      1  /* GPN_OP_CFG */
+#define ARIZONA_GPN_DB                           0x0100  /* GPN_DB */
+#define ARIZONA_GPN_DB_MASK                      0x0100  /* GPN_DB */
+#define ARIZONA_GPN_DB_SHIFT                          8  /* GPN_DB */
+#define ARIZONA_GPN_DB_WIDTH                          1  /* GPN_DB */
+#define ARIZONA_GPN_FN_MASK                      0x007F  /* GPN_FN - [6:0] */
+#define ARIZONA_GPN_FN_SHIFT                          0  /* GPN_FN - [6:0] */
+#define ARIZONA_GPN_FN_WIDTH                          7  /* GPN_FN - [6:0] */
+
+#define ARIZONA_MAX_GPIO 5
+
+#define ARIZONA_32KZ_MCLK1 1
+#define ARIZONA_32KZ_MCLK2 2
+#define ARIZONA_32KZ_NONE  3
+
+#define ARIZONA_MAX_INPUT 4
+
+#define ARIZONA_DMIC_MICVDD   0
+#define ARIZONA_DMIC_MICBIAS1 1
+#define ARIZONA_DMIC_MICBIAS2 2
+#define ARIZONA_DMIC_MICBIAS3 3
+
+#define ARIZONA_INMODE_DIFF 0
+#define ARIZONA_INMODE_SE   1
+#define ARIZONA_INMODE_DMIC 2
+
+#define ARIZONA_MAX_OUTPUT 6
+
+#define ARIZONA_MAX_PDM_SPK 2
+
+struct regulator_init_data;
+
+struct arizona_micd_config {
+       unsigned int src;
+       unsigned int bias;
+       bool gpio;
+};
+
+struct arizona_pdata {
+       int reset;      /** GPIO controlling /RESET, if any */
+       int ldoena;     /** GPIO controlling LODENA, if any */
+
+       /** Regulator configuration for MICVDD */
+       struct regulator_init_data *micvdd;
+
+       /** Regulator configuration for LDO1 */
+       struct regulator_init_data *ldo1;
+
+       /** If a direct 32kHz clock is provided on an MCLK specify it here */
+       int clk32k_src;
+
+       bool irq_active_high; /** IRQ polarity */
+
+       /* Base GPIO */
+       int gpio_base;
+
+       /** Pin state for GPIO pins */
+       int gpio_defaults[ARIZONA_MAX_GPIO];
+
+       /** GPIO for mic detection polarity */
+       int micd_pol_gpio;
+
+       /** Headset polarity configurations */
+       struct arizona_micd_config *micd_configs;
+       int num_micd_configs;
+
+       /** Reference voltage for DMIC inputs */
+       int dmic_ref[ARIZONA_MAX_INPUT];
+
+       /** Mode of input structures */
+       int inmode[ARIZONA_MAX_INPUT];
+
+       /** Mode for outputs */
+       bool out_mono[ARIZONA_MAX_OUTPUT];
+
+       /** PDM speaker mute setting */
+       unsigned int spk_mute[ARIZONA_MAX_PDM_SPK];
+
+       /** PDM speaker format */
+       unsigned int spk_fmt[ARIZONA_MAX_PDM_SPK];
+};
+
+#endif
diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h
new file mode 100644 (file)
index 0000000..7671a28
--- /dev/null
@@ -0,0 +1,6594 @@
+/*
+ * ARIZONA register definitions
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _ARIZONA_REGISTERS_H
+#define _ARIZONA_REGISTERS_H
+
+/*
+ * Register values.
+ */
+#define ARIZONA_SOFTWARE_RESET                   0x00
+#define ARIZONA_DEVICE_REVISION                  0x01
+#define ARIZONA_CTRL_IF_SPI_CFG_1                0x08
+#define ARIZONA_CTRL_IF_I2C1_CFG_1               0x09
+#define ARIZONA_CTRL_IF_I2C2_CFG_1               0x0A
+#define ARIZONA_CTRL_IF_I2C1_CFG_2               0x0B
+#define ARIZONA_CTRL_IF_I2C2_CFG_2               0x0C
+#define ARIZONA_CTRL_IF_STATUS_1                 0x0D
+#define ARIZONA_WRITE_SEQUENCER_CTRL_0           0x16
+#define ARIZONA_WRITE_SEQUENCER_CTRL_1           0x17
+#define ARIZONA_WRITE_SEQUENCER_CTRL_2           0x18
+#define ARIZONA_WRITE_SEQUENCER_PROM             0x1A
+#define ARIZONA_TONE_GENERATOR_1                 0x20
+#define ARIZONA_TONE_GENERATOR_2                 0x21
+#define ARIZONA_TONE_GENERATOR_3                 0x22
+#define ARIZONA_TONE_GENERATOR_4                 0x23
+#define ARIZONA_TONE_GENERATOR_5                 0x24
+#define ARIZONA_PWM_DRIVE_1                      0x30
+#define ARIZONA_PWM_DRIVE_2                      0x31
+#define ARIZONA_PWM_DRIVE_3                      0x32
+#define ARIZONA_WAKE_CONTROL                     0x40
+#define ARIZONA_SEQUENCE_CONTROL                 0x41
+#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1    0x61
+#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2    0x62
+#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3    0x63
+#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4    0x64
+#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1 0x68
+#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2 0x69
+#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3 0x6A
+#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4 0x6B
+#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5 0x6C
+#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6 0x6D
+#define ARIZONA_COMFORT_NOISE_GENERATOR          0x70
+#define ARIZONA_HAPTICS_CONTROL_1                0x90
+#define ARIZONA_HAPTICS_CONTROL_2                0x91
+#define ARIZONA_HAPTICS_PHASE_1_INTENSITY        0x92
+#define ARIZONA_HAPTICS_PHASE_1_DURATION         0x93
+#define ARIZONA_HAPTICS_PHASE_2_INTENSITY        0x94
+#define ARIZONA_HAPTICS_PHASE_2_DURATION         0x95
+#define ARIZONA_HAPTICS_PHASE_3_INTENSITY        0x96
+#define ARIZONA_HAPTICS_PHASE_3_DURATION         0x97
+#define ARIZONA_HAPTICS_STATUS                   0x98
+#define ARIZONA_CLOCK_32K_1                      0x100
+#define ARIZONA_SYSTEM_CLOCK_1                   0x101
+#define ARIZONA_SAMPLE_RATE_1                    0x102
+#define ARIZONA_SAMPLE_RATE_2                    0x103
+#define ARIZONA_SAMPLE_RATE_3                    0x104
+#define ARIZONA_SAMPLE_RATE_1_STATUS             0x10A
+#define ARIZONA_SAMPLE_RATE_2_STATUS             0x10B
+#define ARIZONA_SAMPLE_RATE_3_STATUS             0x10C
+#define ARIZONA_ASYNC_CLOCK_1                    0x112
+#define ARIZONA_ASYNC_SAMPLE_RATE_1              0x113
+#define ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS       0x11B
+#define ARIZONA_OUTPUT_SYSTEM_CLOCK              0x149
+#define ARIZONA_OUTPUT_ASYNC_CLOCK               0x14A
+#define ARIZONA_RATE_ESTIMATOR_1                 0x152
+#define ARIZONA_RATE_ESTIMATOR_2                 0x153
+#define ARIZONA_RATE_ESTIMATOR_3                 0x154
+#define ARIZONA_RATE_ESTIMATOR_4                 0x155
+#define ARIZONA_RATE_ESTIMATOR_5                 0x156
+#define ARIZONA_FLL1_CONTROL_1                   0x171
+#define ARIZONA_FLL1_CONTROL_2                   0x172
+#define ARIZONA_FLL1_CONTROL_3                   0x173
+#define ARIZONA_FLL1_CONTROL_4                   0x174
+#define ARIZONA_FLL1_CONTROL_5                   0x175
+#define ARIZONA_FLL1_CONTROL_6                   0x176
+#define ARIZONA_FLL1_LOOP_FILTER_TEST_1          0x177
+#define ARIZONA_FLL1_NCO_TEST_0                  0x178
+#define ARIZONA_FLL1_SYNCHRONISER_1              0x181
+#define ARIZONA_FLL1_SYNCHRONISER_2              0x182
+#define ARIZONA_FLL1_SYNCHRONISER_3              0x183
+#define ARIZONA_FLL1_SYNCHRONISER_4              0x184
+#define ARIZONA_FLL1_SYNCHRONISER_5              0x185
+#define ARIZONA_FLL1_SYNCHRONISER_6              0x186
+#define ARIZONA_FLL1_SPREAD_SPECTRUM             0x189
+#define ARIZONA_FLL1_GPIO_CLOCK                  0x18A
+#define ARIZONA_FLL2_CONTROL_1                   0x191
+#define ARIZONA_FLL2_CONTROL_2                   0x192
+#define ARIZONA_FLL2_CONTROL_3                   0x193
+#define ARIZONA_FLL2_CONTROL_4                   0x194
+#define ARIZONA_FLL2_CONTROL_5                   0x195
+#define ARIZONA_FLL2_CONTROL_6                   0x196
+#define ARIZONA_FLL2_LOOP_FILTER_TEST_1          0x197
+#define ARIZONA_FLL2_NCO_TEST_0                  0x198
+#define ARIZONA_FLL2_SYNCHRONISER_1              0x1A1
+#define ARIZONA_FLL2_SYNCHRONISER_2              0x1A2
+#define ARIZONA_FLL2_SYNCHRONISER_3              0x1A3
+#define ARIZONA_FLL2_SYNCHRONISER_4              0x1A4
+#define ARIZONA_FLL2_SYNCHRONISER_5              0x1A5
+#define ARIZONA_FLL2_SYNCHRONISER_6              0x1A6
+#define ARIZONA_FLL2_SPREAD_SPECTRUM             0x1A9
+#define ARIZONA_FLL2_GPIO_CLOCK                  0x1AA
+#define ARIZONA_MIC_CHARGE_PUMP_1                0x200
+#define ARIZONA_LDO1_CONTROL_1                   0x210
+#define ARIZONA_LDO2_CONTROL_1                   0x213
+#define ARIZONA_MIC_BIAS_CTRL_1                  0x218
+#define ARIZONA_MIC_BIAS_CTRL_2                  0x219
+#define ARIZONA_MIC_BIAS_CTRL_3                  0x21A
+#define ARIZONA_ACCESSORY_DETECT_MODE_1          0x293
+#define ARIZONA_HEADPHONE_DETECT_1               0x29B
+#define ARIZONA_HEADPHONE_DETECT_2               0x29C
+#define ARIZONA_MIC_DETECT_1                     0x2A3
+#define ARIZONA_MIC_DETECT_2                     0x2A4
+#define ARIZONA_MIC_DETECT_3                     0x2A5
+#define ARIZONA_MIC_NOISE_MIX_CONTROL_1          0x2C3
+#define ARIZONA_ISOLATION_CONTROL                0x2CB
+#define ARIZONA_JACK_DETECT_ANALOGUE             0x2D3
+#define ARIZONA_INPUT_ENABLES                    0x300
+#define ARIZONA_INPUT_ENABLES_STATUS             0x301
+#define ARIZONA_INPUT_RATE                       0x308
+#define ARIZONA_INPUT_VOLUME_RAMP                0x309
+#define ARIZONA_IN1L_CONTROL                     0x310
+#define ARIZONA_ADC_DIGITAL_VOLUME_1L            0x311
+#define ARIZONA_DMIC1L_CONTROL                   0x312
+#define ARIZONA_IN1R_CONTROL                     0x314
+#define ARIZONA_ADC_DIGITAL_VOLUME_1R            0x315
+#define ARIZONA_DMIC1R_CONTROL                   0x316
+#define ARIZONA_IN2L_CONTROL                     0x318
+#define ARIZONA_ADC_DIGITAL_VOLUME_2L            0x319
+#define ARIZONA_DMIC2L_CONTROL                   0x31A
+#define ARIZONA_IN2R_CONTROL                     0x31C
+#define ARIZONA_ADC_DIGITAL_VOLUME_2R            0x31D
+#define ARIZONA_DMIC2R_CONTROL                   0x31E
+#define ARIZONA_IN3L_CONTROL                     0x320
+#define ARIZONA_ADC_DIGITAL_VOLUME_3L            0x321
+#define ARIZONA_DMIC3L_CONTROL                   0x322
+#define ARIZONA_IN3R_CONTROL                     0x324
+#define ARIZONA_ADC_DIGITAL_VOLUME_3R            0x325
+#define ARIZONA_DMIC3R_CONTROL                   0x326
+#define ARIZONA_IN4L_CONTROL                     0x328
+#define ARIZONA_ADC_DIGITAL_VOLUME_4L            0x329
+#define ARIZONA_DMIC4L_CONTROL                   0x32A
+#define ARIZONA_ADC_DIGITAL_VOLUME_4R            0x32D
+#define ARIZONA_DMIC4R_CONTROL                   0x32E
+#define ARIZONA_OUTPUT_ENABLES_1                 0x400
+#define ARIZONA_OUTPUT_STATUS_1                  0x401
+#define ARIZONA_RAW_OUTPUT_STATUS_1              0x406
+#define ARIZONA_OUTPUT_RATE_1                    0x408
+#define ARIZONA_OUTPUT_VOLUME_RAMP               0x409
+#define ARIZONA_OUTPUT_PATH_CONFIG_1L            0x410
+#define ARIZONA_DAC_DIGITAL_VOLUME_1L            0x411
+#define ARIZONA_DAC_VOLUME_LIMIT_1L              0x412
+#define ARIZONA_NOISE_GATE_SELECT_1L             0x413
+#define ARIZONA_OUTPUT_PATH_CONFIG_1R            0x414
+#define ARIZONA_DAC_DIGITAL_VOLUME_1R            0x415
+#define ARIZONA_DAC_VOLUME_LIMIT_1R              0x416
+#define ARIZONA_NOISE_GATE_SELECT_1R             0x417
+#define ARIZONA_OUTPUT_PATH_CONFIG_2L            0x418
+#define ARIZONA_DAC_DIGITAL_VOLUME_2L            0x419
+#define ARIZONA_DAC_VOLUME_LIMIT_2L              0x41A
+#define ARIZONA_NOISE_GATE_SELECT_2L             0x41B
+#define ARIZONA_OUTPUT_PATH_CONFIG_2R            0x41C
+#define ARIZONA_DAC_DIGITAL_VOLUME_2R            0x41D
+#define ARIZONA_DAC_VOLUME_LIMIT_2R              0x41E
+#define ARIZONA_NOISE_GATE_SELECT_2R             0x41F
+#define ARIZONA_OUTPUT_PATH_CONFIG_3L            0x420
+#define ARIZONA_DAC_DIGITAL_VOLUME_3L            0x421
+#define ARIZONA_DAC_VOLUME_LIMIT_3L              0x422
+#define ARIZONA_NOISE_GATE_SELECT_3L             0x423
+#define ARIZONA_OUTPUT_PATH_CONFIG_3R            0x424
+#define ARIZONA_DAC_DIGITAL_VOLUME_3R            0x425
+#define ARIZONA_DAC_VOLUME_LIMIT_3R              0x426
+#define ARIZONA_NOISE_GATE_SELECT_3R             0x427
+#define ARIZONA_OUTPUT_PATH_CONFIG_4L            0x428
+#define ARIZONA_DAC_DIGITAL_VOLUME_4L            0x429
+#define ARIZONA_OUT_VOLUME_4L                    0x42A
+#define ARIZONA_NOISE_GATE_SELECT_4L             0x42B
+#define ARIZONA_OUTPUT_PATH_CONFIG_4R            0x42C
+#define ARIZONA_DAC_DIGITAL_VOLUME_4R            0x42D
+#define ARIZONA_OUT_VOLUME_4R                    0x42E
+#define ARIZONA_NOISE_GATE_SELECT_4R             0x42F
+#define ARIZONA_OUTPUT_PATH_CONFIG_5L            0x430
+#define ARIZONA_DAC_DIGITAL_VOLUME_5L            0x431
+#define ARIZONA_DAC_VOLUME_LIMIT_5L              0x432
+#define ARIZONA_NOISE_GATE_SELECT_5L             0x433
+#define ARIZONA_OUTPUT_PATH_CONFIG_5R            0x434
+#define ARIZONA_DAC_DIGITAL_VOLUME_5R            0x435
+#define ARIZONA_DAC_VOLUME_LIMIT_5R              0x436
+#define ARIZONA_NOISE_GATE_SELECT_5R             0x437
+#define ARIZONA_OUTPUT_PATH_CONFIG_6L            0x438
+#define ARIZONA_DAC_DIGITAL_VOLUME_6L            0x439
+#define ARIZONA_DAC_VOLUME_LIMIT_6L              0x43A
+#define ARIZONA_NOISE_GATE_SELECT_6L             0x43B
+#define ARIZONA_OUTPUT_PATH_CONFIG_6R            0x43C
+#define ARIZONA_DAC_DIGITAL_VOLUME_6R            0x43D
+#define ARIZONA_DAC_VOLUME_LIMIT_6R              0x43E
+#define ARIZONA_NOISE_GATE_SELECT_6R             0x43F
+#define ARIZONA_DAC_AEC_CONTROL_1                0x450
+#define ARIZONA_NOISE_GATE_CONTROL               0x458
+#define ARIZONA_PDM_SPK1_CTRL_1                  0x490
+#define ARIZONA_PDM_SPK1_CTRL_2                  0x491
+#define ARIZONA_PDM_SPK2_CTRL_1                  0x492
+#define ARIZONA_PDM_SPK2_CTRL_2                  0x493
+#define ARIZONA_DAC_COMP_1                       0x4DC
+#define ARIZONA_DAC_COMP_2                       0x4DD
+#define ARIZONA_DAC_COMP_3                       0x4DE
+#define ARIZONA_DAC_COMP_4                       0x4DF
+#define ARIZONA_AIF1_BCLK_CTRL                   0x500
+#define ARIZONA_AIF1_TX_PIN_CTRL                 0x501
+#define ARIZONA_AIF1_RX_PIN_CTRL                 0x502
+#define ARIZONA_AIF1_RATE_CTRL                   0x503
+#define ARIZONA_AIF1_FORMAT                      0x504
+#define ARIZONA_AIF1_TX_BCLK_RATE                0x505
+#define ARIZONA_AIF1_RX_BCLK_RATE                0x506
+#define ARIZONA_AIF1_FRAME_CTRL_1                0x507
+#define ARIZONA_AIF1_FRAME_CTRL_2                0x508
+#define ARIZONA_AIF1_FRAME_CTRL_3                0x509
+#define ARIZONA_AIF1_FRAME_CTRL_4                0x50A
+#define ARIZONA_AIF1_FRAME_CTRL_5                0x50B
+#define ARIZONA_AIF1_FRAME_CTRL_6                0x50C
+#define ARIZONA_AIF1_FRAME_CTRL_7                0x50D
+#define ARIZONA_AIF1_FRAME_CTRL_8                0x50E
+#define ARIZONA_AIF1_FRAME_CTRL_9                0x50F
+#define ARIZONA_AIF1_FRAME_CTRL_10               0x510
+#define ARIZONA_AIF1_FRAME_CTRL_11               0x511
+#define ARIZONA_AIF1_FRAME_CTRL_12               0x512
+#define ARIZONA_AIF1_FRAME_CTRL_13               0x513
+#define ARIZONA_AIF1_FRAME_CTRL_14               0x514
+#define ARIZONA_AIF1_FRAME_CTRL_15               0x515
+#define ARIZONA_AIF1_FRAME_CTRL_16               0x516
+#define ARIZONA_AIF1_FRAME_CTRL_17               0x517
+#define ARIZONA_AIF1_FRAME_CTRL_18               0x518
+#define ARIZONA_AIF1_TX_ENABLES                  0x519
+#define ARIZONA_AIF1_RX_ENABLES                  0x51A
+#define ARIZONA_AIF1_FORCE_WRITE                 0x51B
+#define ARIZONA_AIF2_BCLK_CTRL                   0x540
+#define ARIZONA_AIF2_TX_PIN_CTRL                 0x541
+#define ARIZONA_AIF2_RX_PIN_CTRL                 0x542
+#define ARIZONA_AIF2_RATE_CTRL                   0x543
+#define ARIZONA_AIF2_FORMAT                      0x544
+#define ARIZONA_AIF2_TX_BCLK_RATE                0x545
+#define ARIZONA_AIF2_RX_BCLK_RATE                0x546
+#define ARIZONA_AIF2_FRAME_CTRL_1                0x547
+#define ARIZONA_AIF2_FRAME_CTRL_2                0x548
+#define ARIZONA_AIF2_FRAME_CTRL_3                0x549
+#define ARIZONA_AIF2_FRAME_CTRL_4                0x54A
+#define ARIZONA_AIF2_FRAME_CTRL_11               0x551
+#define ARIZONA_AIF2_FRAME_CTRL_12               0x552
+#define ARIZONA_AIF2_TX_ENABLES                  0x559
+#define ARIZONA_AIF2_RX_ENABLES                  0x55A
+#define ARIZONA_AIF2_FORCE_WRITE                 0x55B
+#define ARIZONA_AIF3_BCLK_CTRL                   0x580
+#define ARIZONA_AIF3_TX_PIN_CTRL                 0x581
+#define ARIZONA_AIF3_RX_PIN_CTRL                 0x582
+#define ARIZONA_AIF3_RATE_CTRL                   0x583
+#define ARIZONA_AIF3_FORMAT                      0x584
+#define ARIZONA_AIF3_TX_BCLK_RATE                0x585
+#define ARIZONA_AIF3_RX_BCLK_RATE                0x586
+#define ARIZONA_AIF3_FRAME_CTRL_1                0x587
+#define ARIZONA_AIF3_FRAME_CTRL_2                0x588
+#define ARIZONA_AIF3_FRAME_CTRL_3                0x589
+#define ARIZONA_AIF3_FRAME_CTRL_4                0x58A
+#define ARIZONA_AIF3_FRAME_CTRL_11               0x591
+#define ARIZONA_AIF3_FRAME_CTRL_12               0x592
+#define ARIZONA_AIF3_TX_ENABLES                  0x599
+#define ARIZONA_AIF3_RX_ENABLES                  0x59A
+#define ARIZONA_AIF3_FORCE_WRITE                 0x59B
+#define ARIZONA_SLIMBUS_FRAMER_REF_GEAR          0x5E3
+#define ARIZONA_SLIMBUS_RATES_1                  0x5E5
+#define ARIZONA_SLIMBUS_RATES_2                  0x5E6
+#define ARIZONA_SLIMBUS_RATES_3                  0x5E7
+#define ARIZONA_SLIMBUS_RATES_4                  0x5E8
+#define ARIZONA_SLIMBUS_RATES_5                  0x5E9
+#define ARIZONA_SLIMBUS_RATES_6                  0x5EA
+#define ARIZONA_SLIMBUS_RATES_7                  0x5EB
+#define ARIZONA_SLIMBUS_RATES_8                  0x5EC
+#define ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE        0x5F5
+#define ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE        0x5F6
+#define ARIZONA_SLIMBUS_RX_PORT_STATUS           0x5F7
+#define ARIZONA_SLIMBUS_TX_PORT_STATUS           0x5F8
+#define ARIZONA_PWM1MIX_INPUT_1_SOURCE           0x640
+#define ARIZONA_PWM1MIX_INPUT_1_VOLUME           0x641
+#define ARIZONA_PWM1MIX_INPUT_2_SOURCE           0x642
+#define ARIZONA_PWM1MIX_INPUT_2_VOLUME           0x643
+#define ARIZONA_PWM1MIX_INPUT_3_SOURCE           0x644
+#define ARIZONA_PWM1MIX_INPUT_3_VOLUME           0x645
+#define ARIZONA_PWM1MIX_INPUT_4_SOURCE           0x646
+#define ARIZONA_PWM1MIX_INPUT_4_VOLUME           0x647
+#define ARIZONA_PWM2MIX_INPUT_1_SOURCE           0x648
+#define ARIZONA_PWM2MIX_INPUT_1_VOLUME           0x649
+#define ARIZONA_PWM2MIX_INPUT_2_SOURCE           0x64A
+#define ARIZONA_PWM2MIX_INPUT_2_VOLUME           0x64B
+#define ARIZONA_PWM2MIX_INPUT_3_SOURCE           0x64C
+#define ARIZONA_PWM2MIX_INPUT_3_VOLUME           0x64D
+#define ARIZONA_PWM2MIX_INPUT_4_SOURCE           0x64E
+#define ARIZONA_PWM2MIX_INPUT_4_VOLUME           0x64F
+#define ARIZONA_MICMIX_INPUT_1_SOURCE            0x660
+#define ARIZONA_MICMIX_INPUT_1_VOLUME            0x661
+#define ARIZONA_MICMIX_INPUT_2_SOURCE            0x662
+#define ARIZONA_MICMIX_INPUT_2_VOLUME            0x663
+#define ARIZONA_MICMIX_INPUT_3_SOURCE            0x664
+#define ARIZONA_MICMIX_INPUT_3_VOLUME            0x665
+#define ARIZONA_MICMIX_INPUT_4_SOURCE            0x666
+#define ARIZONA_MICMIX_INPUT_4_VOLUME            0x667
+#define ARIZONA_NOISEMIX_INPUT_1_SOURCE          0x668
+#define ARIZONA_NOISEMIX_INPUT_1_VOLUME          0x669
+#define ARIZONA_NOISEMIX_INPUT_2_SOURCE          0x66A
+#define ARIZONA_NOISEMIX_INPUT_2_VOLUME          0x66B
+#define ARIZONA_NOISEMIX_INPUT_3_SOURCE          0x66C
+#define ARIZONA_NOISEMIX_INPUT_3_VOLUME          0x66D
+#define ARIZONA_NOISEMIX_INPUT_4_SOURCE          0x66E
+#define ARIZONA_NOISEMIX_INPUT_4_VOLUME          0x66F
+#define ARIZONA_OUT1LMIX_INPUT_1_SOURCE          0x680
+#define ARIZONA_OUT1LMIX_INPUT_1_VOLUME          0x681
+#define ARIZONA_OUT1LMIX_INPUT_2_SOURCE          0x682
+#define ARIZONA_OUT1LMIX_INPUT_2_VOLUME          0x683
+#define ARIZONA_OUT1LMIX_INPUT_3_SOURCE          0x684
+#define ARIZONA_OUT1LMIX_INPUT_3_VOLUME          0x685
+#define ARIZONA_OUT1LMIX_INPUT_4_SOURCE          0x686
+#define ARIZONA_OUT1LMIX_INPUT_4_VOLUME          0x687
+#define ARIZONA_OUT1RMIX_INPUT_1_SOURCE          0x688
+#define ARIZONA_OUT1RMIX_INPUT_1_VOLUME          0x689
+#define ARIZONA_OUT1RMIX_INPUT_2_SOURCE          0x68A
+#define ARIZONA_OUT1RMIX_INPUT_2_VOLUME          0x68B
+#define ARIZONA_OUT1RMIX_INPUT_3_SOURCE          0x68C
+#define ARIZONA_OUT1RMIX_INPUT_3_VOLUME          0x68D
+#define ARIZONA_OUT1RMIX_INPUT_4_SOURCE          0x68E
+#define ARIZONA_OUT1RMIX_INPUT_4_VOLUME          0x68F
+#define ARIZONA_OUT2LMIX_INPUT_1_SOURCE          0x690
+#define ARIZONA_OUT2LMIX_INPUT_1_VOLUME          0x691
+#define ARIZONA_OUT2LMIX_INPUT_2_SOURCE          0x692
+#define ARIZONA_OUT2LMIX_INPUT_2_VOLUME          0x693
+#define ARIZONA_OUT2LMIX_INPUT_3_SOURCE          0x694
+#define ARIZONA_OUT2LMIX_INPUT_3_VOLUME          0x695
+#define ARIZONA_OUT2LMIX_INPUT_4_SOURCE          0x696
+#define ARIZONA_OUT2LMIX_INPUT_4_VOLUME          0x697
+#define ARIZONA_OUT2RMIX_INPUT_1_SOURCE          0x698
+#define ARIZONA_OUT2RMIX_INPUT_1_VOLUME          0x699
+#define ARIZONA_OUT2RMIX_INPUT_2_SOURCE          0x69A
+#define ARIZONA_OUT2RMIX_INPUT_2_VOLUME          0x69B
+#define ARIZONA_OUT2RMIX_INPUT_3_SOURCE          0x69C
+#define ARIZONA_OUT2RMIX_INPUT_3_VOLUME          0x69D
+#define ARIZONA_OUT2RMIX_INPUT_4_SOURCE          0x69E
+#define ARIZONA_OUT2RMIX_INPUT_4_VOLUME          0x69F
+#define ARIZONA_OUT3LMIX_INPUT_1_SOURCE          0x6A0
+#define ARIZONA_OUT3LMIX_INPUT_1_VOLUME          0x6A1
+#define ARIZONA_OUT3LMIX_INPUT_2_SOURCE          0x6A2
+#define ARIZONA_OUT3LMIX_INPUT_2_VOLUME          0x6A3
+#define ARIZONA_OUT3LMIX_INPUT_3_SOURCE          0x6A4
+#define ARIZONA_OUT3LMIX_INPUT_3_VOLUME          0x6A5
+#define ARIZONA_OUT3LMIX_INPUT_4_SOURCE          0x6A6
+#define ARIZONA_OUT3LMIX_INPUT_4_VOLUME          0x6A7
+#define ARIZONA_OUT3RMIX_INPUT_1_SOURCE          0x6A8
+#define ARIZONA_OUT3RMIX_INPUT_1_VOLUME          0x6A9
+#define ARIZONA_OUT3RMIX_INPUT_2_SOURCE          0x6AA
+#define ARIZONA_OUT3RMIX_INPUT_2_VOLUME          0x6AB
+#define ARIZONA_OUT3RMIX_INPUT_3_SOURCE          0x6AC
+#define ARIZONA_OUT3RMIX_INPUT_3_VOLUME          0x6AD
+#define ARIZONA_OUT3RMIX_INPUT_4_SOURCE          0x6AE
+#define ARIZONA_OUT3RMIX_INPUT_4_VOLUME          0x6AF
+#define ARIZONA_OUT4LMIX_INPUT_1_SOURCE          0x6B0
+#define ARIZONA_OUT4LMIX_INPUT_1_VOLUME          0x6B1
+#define ARIZONA_OUT4LMIX_INPUT_2_SOURCE          0x6B2
+#define ARIZONA_OUT4LMIX_INPUT_2_VOLUME          0x6B3
+#define ARIZONA_OUT4LMIX_INPUT_3_SOURCE          0x6B4
+#define ARIZONA_OUT4LMIX_INPUT_3_VOLUME          0x6B5
+#define ARIZONA_OUT4LMIX_INPUT_4_SOURCE          0x6B6
+#define ARIZONA_OUT4LMIX_INPUT_4_VOLUME          0x6B7
+#define ARIZONA_OUT4RMIX_INPUT_1_SOURCE          0x6B8
+#define ARIZONA_OUT4RMIX_INPUT_1_VOLUME          0x6B9
+#define ARIZONA_OUT4RMIX_INPUT_2_SOURCE          0x6BA
+#define ARIZONA_OUT4RMIX_INPUT_2_VOLUME          0x6BB
+#define ARIZONA_OUT4RMIX_INPUT_3_SOURCE          0x6BC
+#define ARIZONA_OUT4RMIX_INPUT_3_VOLUME          0x6BD
+#define ARIZONA_OUT4RMIX_INPUT_4_SOURCE          0x6BE
+#define ARIZONA_OUT4RMIX_INPUT_4_VOLUME          0x6BF
+#define ARIZONA_OUT5LMIX_INPUT_1_SOURCE          0x6C0
+#define ARIZONA_OUT5LMIX_INPUT_1_VOLUME          0x6C1
+#define ARIZONA_OUT5LMIX_INPUT_2_SOURCE          0x6C2
+#define ARIZONA_OUT5LMIX_INPUT_2_VOLUME          0x6C3
+#define ARIZONA_OUT5LMIX_INPUT_3_SOURCE          0x6C4
+#define ARIZONA_OUT5LMIX_INPUT_3_VOLUME          0x6C5
+#define ARIZONA_OUT5LMIX_INPUT_4_SOURCE          0x6C6
+#define ARIZONA_OUT5LMIX_INPUT_4_VOLUME          0x6C7
+#define ARIZONA_OUT5RMIX_INPUT_1_SOURCE          0x6C8
+#define ARIZONA_OUT5RMIX_INPUT_1_VOLUME          0x6C9
+#define ARIZONA_OUT5RMIX_INPUT_2_SOURCE          0x6CA
+#define ARIZONA_OUT5RMIX_INPUT_2_VOLUME          0x6CB
+#define ARIZONA_OUT5RMIX_INPUT_3_SOURCE          0x6CC
+#define ARIZONA_OUT5RMIX_INPUT_3_VOLUME          0x6CD
+#define ARIZONA_OUT5RMIX_INPUT_4_SOURCE          0x6CE
+#define ARIZONA_OUT5RMIX_INPUT_4_VOLUME          0x6CF
+#define ARIZONA_OUT6LMIX_INPUT_1_SOURCE          0x6D0
+#define ARIZONA_OUT6LMIX_INPUT_1_VOLUME          0x6D1
+#define ARIZONA_OUT6LMIX_INPUT_2_SOURCE          0x6D2
+#define ARIZONA_OUT6LMIX_INPUT_2_VOLUME          0x6D3
+#define ARIZONA_OUT6LMIX_INPUT_3_SOURCE          0x6D4
+#define ARIZONA_OUT6LMIX_INPUT_3_VOLUME          0x6D5
+#define ARIZONA_OUT6LMIX_INPUT_4_SOURCE          0x6D6
+#define ARIZONA_OUT6LMIX_INPUT_4_VOLUME          0x6D7
+#define ARIZONA_OUT6RMIX_INPUT_1_SOURCE          0x6D8
+#define ARIZONA_OUT6RMIX_INPUT_1_VOLUME          0x6D9
+#define ARIZONA_OUT6RMIX_INPUT_2_SOURCE          0x6DA
+#define ARIZONA_OUT6RMIX_INPUT_2_VOLUME          0x6DB
+#define ARIZONA_OUT6RMIX_INPUT_3_SOURCE          0x6DC
+#define ARIZONA_OUT6RMIX_INPUT_3_VOLUME          0x6DD
+#define ARIZONA_OUT6RMIX_INPUT_4_SOURCE          0x6DE
+#define ARIZONA_OUT6RMIX_INPUT_4_VOLUME          0x6DF
+#define ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE        0x700
+#define ARIZONA_AIF1TX1MIX_INPUT_1_VOLUME        0x701
+#define ARIZONA_AIF1TX1MIX_INPUT_2_SOURCE        0x702
+#define ARIZONA_AIF1TX1MIX_INPUT_2_VOLUME        0x703
+#define ARIZONA_AIF1TX1MIX_INPUT_3_SOURCE        0x704
+#define ARIZONA_AIF1TX1MIX_INPUT_3_VOLUME        0x705
+#define ARIZONA_AIF1TX1MIX_INPUT_4_SOURCE        0x706
+#define ARIZONA_AIF1TX1MIX_INPUT_4_VOLUME        0x707
+#define ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE        0x708
+#define ARIZONA_AIF1TX2MIX_INPUT_1_VOLUME        0x709
+#define ARIZONA_AIF1TX2MIX_INPUT_2_SOURCE        0x70A
+#define ARIZONA_AIF1TX2MIX_INPUT_2_VOLUME        0x70B
+#define ARIZONA_AIF1TX2MIX_INPUT_3_SOURCE        0x70C
+#define ARIZONA_AIF1TX2MIX_INPUT_3_VOLUME        0x70D
+#define ARIZONA_AIF1TX2MIX_INPUT_4_SOURCE        0x70E
+#define ARIZONA_AIF1TX2MIX_INPUT_4_VOLUME        0x70F
+#define ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE        0x710
+#define ARIZONA_AIF1TX3MIX_INPUT_1_VOLUME        0x711
+#define ARIZONA_AIF1TX3MIX_INPUT_2_SOURCE        0x712
+#define ARIZONA_AIF1TX3MIX_INPUT_2_VOLUME        0x713
+#define ARIZONA_AIF1TX3MIX_INPUT_3_SOURCE        0x714
+#define ARIZONA_AIF1TX3MIX_INPUT_3_VOLUME        0x715
+#define ARIZONA_AIF1TX3MIX_INPUT_4_SOURCE        0x716
+#define ARIZONA_AIF1TX3MIX_INPUT_4_VOLUME        0x717
+#define ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE        0x718
+#define ARIZONA_AIF1TX4MIX_INPUT_1_VOLUME        0x719
+#define ARIZONA_AIF1TX4MIX_INPUT_2_SOURCE        0x71A
+#define ARIZONA_AIF1TX4MIX_INPUT_2_VOLUME        0x71B
+#define ARIZONA_AIF1TX4MIX_INPUT_3_SOURCE        0x71C
+#define ARIZONA_AIF1TX4MIX_INPUT_3_VOLUME        0x71D
+#define ARIZONA_AIF1TX4MIX_INPUT_4_SOURCE        0x71E
+#define ARIZONA_AIF1TX4MIX_INPUT_4_VOLUME        0x71F
+#define ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE        0x720
+#define ARIZONA_AIF1TX5MIX_INPUT_1_VOLUME        0x721
+#define ARIZONA_AIF1TX5MIX_INPUT_2_SOURCE        0x722
+#define ARIZONA_AIF1TX5MIX_INPUT_2_VOLUME        0x723
+#define ARIZONA_AIF1TX5MIX_INPUT_3_SOURCE        0x724
+#define ARIZONA_AIF1TX5MIX_INPUT_3_VOLUME        0x725
+#define ARIZONA_AIF1TX5MIX_INPUT_4_SOURCE        0x726
+#define ARIZONA_AIF1TX5MIX_INPUT_4_VOLUME        0x727
+#define ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE        0x728
+#define ARIZONA_AIF1TX6MIX_INPUT_1_VOLUME        0x729
+#define ARIZONA_AIF1TX6MIX_INPUT_2_SOURCE        0x72A
+#define ARIZONA_AIF1TX6MIX_INPUT_2_VOLUME        0x72B
+#define ARIZONA_AIF1TX6MIX_INPUT_3_SOURCE        0x72C
+#define ARIZONA_AIF1TX6MIX_INPUT_3_VOLUME        0x72D
+#define ARIZONA_AIF1TX6MIX_INPUT_4_SOURCE        0x72E
+#define ARIZONA_AIF1TX6MIX_INPUT_4_VOLUME        0x72F
+#define ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE        0x730
+#define ARIZONA_AIF1TX7MIX_INPUT_1_VOLUME        0x731
+#define ARIZONA_AIF1TX7MIX_INPUT_2_SOURCE        0x732
+#define ARIZONA_AIF1TX7MIX_INPUT_2_VOLUME        0x733
+#define ARIZONA_AIF1TX7MIX_INPUT_3_SOURCE        0x734
+#define ARIZONA_AIF1TX7MIX_INPUT_3_VOLUME        0x735
+#define ARIZONA_AIF1TX7MIX_INPUT_4_SOURCE        0x736
+#define ARIZONA_AIF1TX7MIX_INPUT_4_VOLUME        0x737
+#define ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE        0x738
+#define ARIZONA_AIF1TX8MIX_INPUT_1_VOLUME        0x739
+#define ARIZONA_AIF1TX8MIX_INPUT_2_SOURCE        0x73A
+#define ARIZONA_AIF1TX8MIX_INPUT_2_VOLUME        0x73B
+#define ARIZONA_AIF1TX8MIX_INPUT_3_SOURCE        0x73C
+#define ARIZONA_AIF1TX8MIX_INPUT_3_VOLUME        0x73D
+#define ARIZONA_AIF1TX8MIX_INPUT_4_SOURCE        0x73E
+#define ARIZONA_AIF1TX8MIX_INPUT_4_VOLUME        0x73F
+#define ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE        0x740
+#define ARIZONA_AIF2TX1MIX_INPUT_1_VOLUME        0x741
+#define ARIZONA_AIF2TX1MIX_INPUT_2_SOURCE        0x742
+#define ARIZONA_AIF2TX1MIX_INPUT_2_VOLUME        0x743
+#define ARIZONA_AIF2TX1MIX_INPUT_3_SOURCE        0x744
+#define ARIZONA_AIF2TX1MIX_INPUT_3_VOLUME        0x745
+#define ARIZONA_AIF2TX1MIX_INPUT_4_SOURCE        0x746
+#define ARIZONA_AIF2TX1MIX_INPUT_4_VOLUME        0x747
+#define ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE        0x748
+#define ARIZONA_AIF2TX2MIX_INPUT_1_VOLUME        0x749
+#define ARIZONA_AIF2TX2MIX_INPUT_2_SOURCE        0x74A
+#define ARIZONA_AIF2TX2MIX_INPUT_2_VOLUME        0x74B
+#define ARIZONA_AIF2TX2MIX_INPUT_3_SOURCE        0x74C
+#define ARIZONA_AIF2TX2MIX_INPUT_3_VOLUME        0x74D
+#define ARIZONA_AIF2TX2MIX_INPUT_4_SOURCE        0x74E
+#define ARIZONA_AIF2TX2MIX_INPUT_4_VOLUME        0x74F
+#define ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE        0x780
+#define ARIZONA_AIF3TX1MIX_INPUT_1_VOLUME        0x781
+#define ARIZONA_AIF3TX1MIX_INPUT_2_SOURCE        0x782
+#define ARIZONA_AIF3TX1MIX_INPUT_2_VOLUME        0x783
+#define ARIZONA_AIF3TX1MIX_INPUT_3_SOURCE        0x784
+#define ARIZONA_AIF3TX1MIX_INPUT_3_VOLUME        0x785
+#define ARIZONA_AIF3TX1MIX_INPUT_4_SOURCE        0x786
+#define ARIZONA_AIF3TX1MIX_INPUT_4_VOLUME        0x787
+#define ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE        0x788
+#define ARIZONA_AIF3TX2MIX_INPUT_1_VOLUME        0x789
+#define ARIZONA_AIF3TX2MIX_INPUT_2_SOURCE        0x78A
+#define ARIZONA_AIF3TX2MIX_INPUT_2_VOLUME        0x78B
+#define ARIZONA_AIF3TX2MIX_INPUT_3_SOURCE        0x78C
+#define ARIZONA_AIF3TX2MIX_INPUT_3_VOLUME        0x78D
+#define ARIZONA_AIF3TX2MIX_INPUT_4_SOURCE        0x78E
+#define ARIZONA_AIF3TX2MIX_INPUT_4_VOLUME        0x78F
+#define ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE        0x7C0
+#define ARIZONA_SLIMTX1MIX_INPUT_1_VOLUME        0x7C1
+#define ARIZONA_SLIMTX1MIX_INPUT_2_SOURCE        0x7C2
+#define ARIZONA_SLIMTX1MIX_INPUT_2_VOLUME        0x7C3
+#define ARIZONA_SLIMTX1MIX_INPUT_3_SOURCE        0x7C4
+#define ARIZONA_SLIMTX1MIX_INPUT_3_VOLUME        0x7C5
+#define ARIZONA_SLIMTX1MIX_INPUT_4_SOURCE        0x7C6
+#define ARIZONA_SLIMTX1MIX_INPUT_4_VOLUME        0x7C7
+#define ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE        0x7C8
+#define ARIZONA_SLIMTX2MIX_INPUT_1_VOLUME        0x7C9
+#define ARIZONA_SLIMTX2MIX_INPUT_2_SOURCE        0x7CA
+#define ARIZONA_SLIMTX2MIX_INPUT_2_VOLUME        0x7CB
+#define ARIZONA_SLIMTX2MIX_INPUT_3_SOURCE        0x7CC
+#define ARIZONA_SLIMTX2MIX_INPUT_3_VOLUME        0x7CD
+#define ARIZONA_SLIMTX2MIX_INPUT_4_SOURCE        0x7CE
+#define ARIZONA_SLIMTX2MIX_INPUT_4_VOLUME        0x7CF
+#define ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE        0x7D0
+#define ARIZONA_SLIMTX3MIX_INPUT_1_VOLUME        0x7D1
+#define ARIZONA_SLIMTX3MIX_INPUT_2_SOURCE        0x7D2
+#define ARIZONA_SLIMTX3MIX_INPUT_2_VOLUME        0x7D3
+#define ARIZONA_SLIMTX3MIX_INPUT_3_SOURCE        0x7D4
+#define ARIZONA_SLIMTX3MIX_INPUT_3_VOLUME        0x7D5
+#define ARIZONA_SLIMTX3MIX_INPUT_4_SOURCE        0x7D6
+#define ARIZONA_SLIMTX3MIX_INPUT_4_VOLUME        0x7D7
+#define ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE        0x7D8
+#define ARIZONA_SLIMTX4MIX_INPUT_1_VOLUME        0x7D9
+#define ARIZONA_SLIMTX4MIX_INPUT_2_SOURCE        0x7DA
+#define ARIZONA_SLIMTX4MIX_INPUT_2_VOLUME        0x7DB
+#define ARIZONA_SLIMTX4MIX_INPUT_3_SOURCE        0x7DC
+#define ARIZONA_SLIMTX4MIX_INPUT_3_VOLUME        0x7DD
+#define ARIZONA_SLIMTX4MIX_INPUT_4_SOURCE        0x7DE
+#define ARIZONA_SLIMTX4MIX_INPUT_4_VOLUME        0x7DF
+#define ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE        0x7E0
+#define ARIZONA_SLIMTX5MIX_INPUT_1_VOLUME        0x7E1
+#define ARIZONA_SLIMTX5MIX_INPUT_2_SOURCE        0x7E2
+#define ARIZONA_SLIMTX5MIX_INPUT_2_VOLUME        0x7E3
+#define ARIZONA_SLIMTX5MIX_INPUT_3_SOURCE        0x7E4
+#define ARIZONA_SLIMTX5MIX_INPUT_3_VOLUME        0x7E5
+#define ARIZONA_SLIMTX5MIX_INPUT_4_SOURCE        0x7E6
+#define ARIZONA_SLIMTX5MIX_INPUT_4_VOLUME        0x7E7
+#define ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE        0x7E8
+#define ARIZONA_SLIMTX6MIX_INPUT_1_VOLUME        0x7E9
+#define ARIZONA_SLIMTX6MIX_INPUT_2_SOURCE        0x7EA
+#define ARIZONA_SLIMTX6MIX_INPUT_2_VOLUME        0x7EB
+#define ARIZONA_SLIMTX6MIX_INPUT_3_SOURCE        0x7EC
+#define ARIZONA_SLIMTX6MIX_INPUT_3_VOLUME        0x7ED
+#define ARIZONA_SLIMTX6MIX_INPUT_4_SOURCE        0x7EE
+#define ARIZONA_SLIMTX6MIX_INPUT_4_VOLUME        0x7EF
+#define ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE        0x7F0
+#define ARIZONA_SLIMTX7MIX_INPUT_1_VOLUME        0x7F1
+#define ARIZONA_SLIMTX7MIX_INPUT_2_SOURCE        0x7F2
+#define ARIZONA_SLIMTX7MIX_INPUT_2_VOLUME        0x7F3
+#define ARIZONA_SLIMTX7MIX_INPUT_3_SOURCE        0x7F4
+#define ARIZONA_SLIMTX7MIX_INPUT_3_VOLUME        0x7F5
+#define ARIZONA_SLIMTX7MIX_INPUT_4_SOURCE        0x7F6
+#define ARIZONA_SLIMTX7MIX_INPUT_4_VOLUME        0x7F7
+#define ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE        0x7F8
+#define ARIZONA_SLIMTX8MIX_INPUT_1_VOLUME        0x7F9
+#define ARIZONA_SLIMTX8MIX_INPUT_2_SOURCE        0x7FA
+#define ARIZONA_SLIMTX8MIX_INPUT_2_VOLUME        0x7FB
+#define ARIZONA_SLIMTX8MIX_INPUT_3_SOURCE        0x7FC
+#define ARIZONA_SLIMTX8MIX_INPUT_3_VOLUME        0x7FD
+#define ARIZONA_SLIMTX8MIX_INPUT_4_SOURCE        0x7FE
+#define ARIZONA_SLIMTX8MIX_INPUT_4_VOLUME        0x7FF
+#define ARIZONA_EQ1MIX_INPUT_1_SOURCE            0x880
+#define ARIZONA_EQ1MIX_INPUT_1_VOLUME            0x881
+#define ARIZONA_EQ1MIX_INPUT_2_SOURCE            0x882
+#define ARIZONA_EQ1MIX_INPUT_2_VOLUME            0x883
+#define ARIZONA_EQ1MIX_INPUT_3_SOURCE            0x884
+#define ARIZONA_EQ1MIX_INPUT_3_VOLUME            0x885
+#define ARIZONA_EQ1MIX_INPUT_4_SOURCE            0x886
+#define ARIZONA_EQ1MIX_INPUT_4_VOLUME            0x887
+#define ARIZONA_EQ2MIX_INPUT_1_SOURCE            0x888
+#define ARIZONA_EQ2MIX_INPUT_1_VOLUME            0x889
+#define ARIZONA_EQ2MIX_INPUT_2_SOURCE            0x88A
+#define ARIZONA_EQ2MIX_INPUT_2_VOLUME            0x88B
+#define ARIZONA_EQ2MIX_INPUT_3_SOURCE            0x88C
+#define ARIZONA_EQ2MIX_INPUT_3_VOLUME            0x88D
+#define ARIZONA_EQ2MIX_INPUT_4_SOURCE            0x88E
+#define ARIZONA_EQ2MIX_INPUT_4_VOLUME            0x88F
+#define ARIZONA_EQ3MIX_INPUT_1_SOURCE            0x890
+#define ARIZONA_EQ3MIX_INPUT_1_VOLUME            0x891
+#define ARIZONA_EQ3MIX_INPUT_2_SOURCE            0x892
+#define ARIZONA_EQ3MIX_INPUT_2_VOLUME            0x893
+#define ARIZONA_EQ3MIX_INPUT_3_SOURCE            0x894
+#define ARIZONA_EQ3MIX_INPUT_3_VOLUME            0x895
+#define ARIZONA_EQ3MIX_INPUT_4_SOURCE            0x896
+#define ARIZONA_EQ3MIX_INPUT_4_VOLUME            0x897
+#define ARIZONA_EQ4MIX_INPUT_1_SOURCE            0x898
+#define ARIZONA_EQ4MIX_INPUT_1_VOLUME            0x899
+#define ARIZONA_EQ4MIX_INPUT_2_SOURCE            0x89A
+#define ARIZONA_EQ4MIX_INPUT_2_VOLUME            0x89B
+#define ARIZONA_EQ4MIX_INPUT_3_SOURCE            0x89C
+#define ARIZONA_EQ4MIX_INPUT_3_VOLUME            0x89D
+#define ARIZONA_EQ4MIX_INPUT_4_SOURCE            0x89E
+#define ARIZONA_EQ4MIX_INPUT_4_VOLUME            0x89F
+#define ARIZONA_DRC1LMIX_INPUT_1_SOURCE          0x8C0
+#define ARIZONA_DRC1LMIX_INPUT_1_VOLUME          0x8C1
+#define ARIZONA_DRC1LMIX_INPUT_2_SOURCE          0x8C2
+#define ARIZONA_DRC1LMIX_INPUT_2_VOLUME          0x8C3
+#define ARIZONA_DRC1LMIX_INPUT_3_SOURCE          0x8C4
+#define ARIZONA_DRC1LMIX_INPUT_3_VOLUME          0x8C5
+#define ARIZONA_DRC1LMIX_INPUT_4_SOURCE          0x8C6
+#define ARIZONA_DRC1LMIX_INPUT_4_VOLUME          0x8C7
+#define ARIZONA_DRC1RMIX_INPUT_1_SOURCE          0x8C8
+#define ARIZONA_DRC1RMIX_INPUT_1_VOLUME          0x8C9
+#define ARIZONA_DRC1RMIX_INPUT_2_SOURCE          0x8CA
+#define ARIZONA_DRC1RMIX_INPUT_2_VOLUME          0x8CB
+#define ARIZONA_DRC1RMIX_INPUT_3_SOURCE          0x8CC
+#define ARIZONA_DRC1RMIX_INPUT_3_VOLUME          0x8CD
+#define ARIZONA_DRC1RMIX_INPUT_4_SOURCE          0x8CE
+#define ARIZONA_DRC1RMIX_INPUT_4_VOLUME          0x8CF
+#define ARIZONA_DRC2LMIX_INPUT_1_SOURCE          0x8D0
+#define ARIZONA_DRC2LMIX_INPUT_1_VOLUME          0x8D1
+#define ARIZONA_DRC2LMIX_INPUT_2_SOURCE          0x8D2
+#define ARIZONA_DRC2LMIX_INPUT_2_VOLUME          0x8D3
+#define ARIZONA_DRC2LMIX_INPUT_3_SOURCE          0x8D4
+#define ARIZONA_DRC2LMIX_INPUT_3_VOLUME          0x8D5
+#define ARIZONA_DRC2LMIX_INPUT_4_SOURCE          0x8D6
+#define ARIZONA_DRC2LMIX_INPUT_4_VOLUME          0x8D7
+#define ARIZONA_DRC2RMIX_INPUT_1_SOURCE          0x8D8
+#define ARIZONA_DRC2RMIX_INPUT_1_VOLUME          0x8D9
+#define ARIZONA_DRC2RMIX_INPUT_2_SOURCE          0x8DA
+#define ARIZONA_DRC2RMIX_INPUT_2_VOLUME          0x8DB
+#define ARIZONA_DRC2RMIX_INPUT_3_SOURCE          0x8DC
+#define ARIZONA_DRC2RMIX_INPUT_3_VOLUME          0x8DD
+#define ARIZONA_DRC2RMIX_INPUT_4_SOURCE          0x8DE
+#define ARIZONA_DRC2RMIX_INPUT_4_VOLUME          0x8DF
+#define ARIZONA_HPLP1MIX_INPUT_1_SOURCE          0x900
+#define ARIZONA_HPLP1MIX_INPUT_1_VOLUME          0x901
+#define ARIZONA_HPLP1MIX_INPUT_2_SOURCE          0x902
+#define ARIZONA_HPLP1MIX_INPUT_2_VOLUME          0x903
+#define ARIZONA_HPLP1MIX_INPUT_3_SOURCE          0x904
+#define ARIZONA_HPLP1MIX_INPUT_3_VOLUME          0x905
+#define ARIZONA_HPLP1MIX_INPUT_4_SOURCE          0x906
+#define ARIZONA_HPLP1MIX_INPUT_4_VOLUME          0x907
+#define ARIZONA_HPLP2MIX_INPUT_1_SOURCE          0x908
+#define ARIZONA_HPLP2MIX_INPUT_1_VOLUME          0x909
+#define ARIZONA_HPLP2MIX_INPUT_2_SOURCE          0x90A
+#define ARIZONA_HPLP2MIX_INPUT_2_VOLUME          0x90B
+#define ARIZONA_HPLP2MIX_INPUT_3_SOURCE          0x90C
+#define ARIZONA_HPLP2MIX_INPUT_3_VOLUME          0x90D
+#define ARIZONA_HPLP2MIX_INPUT_4_SOURCE          0x90E
+#define ARIZONA_HPLP2MIX_INPUT_4_VOLUME          0x90F
+#define ARIZONA_HPLP3MIX_INPUT_1_SOURCE          0x910
+#define ARIZONA_HPLP3MIX_INPUT_1_VOLUME          0x911
+#define ARIZONA_HPLP3MIX_INPUT_2_SOURCE          0x912
+#define ARIZONA_HPLP3MIX_INPUT_2_VOLUME          0x913
+#define ARIZONA_HPLP3MIX_INPUT_3_SOURCE          0x914
+#define ARIZONA_HPLP3MIX_INPUT_3_VOLUME          0x915
+#define ARIZONA_HPLP3MIX_INPUT_4_SOURCE          0x916
+#define ARIZONA_HPLP3MIX_INPUT_4_VOLUME          0x917
+#define ARIZONA_HPLP4MIX_INPUT_1_SOURCE          0x918
+#define ARIZONA_HPLP4MIX_INPUT_1_VOLUME          0x919
+#define ARIZONA_HPLP4MIX_INPUT_2_SOURCE          0x91A
+#define ARIZONA_HPLP4MIX_INPUT_2_VOLUME          0x91B
+#define ARIZONA_HPLP4MIX_INPUT_3_SOURCE          0x91C
+#define ARIZONA_HPLP4MIX_INPUT_3_VOLUME          0x91D
+#define ARIZONA_HPLP4MIX_INPUT_4_SOURCE          0x91E
+#define ARIZONA_HPLP4MIX_INPUT_4_VOLUME          0x91F
+#define ARIZONA_DSP1LMIX_INPUT_1_SOURCE          0x940
+#define ARIZONA_DSP1LMIX_INPUT_1_VOLUME          0x941
+#define ARIZONA_DSP1LMIX_INPUT_2_SOURCE          0x942
+#define ARIZONA_DSP1LMIX_INPUT_2_VOLUME          0x943
+#define ARIZONA_DSP1LMIX_INPUT_3_SOURCE          0x944
+#define ARIZONA_DSP1LMIX_INPUT_3_VOLUME          0x945
+#define ARIZONA_DSP1LMIX_INPUT_4_SOURCE          0x946
+#define ARIZONA_DSP1LMIX_INPUT_4_VOLUME          0x947
+#define ARIZONA_DSP1RMIX_INPUT_1_SOURCE          0x948
+#define ARIZONA_DSP1RMIX_INPUT_1_VOLUME          0x949
+#define ARIZONA_DSP1RMIX_INPUT_2_SOURCE          0x94A
+#define ARIZONA_DSP1RMIX_INPUT_2_VOLUME          0x94B
+#define ARIZONA_DSP1RMIX_INPUT_3_SOURCE          0x94C
+#define ARIZONA_DSP1RMIX_INPUT_3_VOLUME          0x94D
+#define ARIZONA_DSP1RMIX_INPUT_4_SOURCE          0x94E
+#define ARIZONA_DSP1RMIX_INPUT_4_VOLUME          0x94F
+#define ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE       0x950
+#define ARIZONA_DSP1AUX2MIX_INPUT_1_SOURCE       0x958
+#define ARIZONA_DSP1AUX3MIX_INPUT_1_SOURCE       0x960
+#define ARIZONA_DSP1AUX4MIX_INPUT_1_SOURCE       0x968
+#define ARIZONA_DSP1AUX5MIX_INPUT_1_SOURCE       0x970
+#define ARIZONA_DSP1AUX6MIX_INPUT_1_SOURCE       0x978
+#define ARIZONA_DSP2LMIX_INPUT_1_SOURCE          0x980
+#define ARIZONA_DSP2LMIX_INPUT_1_VOLUME          0x981
+#define ARIZONA_DSP2LMIX_INPUT_2_SOURCE          0x982
+#define ARIZONA_DSP2LMIX_INPUT_2_VOLUME          0x983
+#define ARIZONA_DSP2LMIX_INPUT_3_SOURCE          0x984
+#define ARIZONA_DSP2LMIX_INPUT_3_VOLUME          0x985
+#define ARIZONA_DSP2LMIX_INPUT_4_SOURCE          0x986
+#define ARIZONA_DSP2LMIX_INPUT_4_VOLUME          0x987
+#define ARIZONA_DSP2RMIX_INPUT_1_SOURCE          0x988
+#define ARIZONA_DSP2RMIX_INPUT_1_VOLUME          0x989
+#define ARIZONA_DSP2RMIX_INPUT_2_SOURCE          0x98A
+#define ARIZONA_DSP2RMIX_INPUT_2_VOLUME          0x98B
+#define ARIZONA_DSP2RMIX_INPUT_3_SOURCE          0x98C
+#define ARIZONA_DSP2RMIX_INPUT_3_VOLUME          0x98D
+#define ARIZONA_DSP2RMIX_INPUT_4_SOURCE          0x98E
+#define ARIZONA_DSP2RMIX_INPUT_4_VOLUME          0x98F
+#define ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE       0x990
+#define ARIZONA_DSP2AUX2MIX_INPUT_1_SOURCE       0x998
+#define ARIZONA_DSP2AUX3MIX_INPUT_1_SOURCE       0x9A0
+#define ARIZONA_DSP2AUX4MIX_INPUT_1_SOURCE       0x9A8
+#define ARIZONA_DSP2AUX5MIX_INPUT_1_SOURCE       0x9B0
+#define ARIZONA_DSP2AUX6MIX_INPUT_1_SOURCE       0x9B8
+#define ARIZONA_DSP3LMIX_INPUT_1_SOURCE          0x9C0
+#define ARIZONA_DSP3LMIX_INPUT_1_VOLUME          0x9C1
+#define ARIZONA_DSP3LMIX_INPUT_2_SOURCE          0x9C2
+#define ARIZONA_DSP3LMIX_INPUT_2_VOLUME          0x9C3
+#define ARIZONA_DSP3LMIX_INPUT_3_SOURCE          0x9C4
+#define ARIZONA_DSP3LMIX_INPUT_3_VOLUME          0x9C5
+#define ARIZONA_DSP3LMIX_INPUT_4_SOURCE          0x9C6
+#define ARIZONA_DSP3LMIX_INPUT_4_VOLUME          0x9C7
+#define ARIZONA_DSP3RMIX_INPUT_1_SOURCE          0x9C8
+#define ARIZONA_DSP3RMIX_INPUT_1_VOLUME          0x9C9
+#define ARIZONA_DSP3RMIX_INPUT_2_SOURCE          0x9CA
+#define ARIZONA_DSP3RMIX_INPUT_2_VOLUME          0x9CB
+#define ARIZONA_DSP3RMIX_INPUT_3_SOURCE          0x9CC
+#define ARIZONA_DSP3RMIX_INPUT_3_VOLUME          0x9CD
+#define ARIZONA_DSP3RMIX_INPUT_4_SOURCE          0x9CE
+#define ARIZONA_DSP3RMIX_INPUT_4_VOLUME          0x9CF
+#define ARIZONA_DSP3AUX1MIX_INPUT_1_SOURCE       0x9D0
+#define ARIZONA_DSP3AUX2MIX_INPUT_1_SOURCE       0x9D8
+#define ARIZONA_DSP3AUX3MIX_INPUT_1_SOURCE       0x9E0
+#define ARIZONA_DSP3AUX4MIX_INPUT_1_SOURCE       0x9E8
+#define ARIZONA_DSP3AUX5MIX_INPUT_1_SOURCE       0x9F0
+#define ARIZONA_DSP3AUX6MIX_INPUT_1_SOURCE       0x9F8
+#define ARIZONA_DSP4LMIX_INPUT_1_SOURCE          0xA00
+#define ARIZONA_DSP4LMIX_INPUT_1_VOLUME          0xA01
+#define ARIZONA_DSP4LMIX_INPUT_2_SOURCE          0xA02
+#define ARIZONA_DSP4LMIX_INPUT_2_VOLUME          0xA03
+#define ARIZONA_DSP4LMIX_INPUT_3_SOURCE          0xA04
+#define ARIZONA_DSP4LMIX_INPUT_3_VOLUME          0xA05
+#define ARIZONA_DSP4LMIX_INPUT_4_SOURCE          0xA06
+#define ARIZONA_DSP4LMIX_INPUT_4_VOLUME          0xA07
+#define ARIZONA_DSP4RMIX_INPUT_1_SOURCE          0xA08
+#define ARIZONA_DSP4RMIX_INPUT_1_VOLUME          0xA09
+#define ARIZONA_DSP4RMIX_INPUT_2_SOURCE          0xA0A
+#define ARIZONA_DSP4RMIX_INPUT_2_VOLUME          0xA0B
+#define ARIZONA_DSP4RMIX_INPUT_3_SOURCE          0xA0C
+#define ARIZONA_DSP4RMIX_INPUT_3_VOLUME          0xA0D
+#define ARIZONA_DSP4RMIX_INPUT_4_SOURCE          0xA0E
+#define ARIZONA_DSP4RMIX_INPUT_4_VOLUME          0xA0F
+#define ARIZONA_DSP4AUX1MIX_INPUT_1_SOURCE       0xA10
+#define ARIZONA_DSP4AUX2MIX_INPUT_1_SOURCE       0xA18
+#define ARIZONA_DSP4AUX3MIX_INPUT_1_SOURCE       0xA20
+#define ARIZONA_DSP4AUX4MIX_INPUT_1_SOURCE       0xA28
+#define ARIZONA_DSP4AUX5MIX_INPUT_1_SOURCE       0xA30
+#define ARIZONA_DSP4AUX6MIX_INPUT_1_SOURCE       0xA38
+#define ARIZONA_ASRC1LMIX_INPUT_1_SOURCE         0xA80
+#define ARIZONA_ASRC1RMIX_INPUT_1_SOURCE         0xA88
+#define ARIZONA_ASRC2LMIX_INPUT_1_SOURCE         0xA90
+#define ARIZONA_ASRC2RMIX_INPUT_1_SOURCE         0xA98
+#define ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE      0xB00
+#define ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE      0xB08
+#define ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE      0xB10
+#define ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE      0xB18
+#define ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE      0xB20
+#define ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE      0xB28
+#define ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE      0xB30
+#define ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE      0xB38
+#define ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE      0xB40
+#define ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE      0xB48
+#define ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE      0xB60
+#define ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE      0xB68
+#define ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE      0xB30
+#define ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE      0xB38
+#define ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE      0xB40
+#define ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE      0xB48
+#define ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE      0xB50
+#define ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE      0xB58
+#define ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE      0xB60
+#define ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE      0xB68
+#define ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE      0xB70
+#define ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE      0xB78
+#define ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE      0xB80
+#define ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE      0xB88
+#define ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE      0xB90
+#define ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE      0xB98
+#define ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE      0xBA0
+#define ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE      0xBA8
+#define ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE      0xBB0
+#define ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE      0xBB8
+#define ARIZONA_GPIO1_CTRL                       0xC00
+#define ARIZONA_GPIO2_CTRL                       0xC01
+#define ARIZONA_GPIO3_CTRL                       0xC02
+#define ARIZONA_GPIO4_CTRL                       0xC03
+#define ARIZONA_GPIO5_CTRL                       0xC04
+#define ARIZONA_IRQ_CTRL_1                       0xC0F
+#define ARIZONA_GPIO_DEBOUNCE_CONFIG             0xC10
+#define ARIZONA_MISC_PAD_CTRL_1                  0xC20
+#define ARIZONA_MISC_PAD_CTRL_2                  0xC21
+#define ARIZONA_MISC_PAD_CTRL_3                  0xC22
+#define ARIZONA_MISC_PAD_CTRL_4                  0xC23
+#define ARIZONA_MISC_PAD_CTRL_5                  0xC24
+#define ARIZONA_MISC_PAD_CTRL_6                  0xC25
+#define ARIZONA_MISC_PAD_CTRL_7                  0xC30
+#define ARIZONA_MISC_PAD_CTRL_8                  0xC31
+#define ARIZONA_MISC_PAD_CTRL_9                  0xC32
+#define ARIZONA_MISC_PAD_CTRL_10                 0xC33
+#define ARIZONA_MISC_PAD_CTRL_11                 0xC34
+#define ARIZONA_MISC_PAD_CTRL_12                 0xC35
+#define ARIZONA_MISC_PAD_CTRL_13                 0xC36
+#define ARIZONA_MISC_PAD_CTRL_14                 0xC37
+#define ARIZONA_MISC_PAD_CTRL_15                 0xC38
+#define ARIZONA_MISC_PAD_CTRL_16                 0xC39
+#define ARIZONA_MISC_PAD_CTRL_17                 0xC3A
+#define ARIZONA_MISC_PAD_CTRL_18                 0xC3B
+#define ARIZONA_INTERRUPT_STATUS_1               0xD00
+#define ARIZONA_INTERRUPT_STATUS_2               0xD01
+#define ARIZONA_INTERRUPT_STATUS_3               0xD02
+#define ARIZONA_INTERRUPT_STATUS_4               0xD03
+#define ARIZONA_INTERRUPT_STATUS_5               0xD04
+#define ARIZONA_INTERRUPT_STATUS_1_MASK          0xD08
+#define ARIZONA_INTERRUPT_STATUS_2_MASK          0xD09
+#define ARIZONA_INTERRUPT_STATUS_3_MASK          0xD0A
+#define ARIZONA_INTERRUPT_STATUS_4_MASK          0xD0B
+#define ARIZONA_INTERRUPT_STATUS_5_MASK          0xD0C
+#define ARIZONA_INTERRUPT_CONTROL                0xD0F
+#define ARIZONA_IRQ2_STATUS_1                    0xD10
+#define ARIZONA_IRQ2_STATUS_2                    0xD11
+#define ARIZONA_IRQ2_STATUS_3                    0xD12
+#define ARIZONA_IRQ2_STATUS_4                    0xD13
+#define ARIZONA_IRQ2_STATUS_5                    0xD14
+#define ARIZONA_IRQ2_STATUS_1_MASK               0xD18
+#define ARIZONA_IRQ2_STATUS_2_MASK               0xD19
+#define ARIZONA_IRQ2_STATUS_3_MASK               0xD1A
+#define ARIZONA_IRQ2_STATUS_4_MASK               0xD1B
+#define ARIZONA_IRQ2_STATUS_5_MASK               0xD1C
+#define ARIZONA_IRQ2_CONTROL                     0xD1F
+#define ARIZONA_INTERRUPT_RAW_STATUS_2           0xD20
+#define ARIZONA_INTERRUPT_RAW_STATUS_3           0xD21
+#define ARIZONA_INTERRUPT_RAW_STATUS_4           0xD22
+#define ARIZONA_INTERRUPT_RAW_STATUS_5           0xD23
+#define ARIZONA_INTERRUPT_RAW_STATUS_6           0xD24
+#define ARIZONA_INTERRUPT_RAW_STATUS_7           0xD25
+#define ARIZONA_INTERRUPT_RAW_STATUS_8           0xD26
+#define ARIZONA_IRQ_PIN_STATUS                   0xD40
+#define ARIZONA_ADSP2_IRQ0                       0xD41
+#define ARIZONA_AOD_WKUP_AND_TRIG                0xD50
+#define ARIZONA_AOD_IRQ1                         0xD51
+#define ARIZONA_AOD_IRQ2                         0xD52
+#define ARIZONA_AOD_IRQ_MASK_IRQ1                0xD53
+#define ARIZONA_AOD_IRQ_MASK_IRQ2                0xD54
+#define ARIZONA_AOD_IRQ_RAW_STATUS               0xD55
+#define ARIZONA_JACK_DETECT_DEBOUNCE             0xD56
+#define ARIZONA_FX_CTRL1                         0xE00
+#define ARIZONA_FX_CTRL2                         0xE01
+#define ARIZONA_EQ1_1                            0xE10
+#define ARIZONA_EQ1_2                            0xE11
+#define ARIZONA_EQ1_3                            0xE12
+#define ARIZONA_EQ1_4                            0xE13
+#define ARIZONA_EQ1_5                            0xE14
+#define ARIZONA_EQ1_6                            0xE15
+#define ARIZONA_EQ1_7                            0xE16
+#define ARIZONA_EQ1_8                            0xE17
+#define ARIZONA_EQ1_9                            0xE18
+#define ARIZONA_EQ1_10                           0xE19
+#define ARIZONA_EQ1_11                           0xE1A
+#define ARIZONA_EQ1_12                           0xE1B
+#define ARIZONA_EQ1_13                           0xE1C
+#define ARIZONA_EQ1_14                           0xE1D
+#define ARIZONA_EQ1_15                           0xE1E
+#define ARIZONA_EQ1_16                           0xE1F
+#define ARIZONA_EQ1_17                           0xE20
+#define ARIZONA_EQ1_18                           0xE21
+#define ARIZONA_EQ1_19                           0xE22
+#define ARIZONA_EQ1_20                           0xE23
+#define ARIZONA_EQ1_21                           0xE24
+#define ARIZONA_EQ2_1                            0xE26
+#define ARIZONA_EQ2_2                            0xE27
+#define ARIZONA_EQ2_3                            0xE28
+#define ARIZONA_EQ2_4                            0xE29
+#define ARIZONA_EQ2_5                            0xE2A
+#define ARIZONA_EQ2_6                            0xE2B
+#define ARIZONA_EQ2_7                            0xE2C
+#define ARIZONA_EQ2_8                            0xE2D
+#define ARIZONA_EQ2_9                            0xE2E
+#define ARIZONA_EQ2_10                           0xE2F
+#define ARIZONA_EQ2_11                           0xE30
+#define ARIZONA_EQ2_12                           0xE31
+#define ARIZONA_EQ2_13                           0xE32
+#define ARIZONA_EQ2_14                           0xE33
+#define ARIZONA_EQ2_15                           0xE34
+#define ARIZONA_EQ2_16                           0xE35
+#define ARIZONA_EQ2_17                           0xE36
+#define ARIZONA_EQ2_18                           0xE37
+#define ARIZONA_EQ2_19                           0xE38
+#define ARIZONA_EQ2_20                           0xE39
+#define ARIZONA_EQ2_21                           0xE3A
+#define ARIZONA_EQ3_1                            0xE3C
+#define ARIZONA_EQ3_2                            0xE3D
+#define ARIZONA_EQ3_3                            0xE3E
+#define ARIZONA_EQ3_4                            0xE3F
+#define ARIZONA_EQ3_5                            0xE40
+#define ARIZONA_EQ3_6                            0xE41
+#define ARIZONA_EQ3_7                            0xE42
+#define ARIZONA_EQ3_8                            0xE43
+#define ARIZONA_EQ3_9                            0xE44
+#define ARIZONA_EQ3_10                           0xE45
+#define ARIZONA_EQ3_11                           0xE46
+#define ARIZONA_EQ3_12                           0xE47
+#define ARIZONA_EQ3_13                           0xE48
+#define ARIZONA_EQ3_14                           0xE49
+#define ARIZONA_EQ3_15                           0xE4A
+#define ARIZONA_EQ3_16                           0xE4B
+#define ARIZONA_EQ3_17                           0xE4C
+#define ARIZONA_EQ3_18                           0xE4D
+#define ARIZONA_EQ3_19                           0xE4E
+#define ARIZONA_EQ3_20                           0xE4F
+#define ARIZONA_EQ3_21                           0xE50
+#define ARIZONA_EQ4_1                            0xE52
+#define ARIZONA_EQ4_2                            0xE53
+#define ARIZONA_EQ4_3                            0xE54
+#define ARIZONA_EQ4_4                            0xE55
+#define ARIZONA_EQ4_5                            0xE56
+#define ARIZONA_EQ4_6                            0xE57
+#define ARIZONA_EQ4_7                            0xE58
+#define ARIZONA_EQ4_8                            0xE59
+#define ARIZONA_EQ4_9                            0xE5A
+#define ARIZONA_EQ4_10                           0xE5B
+#define ARIZONA_EQ4_11                           0xE5C
+#define ARIZONA_EQ4_12                           0xE5D
+#define ARIZONA_EQ4_13                           0xE5E
+#define ARIZONA_EQ4_14                           0xE5F
+#define ARIZONA_EQ4_15                           0xE60
+#define ARIZONA_EQ4_16                           0xE61
+#define ARIZONA_EQ4_17                           0xE62
+#define ARIZONA_EQ4_18                           0xE63
+#define ARIZONA_EQ4_19                           0xE64
+#define ARIZONA_EQ4_20                           0xE65
+#define ARIZONA_EQ4_21                           0xE66
+#define ARIZONA_DRC1_CTRL1                       0xE80
+#define ARIZONA_DRC1_CTRL2                       0xE81
+#define ARIZONA_DRC1_CTRL3                       0xE82
+#define ARIZONA_DRC1_CTRL4                       0xE83
+#define ARIZONA_DRC1_CTRL5                       0xE84
+#define ARIZONA_DRC2_CTRL1                       0xE89
+#define ARIZONA_DRC2_CTRL2                       0xE8A
+#define ARIZONA_DRC2_CTRL3                       0xE8B
+#define ARIZONA_DRC2_CTRL4                       0xE8C
+#define ARIZONA_DRC2_CTRL5                       0xE8D
+#define ARIZONA_HPLPF1_1                         0xEC0
+#define ARIZONA_HPLPF1_2                         0xEC1
+#define ARIZONA_HPLPF2_1                         0xEC4
+#define ARIZONA_HPLPF2_2                         0xEC5
+#define ARIZONA_HPLPF3_1                         0xEC8
+#define ARIZONA_HPLPF3_2                         0xEC9
+#define ARIZONA_HPLPF4_1                         0xECC
+#define ARIZONA_HPLPF4_2                         0xECD
+#define ARIZONA_ASRC_ENABLE                      0xEE0
+#define ARIZONA_ASRC_STATUS                      0xEE1
+#define ARIZONA_ASRC_RATE1                       0xEE2
+#define ARIZONA_ASRC_RATE2                       0xEE3
+#define ARIZONA_ISRC_1_CTRL_1                    0xEF0
+#define ARIZONA_ISRC_1_CTRL_2                    0xEF1
+#define ARIZONA_ISRC_1_CTRL_3                    0xEF2
+#define ARIZONA_ISRC_2_CTRL_1                    0xEF3
+#define ARIZONA_ISRC_2_CTRL_2                    0xEF4
+#define ARIZONA_ISRC_2_CTRL_3                    0xEF5
+#define ARIZONA_ISRC_3_CTRL_1                    0xEF6
+#define ARIZONA_ISRC_3_CTRL_2                    0xEF7
+#define ARIZONA_ISRC_3_CTRL_3                    0xEF8
+#define ARIZONA_CLOCK_CONTROL                    0xF00
+#define ARIZONA_ANC_SRC                          0xF01
+#define ARIZONA_DSP_STATUS                       0xF02
+#define ARIZONA_DSP1_CONTROL_1                   0x1100
+#define ARIZONA_DSP1_CLOCKING_1                  0x1101
+#define ARIZONA_DSP1_STATUS_1                    0x1104
+#define ARIZONA_DSP1_STATUS_2                    0x1105
+#define ARIZONA_DSP2_CONTROL_1                   0x1200
+#define ARIZONA_DSP2_CLOCKING_1                  0x1201
+#define ARIZONA_DSP2_STATUS_1                    0x1204
+#define ARIZONA_DSP2_STATUS_2                    0x1205
+#define ARIZONA_DSP3_CONTROL_1                   0x1300
+#define ARIZONA_DSP3_CLOCKING_1                  0x1301
+#define ARIZONA_DSP3_STATUS_1                    0x1304
+#define ARIZONA_DSP3_STATUS_2                    0x1305
+#define ARIZONA_DSP4_CONTROL_1                   0x1400
+#define ARIZONA_DSP4_CLOCKING_1                  0x1401
+#define ARIZONA_DSP4_STATUS_1                    0x1404
+#define ARIZONA_DSP4_STATUS_2                    0x1405
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - software reset
+ */
+#define ARIZONA_SW_RST_DEV_ID1_MASK              0xFFFF  /* SW_RST_DEV_ID1 - [15:0] */
+#define ARIZONA_SW_RST_DEV_ID1_SHIFT                  0  /* SW_RST_DEV_ID1 - [15:0] */
+#define ARIZONA_SW_RST_DEV_ID1_WIDTH                 16  /* SW_RST_DEV_ID1 - [15:0] */
+
+/*
+ * R1 (0x01) - Device Revision
+ */
+#define ARIZONA_DEVICE_REVISION_MASK             0x00FF  /* DEVICE_REVISION - [7:0] */
+#define ARIZONA_DEVICE_REVISION_SHIFT                 0  /* DEVICE_REVISION - [7:0] */
+#define ARIZONA_DEVICE_REVISION_WIDTH                 8  /* DEVICE_REVISION - [7:0] */
+
+/*
+ * R8 (0x08) - Ctrl IF SPI CFG 1
+ */
+#define ARIZONA_SPI_CFG                          0x0010  /* SPI_CFG */
+#define ARIZONA_SPI_CFG_MASK                     0x0010  /* SPI_CFG */
+#define ARIZONA_SPI_CFG_SHIFT                         4  /* SPI_CFG */
+#define ARIZONA_SPI_CFG_WIDTH                         1  /* SPI_CFG */
+#define ARIZONA_SPI_4WIRE                        0x0008  /* SPI_4WIRE */
+#define ARIZONA_SPI_4WIRE_MASK                   0x0008  /* SPI_4WIRE */
+#define ARIZONA_SPI_4WIRE_SHIFT                       3  /* SPI_4WIRE */
+#define ARIZONA_SPI_4WIRE_WIDTH                       1  /* SPI_4WIRE */
+#define ARIZONA_SPI_AUTO_INC_MASK                0x0003  /* SPI_AUTO_INC - [1:0] */
+#define ARIZONA_SPI_AUTO_INC_SHIFT                    0  /* SPI_AUTO_INC - [1:0] */
+#define ARIZONA_SPI_AUTO_INC_WIDTH                    2  /* SPI_AUTO_INC - [1:0] */
+
+/*
+ * R9 (0x09) - Ctrl IF I2C1 CFG 1
+ */
+#define ARIZONA_I2C1_AUTO_INC_MASK               0x0003  /* I2C1_AUTO_INC - [1:0] */
+#define ARIZONA_I2C1_AUTO_INC_SHIFT                   0  /* I2C1_AUTO_INC - [1:0] */
+#define ARIZONA_I2C1_AUTO_INC_WIDTH                   2  /* I2C1_AUTO_INC - [1:0] */
+
+/*
+ * R13 (0x0D) - Ctrl IF Status 1
+ */
+#define ARIZONA_I2C1_BUSY                        0x0020  /* I2C1_BUSY */
+#define ARIZONA_I2C1_BUSY_MASK                   0x0020  /* I2C1_BUSY */
+#define ARIZONA_I2C1_BUSY_SHIFT                       5  /* I2C1_BUSY */
+#define ARIZONA_I2C1_BUSY_WIDTH                       1  /* I2C1_BUSY */
+#define ARIZONA_SPI_BUSY                         0x0010  /* SPI_BUSY */
+#define ARIZONA_SPI_BUSY_MASK                    0x0010  /* SPI_BUSY */
+#define ARIZONA_SPI_BUSY_SHIFT                        4  /* SPI_BUSY */
+#define ARIZONA_SPI_BUSY_WIDTH                        1  /* SPI_BUSY */
+
+/*
+ * R22 (0x16) - Write Sequencer Ctrl 0
+ */
+#define ARIZONA_WSEQ_ABORT                       0x0800  /* WSEQ_ABORT */
+#define ARIZONA_WSEQ_ABORT_MASK                  0x0800  /* WSEQ_ABORT */
+#define ARIZONA_WSEQ_ABORT_SHIFT                     11  /* WSEQ_ABORT */
+#define ARIZONA_WSEQ_ABORT_WIDTH                      1  /* WSEQ_ABORT */
+#define ARIZONA_WSEQ_START                       0x0400  /* WSEQ_START */
+#define ARIZONA_WSEQ_START_MASK                  0x0400  /* WSEQ_START */
+#define ARIZONA_WSEQ_START_SHIFT                     10  /* WSEQ_START */
+#define ARIZONA_WSEQ_START_WIDTH                      1  /* WSEQ_START */
+#define ARIZONA_WSEQ_ENA                         0x0200  /* WSEQ_ENA */
+#define ARIZONA_WSEQ_ENA_MASK                    0x0200  /* WSEQ_ENA */
+#define ARIZONA_WSEQ_ENA_SHIFT                        9  /* WSEQ_ENA */
+#define ARIZONA_WSEQ_ENA_WIDTH                        1  /* WSEQ_ENA */
+#define ARIZONA_WSEQ_START_INDEX_MASK            0x01FF  /* WSEQ_START_INDEX - [8:0] */
+#define ARIZONA_WSEQ_START_INDEX_SHIFT                0  /* WSEQ_START_INDEX - [8:0] */
+#define ARIZONA_WSEQ_START_INDEX_WIDTH                9  /* WSEQ_START_INDEX - [8:0] */
+
+/*
+ * R23 (0x17) - Write Sequencer Ctrl 1
+ */
+#define ARIZONA_WSEQ_BUSY                        0x0200  /* WSEQ_BUSY */
+#define ARIZONA_WSEQ_BUSY_MASK                   0x0200  /* WSEQ_BUSY */
+#define ARIZONA_WSEQ_BUSY_SHIFT                       9  /* WSEQ_BUSY */
+#define ARIZONA_WSEQ_BUSY_WIDTH                       1  /* WSEQ_BUSY */
+#define ARIZONA_WSEQ_CURRENT_INDEX_MASK          0x01FF  /* WSEQ_CURRENT_INDEX - [8:0] */
+#define ARIZONA_WSEQ_CURRENT_INDEX_SHIFT              0  /* WSEQ_CURRENT_INDEX - [8:0] */
+#define ARIZONA_WSEQ_CURRENT_INDEX_WIDTH              9  /* WSEQ_CURRENT_INDEX - [8:0] */
+
+/*
+ * R24 (0x18) - Write Sequencer Ctrl 2
+ */
+#define ARIZONA_LOAD_DEFAULTS                    0x0002  /* LOAD_DEFAULTS */
+#define ARIZONA_LOAD_DEFAULTS_MASK               0x0002  /* LOAD_DEFAULTS */
+#define ARIZONA_LOAD_DEFAULTS_SHIFT                   1  /* LOAD_DEFAULTS */
+#define ARIZONA_LOAD_DEFAULTS_WIDTH                   1  /* LOAD_DEFAULTS */
+#define ARIZONA_WSEQ_LOAD_MEM                    0x0001  /* WSEQ_LOAD_MEM */
+#define ARIZONA_WSEQ_LOAD_MEM_MASK               0x0001  /* WSEQ_LOAD_MEM */
+#define ARIZONA_WSEQ_LOAD_MEM_SHIFT                   0  /* WSEQ_LOAD_MEM */
+#define ARIZONA_WSEQ_LOAD_MEM_WIDTH                   1  /* WSEQ_LOAD_MEM */
+
+/*
+ * R26 (0x1A) - Write Sequencer PROM
+ */
+#define ARIZONA_WSEQ_OTP_WRITE                   0x0001  /* WSEQ_OTP_WRITE */
+#define ARIZONA_WSEQ_OTP_WRITE_MASK              0x0001  /* WSEQ_OTP_WRITE */
+#define ARIZONA_WSEQ_OTP_WRITE_SHIFT                  0  /* WSEQ_OTP_WRITE */
+#define ARIZONA_WSEQ_OTP_WRITE_WIDTH                  1  /* WSEQ_OTP_WRITE */
+
+/*
+ * R32 (0x20) - Tone Generator 1
+ */
+#define ARIZONA_TONE_RATE_MASK                   0x7800  /* TONE_RATE - [14:11] */
+#define ARIZONA_TONE_RATE_SHIFT                      11  /* TONE_RATE - [14:11] */
+#define ARIZONA_TONE_RATE_WIDTH                       4  /* TONE_RATE - [14:11] */
+#define ARIZONA_TONE_OFFSET_MASK                 0x0300  /* TONE_OFFSET - [9:8] */
+#define ARIZONA_TONE_OFFSET_SHIFT                     8  /* TONE_OFFSET - [9:8] */
+#define ARIZONA_TONE_OFFSET_WIDTH                     2  /* TONE_OFFSET - [9:8] */
+#define ARIZONA_TONE2_OVD                        0x0020  /* TONE2_OVD */
+#define ARIZONA_TONE2_OVD_MASK                   0x0020  /* TONE2_OVD */
+#define ARIZONA_TONE2_OVD_SHIFT                       5  /* TONE2_OVD */
+#define ARIZONA_TONE2_OVD_WIDTH                       1  /* TONE2_OVD */
+#define ARIZONA_TONE1_OVD                        0x0010  /* TONE1_OVD */
+#define ARIZONA_TONE1_OVD_MASK                   0x0010  /* TONE1_OVD */
+#define ARIZONA_TONE1_OVD_SHIFT                       4  /* TONE1_OVD */
+#define ARIZONA_TONE1_OVD_WIDTH                       1  /* TONE1_OVD */
+#define ARIZONA_TONE2_ENA                        0x0002  /* TONE2_ENA */
+#define ARIZONA_TONE2_ENA_MASK                   0x0002  /* TONE2_ENA */
+#define ARIZONA_TONE2_ENA_SHIFT                       1  /* TONE2_ENA */
+#define ARIZONA_TONE2_ENA_WIDTH                       1  /* TONE2_ENA */
+#define ARIZONA_TONE1_ENA                        0x0001  /* TONE1_ENA */
+#define ARIZONA_TONE1_ENA_MASK                   0x0001  /* TONE1_ENA */
+#define ARIZONA_TONE1_ENA_SHIFT                       0  /* TONE1_ENA */
+#define ARIZONA_TONE1_ENA_WIDTH                       1  /* TONE1_ENA */
+
+/*
+ * R33 (0x21) - Tone Generator 2
+ */
+#define ARIZONA_TONE1_LVL_0_MASK                 0xFFFF  /* TONE1_LVL - [15:0] */
+#define ARIZONA_TONE1_LVL_0_SHIFT                     0  /* TONE1_LVL - [15:0] */
+#define ARIZONA_TONE1_LVL_0_WIDTH                    16  /* TONE1_LVL - [15:0] */
+
+/*
+ * R34 (0x22) - Tone Generator 3
+ */
+#define ARIZONA_TONE1_LVL_MASK                   0x00FF  /* TONE1_LVL - [7:0] */
+#define ARIZONA_TONE1_LVL_SHIFT                       0  /* TONE1_LVL - [7:0] */
+#define ARIZONA_TONE1_LVL_WIDTH                       8  /* TONE1_LVL - [7:0] */
+
+/*
+ * R35 (0x23) - Tone Generator 4
+ */
+#define ARIZONA_TONE2_LVL_0_MASK                 0xFFFF  /* TONE2_LVL - [15:0] */
+#define ARIZONA_TONE2_LVL_0_SHIFT                     0  /* TONE2_LVL - [15:0] */
+#define ARIZONA_TONE2_LVL_0_WIDTH                    16  /* TONE2_LVL - [15:0] */
+
+/*
+ * R36 (0x24) - Tone Generator 5
+ */
+#define ARIZONA_TONE2_LVL_MASK                   0x00FF  /* TONE2_LVL - [7:0] */
+#define ARIZONA_TONE2_LVL_SHIFT                       0  /* TONE2_LVL - [7:0] */
+#define ARIZONA_TONE2_LVL_WIDTH                       8  /* TONE2_LVL - [7:0] */
+
+/*
+ * R48 (0x30) - PWM Drive 1
+ */
+#define ARIZONA_PWM_RATE_MASK                    0x7800  /* PWM_RATE - [14:11] */
+#define ARIZONA_PWM_RATE_SHIFT                       11  /* PWM_RATE - [14:11] */
+#define ARIZONA_PWM_RATE_WIDTH                        4  /* PWM_RATE - [14:11] */
+#define ARIZONA_PWM_CLK_SEL_MASK                 0x0700  /* PWM_CLK_SEL - [10:8] */
+#define ARIZONA_PWM_CLK_SEL_SHIFT                     8  /* PWM_CLK_SEL - [10:8] */
+#define ARIZONA_PWM_CLK_SEL_WIDTH                     3  /* PWM_CLK_SEL - [10:8] */
+#define ARIZONA_PWM2_OVD                         0x0020  /* PWM2_OVD */
+#define ARIZONA_PWM2_OVD_MASK                    0x0020  /* PWM2_OVD */
+#define ARIZONA_PWM2_OVD_SHIFT                        5  /* PWM2_OVD */
+#define ARIZONA_PWM2_OVD_WIDTH                        1  /* PWM2_OVD */
+#define ARIZONA_PWM1_OVD                         0x0010  /* PWM1_OVD */
+#define ARIZONA_PWM1_OVD_MASK                    0x0010  /* PWM1_OVD */
+#define ARIZONA_PWM1_OVD_SHIFT                        4  /* PWM1_OVD */
+#define ARIZONA_PWM1_OVD_WIDTH                        1  /* PWM1_OVD */
+#define ARIZONA_PWM2_ENA                         0x0002  /* PWM2_ENA */
+#define ARIZONA_PWM2_ENA_MASK                    0x0002  /* PWM2_ENA */
+#define ARIZONA_PWM2_ENA_SHIFT                        1  /* PWM2_ENA */
+#define ARIZONA_PWM2_ENA_WIDTH                        1  /* PWM2_ENA */
+#define ARIZONA_PWM1_ENA                         0x0001  /* PWM1_ENA */
+#define ARIZONA_PWM1_ENA_MASK                    0x0001  /* PWM1_ENA */
+#define ARIZONA_PWM1_ENA_SHIFT                        0  /* PWM1_ENA */
+#define ARIZONA_PWM1_ENA_WIDTH                        1  /* PWM1_ENA */
+
+/*
+ * R49 (0x31) - PWM Drive 2
+ */
+#define ARIZONA_PWM1_LVL_MASK                    0x03FF  /* PWM1_LVL - [9:0] */
+#define ARIZONA_PWM1_LVL_SHIFT                        0  /* PWM1_LVL - [9:0] */
+#define ARIZONA_PWM1_LVL_WIDTH                       10  /* PWM1_LVL - [9:0] */
+
+/*
+ * R50 (0x32) - PWM Drive 3
+ */
+#define ARIZONA_PWM2_LVL_MASK                    0x03FF  /* PWM2_LVL - [9:0] */
+#define ARIZONA_PWM2_LVL_SHIFT                        0  /* PWM2_LVL - [9:0] */
+#define ARIZONA_PWM2_LVL_WIDTH                       10  /* PWM2_LVL - [9:0] */
+
+/*
+ * R64 (0x40) - Wake control
+ */
+#define ARIZONA_WKUP_GP5_FALL                    0x0020  /* WKUP_GP5_FALL */
+#define ARIZONA_WKUP_GP5_FALL_MASK               0x0020  /* WKUP_GP5_FALL */
+#define ARIZONA_WKUP_GP5_FALL_SHIFT                   5  /* WKUP_GP5_FALL */
+#define ARIZONA_WKUP_GP5_FALL_WIDTH                   1  /* WKUP_GP5_FALL */
+#define ARIZONA_WKUP_GP5_RISE                    0x0010  /* WKUP_GP5_RISE */
+#define ARIZONA_WKUP_GP5_RISE_MASK               0x0010  /* WKUP_GP5_RISE */
+#define ARIZONA_WKUP_GP5_RISE_SHIFT                   4  /* WKUP_GP5_RISE */
+#define ARIZONA_WKUP_GP5_RISE_WIDTH                   1  /* WKUP_GP5_RISE */
+#define ARIZONA_WKUP_JD1_FALL                    0x0008  /* WKUP_JD1_FALL */
+#define ARIZONA_WKUP_JD1_FALL_MASK               0x0008  /* WKUP_JD1_FALL */
+#define ARIZONA_WKUP_JD1_FALL_SHIFT                   3  /* WKUP_JD1_FALL */
+#define ARIZONA_WKUP_JD1_FALL_WIDTH                   1  /* WKUP_JD1_FALL */
+#define ARIZONA_WKUP_JD1_RISE                    0x0004  /* WKUP_JD1_RISE */
+#define ARIZONA_WKUP_JD1_RISE_MASK               0x0004  /* WKUP_JD1_RISE */
+#define ARIZONA_WKUP_JD1_RISE_SHIFT                   2  /* WKUP_JD1_RISE */
+#define ARIZONA_WKUP_JD1_RISE_WIDTH                   1  /* WKUP_JD1_RISE */
+#define ARIZONA_WKUP_JD2_FALL                    0x0002  /* WKUP_JD2_FALL */
+#define ARIZONA_WKUP_JD2_FALL_MASK               0x0002  /* WKUP_JD2_FALL */
+#define ARIZONA_WKUP_JD2_FALL_SHIFT                   1  /* WKUP_JD2_FALL */
+#define ARIZONA_WKUP_JD2_FALL_WIDTH                   1  /* WKUP_JD2_FALL */
+#define ARIZONA_WKUP_JD2_RISE                    0x0001  /* WKUP_JD2_RISE */
+#define ARIZONA_WKUP_JD2_RISE_MASK               0x0001  /* WKUP_JD2_RISE */
+#define ARIZONA_WKUP_JD2_RISE_SHIFT                   0  /* WKUP_JD2_RISE */
+#define ARIZONA_WKUP_JD2_RISE_WIDTH                   1  /* WKUP_JD2_RISE */
+
+/*
+ * R65 (0x41) - Sequence control
+ */
+#define ARIZONA_WSEQ_ENA_GP5_FALL                0x0020  /* WSEQ_ENA_GP5_FALL */
+#define ARIZONA_WSEQ_ENA_GP5_FALL_MASK           0x0020  /* WSEQ_ENA_GP5_FALL */
+#define ARIZONA_WSEQ_ENA_GP5_FALL_SHIFT               5  /* WSEQ_ENA_GP5_FALL */
+#define ARIZONA_WSEQ_ENA_GP5_FALL_WIDTH               1  /* WSEQ_ENA_GP5_FALL */
+#define ARIZONA_WSEQ_ENA_GP5_RISE                0x0010  /* WSEQ_ENA_GP5_RISE */
+#define ARIZONA_WSEQ_ENA_GP5_RISE_MASK           0x0010  /* WSEQ_ENA_GP5_RISE */
+#define ARIZONA_WSEQ_ENA_GP5_RISE_SHIFT               4  /* WSEQ_ENA_GP5_RISE */
+#define ARIZONA_WSEQ_ENA_GP5_RISE_WIDTH               1  /* WSEQ_ENA_GP5_RISE */
+#define ARIZONA_WSEQ_ENA_JD1_FALL                0x0008  /* WSEQ_ENA_JD1_FALL */
+#define ARIZONA_WSEQ_ENA_JD1_FALL_MASK           0x0008  /* WSEQ_ENA_JD1_FALL */
+#define ARIZONA_WSEQ_ENA_JD1_FALL_SHIFT               3  /* WSEQ_ENA_JD1_FALL */
+#define ARIZONA_WSEQ_ENA_JD1_FALL_WIDTH               1  /* WSEQ_ENA_JD1_FALL */
+#define ARIZONA_WSEQ_ENA_JD1_RISE                0x0004  /* WSEQ_ENA_JD1_RISE */
+#define ARIZONA_WSEQ_ENA_JD1_RISE_MASK           0x0004  /* WSEQ_ENA_JD1_RISE */
+#define ARIZONA_WSEQ_ENA_JD1_RISE_SHIFT               2  /* WSEQ_ENA_JD1_RISE */
+#define ARIZONA_WSEQ_ENA_JD1_RISE_WIDTH               1  /* WSEQ_ENA_JD1_RISE */
+#define ARIZONA_WSEQ_ENA_JD2_FALL                0x0002  /* WSEQ_ENA_JD2_FALL */
+#define ARIZONA_WSEQ_ENA_JD2_FALL_MASK           0x0002  /* WSEQ_ENA_JD2_FALL */
+#define ARIZONA_WSEQ_ENA_JD2_FALL_SHIFT               1  /* WSEQ_ENA_JD2_FALL */
+#define ARIZONA_WSEQ_ENA_JD2_FALL_WIDTH               1  /* WSEQ_ENA_JD2_FALL */
+#define ARIZONA_WSEQ_ENA_JD2_RISE                0x0001  /* WSEQ_ENA_JD2_RISE */
+#define ARIZONA_WSEQ_ENA_JD2_RISE_MASK           0x0001  /* WSEQ_ENA_JD2_RISE */
+#define ARIZONA_WSEQ_ENA_JD2_RISE_SHIFT               0  /* WSEQ_ENA_JD2_RISE */
+#define ARIZONA_WSEQ_ENA_JD2_RISE_WIDTH               1  /* WSEQ_ENA_JD2_RISE */
+
+/*
+ * R97 (0x61) - Sample Rate Sequence Select 1
+ */
+#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR_MASK 0x01FF  /* WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR_SHIFT      0  /* WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR_WIDTH      9  /* WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR - [8:0] */
+
+/*
+ * R98 (0x62) - Sample Rate Sequence Select 2
+ */
+#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR_MASK 0x01FF  /* WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR_SHIFT      0  /* WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR_WIDTH      9  /* WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR - [8:0] */
+
+/*
+ * R99 (0x63) - Sample Rate Sequence Select 3
+ */
+#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR_MASK 0x01FF  /* WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR_SHIFT      0  /* WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR_WIDTH      9  /* WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR - [8:0] */
+
+/*
+ * R100 (0x64) - Sample Rate Sequence Select 4
+ */
+#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR_MASK 0x01FF  /* WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR_SHIFT      0  /* WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR_WIDTH      9  /* WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR - [8:0] */
+
+/*
+ * R104 (0x68) - Always On Triggers Sequence Select 1
+ */
+#define ARIZONA_WSEQ_GP5_RISE_SEQ_ADDR_MASK      0x01FF  /* WSEQ_GP5_RISE_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_GP5_RISE_SEQ_ADDR_SHIFT          0  /* WSEQ_GP5_RISE_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_GP5_RISE_SEQ_ADDR_WIDTH          9  /* WSEQ_GP5_RISE_SEQ_ADDR - [8:0] */
+
+/*
+ * R105 (0x69) - Always On Triggers Sequence Select 2
+ */
+#define ARIZONA_WSEQ_GP5_FALL_SEQ_ADDR_MASK      0x01FF  /* WSEQ_GP5_FALL_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_GP5_FALL_SEQ_ADDR_SHIFT          0  /* WSEQ_GP5_FALL_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_GP5_FALL_SEQ_ADDR_WIDTH          9  /* WSEQ_GP5_FALL_SEQ_ADDR - [8:0] */
+
+/*
+ * R106 (0x6A) - Always On Triggers Sequence Select 3
+ */
+#define ARIZONA_WSEQ_JD1_RISE_SEQ_ADDR_MASK      0x01FF  /* WSEQ_JD1_RISE_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_JD1_RISE_SEQ_ADDR_SHIFT          0  /* WSEQ_JD1_RISE_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_JD1_RISE_SEQ_ADDR_WIDTH          9  /* WSEQ_JD1_RISE_SEQ_ADDR - [8:0] */
+
+/*
+ * R107 (0x6B) - Always On Triggers Sequence Select 4
+ */
+#define ARIZONA_WSEQ_JD1_FALL_SEQ_ADDR_MASK      0x01FF  /* WSEQ_JD1_FALL_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_JD1_FALL_SEQ_ADDR_SHIFT          0  /* WSEQ_JD1_FALL_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_JD1_FALL_SEQ_ADDR_WIDTH          9  /* WSEQ_JD1_FALL_SEQ_ADDR - [8:0] */
+
+/*
+ * R108 (0x6C) - Always On Triggers Sequence Select 5
+ */
+#define ARIZONA_WSEQ_JD2_RISE_SEQ_ADDR_MASK      0x01FF  /* WSEQ_JD2_RISE_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_JD2_RISE_SEQ_ADDR_SHIFT          0  /* WSEQ_JD2_RISE_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_JD2_RISE_SEQ_ADDR_WIDTH          9  /* WSEQ_JD2_RISE_SEQ_ADDR - [8:0] */
+
+/*
+ * R109 (0x6D) - Always On Triggers Sequence Select 6
+ */
+#define ARIZONA_WSEQ_JD2_FALL_SEQ_ADDR_MASK      0x01FF  /* WSEQ_JD2_FALL_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_JD2_FALL_SEQ_ADDR_SHIFT          0  /* WSEQ_JD2_FALL_SEQ_ADDR - [8:0] */
+#define ARIZONA_WSEQ_JD2_FALL_SEQ_ADDR_WIDTH          9  /* WSEQ_JD2_FALL_SEQ_ADDR - [8:0] */
+
+/*
+ * R112 (0x70) - Comfort Noise Generator
+ */
+#define ARIZONA_NOISE_GEN_RATE_MASK              0x7800  /* NOISE_GEN_RATE - [14:11] */
+#define ARIZONA_NOISE_GEN_RATE_SHIFT                 11  /* NOISE_GEN_RATE - [14:11] */
+#define ARIZONA_NOISE_GEN_RATE_WIDTH                  4  /* NOISE_GEN_RATE - [14:11] */
+#define ARIZONA_NOISE_GEN_ENA                    0x0020  /* NOISE_GEN_ENA */
+#define ARIZONA_NOISE_GEN_ENA_MASK               0x0020  /* NOISE_GEN_ENA */
+#define ARIZONA_NOISE_GEN_ENA_SHIFT                   5  /* NOISE_GEN_ENA */
+#define ARIZONA_NOISE_GEN_ENA_WIDTH                   1  /* NOISE_GEN_ENA */
+#define ARIZONA_NOISE_GEN_GAIN_MASK              0x001F  /* NOISE_GEN_GAIN - [4:0] */
+#define ARIZONA_NOISE_GEN_GAIN_SHIFT                  0  /* NOISE_GEN_GAIN - [4:0] */
+#define ARIZONA_NOISE_GEN_GAIN_WIDTH                  5  /* NOISE_GEN_GAIN - [4:0] */
+
+/*
+ * R144 (0x90) - Haptics Control 1
+ */
+#define ARIZONA_HAP_RATE_MASK                    0x7800  /* HAP_RATE - [14:11] */
+#define ARIZONA_HAP_RATE_SHIFT                       11  /* HAP_RATE - [14:11] */
+#define ARIZONA_HAP_RATE_WIDTH                        4  /* HAP_RATE - [14:11] */
+#define ARIZONA_ONESHOT_TRIG                     0x0010  /* ONESHOT_TRIG */
+#define ARIZONA_ONESHOT_TRIG_MASK                0x0010  /* ONESHOT_TRIG */
+#define ARIZONA_ONESHOT_TRIG_SHIFT                    4  /* ONESHOT_TRIG */
+#define ARIZONA_ONESHOT_TRIG_WIDTH                    1  /* ONESHOT_TRIG */
+#define ARIZONA_HAP_CTRL_MASK                    0x000C  /* HAP_CTRL - [3:2] */
+#define ARIZONA_HAP_CTRL_SHIFT                        2  /* HAP_CTRL - [3:2] */
+#define ARIZONA_HAP_CTRL_WIDTH                        2  /* HAP_CTRL - [3:2] */
+#define ARIZONA_HAP_ACT                          0x0002  /* HAP_ACT */
+#define ARIZONA_HAP_ACT_MASK                     0x0002  /* HAP_ACT */
+#define ARIZONA_HAP_ACT_SHIFT                         1  /* HAP_ACT */
+#define ARIZONA_HAP_ACT_WIDTH                         1  /* HAP_ACT */
+
+/*
+ * R145 (0x91) - Haptics Control 2
+ */
+#define ARIZONA_LRA_FREQ_MASK                    0x7FFF  /* LRA_FREQ - [14:0] */
+#define ARIZONA_LRA_FREQ_SHIFT                        0  /* LRA_FREQ - [14:0] */
+#define ARIZONA_LRA_FREQ_WIDTH                       15  /* LRA_FREQ - [14:0] */
+
+/*
+ * R146 (0x92) - Haptics phase 1 intensity
+ */
+#define ARIZONA_PHASE1_INTENSITY_MASK            0x00FF  /* PHASE1_INTENSITY - [7:0] */
+#define ARIZONA_PHASE1_INTENSITY_SHIFT                0  /* PHASE1_INTENSITY - [7:0] */
+#define ARIZONA_PHASE1_INTENSITY_WIDTH                8  /* PHASE1_INTENSITY - [7:0] */
+
+/*
+ * R147 (0x93) - Haptics phase 1 duration
+ */
+#define ARIZONA_PHASE1_DURATION_MASK             0x01FF  /* PHASE1_DURATION - [8:0] */
+#define ARIZONA_PHASE1_DURATION_SHIFT                 0  /* PHASE1_DURATION - [8:0] */
+#define ARIZONA_PHASE1_DURATION_WIDTH                 9  /* PHASE1_DURATION - [8:0] */
+
+/*
+ * R148 (0x94) - Haptics phase 2 intensity
+ */
+#define ARIZONA_PHASE2_INTENSITY_MASK            0x00FF  /* PHASE2_INTENSITY - [7:0] */
+#define ARIZONA_PHASE2_INTENSITY_SHIFT                0  /* PHASE2_INTENSITY - [7:0] */
+#define ARIZONA_PHASE2_INTENSITY_WIDTH                8  /* PHASE2_INTENSITY - [7:0] */
+
+/*
+ * R149 (0x95) - Haptics phase 2 duration
+ */
+#define ARIZONA_PHASE2_DURATION_MASK             0x07FF  /* PHASE2_DURATION - [10:0] */
+#define ARIZONA_PHASE2_DURATION_SHIFT                 0  /* PHASE2_DURATION - [10:0] */
+#define ARIZONA_PHASE2_DURATION_WIDTH                11  /* PHASE2_DURATION - [10:0] */
+
+/*
+ * R150 (0x96) - Haptics phase 3 intensity
+ */
+#define ARIZONA_PHASE3_INTENSITY_MASK            0x00FF  /* PHASE3_INTENSITY - [7:0] */
+#define ARIZONA_PHASE3_INTENSITY_SHIFT                0  /* PHASE3_INTENSITY - [7:0] */
+#define ARIZONA_PHASE3_INTENSITY_WIDTH                8  /* PHASE3_INTENSITY - [7:0] */
+
+/*
+ * R151 (0x97) - Haptics phase 3 duration
+ */
+#define ARIZONA_PHASE3_DURATION_MASK             0x01FF  /* PHASE3_DURATION - [8:0] */
+#define ARIZONA_PHASE3_DURATION_SHIFT                 0  /* PHASE3_DURATION - [8:0] */
+#define ARIZONA_PHASE3_DURATION_WIDTH                 9  /* PHASE3_DURATION - [8:0] */
+
+/*
+ * R152 (0x98) - Haptics Status
+ */
+#define ARIZONA_ONESHOT_STS                      0x0001  /* ONESHOT_STS */
+#define ARIZONA_ONESHOT_STS_MASK                 0x0001  /* ONESHOT_STS */
+#define ARIZONA_ONESHOT_STS_SHIFT                     0  /* ONESHOT_STS */
+#define ARIZONA_ONESHOT_STS_WIDTH                     1  /* ONESHOT_STS */
+
+/*
+ * R256 (0x100) - Clock 32k 1
+ */
+#define ARIZONA_CLK_32K_ENA                      0x0040  /* CLK_32K_ENA */
+#define ARIZONA_CLK_32K_ENA_MASK                 0x0040  /* CLK_32K_ENA */
+#define ARIZONA_CLK_32K_ENA_SHIFT                     6  /* CLK_32K_ENA */
+#define ARIZONA_CLK_32K_ENA_WIDTH                     1  /* CLK_32K_ENA */
+#define ARIZONA_CLK_32K_SRC_MASK                 0x0003  /* CLK_32K_SRC - [1:0] */
+#define ARIZONA_CLK_32K_SRC_SHIFT                     0  /* CLK_32K_SRC - [1:0] */
+#define ARIZONA_CLK_32K_SRC_WIDTH                     2  /* CLK_32K_SRC - [1:0] */
+
+/*
+ * R257 (0x101) - System Clock 1
+ */
+#define ARIZONA_SYSCLK_FRAC                      0x8000  /* SYSCLK_FRAC */
+#define ARIZONA_SYSCLK_FRAC_MASK                 0x8000  /* SYSCLK_FRAC */
+#define ARIZONA_SYSCLK_FRAC_SHIFT                    15  /* SYSCLK_FRAC */
+#define ARIZONA_SYSCLK_FRAC_WIDTH                     1  /* SYSCLK_FRAC */
+#define ARIZONA_SYSCLK_FREQ_MASK                 0x0700  /* SYSCLK_FREQ - [10:8] */
+#define ARIZONA_SYSCLK_FREQ_SHIFT                     8  /* SYSCLK_FREQ - [10:8] */
+#define ARIZONA_SYSCLK_FREQ_WIDTH                     3  /* SYSCLK_FREQ - [10:8] */
+#define ARIZONA_SYSCLK_ENA                       0x0040  /* SYSCLK_ENA */
+#define ARIZONA_SYSCLK_ENA_MASK                  0x0040  /* SYSCLK_ENA */
+#define ARIZONA_SYSCLK_ENA_SHIFT                      6  /* SYSCLK_ENA */
+#define ARIZONA_SYSCLK_ENA_WIDTH                      1  /* SYSCLK_ENA */
+#define ARIZONA_SYSCLK_SRC_MASK                  0x000F  /* SYSCLK_SRC - [3:0] */
+#define ARIZONA_SYSCLK_SRC_SHIFT                      0  /* SYSCLK_SRC - [3:0] */
+#define ARIZONA_SYSCLK_SRC_WIDTH                      4  /* SYSCLK_SRC - [3:0] */
+
+/*
+ * R258 (0x102) - Sample rate 1
+ */
+#define ARIZONA_SAMPLE_RATE_1_MASK               0x001F  /* SAMPLE_RATE_1 - [4:0] */
+#define ARIZONA_SAMPLE_RATE_1_SHIFT                   0  /* SAMPLE_RATE_1 - [4:0] */
+#define ARIZONA_SAMPLE_RATE_1_WIDTH                   5  /* SAMPLE_RATE_1 - [4:0] */
+
+/*
+ * R259 (0x103) - Sample rate 2
+ */
+#define ARIZONA_SAMPLE_RATE_2_MASK               0x001F  /* SAMPLE_RATE_2 - [4:0] */
+#define ARIZONA_SAMPLE_RATE_2_SHIFT                   0  /* SAMPLE_RATE_2 - [4:0] */
+#define ARIZONA_SAMPLE_RATE_2_WIDTH                   5  /* SAMPLE_RATE_2 - [4:0] */
+
+/*
+ * R260 (0x104) - Sample rate 3
+ */
+#define ARIZONA_SAMPLE_RATE_3_MASK               0x001F  /* SAMPLE_RATE_3 - [4:0] */
+#define ARIZONA_SAMPLE_RATE_3_SHIFT                   0  /* SAMPLE_RATE_3 - [4:0] */
+#define ARIZONA_SAMPLE_RATE_3_WIDTH                   5  /* SAMPLE_RATE_3 - [4:0] */
+
+/*
+ * R266 (0x10A) - Sample rate 1 status
+ */
+#define ARIZONA_SAMPLE_RATE_1_STS_MASK           0x001F  /* SAMPLE_RATE_1_STS - [4:0] */
+#define ARIZONA_SAMPLE_RATE_1_STS_SHIFT               0  /* SAMPLE_RATE_1_STS - [4:0] */
+#define ARIZONA_SAMPLE_RATE_1_STS_WIDTH               5  /* SAMPLE_RATE_1_STS - [4:0] */
+
+/*
+ * R267 (0x10B) - Sample rate 2 status
+ */
+#define ARIZONA_SAMPLE_RATE_2_STS_MASK           0x001F  /* SAMPLE_RATE_2_STS - [4:0] */
+#define ARIZONA_SAMPLE_RATE_2_STS_SHIFT               0  /* SAMPLE_RATE_2_STS - [4:0] */
+#define ARIZONA_SAMPLE_RATE_2_STS_WIDTH               5  /* SAMPLE_RATE_2_STS - [4:0] */
+
+/*
+ * R268 (0x10C) - Sample rate 3 status
+ */
+#define ARIZONA_SAMPLE_RATE_3_STS_MASK           0x001F  /* SAMPLE_RATE_3_STS - [4:0] */
+#define ARIZONA_SAMPLE_RATE_3_STS_SHIFT               0  /* SAMPLE_RATE_3_STS - [4:0] */
+#define ARIZONA_SAMPLE_RATE_3_STS_WIDTH               5  /* SAMPLE_RATE_3_STS - [4:0] */
+
+/*
+ * R274 (0x112) - Async clock 1
+ */
+#define ARIZONA_ASYNC_CLK_FREQ_MASK              0x0700  /* ASYNC_CLK_FREQ - [10:8] */
+#define ARIZONA_ASYNC_CLK_FREQ_SHIFT                  8  /* ASYNC_CLK_FREQ - [10:8] */
+#define ARIZONA_ASYNC_CLK_FREQ_WIDTH                  3  /* ASYNC_CLK_FREQ - [10:8] */
+#define ARIZONA_ASYNC_CLK_ENA                    0x0040  /* ASYNC_CLK_ENA */
+#define ARIZONA_ASYNC_CLK_ENA_MASK               0x0040  /* ASYNC_CLK_ENA */
+#define ARIZONA_ASYNC_CLK_ENA_SHIFT                   6  /* ASYNC_CLK_ENA */
+#define ARIZONA_ASYNC_CLK_ENA_WIDTH                   1  /* ASYNC_CLK_ENA */
+#define ARIZONA_ASYNC_CLK_SRC_MASK               0x000F  /* ASYNC_CLK_SRC - [3:0] */
+#define ARIZONA_ASYNC_CLK_SRC_SHIFT                   0  /* ASYNC_CLK_SRC - [3:0] */
+#define ARIZONA_ASYNC_CLK_SRC_WIDTH                   4  /* ASYNC_CLK_SRC - [3:0] */
+
+/*
+ * R275 (0x113) - Async sample rate 1
+ */
+#define ARIZONA_ASYNC_SAMPLE_RATE_MASK           0x001F  /* ASYNC_SAMPLE_RATE - [4:0] */
+#define ARIZONA_ASYNC_SAMPLE_RATE_SHIFT               0  /* ASYNC_SAMPLE_RATE - [4:0] */
+#define ARIZONA_ASYNC_SAMPLE_RATE_WIDTH               5  /* ASYNC_SAMPLE_RATE - [4:0] */
+
+/*
+ * R283 (0x11B) - Async sample rate 1 status
+ */
+#define ARIZONA_ASYNC_SAMPLE_RATE_STS_MASK       0x001F  /* ASYNC_SAMPLE_RATE_STS - [4:0] */
+#define ARIZONA_ASYNC_SAMPLE_RATE_STS_SHIFT           0  /* ASYNC_SAMPLE_RATE_STS - [4:0] */
+#define ARIZONA_ASYNC_SAMPLE_RATE_STS_WIDTH           5  /* ASYNC_SAMPLE_RATE_STS - [4:0] */
+
+/*
+ * R329 (0x149) - Output system clock
+ */
+#define ARIZONA_OPCLK_ENA                        0x8000  /* OPCLK_ENA */
+#define ARIZONA_OPCLK_ENA_MASK                   0x8000  /* OPCLK_ENA */
+#define ARIZONA_OPCLK_ENA_SHIFT                      15  /* OPCLK_ENA */
+#define ARIZONA_OPCLK_ENA_WIDTH                       1  /* OPCLK_ENA */
+#define ARIZONA_OPCLK_DIV_MASK                   0x00F8  /* OPCLK_DIV - [7:3] */
+#define ARIZONA_OPCLK_DIV_SHIFT                       3  /* OPCLK_DIV - [7:3] */
+#define ARIZONA_OPCLK_DIV_WIDTH                       5  /* OPCLK_DIV - [7:3] */
+#define ARIZONA_OPCLK_SEL_MASK                   0x0007  /* OPCLK_SEL - [2:0] */
+#define ARIZONA_OPCLK_SEL_SHIFT                       0  /* OPCLK_SEL - [2:0] */
+#define ARIZONA_OPCLK_SEL_WIDTH                       3  /* OPCLK_SEL - [2:0] */
+
+/*
+ * R330 (0x14A) - Output async clock
+ */
+#define ARIZONA_OPCLK_ASYNC_ENA                  0x8000  /* OPCLK_ASYNC_ENA */
+#define ARIZONA_OPCLK_ASYNC_ENA_MASK             0x8000  /* OPCLK_ASYNC_ENA */
+#define ARIZONA_OPCLK_ASYNC_ENA_SHIFT                15  /* OPCLK_ASYNC_ENA */
+#define ARIZONA_OPCLK_ASYNC_ENA_WIDTH                 1  /* OPCLK_ASYNC_ENA */
+#define ARIZONA_OPCLK_ASYNC_DIV_MASK             0x00F8  /* OPCLK_ASYNC_DIV - [7:3] */
+#define ARIZONA_OPCLK_ASYNC_DIV_SHIFT                 3  /* OPCLK_ASYNC_DIV - [7:3] */
+#define ARIZONA_OPCLK_ASYNC_DIV_WIDTH                 5  /* OPCLK_ASYNC_DIV - [7:3] */
+#define ARIZONA_OPCLK_ASYNC_SEL_MASK             0x0007  /* OPCLK_ASYNC_SEL - [2:0] */
+#define ARIZONA_OPCLK_ASYNC_SEL_SHIFT                 0  /* OPCLK_ASYNC_SEL - [2:0] */
+#define ARIZONA_OPCLK_ASYNC_SEL_WIDTH                 3  /* OPCLK_ASYNC_SEL - [2:0] */
+
+/*
+ * R338 (0x152) - Rate Estimator 1
+ */
+#define ARIZONA_TRIG_ON_STARTUP                  0x0010  /* TRIG_ON_STARTUP */
+#define ARIZONA_TRIG_ON_STARTUP_MASK             0x0010  /* TRIG_ON_STARTUP */
+#define ARIZONA_TRIG_ON_STARTUP_SHIFT                 4  /* TRIG_ON_STARTUP */
+#define ARIZONA_TRIG_ON_STARTUP_WIDTH                 1  /* TRIG_ON_STARTUP */
+#define ARIZONA_LRCLK_SRC_MASK                   0x000E  /* LRCLK_SRC - [3:1] */
+#define ARIZONA_LRCLK_SRC_SHIFT                       1  /* LRCLK_SRC - [3:1] */
+#define ARIZONA_LRCLK_SRC_WIDTH                       3  /* LRCLK_SRC - [3:1] */
+#define ARIZONA_RATE_EST_ENA                     0x0001  /* RATE_EST_ENA */
+#define ARIZONA_RATE_EST_ENA_MASK                0x0001  /* RATE_EST_ENA */
+#define ARIZONA_RATE_EST_ENA_SHIFT                    0  /* RATE_EST_ENA */
+#define ARIZONA_RATE_EST_ENA_WIDTH                    1  /* RATE_EST_ENA */
+
+/*
+ * R339 (0x153) - Rate Estimator 2
+ */
+#define ARIZONA_SAMPLE_RATE_DETECT_A_MASK        0x001F  /* SAMPLE_RATE_DETECT_A - [4:0] */
+#define ARIZONA_SAMPLE_RATE_DETECT_A_SHIFT            0  /* SAMPLE_RATE_DETECT_A - [4:0] */
+#define ARIZONA_SAMPLE_RATE_DETECT_A_WIDTH            5  /* SAMPLE_RATE_DETECT_A - [4:0] */
+
+/*
+ * R340 (0x154) - Rate Estimator 3
+ */
+#define ARIZONA_SAMPLE_RATE_DETECT_B_MASK        0x001F  /* SAMPLE_RATE_DETECT_B - [4:0] */
+#define ARIZONA_SAMPLE_RATE_DETECT_B_SHIFT            0  /* SAMPLE_RATE_DETECT_B - [4:0] */
+#define ARIZONA_SAMPLE_RATE_DETECT_B_WIDTH            5  /* SAMPLE_RATE_DETECT_B - [4:0] */
+
+/*
+ * R341 (0x155) - Rate Estimator 4
+ */
+#define ARIZONA_SAMPLE_RATE_DETECT_C_MASK        0x001F  /* SAMPLE_RATE_DETECT_C - [4:0] */
+#define ARIZONA_SAMPLE_RATE_DETECT_C_SHIFT            0  /* SAMPLE_RATE_DETECT_C - [4:0] */
+#define ARIZONA_SAMPLE_RATE_DETECT_C_WIDTH            5  /* SAMPLE_RATE_DETECT_C - [4:0] */
+
+/*
+ * R342 (0x156) - Rate Estimator 5
+ */
+#define ARIZONA_SAMPLE_RATE_DETECT_D_MASK        0x001F  /* SAMPLE_RATE_DETECT_D - [4:0] */
+#define ARIZONA_SAMPLE_RATE_DETECT_D_SHIFT            0  /* SAMPLE_RATE_DETECT_D - [4:0] */
+#define ARIZONA_SAMPLE_RATE_DETECT_D_WIDTH            5  /* SAMPLE_RATE_DETECT_D - [4:0] */
+
+/*
+ * R369 (0x171) - FLL1 Control 1
+ */
+#define ARIZONA_FLL1_FREERUN                     0x0002  /* FLL1_FREERUN */
+#define ARIZONA_FLL1_FREERUN_MASK                0x0002  /* FLL1_FREERUN */
+#define ARIZONA_FLL1_FREERUN_SHIFT                    1  /* FLL1_FREERUN */
+#define ARIZONA_FLL1_FREERUN_WIDTH                    1  /* FLL1_FREERUN */
+#define ARIZONA_FLL1_ENA                         0x0001  /* FLL1_ENA */
+#define ARIZONA_FLL1_ENA_MASK                    0x0001  /* FLL1_ENA */
+#define ARIZONA_FLL1_ENA_SHIFT                        0  /* FLL1_ENA */
+#define ARIZONA_FLL1_ENA_WIDTH                        1  /* FLL1_ENA */
+
+/*
+ * R370 (0x172) - FLL1 Control 2
+ */
+#define ARIZONA_FLL1_CTRL_UPD                    0x8000  /* FLL1_CTRL_UPD */
+#define ARIZONA_FLL1_CTRL_UPD_MASK               0x8000  /* FLL1_CTRL_UPD */
+#define ARIZONA_FLL1_CTRL_UPD_SHIFT                  15  /* FLL1_CTRL_UPD */
+#define ARIZONA_FLL1_CTRL_UPD_WIDTH                   1  /* FLL1_CTRL_UPD */
+#define ARIZONA_FLL1_N_MASK                      0x03FF  /* FLL1_N - [9:0] */
+#define ARIZONA_FLL1_N_SHIFT                          0  /* FLL1_N - [9:0] */
+#define ARIZONA_FLL1_N_WIDTH                         10  /* FLL1_N - [9:0] */
+
+/*
+ * R371 (0x173) - FLL1 Control 3
+ */
+#define ARIZONA_FLL1_THETA_MASK                  0xFFFF  /* FLL1_THETA - [15:0] */
+#define ARIZONA_FLL1_THETA_SHIFT                      0  /* FLL1_THETA - [15:0] */
+#define ARIZONA_FLL1_THETA_WIDTH                     16  /* FLL1_THETA - [15:0] */
+
+/*
+ * R372 (0x174) - FLL1 Control 4
+ */
+#define ARIZONA_FLL1_LAMBDA_MASK                 0xFFFF  /* FLL1_LAMBDA - [15:0] */
+#define ARIZONA_FLL1_LAMBDA_SHIFT                     0  /* FLL1_LAMBDA - [15:0] */
+#define ARIZONA_FLL1_LAMBDA_WIDTH                    16  /* FLL1_LAMBDA - [15:0] */
+
+/*
+ * R373 (0x175) - FLL1 Control 5
+ */
+#define ARIZONA_FLL1_FRATIO_MASK                 0x0700  /* FLL1_FRATIO - [10:8] */
+#define ARIZONA_FLL1_FRATIO_SHIFT                     8  /* FLL1_FRATIO - [10:8] */
+#define ARIZONA_FLL1_FRATIO_WIDTH                     3  /* FLL1_FRATIO - [10:8] */
+#define ARIZONA_FLL1_OUTDIV_MASK                 0x000E  /* FLL1_OUTDIV - [3:1] */
+#define ARIZONA_FLL1_OUTDIV_SHIFT                     1  /* FLL1_OUTDIV - [3:1] */
+#define ARIZONA_FLL1_OUTDIV_WIDTH                     3  /* FLL1_OUTDIV - [3:1] */
+
+/*
+ * R374 (0x176) - FLL1 Control 6
+ */
+#define ARIZONA_FLL1_CLK_REF_DIV_MASK            0x00C0  /* FLL1_CLK_REF_DIV - [7:6] */
+#define ARIZONA_FLL1_CLK_REF_DIV_SHIFT                6  /* FLL1_CLK_REF_DIV - [7:6] */
+#define ARIZONA_FLL1_CLK_REF_DIV_WIDTH                2  /* FLL1_CLK_REF_DIV - [7:6] */
+#define ARIZONA_FLL1_CLK_REF_SRC_MASK            0x000F  /* FLL1_CLK_REF_SRC - [3:0] */
+#define ARIZONA_FLL1_CLK_REF_SRC_SHIFT                0  /* FLL1_CLK_REF_SRC - [3:0] */
+#define ARIZONA_FLL1_CLK_REF_SRC_WIDTH                4  /* FLL1_CLK_REF_SRC - [3:0] */
+
+/*
+ * R375 (0x177) - FLL1 Loop Filter Test 1
+ */
+#define ARIZONA_FLL1_FRC_INTEG_UPD               0x8000  /* FLL1_FRC_INTEG_UPD */
+#define ARIZONA_FLL1_FRC_INTEG_UPD_MASK          0x8000  /* FLL1_FRC_INTEG_UPD */
+#define ARIZONA_FLL1_FRC_INTEG_UPD_SHIFT             15  /* FLL1_FRC_INTEG_UPD */
+#define ARIZONA_FLL1_FRC_INTEG_UPD_WIDTH              1  /* FLL1_FRC_INTEG_UPD */
+#define ARIZONA_FLL1_FRC_INTEG_VAL_MASK          0x0FFF  /* FLL1_FRC_INTEG_VAL - [11:0] */
+#define ARIZONA_FLL1_FRC_INTEG_VAL_SHIFT              0  /* FLL1_FRC_INTEG_VAL - [11:0] */
+#define ARIZONA_FLL1_FRC_INTEG_VAL_WIDTH             12  /* FLL1_FRC_INTEG_VAL - [11:0] */
+
+/*
+ * R385 (0x181) - FLL1 Synchroniser 1
+ */
+#define ARIZONA_FLL1_SYNC_ENA                    0x0001  /* FLL1_SYNC_ENA */
+#define ARIZONA_FLL1_SYNC_ENA_MASK               0x0001  /* FLL1_SYNC_ENA */
+#define ARIZONA_FLL1_SYNC_ENA_SHIFT                   0  /* FLL1_SYNC_ENA */
+#define ARIZONA_FLL1_SYNC_ENA_WIDTH                   1  /* FLL1_SYNC_ENA */
+
+/*
+ * R386 (0x182) - FLL1 Synchroniser 2
+ */
+#define ARIZONA_FLL1_SYNC_N_MASK                 0x03FF  /* FLL1_SYNC_N - [9:0] */
+#define ARIZONA_FLL1_SYNC_N_SHIFT                     0  /* FLL1_SYNC_N - [9:0] */
+#define ARIZONA_FLL1_SYNC_N_WIDTH                    10  /* FLL1_SYNC_N - [9:0] */
+
+/*
+ * R387 (0x183) - FLL1 Synchroniser 3
+ */
+#define ARIZONA_FLL1_SYNC_THETA_MASK             0xFFFF  /* FLL1_SYNC_THETA - [15:0] */
+#define ARIZONA_FLL1_SYNC_THETA_SHIFT                 0  /* FLL1_SYNC_THETA - [15:0] */
+#define ARIZONA_FLL1_SYNC_THETA_WIDTH                16  /* FLL1_SYNC_THETA - [15:0] */
+
+/*
+ * R388 (0x184) - FLL1 Synchroniser 4
+ */
+#define ARIZONA_FLL1_SYNC_LAMBDA_MASK            0xFFFF  /* FLL1_SYNC_LAMBDA - [15:0] */
+#define ARIZONA_FLL1_SYNC_LAMBDA_SHIFT                0  /* FLL1_SYNC_LAMBDA - [15:0] */
+#define ARIZONA_FLL1_SYNC_LAMBDA_WIDTH               16  /* FLL1_SYNC_LAMBDA - [15:0] */
+
+/*
+ * R389 (0x185) - FLL1 Synchroniser 5
+ */
+#define ARIZONA_FLL1_SYNC_FRATIO_MASK            0x0700  /* FLL1_SYNC_FRATIO - [10:8] */
+#define ARIZONA_FLL1_SYNC_FRATIO_SHIFT                8  /* FLL1_SYNC_FRATIO - [10:8] */
+#define ARIZONA_FLL1_SYNC_FRATIO_WIDTH                3  /* FLL1_SYNC_FRATIO - [10:8] */
+
+/*
+ * R390 (0x186) - FLL1 Synchroniser 6
+ */
+#define ARIZONA_FLL1_CLK_SYNC_DIV_MASK           0x00C0  /* FLL1_CLK_SYNC_DIV - [7:6] */
+#define ARIZONA_FLL1_CLK_SYNC_DIV_SHIFT               6  /* FLL1_CLK_SYNC_DIV - [7:6] */
+#define ARIZONA_FLL1_CLK_SYNC_DIV_WIDTH               2  /* FLL1_CLK_SYNC_DIV - [7:6] */
+#define ARIZONA_FLL1_CLK_SYNC_SRC_MASK           0x000F  /* FLL1_CLK_SYNC_SRC - [3:0] */
+#define ARIZONA_FLL1_CLK_SYNC_SRC_SHIFT               0  /* FLL1_CLK_SYNC_SRC - [3:0] */
+#define ARIZONA_FLL1_CLK_SYNC_SRC_WIDTH               4  /* FLL1_CLK_SYNC_SRC - [3:0] */
+
+/*
+ * R393 (0x189) - FLL1 Spread Spectrum
+ */
+#define ARIZONA_FLL1_SS_AMPL_MASK                0x0030  /* FLL1_SS_AMPL - [5:4] */
+#define ARIZONA_FLL1_SS_AMPL_SHIFT                    4  /* FLL1_SS_AMPL - [5:4] */
+#define ARIZONA_FLL1_SS_AMPL_WIDTH                    2  /* FLL1_SS_AMPL - [5:4] */
+#define ARIZONA_FLL1_SS_FREQ_MASK                0x000C  /* FLL1_SS_FREQ - [3:2] */
+#define ARIZONA_FLL1_SS_FREQ_SHIFT                    2  /* FLL1_SS_FREQ - [3:2] */
+#define ARIZONA_FLL1_SS_FREQ_WIDTH                    2  /* FLL1_SS_FREQ - [3:2] */
+#define ARIZONA_FLL1_SS_SEL_MASK                 0x0003  /* FLL1_SS_SEL - [1:0] */
+#define ARIZONA_FLL1_SS_SEL_SHIFT                     0  /* FLL1_SS_SEL - [1:0] */
+#define ARIZONA_FLL1_SS_SEL_WIDTH                     2  /* FLL1_SS_SEL - [1:0] */
+
+/*
+ * R394 (0x18A) - FLL1 GPIO Clock
+ */
+#define ARIZONA_FLL1_GPDIV_MASK                  0x00FE  /* FLL1_GPDIV - [7:1] */
+#define ARIZONA_FLL1_GPDIV_SHIFT                      1  /* FLL1_GPDIV - [7:1] */
+#define ARIZONA_FLL1_GPDIV_WIDTH                      7  /* FLL1_GPDIV - [7:1] */
+#define ARIZONA_FLL1_GPDIV_ENA                   0x0001  /* FLL1_GPDIV_ENA */
+#define ARIZONA_FLL1_GPDIV_ENA_MASK              0x0001  /* FLL1_GPDIV_ENA */
+#define ARIZONA_FLL1_GPDIV_ENA_SHIFT                  0  /* FLL1_GPDIV_ENA */
+#define ARIZONA_FLL1_GPDIV_ENA_WIDTH                  1  /* FLL1_GPDIV_ENA */
+
+/*
+ * R401 (0x191) - FLL2 Control 1
+ */
+#define ARIZONA_FLL2_FREERUN                     0x0002  /* FLL2_FREERUN */
+#define ARIZONA_FLL2_FREERUN_MASK                0x0002  /* FLL2_FREERUN */
+#define ARIZONA_FLL2_FREERUN_SHIFT                    1  /* FLL2_FREERUN */
+#define ARIZONA_FLL2_FREERUN_WIDTH                    1  /* FLL2_FREERUN */
+#define ARIZONA_FLL2_ENA                         0x0001  /* FLL2_ENA */
+#define ARIZONA_FLL2_ENA_MASK                    0x0001  /* FLL2_ENA */
+#define ARIZONA_FLL2_ENA_SHIFT                        0  /* FLL2_ENA */
+#define ARIZONA_FLL2_ENA_WIDTH                        1  /* FLL2_ENA */
+
+/*
+ * R402 (0x192) - FLL2 Control 2
+ */
+#define ARIZONA_FLL2_CTRL_UPD                    0x8000  /* FLL2_CTRL_UPD */
+#define ARIZONA_FLL2_CTRL_UPD_MASK               0x8000  /* FLL2_CTRL_UPD */
+#define ARIZONA_FLL2_CTRL_UPD_SHIFT                  15  /* FLL2_CTRL_UPD */
+#define ARIZONA_FLL2_CTRL_UPD_WIDTH                   1  /* FLL2_CTRL_UPD */
+#define ARIZONA_FLL2_N_MASK                      0x03FF  /* FLL2_N - [9:0] */
+#define ARIZONA_FLL2_N_SHIFT                          0  /* FLL2_N - [9:0] */
+#define ARIZONA_FLL2_N_WIDTH                         10  /* FLL2_N - [9:0] */
+
+/*
+ * R403 (0x193) - FLL2 Control 3
+ */
+#define ARIZONA_FLL2_THETA_MASK                  0xFFFF  /* FLL2_THETA - [15:0] */
+#define ARIZONA_FLL2_THETA_SHIFT                      0  /* FLL2_THETA - [15:0] */
+#define ARIZONA_FLL2_THETA_WIDTH                     16  /* FLL2_THETA - [15:0] */
+
+/*
+ * R404 (0x194) - FLL2 Control 4
+ */
+#define ARIZONA_FLL2_LAMBDA_MASK                 0xFFFF  /* FLL2_LAMBDA - [15:0] */
+#define ARIZONA_FLL2_LAMBDA_SHIFT                     0  /* FLL2_LAMBDA - [15:0] */
+#define ARIZONA_FLL2_LAMBDA_WIDTH                    16  /* FLL2_LAMBDA - [15:0] */
+
+/*
+ * R405 (0x195) - FLL2 Control 5
+ */
+#define ARIZONA_FLL2_FRATIO_MASK                 0x0700  /* FLL2_FRATIO - [10:8] */
+#define ARIZONA_FLL2_FRATIO_SHIFT                     8  /* FLL2_FRATIO - [10:8] */
+#define ARIZONA_FLL2_FRATIO_WIDTH                     3  /* FLL2_FRATIO - [10:8] */
+#define ARIZONA_FLL2_OUTDIV_MASK                 0x000E  /* FLL2_OUTDIV - [3:1] */
+#define ARIZONA_FLL2_OUTDIV_SHIFT                     1  /* FLL2_OUTDIV - [3:1] */
+#define ARIZONA_FLL2_OUTDIV_WIDTH                     3  /* FLL2_OUTDIV - [3:1] */
+
+/*
+ * R406 (0x196) - FLL2 Control 6
+ */
+#define ARIZONA_FLL2_CLK_REF_DIV_MASK            0x00C0  /* FLL2_CLK_REF_DIV - [7:6] */
+#define ARIZONA_FLL2_CLK_REF_DIV_SHIFT                6  /* FLL2_CLK_REF_DIV - [7:6] */
+#define ARIZONA_FLL2_CLK_REF_DIV_WIDTH                2  /* FLL2_CLK_REF_DIV - [7:6] */
+#define ARIZONA_FLL2_CLK_REF_SRC_MASK            0x000F  /* FLL2_CLK_REF_SRC - [3:0] */
+#define ARIZONA_FLL2_CLK_REF_SRC_SHIFT                0  /* FLL2_CLK_REF_SRC - [3:0] */
+#define ARIZONA_FLL2_CLK_REF_SRC_WIDTH                4  /* FLL2_CLK_REF_SRC - [3:0] */
+
+/*
+ * R407 (0x197) - FLL2 Loop Filter Test 1
+ */
+#define ARIZONA_FLL2_FRC_INTEG_UPD               0x8000  /* FLL2_FRC_INTEG_UPD */
+#define ARIZONA_FLL2_FRC_INTEG_UPD_MASK          0x8000  /* FLL2_FRC_INTEG_UPD */
+#define ARIZONA_FLL2_FRC_INTEG_UPD_SHIFT             15  /* FLL2_FRC_INTEG_UPD */
+#define ARIZONA_FLL2_FRC_INTEG_UPD_WIDTH              1  /* FLL2_FRC_INTEG_UPD */
+#define ARIZONA_FLL2_FRC_INTEG_VAL_MASK          0x0FFF  /* FLL2_FRC_INTEG_VAL - [11:0] */
+#define ARIZONA_FLL2_FRC_INTEG_VAL_SHIFT              0  /* FLL2_FRC_INTEG_VAL - [11:0] */
+#define ARIZONA_FLL2_FRC_INTEG_VAL_WIDTH             12  /* FLL2_FRC_INTEG_VAL - [11:0] */
+
+/*
+ * R417 (0x1A1) - FLL2 Synchroniser 1
+ */
+#define ARIZONA_FLL2_SYNC_ENA                    0x0001  /* FLL2_SYNC_ENA */
+#define ARIZONA_FLL2_SYNC_ENA_MASK               0x0001  /* FLL2_SYNC_ENA */
+#define ARIZONA_FLL2_SYNC_ENA_SHIFT                   0  /* FLL2_SYNC_ENA */
+#define ARIZONA_FLL2_SYNC_ENA_WIDTH                   1  /* FLL2_SYNC_ENA */
+
+/*
+ * R418 (0x1A2) - FLL2 Synchroniser 2
+ */
+#define ARIZONA_FLL2_SYNC_N_MASK                 0x03FF  /* FLL2_SYNC_N - [9:0] */
+#define ARIZONA_FLL2_SYNC_N_SHIFT                     0  /* FLL2_SYNC_N - [9:0] */
+#define ARIZONA_FLL2_SYNC_N_WIDTH                    10  /* FLL2_SYNC_N - [9:0] */
+
+/*
+ * R419 (0x1A3) - FLL2 Synchroniser 3
+ */
+#define ARIZONA_FLL2_SYNC_THETA_MASK             0xFFFF  /* FLL2_SYNC_THETA - [15:0] */
+#define ARIZONA_FLL2_SYNC_THETA_SHIFT                 0  /* FLL2_SYNC_THETA - [15:0] */
+#define ARIZONA_FLL2_SYNC_THETA_WIDTH                16  /* FLL2_SYNC_THETA - [15:0] */
+
+/*
+ * R420 (0x1A4) - FLL2 Synchroniser 4
+ */
+#define ARIZONA_FLL2_SYNC_LAMBDA_MASK            0xFFFF  /* FLL2_SYNC_LAMBDA - [15:0] */
+#define ARIZONA_FLL2_SYNC_LAMBDA_SHIFT                0  /* FLL2_SYNC_LAMBDA - [15:0] */
+#define ARIZONA_FLL2_SYNC_LAMBDA_WIDTH               16  /* FLL2_SYNC_LAMBDA - [15:0] */
+
+/*
+ * R421 (0x1A5) - FLL2 Synchroniser 5
+ */
+#define ARIZONA_FLL2_SYNC_FRATIO_MASK            0x0700  /* FLL2_SYNC_FRATIO - [10:8] */
+#define ARIZONA_FLL2_SYNC_FRATIO_SHIFT                8  /* FLL2_SYNC_FRATIO - [10:8] */
+#define ARIZONA_FLL2_SYNC_FRATIO_WIDTH                3  /* FLL2_SYNC_FRATIO - [10:8] */
+
+/*
+ * R422 (0x1A6) - FLL2 Synchroniser 6
+ */
+#define ARIZONA_FLL2_CLK_SYNC_DIV_MASK           0x00C0  /* FLL2_CLK_SYNC_DIV - [7:6] */
+#define ARIZONA_FLL2_CLK_SYNC_DIV_SHIFT               6  /* FLL2_CLK_SYNC_DIV - [7:6] */
+#define ARIZONA_FLL2_CLK_SYNC_DIV_WIDTH               2  /* FLL2_CLK_SYNC_DIV - [7:6] */
+#define ARIZONA_FLL2_CLK_SYNC_SRC_MASK           0x000F  /* FLL2_CLK_SYNC_SRC - [3:0] */
+#define ARIZONA_FLL2_CLK_SYNC_SRC_SHIFT               0  /* FLL2_CLK_SYNC_SRC - [3:0] */
+#define ARIZONA_FLL2_CLK_SYNC_SRC_WIDTH               4  /* FLL2_CLK_SYNC_SRC - [3:0] */
+
+/*
+ * R425 (0x1A9) - FLL2 Spread Spectrum
+ */
+#define ARIZONA_FLL2_SS_AMPL_MASK                0x0030  /* FLL2_SS_AMPL - [5:4] */
+#define ARIZONA_FLL2_SS_AMPL_SHIFT                    4  /* FLL2_SS_AMPL - [5:4] */
+#define ARIZONA_FLL2_SS_AMPL_WIDTH                    2  /* FLL2_SS_AMPL - [5:4] */
+#define ARIZONA_FLL2_SS_FREQ_MASK                0x000C  /* FLL2_SS_FREQ - [3:2] */
+#define ARIZONA_FLL2_SS_FREQ_SHIFT                    2  /* FLL2_SS_FREQ - [3:2] */
+#define ARIZONA_FLL2_SS_FREQ_WIDTH                    2  /* FLL2_SS_FREQ - [3:2] */
+#define ARIZONA_FLL2_SS_SEL_MASK                 0x0003  /* FLL2_SS_SEL - [1:0] */
+#define ARIZONA_FLL2_SS_SEL_SHIFT                     0  /* FLL2_SS_SEL - [1:0] */
+#define ARIZONA_FLL2_SS_SEL_WIDTH                     2  /* FLL2_SS_SEL - [1:0] */
+
+/*
+ * R426 (0x1AA) - FLL2 GPIO Clock
+ */
+#define ARIZONA_FLL2_GPDIV_MASK                  0x00FE  /* FLL2_GPDIV - [7:1] */
+#define ARIZONA_FLL2_GPDIV_SHIFT                      1  /* FLL2_GPDIV - [7:1] */
+#define ARIZONA_FLL2_GPDIV_WIDTH                      7  /* FLL2_GPDIV - [7:1] */
+#define ARIZONA_FLL2_GPDIV_ENA                   0x0001  /* FLL2_GPDIV_ENA */
+#define ARIZONA_FLL2_GPDIV_ENA_MASK              0x0001  /* FLL2_GPDIV_ENA */
+#define ARIZONA_FLL2_GPDIV_ENA_SHIFT                  0  /* FLL2_GPDIV_ENA */
+#define ARIZONA_FLL2_GPDIV_ENA_WIDTH                  1  /* FLL2_GPDIV_ENA */
+
+/*
+ * R512 (0x200) - Mic Charge Pump 1
+ */
+#define ARIZONA_CPMIC_DISCH                      0x0004  /* CPMIC_DISCH */
+#define ARIZONA_CPMIC_DISCH_MASK                 0x0004  /* CPMIC_DISCH */
+#define ARIZONA_CPMIC_DISCH_SHIFT                     2  /* CPMIC_DISCH */
+#define ARIZONA_CPMIC_DISCH_WIDTH                     1  /* CPMIC_DISCH */
+#define ARIZONA_CPMIC_BYPASS                     0x0002  /* CPMIC_BYPASS */
+#define ARIZONA_CPMIC_BYPASS_MASK                0x0002  /* CPMIC_BYPASS */
+#define ARIZONA_CPMIC_BYPASS_SHIFT                    1  /* CPMIC_BYPASS */
+#define ARIZONA_CPMIC_BYPASS_WIDTH                    1  /* CPMIC_BYPASS */
+#define ARIZONA_CPMIC_ENA                        0x0001  /* CPMIC_ENA */
+#define ARIZONA_CPMIC_ENA_MASK                   0x0001  /* CPMIC_ENA */
+#define ARIZONA_CPMIC_ENA_SHIFT                       0  /* CPMIC_ENA */
+#define ARIZONA_CPMIC_ENA_WIDTH                       1  /* CPMIC_ENA */
+
+/*
+ * R528 (0x210) - LDO1 Control 1
+ */
+#define ARIZONA_LDO1_VSEL_MASK                   0x07E0  /* LDO1_VSEL - [10:5] */
+#define ARIZONA_LDO1_VSEL_SHIFT                       5  /* LDO1_VSEL - [10:5] */
+#define ARIZONA_LDO1_VSEL_WIDTH                       6  /* LDO1_VSEL - [10:5] */
+#define ARIZONA_LDO1_FAST                        0x0010  /* LDO1_FAST */
+#define ARIZONA_LDO1_FAST_MASK                   0x0010  /* LDO1_FAST */
+#define ARIZONA_LDO1_FAST_SHIFT                       4  /* LDO1_FAST */
+#define ARIZONA_LDO1_FAST_WIDTH                       1  /* LDO1_FAST */
+#define ARIZONA_LDO1_DISCH                       0x0004  /* LDO1_DISCH */
+#define ARIZONA_LDO1_DISCH_MASK                  0x0004  /* LDO1_DISCH */
+#define ARIZONA_LDO1_DISCH_SHIFT                      2  /* LDO1_DISCH */
+#define ARIZONA_LDO1_DISCH_WIDTH                      1  /* LDO1_DISCH */
+#define ARIZONA_LDO1_BYPASS                      0x0002  /* LDO1_BYPASS */
+#define ARIZONA_LDO1_BYPASS_MASK                 0x0002  /* LDO1_BYPASS */
+#define ARIZONA_LDO1_BYPASS_SHIFT                     1  /* LDO1_BYPASS */
+#define ARIZONA_LDO1_BYPASS_WIDTH                     1  /* LDO1_BYPASS */
+#define ARIZONA_LDO1_ENA                         0x0001  /* LDO1_ENA */
+#define ARIZONA_LDO1_ENA_MASK                    0x0001  /* LDO1_ENA */
+#define ARIZONA_LDO1_ENA_SHIFT                        0  /* LDO1_ENA */
+#define ARIZONA_LDO1_ENA_WIDTH                        1  /* LDO1_ENA */
+
+/*
+ * R531 (0x213) - LDO2 Control 1
+ */
+#define ARIZONA_LDO2_VSEL_MASK                   0x07E0  /* LDO2_VSEL - [10:5] */
+#define ARIZONA_LDO2_VSEL_SHIFT                       5  /* LDO2_VSEL - [10:5] */
+#define ARIZONA_LDO2_VSEL_WIDTH                       6  /* LDO2_VSEL - [10:5] */
+#define ARIZONA_LDO2_FAST                        0x0010  /* LDO2_FAST */
+#define ARIZONA_LDO2_FAST_MASK                   0x0010  /* LDO2_FAST */
+#define ARIZONA_LDO2_FAST_SHIFT                       4  /* LDO2_FAST */
+#define ARIZONA_LDO2_FAST_WIDTH                       1  /* LDO2_FAST */
+#define ARIZONA_LDO2_DISCH                       0x0004  /* LDO2_DISCH */
+#define ARIZONA_LDO2_DISCH_MASK                  0x0004  /* LDO2_DISCH */
+#define ARIZONA_LDO2_DISCH_SHIFT                      2  /* LDO2_DISCH */
+#define ARIZONA_LDO2_DISCH_WIDTH                      1  /* LDO2_DISCH */
+#define ARIZONA_LDO2_BYPASS                      0x0002  /* LDO2_BYPASS */
+#define ARIZONA_LDO2_BYPASS_MASK                 0x0002  /* LDO2_BYPASS */
+#define ARIZONA_LDO2_BYPASS_SHIFT                     1  /* LDO2_BYPASS */
+#define ARIZONA_LDO2_BYPASS_WIDTH                     1  /* LDO2_BYPASS */
+#define ARIZONA_LDO2_ENA                         0x0001  /* LDO2_ENA */
+#define ARIZONA_LDO2_ENA_MASK                    0x0001  /* LDO2_ENA */
+#define ARIZONA_LDO2_ENA_SHIFT                        0  /* LDO2_ENA */
+#define ARIZONA_LDO2_ENA_WIDTH                        1  /* LDO2_ENA */
+
+/*
+ * R536 (0x218) - Mic Bias Ctrl 1
+ */
+#define ARIZONA_MICB1_EXT_CAP                    0x8000  /* MICB1_EXT_CAP */
+#define ARIZONA_MICB1_EXT_CAP_MASK               0x8000  /* MICB1_EXT_CAP */
+#define ARIZONA_MICB1_EXT_CAP_SHIFT                  15  /* MICB1_EXT_CAP */
+#define ARIZONA_MICB1_EXT_CAP_WIDTH                   1  /* MICB1_EXT_CAP */
+#define ARIZONA_MICB1_LVL_MASK                   0x01E0  /* MICB1_LVL - [8:5] */
+#define ARIZONA_MICB1_LVL_SHIFT                       5  /* MICB1_LVL - [8:5] */
+#define ARIZONA_MICB1_LVL_WIDTH                       4  /* MICB1_LVL - [8:5] */
+#define ARIZONA_MICB1_FAST                       0x0010  /* MICB1_FAST */
+#define ARIZONA_MICB1_FAST_MASK                  0x0010  /* MICB1_FAST */
+#define ARIZONA_MICB1_FAST_SHIFT                      4  /* MICB1_FAST */
+#define ARIZONA_MICB1_FAST_WIDTH                      1  /* MICB1_FAST */
+#define ARIZONA_MICB1_RATE                       0x0008  /* MICB1_RATE */
+#define ARIZONA_MICB1_RATE_MASK                  0x0008  /* MICB1_RATE */
+#define ARIZONA_MICB1_RATE_SHIFT                      3  /* MICB1_RATE */
+#define ARIZONA_MICB1_RATE_WIDTH                      1  /* MICB1_RATE */
+#define ARIZONA_MICB1_DISCH                      0x0004  /* MICB1_DISCH */
+#define ARIZONA_MICB1_DISCH_MASK                 0x0004  /* MICB1_DISCH */
+#define ARIZONA_MICB1_DISCH_SHIFT                     2  /* MICB1_DISCH */
+#define ARIZONA_MICB1_DISCH_WIDTH                     1  /* MICB1_DISCH */
+#define ARIZONA_MICB1_BYPASS                     0x0002  /* MICB1_BYPASS */
+#define ARIZONA_MICB1_BYPASS_MASK                0x0002  /* MICB1_BYPASS */
+#define ARIZONA_MICB1_BYPASS_SHIFT                    1  /* MICB1_BYPASS */
+#define ARIZONA_MICB1_BYPASS_WIDTH                    1  /* MICB1_BYPASS */
+#define ARIZONA_MICB1_ENA                        0x0001  /* MICB1_ENA */
+#define ARIZONA_MICB1_ENA_MASK                   0x0001  /* MICB1_ENA */
+#define ARIZONA_MICB1_ENA_SHIFT                       0  /* MICB1_ENA */
+#define ARIZONA_MICB1_ENA_WIDTH                       1  /* MICB1_ENA */
+
+/*
+ * R537 (0x219) - Mic Bias Ctrl 2
+ */
+#define ARIZONA_MICB2_EXT_CAP                    0x8000  /* MICB2_EXT_CAP */
+#define ARIZONA_MICB2_EXT_CAP_MASK               0x8000  /* MICB2_EXT_CAP */
+#define ARIZONA_MICB2_EXT_CAP_SHIFT                  15  /* MICB2_EXT_CAP */
+#define ARIZONA_MICB2_EXT_CAP_WIDTH                   1  /* MICB2_EXT_CAP */
+#define ARIZONA_MICB2_LVL_MASK                   0x01E0  /* MICB2_LVL - [8:5] */
+#define ARIZONA_MICB2_LVL_SHIFT                       5  /* MICB2_LVL - [8:5] */
+#define ARIZONA_MICB2_LVL_WIDTH                       4  /* MICB2_LVL - [8:5] */
+#define ARIZONA_MICB2_FAST                       0x0010  /* MICB2_FAST */
+#define ARIZONA_MICB2_FAST_MASK                  0x0010  /* MICB2_FAST */
+#define ARIZONA_MICB2_FAST_SHIFT                      4  /* MICB2_FAST */
+#define ARIZONA_MICB2_FAST_WIDTH                      1  /* MICB2_FAST */
+#define ARIZONA_MICB2_RATE                       0x0008  /* MICB2_RATE */
+#define ARIZONA_MICB2_RATE_MASK                  0x0008  /* MICB2_RATE */
+#define ARIZONA_MICB2_RATE_SHIFT                      3  /* MICB2_RATE */
+#define ARIZONA_MICB2_RATE_WIDTH                      1  /* MICB2_RATE */
+#define ARIZONA_MICB2_DISCH                      0x0004  /* MICB2_DISCH */
+#define ARIZONA_MICB2_DISCH_MASK                 0x0004  /* MICB2_DISCH */
+#define ARIZONA_MICB2_DISCH_SHIFT                     2  /* MICB2_DISCH */
+#define ARIZONA_MICB2_DISCH_WIDTH                     1  /* MICB2_DISCH */
+#define ARIZONA_MICB2_BYPASS                     0x0002  /* MICB2_BYPASS */
+#define ARIZONA_MICB2_BYPASS_MASK                0x0002  /* MICB2_BYPASS */
+#define ARIZONA_MICB2_BYPASS_SHIFT                    1  /* MICB2_BYPASS */
+#define ARIZONA_MICB2_BYPASS_WIDTH                    1  /* MICB2_BYPASS */
+#define ARIZONA_MICB2_ENA                        0x0001  /* MICB2_ENA */
+#define ARIZONA_MICB2_ENA_MASK                   0x0001  /* MICB2_ENA */
+#define ARIZONA_MICB2_ENA_SHIFT                       0  /* MICB2_ENA */
+#define ARIZONA_MICB2_ENA_WIDTH                       1  /* MICB2_ENA */
+
+/*
+ * R538 (0x21A) - Mic Bias Ctrl 3
+ */
+#define ARIZONA_MICB3_EXT_CAP                    0x8000  /* MICB3_EXT_CAP */
+#define ARIZONA_MICB3_EXT_CAP_MASK               0x8000  /* MICB3_EXT_CAP */
+#define ARIZONA_MICB3_EXT_CAP_SHIFT                  15  /* MICB3_EXT_CAP */
+#define ARIZONA_MICB3_EXT_CAP_WIDTH                   1  /* MICB3_EXT_CAP */
+#define ARIZONA_MICB3_LVL_MASK                   0x01E0  /* MICB3_LVL - [8:5] */
+#define ARIZONA_MICB3_LVL_SHIFT                       5  /* MICB3_LVL - [8:5] */
+#define ARIZONA_MICB3_LVL_WIDTH                       4  /* MICB3_LVL - [8:5] */
+#define ARIZONA_MICB3_FAST                       0x0010  /* MICB3_FAST */
+#define ARIZONA_MICB3_FAST_MASK                  0x0010  /* MICB3_FAST */
+#define ARIZONA_MICB3_FAST_SHIFT                      4  /* MICB3_FAST */
+#define ARIZONA_MICB3_FAST_WIDTH                      1  /* MICB3_FAST */
+#define ARIZONA_MICB3_RATE                       0x0008  /* MICB3_RATE */
+#define ARIZONA_MICB3_RATE_MASK                  0x0008  /* MICB3_RATE */
+#define ARIZONA_MICB3_RATE_SHIFT                      3  /* MICB3_RATE */
+#define ARIZONA_MICB3_RATE_WIDTH                      1  /* MICB3_RATE */
+#define ARIZONA_MICB3_DISCH                      0x0004  /* MICB3_DISCH */
+#define ARIZONA_MICB3_DISCH_MASK                 0x0004  /* MICB3_DISCH */
+#define ARIZONA_MICB3_DISCH_SHIFT                     2  /* MICB3_DISCH */
+#define ARIZONA_MICB3_DISCH_WIDTH                     1  /* MICB3_DISCH */
+#define ARIZONA_MICB3_BYPASS                     0x0002  /* MICB3_BYPASS */
+#define ARIZONA_MICB3_BYPASS_MASK                0x0002  /* MICB3_BYPASS */
+#define ARIZONA_MICB3_BYPASS_SHIFT                    1  /* MICB3_BYPASS */
+#define ARIZONA_MICB3_BYPASS_WIDTH                    1  /* MICB3_BYPASS */
+#define ARIZONA_MICB3_ENA                        0x0001  /* MICB3_ENA */
+#define ARIZONA_MICB3_ENA_MASK                   0x0001  /* MICB3_ENA */
+#define ARIZONA_MICB3_ENA_SHIFT                       0  /* MICB3_ENA */
+#define ARIZONA_MICB3_ENA_WIDTH                       1  /* MICB3_ENA */
+
+/*
+ * R659 (0x293) - Accessory Detect Mode 1
+ */
+#define ARIZONA_ACCDET_SRC                       0x2000  /* ACCDET_SRC */
+#define ARIZONA_ACCDET_SRC_MASK                  0x2000  /* ACCDET_SRC */
+#define ARIZONA_ACCDET_SRC_SHIFT                     13  /* ACCDET_SRC */
+#define ARIZONA_ACCDET_SRC_WIDTH                      1  /* ACCDET_SRC */
+#define ARIZONA_ACCDET_MODE_MASK                 0x0003  /* ACCDET_MODE - [1:0] */
+#define ARIZONA_ACCDET_MODE_SHIFT                     0  /* ACCDET_MODE - [1:0] */
+#define ARIZONA_ACCDET_MODE_WIDTH                     2  /* ACCDET_MODE - [1:0] */
+
+/*
+ * R667 (0x29B) - Headphone Detect 1
+ */
+#define ARIZONA_HP_STEP_SIZE                     0x0100  /* HP_STEP_SIZE */
+#define ARIZONA_HP_STEP_SIZE_MASK                0x0100  /* HP_STEP_SIZE */
+#define ARIZONA_HP_STEP_SIZE_SHIFT                    8  /* HP_STEP_SIZE */
+#define ARIZONA_HP_STEP_SIZE_WIDTH                    1  /* HP_STEP_SIZE */
+#define ARIZONA_HP_HOLDTIME_MASK                 0x00E0  /* HP_HOLDTIME - [7:5] */
+#define ARIZONA_HP_HOLDTIME_SHIFT                     5  /* HP_HOLDTIME - [7:5] */
+#define ARIZONA_HP_HOLDTIME_WIDTH                     3  /* HP_HOLDTIME - [7:5] */
+#define ARIZONA_HP_CLK_DIV_MASK                  0x0018  /* HP_CLK_DIV - [4:3] */
+#define ARIZONA_HP_CLK_DIV_SHIFT                      3  /* HP_CLK_DIV - [4:3] */
+#define ARIZONA_HP_CLK_DIV_WIDTH                      2  /* HP_CLK_DIV - [4:3] */
+#define ARIZONA_HP_IDAC_STEER                    0x0004  /* HP_IDAC_STEER */
+#define ARIZONA_HP_IDAC_STEER_MASK               0x0004  /* HP_IDAC_STEER */
+#define ARIZONA_HP_IDAC_STEER_SHIFT                   2  /* HP_IDAC_STEER */
+#define ARIZONA_HP_IDAC_STEER_WIDTH                   1  /* HP_IDAC_STEER */
+#define ARIZONA_HP_RATE                          0x0002  /* HP_RATE */
+#define ARIZONA_HP_RATE_MASK                     0x0002  /* HP_RATE */
+#define ARIZONA_HP_RATE_SHIFT                         1  /* HP_RATE */
+#define ARIZONA_HP_RATE_WIDTH                         1  /* HP_RATE */
+#define ARIZONA_HP_POLL                          0x0001  /* HP_POLL */
+#define ARIZONA_HP_POLL_MASK                     0x0001  /* HP_POLL */
+#define ARIZONA_HP_POLL_SHIFT                         0  /* HP_POLL */
+#define ARIZONA_HP_POLL_WIDTH                         1  /* HP_POLL */
+
+/*
+ * R668 (0x29C) - Headphone Detect 2
+ */
+#define ARIZONA_HP_DONE                          0x0080  /* HP_DONE */
+#define ARIZONA_HP_DONE_MASK                     0x0080  /* HP_DONE */
+#define ARIZONA_HP_DONE_SHIFT                         7  /* HP_DONE */
+#define ARIZONA_HP_DONE_WIDTH                         1  /* HP_DONE */
+#define ARIZONA_HP_LVL_MASK                      0x007F  /* HP_LVL - [6:0] */
+#define ARIZONA_HP_LVL_SHIFT                          0  /* HP_LVL - [6:0] */
+#define ARIZONA_HP_LVL_WIDTH                          7  /* HP_LVL - [6:0] */
+
+/*
+ * R675 (0x2A3) - Mic Detect 1
+ */
+#define ARIZONA_MICD_BIAS_STARTTIME_MASK         0xF000  /* MICD_BIAS_STARTTIME - [15:12] */
+#define ARIZONA_MICD_BIAS_STARTTIME_SHIFT            12  /* MICD_BIAS_STARTTIME - [15:12] */
+#define ARIZONA_MICD_BIAS_STARTTIME_WIDTH             4  /* MICD_BIAS_STARTTIME - [15:12] */
+#define ARIZONA_MICD_RATE_MASK                   0x0F00  /* MICD_RATE - [11:8] */
+#define ARIZONA_MICD_RATE_SHIFT                       8  /* MICD_RATE - [11:8] */
+#define ARIZONA_MICD_RATE_WIDTH                       4  /* MICD_RATE - [11:8] */
+#define ARIZONA_MICD_BIAS_SRC_MASK               0x0030  /* MICD_BIAS_SRC - [5:4] */
+#define ARIZONA_MICD_BIAS_SRC_SHIFT                   4  /* MICD_BIAS_SRC - [5:4] */
+#define ARIZONA_MICD_BIAS_SRC_WIDTH                   2  /* MICD_BIAS_SRC - [5:4] */
+#define ARIZONA_MICD_DBTIME                      0x0002  /* MICD_DBTIME */
+#define ARIZONA_MICD_DBTIME_MASK                 0x0002  /* MICD_DBTIME */
+#define ARIZONA_MICD_DBTIME_SHIFT                     1  /* MICD_DBTIME */
+#define ARIZONA_MICD_DBTIME_WIDTH                     1  /* MICD_DBTIME */
+#define ARIZONA_MICD_ENA                         0x0001  /* MICD_ENA */
+#define ARIZONA_MICD_ENA_MASK                    0x0001  /* MICD_ENA */
+#define ARIZONA_MICD_ENA_SHIFT                        0  /* MICD_ENA */
+#define ARIZONA_MICD_ENA_WIDTH                        1  /* MICD_ENA */
+
+/*
+ * R676 (0x2A4) - Mic Detect 2
+ */
+#define ARIZONA_MICD_LVL_SEL_MASK                0x00FF  /* MICD_LVL_SEL - [7:0] */
+#define ARIZONA_MICD_LVL_SEL_SHIFT                    0  /* MICD_LVL_SEL - [7:0] */
+#define ARIZONA_MICD_LVL_SEL_WIDTH                    8  /* MICD_LVL_SEL - [7:0] */
+
+/*
+ * R677 (0x2A5) - Mic Detect 3
+ */
+#define ARIZONA_MICD_LVL_MASK                    0x07FC  /* MICD_LVL - [10:2] */
+#define ARIZONA_MICD_LVL_SHIFT                        2  /* MICD_LVL - [10:2] */
+#define ARIZONA_MICD_LVL_WIDTH                        9  /* MICD_LVL - [10:2] */
+#define ARIZONA_MICD_VALID                       0x0002  /* MICD_VALID */
+#define ARIZONA_MICD_VALID_MASK                  0x0002  /* MICD_VALID */
+#define ARIZONA_MICD_VALID_SHIFT                      1  /* MICD_VALID */
+#define ARIZONA_MICD_VALID_WIDTH                      1  /* MICD_VALID */
+#define ARIZONA_MICD_STS                         0x0001  /* MICD_STS */
+#define ARIZONA_MICD_STS_MASK                    0x0001  /* MICD_STS */
+#define ARIZONA_MICD_STS_SHIFT                        0  /* MICD_STS */
+#define ARIZONA_MICD_STS_WIDTH                        1  /* MICD_STS */
+
+/*
+ * R707 (0x2C3) - Mic noise mix control 1
+ */
+#define ARIZONA_MICMUTE_RATE_MASK                0x7800  /* MICMUTE_RATE - [14:11] */
+#define ARIZONA_MICMUTE_RATE_SHIFT                   11  /* MICMUTE_RATE - [14:11] */
+#define ARIZONA_MICMUTE_RATE_WIDTH                    4  /* MICMUTE_RATE - [14:11] */
+#define ARIZONA_MICMUTE_MIX_ENA                  0x0040  /* MICMUTE_MIX_ENA */
+#define ARIZONA_MICMUTE_MIX_ENA_MASK             0x0040  /* MICMUTE_MIX_ENA */
+#define ARIZONA_MICMUTE_MIX_ENA_SHIFT                 6  /* MICMUTE_MIX_ENA */
+#define ARIZONA_MICMUTE_MIX_ENA_WIDTH                 1  /* MICMUTE_MIX_ENA */
+
+/*
+ * R715 (0x2CB) - Isolation control
+ */
+#define ARIZONA_ISOLATE_DCVDD1                   0x0001  /* ISOLATE_DCVDD1 */
+#define ARIZONA_ISOLATE_DCVDD1_MASK              0x0001  /* ISOLATE_DCVDD1 */
+#define ARIZONA_ISOLATE_DCVDD1_SHIFT                  0  /* ISOLATE_DCVDD1 */
+#define ARIZONA_ISOLATE_DCVDD1_WIDTH                  1  /* ISOLATE_DCVDD1 */
+
+/*
+ * R723 (0x2D3) - Jack detect analogue
+ */
+#define ARIZONA_JD2_ENA                          0x0002  /* JD2_ENA */
+#define ARIZONA_JD2_ENA_MASK                     0x0002  /* JD2_ENA */
+#define ARIZONA_JD2_ENA_SHIFT                         1  /* JD2_ENA */
+#define ARIZONA_JD2_ENA_WIDTH                         1  /* JD2_ENA */
+#define ARIZONA_JD1_ENA                          0x0001  /* JD1_ENA */
+#define ARIZONA_JD1_ENA_MASK                     0x0001  /* JD1_ENA */
+#define ARIZONA_JD1_ENA_SHIFT                         0  /* JD1_ENA */
+#define ARIZONA_JD1_ENA_WIDTH                         1  /* JD1_ENA */
+
+/*
+ * R768 (0x300) - Input Enables
+ */
+#define ARIZONA_IN4L_ENA                         0x0080  /* IN4L_ENA */
+#define ARIZONA_IN4L_ENA_MASK                    0x0080  /* IN4L_ENA */
+#define ARIZONA_IN4L_ENA_SHIFT                        7  /* IN4L_ENA */
+#define ARIZONA_IN4L_ENA_WIDTH                        1  /* IN4L_ENA */
+#define ARIZONA_IN4R_ENA                         0x0040  /* IN4R_ENA */
+#define ARIZONA_IN4R_ENA_MASK                    0x0040  /* IN4R_ENA */
+#define ARIZONA_IN4R_ENA_SHIFT                        6  /* IN4R_ENA */
+#define ARIZONA_IN4R_ENA_WIDTH                        1  /* IN4R_ENA */
+#define ARIZONA_IN3L_ENA                         0x0020  /* IN3L_ENA */
+#define ARIZONA_IN3L_ENA_MASK                    0x0020  /* IN3L_ENA */
+#define ARIZONA_IN3L_ENA_SHIFT                        5  /* IN3L_ENA */
+#define ARIZONA_IN3L_ENA_WIDTH                        1  /* IN3L_ENA */
+#define ARIZONA_IN3R_ENA                         0x0010  /* IN3R_ENA */
+#define ARIZONA_IN3R_ENA_MASK                    0x0010  /* IN3R_ENA */
+#define ARIZONA_IN3R_ENA_SHIFT                        4  /* IN3R_ENA */
+#define ARIZONA_IN3R_ENA_WIDTH                        1  /* IN3R_ENA */
+#define ARIZONA_IN2L_ENA                         0x0008  /* IN2L_ENA */
+#define ARIZONA_IN2L_ENA_MASK                    0x0008  /* IN2L_ENA */
+#define ARIZONA_IN2L_ENA_SHIFT                        3  /* IN2L_ENA */
+#define ARIZONA_IN2L_ENA_WIDTH                        1  /* IN2L_ENA */
+#define ARIZONA_IN2R_ENA                         0x0004  /* IN2R_ENA */
+#define ARIZONA_IN2R_ENA_MASK                    0x0004  /* IN2R_ENA */
+#define ARIZONA_IN2R_ENA_SHIFT                        2  /* IN2R_ENA */
+#define ARIZONA_IN2R_ENA_WIDTH                        1  /* IN2R_ENA */
+#define ARIZONA_IN1L_ENA                         0x0002  /* IN1L_ENA */
+#define ARIZONA_IN1L_ENA_MASK                    0x0002  /* IN1L_ENA */
+#define ARIZONA_IN1L_ENA_SHIFT                        1  /* IN1L_ENA */
+#define ARIZONA_IN1L_ENA_WIDTH                        1  /* IN1L_ENA */
+#define ARIZONA_IN1R_ENA                         0x0001  /* IN1R_ENA */
+#define ARIZONA_IN1R_ENA_MASK                    0x0001  /* IN1R_ENA */
+#define ARIZONA_IN1R_ENA_SHIFT                        0  /* IN1R_ENA */
+#define ARIZONA_IN1R_ENA_WIDTH                        1  /* IN1R_ENA */
+
+/*
+ * R776 (0x308) - Input Rate
+ */
+#define ARIZONA_IN_RATE_MASK                     0x7800  /* IN_RATE - [14:11] */
+#define ARIZONA_IN_RATE_SHIFT                        11  /* IN_RATE - [14:11] */
+#define ARIZONA_IN_RATE_WIDTH                         4  /* IN_RATE - [14:11] */
+
+/*
+ * R777 (0x309) - Input Volume Ramp
+ */
+#define ARIZONA_IN_VD_RAMP_MASK                  0x0070  /* IN_VD_RAMP - [6:4] */
+#define ARIZONA_IN_VD_RAMP_SHIFT                      4  /* IN_VD_RAMP - [6:4] */
+#define ARIZONA_IN_VD_RAMP_WIDTH                      3  /* IN_VD_RAMP - [6:4] */
+#define ARIZONA_IN_VI_RAMP_MASK                  0x0007  /* IN_VI_RAMP - [2:0] */
+#define ARIZONA_IN_VI_RAMP_SHIFT                      0  /* IN_VI_RAMP - [2:0] */
+#define ARIZONA_IN_VI_RAMP_WIDTH                      3  /* IN_VI_RAMP - [2:0] */
+
+/*
+ * R784 (0x310) - IN1L Control
+ */
+#define ARIZONA_IN1_OSR_MASK                     0x6000  /* IN1_OSR - [14:13] */
+#define ARIZONA_IN1_OSR_SHIFT                        13  /* IN1_OSR - [14:13] */
+#define ARIZONA_IN1_OSR_WIDTH                         2  /* IN1_OSR - [14:13] */
+#define ARIZONA_IN1_DMIC_SUP_MASK                0x1800  /* IN1_DMIC_SUP - [12:11] */
+#define ARIZONA_IN1_DMIC_SUP_SHIFT                   11  /* IN1_DMIC_SUP - [12:11] */
+#define ARIZONA_IN1_DMIC_SUP_WIDTH                    2  /* IN1_DMIC_SUP - [12:11] */
+#define ARIZONA_IN1_MODE_MASK                    0x0600  /* IN1_MODE - [10:9] */
+#define ARIZONA_IN1_MODE_SHIFT                        9  /* IN1_MODE - [10:9] */
+#define ARIZONA_IN1_MODE_WIDTH                        2  /* IN1_MODE - [10:9] */
+#define ARIZONA_IN1L_PGA_VOL_MASK                0x00FE  /* IN1L_PGA_VOL - [7:1] */
+#define ARIZONA_IN1L_PGA_VOL_SHIFT                    1  /* IN1L_PGA_VOL - [7:1] */
+#define ARIZONA_IN1L_PGA_VOL_WIDTH                    7  /* IN1L_PGA_VOL - [7:1] */
+
+/*
+ * R785 (0x311) - ADC Digital Volume 1L
+ */
+#define ARIZONA_IN_VU                            0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_MASK                       0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_SHIFT                           9  /* IN_VU */
+#define ARIZONA_IN_VU_WIDTH                           1  /* IN_VU */
+#define ARIZONA_IN1L_MUTE                        0x0100  /* IN1L_MUTE */
+#define ARIZONA_IN1L_MUTE_MASK                   0x0100  /* IN1L_MUTE */
+#define ARIZONA_IN1L_MUTE_SHIFT                       8  /* IN1L_MUTE */
+#define ARIZONA_IN1L_MUTE_WIDTH                       1  /* IN1L_MUTE */
+#define ARIZONA_IN1L_DIG_VOL_MASK                0x00FF  /* IN1L_DIG_VOL - [7:0] */
+#define ARIZONA_IN1L_DIG_VOL_SHIFT                    0  /* IN1L_DIG_VOL - [7:0] */
+#define ARIZONA_IN1L_DIG_VOL_WIDTH                    8  /* IN1L_DIG_VOL - [7:0] */
+
+/*
+ * R786 (0x312) - DMIC1L Control
+ */
+#define ARIZONA_IN1_DMICL_DLY_MASK               0x003F  /* IN1_DMICL_DLY - [5:0] */
+#define ARIZONA_IN1_DMICL_DLY_SHIFT                   0  /* IN1_DMICL_DLY - [5:0] */
+#define ARIZONA_IN1_DMICL_DLY_WIDTH                   6  /* IN1_DMICL_DLY - [5:0] */
+
+/*
+ * R788 (0x314) - IN1R Control
+ */
+#define ARIZONA_IN1R_PGA_VOL_MASK                0x00FE  /* IN1R_PGA_VOL - [7:1] */
+#define ARIZONA_IN1R_PGA_VOL_SHIFT                    1  /* IN1R_PGA_VOL - [7:1] */
+#define ARIZONA_IN1R_PGA_VOL_WIDTH                    7  /* IN1R_PGA_VOL - [7:1] */
+
+/*
+ * R789 (0x315) - ADC Digital Volume 1R
+ */
+#define ARIZONA_IN_VU                            0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_MASK                       0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_SHIFT                           9  /* IN_VU */
+#define ARIZONA_IN_VU_WIDTH                           1  /* IN_VU */
+#define ARIZONA_IN1R_MUTE                        0x0100  /* IN1R_MUTE */
+#define ARIZONA_IN1R_MUTE_MASK                   0x0100  /* IN1R_MUTE */
+#define ARIZONA_IN1R_MUTE_SHIFT                       8  /* IN1R_MUTE */
+#define ARIZONA_IN1R_MUTE_WIDTH                       1  /* IN1R_MUTE */
+#define ARIZONA_IN1R_DIG_VOL_MASK                0x00FF  /* IN1R_DIG_VOL - [7:0] */
+#define ARIZONA_IN1R_DIG_VOL_SHIFT                    0  /* IN1R_DIG_VOL - [7:0] */
+#define ARIZONA_IN1R_DIG_VOL_WIDTH                    8  /* IN1R_DIG_VOL - [7:0] */
+
+/*
+ * R790 (0x316) - DMIC1R Control
+ */
+#define ARIZONA_IN1_DMICR_DLY_MASK               0x003F  /* IN1_DMICR_DLY - [5:0] */
+#define ARIZONA_IN1_DMICR_DLY_SHIFT                   0  /* IN1_DMICR_DLY - [5:0] */
+#define ARIZONA_IN1_DMICR_DLY_WIDTH                   6  /* IN1_DMICR_DLY - [5:0] */
+
+/*
+ * R792 (0x318) - IN2L Control
+ */
+#define ARIZONA_IN2_OSR_MASK                     0x6000  /* IN2_OSR - [14:13] */
+#define ARIZONA_IN2_OSR_SHIFT                        13  /* IN2_OSR - [14:13] */
+#define ARIZONA_IN2_OSR_WIDTH                         2  /* IN2_OSR - [14:13] */
+#define ARIZONA_IN2_DMIC_SUP_MASK                0x1800  /* IN2_DMIC_SUP - [12:11] */
+#define ARIZONA_IN2_DMIC_SUP_SHIFT                   11  /* IN2_DMIC_SUP - [12:11] */
+#define ARIZONA_IN2_DMIC_SUP_WIDTH                    2  /* IN2_DMIC_SUP - [12:11] */
+#define ARIZONA_IN2_MODE_MASK                    0x0600  /* IN2_MODE - [10:9] */
+#define ARIZONA_IN2_MODE_SHIFT                        9  /* IN2_MODE - [10:9] */
+#define ARIZONA_IN2_MODE_WIDTH                        2  /* IN2_MODE - [10:9] */
+#define ARIZONA_IN2L_PGA_VOL_MASK                0x00FE  /* IN2L_PGA_VOL - [7:1] */
+#define ARIZONA_IN2L_PGA_VOL_SHIFT                    1  /* IN2L_PGA_VOL - [7:1] */
+#define ARIZONA_IN2L_PGA_VOL_WIDTH                    7  /* IN2L_PGA_VOL - [7:1] */
+
+/*
+ * R793 (0x319) - ADC Digital Volume 2L
+ */
+#define ARIZONA_IN_VU                            0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_MASK                       0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_SHIFT                           9  /* IN_VU */
+#define ARIZONA_IN_VU_WIDTH                           1  /* IN_VU */
+#define ARIZONA_IN2L_MUTE                        0x0100  /* IN2L_MUTE */
+#define ARIZONA_IN2L_MUTE_MASK                   0x0100  /* IN2L_MUTE */
+#define ARIZONA_IN2L_MUTE_SHIFT                       8  /* IN2L_MUTE */
+#define ARIZONA_IN2L_MUTE_WIDTH                       1  /* IN2L_MUTE */
+#define ARIZONA_IN2L_DIG_VOL_MASK                0x00FF  /* IN2L_DIG_VOL - [7:0] */
+#define ARIZONA_IN2L_DIG_VOL_SHIFT                    0  /* IN2L_DIG_VOL - [7:0] */
+#define ARIZONA_IN2L_DIG_VOL_WIDTH                    8  /* IN2L_DIG_VOL - [7:0] */
+
+/*
+ * R794 (0x31A) - DMIC2L Control
+ */
+#define ARIZONA_IN2_DMICL_DLY_MASK               0x003F  /* IN2_DMICL_DLY - [5:0] */
+#define ARIZONA_IN2_DMICL_DLY_SHIFT                   0  /* IN2_DMICL_DLY - [5:0] */
+#define ARIZONA_IN2_DMICL_DLY_WIDTH                   6  /* IN2_DMICL_DLY - [5:0] */
+
+/*
+ * R796 (0x31C) - IN2R Control
+ */
+#define ARIZONA_IN2R_PGA_VOL_MASK                0x00FE  /* IN2R_PGA_VOL - [7:1] */
+#define ARIZONA_IN2R_PGA_VOL_SHIFT                    1  /* IN2R_PGA_VOL - [7:1] */
+#define ARIZONA_IN2R_PGA_VOL_WIDTH                    7  /* IN2R_PGA_VOL - [7:1] */
+
+/*
+ * R797 (0x31D) - ADC Digital Volume 2R
+ */
+#define ARIZONA_IN_VU                            0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_MASK                       0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_SHIFT                           9  /* IN_VU */
+#define ARIZONA_IN_VU_WIDTH                           1  /* IN_VU */
+#define ARIZONA_IN2R_MUTE                        0x0100  /* IN2R_MUTE */
+#define ARIZONA_IN2R_MUTE_MASK                   0x0100  /* IN2R_MUTE */
+#define ARIZONA_IN2R_MUTE_SHIFT                       8  /* IN2R_MUTE */
+#define ARIZONA_IN2R_MUTE_WIDTH                       1  /* IN2R_MUTE */
+#define ARIZONA_IN2R_DIG_VOL_MASK                0x00FF  /* IN2R_DIG_VOL - [7:0] */
+#define ARIZONA_IN2R_DIG_VOL_SHIFT                    0  /* IN2R_DIG_VOL - [7:0] */
+#define ARIZONA_IN2R_DIG_VOL_WIDTH                    8  /* IN2R_DIG_VOL - [7:0] */
+
+/*
+ * R798 (0x31E) - DMIC2R Control
+ */
+#define ARIZONA_IN2_DMICR_DLY_MASK               0x003F  /* IN2_DMICR_DLY - [5:0] */
+#define ARIZONA_IN2_DMICR_DLY_SHIFT                   0  /* IN2_DMICR_DLY - [5:0] */
+#define ARIZONA_IN2_DMICR_DLY_WIDTH                   6  /* IN2_DMICR_DLY - [5:0] */
+
+/*
+ * R800 (0x320) - IN3L Control
+ */
+#define ARIZONA_IN3_OSR_MASK                     0x6000  /* IN3_OSR - [14:13] */
+#define ARIZONA_IN3_OSR_SHIFT                        13  /* IN3_OSR - [14:13] */
+#define ARIZONA_IN3_OSR_WIDTH                         2  /* IN3_OSR - [14:13] */
+#define ARIZONA_IN3_DMIC_SUP_MASK                0x1800  /* IN3_DMIC_SUP - [12:11] */
+#define ARIZONA_IN3_DMIC_SUP_SHIFT                   11  /* IN3_DMIC_SUP - [12:11] */
+#define ARIZONA_IN3_DMIC_SUP_WIDTH                    2  /* IN3_DMIC_SUP - [12:11] */
+#define ARIZONA_IN3_MODE_MASK                    0x0600  /* IN3_MODE - [10:9] */
+#define ARIZONA_IN3_MODE_SHIFT                        9  /* IN3_MODE - [10:9] */
+#define ARIZONA_IN3_MODE_WIDTH                        2  /* IN3_MODE - [10:9] */
+#define ARIZONA_IN3L_PGA_VOL_MASK                0x00FE  /* IN3L_PGA_VOL - [7:1] */
+#define ARIZONA_IN3L_PGA_VOL_SHIFT                    1  /* IN3L_PGA_VOL - [7:1] */
+#define ARIZONA_IN3L_PGA_VOL_WIDTH                    7  /* IN3L_PGA_VOL - [7:1] */
+
+/*
+ * R801 (0x321) - ADC Digital Volume 3L
+ */
+#define ARIZONA_IN_VU                            0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_MASK                       0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_SHIFT                           9  /* IN_VU */
+#define ARIZONA_IN_VU_WIDTH                           1  /* IN_VU */
+#define ARIZONA_IN3L_MUTE                        0x0100  /* IN3L_MUTE */
+#define ARIZONA_IN3L_MUTE_MASK                   0x0100  /* IN3L_MUTE */
+#define ARIZONA_IN3L_MUTE_SHIFT                       8  /* IN3L_MUTE */
+#define ARIZONA_IN3L_MUTE_WIDTH                       1  /* IN3L_MUTE */
+#define ARIZONA_IN3L_DIG_VOL_MASK                0x00FF  /* IN3L_DIG_VOL - [7:0] */
+#define ARIZONA_IN3L_DIG_VOL_SHIFT                    0  /* IN3L_DIG_VOL - [7:0] */
+#define ARIZONA_IN3L_DIG_VOL_WIDTH                    8  /* IN3L_DIG_VOL - [7:0] */
+
+/*
+ * R802 (0x322) - DMIC3L Control
+ */
+#define ARIZONA_IN3_DMICL_DLY_MASK               0x003F  /* IN3_DMICL_DLY - [5:0] */
+#define ARIZONA_IN3_DMICL_DLY_SHIFT                   0  /* IN3_DMICL_DLY - [5:0] */
+#define ARIZONA_IN3_DMICL_DLY_WIDTH                   6  /* IN3_DMICL_DLY - [5:0] */
+
+/*
+ * R804 (0x324) - IN3R Control
+ */
+#define ARIZONA_IN3R_PGA_VOL_MASK                0x00FE  /* IN3R_PGA_VOL - [7:1] */
+#define ARIZONA_IN3R_PGA_VOL_SHIFT                    1  /* IN3R_PGA_VOL - [7:1] */
+#define ARIZONA_IN3R_PGA_VOL_WIDTH                    7  /* IN3R_PGA_VOL - [7:1] */
+
+/*
+ * R805 (0x325) - ADC Digital Volume 3R
+ */
+#define ARIZONA_IN_VU                            0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_MASK                       0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_SHIFT                           9  /* IN_VU */
+#define ARIZONA_IN_VU_WIDTH                           1  /* IN_VU */
+#define ARIZONA_IN3R_MUTE                        0x0100  /* IN3R_MUTE */
+#define ARIZONA_IN3R_MUTE_MASK                   0x0100  /* IN3R_MUTE */
+#define ARIZONA_IN3R_MUTE_SHIFT                       8  /* IN3R_MUTE */
+#define ARIZONA_IN3R_MUTE_WIDTH                       1  /* IN3R_MUTE */
+#define ARIZONA_IN3R_DIG_VOL_MASK                0x00FF  /* IN3R_DIG_VOL - [7:0] */
+#define ARIZONA_IN3R_DIG_VOL_SHIFT                    0  /* IN3R_DIG_VOL - [7:0] */
+#define ARIZONA_IN3R_DIG_VOL_WIDTH                    8  /* IN3R_DIG_VOL - [7:0] */
+
+/*
+ * R806 (0x326) - DMIC3R Control
+ */
+#define ARIZONA_IN3_DMICR_DLY_MASK               0x003F  /* IN3_DMICR_DLY - [5:0] */
+#define ARIZONA_IN3_DMICR_DLY_SHIFT                   0  /* IN3_DMICR_DLY - [5:0] */
+#define ARIZONA_IN3_DMICR_DLY_WIDTH                   6  /* IN3_DMICR_DLY - [5:0] */
+
+/*
+ * R808 (0x328) - IN4 Control
+ */
+#define ARIZONA_IN4_OSR_MASK                     0x6000  /* IN4_OSR - [14:13] */
+#define ARIZONA_IN4_OSR_SHIFT                        13  /* IN4_OSR - [14:13] */
+#define ARIZONA_IN4_OSR_WIDTH                         2  /* IN4_OSR - [14:13] */
+#define ARIZONA_IN4_DMIC_SUP_MASK                0x1800  /* IN4_DMIC_SUP - [12:11] */
+#define ARIZONA_IN4_DMIC_SUP_SHIFT                   11  /* IN4_DMIC_SUP - [12:11] */
+#define ARIZONA_IN4_DMIC_SUP_WIDTH                    2  /* IN4_DMIC_SUP - [12:11] */
+
+/*
+ * R809 (0x329) - ADC Digital Volume 4L
+ */
+#define ARIZONA_IN_VU                            0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_MASK                       0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_SHIFT                           9  /* IN_VU */
+#define ARIZONA_IN_VU_WIDTH                           1  /* IN_VU */
+#define ARIZONA_IN4L_MUTE                        0x0100  /* IN4L_MUTE */
+#define ARIZONA_IN4L_MUTE_MASK                   0x0100  /* IN4L_MUTE */
+#define ARIZONA_IN4L_MUTE_SHIFT                       8  /* IN4L_MUTE */
+#define ARIZONA_IN4L_MUTE_WIDTH                       1  /* IN4L_MUTE */
+#define ARIZONA_IN4L_DIG_VOL_MASK                0x00FF  /* IN4L_DIG_VOL - [7:0] */
+#define ARIZONA_IN4L_DIG_VOL_SHIFT                    0  /* IN4L_DIG_VOL - [7:0] */
+#define ARIZONA_IN4L_DIG_VOL_WIDTH                    8  /* IN4L_DIG_VOL - [7:0] */
+
+/*
+ * R810 (0x32A) - DMIC4L Control
+ */
+#define ARIZONA_IN4L_DMIC_DLY_MASK               0x003F  /* IN4L_DMIC_DLY - [5:0] */
+#define ARIZONA_IN4L_DMIC_DLY_SHIFT                   0  /* IN4L_DMIC_DLY - [5:0] */
+#define ARIZONA_IN4L_DMIC_DLY_WIDTH                   6  /* IN4L_DMIC_DLY - [5:0] */
+
+/*
+ * R813 (0x32D) - ADC Digital Volume 4R
+ */
+#define ARIZONA_IN_VU                            0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_MASK                       0x0200  /* IN_VU */
+#define ARIZONA_IN_VU_SHIFT                           9  /* IN_VU */
+#define ARIZONA_IN_VU_WIDTH                           1  /* IN_VU */
+#define ARIZONA_IN4R_MUTE                        0x0100  /* IN4R_MUTE */
+#define ARIZONA_IN4R_MUTE_MASK                   0x0100  /* IN4R_MUTE */
+#define ARIZONA_IN4R_MUTE_SHIFT                       8  /* IN4R_MUTE */
+#define ARIZONA_IN4R_MUTE_WIDTH                       1  /* IN4R_MUTE */
+#define ARIZONA_IN4R_DIG_VOL_MASK                0x00FF  /* IN4R_DIG_VOL - [7:0] */
+#define ARIZONA_IN4R_DIG_VOL_SHIFT                    0  /* IN4R_DIG_VOL - [7:0] */
+#define ARIZONA_IN4R_DIG_VOL_WIDTH                    8  /* IN4R_DIG_VOL - [7:0] */
+
+/*
+ * R814 (0x32E) - DMIC4R Control
+ */
+#define ARIZONA_IN4R_DMIC_DLY_MASK               0x003F  /* IN4R_DMIC_DLY - [5:0] */
+#define ARIZONA_IN4R_DMIC_DLY_SHIFT                   0  /* IN4R_DMIC_DLY - [5:0] */
+#define ARIZONA_IN4R_DMIC_DLY_WIDTH                   6  /* IN4R_DMIC_DLY - [5:0] */
+
+/*
+ * R1024 (0x400) - Output Enables 1
+ */
+#define ARIZONA_OUT6L_ENA                        0x0800  /* OUT6L_ENA */
+#define ARIZONA_OUT6L_ENA_MASK                   0x0800  /* OUT6L_ENA */
+#define ARIZONA_OUT6L_ENA_SHIFT                      11  /* OUT6L_ENA */
+#define ARIZONA_OUT6L_ENA_WIDTH                       1  /* OUT6L_ENA */
+#define ARIZONA_OUT6R_ENA                        0x0400  /* OUT6R_ENA */
+#define ARIZONA_OUT6R_ENA_MASK                   0x0400  /* OUT6R_ENA */
+#define ARIZONA_OUT6R_ENA_SHIFT                      10  /* OUT6R_ENA */
+#define ARIZONA_OUT6R_ENA_WIDTH                       1  /* OUT6R_ENA */
+#define ARIZONA_OUT5L_ENA                        0x0200  /* OUT5L_ENA */
+#define ARIZONA_OUT5L_ENA_MASK                   0x0200  /* OUT5L_ENA */
+#define ARIZONA_OUT5L_ENA_SHIFT                       9  /* OUT5L_ENA */
+#define ARIZONA_OUT5L_ENA_WIDTH                       1  /* OUT5L_ENA */
+#define ARIZONA_OUT5R_ENA                        0x0100  /* OUT5R_ENA */
+#define ARIZONA_OUT5R_ENA_MASK                   0x0100  /* OUT5R_ENA */
+#define ARIZONA_OUT5R_ENA_SHIFT                       8  /* OUT5R_ENA */
+#define ARIZONA_OUT5R_ENA_WIDTH                       1  /* OUT5R_ENA */
+#define ARIZONA_OUT4L_ENA                        0x0080  /* OUT4L_ENA */
+#define ARIZONA_OUT4L_ENA_MASK                   0x0080  /* OUT4L_ENA */
+#define ARIZONA_OUT4L_ENA_SHIFT                       7  /* OUT4L_ENA */
+#define ARIZONA_OUT4L_ENA_WIDTH                       1  /* OUT4L_ENA */
+#define ARIZONA_OUT4R_ENA                        0x0040  /* OUT4R_ENA */
+#define ARIZONA_OUT4R_ENA_MASK                   0x0040  /* OUT4R_ENA */
+#define ARIZONA_OUT4R_ENA_SHIFT                       6  /* OUT4R_ENA */
+#define ARIZONA_OUT4R_ENA_WIDTH                       1  /* OUT4R_ENA */
+#define ARIZONA_OUT3L_ENA                        0x0020  /* OUT3L_ENA */
+#define ARIZONA_OUT3L_ENA_MASK                   0x0020  /* OUT3L_ENA */
+#define ARIZONA_OUT3L_ENA_SHIFT                       5  /* OUT3L_ENA */
+#define ARIZONA_OUT3L_ENA_WIDTH                       1  /* OUT3L_ENA */
+#define ARIZONA_OUT3R_ENA                        0x0010  /* OUT3R_ENA */
+#define ARIZONA_OUT3R_ENA_MASK                   0x0010  /* OUT3R_ENA */
+#define ARIZONA_OUT3R_ENA_SHIFT                       4  /* OUT3R_ENA */
+#define ARIZONA_OUT3R_ENA_WIDTH                       1  /* OUT3R_ENA */
+#define ARIZONA_OUT2L_ENA                        0x0008  /* OUT2L_ENA */
+#define ARIZONA_OUT2L_ENA_MASK                   0x0008  /* OUT2L_ENA */
+#define ARIZONA_OUT2L_ENA_SHIFT                       3  /* OUT2L_ENA */
+#define ARIZONA_OUT2L_ENA_WIDTH                       1  /* OUT2L_ENA */
+#define ARIZONA_OUT2R_ENA                        0x0004  /* OUT2R_ENA */
+#define ARIZONA_OUT2R_ENA_MASK                   0x0004  /* OUT2R_ENA */
+#define ARIZONA_OUT2R_ENA_SHIFT                       2  /* OUT2R_ENA */
+#define ARIZONA_OUT2R_ENA_WIDTH                       1  /* OUT2R_ENA */
+#define ARIZONA_OUT1L_ENA                        0x0002  /* OUT1L_ENA */
+#define ARIZONA_OUT1L_ENA_MASK                   0x0002  /* OUT1L_ENA */
+#define ARIZONA_OUT1L_ENA_SHIFT                       1  /* OUT1L_ENA */
+#define ARIZONA_OUT1L_ENA_WIDTH                       1  /* OUT1L_ENA */
+#define ARIZONA_OUT1R_ENA                        0x0001  /* OUT1R_ENA */
+#define ARIZONA_OUT1R_ENA_MASK                   0x0001  /* OUT1R_ENA */
+#define ARIZONA_OUT1R_ENA_SHIFT                       0  /* OUT1R_ENA */
+#define ARIZONA_OUT1R_ENA_WIDTH                       1  /* OUT1R_ENA */
+
+/*
+ * R1025 (0x401) - Output Status 1
+ */
+#define ARIZONA_OUT6L_ENA_STS                    0x0800  /* OUT6L_ENA_STS */
+#define ARIZONA_OUT6L_ENA_STS_MASK               0x0800  /* OUT6L_ENA_STS */
+#define ARIZONA_OUT6L_ENA_STS_SHIFT                  11  /* OUT6L_ENA_STS */
+#define ARIZONA_OUT6L_ENA_STS_WIDTH                   1  /* OUT6L_ENA_STS */
+#define ARIZONA_OUT6R_ENA_STS                    0x0400  /* OUT6R_ENA_STS */
+#define ARIZONA_OUT6R_ENA_STS_MASK               0x0400  /* OUT6R_ENA_STS */
+#define ARIZONA_OUT6R_ENA_STS_SHIFT                  10  /* OUT6R_ENA_STS */
+#define ARIZONA_OUT6R_ENA_STS_WIDTH                   1  /* OUT6R_ENA_STS */
+#define ARIZONA_OUT5L_ENA_STS                    0x0200  /* OUT5L_ENA_STS */
+#define ARIZONA_OUT5L_ENA_STS_MASK               0x0200  /* OUT5L_ENA_STS */
+#define ARIZONA_OUT5L_ENA_STS_SHIFT                   9  /* OUT5L_ENA_STS */
+#define ARIZONA_OUT5L_ENA_STS_WIDTH                   1  /* OUT5L_ENA_STS */
+#define ARIZONA_OUT5R_ENA_STS                    0x0100  /* OUT5R_ENA_STS */
+#define ARIZONA_OUT5R_ENA_STS_MASK               0x0100  /* OUT5R_ENA_STS */
+#define ARIZONA_OUT5R_ENA_STS_SHIFT                   8  /* OUT5R_ENA_STS */
+#define ARIZONA_OUT5R_ENA_STS_WIDTH                   1  /* OUT5R_ENA_STS */
+#define ARIZONA_OUT4L_ENA_STS                    0x0080  /* OUT4L_ENA_STS */
+#define ARIZONA_OUT4L_ENA_STS_MASK               0x0080  /* OUT4L_ENA_STS */
+#define ARIZONA_OUT4L_ENA_STS_SHIFT                   7  /* OUT4L_ENA_STS */
+#define ARIZONA_OUT4L_ENA_STS_WIDTH                   1  /* OUT4L_ENA_STS */
+#define ARIZONA_OUT4R_ENA_STS                    0x0040  /* OUT4R_ENA_STS */
+#define ARIZONA_OUT4R_ENA_STS_MASK               0x0040  /* OUT4R_ENA_STS */
+#define ARIZONA_OUT4R_ENA_STS_SHIFT                   6  /* OUT4R_ENA_STS */
+#define ARIZONA_OUT4R_ENA_STS_WIDTH                   1  /* OUT4R_ENA_STS */
+
+/*
+ * R1032 (0x408) - Output Rate 1
+ */
+#define ARIZONA_OUT_RATE_MASK                    0x7800  /* OUT_RATE - [14:11] */
+#define ARIZONA_OUT_RATE_SHIFT                       11  /* OUT_RATE - [14:11] */
+#define ARIZONA_OUT_RATE_WIDTH                        4  /* OUT_RATE - [14:11] */
+
+/*
+ * R1033 (0x409) - Output Volume Ramp
+ */
+#define ARIZONA_OUT_VD_RAMP_MASK                 0x0070  /* OUT_VD_RAMP - [6:4] */
+#define ARIZONA_OUT_VD_RAMP_SHIFT                     4  /* OUT_VD_RAMP - [6:4] */
+#define ARIZONA_OUT_VD_RAMP_WIDTH                     3  /* OUT_VD_RAMP - [6:4] */
+#define ARIZONA_OUT_VI_RAMP_MASK                 0x0007  /* OUT_VI_RAMP - [2:0] */
+#define ARIZONA_OUT_VI_RAMP_SHIFT                     0  /* OUT_VI_RAMP - [2:0] */
+#define ARIZONA_OUT_VI_RAMP_WIDTH                     3  /* OUT_VI_RAMP - [2:0] */
+
+/*
+ * R1040 (0x410) - Output Path Config 1L
+ */
+#define ARIZONA_OUT1_LP_MODE                     0x8000  /* OUT1_LP_MODE */
+#define ARIZONA_OUT1_LP_MODE_MASK                0x8000  /* OUT1_LP_MODE */
+#define ARIZONA_OUT1_LP_MODE_SHIFT                   15  /* OUT1_LP_MODE */
+#define ARIZONA_OUT1_LP_MODE_WIDTH                    1  /* OUT1_LP_MODE */
+#define ARIZONA_OUT1_OSR                         0x2000  /* OUT1_OSR */
+#define ARIZONA_OUT1_OSR_MASK                    0x2000  /* OUT1_OSR */
+#define ARIZONA_OUT1_OSR_SHIFT                       13  /* OUT1_OSR */
+#define ARIZONA_OUT1_OSR_WIDTH                        1  /* OUT1_OSR */
+#define ARIZONA_OUT1_MONO                        0x1000  /* OUT1_MONO */
+#define ARIZONA_OUT1_MONO_MASK                   0x1000  /* OUT1_MONO */
+#define ARIZONA_OUT1_MONO_SHIFT                      12  /* OUT1_MONO */
+#define ARIZONA_OUT1_MONO_WIDTH                       1  /* OUT1_MONO */
+#define ARIZONA_OUT1L_ANC_SRC_MASK               0x0C00  /* OUT1L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT1L_ANC_SRC_SHIFT                  10  /* OUT1L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT1L_ANC_SRC_WIDTH                   2  /* OUT1L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT1L_PGA_VOL_MASK               0x00FE  /* OUT1L_PGA_VOL - [7:1] */
+#define ARIZONA_OUT1L_PGA_VOL_SHIFT                   1  /* OUT1L_PGA_VOL - [7:1] */
+#define ARIZONA_OUT1L_PGA_VOL_WIDTH                   7  /* OUT1L_PGA_VOL - [7:1] */
+
+/*
+ * R1041 (0x411) - DAC Digital Volume 1L
+ */
+#define ARIZONA_OUT_VU                           0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_MASK                      0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_SHIFT                          9  /* OUT_VU */
+#define ARIZONA_OUT_VU_WIDTH                          1  /* OUT_VU */
+#define ARIZONA_OUT1L_MUTE                       0x0100  /* OUT1L_MUTE */
+#define ARIZONA_OUT1L_MUTE_MASK                  0x0100  /* OUT1L_MUTE */
+#define ARIZONA_OUT1L_MUTE_SHIFT                      8  /* OUT1L_MUTE */
+#define ARIZONA_OUT1L_MUTE_WIDTH                      1  /* OUT1L_MUTE */
+#define ARIZONA_OUT1L_VOL_MASK                   0x00FF  /* OUT1L_VOL - [7:0] */
+#define ARIZONA_OUT1L_VOL_SHIFT                       0  /* OUT1L_VOL - [7:0] */
+#define ARIZONA_OUT1L_VOL_WIDTH                       8  /* OUT1L_VOL - [7:0] */
+
+/*
+ * R1042 (0x412) - DAC Volume Limit 1L
+ */
+#define ARIZONA_OUT1L_VOL_LIM_MASK               0x00FF  /* OUT1L_VOL_LIM - [7:0] */
+#define ARIZONA_OUT1L_VOL_LIM_SHIFT                   0  /* OUT1L_VOL_LIM - [7:0] */
+#define ARIZONA_OUT1L_VOL_LIM_WIDTH                   8  /* OUT1L_VOL_LIM - [7:0] */
+
+/*
+ * R1043 (0x413) - Noise Gate Select 1L
+ */
+#define ARIZONA_OUT1L_NGATE_SRC_MASK             0x0FFF  /* OUT1L_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT1L_NGATE_SRC_SHIFT                 0  /* OUT1L_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT1L_NGATE_SRC_WIDTH                12  /* OUT1L_NGATE_SRC - [11:0] */
+
+/*
+ * R1044 (0x414) - Output Path Config 1R
+ */
+#define ARIZONA_OUT1R_ANC_SRC_MASK               0x0C00  /* OUT1R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT1R_ANC_SRC_SHIFT                  10  /* OUT1R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT1R_ANC_SRC_WIDTH                   2  /* OUT1R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT1R_PGA_VOL_MASK               0x00FE  /* OUT1R_PGA_VOL - [7:1] */
+#define ARIZONA_OUT1R_PGA_VOL_SHIFT                   1  /* OUT1R_PGA_VOL - [7:1] */
+#define ARIZONA_OUT1R_PGA_VOL_WIDTH                   7  /* OUT1R_PGA_VOL - [7:1] */
+
+/*
+ * R1045 (0x415) - DAC Digital Volume 1R
+ */
+#define ARIZONA_OUT_VU                           0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_MASK                      0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_SHIFT                          9  /* OUT_VU */
+#define ARIZONA_OUT_VU_WIDTH                          1  /* OUT_VU */
+#define ARIZONA_OUT1R_MUTE                       0x0100  /* OUT1R_MUTE */
+#define ARIZONA_OUT1R_MUTE_MASK                  0x0100  /* OUT1R_MUTE */
+#define ARIZONA_OUT1R_MUTE_SHIFT                      8  /* OUT1R_MUTE */
+#define ARIZONA_OUT1R_MUTE_WIDTH                      1  /* OUT1R_MUTE */
+#define ARIZONA_OUT1R_VOL_MASK                   0x00FF  /* OUT1R_VOL - [7:0] */
+#define ARIZONA_OUT1R_VOL_SHIFT                       0  /* OUT1R_VOL - [7:0] */
+#define ARIZONA_OUT1R_VOL_WIDTH                       8  /* OUT1R_VOL - [7:0] */
+
+/*
+ * R1046 (0x416) - DAC Volume Limit 1R
+ */
+#define ARIZONA_OUT1R_VOL_LIM_MASK               0x00FF  /* OUT1R_VOL_LIM - [7:0] */
+#define ARIZONA_OUT1R_VOL_LIM_SHIFT                   0  /* OUT1R_VOL_LIM - [7:0] */
+#define ARIZONA_OUT1R_VOL_LIM_WIDTH                   8  /* OUT1R_VOL_LIM - [7:0] */
+
+/*
+ * R1047 (0x417) - Noise Gate Select 1R
+ */
+#define ARIZONA_OUT1R_NGATE_SRC_MASK             0x0FFF  /* OUT1R_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT1R_NGATE_SRC_SHIFT                 0  /* OUT1R_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT1R_NGATE_SRC_WIDTH                12  /* OUT1R_NGATE_SRC - [11:0] */
+
+/*
+ * R1048 (0x418) - Output Path Config 2L
+ */
+#define ARIZONA_OUT2_LP_MODE                     0x8000  /* OUT2_LP_MODE */
+#define ARIZONA_OUT2_LP_MODE_MASK                0x8000  /* OUT2_LP_MODE */
+#define ARIZONA_OUT2_LP_MODE_SHIFT                   15  /* OUT2_LP_MODE */
+#define ARIZONA_OUT2_LP_MODE_WIDTH                    1  /* OUT2_LP_MODE */
+#define ARIZONA_OUT2_OSR                         0x2000  /* OUT2_OSR */
+#define ARIZONA_OUT2_OSR_MASK                    0x2000  /* OUT2_OSR */
+#define ARIZONA_OUT2_OSR_SHIFT                       13  /* OUT2_OSR */
+#define ARIZONA_OUT2_OSR_WIDTH                        1  /* OUT2_OSR */
+#define ARIZONA_OUT2_MONO                        0x1000  /* OUT2_MONO */
+#define ARIZONA_OUT2_MONO_MASK                   0x1000  /* OUT2_MONO */
+#define ARIZONA_OUT2_MONO_SHIFT                      12  /* OUT2_MONO */
+#define ARIZONA_OUT2_MONO_WIDTH                       1  /* OUT2_MONO */
+#define ARIZONA_OUT2L_ANC_SRC_MASK               0x0C00  /* OUT2L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT2L_ANC_SRC_SHIFT                  10  /* OUT2L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT2L_ANC_SRC_WIDTH                   2  /* OUT2L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT2L_PGA_VOL_MASK               0x00FE  /* OUT2L_PGA_VOL - [7:1] */
+#define ARIZONA_OUT2L_PGA_VOL_SHIFT                   1  /* OUT2L_PGA_VOL - [7:1] */
+#define ARIZONA_OUT2L_PGA_VOL_WIDTH                   7  /* OUT2L_PGA_VOL - [7:1] */
+
+/*
+ * R1049 (0x419) - DAC Digital Volume 2L
+ */
+#define ARIZONA_OUT_VU                           0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_MASK                      0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_SHIFT                          9  /* OUT_VU */
+#define ARIZONA_OUT_VU_WIDTH                          1  /* OUT_VU */
+#define ARIZONA_OUT2L_MUTE                       0x0100  /* OUT2L_MUTE */
+#define ARIZONA_OUT2L_MUTE_MASK                  0x0100  /* OUT2L_MUTE */
+#define ARIZONA_OUT2L_MUTE_SHIFT                      8  /* OUT2L_MUTE */
+#define ARIZONA_OUT2L_MUTE_WIDTH                      1  /* OUT2L_MUTE */
+#define ARIZONA_OUT2L_VOL_MASK                   0x00FF  /* OUT2L_VOL - [7:0] */
+#define ARIZONA_OUT2L_VOL_SHIFT                       0  /* OUT2L_VOL - [7:0] */
+#define ARIZONA_OUT2L_VOL_WIDTH                       8  /* OUT2L_VOL - [7:0] */
+
+/*
+ * R1050 (0x41A) - DAC Volume Limit 2L
+ */
+#define ARIZONA_OUT2L_VOL_LIM_MASK               0x00FF  /* OUT2L_VOL_LIM - [7:0] */
+#define ARIZONA_OUT2L_VOL_LIM_SHIFT                   0  /* OUT2L_VOL_LIM - [7:0] */
+#define ARIZONA_OUT2L_VOL_LIM_WIDTH                   8  /* OUT2L_VOL_LIM - [7:0] */
+
+/*
+ * R1051 (0x41B) - Noise Gate Select 2L
+ */
+#define ARIZONA_OUT2L_NGATE_SRC_MASK             0x0FFF  /* OUT2L_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT2L_NGATE_SRC_SHIFT                 0  /* OUT2L_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT2L_NGATE_SRC_WIDTH                12  /* OUT2L_NGATE_SRC - [11:0] */
+
+/*
+ * R1052 (0x41C) - Output Path Config 2R
+ */
+#define ARIZONA_OUT2R_ANC_SRC_MASK               0x0C00  /* OUT2R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT2R_ANC_SRC_SHIFT                  10  /* OUT2R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT2R_ANC_SRC_WIDTH                   2  /* OUT2R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT2R_PGA_VOL_MASK               0x00FE  /* OUT2R_PGA_VOL - [7:1] */
+#define ARIZONA_OUT2R_PGA_VOL_SHIFT                   1  /* OUT2R_PGA_VOL - [7:1] */
+#define ARIZONA_OUT2R_PGA_VOL_WIDTH                   7  /* OUT2R_PGA_VOL - [7:1] */
+
+/*
+ * R1053 (0x41D) - DAC Digital Volume 2R
+ */
+#define ARIZONA_OUT_VU                           0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_MASK                      0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_SHIFT                          9  /* OUT_VU */
+#define ARIZONA_OUT_VU_WIDTH                          1  /* OUT_VU */
+#define ARIZONA_OUT2R_MUTE                       0x0100  /* OUT2R_MUTE */
+#define ARIZONA_OUT2R_MUTE_MASK                  0x0100  /* OUT2R_MUTE */
+#define ARIZONA_OUT2R_MUTE_SHIFT                      8  /* OUT2R_MUTE */
+#define ARIZONA_OUT2R_MUTE_WIDTH                      1  /* OUT2R_MUTE */
+#define ARIZONA_OUT2R_VOL_MASK                   0x00FF  /* OUT2R_VOL - [7:0] */
+#define ARIZONA_OUT2R_VOL_SHIFT                       0  /* OUT2R_VOL - [7:0] */
+#define ARIZONA_OUT2R_VOL_WIDTH                       8  /* OUT2R_VOL - [7:0] */
+
+/*
+ * R1054 (0x41E) - DAC Volume Limit 2R
+ */
+#define ARIZONA_OUT2R_VOL_LIM_MASK               0x00FF  /* OUT2R_VOL_LIM - [7:0] */
+#define ARIZONA_OUT2R_VOL_LIM_SHIFT                   0  /* OUT2R_VOL_LIM - [7:0] */
+#define ARIZONA_OUT2R_VOL_LIM_WIDTH                   8  /* OUT2R_VOL_LIM - [7:0] */
+
+/*
+ * R1055 (0x41F) - Noise Gate Select 2R
+ */
+#define ARIZONA_OUT2R_NGATE_SRC_MASK             0x0FFF  /* OUT2R_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT2R_NGATE_SRC_SHIFT                 0  /* OUT2R_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT2R_NGATE_SRC_WIDTH                12  /* OUT2R_NGATE_SRC - [11:0] */
+
+/*
+ * R1056 (0x420) - Output Path Config 3L
+ */
+#define ARIZONA_OUT3_LP_MODE                     0x8000  /* OUT3_LP_MODE */
+#define ARIZONA_OUT3_LP_MODE_MASK                0x8000  /* OUT3_LP_MODE */
+#define ARIZONA_OUT3_LP_MODE_SHIFT                   15  /* OUT3_LP_MODE */
+#define ARIZONA_OUT3_LP_MODE_WIDTH                    1  /* OUT3_LP_MODE */
+#define ARIZONA_OUT3_OSR                         0x2000  /* OUT3_OSR */
+#define ARIZONA_OUT3_OSR_MASK                    0x2000  /* OUT3_OSR */
+#define ARIZONA_OUT3_OSR_SHIFT                       13  /* OUT3_OSR */
+#define ARIZONA_OUT3_OSR_WIDTH                        1  /* OUT3_OSR */
+#define ARIZONA_OUT3_MONO                        0x1000  /* OUT3_MONO */
+#define ARIZONA_OUT3_MONO_MASK                   0x1000  /* OUT3_MONO */
+#define ARIZONA_OUT3_MONO_SHIFT                      12  /* OUT3_MONO */
+#define ARIZONA_OUT3_MONO_WIDTH                       1  /* OUT3_MONO */
+#define ARIZONA_OUT3L_ANC_SRC_MASK               0x0C00  /* OUT3L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT3L_ANC_SRC_SHIFT                  10  /* OUT3L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT3L_ANC_SRC_WIDTH                   2  /* OUT3L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT3L_PGA_VOL_MASK               0x00FE  /* OUT3L_PGA_VOL - [7:1] */
+#define ARIZONA_OUT3L_PGA_VOL_SHIFT                   1  /* OUT3L_PGA_VOL - [7:1] */
+#define ARIZONA_OUT3L_PGA_VOL_WIDTH                   7  /* OUT3L_PGA_VOL - [7:1] */
+
+/*
+ * R1057 (0x421) - DAC Digital Volume 3L
+ */
+#define ARIZONA_OUT_VU                           0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_MASK                      0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_SHIFT                          9  /* OUT_VU */
+#define ARIZONA_OUT_VU_WIDTH                          1  /* OUT_VU */
+#define ARIZONA_OUT3L_MUTE                       0x0100  /* OUT3L_MUTE */
+#define ARIZONA_OUT3L_MUTE_MASK                  0x0100  /* OUT3L_MUTE */
+#define ARIZONA_OUT3L_MUTE_SHIFT                      8  /* OUT3L_MUTE */
+#define ARIZONA_OUT3L_MUTE_WIDTH                      1  /* OUT3L_MUTE */
+#define ARIZONA_OUT3L_VOL_MASK                   0x00FF  /* OUT3L_VOL - [7:0] */
+#define ARIZONA_OUT3L_VOL_SHIFT                       0  /* OUT3L_VOL - [7:0] */
+#define ARIZONA_OUT3L_VOL_WIDTH                       8  /* OUT3L_VOL - [7:0] */
+
+/*
+ * R1058 (0x422) - DAC Volume Limit 3L
+ */
+#define ARIZONA_OUT3L_VOL_LIM_MASK               0x00FF  /* OUT3L_VOL_LIM - [7:0] */
+#define ARIZONA_OUT3L_VOL_LIM_SHIFT                   0  /* OUT3L_VOL_LIM - [7:0] */
+#define ARIZONA_OUT3L_VOL_LIM_WIDTH                   8  /* OUT3L_VOL_LIM - [7:0] */
+
+/*
+ * R1059 (0x423) - Noise Gate Select 3L
+ */
+#define ARIZONA_OUT3_NGATE_SRC_MASK              0x0FFF  /* OUT3_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT3_NGATE_SRC_SHIFT                  0  /* OUT3_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT3_NGATE_SRC_WIDTH                 12  /* OUT3_NGATE_SRC - [11:0] */
+
+/*
+ * R1060 (0x424) - Output Path Config 3R
+ */
+#define ARIZONA_OUT3R_PGA_VOL_MASK               0x00FE  /* OUT3R_PGA_VOL - [7:1] */
+#define ARIZONA_OUT3R_PGA_VOL_SHIFT                   1  /* OUT3R_PGA_VOL - [7:1] */
+#define ARIZONA_OUT3R_PGA_VOL_WIDTH                   7  /* OUT3R_PGA_VOL - [7:1] */
+
+/*
+ * R1061 (0x425) - DAC Digital Volume 3R
+ */
+#define ARIZONA_OUT_VU                           0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_MASK                      0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_SHIFT                          9  /* OUT_VU */
+#define ARIZONA_OUT_VU_WIDTH                          1  /* OUT_VU */
+#define ARIZONA_OUT3R_MUTE                       0x0100  /* OUT3R_MUTE */
+#define ARIZONA_OUT3R_MUTE_MASK                  0x0100  /* OUT3R_MUTE */
+#define ARIZONA_OUT3R_MUTE_SHIFT                      8  /* OUT3R_MUTE */
+#define ARIZONA_OUT3R_MUTE_WIDTH                      1  /* OUT3R_MUTE */
+#define ARIZONA_OUT3R_VOL_MASK                   0x00FF  /* OUT3R_VOL - [7:0] */
+#define ARIZONA_OUT3R_VOL_SHIFT                       0  /* OUT3R_VOL - [7:0] */
+#define ARIZONA_OUT3R_VOL_WIDTH                       8  /* OUT3R_VOL - [7:0] */
+
+/*
+ * R1062 (0x426) - DAC Volume Limit 3R
+ */
+#define ARIZONA_OUT3R_ANC_SRC_MASK               0x0C00  /* OUT3R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT3R_ANC_SRC_SHIFT                  10  /* OUT3R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT3R_ANC_SRC_WIDTH                   2  /* OUT3R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT3R_VOL_LIM_MASK               0x00FF  /* OUT3R_VOL_LIM - [7:0] */
+#define ARIZONA_OUT3R_VOL_LIM_SHIFT                   0  /* OUT3R_VOL_LIM - [7:0] */
+#define ARIZONA_OUT3R_VOL_LIM_WIDTH                   8  /* OUT3R_VOL_LIM - [7:0] */
+
+/*
+ * R1064 (0x428) - Output Path Config 4L
+ */
+#define ARIZONA_OUT4_OSR                         0x2000  /* OUT4_OSR */
+#define ARIZONA_OUT4_OSR_MASK                    0x2000  /* OUT4_OSR */
+#define ARIZONA_OUT4_OSR_SHIFT                       13  /* OUT4_OSR */
+#define ARIZONA_OUT4_OSR_WIDTH                        1  /* OUT4_OSR */
+#define ARIZONA_OUT4L_ANC_SRC_MASK               0x0C00  /* OUT4L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT4L_ANC_SRC_SHIFT                  10  /* OUT4L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT4L_ANC_SRC_WIDTH                   2  /* OUT4L_ANC_SRC - [11:10] */
+
+/*
+ * R1065 (0x429) - DAC Digital Volume 4L
+ */
+#define ARIZONA_OUT_VU                           0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_MASK                      0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_SHIFT                          9  /* OUT_VU */
+#define ARIZONA_OUT_VU_WIDTH                          1  /* OUT_VU */
+#define ARIZONA_OUT4L_MUTE                       0x0100  /* OUT4L_MUTE */
+#define ARIZONA_OUT4L_MUTE_MASK                  0x0100  /* OUT4L_MUTE */
+#define ARIZONA_OUT4L_MUTE_SHIFT                      8  /* OUT4L_MUTE */
+#define ARIZONA_OUT4L_MUTE_WIDTH                      1  /* OUT4L_MUTE */
+#define ARIZONA_OUT4L_VOL_MASK                   0x00FF  /* OUT4L_VOL - [7:0] */
+#define ARIZONA_OUT4L_VOL_SHIFT                       0  /* OUT4L_VOL - [7:0] */
+#define ARIZONA_OUT4L_VOL_WIDTH                       8  /* OUT4L_VOL - [7:0] */
+
+/*
+ * R1066 (0x42A) - Out Volume 4L
+ */
+#define ARIZONA_OUT4L_VOL_LIM_MASK               0x00FF  /* OUT4L_VOL_LIM - [7:0] */
+#define ARIZONA_OUT4L_VOL_LIM_SHIFT                   0  /* OUT4L_VOL_LIM - [7:0] */
+#define ARIZONA_OUT4L_VOL_LIM_WIDTH                   8  /* OUT4L_VOL_LIM - [7:0] */
+
+/*
+ * R1067 (0x42B) - Noise Gate Select 4L
+ */
+#define ARIZONA_OUT4L_NGATE_SRC_MASK             0x0FFF  /* OUT4L_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT4L_NGATE_SRC_SHIFT                 0  /* OUT4L_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT4L_NGATE_SRC_WIDTH                12  /* OUT4L_NGATE_SRC - [11:0] */
+
+/*
+ * R1068 (0x42C) - Output Path Config 4R
+ */
+#define ARIZONA_OUT4R_ANC_SRC_MASK               0x0C00  /* OUT4R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT4R_ANC_SRC_SHIFT                  10  /* OUT4R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT4R_ANC_SRC_WIDTH                   2  /* OUT4R_ANC_SRC - [11:10] */
+
+/*
+ * R1069 (0x42D) - DAC Digital Volume 4R
+ */
+#define ARIZONA_OUT_VU                           0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_MASK                      0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_SHIFT                          9  /* OUT_VU */
+#define ARIZONA_OUT_VU_WIDTH                          1  /* OUT_VU */
+#define ARIZONA_OUT4R_MUTE                       0x0100  /* OUT4R_MUTE */
+#define ARIZONA_OUT4R_MUTE_MASK                  0x0100  /* OUT4R_MUTE */
+#define ARIZONA_OUT4R_MUTE_SHIFT                      8  /* OUT4R_MUTE */
+#define ARIZONA_OUT4R_MUTE_WIDTH                      1  /* OUT4R_MUTE */
+#define ARIZONA_OUT4R_VOL_MASK                   0x00FF  /* OUT4R_VOL - [7:0] */
+#define ARIZONA_OUT4R_VOL_SHIFT                       0  /* OUT4R_VOL - [7:0] */
+#define ARIZONA_OUT4R_VOL_WIDTH                       8  /* OUT4R_VOL - [7:0] */
+
+/*
+ * R1070 (0x42E) - Out Volume 4R
+ */
+#define ARIZONA_OUT4R_VOL_LIM_MASK               0x00FF  /* OUT4R_VOL_LIM - [7:0] */
+#define ARIZONA_OUT4R_VOL_LIM_SHIFT                   0  /* OUT4R_VOL_LIM - [7:0] */
+#define ARIZONA_OUT4R_VOL_LIM_WIDTH                   8  /* OUT4R_VOL_LIM - [7:0] */
+
+/*
+ * R1071 (0x42F) - Noise Gate Select 4R
+ */
+#define ARIZONA_OUT4R_NGATE_SRC_MASK             0x0FFF  /* OUT4R_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT4R_NGATE_SRC_SHIFT                 0  /* OUT4R_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT4R_NGATE_SRC_WIDTH                12  /* OUT4R_NGATE_SRC - [11:0] */
+
+/*
+ * R1072 (0x430) - Output Path Config 5L
+ */
+#define ARIZONA_OUT5_OSR                         0x2000  /* OUT5_OSR */
+#define ARIZONA_OUT5_OSR_MASK                    0x2000  /* OUT5_OSR */
+#define ARIZONA_OUT5_OSR_SHIFT                       13  /* OUT5_OSR */
+#define ARIZONA_OUT5_OSR_WIDTH                        1  /* OUT5_OSR */
+#define ARIZONA_OUT5L_ANC_SRC_MASK               0x0C00  /* OUT5L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT5L_ANC_SRC_SHIFT                  10  /* OUT5L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT5L_ANC_SRC_WIDTH                   2  /* OUT5L_ANC_SRC - [11:10] */
+
+/*
+ * R1073 (0x431) - DAC Digital Volume 5L
+ */
+#define ARIZONA_OUT_VU                           0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_MASK                      0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_SHIFT                          9  /* OUT_VU */
+#define ARIZONA_OUT_VU_WIDTH                          1  /* OUT_VU */
+#define ARIZONA_OUT5L_MUTE                       0x0100  /* OUT5L_MUTE */
+#define ARIZONA_OUT5L_MUTE_MASK                  0x0100  /* OUT5L_MUTE */
+#define ARIZONA_OUT5L_MUTE_SHIFT                      8  /* OUT5L_MUTE */
+#define ARIZONA_OUT5L_MUTE_WIDTH                      1  /* OUT5L_MUTE */
+#define ARIZONA_OUT5L_VOL_MASK                   0x00FF  /* OUT5L_VOL - [7:0] */
+#define ARIZONA_OUT5L_VOL_SHIFT                       0  /* OUT5L_VOL - [7:0] */
+#define ARIZONA_OUT5L_VOL_WIDTH                       8  /* OUT5L_VOL - [7:0] */
+
+/*
+ * R1074 (0x432) - DAC Volume Limit 5L
+ */
+#define ARIZONA_OUT5L_VOL_LIM_MASK               0x00FF  /* OUT5L_VOL_LIM - [7:0] */
+#define ARIZONA_OUT5L_VOL_LIM_SHIFT                   0  /* OUT5L_VOL_LIM - [7:0] */
+#define ARIZONA_OUT5L_VOL_LIM_WIDTH                   8  /* OUT5L_VOL_LIM - [7:0] */
+
+/*
+ * R1075 (0x433) - Noise Gate Select 5L
+ */
+#define ARIZONA_OUT5L_NGATE_SRC_MASK             0x0FFF  /* OUT5L_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT5L_NGATE_SRC_SHIFT                 0  /* OUT5L_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT5L_NGATE_SRC_WIDTH                12  /* OUT5L_NGATE_SRC - [11:0] */
+
+/*
+ * R1076 (0x434) - Output Path Config 5R
+ */
+#define ARIZONA_OUT5R_ANC_SRC_MASK               0x0C00  /* OUT5R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT5R_ANC_SRC_SHIFT                  10  /* OUT5R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT5R_ANC_SRC_WIDTH                   2  /* OUT5R_ANC_SRC - [11:10] */
+
+/*
+ * R1077 (0x435) - DAC Digital Volume 5R
+ */
+#define ARIZONA_OUT_VU                           0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_MASK                      0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_SHIFT                          9  /* OUT_VU */
+#define ARIZONA_OUT_VU_WIDTH                          1  /* OUT_VU */
+#define ARIZONA_OUT5R_MUTE                       0x0100  /* OUT5R_MUTE */
+#define ARIZONA_OUT5R_MUTE_MASK                  0x0100  /* OUT5R_MUTE */
+#define ARIZONA_OUT5R_MUTE_SHIFT                      8  /* OUT5R_MUTE */
+#define ARIZONA_OUT5R_MUTE_WIDTH                      1  /* OUT5R_MUTE */
+#define ARIZONA_OUT5R_VOL_MASK                   0x00FF  /* OUT5R_VOL - [7:0] */
+#define ARIZONA_OUT5R_VOL_SHIFT                       0  /* OUT5R_VOL - [7:0] */
+#define ARIZONA_OUT5R_VOL_WIDTH                       8  /* OUT5R_VOL - [7:0] */
+
+/*
+ * R1078 (0x436) - DAC Volume Limit 5R
+ */
+#define ARIZONA_OUT5R_VOL_LIM_MASK               0x00FF  /* OUT5R_VOL_LIM - [7:0] */
+#define ARIZONA_OUT5R_VOL_LIM_SHIFT                   0  /* OUT5R_VOL_LIM - [7:0] */
+#define ARIZONA_OUT5R_VOL_LIM_WIDTH                   8  /* OUT5R_VOL_LIM - [7:0] */
+
+/*
+ * R1079 (0x437) - Noise Gate Select 5R
+ */
+#define ARIZONA_OUT5R_NGATE_SRC_MASK             0x0FFF  /* OUT5R_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT5R_NGATE_SRC_SHIFT                 0  /* OUT5R_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT5R_NGATE_SRC_WIDTH                12  /* OUT5R_NGATE_SRC - [11:0] */
+
+/*
+ * R1080 (0x438) - Output Path Config 6L
+ */
+#define ARIZONA_OUT6_OSR                         0x2000  /* OUT6_OSR */
+#define ARIZONA_OUT6_OSR_MASK                    0x2000  /* OUT6_OSR */
+#define ARIZONA_OUT6_OSR_SHIFT                       13  /* OUT6_OSR */
+#define ARIZONA_OUT6_OSR_WIDTH                        1  /* OUT6_OSR */
+#define ARIZONA_OUT6L_ANC_SRC_MASK               0x0C00  /* OUT6L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT6L_ANC_SRC_SHIFT                  10  /* OUT6L_ANC_SRC - [11:10] */
+#define ARIZONA_OUT6L_ANC_SRC_WIDTH                   2  /* OUT6L_ANC_SRC - [11:10] */
+
+/*
+ * R1081 (0x439) - DAC Digital Volume 6L
+ */
+#define ARIZONA_OUT_VU                           0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_MASK                      0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_SHIFT                          9  /* OUT_VU */
+#define ARIZONA_OUT_VU_WIDTH                          1  /* OUT_VU */
+#define ARIZONA_OUT6L_MUTE                       0x0100  /* OUT6L_MUTE */
+#define ARIZONA_OUT6L_MUTE_MASK                  0x0100  /* OUT6L_MUTE */
+#define ARIZONA_OUT6L_MUTE_SHIFT                      8  /* OUT6L_MUTE */
+#define ARIZONA_OUT6L_MUTE_WIDTH                      1  /* OUT6L_MUTE */
+#define ARIZONA_OUT6L_VOL_MASK                   0x00FF  /* OUT6L_VOL - [7:0] */
+#define ARIZONA_OUT6L_VOL_SHIFT                       0  /* OUT6L_VOL - [7:0] */
+#define ARIZONA_OUT6L_VOL_WIDTH                       8  /* OUT6L_VOL - [7:0] */
+
+/*
+ * R1082 (0x43A) - DAC Volume Limit 6L
+ */
+#define ARIZONA_OUT6L_VOL_LIM_MASK               0x00FF  /* OUT6L_VOL_LIM - [7:0] */
+#define ARIZONA_OUT6L_VOL_LIM_SHIFT                   0  /* OUT6L_VOL_LIM - [7:0] */
+#define ARIZONA_OUT6L_VOL_LIM_WIDTH                   8  /* OUT6L_VOL_LIM - [7:0] */
+
+/*
+ * R1083 (0x43B) - Noise Gate Select 6L
+ */
+#define ARIZONA_OUT6L_NGATE_SRC_MASK             0x0FFF  /* OUT6L_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT6L_NGATE_SRC_SHIFT                 0  /* OUT6L_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT6L_NGATE_SRC_WIDTH                12  /* OUT6L_NGATE_SRC - [11:0] */
+
+/*
+ * R1084 (0x43C) - Output Path Config 6R
+ */
+#define ARIZONA_OUT6R_ANC_SRC_MASK               0x0C00  /* OUT6R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT6R_ANC_SRC_SHIFT                  10  /* OUT6R_ANC_SRC - [11:10] */
+#define ARIZONA_OUT6R_ANC_SRC_WIDTH                   2  /* OUT6R_ANC_SRC - [11:10] */
+
+/*
+ * R1085 (0x43D) - DAC Digital Volume 6R
+ */
+#define ARIZONA_OUT_VU                           0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_MASK                      0x0200  /* OUT_VU */
+#define ARIZONA_OUT_VU_SHIFT                          9  /* OUT_VU */
+#define ARIZONA_OUT_VU_WIDTH                          1  /* OUT_VU */
+#define ARIZONA_OUT6R_MUTE                       0x0100  /* OUT6R_MUTE */
+#define ARIZONA_OUT6R_MUTE_MASK                  0x0100  /* OUT6R_MUTE */
+#define ARIZONA_OUT6R_MUTE_SHIFT                      8  /* OUT6R_MUTE */
+#define ARIZONA_OUT6R_MUTE_WIDTH                      1  /* OUT6R_MUTE */
+#define ARIZONA_OUT6R_VOL_MASK                   0x00FF  /* OUT6R_VOL - [7:0] */
+#define ARIZONA_OUT6R_VOL_SHIFT                       0  /* OUT6R_VOL - [7:0] */
+#define ARIZONA_OUT6R_VOL_WIDTH                       8  /* OUT6R_VOL - [7:0] */
+
+/*
+ * R1086 (0x43E) - DAC Volume Limit 6R
+ */
+#define ARIZONA_OUT6R_VOL_LIM_MASK               0x00FF  /* OUT6R_VOL_LIM - [7:0] */
+#define ARIZONA_OUT6R_VOL_LIM_SHIFT                   0  /* OUT6R_VOL_LIM - [7:0] */
+#define ARIZONA_OUT6R_VOL_LIM_WIDTH                   8  /* OUT6R_VOL_LIM - [7:0] */
+
+/*
+ * R1087 (0x43F) - Noise Gate Select 6R
+ */
+#define ARIZONA_OUT6R_NGATE_SRC_MASK             0x0FFF  /* OUT6R_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT6R_NGATE_SRC_SHIFT                 0  /* OUT6R_NGATE_SRC - [11:0] */
+#define ARIZONA_OUT6R_NGATE_SRC_WIDTH                12  /* OUT6R_NGATE_SRC - [11:0] */
+
+/*
+ * R1104 (0x450) - DAC AEC Control 1
+ */
+#define ARIZONA_AEC_LOOPBACK_SRC_MASK            0x003C  /* AEC_LOOPBACK_SRC - [5:2] */
+#define ARIZONA_AEC_LOOPBACK_SRC_SHIFT                2  /* AEC_LOOPBACK_SRC - [5:2] */
+#define ARIZONA_AEC_LOOPBACK_SRC_WIDTH                4  /* AEC_LOOPBACK_SRC - [5:2] */
+#define ARIZONA_AEC_ENA_STS                      0x0002  /* AEC_ENA_STS */
+#define ARIZONA_AEC_ENA_STS_MASK                 0x0002  /* AEC_ENA_STS */
+#define ARIZONA_AEC_ENA_STS_SHIFT                     1  /* AEC_ENA_STS */
+#define ARIZONA_AEC_ENA_STS_WIDTH                     1  /* AEC_ENA_STS */
+#define ARIZONA_AEC_LOOPBACK_ENA                 0x0001  /* AEC_LOOPBACK_ENA */
+#define ARIZONA_AEC_LOOPBACK_ENA_MASK            0x0001  /* AEC_LOOPBACK_ENA */
+#define ARIZONA_AEC_LOOPBACK_ENA_SHIFT                0  /* AEC_LOOPBACK_ENA */
+#define ARIZONA_AEC_LOOPBACK_ENA_WIDTH                1  /* AEC_LOOPBACK_ENA */
+
+/*
+ * R1112 (0x458) - Noise Gate Control
+ */
+#define ARIZONA_NGATE_HOLD_MASK                  0x0030  /* NGATE_HOLD - [5:4] */
+#define ARIZONA_NGATE_HOLD_SHIFT                      4  /* NGATE_HOLD - [5:4] */
+#define ARIZONA_NGATE_HOLD_WIDTH                      2  /* NGATE_HOLD - [5:4] */
+#define ARIZONA_NGATE_THR_MASK                   0x000E  /* NGATE_THR - [3:1] */
+#define ARIZONA_NGATE_THR_SHIFT                       1  /* NGATE_THR - [3:1] */
+#define ARIZONA_NGATE_THR_WIDTH                       3  /* NGATE_THR - [3:1] */
+#define ARIZONA_NGATE_ENA                        0x0001  /* NGATE_ENA */
+#define ARIZONA_NGATE_ENA_MASK                   0x0001  /* NGATE_ENA */
+#define ARIZONA_NGATE_ENA_SHIFT                       0  /* NGATE_ENA */
+#define ARIZONA_NGATE_ENA_WIDTH                       1  /* NGATE_ENA */
+
+/*
+ * R1168 (0x490) - PDM SPK1 CTRL 1
+ */
+#define ARIZONA_SPK1R_MUTE                       0x2000  /* SPK1R_MUTE */
+#define ARIZONA_SPK1R_MUTE_MASK                  0x2000  /* SPK1R_MUTE */
+#define ARIZONA_SPK1R_MUTE_SHIFT                     13  /* SPK1R_MUTE */
+#define ARIZONA_SPK1R_MUTE_WIDTH                      1  /* SPK1R_MUTE */
+#define ARIZONA_SPK1L_MUTE                       0x1000  /* SPK1L_MUTE */
+#define ARIZONA_SPK1L_MUTE_MASK                  0x1000  /* SPK1L_MUTE */
+#define ARIZONA_SPK1L_MUTE_SHIFT                     12  /* SPK1L_MUTE */
+#define ARIZONA_SPK1L_MUTE_WIDTH                      1  /* SPK1L_MUTE */
+#define ARIZONA_SPK1_MUTE_ENDIAN                 0x0100  /* SPK1_MUTE_ENDIAN */
+#define ARIZONA_SPK1_MUTE_ENDIAN_MASK            0x0100  /* SPK1_MUTE_ENDIAN */
+#define ARIZONA_SPK1_MUTE_ENDIAN_SHIFT                8  /* SPK1_MUTE_ENDIAN */
+#define ARIZONA_SPK1_MUTE_ENDIAN_WIDTH                1  /* SPK1_MUTE_ENDIAN */
+#define ARIZONA_SPK1_MUTE_SEQ1_MASK              0x00FF  /* SPK1_MUTE_SEQ1 - [7:0] */
+#define ARIZONA_SPK1_MUTE_SEQ1_SHIFT                  0  /* SPK1_MUTE_SEQ1 - [7:0] */
+#define ARIZONA_SPK1_MUTE_SEQ1_WIDTH                  8  /* SPK1_MUTE_SEQ1 - [7:0] */
+
+/*
+ * R1169 (0x491) - PDM SPK1 CTRL 2
+ */
+#define ARIZONA_SPK1_FMT                         0x0001  /* SPK1_FMT */
+#define ARIZONA_SPK1_FMT_MASK                    0x0001  /* SPK1_FMT */
+#define ARIZONA_SPK1_FMT_SHIFT                        0  /* SPK1_FMT */
+#define ARIZONA_SPK1_FMT_WIDTH                        1  /* SPK1_FMT */
+
+/*
+ * R1170 (0x492) - PDM SPK2 CTRL 1
+ */
+#define ARIZONA_SPK2R_MUTE                       0x2000  /* SPK2R_MUTE */
+#define ARIZONA_SPK2R_MUTE_MASK                  0x2000  /* SPK2R_MUTE */
+#define ARIZONA_SPK2R_MUTE_SHIFT                     13  /* SPK2R_MUTE */
+#define ARIZONA_SPK2R_MUTE_WIDTH                      1  /* SPK2R_MUTE */
+#define ARIZONA_SPK2L_MUTE                       0x1000  /* SPK2L_MUTE */
+#define ARIZONA_SPK2L_MUTE_MASK                  0x1000  /* SPK2L_MUTE */
+#define ARIZONA_SPK2L_MUTE_SHIFT                     12  /* SPK2L_MUTE */
+#define ARIZONA_SPK2L_MUTE_WIDTH                      1  /* SPK2L_MUTE */
+#define ARIZONA_SPK2_MUTE_ENDIAN                 0x0100  /* SPK2_MUTE_ENDIAN */
+#define ARIZONA_SPK2_MUTE_ENDIAN_MASK            0x0100  /* SPK2_MUTE_ENDIAN */
+#define ARIZONA_SPK2_MUTE_ENDIAN_SHIFT                8  /* SPK2_MUTE_ENDIAN */
+#define ARIZONA_SPK2_MUTE_ENDIAN_WIDTH                1  /* SPK2_MUTE_ENDIAN */
+#define ARIZONA_SPK2_MUTE_SEQ_MASK               0x00FF  /* SPK2_MUTE_SEQ - [7:0] */
+#define ARIZONA_SPK2_MUTE_SEQ_SHIFT                   0  /* SPK2_MUTE_SEQ - [7:0] */
+#define ARIZONA_SPK2_MUTE_SEQ_WIDTH                   8  /* SPK2_MUTE_SEQ - [7:0] */
+
+/*
+ * R1171 (0x493) - PDM SPK2 CTRL 2
+ */
+#define ARIZONA_SPK2_FMT                         0x0001  /* SPK2_FMT */
+#define ARIZONA_SPK2_FMT_MASK                    0x0001  /* SPK2_FMT */
+#define ARIZONA_SPK2_FMT_SHIFT                        0  /* SPK2_FMT */
+#define ARIZONA_SPK2_FMT_WIDTH                        1  /* SPK2_FMT */
+
+/*
+ * R1244 (0x4DC) - DAC comp 1
+ */
+#define ARIZONA_OUT_COMP_COEFF_MASK              0xFFFF  /* OUT_COMP_COEFF - [15:0] */
+#define ARIZONA_OUT_COMP_COEFF_SHIFT                  0  /* OUT_COMP_COEFF - [15:0] */
+#define ARIZONA_OUT_COMP_COEFF_WIDTH                 16  /* OUT_COMP_COEFF - [15:0] */
+
+/*
+ * R1245 (0x4DD) - DAC comp 2
+ */
+#define ARIZONA_OUT_COMP_COEFF_1                 0x0002  /* OUT_COMP_COEFF */
+#define ARIZONA_OUT_COMP_COEFF_1_MASK            0x0002  /* OUT_COMP_COEFF */
+#define ARIZONA_OUT_COMP_COEFF_1_SHIFT                1  /* OUT_COMP_COEFF */
+#define ARIZONA_OUT_COMP_COEFF_1_WIDTH                1  /* OUT_COMP_COEFF */
+#define ARIZONA_OUT_COMP_COEFF_SEL               0x0001  /* OUT_COMP_COEFF_SEL */
+#define ARIZONA_OUT_COMP_COEFF_SEL_MASK          0x0001  /* OUT_COMP_COEFF_SEL */
+#define ARIZONA_OUT_COMP_COEFF_SEL_SHIFT              0  /* OUT_COMP_COEFF_SEL */
+#define ARIZONA_OUT_COMP_COEFF_SEL_WIDTH              1  /* OUT_COMP_COEFF_SEL */
+
+/*
+ * R1246 (0x4DE) - DAC comp 3
+ */
+#define ARIZONA_AEC_COMP_COEFF_MASK              0xFFFF  /* AEC_COMP_COEFF - [15:0] */
+#define ARIZONA_AEC_COMP_COEFF_SHIFT                  0  /* AEC_COMP_COEFF - [15:0] */
+#define ARIZONA_AEC_COMP_COEFF_WIDTH                 16  /* AEC_COMP_COEFF - [15:0] */
+
+/*
+ * R1247 (0x4DF) - DAC comp 4
+ */
+#define ARIZONA_AEC_COMP_COEFF_1                 0x0002  /* AEC_COMP_COEFF */
+#define ARIZONA_AEC_COMP_COEFF_1_MASK            0x0002  /* AEC_COMP_COEFF */
+#define ARIZONA_AEC_COMP_COEFF_1_SHIFT                1  /* AEC_COMP_COEFF */
+#define ARIZONA_AEC_COMP_COEFF_1_WIDTH                1  /* AEC_COMP_COEFF */
+#define ARIZONA_AEC_COMP_COEFF_SEL               0x0001  /* AEC_COMP_COEFF_SEL */
+#define ARIZONA_AEC_COMP_COEFF_SEL_MASK          0x0001  /* AEC_COMP_COEFF_SEL */
+#define ARIZONA_AEC_COMP_COEFF_SEL_SHIFT              0  /* AEC_COMP_COEFF_SEL */
+#define ARIZONA_AEC_COMP_COEFF_SEL_WIDTH              1  /* AEC_COMP_COEFF_SEL */
+
+/*
+ * R1280 (0x500) - AIF1 BCLK Ctrl
+ */
+#define ARIZONA_AIF1_BCLK_INV                    0x0080  /* AIF1_BCLK_INV */
+#define ARIZONA_AIF1_BCLK_INV_MASK               0x0080  /* AIF1_BCLK_INV */
+#define ARIZONA_AIF1_BCLK_INV_SHIFT                   7  /* AIF1_BCLK_INV */
+#define ARIZONA_AIF1_BCLK_INV_WIDTH                   1  /* AIF1_BCLK_INV */
+#define ARIZONA_AIF1_BCLK_FRC                    0x0040  /* AIF1_BCLK_FRC */
+#define ARIZONA_AIF1_BCLK_FRC_MASK               0x0040  /* AIF1_BCLK_FRC */
+#define ARIZONA_AIF1_BCLK_FRC_SHIFT                   6  /* AIF1_BCLK_FRC */
+#define ARIZONA_AIF1_BCLK_FRC_WIDTH                   1  /* AIF1_BCLK_FRC */
+#define ARIZONA_AIF1_BCLK_MSTR                   0x0020  /* AIF1_BCLK_MSTR */
+#define ARIZONA_AIF1_BCLK_MSTR_MASK              0x0020  /* AIF1_BCLK_MSTR */
+#define ARIZONA_AIF1_BCLK_MSTR_SHIFT                  5  /* AIF1_BCLK_MSTR */
+#define ARIZONA_AIF1_BCLK_MSTR_WIDTH                  1  /* AIF1_BCLK_MSTR */
+#define ARIZONA_AIF1_BCLK_FREQ_MASK              0x001F  /* AIF1_BCLK_FREQ - [4:0] */
+#define ARIZONA_AIF1_BCLK_FREQ_SHIFT                  0  /* AIF1_BCLK_FREQ - [4:0] */
+#define ARIZONA_AIF1_BCLK_FREQ_WIDTH                  5  /* AIF1_BCLK_FREQ - [4:0] */
+
+/*
+ * R1281 (0x501) - AIF1 Tx Pin Ctrl
+ */
+#define ARIZONA_AIF1TX_DAT_TRI                   0x0020  /* AIF1TX_DAT_TRI */
+#define ARIZONA_AIF1TX_DAT_TRI_MASK              0x0020  /* AIF1TX_DAT_TRI */
+#define ARIZONA_AIF1TX_DAT_TRI_SHIFT                  5  /* AIF1TX_DAT_TRI */
+#define ARIZONA_AIF1TX_DAT_TRI_WIDTH                  1  /* AIF1TX_DAT_TRI */
+#define ARIZONA_AIF1TX_LRCLK_SRC                 0x0008  /* AIF1TX_LRCLK_SRC */
+#define ARIZONA_AIF1TX_LRCLK_SRC_MASK            0x0008  /* AIF1TX_LRCLK_SRC */
+#define ARIZONA_AIF1TX_LRCLK_SRC_SHIFT                3  /* AIF1TX_LRCLK_SRC */
+#define ARIZONA_AIF1TX_LRCLK_SRC_WIDTH                1  /* AIF1TX_LRCLK_SRC */
+#define ARIZONA_AIF1TX_LRCLK_INV                 0x0004  /* AIF1TX_LRCLK_INV */
+#define ARIZONA_AIF1TX_LRCLK_INV_MASK            0x0004  /* AIF1TX_LRCLK_INV */
+#define ARIZONA_AIF1TX_LRCLK_INV_SHIFT                2  /* AIF1TX_LRCLK_INV */
+#define ARIZONA_AIF1TX_LRCLK_INV_WIDTH                1  /* AIF1TX_LRCLK_INV */
+#define ARIZONA_AIF1TX_LRCLK_FRC                 0x0002  /* AIF1TX_LRCLK_FRC */
+#define ARIZONA_AIF1TX_LRCLK_FRC_MASK            0x0002  /* AIF1TX_LRCLK_FRC */
+#define ARIZONA_AIF1TX_LRCLK_FRC_SHIFT                1  /* AIF1TX_LRCLK_FRC */
+#define ARIZONA_AIF1TX_LRCLK_FRC_WIDTH                1  /* AIF1TX_LRCLK_FRC */
+#define ARIZONA_AIF1TX_LRCLK_MSTR                0x0001  /* AIF1TX_LRCLK_MSTR */
+#define ARIZONA_AIF1TX_LRCLK_MSTR_MASK           0x0001  /* AIF1TX_LRCLK_MSTR */
+#define ARIZONA_AIF1TX_LRCLK_MSTR_SHIFT               0  /* AIF1TX_LRCLK_MSTR */
+#define ARIZONA_AIF1TX_LRCLK_MSTR_WIDTH               1  /* AIF1TX_LRCLK_MSTR */
+
+/*
+ * R1282 (0x502) - AIF1 Rx Pin Ctrl
+ */
+#define ARIZONA_AIF1RX_LRCLK_INV                 0x0004  /* AIF1RX_LRCLK_INV */
+#define ARIZONA_AIF1RX_LRCLK_INV_MASK            0x0004  /* AIF1RX_LRCLK_INV */
+#define ARIZONA_AIF1RX_LRCLK_INV_SHIFT                2  /* AIF1RX_LRCLK_INV */
+#define ARIZONA_AIF1RX_LRCLK_INV_WIDTH                1  /* AIF1RX_LRCLK_INV */
+#define ARIZONA_AIF1RX_LRCLK_FRC                 0x0002  /* AIF1RX_LRCLK_FRC */
+#define ARIZONA_AIF1RX_LRCLK_FRC_MASK            0x0002  /* AIF1RX_LRCLK_FRC */
+#define ARIZONA_AIF1RX_LRCLK_FRC_SHIFT                1  /* AIF1RX_LRCLK_FRC */
+#define ARIZONA_AIF1RX_LRCLK_FRC_WIDTH                1  /* AIF1RX_LRCLK_FRC */
+#define ARIZONA_AIF1RX_LRCLK_MSTR                0x0001  /* AIF1RX_LRCLK_MSTR */
+#define ARIZONA_AIF1RX_LRCLK_MSTR_MASK           0x0001  /* AIF1RX_LRCLK_MSTR */
+#define ARIZONA_AIF1RX_LRCLK_MSTR_SHIFT               0  /* AIF1RX_LRCLK_MSTR */
+#define ARIZONA_AIF1RX_LRCLK_MSTR_WIDTH               1  /* AIF1RX_LRCLK_MSTR */
+
+/*
+ * R1283 (0x503) - AIF1 Rate Ctrl
+ */
+#define ARIZONA_AIF1_RATE_MASK                   0x7800  /* AIF1_RATE - [14:11] */
+#define ARIZONA_AIF1_RATE_SHIFT                      11  /* AIF1_RATE - [14:11] */
+#define ARIZONA_AIF1_RATE_WIDTH                       4  /* AIF1_RATE - [14:11] */
+#define ARIZONA_AIF1_TRI                         0x0040  /* AIF1_TRI */
+#define ARIZONA_AIF1_TRI_MASK                    0x0040  /* AIF1_TRI */
+#define ARIZONA_AIF1_TRI_SHIFT                        6  /* AIF1_TRI */
+#define ARIZONA_AIF1_TRI_WIDTH                        1  /* AIF1_TRI */
+
+/*
+ * R1284 (0x504) - AIF1 Format
+ */
+#define ARIZONA_AIF1_FMT_MASK                    0x0007  /* AIF1_FMT - [2:0] */
+#define ARIZONA_AIF1_FMT_SHIFT                        0  /* AIF1_FMT - [2:0] */
+#define ARIZONA_AIF1_FMT_WIDTH                        3  /* AIF1_FMT - [2:0] */
+
+/*
+ * R1285 (0x505) - AIF1 Tx BCLK Rate
+ */
+#define ARIZONA_AIF1TX_BCPF_MASK                 0x1FFF  /* AIF1TX_BCPF - [12:0] */
+#define ARIZONA_AIF1TX_BCPF_SHIFT                     0  /* AIF1TX_BCPF - [12:0] */
+#define ARIZONA_AIF1TX_BCPF_WIDTH                    13  /* AIF1TX_BCPF - [12:0] */
+
+/*
+ * R1286 (0x506) - AIF1 Rx BCLK Rate
+ */
+#define ARIZONA_AIF1RX_BCPF_MASK                 0x1FFF  /* AIF1RX_BCPF - [12:0] */
+#define ARIZONA_AIF1RX_BCPF_SHIFT                     0  /* AIF1RX_BCPF - [12:0] */
+#define ARIZONA_AIF1RX_BCPF_WIDTH                    13  /* AIF1RX_BCPF - [12:0] */
+
+/*
+ * R1287 (0x507) - AIF1 Frame Ctrl 1
+ */
+#define ARIZONA_AIF1TX_WL_MASK                   0x3F00  /* AIF1TX_WL - [13:8] */
+#define ARIZONA_AIF1TX_WL_SHIFT                       8  /* AIF1TX_WL - [13:8] */
+#define ARIZONA_AIF1TX_WL_WIDTH                       6  /* AIF1TX_WL - [13:8] */
+#define ARIZONA_AIF1TX_SLOT_LEN_MASK             0x00FF  /* AIF1TX_SLOT_LEN - [7:0] */
+#define ARIZONA_AIF1TX_SLOT_LEN_SHIFT                 0  /* AIF1TX_SLOT_LEN - [7:0] */
+#define ARIZONA_AIF1TX_SLOT_LEN_WIDTH                 8  /* AIF1TX_SLOT_LEN - [7:0] */
+
+/*
+ * R1288 (0x508) - AIF1 Frame Ctrl 2
+ */
+#define ARIZONA_AIF1RX_WL_MASK                   0x3F00  /* AIF1RX_WL - [13:8] */
+#define ARIZONA_AIF1RX_WL_SHIFT                       8  /* AIF1RX_WL - [13:8] */
+#define ARIZONA_AIF1RX_WL_WIDTH                       6  /* AIF1RX_WL - [13:8] */
+#define ARIZONA_AIF1RX_SLOT_LEN_MASK             0x00FF  /* AIF1RX_SLOT_LEN - [7:0] */
+#define ARIZONA_AIF1RX_SLOT_LEN_SHIFT                 0  /* AIF1RX_SLOT_LEN - [7:0] */
+#define ARIZONA_AIF1RX_SLOT_LEN_WIDTH                 8  /* AIF1RX_SLOT_LEN - [7:0] */
+
+/*
+ * R1289 (0x509) - AIF1 Frame Ctrl 3
+ */
+#define ARIZONA_AIF1TX1_SLOT_MASK                0x003F  /* AIF1TX1_SLOT - [5:0] */
+#define ARIZONA_AIF1TX1_SLOT_SHIFT                    0  /* AIF1TX1_SLOT - [5:0] */
+#define ARIZONA_AIF1TX1_SLOT_WIDTH                    6  /* AIF1TX1_SLOT - [5:0] */
+
+/*
+ * R1290 (0x50A) - AIF1 Frame Ctrl 4
+ */
+#define ARIZONA_AIF1TX2_SLOT_MASK                0x003F  /* AIF1TX2_SLOT - [5:0] */
+#define ARIZONA_AIF1TX2_SLOT_SHIFT                    0  /* AIF1TX2_SLOT - [5:0] */
+#define ARIZONA_AIF1TX2_SLOT_WIDTH                    6  /* AIF1TX2_SLOT - [5:0] */
+
+/*
+ * R1291 (0x50B) - AIF1 Frame Ctrl 5
+ */
+#define ARIZONA_AIF1TX3_SLOT_MASK                0x003F  /* AIF1TX3_SLOT - [5:0] */
+#define ARIZONA_AIF1TX3_SLOT_SHIFT                    0  /* AIF1TX3_SLOT - [5:0] */
+#define ARIZONA_AIF1TX3_SLOT_WIDTH                    6  /* AIF1TX3_SLOT - [5:0] */
+
+/*
+ * R1292 (0x50C) - AIF1 Frame Ctrl 6
+ */
+#define ARIZONA_AIF1TX4_SLOT_MASK                0x003F  /* AIF1TX4_SLOT - [5:0] */
+#define ARIZONA_AIF1TX4_SLOT_SHIFT                    0  /* AIF1TX4_SLOT - [5:0] */
+#define ARIZONA_AIF1TX4_SLOT_WIDTH                    6  /* AIF1TX4_SLOT - [5:0] */
+
+/*
+ * R1293 (0x50D) - AIF1 Frame Ctrl 7
+ */
+#define ARIZONA_AIF1TX5_SLOT_MASK                0x003F  /* AIF1TX5_SLOT - [5:0] */
+#define ARIZONA_AIF1TX5_SLOT_SHIFT                    0  /* AIF1TX5_SLOT - [5:0] */
+#define ARIZONA_AIF1TX5_SLOT_WIDTH                    6  /* AIF1TX5_SLOT - [5:0] */
+
+/*
+ * R1294 (0x50E) - AIF1 Frame Ctrl 8
+ */
+#define ARIZONA_AIF1TX6_SLOT_MASK                0x003F  /* AIF1TX6_SLOT - [5:0] */
+#define ARIZONA_AIF1TX6_SLOT_SHIFT                    0  /* AIF1TX6_SLOT - [5:0] */
+#define ARIZONA_AIF1TX6_SLOT_WIDTH                    6  /* AIF1TX6_SLOT - [5:0] */
+
+/*
+ * R1295 (0x50F) - AIF1 Frame Ctrl 9
+ */
+#define ARIZONA_AIF1TX7_SLOT_MASK                0x003F  /* AIF1TX7_SLOT - [5:0] */
+#define ARIZONA_AIF1TX7_SLOT_SHIFT                    0  /* AIF1TX7_SLOT - [5:0] */
+#define ARIZONA_AIF1TX7_SLOT_WIDTH                    6  /* AIF1TX7_SLOT - [5:0] */
+
+/*
+ * R1296 (0x510) - AIF1 Frame Ctrl 10
+ */
+#define ARIZONA_AIF1TX8_SLOT_MASK                0x003F  /* AIF1TX8_SLOT - [5:0] */
+#define ARIZONA_AIF1TX8_SLOT_SHIFT                    0  /* AIF1TX8_SLOT - [5:0] */
+#define ARIZONA_AIF1TX8_SLOT_WIDTH                    6  /* AIF1TX8_SLOT - [5:0] */
+
+/*
+ * R1297 (0x511) - AIF1 Frame Ctrl 11
+ */
+#define ARIZONA_AIF1RX1_SLOT_MASK                0x003F  /* AIF1RX1_SLOT - [5:0] */
+#define ARIZONA_AIF1RX1_SLOT_SHIFT                    0  /* AIF1RX1_SLOT - [5:0] */
+#define ARIZONA_AIF1RX1_SLOT_WIDTH                    6  /* AIF1RX1_SLOT - [5:0] */
+
+/*
+ * R1298 (0x512) - AIF1 Frame Ctrl 12
+ */
+#define ARIZONA_AIF1RX2_SLOT_MASK                0x003F  /* AIF1RX2_SLOT - [5:0] */
+#define ARIZONA_AIF1RX2_SLOT_SHIFT                    0  /* AIF1RX2_SLOT - [5:0] */
+#define ARIZONA_AIF1RX2_SLOT_WIDTH                    6  /* AIF1RX2_SLOT - [5:0] */
+
+/*
+ * R1299 (0x513) - AIF1 Frame Ctrl 13
+ */
+#define ARIZONA_AIF1RX3_SLOT_MASK                0x003F  /* AIF1RX3_SLOT - [5:0] */
+#define ARIZONA_AIF1RX3_SLOT_SHIFT                    0  /* AIF1RX3_SLOT - [5:0] */
+#define ARIZONA_AIF1RX3_SLOT_WIDTH                    6  /* AIF1RX3_SLOT - [5:0] */
+
+/*
+ * R1300 (0x514) - AIF1 Frame Ctrl 14
+ */
+#define ARIZONA_AIF1RX4_SLOT_MASK                0x003F  /* AIF1RX4_SLOT - [5:0] */
+#define ARIZONA_AIF1RX4_SLOT_SHIFT                    0  /* AIF1RX4_SLOT - [5:0] */
+#define ARIZONA_AIF1RX4_SLOT_WIDTH                    6  /* AIF1RX4_SLOT - [5:0] */
+
+/*
+ * R1301 (0x515) - AIF1 Frame Ctrl 15
+ */
+#define ARIZONA_AIF1RX5_SLOT_MASK                0x003F  /* AIF1RX5_SLOT - [5:0] */
+#define ARIZONA_AIF1RX5_SLOT_SHIFT                    0  /* AIF1RX5_SLOT - [5:0] */
+#define ARIZONA_AIF1RX5_SLOT_WIDTH                    6  /* AIF1RX5_SLOT - [5:0] */
+
+/*
+ * R1302 (0x516) - AIF1 Frame Ctrl 16
+ */
+#define ARIZONA_AIF1RX6_SLOT_MASK                0x003F  /* AIF1RX6_SLOT - [5:0] */
+#define ARIZONA_AIF1RX6_SLOT_SHIFT                    0  /* AIF1RX6_SLOT - [5:0] */
+#define ARIZONA_AIF1RX6_SLOT_WIDTH                    6  /* AIF1RX6_SLOT - [5:0] */
+
+/*
+ * R1303 (0x517) - AIF1 Frame Ctrl 17
+ */
+#define ARIZONA_AIF1RX7_SLOT_MASK                0x003F  /* AIF1RX7_SLOT - [5:0] */
+#define ARIZONA_AIF1RX7_SLOT_SHIFT                    0  /* AIF1RX7_SLOT - [5:0] */
+#define ARIZONA_AIF1RX7_SLOT_WIDTH                    6  /* AIF1RX7_SLOT - [5:0] */
+
+/*
+ * R1304 (0x518) - AIF1 Frame Ctrl 18
+ */
+#define ARIZONA_AIF1RX8_SLOT_MASK                0x003F  /* AIF1RX8_SLOT - [5:0] */
+#define ARIZONA_AIF1RX8_SLOT_SHIFT                    0  /* AIF1RX8_SLOT - [5:0] */
+#define ARIZONA_AIF1RX8_SLOT_WIDTH                    6  /* AIF1RX8_SLOT - [5:0] */
+
+/*
+ * R1305 (0x519) - AIF1 Tx Enables
+ */
+#define ARIZONA_AIF1TX8_ENA                      0x0080  /* AIF1TX8_ENA */
+#define ARIZONA_AIF1TX8_ENA_MASK                 0x0080  /* AIF1TX8_ENA */
+#define ARIZONA_AIF1TX8_ENA_SHIFT                     7  /* AIF1TX8_ENA */
+#define ARIZONA_AIF1TX8_ENA_WIDTH                     1  /* AIF1TX8_ENA */
+#define ARIZONA_AIF1TX7_ENA                      0x0040  /* AIF1TX7_ENA */
+#define ARIZONA_AIF1TX7_ENA_MASK                 0x0040  /* AIF1TX7_ENA */
+#define ARIZONA_AIF1TX7_ENA_SHIFT                     6  /* AIF1TX7_ENA */
+#define ARIZONA_AIF1TX7_ENA_WIDTH                     1  /* AIF1TX7_ENA */
+#define ARIZONA_AIF1TX6_ENA                      0x0020  /* AIF1TX6_ENA */
+#define ARIZONA_AIF1TX6_ENA_MASK                 0x0020  /* AIF1TX6_ENA */
+#define ARIZONA_AIF1TX6_ENA_SHIFT                     5  /* AIF1TX6_ENA */
+#define ARIZONA_AIF1TX6_ENA_WIDTH                     1  /* AIF1TX6_ENA */
+#define ARIZONA_AIF1TX5_ENA                      0x0010  /* AIF1TX5_ENA */
+#define ARIZONA_AIF1TX5_ENA_MASK                 0x0010  /* AIF1TX5_ENA */
+#define ARIZONA_AIF1TX5_ENA_SHIFT                     4  /* AIF1TX5_ENA */
+#define ARIZONA_AIF1TX5_ENA_WIDTH                     1  /* AIF1TX5_ENA */
+#define ARIZONA_AIF1TX4_ENA                      0x0008  /* AIF1TX4_ENA */
+#define ARIZONA_AIF1TX4_ENA_MASK                 0x0008  /* AIF1TX4_ENA */
+#define ARIZONA_AIF1TX4_ENA_SHIFT                     3  /* AIF1TX4_ENA */
+#define ARIZONA_AIF1TX4_ENA_WIDTH                     1  /* AIF1TX4_ENA */
+#define ARIZONA_AIF1TX3_ENA                      0x0004  /* AIF1TX3_ENA */
+#define ARIZONA_AIF1TX3_ENA_MASK                 0x0004  /* AIF1TX3_ENA */
+#define ARIZONA_AIF1TX3_ENA_SHIFT                     2  /* AIF1TX3_ENA */
+#define ARIZONA_AIF1TX3_ENA_WIDTH                     1  /* AIF1TX3_ENA */
+#define ARIZONA_AIF1TX2_ENA                      0x0002  /* AIF1TX2_ENA */
+#define ARIZONA_AIF1TX2_ENA_MASK                 0x0002  /* AIF1TX2_ENA */
+#define ARIZONA_AIF1TX2_ENA_SHIFT                     1  /* AIF1TX2_ENA */
+#define ARIZONA_AIF1TX2_ENA_WIDTH                     1  /* AIF1TX2_ENA */
+#define ARIZONA_AIF1TX1_ENA                      0x0001  /* AIF1TX1_ENA */
+#define ARIZONA_AIF1TX1_ENA_MASK                 0x0001  /* AIF1TX1_ENA */
+#define ARIZONA_AIF1TX1_ENA_SHIFT                     0  /* AIF1TX1_ENA */
+#define ARIZONA_AIF1TX1_ENA_WIDTH                     1  /* AIF1TX1_ENA */
+
+/*
+ * R1306 (0x51A) - AIF1 Rx Enables
+ */
+#define ARIZONA_AIF1RX8_ENA                      0x0080  /* AIF1RX8_ENA */
+#define ARIZONA_AIF1RX8_ENA_MASK                 0x0080  /* AIF1RX8_ENA */
+#define ARIZONA_AIF1RX8_ENA_SHIFT                     7  /* AIF1RX8_ENA */
+#define ARIZONA_AIF1RX8_ENA_WIDTH                     1  /* AIF1RX8_ENA */
+#define ARIZONA_AIF1RX7_ENA                      0x0040  /* AIF1RX7_ENA */
+#define ARIZONA_AIF1RX7_ENA_MASK                 0x0040  /* AIF1RX7_ENA */
+#define ARIZONA_AIF1RX7_ENA_SHIFT                     6  /* AIF1RX7_ENA */
+#define ARIZONA_AIF1RX7_ENA_WIDTH                     1  /* AIF1RX7_ENA */
+#define ARIZONA_AIF1RX6_ENA                      0x0020  /* AIF1RX6_ENA */
+#define ARIZONA_AIF1RX6_ENA_MASK                 0x0020  /* AIF1RX6_ENA */
+#define ARIZONA_AIF1RX6_ENA_SHIFT                     5  /* AIF1RX6_ENA */
+#define ARIZONA_AIF1RX6_ENA_WIDTH                     1  /* AIF1RX6_ENA */
+#define ARIZONA_AIF1RX5_ENA                      0x0010  /* AIF1RX5_ENA */
+#define ARIZONA_AIF1RX5_ENA_MASK                 0x0010  /* AIF1RX5_ENA */
+#define ARIZONA_AIF1RX5_ENA_SHIFT                     4  /* AIF1RX5_ENA */
+#define ARIZONA_AIF1RX5_ENA_WIDTH                     1  /* AIF1RX5_ENA */
+#define ARIZONA_AIF1RX4_ENA                      0x0008  /* AIF1RX4_ENA */
+#define ARIZONA_AIF1RX4_ENA_MASK                 0x0008  /* AIF1RX4_ENA */
+#define ARIZONA_AIF1RX4_ENA_SHIFT                     3  /* AIF1RX4_ENA */
+#define ARIZONA_AIF1RX4_ENA_WIDTH                     1  /* AIF1RX4_ENA */
+#define ARIZONA_AIF1RX3_ENA                      0x0004  /* AIF1RX3_ENA */
+#define ARIZONA_AIF1RX3_ENA_MASK                 0x0004  /* AIF1RX3_ENA */
+#define ARIZONA_AIF1RX3_ENA_SHIFT                     2  /* AIF1RX3_ENA */
+#define ARIZONA_AIF1RX3_ENA_WIDTH                     1  /* AIF1RX3_ENA */
+#define ARIZONA_AIF1RX2_ENA                      0x0002  /* AIF1RX2_ENA */
+#define ARIZONA_AIF1RX2_ENA_MASK                 0x0002  /* AIF1RX2_ENA */
+#define ARIZONA_AIF1RX2_ENA_SHIFT                     1  /* AIF1RX2_ENA */
+#define ARIZONA_AIF1RX2_ENA_WIDTH                     1  /* AIF1RX2_ENA */
+#define ARIZONA_AIF1RX1_ENA                      0x0001  /* AIF1RX1_ENA */
+#define ARIZONA_AIF1RX1_ENA_MASK                 0x0001  /* AIF1RX1_ENA */
+#define ARIZONA_AIF1RX1_ENA_SHIFT                     0  /* AIF1RX1_ENA */
+#define ARIZONA_AIF1RX1_ENA_WIDTH                     1  /* AIF1RX1_ENA */
+
+/*
+ * R1307 (0x51B) - AIF1 Force Write
+ */
+#define ARIZONA_AIF1_FRC_WR                      0x0001  /* AIF1_FRC_WR */
+#define ARIZONA_AIF1_FRC_WR_MASK                 0x0001  /* AIF1_FRC_WR */
+#define ARIZONA_AIF1_FRC_WR_SHIFT                     0  /* AIF1_FRC_WR */
+#define ARIZONA_AIF1_FRC_WR_WIDTH                     1  /* AIF1_FRC_WR */
+
+/*
+ * R1344 (0x540) - AIF2 BCLK Ctrl
+ */
+#define ARIZONA_AIF2_BCLK_INV                    0x0080  /* AIF2_BCLK_INV */
+#define ARIZONA_AIF2_BCLK_INV_MASK               0x0080  /* AIF2_BCLK_INV */
+#define ARIZONA_AIF2_BCLK_INV_SHIFT                   7  /* AIF2_BCLK_INV */
+#define ARIZONA_AIF2_BCLK_INV_WIDTH                   1  /* AIF2_BCLK_INV */
+#define ARIZONA_AIF2_BCLK_FRC                    0x0040  /* AIF2_BCLK_FRC */
+#define ARIZONA_AIF2_BCLK_FRC_MASK               0x0040  /* AIF2_BCLK_FRC */
+#define ARIZONA_AIF2_BCLK_FRC_SHIFT                   6  /* AIF2_BCLK_FRC */
+#define ARIZONA_AIF2_BCLK_FRC_WIDTH                   1  /* AIF2_BCLK_FRC */
+#define ARIZONA_AIF2_BCLK_MSTR                   0x0020  /* AIF2_BCLK_MSTR */
+#define ARIZONA_AIF2_BCLK_MSTR_MASK              0x0020  /* AIF2_BCLK_MSTR */
+#define ARIZONA_AIF2_BCLK_MSTR_SHIFT                  5  /* AIF2_BCLK_MSTR */
+#define ARIZONA_AIF2_BCLK_MSTR_WIDTH                  1  /* AIF2_BCLK_MSTR */
+#define ARIZONA_AIF2_BCLK_FREQ_MASK              0x001F  /* AIF2_BCLK_FREQ - [4:0] */
+#define ARIZONA_AIF2_BCLK_FREQ_SHIFT                  0  /* AIF2_BCLK_FREQ - [4:0] */
+#define ARIZONA_AIF2_BCLK_FREQ_WIDTH                  5  /* AIF2_BCLK_FREQ - [4:0] */
+
+/*
+ * R1345 (0x541) - AIF2 Tx Pin Ctrl
+ */
+#define ARIZONA_AIF2TX_DAT_TRI                   0x0020  /* AIF2TX_DAT_TRI */
+#define ARIZONA_AIF2TX_DAT_TRI_MASK              0x0020  /* AIF2TX_DAT_TRI */
+#define ARIZONA_AIF2TX_DAT_TRI_SHIFT                  5  /* AIF2TX_DAT_TRI */
+#define ARIZONA_AIF2TX_DAT_TRI_WIDTH                  1  /* AIF2TX_DAT_TRI */
+#define ARIZONA_AIF2TX_LRCLK_SRC                 0x0008  /* AIF2TX_LRCLK_SRC */
+#define ARIZONA_AIF2TX_LRCLK_SRC_MASK            0x0008  /* AIF2TX_LRCLK_SRC */
+#define ARIZONA_AIF2TX_LRCLK_SRC_SHIFT                3  /* AIF2TX_LRCLK_SRC */
+#define ARIZONA_AIF2TX_LRCLK_SRC_WIDTH                1  /* AIF2TX_LRCLK_SRC */
+#define ARIZONA_AIF2TX_LRCLK_INV                 0x0004  /* AIF2TX_LRCLK_INV */
+#define ARIZONA_AIF2TX_LRCLK_INV_MASK            0x0004  /* AIF2TX_LRCLK_INV */
+#define ARIZONA_AIF2TX_LRCLK_INV_SHIFT                2  /* AIF2TX_LRCLK_INV */
+#define ARIZONA_AIF2TX_LRCLK_INV_WIDTH                1  /* AIF2TX_LRCLK_INV */
+#define ARIZONA_AIF2TX_LRCLK_FRC                 0x0002  /* AIF2TX_LRCLK_FRC */
+#define ARIZONA_AIF2TX_LRCLK_FRC_MASK            0x0002  /* AIF2TX_LRCLK_FRC */
+#define ARIZONA_AIF2TX_LRCLK_FRC_SHIFT                1  /* AIF2TX_LRCLK_FRC */
+#define ARIZONA_AIF2TX_LRCLK_FRC_WIDTH                1  /* AIF2TX_LRCLK_FRC */
+#define ARIZONA_AIF2TX_LRCLK_MSTR                0x0001  /* AIF2TX_LRCLK_MSTR */
+#define ARIZONA_AIF2TX_LRCLK_MSTR_MASK           0x0001  /* AIF2TX_LRCLK_MSTR */
+#define ARIZONA_AIF2TX_LRCLK_MSTR_SHIFT               0  /* AIF2TX_LRCLK_MSTR */
+#define ARIZONA_AIF2TX_LRCLK_MSTR_WIDTH               1  /* AIF2TX_LRCLK_MSTR */
+
+/*
+ * R1346 (0x542) - AIF2 Rx Pin Ctrl
+ */
+#define ARIZONA_AIF2RX_LRCLK_INV                 0x0004  /* AIF2RX_LRCLK_INV */
+#define ARIZONA_AIF2RX_LRCLK_INV_MASK            0x0004  /* AIF2RX_LRCLK_INV */
+#define ARIZONA_AIF2RX_LRCLK_INV_SHIFT                2  /* AIF2RX_LRCLK_INV */
+#define ARIZONA_AIF2RX_LRCLK_INV_WIDTH                1  /* AIF2RX_LRCLK_INV */
+#define ARIZONA_AIF2RX_LRCLK_FRC                 0x0002  /* AIF2RX_LRCLK_FRC */
+#define ARIZONA_AIF2RX_LRCLK_FRC_MASK            0x0002  /* AIF2RX_LRCLK_FRC */
+#define ARIZONA_AIF2RX_LRCLK_FRC_SHIFT                1  /* AIF2RX_LRCLK_FRC */
+#define ARIZONA_AIF2RX_LRCLK_FRC_WIDTH                1  /* AIF2RX_LRCLK_FRC */
+#define ARIZONA_AIF2RX_LRCLK_MSTR                0x0001  /* AIF2RX_LRCLK_MSTR */
+#define ARIZONA_AIF2RX_LRCLK_MSTR_MASK           0x0001  /* AIF2RX_LRCLK_MSTR */
+#define ARIZONA_AIF2RX_LRCLK_MSTR_SHIFT               0  /* AIF2RX_LRCLK_MSTR */
+#define ARIZONA_AIF2RX_LRCLK_MSTR_WIDTH               1  /* AIF2RX_LRCLK_MSTR */
+
+/*
+ * R1347 (0x543) - AIF2 Rate Ctrl
+ */
+#define ARIZONA_AIF2_RATE_MASK                   0x7800  /* AIF2_RATE - [14:11] */
+#define ARIZONA_AIF2_RATE_SHIFT                      11  /* AIF2_RATE - [14:11] */
+#define ARIZONA_AIF2_RATE_WIDTH                       4  /* AIF2_RATE - [14:11] */
+#define ARIZONA_AIF2_TRI                         0x0040  /* AIF2_TRI */
+#define ARIZONA_AIF2_TRI_MASK                    0x0040  /* AIF2_TRI */
+#define ARIZONA_AIF2_TRI_SHIFT                        6  /* AIF2_TRI */
+#define ARIZONA_AIF2_TRI_WIDTH                        1  /* AIF2_TRI */
+
+/*
+ * R1348 (0x544) - AIF2 Format
+ */
+#define ARIZONA_AIF2_FMT_MASK                    0x0007  /* AIF2_FMT - [2:0] */
+#define ARIZONA_AIF2_FMT_SHIFT                        0  /* AIF2_FMT - [2:0] */
+#define ARIZONA_AIF2_FMT_WIDTH                        3  /* AIF2_FMT - [2:0] */
+
+/*
+ * R1349 (0x545) - AIF2 Tx BCLK Rate
+ */
+#define ARIZONA_AIF2TX_BCPF_MASK                 0x1FFF  /* AIF2TX_BCPF - [12:0] */
+#define ARIZONA_AIF2TX_BCPF_SHIFT                     0  /* AIF2TX_BCPF - [12:0] */
+#define ARIZONA_AIF2TX_BCPF_WIDTH                    13  /* AIF2TX_BCPF - [12:0] */
+
+/*
+ * R1350 (0x546) - AIF2 Rx BCLK Rate
+ */
+#define ARIZONA_AIF2RX_BCPF_MASK                 0x1FFF  /* AIF2RX_BCPF - [12:0] */
+#define ARIZONA_AIF2RX_BCPF_SHIFT                     0  /* AIF2RX_BCPF - [12:0] */
+#define ARIZONA_AIF2RX_BCPF_WIDTH                    13  /* AIF2RX_BCPF - [12:0] */
+
+/*
+ * R1351 (0x547) - AIF2 Frame Ctrl 1
+ */
+#define ARIZONA_AIF2TX_WL_MASK                   0x3F00  /* AIF2TX_WL - [13:8] */
+#define ARIZONA_AIF2TX_WL_SHIFT                       8  /* AIF2TX_WL - [13:8] */
+#define ARIZONA_AIF2TX_WL_WIDTH                       6  /* AIF2TX_WL - [13:8] */
+#define ARIZONA_AIF2TX_SLOT_LEN_MASK             0x00FF  /* AIF2TX_SLOT_LEN - [7:0] */
+#define ARIZONA_AIF2TX_SLOT_LEN_SHIFT                 0  /* AIF2TX_SLOT_LEN - [7:0] */
+#define ARIZONA_AIF2TX_SLOT_LEN_WIDTH                 8  /* AIF2TX_SLOT_LEN - [7:0] */
+
+/*
+ * R1352 (0x548) - AIF2 Frame Ctrl 2
+ */
+#define ARIZONA_AIF2RX_WL_MASK                   0x3F00  /* AIF2RX_WL - [13:8] */
+#define ARIZONA_AIF2RX_WL_SHIFT                       8  /* AIF2RX_WL - [13:8] */
+#define ARIZONA_AIF2RX_WL_WIDTH                       6  /* AIF2RX_WL - [13:8] */
+#define ARIZONA_AIF2RX_SLOT_LEN_MASK             0x00FF  /* AIF2RX_SLOT_LEN - [7:0] */
+#define ARIZONA_AIF2RX_SLOT_LEN_SHIFT                 0  /* AIF2RX_SLOT_LEN - [7:0] */
+#define ARIZONA_AIF2RX_SLOT_LEN_WIDTH                 8  /* AIF2RX_SLOT_LEN - [7:0] */
+
+/*
+ * R1353 (0x549) - AIF2 Frame Ctrl 3
+ */
+#define ARIZONA_AIF2TX1_SLOT_MASK                0x003F  /* AIF2TX1_SLOT - [5:0] */
+#define ARIZONA_AIF2TX1_SLOT_SHIFT                    0  /* AIF2TX1_SLOT - [5:0] */
+#define ARIZONA_AIF2TX1_SLOT_WIDTH                    6  /* AIF2TX1_SLOT - [5:0] */
+
+/*
+ * R1354 (0x54A) - AIF2 Frame Ctrl 4
+ */
+#define ARIZONA_AIF2TX2_SLOT_MASK                0x003F  /* AIF2TX2_SLOT - [5:0] */
+#define ARIZONA_AIF2TX2_SLOT_SHIFT                    0  /* AIF2TX2_SLOT - [5:0] */
+#define ARIZONA_AIF2TX2_SLOT_WIDTH                    6  /* AIF2TX2_SLOT - [5:0] */
+
+/*
+ * R1361 (0x551) - AIF2 Frame Ctrl 11
+ */
+#define ARIZONA_AIF2RX1_SLOT_MASK                0x003F  /* AIF2RX1_SLOT - [5:0] */
+#define ARIZONA_AIF2RX1_SLOT_SHIFT                    0  /* AIF2RX1_SLOT - [5:0] */
+#define ARIZONA_AIF2RX1_SLOT_WIDTH                    6  /* AIF2RX1_SLOT - [5:0] */
+
+/*
+ * R1362 (0x552) - AIF2 Frame Ctrl 12
+ */
+#define ARIZONA_AIF2RX2_SLOT_MASK                0x003F  /* AIF2RX2_SLOT - [5:0] */
+#define ARIZONA_AIF2RX2_SLOT_SHIFT                    0  /* AIF2RX2_SLOT - [5:0] */
+#define ARIZONA_AIF2RX2_SLOT_WIDTH                    6  /* AIF2RX2_SLOT - [5:0] */
+
+/*
+ * R1369 (0x559) - AIF2 Tx Enables
+ */
+#define ARIZONA_AIF2TX2_ENA                      0x0002  /* AIF2TX2_ENA */
+#define ARIZONA_AIF2TX2_ENA_MASK                 0x0002  /* AIF2TX2_ENA */
+#define ARIZONA_AIF2TX2_ENA_SHIFT                     1  /* AIF2TX2_ENA */
+#define ARIZONA_AIF2TX2_ENA_WIDTH                     1  /* AIF2TX2_ENA */
+#define ARIZONA_AIF2TX1_ENA                      0x0001  /* AIF2TX1_ENA */
+#define ARIZONA_AIF2TX1_ENA_MASK                 0x0001  /* AIF2TX1_ENA */
+#define ARIZONA_AIF2TX1_ENA_SHIFT                     0  /* AIF2TX1_ENA */
+#define ARIZONA_AIF2TX1_ENA_WIDTH                     1  /* AIF2TX1_ENA */
+
+/*
+ * R1370 (0x55A) - AIF2 Rx Enables
+ */
+#define ARIZONA_AIF2RX2_ENA                      0x0002  /* AIF2RX2_ENA */
+#define ARIZONA_AIF2RX2_ENA_MASK                 0x0002  /* AIF2RX2_ENA */
+#define ARIZONA_AIF2RX2_ENA_SHIFT                     1  /* AIF2RX2_ENA */
+#define ARIZONA_AIF2RX2_ENA_WIDTH                     1  /* AIF2RX2_ENA */
+#define ARIZONA_AIF2RX1_ENA                      0x0001  /* AIF2RX1_ENA */
+#define ARIZONA_AIF2RX1_ENA_MASK                 0x0001  /* AIF2RX1_ENA */
+#define ARIZONA_AIF2RX1_ENA_SHIFT                     0  /* AIF2RX1_ENA */
+#define ARIZONA_AIF2RX1_ENA_WIDTH                     1  /* AIF2RX1_ENA */
+
+/*
+ * R1371 (0x55B) - AIF2 Force Write
+ */
+#define ARIZONA_AIF2_FRC_WR                      0x0001  /* AIF2_FRC_WR */
+#define ARIZONA_AIF2_FRC_WR_MASK                 0x0001  /* AIF2_FRC_WR */
+#define ARIZONA_AIF2_FRC_WR_SHIFT                     0  /* AIF2_FRC_WR */
+#define ARIZONA_AIF2_FRC_WR_WIDTH                     1  /* AIF2_FRC_WR */
+
+/*
+ * R1408 (0x580) - AIF3 BCLK Ctrl
+ */
+#define ARIZONA_AIF3_BCLK_INV                    0x0080  /* AIF3_BCLK_INV */
+#define ARIZONA_AIF3_BCLK_INV_MASK               0x0080  /* AIF3_BCLK_INV */
+#define ARIZONA_AIF3_BCLK_INV_SHIFT                   7  /* AIF3_BCLK_INV */
+#define ARIZONA_AIF3_BCLK_INV_WIDTH                   1  /* AIF3_BCLK_INV */
+#define ARIZONA_AIF3_BCLK_FRC                    0x0040  /* AIF3_BCLK_FRC */
+#define ARIZONA_AIF3_BCLK_FRC_MASK               0x0040  /* AIF3_BCLK_FRC */
+#define ARIZONA_AIF3_BCLK_FRC_SHIFT                   6  /* AIF3_BCLK_FRC */
+#define ARIZONA_AIF3_BCLK_FRC_WIDTH                   1  /* AIF3_BCLK_FRC */
+#define ARIZONA_AIF3_BCLK_MSTR                   0x0020  /* AIF3_BCLK_MSTR */
+#define ARIZONA_AIF3_BCLK_MSTR_MASK              0x0020  /* AIF3_BCLK_MSTR */
+#define ARIZONA_AIF3_BCLK_MSTR_SHIFT                  5  /* AIF3_BCLK_MSTR */
+#define ARIZONA_AIF3_BCLK_MSTR_WIDTH                  1  /* AIF3_BCLK_MSTR */
+#define ARIZONA_AIF3_BCLK_FREQ_MASK              0x001F  /* AIF3_BCLK_FREQ - [4:0] */
+#define ARIZONA_AIF3_BCLK_FREQ_SHIFT                  0  /* AIF3_BCLK_FREQ - [4:0] */
+#define ARIZONA_AIF3_BCLK_FREQ_WIDTH                  5  /* AIF3_BCLK_FREQ - [4:0] */
+
+/*
+ * R1409 (0x581) - AIF3 Tx Pin Ctrl
+ */
+#define ARIZONA_AIF3TX_DAT_TRI                   0x0020  /* AIF3TX_DAT_TRI */
+#define ARIZONA_AIF3TX_DAT_TRI_MASK              0x0020  /* AIF3TX_DAT_TRI */
+#define ARIZONA_AIF3TX_DAT_TRI_SHIFT                  5  /* AIF3TX_DAT_TRI */
+#define ARIZONA_AIF3TX_DAT_TRI_WIDTH                  1  /* AIF3TX_DAT_TRI */
+#define ARIZONA_AIF3TX_LRCLK_SRC                 0x0008  /* AIF3TX_LRCLK_SRC */
+#define ARIZONA_AIF3TX_LRCLK_SRC_MASK            0x0008  /* AIF3TX_LRCLK_SRC */
+#define ARIZONA_AIF3TX_LRCLK_SRC_SHIFT                3  /* AIF3TX_LRCLK_SRC */
+#define ARIZONA_AIF3TX_LRCLK_SRC_WIDTH                1  /* AIF3TX_LRCLK_SRC */
+#define ARIZONA_AIF3TX_LRCLK_INV                 0x0004  /* AIF3TX_LRCLK_INV */
+#define ARIZONA_AIF3TX_LRCLK_INV_MASK            0x0004  /* AIF3TX_LRCLK_INV */
+#define ARIZONA_AIF3TX_LRCLK_INV_SHIFT                2  /* AIF3TX_LRCLK_INV */
+#define ARIZONA_AIF3TX_LRCLK_INV_WIDTH                1  /* AIF3TX_LRCLK_INV */
+#define ARIZONA_AIF3TX_LRCLK_FRC                 0x0002  /* AIF3TX_LRCLK_FRC */
+#define ARIZONA_AIF3TX_LRCLK_FRC_MASK            0x0002  /* AIF3TX_LRCLK_FRC */
+#define ARIZONA_AIF3TX_LRCLK_FRC_SHIFT                1  /* AIF3TX_LRCLK_FRC */
+#define ARIZONA_AIF3TX_LRCLK_FRC_WIDTH                1  /* AIF3TX_LRCLK_FRC */
+#define ARIZONA_AIF3TX_LRCLK_MSTR                0x0001  /* AIF3TX_LRCLK_MSTR */
+#define ARIZONA_AIF3TX_LRCLK_MSTR_MASK           0x0001  /* AIF3TX_LRCLK_MSTR */
+#define ARIZONA_AIF3TX_LRCLK_MSTR_SHIFT               0  /* AIF3TX_LRCLK_MSTR */
+#define ARIZONA_AIF3TX_LRCLK_MSTR_WIDTH               1  /* AIF3TX_LRCLK_MSTR */
+
+/*
+ * R1410 (0x582) - AIF3 Rx Pin Ctrl
+ */
+#define ARIZONA_AIF3RX_LRCLK_INV                 0x0004  /* AIF3RX_LRCLK_INV */
+#define ARIZONA_AIF3RX_LRCLK_INV_MASK            0x0004  /* AIF3RX_LRCLK_INV */
+#define ARIZONA_AIF3RX_LRCLK_INV_SHIFT                2  /* AIF3RX_LRCLK_INV */
+#define ARIZONA_AIF3RX_LRCLK_INV_WIDTH                1  /* AIF3RX_LRCLK_INV */
+#define ARIZONA_AIF3RX_LRCLK_FRC                 0x0002  /* AIF3RX_LRCLK_FRC */
+#define ARIZONA_AIF3RX_LRCLK_FRC_MASK            0x0002  /* AIF3RX_LRCLK_FRC */
+#define ARIZONA_AIF3RX_LRCLK_FRC_SHIFT                1  /* AIF3RX_LRCLK_FRC */
+#define ARIZONA_AIF3RX_LRCLK_FRC_WIDTH                1  /* AIF3RX_LRCLK_FRC */
+#define ARIZONA_AIF3RX_LRCLK_MSTR                0x0001  /* AIF3RX_LRCLK_MSTR */
+#define ARIZONA_AIF3RX_LRCLK_MSTR_MASK           0x0001  /* AIF3RX_LRCLK_MSTR */
+#define ARIZONA_AIF3RX_LRCLK_MSTR_SHIFT               0  /* AIF3RX_LRCLK_MSTR */
+#define ARIZONA_AIF3RX_LRCLK_MSTR_WIDTH               1  /* AIF3RX_LRCLK_MSTR */
+
+/*
+ * R1411 (0x583) - AIF3 Rate Ctrl
+ */
+#define ARIZONA_AIF3_RATE_MASK                   0x7800  /* AIF3_RATE - [14:11] */
+#define ARIZONA_AIF3_RATE_SHIFT                      11  /* AIF3_RATE - [14:11] */
+#define ARIZONA_AIF3_RATE_WIDTH                       4  /* AIF3_RATE - [14:11] */
+#define ARIZONA_AIF3_TRI                         0x0040  /* AIF3_TRI */
+#define ARIZONA_AIF3_TRI_MASK                    0x0040  /* AIF3_TRI */
+#define ARIZONA_AIF3_TRI_SHIFT                        6  /* AIF3_TRI */
+#define ARIZONA_AIF3_TRI_WIDTH                        1  /* AIF3_TRI */
+
+/*
+ * R1412 (0x584) - AIF3 Format
+ */
+#define ARIZONA_AIF3_FMT_MASK                    0x0007  /* AIF3_FMT - [2:0] */
+#define ARIZONA_AIF3_FMT_SHIFT                        0  /* AIF3_FMT - [2:0] */
+#define ARIZONA_AIF3_FMT_WIDTH                        3  /* AIF3_FMT - [2:0] */
+
+/*
+ * R1413 (0x585) - AIF3 Tx BCLK Rate
+ */
+#define ARIZONA_AIF3TX_BCPF_MASK                 0x1FFF  /* AIF3TX_BCPF - [12:0] */
+#define ARIZONA_AIF3TX_BCPF_SHIFT                     0  /* AIF3TX_BCPF - [12:0] */
+#define ARIZONA_AIF3TX_BCPF_WIDTH                    13  /* AIF3TX_BCPF - [12:0] */
+
+/*
+ * R1414 (0x586) - AIF3 Rx BCLK Rate
+ */
+#define ARIZONA_AIF3RX_BCPF_MASK                 0x1FFF  /* AIF3RX_BCPF - [12:0] */
+#define ARIZONA_AIF3RX_BCPF_SHIFT                     0  /* AIF3RX_BCPF - [12:0] */
+#define ARIZONA_AIF3RX_BCPF_WIDTH                    13  /* AIF3RX_BCPF - [12:0] */
+
+/*
+ * R1415 (0x587) - AIF3 Frame Ctrl 1
+ */
+#define ARIZONA_AIF3TX_WL_MASK                   0x3F00  /* AIF3TX_WL - [13:8] */
+#define ARIZONA_AIF3TX_WL_SHIFT                       8  /* AIF3TX_WL - [13:8] */
+#define ARIZONA_AIF3TX_WL_WIDTH                       6  /* AIF3TX_WL - [13:8] */
+#define ARIZONA_AIF3TX_SLOT_LEN_MASK             0x00FF  /* AIF3TX_SLOT_LEN - [7:0] */
+#define ARIZONA_AIF3TX_SLOT_LEN_SHIFT                 0  /* AIF3TX_SLOT_LEN - [7:0] */
+#define ARIZONA_AIF3TX_SLOT_LEN_WIDTH                 8  /* AIF3TX_SLOT_LEN - [7:0] */
+
+/*
+ * R1416 (0x588) - AIF3 Frame Ctrl 2
+ */
+#define ARIZONA_AIF3RX_WL_MASK                   0x3F00  /* AIF3RX_WL - [13:8] */
+#define ARIZONA_AIF3RX_WL_SHIFT                       8  /* AIF3RX_WL - [13:8] */
+#define ARIZONA_AIF3RX_WL_WIDTH                       6  /* AIF3RX_WL - [13:8] */
+#define ARIZONA_AIF3RX_SLOT_LEN_MASK             0x00FF  /* AIF3RX_SLOT_LEN - [7:0] */
+#define ARIZONA_AIF3RX_SLOT_LEN_SHIFT                 0  /* AIF3RX_SLOT_LEN - [7:0] */
+#define ARIZONA_AIF3RX_SLOT_LEN_WIDTH                 8  /* AIF3RX_SLOT_LEN - [7:0] */
+
+/*
+ * R1417 (0x589) - AIF3 Frame Ctrl 3
+ */
+#define ARIZONA_AIF3TX1_SLOT_MASK                0x003F  /* AIF3TX1_SLOT - [5:0] */
+#define ARIZONA_AIF3TX1_SLOT_SHIFT                    0  /* AIF3TX1_SLOT - [5:0] */
+#define ARIZONA_AIF3TX1_SLOT_WIDTH                    6  /* AIF3TX1_SLOT - [5:0] */
+
+/*
+ * R1418 (0x58A) - AIF3 Frame Ctrl 4
+ */
+#define ARIZONA_AIF3TX2_SLOT_MASK                0x003F  /* AIF3TX2_SLOT - [5:0] */
+#define ARIZONA_AIF3TX2_SLOT_SHIFT                    0  /* AIF3TX2_SLOT - [5:0] */
+#define ARIZONA_AIF3TX2_SLOT_WIDTH                    6  /* AIF3TX2_SLOT - [5:0] */
+
+/*
+ * R1425 (0x591) - AIF3 Frame Ctrl 11
+ */
+#define ARIZONA_AIF3RX1_SLOT_MASK                0x003F  /* AIF3RX1_SLOT - [5:0] */
+#define ARIZONA_AIF3RX1_SLOT_SHIFT                    0  /* AIF3RX1_SLOT - [5:0] */
+#define ARIZONA_AIF3RX1_SLOT_WIDTH                    6  /* AIF3RX1_SLOT - [5:0] */
+
+/*
+ * R1426 (0x592) - AIF3 Frame Ctrl 12
+ */
+#define ARIZONA_AIF3RX2_SLOT_MASK                0x003F  /* AIF3RX2_SLOT - [5:0] */
+#define ARIZONA_AIF3RX2_SLOT_SHIFT                    0  /* AIF3RX2_SLOT - [5:0] */
+#define ARIZONA_AIF3RX2_SLOT_WIDTH                    6  /* AIF3RX2_SLOT - [5:0] */
+
+/*
+ * R1433 (0x599) - AIF3 Tx Enables
+ */
+#define ARIZONA_AIF3TX2_ENA                      0x0002  /* AIF3TX2_ENA */
+#define ARIZONA_AIF3TX2_ENA_MASK                 0x0002  /* AIF3TX2_ENA */
+#define ARIZONA_AIF3TX2_ENA_SHIFT                     1  /* AIF3TX2_ENA */
+#define ARIZONA_AIF3TX2_ENA_WIDTH                     1  /* AIF3TX2_ENA */
+#define ARIZONA_AIF3TX1_ENA                      0x0001  /* AIF3TX1_ENA */
+#define ARIZONA_AIF3TX1_ENA_MASK                 0x0001  /* AIF3TX1_ENA */
+#define ARIZONA_AIF3TX1_ENA_SHIFT                     0  /* AIF3TX1_ENA */
+#define ARIZONA_AIF3TX1_ENA_WIDTH                     1  /* AIF3TX1_ENA */
+
+/*
+ * R1434 (0x59A) - AIF3 Rx Enables
+ */
+#define ARIZONA_AIF3RX2_ENA                      0x0002  /* AIF3RX2_ENA */
+#define ARIZONA_AIF3RX2_ENA_MASK                 0x0002  /* AIF3RX2_ENA */
+#define ARIZONA_AIF3RX2_ENA_SHIFT                     1  /* AIF3RX2_ENA */
+#define ARIZONA_AIF3RX2_ENA_WIDTH                     1  /* AIF3RX2_ENA */
+#define ARIZONA_AIF3RX1_ENA                      0x0001  /* AIF3RX1_ENA */
+#define ARIZONA_AIF3RX1_ENA_MASK                 0x0001  /* AIF3RX1_ENA */
+#define ARIZONA_AIF3RX1_ENA_SHIFT                     0  /* AIF3RX1_ENA */
+#define ARIZONA_AIF3RX1_ENA_WIDTH                     1  /* AIF3RX1_ENA */
+
+/*
+ * R1435 (0x59B) - AIF3 Force Write
+ */
+#define ARIZONA_AIF3_FRC_WR                      0x0001  /* AIF3_FRC_WR */
+#define ARIZONA_AIF3_FRC_WR_MASK                 0x0001  /* AIF3_FRC_WR */
+#define ARIZONA_AIF3_FRC_WR_SHIFT                     0  /* AIF3_FRC_WR */
+#define ARIZONA_AIF3_FRC_WR_WIDTH                     1  /* AIF3_FRC_WR */
+
+/*
+ * R1507 (0x5E3) - SLIMbus Framer Ref Gear
+ */
+#define ARIZONA_SLIMCLK_SRC                      0x0010  /* SLIMCLK_SRC */
+#define ARIZONA_SLIMCLK_SRC_MASK                 0x0010  /* SLIMCLK_SRC */
+#define ARIZONA_SLIMCLK_SRC_SHIFT                     4  /* SLIMCLK_SRC */
+#define ARIZONA_SLIMCLK_SRC_WIDTH                     1  /* SLIMCLK_SRC */
+#define ARIZONA_FRAMER_REF_GEAR_MASK             0x000F  /* FRAMER_REF_GEAR - [3:0] */
+#define ARIZONA_FRAMER_REF_GEAR_SHIFT                 0  /* FRAMER_REF_GEAR - [3:0] */
+#define ARIZONA_FRAMER_REF_GEAR_WIDTH                 4  /* FRAMER_REF_GEAR - [3:0] */
+
+/*
+ * R1509 (0x5E5) - SLIMbus Rates 1
+ */
+#define ARIZONA_SLIMRX2_RATE_MASK                0x7800  /* SLIMRX2_RATE - [14:11] */
+#define ARIZONA_SLIMRX2_RATE_SHIFT                   11  /* SLIMRX2_RATE - [14:11] */
+#define ARIZONA_SLIMRX2_RATE_WIDTH                    4  /* SLIMRX2_RATE - [14:11] */
+#define ARIZONA_SLIMRX1_RATE_MASK                0x0078  /* SLIMRX1_RATE - [6:3] */
+#define ARIZONA_SLIMRX1_RATE_SHIFT                    3  /* SLIMRX1_RATE - [6:3] */
+#define ARIZONA_SLIMRX1_RATE_WIDTH                    4  /* SLIMRX1_RATE - [6:3] */
+
+/*
+ * R1510 (0x5E6) - SLIMbus Rates 2
+ */
+#define ARIZONA_SLIMRX4_RATE_MASK                0x7800  /* SLIMRX4_RATE - [14:11] */
+#define ARIZONA_SLIMRX4_RATE_SHIFT                   11  /* SLIMRX4_RATE - [14:11] */
+#define ARIZONA_SLIMRX4_RATE_WIDTH                    4  /* SLIMRX4_RATE - [14:11] */
+#define ARIZONA_SLIMRX3_RATE_MASK                0x0078  /* SLIMRX3_RATE - [6:3] */
+#define ARIZONA_SLIMRX3_RATE_SHIFT                    3  /* SLIMRX3_RATE - [6:3] */
+#define ARIZONA_SLIMRX3_RATE_WIDTH                    4  /* SLIMRX3_RATE - [6:3] */
+
+/*
+ * R1511 (0x5E7) - SLIMbus Rates 3
+ */
+#define ARIZONA_SLIMRX6_RATE_MASK                0x7800  /* SLIMRX6_RATE - [14:11] */
+#define ARIZONA_SLIMRX6_RATE_SHIFT                   11  /* SLIMRX6_RATE - [14:11] */
+#define ARIZONA_SLIMRX6_RATE_WIDTH                    4  /* SLIMRX6_RATE - [14:11] */
+#define ARIZONA_SLIMRX5_RATE_MASK                0x0078  /* SLIMRX5_RATE - [6:3] */
+#define ARIZONA_SLIMRX5_RATE_SHIFT                    3  /* SLIMRX5_RATE - [6:3] */
+#define ARIZONA_SLIMRX5_RATE_WIDTH                    4  /* SLIMRX5_RATE - [6:3] */
+
+/*
+ * R1512 (0x5E8) - SLIMbus Rates 4
+ */
+#define ARIZONA_SLIMRX8_RATE_MASK                0x7800  /* SLIMRX8_RATE - [14:11] */
+#define ARIZONA_SLIMRX8_RATE_SHIFT                   11  /* SLIMRX8_RATE - [14:11] */
+#define ARIZONA_SLIMRX8_RATE_WIDTH                    4  /* SLIMRX8_RATE - [14:11] */
+#define ARIZONA_SLIMRX7_RATE_MASK                0x0078  /* SLIMRX7_RATE - [6:3] */
+#define ARIZONA_SLIMRX7_RATE_SHIFT                    3  /* SLIMRX7_RATE - [6:3] */
+#define ARIZONA_SLIMRX7_RATE_WIDTH                    4  /* SLIMRX7_RATE - [6:3] */
+
+/*
+ * R1513 (0x5E9) - SLIMbus Rates 5
+ */
+#define ARIZONA_SLIMTX2_RATE_MASK                0x7800  /* SLIMTX2_RATE - [14:11] */
+#define ARIZONA_SLIMTX2_RATE_SHIFT                   11  /* SLIMTX2_RATE - [14:11] */
+#define ARIZONA_SLIMTX2_RATE_WIDTH                    4  /* SLIMTX2_RATE - [14:11] */
+#define ARIZONA_SLIMTX1_RATE_MASK                0x0078  /* SLIMTX1_RATE - [6:3] */
+#define ARIZONA_SLIMTX1_RATE_SHIFT                    3  /* SLIMTX1_RATE - [6:3] */
+#define ARIZONA_SLIMTX1_RATE_WIDTH                    4  /* SLIMTX1_RATE - [6:3] */
+
+/*
+ * R1514 (0x5EA) - SLIMbus Rates 6
+ */
+#define ARIZONA_SLIMTX4_RATE_MASK                0x7800  /* SLIMTX4_RATE - [14:11] */
+#define ARIZONA_SLIMTX4_RATE_SHIFT                   11  /* SLIMTX4_RATE - [14:11] */
+#define ARIZONA_SLIMTX4_RATE_WIDTH                    4  /* SLIMTX4_RATE - [14:11] */
+#define ARIZONA_SLIMTX3_RATE_MASK                0x0078  /* SLIMTX3_RATE - [6:3] */
+#define ARIZONA_SLIMTX3_RATE_SHIFT                    3  /* SLIMTX3_RATE - [6:3] */
+#define ARIZONA_SLIMTX3_RATE_WIDTH                    4  /* SLIMTX3_RATE - [6:3] */
+
+/*
+ * R1515 (0x5EB) - SLIMbus Rates 7
+ */
+#define ARIZONA_SLIMTX6_RATE_MASK                0x7800  /* SLIMTX6_RATE - [14:11] */
+#define ARIZONA_SLIMTX6_RATE_SHIFT                   11  /* SLIMTX6_RATE - [14:11] */
+#define ARIZONA_SLIMTX6_RATE_WIDTH                    4  /* SLIMTX6_RATE - [14:11] */
+#define ARIZONA_SLIMTX5_RATE_MASK                0x0078  /* SLIMTX5_RATE - [6:3] */
+#define ARIZONA_SLIMTX5_RATE_SHIFT                    3  /* SLIMTX5_RATE - [6:3] */
+#define ARIZONA_SLIMTX5_RATE_WIDTH                    4  /* SLIMTX5_RATE - [6:3] */
+
+/*
+ * R1516 (0x5EC) - SLIMbus Rates 8
+ */
+#define ARIZONA_SLIMTX8_RATE_MASK                0x7800  /* SLIMTX8_RATE - [14:11] */
+#define ARIZONA_SLIMTX8_RATE_SHIFT                   11  /* SLIMTX8_RATE - [14:11] */
+#define ARIZONA_SLIMTX8_RATE_WIDTH                    4  /* SLIMTX8_RATE - [14:11] */
+#define ARIZONA_SLIMTX7_RATE_MASK                0x0078  /* SLIMTX7_RATE - [6:3] */
+#define ARIZONA_SLIMTX7_RATE_SHIFT                    3  /* SLIMTX7_RATE - [6:3] */
+#define ARIZONA_SLIMTX7_RATE_WIDTH                    4  /* SLIMTX7_RATE - [6:3] */
+
+/*
+ * R1525 (0x5F5) - SLIMbus RX Channel Enable
+ */
+#define ARIZONA_SLIMRX8_ENA                      0x0080  /* SLIMRX8_ENA */
+#define ARIZONA_SLIMRX8_ENA_MASK                 0x0080  /* SLIMRX8_ENA */
+#define ARIZONA_SLIMRX8_ENA_SHIFT                     7  /* SLIMRX8_ENA */
+#define ARIZONA_SLIMRX8_ENA_WIDTH                     1  /* SLIMRX8_ENA */
+#define ARIZONA_SLIMRX7_ENA                      0x0040  /* SLIMRX7_ENA */
+#define ARIZONA_SLIMRX7_ENA_MASK                 0x0040  /* SLIMRX7_ENA */
+#define ARIZONA_SLIMRX7_ENA_SHIFT                     6  /* SLIMRX7_ENA */
+#define ARIZONA_SLIMRX7_ENA_WIDTH                     1  /* SLIMRX7_ENA */
+#define ARIZONA_SLIMRX6_ENA                      0x0020  /* SLIMRX6_ENA */
+#define ARIZONA_SLIMRX6_ENA_MASK                 0x0020  /* SLIMRX6_ENA */
+#define ARIZONA_SLIMRX6_ENA_SHIFT                     5  /* SLIMRX6_ENA */
+#define ARIZONA_SLIMRX6_ENA_WIDTH                     1  /* SLIMRX6_ENA */
+#define ARIZONA_SLIMRX5_ENA                      0x0010  /* SLIMRX5_ENA */
+#define ARIZONA_SLIMRX5_ENA_MASK                 0x0010  /* SLIMRX5_ENA */
+#define ARIZONA_SLIMRX5_ENA_SHIFT                     4  /* SLIMRX5_ENA */
+#define ARIZONA_SLIMRX5_ENA_WIDTH                     1  /* SLIMRX5_ENA */
+#define ARIZONA_SLIMRX4_ENA                      0x0008  /* SLIMRX4_ENA */
+#define ARIZONA_SLIMRX4_ENA_MASK                 0x0008  /* SLIMRX4_ENA */
+#define ARIZONA_SLIMRX4_ENA_SHIFT                     3  /* SLIMRX4_ENA */
+#define ARIZONA_SLIMRX4_ENA_WIDTH                     1  /* SLIMRX4_ENA */
+#define ARIZONA_SLIMRX3_ENA                      0x0004  /* SLIMRX3_ENA */
+#define ARIZONA_SLIMRX3_ENA_MASK                 0x0004  /* SLIMRX3_ENA */
+#define ARIZONA_SLIMRX3_ENA_SHIFT                     2  /* SLIMRX3_ENA */
+#define ARIZONA_SLIMRX3_ENA_WIDTH                     1  /* SLIMRX3_ENA */
+#define ARIZONA_SLIMRX2_ENA                      0x0002  /* SLIMRX2_ENA */
+#define ARIZONA_SLIMRX2_ENA_MASK                 0x0002  /* SLIMRX2_ENA */
+#define ARIZONA_SLIMRX2_ENA_SHIFT                     1  /* SLIMRX2_ENA */
+#define ARIZONA_SLIMRX2_ENA_WIDTH                     1  /* SLIMRX2_ENA */
+#define ARIZONA_SLIMRX1_ENA                      0x0001  /* SLIMRX1_ENA */
+#define ARIZONA_SLIMRX1_ENA_MASK                 0x0001  /* SLIMRX1_ENA */
+#define ARIZONA_SLIMRX1_ENA_SHIFT                     0  /* SLIMRX1_ENA */
+#define ARIZONA_SLIMRX1_ENA_WIDTH                     1  /* SLIMRX1_ENA */
+
+/*
+ * R1526 (0x5F6) - SLIMbus TX Channel Enable
+ */
+#define ARIZONA_SLIMTX8_ENA                      0x0080  /* SLIMTX8_ENA */
+#define ARIZONA_SLIMTX8_ENA_MASK                 0x0080  /* SLIMTX8_ENA */
+#define ARIZONA_SLIMTX8_ENA_SHIFT                     7  /* SLIMTX8_ENA */
+#define ARIZONA_SLIMTX8_ENA_WIDTH                     1  /* SLIMTX8_ENA */
+#define ARIZONA_SLIMTX7_ENA                      0x0040  /* SLIMTX7_ENA */
+#define ARIZONA_SLIMTX7_ENA_MASK                 0x0040  /* SLIMTX7_ENA */
+#define ARIZONA_SLIMTX7_ENA_SHIFT                     6  /* SLIMTX7_ENA */
+#define ARIZONA_SLIMTX7_ENA_WIDTH                     1  /* SLIMTX7_ENA */
+#define ARIZONA_SLIMTX6_ENA                      0x0020  /* SLIMTX6_ENA */
+#define ARIZONA_SLIMTX6_ENA_MASK                 0x0020  /* SLIMTX6_ENA */
+#define ARIZONA_SLIMTX6_ENA_SHIFT                     5  /* SLIMTX6_ENA */
+#define ARIZONA_SLIMTX6_ENA_WIDTH                     1  /* SLIMTX6_ENA */
+#define ARIZONA_SLIMTX5_ENA                      0x0010  /* SLIMTX5_ENA */
+#define ARIZONA_SLIMTX5_ENA_MASK                 0x0010  /* SLIMTX5_ENA */
+#define ARIZONA_SLIMTX5_ENA_SHIFT                     4  /* SLIMTX5_ENA */
+#define ARIZONA_SLIMTX5_ENA_WIDTH                     1  /* SLIMTX5_ENA */
+#define ARIZONA_SLIMTX4_ENA                      0x0008  /* SLIMTX4_ENA */
+#define ARIZONA_SLIMTX4_ENA_MASK                 0x0008  /* SLIMTX4_ENA */
+#define ARIZONA_SLIMTX4_ENA_SHIFT                     3  /* SLIMTX4_ENA */
+#define ARIZONA_SLIMTX4_ENA_WIDTH                     1  /* SLIMTX4_ENA */
+#define ARIZONA_SLIMTX3_ENA                      0x0004  /* SLIMTX3_ENA */
+#define ARIZONA_SLIMTX3_ENA_MASK                 0x0004  /* SLIMTX3_ENA */
+#define ARIZONA_SLIMTX3_ENA_SHIFT                     2  /* SLIMTX3_ENA */
+#define ARIZONA_SLIMTX3_ENA_WIDTH                     1  /* SLIMTX3_ENA */
+#define ARIZONA_SLIMTX2_ENA                      0x0002  /* SLIMTX2_ENA */
+#define ARIZONA_SLIMTX2_ENA_MASK                 0x0002  /* SLIMTX2_ENA */
+#define ARIZONA_SLIMTX2_ENA_SHIFT                     1  /* SLIMTX2_ENA */
+#define ARIZONA_SLIMTX2_ENA_WIDTH                     1  /* SLIMTX2_ENA */
+#define ARIZONA_SLIMTX1_ENA                      0x0001  /* SLIMTX1_ENA */
+#define ARIZONA_SLIMTX1_ENA_MASK                 0x0001  /* SLIMTX1_ENA */
+#define ARIZONA_SLIMTX1_ENA_SHIFT                     0  /* SLIMTX1_ENA */
+#define ARIZONA_SLIMTX1_ENA_WIDTH                     1  /* SLIMTX1_ENA */
+
+/*
+ * R1527 (0x5F7) - SLIMbus RX Port Status
+ */
+#define ARIZONA_SLIMRX8_PORT_STS                 0x0080  /* SLIMRX8_PORT_STS */
+#define ARIZONA_SLIMRX8_PORT_STS_MASK            0x0080  /* SLIMRX8_PORT_STS */
+#define ARIZONA_SLIMRX8_PORT_STS_SHIFT                7  /* SLIMRX8_PORT_STS */
+#define ARIZONA_SLIMRX8_PORT_STS_WIDTH                1  /* SLIMRX8_PORT_STS */
+#define ARIZONA_SLIMRX7_PORT_STS                 0x0040  /* SLIMRX7_PORT_STS */
+#define ARIZONA_SLIMRX7_PORT_STS_MASK            0x0040  /* SLIMRX7_PORT_STS */
+#define ARIZONA_SLIMRX7_PORT_STS_SHIFT                6  /* SLIMRX7_PORT_STS */
+#define ARIZONA_SLIMRX7_PORT_STS_WIDTH                1  /* SLIMRX7_PORT_STS */
+#define ARIZONA_SLIMRX6_PORT_STS                 0x0020  /* SLIMRX6_PORT_STS */
+#define ARIZONA_SLIMRX6_PORT_STS_MASK            0x0020  /* SLIMRX6_PORT_STS */
+#define ARIZONA_SLIMRX6_PORT_STS_SHIFT                5  /* SLIMRX6_PORT_STS */
+#define ARIZONA_SLIMRX6_PORT_STS_WIDTH                1  /* SLIMRX6_PORT_STS */
+#define ARIZONA_SLIMRX5_PORT_STS                 0x0010  /* SLIMRX5_PORT_STS */
+#define ARIZONA_SLIMRX5_PORT_STS_MASK            0x0010  /* SLIMRX5_PORT_STS */
+#define ARIZONA_SLIMRX5_PORT_STS_SHIFT                4  /* SLIMRX5_PORT_STS */
+#define ARIZONA_SLIMRX5_PORT_STS_WIDTH                1  /* SLIMRX5_PORT_STS */
+#define ARIZONA_SLIMRX4_PORT_STS                 0x0008  /* SLIMRX4_PORT_STS */
+#define ARIZONA_SLIMRX4_PORT_STS_MASK            0x0008  /* SLIMRX4_PORT_STS */
+#define ARIZONA_SLIMRX4_PORT_STS_SHIFT                3  /* SLIMRX4_PORT_STS */
+#define ARIZONA_SLIMRX4_PORT_STS_WIDTH                1  /* SLIMRX4_PORT_STS */
+#define ARIZONA_SLIMRX3_PORT_STS                 0x0004  /* SLIMRX3_PORT_STS */
+#define ARIZONA_SLIMRX3_PORT_STS_MASK            0x0004  /* SLIMRX3_PORT_STS */
+#define ARIZONA_SLIMRX3_PORT_STS_SHIFT                2  /* SLIMRX3_PORT_STS */
+#define ARIZONA_SLIMRX3_PORT_STS_WIDTH                1  /* SLIMRX3_PORT_STS */
+#define ARIZONA_SLIMRX2_PORT_STS                 0x0002  /* SLIMRX2_PORT_STS */
+#define ARIZONA_SLIMRX2_PORT_STS_MASK            0x0002  /* SLIMRX2_PORT_STS */
+#define ARIZONA_SLIMRX2_PORT_STS_SHIFT                1  /* SLIMRX2_PORT_STS */
+#define ARIZONA_SLIMRX2_PORT_STS_WIDTH                1  /* SLIMRX2_PORT_STS */
+#define ARIZONA_SLIMRX1_PORT_STS                 0x0001  /* SLIMRX1_PORT_STS */
+#define ARIZONA_SLIMRX1_PORT_STS_MASK            0x0001  /* SLIMRX1_PORT_STS */
+#define ARIZONA_SLIMRX1_PORT_STS_SHIFT                0  /* SLIMRX1_PORT_STS */
+#define ARIZONA_SLIMRX1_PORT_STS_WIDTH                1  /* SLIMRX1_PORT_STS */
+
+/*
+ * R1528 (0x5F8) - SLIMbus TX Port Status
+ */
+#define ARIZONA_SLIMTX8_PORT_STS                 0x0080  /* SLIMTX8_PORT_STS */
+#define ARIZONA_SLIMTX8_PORT_STS_MASK            0x0080  /* SLIMTX8_PORT_STS */
+#define ARIZONA_SLIMTX8_PORT_STS_SHIFT                7  /* SLIMTX8_PORT_STS */
+#define ARIZONA_SLIMTX8_PORT_STS_WIDTH                1  /* SLIMTX8_PORT_STS */
+#define ARIZONA_SLIMTX7_PORT_STS                 0x0040  /* SLIMTX7_PORT_STS */
+#define ARIZONA_SLIMTX7_PORT_STS_MASK            0x0040  /* SLIMTX7_PORT_STS */
+#define ARIZONA_SLIMTX7_PORT_STS_SHIFT                6  /* SLIMTX7_PORT_STS */
+#define ARIZONA_SLIMTX7_PORT_STS_WIDTH                1  /* SLIMTX7_PORT_STS */
+#define ARIZONA_SLIMTX6_PORT_STS                 0x0020  /* SLIMTX6_PORT_STS */
+#define ARIZONA_SLIMTX6_PORT_STS_MASK            0x0020  /* SLIMTX6_PORT_STS */
+#define ARIZONA_SLIMTX6_PORT_STS_SHIFT                5  /* SLIMTX6_PORT_STS */
+#define ARIZONA_SLIMTX6_PORT_STS_WIDTH                1  /* SLIMTX6_PORT_STS */
+#define ARIZONA_SLIMTX5_PORT_STS                 0x0010  /* SLIMTX5_PORT_STS */
+#define ARIZONA_SLIMTX5_PORT_STS_MASK            0x0010  /* SLIMTX5_PORT_STS */
+#define ARIZONA_SLIMTX5_PORT_STS_SHIFT                4  /* SLIMTX5_PORT_STS */
+#define ARIZONA_SLIMTX5_PORT_STS_WIDTH                1  /* SLIMTX5_PORT_STS */
+#define ARIZONA_SLIMTX4_PORT_STS                 0x0008  /* SLIMTX4_PORT_STS */
+#define ARIZONA_SLIMTX4_PORT_STS_MASK            0x0008  /* SLIMTX4_PORT_STS */
+#define ARIZONA_SLIMTX4_PORT_STS_SHIFT                3  /* SLIMTX4_PORT_STS */
+#define ARIZONA_SLIMTX4_PORT_STS_WIDTH                1  /* SLIMTX4_PORT_STS */
+#define ARIZONA_SLIMTX3_PORT_STS                 0x0004  /* SLIMTX3_PORT_STS */
+#define ARIZONA_SLIMTX3_PORT_STS_MASK            0x0004  /* SLIMTX3_PORT_STS */
+#define ARIZONA_SLIMTX3_PORT_STS_SHIFT                2  /* SLIMTX3_PORT_STS */
+#define ARIZONA_SLIMTX3_PORT_STS_WIDTH                1  /* SLIMTX3_PORT_STS */
+#define ARIZONA_SLIMTX2_PORT_STS                 0x0002  /* SLIMTX2_PORT_STS */
+#define ARIZONA_SLIMTX2_PORT_STS_MASK            0x0002  /* SLIMTX2_PORT_STS */
+#define ARIZONA_SLIMTX2_PORT_STS_SHIFT                1  /* SLIMTX2_PORT_STS */
+#define ARIZONA_SLIMTX2_PORT_STS_WIDTH                1  /* SLIMTX2_PORT_STS */
+#define ARIZONA_SLIMTX1_PORT_STS                 0x0001  /* SLIMTX1_PORT_STS */
+#define ARIZONA_SLIMTX1_PORT_STS_MASK            0x0001  /* SLIMTX1_PORT_STS */
+#define ARIZONA_SLIMTX1_PORT_STS_SHIFT                0  /* SLIMTX1_PORT_STS */
+#define ARIZONA_SLIMTX1_PORT_STS_WIDTH                1  /* SLIMTX1_PORT_STS */
+
+/*
+ * R3087 (0xC0F) - IRQ CTRL 1
+ */
+#define ARIZONA_IRQ_POL                          0x0400  /* IRQ_POL */
+#define ARIZONA_IRQ_POL_MASK                     0x0400  /* IRQ_POL */
+#define ARIZONA_IRQ_POL_SHIFT                        10  /* IRQ_POL */
+#define ARIZONA_IRQ_POL_WIDTH                         1  /* IRQ_POL */
+#define ARIZONA_IRQ_OP_CFG                       0x0200  /* IRQ_OP_CFG */
+#define ARIZONA_IRQ_OP_CFG_MASK                  0x0200  /* IRQ_OP_CFG */
+#define ARIZONA_IRQ_OP_CFG_SHIFT                      9  /* IRQ_OP_CFG */
+#define ARIZONA_IRQ_OP_CFG_WIDTH                      1  /* IRQ_OP_CFG */
+
+/*
+ * R3088 (0xC10) - GPIO Debounce Config
+ */
+#define ARIZONA_GP_DBTIME_MASK                   0xF000  /* GP_DBTIME - [15:12] */
+#define ARIZONA_GP_DBTIME_SHIFT                      12  /* GP_DBTIME - [15:12] */
+#define ARIZONA_GP_DBTIME_WIDTH                       4  /* GP_DBTIME - [15:12] */
+
+/*
+ * R3104 (0xC20) - Misc Pad Ctrl 1
+ */
+#define ARIZONA_LDO1ENA_PD                       0x8000  /* LDO1ENA_PD */
+#define ARIZONA_LDO1ENA_PD_MASK                  0x8000  /* LDO1ENA_PD */
+#define ARIZONA_LDO1ENA_PD_SHIFT                     15  /* LDO1ENA_PD */
+#define ARIZONA_LDO1ENA_PD_WIDTH                      1  /* LDO1ENA_PD */
+#define ARIZONA_MCLK2_PD                         0x2000  /* MCLK2_PD */
+#define ARIZONA_MCLK2_PD_MASK                    0x2000  /* MCLK2_PD */
+#define ARIZONA_MCLK2_PD_SHIFT                       13  /* MCLK2_PD */
+#define ARIZONA_MCLK2_PD_WIDTH                        1  /* MCLK2_PD */
+#define ARIZONA_RSTB_PU                          0x0002  /* RSTB_PU */
+#define ARIZONA_RSTB_PU_MASK                     0x0002  /* RSTB_PU */
+#define ARIZONA_RSTB_PU_SHIFT                         1  /* RSTB_PU */
+#define ARIZONA_RSTB_PU_WIDTH                         1  /* RSTB_PU */
+
+/*
+ * R3105 (0xC21) - Misc Pad Ctrl 2
+ */
+#define ARIZONA_MCLK1_PD                         0x1000  /* MCLK1_PD */
+#define ARIZONA_MCLK1_PD_MASK                    0x1000  /* MCLK1_PD */
+#define ARIZONA_MCLK1_PD_SHIFT                       12  /* MCLK1_PD */
+#define ARIZONA_MCLK1_PD_WIDTH                        1  /* MCLK1_PD */
+#define ARIZONA_MICD_PD                          0x0100  /* MICD_PD */
+#define ARIZONA_MICD_PD_MASK                     0x0100  /* MICD_PD */
+#define ARIZONA_MICD_PD_SHIFT                         8  /* MICD_PD */
+#define ARIZONA_MICD_PD_WIDTH                         1  /* MICD_PD */
+#define ARIZONA_ADDR_PD                          0x0001  /* ADDR_PD */
+#define ARIZONA_ADDR_PD_MASK                     0x0001  /* ADDR_PD */
+#define ARIZONA_ADDR_PD_SHIFT                         0  /* ADDR_PD */
+#define ARIZONA_ADDR_PD_WIDTH                         1  /* ADDR_PD */
+
+/*
+ * R3106 (0xC22) - Misc Pad Ctrl 3
+ */
+#define ARIZONA_DMICDAT4_PD                      0x0008  /* DMICDAT4_PD */
+#define ARIZONA_DMICDAT4_PD_MASK                 0x0008  /* DMICDAT4_PD */
+#define ARIZONA_DMICDAT4_PD_SHIFT                     3  /* DMICDAT4_PD */
+#define ARIZONA_DMICDAT4_PD_WIDTH                     1  /* DMICDAT4_PD */
+#define ARIZONA_DMICDAT3_PD                      0x0004  /* DMICDAT3_PD */
+#define ARIZONA_DMICDAT3_PD_MASK                 0x0004  /* DMICDAT3_PD */
+#define ARIZONA_DMICDAT3_PD_SHIFT                     2  /* DMICDAT3_PD */
+#define ARIZONA_DMICDAT3_PD_WIDTH                     1  /* DMICDAT3_PD */
+#define ARIZONA_DMICDAT2_PD                      0x0002  /* DMICDAT2_PD */
+#define ARIZONA_DMICDAT2_PD_MASK                 0x0002  /* DMICDAT2_PD */
+#define ARIZONA_DMICDAT2_PD_SHIFT                     1  /* DMICDAT2_PD */
+#define ARIZONA_DMICDAT2_PD_WIDTH                     1  /* DMICDAT2_PD */
+#define ARIZONA_DMICDAT1_PD                      0x0001  /* DMICDAT1_PD */
+#define ARIZONA_DMICDAT1_PD_MASK                 0x0001  /* DMICDAT1_PD */
+#define ARIZONA_DMICDAT1_PD_SHIFT                     0  /* DMICDAT1_PD */
+#define ARIZONA_DMICDAT1_PD_WIDTH                     1  /* DMICDAT1_PD */
+
+/*
+ * R3107 (0xC23) - Misc Pad Ctrl 4
+ */
+#define ARIZONA_AIF1RXLRCLK_PU                   0x0020  /* AIF1RXLRCLK_PU */
+#define ARIZONA_AIF1RXLRCLK_PU_MASK              0x0020  /* AIF1RXLRCLK_PU */
+#define ARIZONA_AIF1RXLRCLK_PU_SHIFT                  5  /* AIF1RXLRCLK_PU */
+#define ARIZONA_AIF1RXLRCLK_PU_WIDTH                  1  /* AIF1RXLRCLK_PU */
+#define ARIZONA_AIF1RXLRCLK_PD                   0x0010  /* AIF1RXLRCLK_PD */
+#define ARIZONA_AIF1RXLRCLK_PD_MASK              0x0010  /* AIF1RXLRCLK_PD */
+#define ARIZONA_AIF1RXLRCLK_PD_SHIFT                  4  /* AIF1RXLRCLK_PD */
+#define ARIZONA_AIF1RXLRCLK_PD_WIDTH                  1  /* AIF1RXLRCLK_PD */
+#define ARIZONA_AIF1BCLK_PU                      0x0008  /* AIF1BCLK_PU */
+#define ARIZONA_AIF1BCLK_PU_MASK                 0x0008  /* AIF1BCLK_PU */
+#define ARIZONA_AIF1BCLK_PU_SHIFT                     3  /* AIF1BCLK_PU */
+#define ARIZONA_AIF1BCLK_PU_WIDTH                     1  /* AIF1BCLK_PU */
+#define ARIZONA_AIF1BCLK_PD                      0x0004  /* AIF1BCLK_PD */
+#define ARIZONA_AIF1BCLK_PD_MASK                 0x0004  /* AIF1BCLK_PD */
+#define ARIZONA_AIF1BCLK_PD_SHIFT                     2  /* AIF1BCLK_PD */
+#define ARIZONA_AIF1BCLK_PD_WIDTH                     1  /* AIF1BCLK_PD */
+#define ARIZONA_AIF1RXDAT_PU                     0x0002  /* AIF1RXDAT_PU */
+#define ARIZONA_AIF1RXDAT_PU_MASK                0x0002  /* AIF1RXDAT_PU */
+#define ARIZONA_AIF1RXDAT_PU_SHIFT                    1  /* AIF1RXDAT_PU */
+#define ARIZONA_AIF1RXDAT_PU_WIDTH                    1  /* AIF1RXDAT_PU */
+#define ARIZONA_AIF1RXDAT_PD                     0x0001  /* AIF1RXDAT_PD */
+#define ARIZONA_AIF1RXDAT_PD_MASK                0x0001  /* AIF1RXDAT_PD */
+#define ARIZONA_AIF1RXDAT_PD_SHIFT                    0  /* AIF1RXDAT_PD */
+#define ARIZONA_AIF1RXDAT_PD_WIDTH                    1  /* AIF1RXDAT_PD */
+
+/*
+ * R3108 (0xC24) - Misc Pad Ctrl 5
+ */
+#define ARIZONA_AIF2RXLRCLK_PU                   0x0020  /* AIF2RXLRCLK_PU */
+#define ARIZONA_AIF2RXLRCLK_PU_MASK              0x0020  /* AIF2RXLRCLK_PU */
+#define ARIZONA_AIF2RXLRCLK_PU_SHIFT                  5  /* AIF2RXLRCLK_PU */
+#define ARIZONA_AIF2RXLRCLK_PU_WIDTH                  1  /* AIF2RXLRCLK_PU */
+#define ARIZONA_AIF2RXLRCLK_PD                   0x0010  /* AIF2RXLRCLK_PD */
+#define ARIZONA_AIF2RXLRCLK_PD_MASK              0x0010  /* AIF2RXLRCLK_PD */
+#define ARIZONA_AIF2RXLRCLK_PD_SHIFT                  4  /* AIF2RXLRCLK_PD */
+#define ARIZONA_AIF2RXLRCLK_PD_WIDTH                  1  /* AIF2RXLRCLK_PD */
+#define ARIZONA_AIF2BCLK_PU                      0x0008  /* AIF2BCLK_PU */
+#define ARIZONA_AIF2BCLK_PU_MASK                 0x0008  /* AIF2BCLK_PU */
+#define ARIZONA_AIF2BCLK_PU_SHIFT                     3  /* AIF2BCLK_PU */
+#define ARIZONA_AIF2BCLK_PU_WIDTH                     1  /* AIF2BCLK_PU */
+#define ARIZONA_AIF2BCLK_PD                      0x0004  /* AIF2BCLK_PD */
+#define ARIZONA_AIF2BCLK_PD_MASK                 0x0004  /* AIF2BCLK_PD */
+#define ARIZONA_AIF2BCLK_PD_SHIFT                     2  /* AIF2BCLK_PD */
+#define ARIZONA_AIF2BCLK_PD_WIDTH                     1  /* AIF2BCLK_PD */
+#define ARIZONA_AIF2RXDAT_PU                     0x0002  /* AIF2RXDAT_PU */
+#define ARIZONA_AIF2RXDAT_PU_MASK                0x0002  /* AIF2RXDAT_PU */
+#define ARIZONA_AIF2RXDAT_PU_SHIFT                    1  /* AIF2RXDAT_PU */
+#define ARIZONA_AIF2RXDAT_PU_WIDTH                    1  /* AIF2RXDAT_PU */
+#define ARIZONA_AIF2RXDAT_PD                     0x0001  /* AIF2RXDAT_PD */
+#define ARIZONA_AIF2RXDAT_PD_MASK                0x0001  /* AIF2RXDAT_PD */
+#define ARIZONA_AIF2RXDAT_PD_SHIFT                    0  /* AIF2RXDAT_PD */
+#define ARIZONA_AIF2RXDAT_PD_WIDTH                    1  /* AIF2RXDAT_PD */
+
+/*
+ * R3109 (0xC25) - Misc Pad Ctrl 6
+ */
+#define ARIZONA_AIF3RXLRCLK_PU                   0x0020  /* AIF3RXLRCLK_PU */
+#define ARIZONA_AIF3RXLRCLK_PU_MASK              0x0020  /* AIF3RXLRCLK_PU */
+#define ARIZONA_AIF3RXLRCLK_PU_SHIFT                  5  /* AIF3RXLRCLK_PU */
+#define ARIZONA_AIF3RXLRCLK_PU_WIDTH                  1  /* AIF3RXLRCLK_PU */
+#define ARIZONA_AIF3RXLRCLK_PD                   0x0010  /* AIF3RXLRCLK_PD */
+#define ARIZONA_AIF3RXLRCLK_PD_MASK              0x0010  /* AIF3RXLRCLK_PD */
+#define ARIZONA_AIF3RXLRCLK_PD_SHIFT                  4  /* AIF3RXLRCLK_PD */
+#define ARIZONA_AIF3RXLRCLK_PD_WIDTH                  1  /* AIF3RXLRCLK_PD */
+#define ARIZONA_AIF3BCLK_PU                      0x0008  /* AIF3BCLK_PU */
+#define ARIZONA_AIF3BCLK_PU_MASK                 0x0008  /* AIF3BCLK_PU */
+#define ARIZONA_AIF3BCLK_PU_SHIFT                     3  /* AIF3BCLK_PU */
+#define ARIZONA_AIF3BCLK_PU_WIDTH                     1  /* AIF3BCLK_PU */
+#define ARIZONA_AIF3BCLK_PD                      0x0004  /* AIF3BCLK_PD */
+#define ARIZONA_AIF3BCLK_PD_MASK                 0x0004  /* AIF3BCLK_PD */
+#define ARIZONA_AIF3BCLK_PD_SHIFT                     2  /* AIF3BCLK_PD */
+#define ARIZONA_AIF3BCLK_PD_WIDTH                     1  /* AIF3BCLK_PD */
+#define ARIZONA_AIF3RXDAT_PU                     0x0002  /* AIF3RXDAT_PU */
+#define ARIZONA_AIF3RXDAT_PU_MASK                0x0002  /* AIF3RXDAT_PU */
+#define ARIZONA_AIF3RXDAT_PU_SHIFT                    1  /* AIF3RXDAT_PU */
+#define ARIZONA_AIF3RXDAT_PU_WIDTH                    1  /* AIF3RXDAT_PU */
+#define ARIZONA_AIF3RXDAT_PD                     0x0001  /* AIF3RXDAT_PD */
+#define ARIZONA_AIF3RXDAT_PD_MASK                0x0001  /* AIF3RXDAT_PD */
+#define ARIZONA_AIF3RXDAT_PD_SHIFT                    0  /* AIF3RXDAT_PD */
+#define ARIZONA_AIF3RXDAT_PD_WIDTH                    1  /* AIF3RXDAT_PD */
+
+/*
+ * R3328 (0xD00) - Interrupt Status 1
+ */
+#define ARIZONA_GP4_EINT1                        0x0008  /* GP4_EINT1 */
+#define ARIZONA_GP4_EINT1_MASK                   0x0008  /* GP4_EINT1 */
+#define ARIZONA_GP4_EINT1_SHIFT                       3  /* GP4_EINT1 */
+#define ARIZONA_GP4_EINT1_WIDTH                       1  /* GP4_EINT1 */
+#define ARIZONA_GP3_EINT1                        0x0004  /* GP3_EINT1 */
+#define ARIZONA_GP3_EINT1_MASK                   0x0004  /* GP3_EINT1 */
+#define ARIZONA_GP3_EINT1_SHIFT                       2  /* GP3_EINT1 */
+#define ARIZONA_GP3_EINT1_WIDTH                       1  /* GP3_EINT1 */
+#define ARIZONA_GP2_EINT1                        0x0002  /* GP2_EINT1 */
+#define ARIZONA_GP2_EINT1_MASK                   0x0002  /* GP2_EINT1 */
+#define ARIZONA_GP2_EINT1_SHIFT                       1  /* GP2_EINT1 */
+#define ARIZONA_GP2_EINT1_WIDTH                       1  /* GP2_EINT1 */
+#define ARIZONA_GP1_EINT1                        0x0001  /* GP1_EINT1 */
+#define ARIZONA_GP1_EINT1_MASK                   0x0001  /* GP1_EINT1 */
+#define ARIZONA_GP1_EINT1_SHIFT                       0  /* GP1_EINT1 */
+#define ARIZONA_GP1_EINT1_WIDTH                       1  /* GP1_EINT1 */
+
+/*
+ * R3329 (0xD01) - Interrupt Status 2
+ */
+#define ARIZONA_DSP4_RAM_RDY_EINT1               0x0800  /* DSP4_RAM_RDY_EINT1 */
+#define ARIZONA_DSP4_RAM_RDY_EINT1_MASK          0x0800  /* DSP4_RAM_RDY_EINT1 */
+#define ARIZONA_DSP4_RAM_RDY_EINT1_SHIFT             11  /* DSP4_RAM_RDY_EINT1 */
+#define ARIZONA_DSP4_RAM_RDY_EINT1_WIDTH              1  /* DSP4_RAM_RDY_EINT1 */
+#define ARIZONA_DSP3_RAM_RDY_EINT1               0x0400  /* DSP3_RAM_RDY_EINT1 */
+#define ARIZONA_DSP3_RAM_RDY_EINT1_MASK          0x0400  /* DSP3_RAM_RDY_EINT1 */
+#define ARIZONA_DSP3_RAM_RDY_EINT1_SHIFT             10  /* DSP3_RAM_RDY_EINT1 */
+#define ARIZONA_DSP3_RAM_RDY_EINT1_WIDTH              1  /* DSP3_RAM_RDY_EINT1 */
+#define ARIZONA_DSP2_RAM_RDY_EINT1               0x0200  /* DSP2_RAM_RDY_EINT1 */
+#define ARIZONA_DSP2_RAM_RDY_EINT1_MASK          0x0200  /* DSP2_RAM_RDY_EINT1 */
+#define ARIZONA_DSP2_RAM_RDY_EINT1_SHIFT              9  /* DSP2_RAM_RDY_EINT1 */
+#define ARIZONA_DSP2_RAM_RDY_EINT1_WIDTH              1  /* DSP2_RAM_RDY_EINT1 */
+#define ARIZONA_DSP1_RAM_RDY_EINT1               0x0100  /* DSP1_RAM_RDY_EINT1 */
+#define ARIZONA_DSP1_RAM_RDY_EINT1_MASK          0x0100  /* DSP1_RAM_RDY_EINT1 */
+#define ARIZONA_DSP1_RAM_RDY_EINT1_SHIFT              8  /* DSP1_RAM_RDY_EINT1 */
+#define ARIZONA_DSP1_RAM_RDY_EINT1_WIDTH              1  /* DSP1_RAM_RDY_EINT1 */
+#define ARIZONA_DSP_IRQ8_EINT1                   0x0080  /* DSP_IRQ8_EINT1 */
+#define ARIZONA_DSP_IRQ8_EINT1_MASK              0x0080  /* DSP_IRQ8_EINT1 */
+#define ARIZONA_DSP_IRQ8_EINT1_SHIFT                  7  /* DSP_IRQ8_EINT1 */
+#define ARIZONA_DSP_IRQ8_EINT1_WIDTH                  1  /* DSP_IRQ8_EINT1 */
+#define ARIZONA_DSP_IRQ7_EINT1                   0x0040  /* DSP_IRQ7_EINT1 */
+#define ARIZONA_DSP_IRQ7_EINT1_MASK              0x0040  /* DSP_IRQ7_EINT1 */
+#define ARIZONA_DSP_IRQ7_EINT1_SHIFT                  6  /* DSP_IRQ7_EINT1 */
+#define ARIZONA_DSP_IRQ7_EINT1_WIDTH                  1  /* DSP_IRQ7_EINT1 */
+#define ARIZONA_DSP_IRQ6_EINT1                   0x0020  /* DSP_IRQ6_EINT1 */
+#define ARIZONA_DSP_IRQ6_EINT1_MASK              0x0020  /* DSP_IRQ6_EINT1 */
+#define ARIZONA_DSP_IRQ6_EINT1_SHIFT                  5  /* DSP_IRQ6_EINT1 */
+#define ARIZONA_DSP_IRQ6_EINT1_WIDTH                  1  /* DSP_IRQ6_EINT1 */
+#define ARIZONA_DSP_IRQ5_EINT1                   0x0010  /* DSP_IRQ5_EINT1 */
+#define ARIZONA_DSP_IRQ5_EINT1_MASK              0x0010  /* DSP_IRQ5_EINT1 */
+#define ARIZONA_DSP_IRQ5_EINT1_SHIFT                  4  /* DSP_IRQ5_EINT1 */
+#define ARIZONA_DSP_IRQ5_EINT1_WIDTH                  1  /* DSP_IRQ5_EINT1 */
+#define ARIZONA_DSP_IRQ4_EINT1                   0x0008  /* DSP_IRQ4_EINT1 */
+#define ARIZONA_DSP_IRQ4_EINT1_MASK              0x0008  /* DSP_IRQ4_EINT1 */
+#define ARIZONA_DSP_IRQ4_EINT1_SHIFT                  3  /* DSP_IRQ4_EINT1 */
+#define ARIZONA_DSP_IRQ4_EINT1_WIDTH                  1  /* DSP_IRQ4_EINT1 */
+#define ARIZONA_DSP_IRQ3_EINT1                   0x0004  /* DSP_IRQ3_EINT1 */
+#define ARIZONA_DSP_IRQ3_EINT1_MASK              0x0004  /* DSP_IRQ3_EINT1 */
+#define ARIZONA_DSP_IRQ3_EINT1_SHIFT                  2  /* DSP_IRQ3_EINT1 */
+#define ARIZONA_DSP_IRQ3_EINT1_WIDTH                  1  /* DSP_IRQ3_EINT1 */
+#define ARIZONA_DSP_IRQ2_EINT1                   0x0002  /* DSP_IRQ2_EINT1 */
+#define ARIZONA_DSP_IRQ2_EINT1_MASK              0x0002  /* DSP_IRQ2_EINT1 */
+#define ARIZONA_DSP_IRQ2_EINT1_SHIFT                  1  /* DSP_IRQ2_EINT1 */
+#define ARIZONA_DSP_IRQ2_EINT1_WIDTH                  1  /* DSP_IRQ2_EINT1 */
+#define ARIZONA_DSP_IRQ1_EINT1                   0x0001  /* DSP_IRQ1_EINT1 */
+#define ARIZONA_DSP_IRQ1_EINT1_MASK              0x0001  /* DSP_IRQ1_EINT1 */
+#define ARIZONA_DSP_IRQ1_EINT1_SHIFT                  0  /* DSP_IRQ1_EINT1 */
+#define ARIZONA_DSP_IRQ1_EINT1_WIDTH                  1  /* DSP_IRQ1_EINT1 */
+
+/*
+ * R3330 (0xD02) - Interrupt Status 3
+ */
+#define ARIZONA_SPK_SHUTDOWN_WARN_EINT1          0x8000  /* SPK_SHUTDOWN_WARN_EINT1 */
+#define ARIZONA_SPK_SHUTDOWN_WARN_EINT1_MASK     0x8000  /* SPK_SHUTDOWN_WARN_EINT1 */
+#define ARIZONA_SPK_SHUTDOWN_WARN_EINT1_SHIFT        15  /* SPK_SHUTDOWN_WARN_EINT1 */
+#define ARIZONA_SPK_SHUTDOWN_WARN_EINT1_WIDTH         1  /* SPK_SHUTDOWN_WARN_EINT1 */
+#define ARIZONA_SPK_SHUTDOWN_EINT1               0x4000  /* SPK_SHUTDOWN_EINT1 */
+#define ARIZONA_SPK_SHUTDOWN_EINT1_MASK          0x4000  /* SPK_SHUTDOWN_EINT1 */
+#define ARIZONA_SPK_SHUTDOWN_EINT1_SHIFT             14  /* SPK_SHUTDOWN_EINT1 */
+#define ARIZONA_SPK_SHUTDOWN_EINT1_WIDTH              1  /* SPK_SHUTDOWN_EINT1 */
+#define ARIZONA_HPDET_EINT1                      0x2000  /* HPDET_EINT1 */
+#define ARIZONA_HPDET_EINT1_MASK                 0x2000  /* HPDET_EINT1 */
+#define ARIZONA_HPDET_EINT1_SHIFT                    13  /* HPDET_EINT1 */
+#define ARIZONA_HPDET_EINT1_WIDTH                     1  /* HPDET_EINT1 */
+#define ARIZONA_MICDET_EINT1                     0x1000  /* MICDET_EINT1 */
+#define ARIZONA_MICDET_EINT1_MASK                0x1000  /* MICDET_EINT1 */
+#define ARIZONA_MICDET_EINT1_SHIFT                   12  /* MICDET_EINT1 */
+#define ARIZONA_MICDET_EINT1_WIDTH                    1  /* MICDET_EINT1 */
+#define ARIZONA_WSEQ_DONE_EINT1                  0x0800  /* WSEQ_DONE_EINT1 */
+#define ARIZONA_WSEQ_DONE_EINT1_MASK             0x0800  /* WSEQ_DONE_EINT1 */
+#define ARIZONA_WSEQ_DONE_EINT1_SHIFT                11  /* WSEQ_DONE_EINT1 */
+#define ARIZONA_WSEQ_DONE_EINT1_WIDTH                 1  /* WSEQ_DONE_EINT1 */
+#define ARIZONA_DRC2_SIG_DET_EINT1               0x0400  /* DRC2_SIG_DET_EINT1 */
+#define ARIZONA_DRC2_SIG_DET_EINT1_MASK          0x0400  /* DRC2_SIG_DET_EINT1 */
+#define ARIZONA_DRC2_SIG_DET_EINT1_SHIFT             10  /* DRC2_SIG_DET_EINT1 */
+#define ARIZONA_DRC2_SIG_DET_EINT1_WIDTH              1  /* DRC2_SIG_DET_EINT1 */
+#define ARIZONA_DRC1_SIG_DET_EINT1               0x0200  /* DRC1_SIG_DET_EINT1 */
+#define ARIZONA_DRC1_SIG_DET_EINT1_MASK          0x0200  /* DRC1_SIG_DET_EINT1 */
+#define ARIZONA_DRC1_SIG_DET_EINT1_SHIFT              9  /* DRC1_SIG_DET_EINT1 */
+#define ARIZONA_DRC1_SIG_DET_EINT1_WIDTH              1  /* DRC1_SIG_DET_EINT1 */
+#define ARIZONA_ASRC2_LOCK_EINT1                 0x0100  /* ASRC2_LOCK_EINT1 */
+#define ARIZONA_ASRC2_LOCK_EINT1_MASK            0x0100  /* ASRC2_LOCK_EINT1 */
+#define ARIZONA_ASRC2_LOCK_EINT1_SHIFT                8  /* ASRC2_LOCK_EINT1 */
+#define ARIZONA_ASRC2_LOCK_EINT1_WIDTH                1  /* ASRC2_LOCK_EINT1 */
+#define ARIZONA_ASRC1_LOCK_EINT1                 0x0080  /* ASRC1_LOCK_EINT1 */
+#define ARIZONA_ASRC1_LOCK_EINT1_MASK            0x0080  /* ASRC1_LOCK_EINT1 */
+#define ARIZONA_ASRC1_LOCK_EINT1_SHIFT                7  /* ASRC1_LOCK_EINT1 */
+#define ARIZONA_ASRC1_LOCK_EINT1_WIDTH                1  /* ASRC1_LOCK_EINT1 */
+#define ARIZONA_UNDERCLOCKED_EINT1               0x0040  /* UNDERCLOCKED_EINT1 */
+#define ARIZONA_UNDERCLOCKED_EINT1_MASK          0x0040  /* UNDERCLOCKED_EINT1 */
+#define ARIZONA_UNDERCLOCKED_EINT1_SHIFT              6  /* UNDERCLOCKED_EINT1 */
+#define ARIZONA_UNDERCLOCKED_EINT1_WIDTH              1  /* UNDERCLOCKED_EINT1 */
+#define ARIZONA_OVERCLOCKED_EINT1                0x0020  /* OVERCLOCKED_EINT1 */
+#define ARIZONA_OVERCLOCKED_EINT1_MASK           0x0020  /* OVERCLOCKED_EINT1 */
+#define ARIZONA_OVERCLOCKED_EINT1_SHIFT               5  /* OVERCLOCKED_EINT1 */
+#define ARIZONA_OVERCLOCKED_EINT1_WIDTH               1  /* OVERCLOCKED_EINT1 */
+#define ARIZONA_FLL2_LOCK_EINT1                  0x0008  /* FLL2_LOCK_EINT1 */
+#define ARIZONA_FLL2_LOCK_EINT1_MASK             0x0008  /* FLL2_LOCK_EINT1 */
+#define ARIZONA_FLL2_LOCK_EINT1_SHIFT                 3  /* FLL2_LOCK_EINT1 */
+#define ARIZONA_FLL2_LOCK_EINT1_WIDTH                 1  /* FLL2_LOCK_EINT1 */
+#define ARIZONA_FLL1_LOCK_EINT1                  0x0004  /* FLL1_LOCK_EINT1 */
+#define ARIZONA_FLL1_LOCK_EINT1_MASK             0x0004  /* FLL1_LOCK_EINT1 */
+#define ARIZONA_FLL1_LOCK_EINT1_SHIFT                 2  /* FLL1_LOCK_EINT1 */
+#define ARIZONA_FLL1_LOCK_EINT1_WIDTH                 1  /* FLL1_LOCK_EINT1 */
+#define ARIZONA_CLKGEN_ERR_EINT1                 0x0002  /* CLKGEN_ERR_EINT1 */
+#define ARIZONA_CLKGEN_ERR_EINT1_MASK            0x0002  /* CLKGEN_ERR_EINT1 */
+#define ARIZONA_CLKGEN_ERR_EINT1_SHIFT                1  /* CLKGEN_ERR_EINT1 */
+#define ARIZONA_CLKGEN_ERR_EINT1_WIDTH                1  /* CLKGEN_ERR_EINT1 */
+#define ARIZONA_CLKGEN_ERR_ASYNC_EINT1           0x0001  /* CLKGEN_ERR_ASYNC_EINT1 */
+#define ARIZONA_CLKGEN_ERR_ASYNC_EINT1_MASK      0x0001  /* CLKGEN_ERR_ASYNC_EINT1 */
+#define ARIZONA_CLKGEN_ERR_ASYNC_EINT1_SHIFT          0  /* CLKGEN_ERR_ASYNC_EINT1 */
+#define ARIZONA_CLKGEN_ERR_ASYNC_EINT1_WIDTH          1  /* CLKGEN_ERR_ASYNC_EINT1 */
+
+/*
+ * R3331 (0xD03) - Interrupt Status 4
+ */
+#define ARIZONA_ASRC_CFG_ERR_EINT1               0x8000  /* ASRC_CFG_ERR_EINT1 */
+#define ARIZONA_ASRC_CFG_ERR_EINT1_MASK          0x8000  /* ASRC_CFG_ERR_EINT1 */
+#define ARIZONA_ASRC_CFG_ERR_EINT1_SHIFT             15  /* ASRC_CFG_ERR_EINT1 */
+#define ARIZONA_ASRC_CFG_ERR_EINT1_WIDTH              1  /* ASRC_CFG_ERR_EINT1 */
+#define ARIZONA_AIF3_ERR_EINT1                   0x4000  /* AIF3_ERR_EINT1 */
+#define ARIZONA_AIF3_ERR_EINT1_MASK              0x4000  /* AIF3_ERR_EINT1 */
+#define ARIZONA_AIF3_ERR_EINT1_SHIFT                 14  /* AIF3_ERR_EINT1 */
+#define ARIZONA_AIF3_ERR_EINT1_WIDTH                  1  /* AIF3_ERR_EINT1 */
+#define ARIZONA_AIF2_ERR_EINT1                   0x2000  /* AIF2_ERR_EINT1 */
+#define ARIZONA_AIF2_ERR_EINT1_MASK              0x2000  /* AIF2_ERR_EINT1 */
+#define ARIZONA_AIF2_ERR_EINT1_SHIFT                 13  /* AIF2_ERR_EINT1 */
+#define ARIZONA_AIF2_ERR_EINT1_WIDTH                  1  /* AIF2_ERR_EINT1 */
+#define ARIZONA_AIF1_ERR_EINT1                   0x1000  /* AIF1_ERR_EINT1 */
+#define ARIZONA_AIF1_ERR_EINT1_MASK              0x1000  /* AIF1_ERR_EINT1 */
+#define ARIZONA_AIF1_ERR_EINT1_SHIFT                 12  /* AIF1_ERR_EINT1 */
+#define ARIZONA_AIF1_ERR_EINT1_WIDTH                  1  /* AIF1_ERR_EINT1 */
+#define ARIZONA_CTRLIF_ERR_EINT1                 0x0800  /* CTRLIF_ERR_EINT1 */
+#define ARIZONA_CTRLIF_ERR_EINT1_MASK            0x0800  /* CTRLIF_ERR_EINT1 */
+#define ARIZONA_CTRLIF_ERR_EINT1_SHIFT               11  /* CTRLIF_ERR_EINT1 */
+#define ARIZONA_CTRLIF_ERR_EINT1_WIDTH                1  /* CTRLIF_ERR_EINT1 */
+#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT1       0x0400  /* MIXER_DROPPED_SAMPLE_EINT1 */
+#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT1_MASK  0x0400  /* MIXER_DROPPED_SAMPLE_EINT1 */
+#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT1_SHIFT     10  /* MIXER_DROPPED_SAMPLE_EINT1 */
+#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT1_WIDTH      1  /* MIXER_DROPPED_SAMPLE_EINT1 */
+#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT1          0x0200  /* ASYNC_CLK_ENA_LOW_EINT1 */
+#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT1_MASK     0x0200  /* ASYNC_CLK_ENA_LOW_EINT1 */
+#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT1_SHIFT         9  /* ASYNC_CLK_ENA_LOW_EINT1 */
+#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT1_WIDTH         1  /* ASYNC_CLK_ENA_LOW_EINT1 */
+#define ARIZONA_SYSCLK_ENA_LOW_EINT1             0x0100  /* SYSCLK_ENA_LOW_EINT1 */
+#define ARIZONA_SYSCLK_ENA_LOW_EINT1_MASK        0x0100  /* SYSCLK_ENA_LOW_EINT1 */
+#define ARIZONA_SYSCLK_ENA_LOW_EINT1_SHIFT            8  /* SYSCLK_ENA_LOW_EINT1 */
+#define ARIZONA_SYSCLK_ENA_LOW_EINT1_WIDTH            1  /* SYSCLK_ENA_LOW_EINT1 */
+#define ARIZONA_ISRC1_CFG_ERR_EINT1              0x0080  /* ISRC1_CFG_ERR_EINT1 */
+#define ARIZONA_ISRC1_CFG_ERR_EINT1_MASK         0x0080  /* ISRC1_CFG_ERR_EINT1 */
+#define ARIZONA_ISRC1_CFG_ERR_EINT1_SHIFT             7  /* ISRC1_CFG_ERR_EINT1 */
+#define ARIZONA_ISRC1_CFG_ERR_EINT1_WIDTH             1  /* ISRC1_CFG_ERR_EINT1 */
+#define ARIZONA_ISRC2_CFG_ERR_EINT1              0x0040  /* ISRC2_CFG_ERR_EINT1 */
+#define ARIZONA_ISRC2_CFG_ERR_EINT1_MASK         0x0040  /* ISRC2_CFG_ERR_EINT1 */
+#define ARIZONA_ISRC2_CFG_ERR_EINT1_SHIFT             6  /* ISRC2_CFG_ERR_EINT1 */
+#define ARIZONA_ISRC2_CFG_ERR_EINT1_WIDTH             1  /* ISRC2_CFG_ERR_EINT1 */
+
+/*
+ * R3332 (0xD04) - Interrupt Status 5
+ */
+#define ARIZONA_BOOT_DONE_EINT1                  0x0100  /* BOOT_DONE_EINT1 */
+#define ARIZONA_BOOT_DONE_EINT1_MASK             0x0100  /* BOOT_DONE_EINT1 */
+#define ARIZONA_BOOT_DONE_EINT1_SHIFT                 8  /* BOOT_DONE_EINT1 */
+#define ARIZONA_BOOT_DONE_EINT1_WIDTH                 1  /* BOOT_DONE_EINT1 */
+#define ARIZONA_DCS_DAC_DONE_EINT1               0x0080  /* DCS_DAC_DONE_EINT1 */
+#define ARIZONA_DCS_DAC_DONE_EINT1_MASK          0x0080  /* DCS_DAC_DONE_EINT1 */
+#define ARIZONA_DCS_DAC_DONE_EINT1_SHIFT              7  /* DCS_DAC_DONE_EINT1 */
+#define ARIZONA_DCS_DAC_DONE_EINT1_WIDTH              1  /* DCS_DAC_DONE_EINT1 */
+#define ARIZONA_DCS_HP_DONE_EINT1                0x0040  /* DCS_HP_DONE_EINT1 */
+#define ARIZONA_DCS_HP_DONE_EINT1_MASK           0x0040  /* DCS_HP_DONE_EINT1 */
+#define ARIZONA_DCS_HP_DONE_EINT1_SHIFT               6  /* DCS_HP_DONE_EINT1 */
+#define ARIZONA_DCS_HP_DONE_EINT1_WIDTH               1  /* DCS_HP_DONE_EINT1 */
+#define ARIZONA_FLL2_CLOCK_OK_EINT1              0x0002  /* FLL2_CLOCK_OK_EINT1 */
+#define ARIZONA_FLL2_CLOCK_OK_EINT1_MASK         0x0002  /* FLL2_CLOCK_OK_EINT1 */
+#define ARIZONA_FLL2_CLOCK_OK_EINT1_SHIFT             1  /* FLL2_CLOCK_OK_EINT1 */
+#define ARIZONA_FLL2_CLOCK_OK_EINT1_WIDTH             1  /* FLL2_CLOCK_OK_EINT1 */
+#define ARIZONA_FLL1_CLOCK_OK_EINT1              0x0001  /* FLL1_CLOCK_OK_EINT1 */
+#define ARIZONA_FLL1_CLOCK_OK_EINT1_MASK         0x0001  /* FLL1_CLOCK_OK_EINT1 */
+#define ARIZONA_FLL1_CLOCK_OK_EINT1_SHIFT             0  /* FLL1_CLOCK_OK_EINT1 */
+#define ARIZONA_FLL1_CLOCK_OK_EINT1_WIDTH             1  /* FLL1_CLOCK_OK_EINT1 */
+
+/*
+ * R3336 (0xD08) - Interrupt Status 1 Mask
+ */
+#define ARIZONA_IM_GP4_EINT1                     0x0008  /* IM_GP4_EINT1 */
+#define ARIZONA_IM_GP4_EINT1_MASK                0x0008  /* IM_GP4_EINT1 */
+#define ARIZONA_IM_GP4_EINT1_SHIFT                    3  /* IM_GP4_EINT1 */
+#define ARIZONA_IM_GP4_EINT1_WIDTH                    1  /* IM_GP4_EINT1 */
+#define ARIZONA_IM_GP3_EINT1                     0x0004  /* IM_GP3_EINT1 */
+#define ARIZONA_IM_GP3_EINT1_MASK                0x0004  /* IM_GP3_EINT1 */
+#define ARIZONA_IM_GP3_EINT1_SHIFT                    2  /* IM_GP3_EINT1 */
+#define ARIZONA_IM_GP3_EINT1_WIDTH                    1  /* IM_GP3_EINT1 */
+#define ARIZONA_IM_GP2_EINT1                     0x0002  /* IM_GP2_EINT1 */
+#define ARIZONA_IM_GP2_EINT1_MASK                0x0002  /* IM_GP2_EINT1 */
+#define ARIZONA_IM_GP2_EINT1_SHIFT                    1  /* IM_GP2_EINT1 */
+#define ARIZONA_IM_GP2_EINT1_WIDTH                    1  /* IM_GP2_EINT1 */
+#define ARIZONA_IM_GP1_EINT1                     0x0001  /* IM_GP1_EINT1 */
+#define ARIZONA_IM_GP1_EINT1_MASK                0x0001  /* IM_GP1_EINT1 */
+#define ARIZONA_IM_GP1_EINT1_SHIFT                    0  /* IM_GP1_EINT1 */
+#define ARIZONA_IM_GP1_EINT1_WIDTH                    1  /* IM_GP1_EINT1 */
+
+/*
+ * R3337 (0xD09) - Interrupt Status 2 Mask
+ */
+#define ARIZONA_IM_DSP1_RAM_RDY_EINT1            0x0100  /* IM_DSP1_RAM_RDY_EINT1 */
+#define ARIZONA_IM_DSP1_RAM_RDY_EINT1_MASK       0x0100  /* IM_DSP1_RAM_RDY_EINT1 */
+#define ARIZONA_IM_DSP1_RAM_RDY_EINT1_SHIFT           8  /* IM_DSP1_RAM_RDY_EINT1 */
+#define ARIZONA_IM_DSP1_RAM_RDY_EINT1_WIDTH           1  /* IM_DSP1_RAM_RDY_EINT1 */
+#define ARIZONA_IM_DSP_IRQ2_EINT1                0x0002  /* IM_DSP_IRQ2_EINT1 */
+#define ARIZONA_IM_DSP_IRQ2_EINT1_MASK           0x0002  /* IM_DSP_IRQ2_EINT1 */
+#define ARIZONA_IM_DSP_IRQ2_EINT1_SHIFT               1  /* IM_DSP_IRQ2_EINT1 */
+#define ARIZONA_IM_DSP_IRQ2_EINT1_WIDTH               1  /* IM_DSP_IRQ2_EINT1 */
+#define ARIZONA_IM_DSP_IRQ1_EINT1                0x0001  /* IM_DSP_IRQ1_EINT1 */
+#define ARIZONA_IM_DSP_IRQ1_EINT1_MASK           0x0001  /* IM_DSP_IRQ1_EINT1 */
+#define ARIZONA_IM_DSP_IRQ1_EINT1_SHIFT               0  /* IM_DSP_IRQ1_EINT1 */
+#define ARIZONA_IM_DSP_IRQ1_EINT1_WIDTH               1  /* IM_DSP_IRQ1_EINT1 */
+
+/*
+ * R3338 (0xD0A) - Interrupt Status 3 Mask
+ */
+#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT1       0x8000  /* IM_SPK_SHUTDOWN_WARN_EINT1 */
+#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT1_MASK  0x8000  /* IM_SPK_SHUTDOWN_WARN_EINT1 */
+#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT1_SHIFT     15  /* IM_SPK_SHUTDOWN_WARN_EINT1 */
+#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT1_WIDTH      1  /* IM_SPK_SHUTDOWN_WARN_EINT1 */
+#define ARIZONA_IM_SPK_SHUTDOWN_EINT1            0x4000  /* IM_SPK_SHUTDOWN_EINT1 */
+#define ARIZONA_IM_SPK_SHUTDOWN_EINT1_MASK       0x4000  /* IM_SPK_SHUTDOWN_EINT1 */
+#define ARIZONA_IM_SPK_SHUTDOWN_EINT1_SHIFT          14  /* IM_SPK_SHUTDOWN_EINT1 */
+#define ARIZONA_IM_SPK_SHUTDOWN_EINT1_WIDTH           1  /* IM_SPK_SHUTDOWN_EINT1 */
+#define ARIZONA_IM_HPDET_EINT1                   0x2000  /* IM_HPDET_EINT1 */
+#define ARIZONA_IM_HPDET_EINT1_MASK              0x2000  /* IM_HPDET_EINT1 */
+#define ARIZONA_IM_HPDET_EINT1_SHIFT                 13  /* IM_HPDET_EINT1 */
+#define ARIZONA_IM_HPDET_EINT1_WIDTH                  1  /* IM_HPDET_EINT1 */
+#define ARIZONA_IM_MICDET_EINT1                  0x1000  /* IM_MICDET_EINT1 */
+#define ARIZONA_IM_MICDET_EINT1_MASK             0x1000  /* IM_MICDET_EINT1 */
+#define ARIZONA_IM_MICDET_EINT1_SHIFT                12  /* IM_MICDET_EINT1 */
+#define ARIZONA_IM_MICDET_EINT1_WIDTH                 1  /* IM_MICDET_EINT1 */
+#define ARIZONA_IM_WSEQ_DONE_EINT1               0x0800  /* IM_WSEQ_DONE_EINT1 */
+#define ARIZONA_IM_WSEQ_DONE_EINT1_MASK          0x0800  /* IM_WSEQ_DONE_EINT1 */
+#define ARIZONA_IM_WSEQ_DONE_EINT1_SHIFT             11  /* IM_WSEQ_DONE_EINT1 */
+#define ARIZONA_IM_WSEQ_DONE_EINT1_WIDTH              1  /* IM_WSEQ_DONE_EINT1 */
+#define ARIZONA_IM_DRC2_SIG_DET_EINT1            0x0400  /* IM_DRC2_SIG_DET_EINT1 */
+#define ARIZONA_IM_DRC2_SIG_DET_EINT1_MASK       0x0400  /* IM_DRC2_SIG_DET_EINT1 */
+#define ARIZONA_IM_DRC2_SIG_DET_EINT1_SHIFT          10  /* IM_DRC2_SIG_DET_EINT1 */
+#define ARIZONA_IM_DRC2_SIG_DET_EINT1_WIDTH           1  /* IM_DRC2_SIG_DET_EINT1 */
+#define ARIZONA_IM_DRC1_SIG_DET_EINT1            0x0200  /* IM_DRC1_SIG_DET_EINT1 */
+#define ARIZONA_IM_DRC1_SIG_DET_EINT1_MASK       0x0200  /* IM_DRC1_SIG_DET_EINT1 */
+#define ARIZONA_IM_DRC1_SIG_DET_EINT1_SHIFT           9  /* IM_DRC1_SIG_DET_EINT1 */
+#define ARIZONA_IM_DRC1_SIG_DET_EINT1_WIDTH           1  /* IM_DRC1_SIG_DET_EINT1 */
+#define ARIZONA_IM_ASRC2_LOCK_EINT1              0x0100  /* IM_ASRC2_LOCK_EINT1 */
+#define ARIZONA_IM_ASRC2_LOCK_EINT1_MASK         0x0100  /* IM_ASRC2_LOCK_EINT1 */
+#define ARIZONA_IM_ASRC2_LOCK_EINT1_SHIFT             8  /* IM_ASRC2_LOCK_EINT1 */
+#define ARIZONA_IM_ASRC2_LOCK_EINT1_WIDTH             1  /* IM_ASRC2_LOCK_EINT1 */
+#define ARIZONA_IM_ASRC1_LOCK_EINT1              0x0080  /* IM_ASRC1_LOCK_EINT1 */
+#define ARIZONA_IM_ASRC1_LOCK_EINT1_MASK         0x0080  /* IM_ASRC1_LOCK_EINT1 */
+#define ARIZONA_IM_ASRC1_LOCK_EINT1_SHIFT             7  /* IM_ASRC1_LOCK_EINT1 */
+#define ARIZONA_IM_ASRC1_LOCK_EINT1_WIDTH             1  /* IM_ASRC1_LOCK_EINT1 */
+#define ARIZONA_IM_UNDERCLOCKED_EINT1            0x0040  /* IM_UNDERCLOCKED_EINT1 */
+#define ARIZONA_IM_UNDERCLOCKED_EINT1_MASK       0x0040  /* IM_UNDERCLOCKED_EINT1 */
+#define ARIZONA_IM_UNDERCLOCKED_EINT1_SHIFT           6  /* IM_UNDERCLOCKED_EINT1 */
+#define ARIZONA_IM_UNDERCLOCKED_EINT1_WIDTH           1  /* IM_UNDERCLOCKED_EINT1 */
+#define ARIZONA_IM_OVERCLOCKED_EINT1             0x0020  /* IM_OVERCLOCKED_EINT1 */
+#define ARIZONA_IM_OVERCLOCKED_EINT1_MASK        0x0020  /* IM_OVERCLOCKED_EINT1 */
+#define ARIZONA_IM_OVERCLOCKED_EINT1_SHIFT            5  /* IM_OVERCLOCKED_EINT1 */
+#define ARIZONA_IM_OVERCLOCKED_EINT1_WIDTH            1  /* IM_OVERCLOCKED_EINT1 */
+#define ARIZONA_IM_FLL2_LOCK_EINT1               0x0008  /* IM_FLL2_LOCK_EINT1 */
+#define ARIZONA_IM_FLL2_LOCK_EINT1_MASK          0x0008  /* IM_FLL2_LOCK_EINT1 */
+#define ARIZONA_IM_FLL2_LOCK_EINT1_SHIFT              3  /* IM_FLL2_LOCK_EINT1 */
+#define ARIZONA_IM_FLL2_LOCK_EINT1_WIDTH              1  /* IM_FLL2_LOCK_EINT1 */
+#define ARIZONA_IM_FLL1_LOCK_EINT1               0x0004  /* IM_FLL1_LOCK_EINT1 */
+#define ARIZONA_IM_FLL1_LOCK_EINT1_MASK          0x0004  /* IM_FLL1_LOCK_EINT1 */
+#define ARIZONA_IM_FLL1_LOCK_EINT1_SHIFT              2  /* IM_FLL1_LOCK_EINT1 */
+#define ARIZONA_IM_FLL1_LOCK_EINT1_WIDTH              1  /* IM_FLL1_LOCK_EINT1 */
+#define ARIZONA_IM_CLKGEN_ERR_EINT1              0x0002  /* IM_CLKGEN_ERR_EINT1 */
+#define ARIZONA_IM_CLKGEN_ERR_EINT1_MASK         0x0002  /* IM_CLKGEN_ERR_EINT1 */
+#define ARIZONA_IM_CLKGEN_ERR_EINT1_SHIFT             1  /* IM_CLKGEN_ERR_EINT1 */
+#define ARIZONA_IM_CLKGEN_ERR_EINT1_WIDTH             1  /* IM_CLKGEN_ERR_EINT1 */
+#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT1        0x0001  /* IM_CLKGEN_ERR_ASYNC_EINT1 */
+#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT1_MASK   0x0001  /* IM_CLKGEN_ERR_ASYNC_EINT1 */
+#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT1_SHIFT       0  /* IM_CLKGEN_ERR_ASYNC_EINT1 */
+#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT1_WIDTH       1  /* IM_CLKGEN_ERR_ASYNC_EINT1 */
+
+/*
+ * R3339 (0xD0B) - Interrupt Status 4 Mask
+ */
+#define ARIZONA_IM_ASRC_CFG_ERR_EINT1            0x8000  /* IM_ASRC_CFG_ERR_EINT1 */
+#define ARIZONA_IM_ASRC_CFG_ERR_EINT1_MASK       0x8000  /* IM_ASRC_CFG_ERR_EINT1 */
+#define ARIZONA_IM_ASRC_CFG_ERR_EINT1_SHIFT          15  /* IM_ASRC_CFG_ERR_EINT1 */
+#define ARIZONA_IM_ASRC_CFG_ERR_EINT1_WIDTH           1  /* IM_ASRC_CFG_ERR_EINT1 */
+#define ARIZONA_IM_AIF3_ERR_EINT1                0x4000  /* IM_AIF3_ERR_EINT1 */
+#define ARIZONA_IM_AIF3_ERR_EINT1_MASK           0x4000  /* IM_AIF3_ERR_EINT1 */
+#define ARIZONA_IM_AIF3_ERR_EINT1_SHIFT              14  /* IM_AIF3_ERR_EINT1 */
+#define ARIZONA_IM_AIF3_ERR_EINT1_WIDTH               1  /* IM_AIF3_ERR_EINT1 */
+#define ARIZONA_IM_AIF2_ERR_EINT1                0x2000  /* IM_AIF2_ERR_EINT1 */
+#define ARIZONA_IM_AIF2_ERR_EINT1_MASK           0x2000  /* IM_AIF2_ERR_EINT1 */
+#define ARIZONA_IM_AIF2_ERR_EINT1_SHIFT              13  /* IM_AIF2_ERR_EINT1 */
+#define ARIZONA_IM_AIF2_ERR_EINT1_WIDTH               1  /* IM_AIF2_ERR_EINT1 */
+#define ARIZONA_IM_AIF1_ERR_EINT1                0x1000  /* IM_AIF1_ERR_EINT1 */
+#define ARIZONA_IM_AIF1_ERR_EINT1_MASK           0x1000  /* IM_AIF1_ERR_EINT1 */
+#define ARIZONA_IM_AIF1_ERR_EINT1_SHIFT              12  /* IM_AIF1_ERR_EINT1 */
+#define ARIZONA_IM_AIF1_ERR_EINT1_WIDTH               1  /* IM_AIF1_ERR_EINT1 */
+#define ARIZONA_IM_CTRLIF_ERR_EINT1              0x0800  /* IM_CTRLIF_ERR_EINT1 */
+#define ARIZONA_IM_CTRLIF_ERR_EINT1_MASK         0x0800  /* IM_CTRLIF_ERR_EINT1 */
+#define ARIZONA_IM_CTRLIF_ERR_EINT1_SHIFT            11  /* IM_CTRLIF_ERR_EINT1 */
+#define ARIZONA_IM_CTRLIF_ERR_EINT1_WIDTH             1  /* IM_CTRLIF_ERR_EINT1 */
+#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT1    0x0400  /* IM_MIXER_DROPPED_SAMPLE_EINT1 */
+#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT1_MASK 0x0400  /* IM_MIXER_DROPPED_SAMPLE_EINT1 */
+#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT1_SHIFT     10  /* IM_MIXER_DROPPED_SAMPLE_EINT1 */
+#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT1_WIDTH      1  /* IM_MIXER_DROPPED_SAMPLE_EINT1 */
+#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT1       0x0200  /* IM_ASYNC_CLK_ENA_LOW_EINT1 */
+#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT1_MASK  0x0200  /* IM_ASYNC_CLK_ENA_LOW_EINT1 */
+#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT1_SHIFT      9  /* IM_ASYNC_CLK_ENA_LOW_EINT1 */
+#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT1_WIDTH      1  /* IM_ASYNC_CLK_ENA_LOW_EINT1 */
+#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT1          0x0100  /* IM_SYSCLK_ENA_LOW_EINT1 */
+#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT1_MASK     0x0100  /* IM_SYSCLK_ENA_LOW_EINT1 */
+#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT1_SHIFT         8  /* IM_SYSCLK_ENA_LOW_EINT1 */
+#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT1_WIDTH         1  /* IM_SYSCLK_ENA_LOW_EINT1 */
+#define ARIZONA_IM_ISRC1_CFG_ERR_EINT1           0x0080  /* IM_ISRC1_CFG_ERR_EINT1 */
+#define ARIZONA_IM_ISRC1_CFG_ERR_EINT1_MASK      0x0080  /* IM_ISRC1_CFG_ERR_EINT1 */
+#define ARIZONA_IM_ISRC1_CFG_ERR_EINT1_SHIFT          7  /* IM_ISRC1_CFG_ERR_EINT1 */
+#define ARIZONA_IM_ISRC1_CFG_ERR_EINT1_WIDTH          1  /* IM_ISRC1_CFG_ERR_EINT1 */
+#define ARIZONA_IM_ISRC2_CFG_ERR_EINT1           0x0040  /* IM_ISRC2_CFG_ERR_EINT1 */
+#define ARIZONA_IM_ISRC2_CFG_ERR_EINT1_MASK      0x0040  /* IM_ISRC2_CFG_ERR_EINT1 */
+#define ARIZONA_IM_ISRC2_CFG_ERR_EINT1_SHIFT          6  /* IM_ISRC2_CFG_ERR_EINT1 */
+#define ARIZONA_IM_ISRC2_CFG_ERR_EINT1_WIDTH          1  /* IM_ISRC2_CFG_ERR_EINT1 */
+
+/*
+ * R3340 (0xD0C) - Interrupt Status 5 Mask
+ */
+#define ARIZONA_IM_BOOT_DONE_EINT1               0x0100  /* IM_BOOT_DONE_EINT1 */
+#define ARIZONA_IM_BOOT_DONE_EINT1_MASK          0x0100  /* IM_BOOT_DONE_EINT1 */
+#define ARIZONA_IM_BOOT_DONE_EINT1_SHIFT              8  /* IM_BOOT_DONE_EINT1 */
+#define ARIZONA_IM_BOOT_DONE_EINT1_WIDTH              1  /* IM_BOOT_DONE_EINT1 */
+#define ARIZONA_IM_DCS_DAC_DONE_EINT1            0x0080  /* IM_DCS_DAC_DONE_EINT1 */
+#define ARIZONA_IM_DCS_DAC_DONE_EINT1_MASK       0x0080  /* IM_DCS_DAC_DONE_EINT1 */
+#define ARIZONA_IM_DCS_DAC_DONE_EINT1_SHIFT           7  /* IM_DCS_DAC_DONE_EINT1 */
+#define ARIZONA_IM_DCS_DAC_DONE_EINT1_WIDTH           1  /* IM_DCS_DAC_DONE_EINT1 */
+#define ARIZONA_IM_DCS_HP_DONE_EINT1             0x0040  /* IM_DCS_HP_DONE_EINT1 */
+#define ARIZONA_IM_DCS_HP_DONE_EINT1_MASK        0x0040  /* IM_DCS_HP_DONE_EINT1 */
+#define ARIZONA_IM_DCS_HP_DONE_EINT1_SHIFT            6  /* IM_DCS_HP_DONE_EINT1 */
+#define ARIZONA_IM_DCS_HP_DONE_EINT1_WIDTH            1  /* IM_DCS_HP_DONE_EINT1 */
+#define ARIZONA_IM_FLL2_CLOCK_OK_EINT1           0x0002  /* IM_FLL2_CLOCK_OK_EINT1 */
+#define ARIZONA_IM_FLL2_CLOCK_OK_EINT1_MASK      0x0002  /* IM_FLL2_CLOCK_OK_EINT1 */
+#define ARIZONA_IM_FLL2_CLOCK_OK_EINT1_SHIFT          1  /* IM_FLL2_CLOCK_OK_EINT1 */
+#define ARIZONA_IM_FLL2_CLOCK_OK_EINT1_WIDTH          1  /* IM_FLL2_CLOCK_OK_EINT1 */
+#define ARIZONA_IM_FLL1_CLOCK_OK_EINT1           0x0001  /* IM_FLL1_CLOCK_OK_EINT1 */
+#define ARIZONA_IM_FLL1_CLOCK_OK_EINT1_MASK      0x0001  /* IM_FLL1_CLOCK_OK_EINT1 */
+#define ARIZONA_IM_FLL1_CLOCK_OK_EINT1_SHIFT          0  /* IM_FLL1_CLOCK_OK_EINT1 */
+#define ARIZONA_IM_FLL1_CLOCK_OK_EINT1_WIDTH          1  /* IM_FLL1_CLOCK_OK_EINT1 */
+
+/*
+ * R3343 (0xD0F) - Interrupt Control
+ */
+#define ARIZONA_IM_IRQ1                          0x0001  /* IM_IRQ1 */
+#define ARIZONA_IM_IRQ1_MASK                     0x0001  /* IM_IRQ1 */
+#define ARIZONA_IM_IRQ1_SHIFT                         0  /* IM_IRQ1 */
+#define ARIZONA_IM_IRQ1_WIDTH                         1  /* IM_IRQ1 */
+
+/*
+ * R3344 (0xD10) - IRQ2 Status 1
+ */
+#define ARIZONA_GP4_EINT2                        0x0008  /* GP4_EINT2 */
+#define ARIZONA_GP4_EINT2_MASK                   0x0008  /* GP4_EINT2 */
+#define ARIZONA_GP4_EINT2_SHIFT                       3  /* GP4_EINT2 */
+#define ARIZONA_GP4_EINT2_WIDTH                       1  /* GP4_EINT2 */
+#define ARIZONA_GP3_EINT2                        0x0004  /* GP3_EINT2 */
+#define ARIZONA_GP3_EINT2_MASK                   0x0004  /* GP3_EINT2 */
+#define ARIZONA_GP3_EINT2_SHIFT                       2  /* GP3_EINT2 */
+#define ARIZONA_GP3_EINT2_WIDTH                       1  /* GP3_EINT2 */
+#define ARIZONA_GP2_EINT2                        0x0002  /* GP2_EINT2 */
+#define ARIZONA_GP2_EINT2_MASK                   0x0002  /* GP2_EINT2 */
+#define ARIZONA_GP2_EINT2_SHIFT                       1  /* GP2_EINT2 */
+#define ARIZONA_GP2_EINT2_WIDTH                       1  /* GP2_EINT2 */
+#define ARIZONA_GP1_EINT2                        0x0001  /* GP1_EINT2 */
+#define ARIZONA_GP1_EINT2_MASK                   0x0001  /* GP1_EINT2 */
+#define ARIZONA_GP1_EINT2_SHIFT                       0  /* GP1_EINT2 */
+#define ARIZONA_GP1_EINT2_WIDTH                       1  /* GP1_EINT2 */
+
+/*
+ * R3345 (0xD11) - IRQ2 Status 2
+ */
+#define ARIZONA_DSP1_RAM_RDY_EINT2               0x0100  /* DSP1_RAM_RDY_EINT2 */
+#define ARIZONA_DSP1_RAM_RDY_EINT2_MASK          0x0100  /* DSP1_RAM_RDY_EINT2 */
+#define ARIZONA_DSP1_RAM_RDY_EINT2_SHIFT              8  /* DSP1_RAM_RDY_EINT2 */
+#define ARIZONA_DSP1_RAM_RDY_EINT2_WIDTH              1  /* DSP1_RAM_RDY_EINT2 */
+#define ARIZONA_DSP_IRQ2_EINT2                   0x0002  /* DSP_IRQ2_EINT2 */
+#define ARIZONA_DSP_IRQ2_EINT2_MASK              0x0002  /* DSP_IRQ2_EINT2 */
+#define ARIZONA_DSP_IRQ2_EINT2_SHIFT                  1  /* DSP_IRQ2_EINT2 */
+#define ARIZONA_DSP_IRQ2_EINT2_WIDTH                  1  /* DSP_IRQ2_EINT2 */
+#define ARIZONA_DSP_IRQ1_EINT2                   0x0001  /* DSP_IRQ1_EINT2 */
+#define ARIZONA_DSP_IRQ1_EINT2_MASK              0x0001  /* DSP_IRQ1_EINT2 */
+#define ARIZONA_DSP_IRQ1_EINT2_SHIFT                  0  /* DSP_IRQ1_EINT2 */
+#define ARIZONA_DSP_IRQ1_EINT2_WIDTH                  1  /* DSP_IRQ1_EINT2 */
+
+/*
+ * R3346 (0xD12) - IRQ2 Status 3
+ */
+#define ARIZONA_SPK_SHUTDOWN_WARN_EINT2          0x8000  /* SPK_SHUTDOWN_WARN_EINT2 */
+#define ARIZONA_SPK_SHUTDOWN_WARN_EINT2_MASK     0x8000  /* SPK_SHUTDOWN_WARN_EINT2 */
+#define ARIZONA_SPK_SHUTDOWN_WARN_EINT2_SHIFT        15  /* SPK_SHUTDOWN_WARN_EINT2 */
+#define ARIZONA_SPK_SHUTDOWN_WARN_EINT2_WIDTH         1  /* SPK_SHUTDOWN_WARN_EINT2 */
+#define ARIZONA_SPK_SHUTDOWN_EINT2               0x4000  /* SPK_SHUTDOWN_EINT2 */
+#define ARIZONA_SPK_SHUTDOWN_EINT2_MASK          0x4000  /* SPK_SHUTDOWN_EINT2 */
+#define ARIZONA_SPK_SHUTDOWN_EINT2_SHIFT             14  /* SPK_SHUTDOWN_EINT2 */
+#define ARIZONA_SPK_SHUTDOWN_EINT2_WIDTH              1  /* SPK_SHUTDOWN_EINT2 */
+#define ARIZONA_HPDET_EINT2                      0x2000  /* HPDET_EINT2 */
+#define ARIZONA_HPDET_EINT2_MASK                 0x2000  /* HPDET_EINT2 */
+#define ARIZONA_HPDET_EINT2_SHIFT                    13  /* HPDET_EINT2 */
+#define ARIZONA_HPDET_EINT2_WIDTH                     1  /* HPDET_EINT2 */
+#define ARIZONA_MICDET_EINT2                     0x1000  /* MICDET_EINT2 */
+#define ARIZONA_MICDET_EINT2_MASK                0x1000  /* MICDET_EINT2 */
+#define ARIZONA_MICDET_EINT2_SHIFT                   12  /* MICDET_EINT2 */
+#define ARIZONA_MICDET_EINT2_WIDTH                    1  /* MICDET_EINT2 */
+#define ARIZONA_WSEQ_DONE_EINT2                  0x0800  /* WSEQ_DONE_EINT2 */
+#define ARIZONA_WSEQ_DONE_EINT2_MASK             0x0800  /* WSEQ_DONE_EINT2 */
+#define ARIZONA_WSEQ_DONE_EINT2_SHIFT                11  /* WSEQ_DONE_EINT2 */
+#define ARIZONA_WSEQ_DONE_EINT2_WIDTH                 1  /* WSEQ_DONE_EINT2 */
+#define ARIZONA_DRC2_SIG_DET_EINT2               0x0400  /* DRC2_SIG_DET_EINT2 */
+#define ARIZONA_DRC2_SIG_DET_EINT2_MASK          0x0400  /* DRC2_SIG_DET_EINT2 */
+#define ARIZONA_DRC2_SIG_DET_EINT2_SHIFT             10  /* DRC2_SIG_DET_EINT2 */
+#define ARIZONA_DRC2_SIG_DET_EINT2_WIDTH              1  /* DRC2_SIG_DET_EINT2 */
+#define ARIZONA_DRC1_SIG_DET_EINT2               0x0200  /* DRC1_SIG_DET_EINT2 */
+#define ARIZONA_DRC1_SIG_DET_EINT2_MASK          0x0200  /* DRC1_SIG_DET_EINT2 */
+#define ARIZONA_DRC1_SIG_DET_EINT2_SHIFT              9  /* DRC1_SIG_DET_EINT2 */
+#define ARIZONA_DRC1_SIG_DET_EINT2_WIDTH              1  /* DRC1_SIG_DET_EINT2 */
+#define ARIZONA_ASRC2_LOCK_EINT2                 0x0100  /* ASRC2_LOCK_EINT2 */
+#define ARIZONA_ASRC2_LOCK_EINT2_MASK            0x0100  /* ASRC2_LOCK_EINT2 */
+#define ARIZONA_ASRC2_LOCK_EINT2_SHIFT                8  /* ASRC2_LOCK_EINT2 */
+#define ARIZONA_ASRC2_LOCK_EINT2_WIDTH                1  /* ASRC2_LOCK_EINT2 */
+#define ARIZONA_ASRC1_LOCK_EINT2                 0x0080  /* ASRC1_LOCK_EINT2 */
+#define ARIZONA_ASRC1_LOCK_EINT2_MASK            0x0080  /* ASRC1_LOCK_EINT2 */
+#define ARIZONA_ASRC1_LOCK_EINT2_SHIFT                7  /* ASRC1_LOCK_EINT2 */
+#define ARIZONA_ASRC1_LOCK_EINT2_WIDTH                1  /* ASRC1_LOCK_EINT2 */
+#define ARIZONA_UNDERCLOCKED_EINT2               0x0040  /* UNDERCLOCKED_EINT2 */
+#define ARIZONA_UNDERCLOCKED_EINT2_MASK          0x0040  /* UNDERCLOCKED_EINT2 */
+#define ARIZONA_UNDERCLOCKED_EINT2_SHIFT              6  /* UNDERCLOCKED_EINT2 */
+#define ARIZONA_UNDERCLOCKED_EINT2_WIDTH              1  /* UNDERCLOCKED_EINT2 */
+#define ARIZONA_OVERCLOCKED_EINT2                0x0020  /* OVERCLOCKED_EINT2 */
+#define ARIZONA_OVERCLOCKED_EINT2_MASK           0x0020  /* OVERCLOCKED_EINT2 */
+#define ARIZONA_OVERCLOCKED_EINT2_SHIFT               5  /* OVERCLOCKED_EINT2 */
+#define ARIZONA_OVERCLOCKED_EINT2_WIDTH               1  /* OVERCLOCKED_EINT2 */
+#define ARIZONA_FLL2_LOCK_EINT2                  0x0008  /* FLL2_LOCK_EINT2 */
+#define ARIZONA_FLL2_LOCK_EINT2_MASK             0x0008  /* FLL2_LOCK_EINT2 */
+#define ARIZONA_FLL2_LOCK_EINT2_SHIFT                 3  /* FLL2_LOCK_EINT2 */
+#define ARIZONA_FLL2_LOCK_EINT2_WIDTH                 1  /* FLL2_LOCK_EINT2 */
+#define ARIZONA_FLL1_LOCK_EINT2                  0x0004  /* FLL1_LOCK_EINT2 */
+#define ARIZONA_FLL1_LOCK_EINT2_MASK             0x0004  /* FLL1_LOCK_EINT2 */
+#define ARIZONA_FLL1_LOCK_EINT2_SHIFT                 2  /* FLL1_LOCK_EINT2 */
+#define ARIZONA_FLL1_LOCK_EINT2_WIDTH                 1  /* FLL1_LOCK_EINT2 */
+#define ARIZONA_CLKGEN_ERR_EINT2                 0x0002  /* CLKGEN_ERR_EINT2 */
+#define ARIZONA_CLKGEN_ERR_EINT2_MASK            0x0002  /* CLKGEN_ERR_EINT2 */
+#define ARIZONA_CLKGEN_ERR_EINT2_SHIFT                1  /* CLKGEN_ERR_EINT2 */
+#define ARIZONA_CLKGEN_ERR_EINT2_WIDTH                1  /* CLKGEN_ERR_EINT2 */
+#define ARIZONA_CLKGEN_ERR_ASYNC_EINT2           0x0001  /* CLKGEN_ERR_ASYNC_EINT2 */
+#define ARIZONA_CLKGEN_ERR_ASYNC_EINT2_MASK      0x0001  /* CLKGEN_ERR_ASYNC_EINT2 */
+#define ARIZONA_CLKGEN_ERR_ASYNC_EINT2_SHIFT          0  /* CLKGEN_ERR_ASYNC_EINT2 */
+#define ARIZONA_CLKGEN_ERR_ASYNC_EINT2_WIDTH          1  /* CLKGEN_ERR_ASYNC_EINT2 */
+
+/*
+ * R3347 (0xD13) - IRQ2 Status 4
+ */
+#define ARIZONA_ASRC_CFG_ERR_EINT2               0x8000  /* ASRC_CFG_ERR_EINT2 */
+#define ARIZONA_ASRC_CFG_ERR_EINT2_MASK          0x8000  /* ASRC_CFG_ERR_EINT2 */
+#define ARIZONA_ASRC_CFG_ERR_EINT2_SHIFT             15  /* ASRC_CFG_ERR_EINT2 */
+#define ARIZONA_ASRC_CFG_ERR_EINT2_WIDTH              1  /* ASRC_CFG_ERR_EINT2 */
+#define ARIZONA_AIF3_ERR_EINT2                   0x4000  /* AIF3_ERR_EINT2 */
+#define ARIZONA_AIF3_ERR_EINT2_MASK              0x4000  /* AIF3_ERR_EINT2 */
+#define ARIZONA_AIF3_ERR_EINT2_SHIFT                 14  /* AIF3_ERR_EINT2 */
+#define ARIZONA_AIF3_ERR_EINT2_WIDTH                  1  /* AIF3_ERR_EINT2 */
+#define ARIZONA_AIF2_ERR_EINT2                   0x2000  /* AIF2_ERR_EINT2 */
+#define ARIZONA_AIF2_ERR_EINT2_MASK              0x2000  /* AIF2_ERR_EINT2 */
+#define ARIZONA_AIF2_ERR_EINT2_SHIFT                 13  /* AIF2_ERR_EINT2 */
+#define ARIZONA_AIF2_ERR_EINT2_WIDTH                  1  /* AIF2_ERR_EINT2 */
+#define ARIZONA_AIF1_ERR_EINT2                   0x1000  /* AIF1_ERR_EINT2 */
+#define ARIZONA_AIF1_ERR_EINT2_MASK              0x1000  /* AIF1_ERR_EINT2 */
+#define ARIZONA_AIF1_ERR_EINT2_SHIFT                 12  /* AIF1_ERR_EINT2 */
+#define ARIZONA_AIF1_ERR_EINT2_WIDTH                  1  /* AIF1_ERR_EINT2 */
+#define ARIZONA_CTRLIF_ERR_EINT2                 0x0800  /* CTRLIF_ERR_EINT2 */
+#define ARIZONA_CTRLIF_ERR_EINT2_MASK            0x0800  /* CTRLIF_ERR_EINT2 */
+#define ARIZONA_CTRLIF_ERR_EINT2_SHIFT               11  /* CTRLIF_ERR_EINT2 */
+#define ARIZONA_CTRLIF_ERR_EINT2_WIDTH                1  /* CTRLIF_ERR_EINT2 */
+#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT2       0x0400  /* MIXER_DROPPED_SAMPLE_EINT2 */
+#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT2_MASK  0x0400  /* MIXER_DROPPED_SAMPLE_EINT2 */
+#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT2_SHIFT     10  /* MIXER_DROPPED_SAMPLE_EINT2 */
+#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT2_WIDTH      1  /* MIXER_DROPPED_SAMPLE_EINT2 */
+#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT2          0x0200  /* ASYNC_CLK_ENA_LOW_EINT2 */
+#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT2_MASK     0x0200  /* ASYNC_CLK_ENA_LOW_EINT2 */
+#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT2_SHIFT         9  /* ASYNC_CLK_ENA_LOW_EINT2 */
+#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT2_WIDTH         1  /* ASYNC_CLK_ENA_LOW_EINT2 */
+#define ARIZONA_SYSCLK_ENA_LOW_EINT2             0x0100  /* SYSCLK_ENA_LOW_EINT2 */
+#define ARIZONA_SYSCLK_ENA_LOW_EINT2_MASK        0x0100  /* SYSCLK_ENA_LOW_EINT2 */
+#define ARIZONA_SYSCLK_ENA_LOW_EINT2_SHIFT            8  /* SYSCLK_ENA_LOW_EINT2 */
+#define ARIZONA_SYSCLK_ENA_LOW_EINT2_WIDTH            1  /* SYSCLK_ENA_LOW_EINT2 */
+#define ARIZONA_ISRC1_CFG_ERR_EINT2              0x0080  /* ISRC1_CFG_ERR_EINT2 */
+#define ARIZONA_ISRC1_CFG_ERR_EINT2_MASK         0x0080  /* ISRC1_CFG_ERR_EINT2 */
+#define ARIZONA_ISRC1_CFG_ERR_EINT2_SHIFT             7  /* ISRC1_CFG_ERR_EINT2 */
+#define ARIZONA_ISRC1_CFG_ERR_EINT2_WIDTH             1  /* ISRC1_CFG_ERR_EINT2 */
+#define ARIZONA_ISRC2_CFG_ERR_EINT2              0x0040  /* ISRC2_CFG_ERR_EINT2 */
+#define ARIZONA_ISRC2_CFG_ERR_EINT2_MASK         0x0040  /* ISRC2_CFG_ERR_EINT2 */
+#define ARIZONA_ISRC2_CFG_ERR_EINT2_SHIFT             6  /* ISRC2_CFG_ERR_EINT2 */
+#define ARIZONA_ISRC2_CFG_ERR_EINT2_WIDTH             1  /* ISRC2_CFG_ERR_EINT2 */
+
+/*
+ * R3348 (0xD14) - IRQ2 Status 5
+ */
+#define ARIZONA_BOOT_DONE_EINT2                  0x0100  /* BOOT_DONE_EINT2 */
+#define ARIZONA_BOOT_DONE_EINT2_MASK             0x0100  /* BOOT_DONE_EINT2 */
+#define ARIZONA_BOOT_DONE_EINT2_SHIFT                 8  /* BOOT_DONE_EINT2 */
+#define ARIZONA_BOOT_DONE_EINT2_WIDTH                 1  /* BOOT_DONE_EINT2 */
+#define ARIZONA_DCS_DAC_DONE_EINT2               0x0080  /* DCS_DAC_DONE_EINT2 */
+#define ARIZONA_DCS_DAC_DONE_EINT2_MASK          0x0080  /* DCS_DAC_DONE_EINT2 */
+#define ARIZONA_DCS_DAC_DONE_EINT2_SHIFT              7  /* DCS_DAC_DONE_EINT2 */
+#define ARIZONA_DCS_DAC_DONE_EINT2_WIDTH              1  /* DCS_DAC_DONE_EINT2 */
+#define ARIZONA_DCS_HP_DONE_EINT2                0x0040  /* DCS_HP_DONE_EINT2 */
+#define ARIZONA_DCS_HP_DONE_EINT2_MASK           0x0040  /* DCS_HP_DONE_EINT2 */
+#define ARIZONA_DCS_HP_DONE_EINT2_SHIFT               6  /* DCS_HP_DONE_EINT2 */
+#define ARIZONA_DCS_HP_DONE_EINT2_WIDTH               1  /* DCS_HP_DONE_EINT2 */
+#define ARIZONA_FLL2_CLOCK_OK_EINT2              0x0002  /* FLL2_CLOCK_OK_EINT2 */
+#define ARIZONA_FLL2_CLOCK_OK_EINT2_MASK         0x0002  /* FLL2_CLOCK_OK_EINT2 */
+#define ARIZONA_FLL2_CLOCK_OK_EINT2_SHIFT             1  /* FLL2_CLOCK_OK_EINT2 */
+#define ARIZONA_FLL2_CLOCK_OK_EINT2_WIDTH             1  /* FLL2_CLOCK_OK_EINT2 */
+#define ARIZONA_FLL1_CLOCK_OK_EINT2              0x0001  /* FLL1_CLOCK_OK_EINT2 */
+#define ARIZONA_FLL1_CLOCK_OK_EINT2_MASK         0x0001  /* FLL1_CLOCK_OK_EINT2 */
+#define ARIZONA_FLL1_CLOCK_OK_EINT2_SHIFT             0  /* FLL1_CLOCK_OK_EINT2 */
+#define ARIZONA_FLL1_CLOCK_OK_EINT2_WIDTH             1  /* FLL1_CLOCK_OK_EINT2 */
+
+/*
+ * R3352 (0xD18) - IRQ2 Status 1 Mask
+ */
+#define ARIZONA_IM_GP4_EINT2                     0x0008  /* IM_GP4_EINT2 */
+#define ARIZONA_IM_GP4_EINT2_MASK                0x0008  /* IM_GP4_EINT2 */
+#define ARIZONA_IM_GP4_EINT2_SHIFT                    3  /* IM_GP4_EINT2 */
+#define ARIZONA_IM_GP4_EINT2_WIDTH                    1  /* IM_GP4_EINT2 */
+#define ARIZONA_IM_GP3_EINT2                     0x0004  /* IM_GP3_EINT2 */
+#define ARIZONA_IM_GP3_EINT2_MASK                0x0004  /* IM_GP3_EINT2 */
+#define ARIZONA_IM_GP3_EINT2_SHIFT                    2  /* IM_GP3_EINT2 */
+#define ARIZONA_IM_GP3_EINT2_WIDTH                    1  /* IM_GP3_EINT2 */
+#define ARIZONA_IM_GP2_EINT2                     0x0002  /* IM_GP2_EINT2 */
+#define ARIZONA_IM_GP2_EINT2_MASK                0x0002  /* IM_GP2_EINT2 */
+#define ARIZONA_IM_GP2_EINT2_SHIFT                    1  /* IM_GP2_EINT2 */
+#define ARIZONA_IM_GP2_EINT2_WIDTH                    1  /* IM_GP2_EINT2 */
+#define ARIZONA_IM_GP1_EINT2                     0x0001  /* IM_GP1_EINT2 */
+#define ARIZONA_IM_GP1_EINT2_MASK                0x0001  /* IM_GP1_EINT2 */
+#define ARIZONA_IM_GP1_EINT2_SHIFT                    0  /* IM_GP1_EINT2 */
+#define ARIZONA_IM_GP1_EINT2_WIDTH                    1  /* IM_GP1_EINT2 */
+
+/*
+ * R3353 (0xD19) - IRQ2 Status 2 Mask
+ */
+#define ARIZONA_IM_DSP1_RAM_RDY_EINT2            0x0100  /* IM_DSP1_RAM_RDY_EINT2 */
+#define ARIZONA_IM_DSP1_RAM_RDY_EINT2_MASK       0x0100  /* IM_DSP1_RAM_RDY_EINT2 */
+#define ARIZONA_IM_DSP1_RAM_RDY_EINT2_SHIFT           8  /* IM_DSP1_RAM_RDY_EINT2 */
+#define ARIZONA_IM_DSP1_RAM_RDY_EINT2_WIDTH           1  /* IM_DSP1_RAM_RDY_EINT2 */
+#define ARIZONA_IM_DSP_IRQ2_EINT2                0x0002  /* IM_DSP_IRQ2_EINT2 */
+#define ARIZONA_IM_DSP_IRQ2_EINT2_MASK           0x0002  /* IM_DSP_IRQ2_EINT2 */
+#define ARIZONA_IM_DSP_IRQ2_EINT2_SHIFT               1  /* IM_DSP_IRQ2_EINT2 */
+#define ARIZONA_IM_DSP_IRQ2_EINT2_WIDTH               1  /* IM_DSP_IRQ2_EINT2 */
+#define ARIZONA_IM_DSP_IRQ1_EINT2                0x0001  /* IM_DSP_IRQ1_EINT2 */
+#define ARIZONA_IM_DSP_IRQ1_EINT2_MASK           0x0001  /* IM_DSP_IRQ1_EINT2 */
+#define ARIZONA_IM_DSP_IRQ1_EINT2_SHIFT               0  /* IM_DSP_IRQ1_EINT2 */
+#define ARIZONA_IM_DSP_IRQ1_EINT2_WIDTH               1  /* IM_DSP_IRQ1_EINT2 */
+
+/*
+ * R3354 (0xD1A) - IRQ2 Status 3 Mask
+ */
+#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT2       0x8000  /* IM_SPK_SHUTDOWN_WARN_EINT2 */
+#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT2_MASK  0x8000  /* IM_SPK_SHUTDOWN_WARN_EINT2 */
+#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT2_SHIFT     15  /* IM_SPK_SHUTDOWN_WARN_EINT2 */
+#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT2_WIDTH      1  /* IM_SPK_SHUTDOWN_WARN_EINT2 */
+#define ARIZONA_IM_SPK_SHUTDOWN_EINT2            0x4000  /* IM_SPK_SHUTDOWN_EINT2 */
+#define ARIZONA_IM_SPK_SHUTDOWN_EINT2_MASK       0x4000  /* IM_SPK_SHUTDOWN_EINT2 */
+#define ARIZONA_IM_SPK_SHUTDOWN_EINT2_SHIFT          14  /* IM_SPK_SHUTDOWN_EINT2 */
+#define ARIZONA_IM_SPK_SHUTDOWN_EINT2_WIDTH           1  /* IM_SPK_SHUTDOWN_EINT2 */
+#define ARIZONA_IM_HPDET_EINT2                   0x2000  /* IM_HPDET_EINT2 */
+#define ARIZONA_IM_HPDET_EINT2_MASK              0x2000  /* IM_HPDET_EINT2 */
+#define ARIZONA_IM_HPDET_EINT2_SHIFT                 13  /* IM_HPDET_EINT2 */
+#define ARIZONA_IM_HPDET_EINT2_WIDTH                  1  /* IM_HPDET_EINT2 */
+#define ARIZONA_IM_MICDET_EINT2                  0x1000  /* IM_MICDET_EINT2 */
+#define ARIZONA_IM_MICDET_EINT2_MASK             0x1000  /* IM_MICDET_EINT2 */
+#define ARIZONA_IM_MICDET_EINT2_SHIFT                12  /* IM_MICDET_EINT2 */
+#define ARIZONA_IM_MICDET_EINT2_WIDTH                 1  /* IM_MICDET_EINT2 */
+#define ARIZONA_IM_WSEQ_DONE_EINT2               0x0800  /* IM_WSEQ_DONE_EINT2 */
+#define ARIZONA_IM_WSEQ_DONE_EINT2_MASK          0x0800  /* IM_WSEQ_DONE_EINT2 */
+#define ARIZONA_IM_WSEQ_DONE_EINT2_SHIFT             11  /* IM_WSEQ_DONE_EINT2 */
+#define ARIZONA_IM_WSEQ_DONE_EINT2_WIDTH              1  /* IM_WSEQ_DONE_EINT2 */
+#define ARIZONA_IM_DRC2_SIG_DET_EINT2            0x0400  /* IM_DRC2_SIG_DET_EINT2 */
+#define ARIZONA_IM_DRC2_SIG_DET_EINT2_MASK       0x0400  /* IM_DRC2_SIG_DET_EINT2 */
+#define ARIZONA_IM_DRC2_SIG_DET_EINT2_SHIFT          10  /* IM_DRC2_SIG_DET_EINT2 */
+#define ARIZONA_IM_DRC2_SIG_DET_EINT2_WIDTH           1  /* IM_DRC2_SIG_DET_EINT2 */
+#define ARIZONA_IM_DRC1_SIG_DET_EINT2            0x0200  /* IM_DRC1_SIG_DET_EINT2 */
+#define ARIZONA_IM_DRC1_SIG_DET_EINT2_MASK       0x0200  /* IM_DRC1_SIG_DET_EINT2 */
+#define ARIZONA_IM_DRC1_SIG_DET_EINT2_SHIFT           9  /* IM_DRC1_SIG_DET_EINT2 */
+#define ARIZONA_IM_DRC1_SIG_DET_EINT2_WIDTH           1  /* IM_DRC1_SIG_DET_EINT2 */
+#define ARIZONA_IM_ASRC2_LOCK_EINT2              0x0100  /* IM_ASRC2_LOCK_EINT2 */
+#define ARIZONA_IM_ASRC2_LOCK_EINT2_MASK         0x0100  /* IM_ASRC2_LOCK_EINT2 */
+#define ARIZONA_IM_ASRC2_LOCK_EINT2_SHIFT             8  /* IM_ASRC2_LOCK_EINT2 */
+#define ARIZONA_IM_ASRC2_LOCK_EINT2_WIDTH             1  /* IM_ASRC2_LOCK_EINT2 */
+#define ARIZONA_IM_ASRC1_LOCK_EINT2              0x0080  /* IM_ASRC1_LOCK_EINT2 */
+#define ARIZONA_IM_ASRC1_LOCK_EINT2_MASK         0x0080  /* IM_ASRC1_LOCK_EINT2 */
+#define ARIZONA_IM_ASRC1_LOCK_EINT2_SHIFT             7  /* IM_ASRC1_LOCK_EINT2 */
+#define ARIZONA_IM_ASRC1_LOCK_EINT2_WIDTH             1  /* IM_ASRC1_LOCK_EINT2 */
+#define ARIZONA_IM_UNDERCLOCKED_EINT2            0x0040  /* IM_UNDERCLOCKED_EINT2 */
+#define ARIZONA_IM_UNDERCLOCKED_EINT2_MASK       0x0040  /* IM_UNDERCLOCKED_EINT2 */
+#define ARIZONA_IM_UNDERCLOCKED_EINT2_SHIFT           6  /* IM_UNDERCLOCKED_EINT2 */
+#define ARIZONA_IM_UNDERCLOCKED_EINT2_WIDTH           1  /* IM_UNDERCLOCKED_EINT2 */
+#define ARIZONA_IM_OVERCLOCKED_EINT2             0x0020  /* IM_OVERCLOCKED_EINT2 */
+#define ARIZONA_IM_OVERCLOCKED_EINT2_MASK        0x0020  /* IM_OVERCLOCKED_EINT2 */
+#define ARIZONA_IM_OVERCLOCKED_EINT2_SHIFT            5  /* IM_OVERCLOCKED_EINT2 */
+#define ARIZONA_IM_OVERCLOCKED_EINT2_WIDTH            1  /* IM_OVERCLOCKED_EINT2 */
+#define ARIZONA_IM_FLL2_LOCK_EINT2               0x0008  /* IM_FLL2_LOCK_EINT2 */
+#define ARIZONA_IM_FLL2_LOCK_EINT2_MASK          0x0008  /* IM_FLL2_LOCK_EINT2 */
+#define ARIZONA_IM_FLL2_LOCK_EINT2_SHIFT              3  /* IM_FLL2_LOCK_EINT2 */
+#define ARIZONA_IM_FLL2_LOCK_EINT2_WIDTH              1  /* IM_FLL2_LOCK_EINT2 */
+#define ARIZONA_IM_FLL1_LOCK_EINT2               0x0004  /* IM_FLL1_LOCK_EINT2 */
+#define ARIZONA_IM_FLL1_LOCK_EINT2_MASK          0x0004  /* IM_FLL1_LOCK_EINT2 */
+#define ARIZONA_IM_FLL1_LOCK_EINT2_SHIFT              2  /* IM_FLL1_LOCK_EINT2 */
+#define ARIZONA_IM_FLL1_LOCK_EINT2_WIDTH              1  /* IM_FLL1_LOCK_EINT2 */
+#define ARIZONA_IM_CLKGEN_ERR_EINT2              0x0002  /* IM_CLKGEN_ERR_EINT2 */
+#define ARIZONA_IM_CLKGEN_ERR_EINT2_MASK         0x0002  /* IM_CLKGEN_ERR_EINT2 */
+#define ARIZONA_IM_CLKGEN_ERR_EINT2_SHIFT             1  /* IM_CLKGEN_ERR_EINT2 */
+#define ARIZONA_IM_CLKGEN_ERR_EINT2_WIDTH             1  /* IM_CLKGEN_ERR_EINT2 */
+#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT2        0x0001  /* IM_CLKGEN_ERR_ASYNC_EINT2 */
+#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT2_MASK   0x0001  /* IM_CLKGEN_ERR_ASYNC_EINT2 */
+#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT2_SHIFT       0  /* IM_CLKGEN_ERR_ASYNC_EINT2 */
+#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT2_WIDTH       1  /* IM_CLKGEN_ERR_ASYNC_EINT2 */
+
+/*
+ * R3355 (0xD1B) - IRQ2 Status 4 Mask
+ */
+#define ARIZONA_IM_ASRC_CFG_ERR_EINT2            0x8000  /* IM_ASRC_CFG_ERR_EINT2 */
+#define ARIZONA_IM_ASRC_CFG_ERR_EINT2_MASK       0x8000  /* IM_ASRC_CFG_ERR_EINT2 */
+#define ARIZONA_IM_ASRC_CFG_ERR_EINT2_SHIFT          15  /* IM_ASRC_CFG_ERR_EINT2 */
+#define ARIZONA_IM_ASRC_CFG_ERR_EINT2_WIDTH           1  /* IM_ASRC_CFG_ERR_EINT2 */
+#define ARIZONA_IM_AIF3_ERR_EINT2                0x4000  /* IM_AIF3_ERR_EINT2 */
+#define ARIZONA_IM_AIF3_ERR_EINT2_MASK           0x4000  /* IM_AIF3_ERR_EINT2 */
+#define ARIZONA_IM_AIF3_ERR_EINT2_SHIFT              14  /* IM_AIF3_ERR_EINT2 */
+#define ARIZONA_IM_AIF3_ERR_EINT2_WIDTH               1  /* IM_AIF3_ERR_EINT2 */
+#define ARIZONA_IM_AIF2_ERR_EINT2                0x2000  /* IM_AIF2_ERR_EINT2 */
+#define ARIZONA_IM_AIF2_ERR_EINT2_MASK           0x2000  /* IM_AIF2_ERR_EINT2 */
+#define ARIZONA_IM_AIF2_ERR_EINT2_SHIFT              13  /* IM_AIF2_ERR_EINT2 */
+#define ARIZONA_IM_AIF2_ERR_EINT2_WIDTH               1  /* IM_AIF2_ERR_EINT2 */
+#define ARIZONA_IM_AIF1_ERR_EINT2                0x1000  /* IM_AIF1_ERR_EINT2 */
+#define ARIZONA_IM_AIF1_ERR_EINT2_MASK           0x1000  /* IM_AIF1_ERR_EINT2 */
+#define ARIZONA_IM_AIF1_ERR_EINT2_SHIFT              12  /* IM_AIF1_ERR_EINT2 */
+#define ARIZONA_IM_AIF1_ERR_EINT2_WIDTH               1  /* IM_AIF1_ERR_EINT2 */
+#define ARIZONA_IM_CTRLIF_ERR_EINT2              0x0800  /* IM_CTRLIF_ERR_EINT2 */
+#define ARIZONA_IM_CTRLIF_ERR_EINT2_MASK         0x0800  /* IM_CTRLIF_ERR_EINT2 */
+#define ARIZONA_IM_CTRLIF_ERR_EINT2_SHIFT            11  /* IM_CTRLIF_ERR_EINT2 */
+#define ARIZONA_IM_CTRLIF_ERR_EINT2_WIDTH             1  /* IM_CTRLIF_ERR_EINT2 */
+#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT2    0x0400  /* IM_MIXER_DROPPED_SAMPLE_EINT2 */
+#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT2_MASK 0x0400  /* IM_MIXER_DROPPED_SAMPLE_EINT2 */
+#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT2_SHIFT     10  /* IM_MIXER_DROPPED_SAMPLE_EINT2 */
+#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT2_WIDTH      1  /* IM_MIXER_DROPPED_SAMPLE_EINT2 */
+#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT2       0x0200  /* IM_ASYNC_CLK_ENA_LOW_EINT2 */
+#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT2_MASK  0x0200  /* IM_ASYNC_CLK_ENA_LOW_EINT2 */
+#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT2_SHIFT      9  /* IM_ASYNC_CLK_ENA_LOW_EINT2 */
+#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT2_WIDTH      1  /* IM_ASYNC_CLK_ENA_LOW_EINT2 */
+#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT2          0x0100  /* IM_SYSCLK_ENA_LOW_EINT2 */
+#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT2_MASK     0x0100  /* IM_SYSCLK_ENA_LOW_EINT2 */
+#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT2_SHIFT         8  /* IM_SYSCLK_ENA_LOW_EINT2 */
+#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT2_WIDTH         1  /* IM_SYSCLK_ENA_LOW_EINT2 */
+#define ARIZONA_IM_ISRC1_CFG_ERR_EINT2           0x0080  /* IM_ISRC1_CFG_ERR_EINT2 */
+#define ARIZONA_IM_ISRC1_CFG_ERR_EINT2_MASK      0x0080  /* IM_ISRC1_CFG_ERR_EINT2 */
+#define ARIZONA_IM_ISRC1_CFG_ERR_EINT2_SHIFT          7  /* IM_ISRC1_CFG_ERR_EINT2 */
+#define ARIZONA_IM_ISRC1_CFG_ERR_EINT2_WIDTH          1  /* IM_ISRC1_CFG_ERR_EINT2 */
+#define ARIZONA_IM_ISRC2_CFG_ERR_EINT2           0x0040  /* IM_ISRC2_CFG_ERR_EINT2 */
+#define ARIZONA_IM_ISRC2_CFG_ERR_EINT2_MASK      0x0040  /* IM_ISRC2_CFG_ERR_EINT2 */
+#define ARIZONA_IM_ISRC2_CFG_ERR_EINT2_SHIFT          6  /* IM_ISRC2_CFG_ERR_EINT2 */
+#define ARIZONA_IM_ISRC2_CFG_ERR_EINT2_WIDTH          1  /* IM_ISRC2_CFG_ERR_EINT2 */
+
+/*
+ * R3356 (0xD1C) - IRQ2 Status 5 Mask
+ */
+
+#define ARIZONA_IM_BOOT_DONE_EINT2               0x0100  /* IM_BOOT_DONE_EINT2 */
+#define ARIZONA_IM_BOOT_DONE_EINT2_MASK          0x0100  /* IM_BOOT_DONE_EINT2 */
+#define ARIZONA_IM_BOOT_DONE_EINT2_SHIFT              8  /* IM_BOOT_DONE_EINT2 */
+#define ARIZONA_IM_BOOT_DONE_EINT2_WIDTH              1  /* IM_BOOT_DONE_EINT2 */
+#define ARIZONA_IM_DCS_DAC_DONE_EINT2            0x0080  /* IM_DCS_DAC_DONE_EINT2 */
+#define ARIZONA_IM_DCS_DAC_DONE_EINT2_MASK       0x0080  /* IM_DCS_DAC_DONE_EINT2 */
+#define ARIZONA_IM_DCS_DAC_DONE_EINT2_SHIFT           7  /* IM_DCS_DAC_DONE_EINT2 */
+#define ARIZONA_IM_DCS_DAC_DONE_EINT2_WIDTH           1  /* IM_DCS_DAC_DONE_EINT2 */
+#define ARIZONA_IM_DCS_HP_DONE_EINT2             0x0040  /* IM_DCS_HP_DONE_EINT2 */
+#define ARIZONA_IM_DCS_HP_DONE_EINT2_MASK        0x0040  /* IM_DCS_HP_DONE_EINT2 */
+#define ARIZONA_IM_DCS_HP_DONE_EINT2_SHIFT            6  /* IM_DCS_HP_DONE_EINT2 */
+#define ARIZONA_IM_DCS_HP_DONE_EINT2_WIDTH            1  /* IM_DCS_HP_DONE_EINT2 */
+#define ARIZONA_IM_FLL2_CLOCK_OK_EINT2           0x0002  /* IM_FLL2_CLOCK_OK_EINT2 */
+#define ARIZONA_IM_FLL2_CLOCK_OK_EINT2_MASK      0x0002  /* IM_FLL2_CLOCK_OK_EINT2 */
+#define ARIZONA_IM_FLL2_CLOCK_OK_EINT2_SHIFT          1  /* IM_FLL2_CLOCK_OK_EINT2 */
+#define ARIZONA_IM_FLL2_CLOCK_OK_EINT2_WIDTH          1  /* IM_FLL2_CLOCK_OK_EINT2 */
+#define ARIZONA_IM_FLL1_CLOCK_OK_EINT2           0x0001  /* IM_FLL1_CLOCK_OK_EINT2 */
+#define ARIZONA_IM_FLL1_CLOCK_OK_EINT2_MASK      0x0001  /* IM_FLL1_CLOCK_OK_EINT2 */
+#define ARIZONA_IM_FLL1_CLOCK_OK_EINT2_SHIFT          0  /* IM_FLL1_CLOCK_OK_EINT2 */
+#define ARIZONA_IM_FLL1_CLOCK_OK_EINT2_WIDTH          1  /* IM_FLL1_CLOCK_OK_EINT2 */
+
+/*
+ * R3359 (0xD1F) - IRQ2 Control
+ */
+#define ARIZONA_IM_IRQ2                          0x0001  /* IM_IRQ2 */
+#define ARIZONA_IM_IRQ2_MASK                     0x0001  /* IM_IRQ2 */
+#define ARIZONA_IM_IRQ2_SHIFT                         0  /* IM_IRQ2 */
+#define ARIZONA_IM_IRQ2_WIDTH                         1  /* IM_IRQ2 */
+
+/*
+ * R3360 (0xD20) - Interrupt Raw Status 2
+ */
+#define ARIZONA_DSP1_RAM_RDY_STS                 0x0100  /* DSP1_RAM_RDY_STS */
+#define ARIZONA_DSP1_RAM_RDY_STS_MASK            0x0100  /* DSP1_RAM_RDY_STS */
+#define ARIZONA_DSP1_RAM_RDY_STS_SHIFT                8  /* DSP1_RAM_RDY_STS */
+#define ARIZONA_DSP1_RAM_RDY_STS_WIDTH                1  /* DSP1_RAM_RDY_STS */
+#define ARIZONA_DSP_IRQ2_STS                     0x0002  /* DSP_IRQ2_STS */
+#define ARIZONA_DSP_IRQ2_STS_MASK                0x0002  /* DSP_IRQ2_STS */
+#define ARIZONA_DSP_IRQ2_STS_SHIFT                    1  /* DSP_IRQ2_STS */
+#define ARIZONA_DSP_IRQ2_STS_WIDTH                    1  /* DSP_IRQ2_STS */
+#define ARIZONA_DSP_IRQ1_STS                     0x0001  /* DSP_IRQ1_STS */
+#define ARIZONA_DSP_IRQ1_STS_MASK                0x0001  /* DSP_IRQ1_STS */
+#define ARIZONA_DSP_IRQ1_STS_SHIFT                    0  /* DSP_IRQ1_STS */
+#define ARIZONA_DSP_IRQ1_STS_WIDTH                    1  /* DSP_IRQ1_STS */
+
+/*
+ * R3361 (0xD21) - Interrupt Raw Status 3
+ */
+#define ARIZONA_SPK_SHUTDOWN_WARN_STS            0x8000  /* SPK_SHUTDOWN_WARN_STS */
+#define ARIZONA_SPK_SHUTDOWN_WARN_STS_MASK       0x8000  /* SPK_SHUTDOWN_WARN_STS */
+#define ARIZONA_SPK_SHUTDOWN_WARN_STS_SHIFT          15  /* SPK_SHUTDOWN_WARN_STS */
+#define ARIZONA_SPK_SHUTDOWN_WARN_STS_WIDTH           1  /* SPK_SHUTDOWN_WARN_STS */
+#define ARIZONA_SPK_SHUTDOWN_STS                 0x4000  /* SPK_SHUTDOWN_STS */
+#define ARIZONA_SPK_SHUTDOWN_STS_MASK            0x4000  /* SPK_SHUTDOWN_STS */
+#define ARIZONA_SPK_SHUTDOWN_STS_SHIFT               14  /* SPK_SHUTDOWN_STS */
+#define ARIZONA_SPK_SHUTDOWN_STS_WIDTH                1  /* SPK_SHUTDOWN_STS */
+#define ARIZONA_HPDET_STS                        0x2000  /* HPDET_STS */
+#define ARIZONA_HPDET_STS_MASK                   0x2000  /* HPDET_STS */
+#define ARIZONA_HPDET_STS_SHIFT                      13  /* HPDET_STS */
+#define ARIZONA_HPDET_STS_WIDTH                       1  /* HPDET_STS */
+#define ARIZONA_MICDET_STS                       0x1000  /* MICDET_STS */
+#define ARIZONA_MICDET_STS_MASK                  0x1000  /* MICDET_STS */
+#define ARIZONA_MICDET_STS_SHIFT                     12  /* MICDET_STS */
+#define ARIZONA_MICDET_STS_WIDTH                      1  /* MICDET_STS */
+#define ARIZONA_WSEQ_DONE_STS                    0x0800  /* WSEQ_DONE_STS */
+#define ARIZONA_WSEQ_DONE_STS_MASK               0x0800  /* WSEQ_DONE_STS */
+#define ARIZONA_WSEQ_DONE_STS_SHIFT                  11  /* WSEQ_DONE_STS */
+#define ARIZONA_WSEQ_DONE_STS_WIDTH                   1  /* WSEQ_DONE_STS */
+#define ARIZONA_DRC2_SIG_DET_STS                 0x0400  /* DRC2_SIG_DET_STS */
+#define ARIZONA_DRC2_SIG_DET_STS_MASK            0x0400  /* DRC2_SIG_DET_STS */
+#define ARIZONA_DRC2_SIG_DET_STS_SHIFT               10  /* DRC2_SIG_DET_STS */
+#define ARIZONA_DRC2_SIG_DET_STS_WIDTH                1  /* DRC2_SIG_DET_STS */
+#define ARIZONA_DRC1_SIG_DET_STS                 0x0200  /* DRC1_SIG_DET_STS */
+#define ARIZONA_DRC1_SIG_DET_STS_MASK            0x0200  /* DRC1_SIG_DET_STS */
+#define ARIZONA_DRC1_SIG_DET_STS_SHIFT                9  /* DRC1_SIG_DET_STS */
+#define ARIZONA_DRC1_SIG_DET_STS_WIDTH                1  /* DRC1_SIG_DET_STS */
+#define ARIZONA_ASRC2_LOCK_STS                   0x0100  /* ASRC2_LOCK_STS */
+#define ARIZONA_ASRC2_LOCK_STS_MASK              0x0100  /* ASRC2_LOCK_STS */
+#define ARIZONA_ASRC2_LOCK_STS_SHIFT                  8  /* ASRC2_LOCK_STS */
+#define ARIZONA_ASRC2_LOCK_STS_WIDTH                  1  /* ASRC2_LOCK_STS */
+#define ARIZONA_ASRC1_LOCK_STS                   0x0080  /* ASRC1_LOCK_STS */
+#define ARIZONA_ASRC1_LOCK_STS_MASK              0x0080  /* ASRC1_LOCK_STS */
+#define ARIZONA_ASRC1_LOCK_STS_SHIFT                  7  /* ASRC1_LOCK_STS */
+#define ARIZONA_ASRC1_LOCK_STS_WIDTH                  1  /* ASRC1_LOCK_STS */
+#define ARIZONA_UNDERCLOCKED_STS                 0x0040  /* UNDERCLOCKED_STS */
+#define ARIZONA_UNDERCLOCKED_STS_MASK            0x0040  /* UNDERCLOCKED_STS */
+#define ARIZONA_UNDERCLOCKED_STS_SHIFT                6  /* UNDERCLOCKED_STS */
+#define ARIZONA_UNDERCLOCKED_STS_WIDTH                1  /* UNDERCLOCKED_STS */
+#define ARIZONA_OVERCLOCKED_STS                  0x0020  /* OVERCLOCKED_STS */
+#define ARIZONA_OVERCLOCKED_STS_MASK             0x0020  /* OVERCLOCKED_STS */
+#define ARIZONA_OVERCLOCKED_STS_SHIFT                 5  /* OVERCLOCKED_STS */
+#define ARIZONA_OVERCLOCKED_STS_WIDTH                 1  /* OVERCLOCKED_STS */
+#define ARIZONA_FLL2_LOCK_STS                    0x0008  /* FLL2_LOCK_STS */
+#define ARIZONA_FLL2_LOCK_STS_MASK               0x0008  /* FLL2_LOCK_STS */
+#define ARIZONA_FLL2_LOCK_STS_SHIFT                   3  /* FLL2_LOCK_STS */
+#define ARIZONA_FLL2_LOCK_STS_WIDTH                   1  /* FLL2_LOCK_STS */
+#define ARIZONA_FLL1_LOCK_STS                    0x0004  /* FLL1_LOCK_STS */
+#define ARIZONA_FLL1_LOCK_STS_MASK               0x0004  /* FLL1_LOCK_STS */
+#define ARIZONA_FLL1_LOCK_STS_SHIFT                   2  /* FLL1_LOCK_STS */
+#define ARIZONA_FLL1_LOCK_STS_WIDTH                   1  /* FLL1_LOCK_STS */
+#define ARIZONA_CLKGEN_ERR_STS                   0x0002  /* CLKGEN_ERR_STS */
+#define ARIZONA_CLKGEN_ERR_STS_MASK              0x0002  /* CLKGEN_ERR_STS */
+#define ARIZONA_CLKGEN_ERR_STS_SHIFT                  1  /* CLKGEN_ERR_STS */
+#define ARIZONA_CLKGEN_ERR_STS_WIDTH                  1  /* CLKGEN_ERR_STS */
+#define ARIZONA_CLKGEN_ERR_ASYNC_STS             0x0001  /* CLKGEN_ERR_ASYNC_STS */
+#define ARIZONA_CLKGEN_ERR_ASYNC_STS_MASK        0x0001  /* CLKGEN_ERR_ASYNC_STS */
+#define ARIZONA_CLKGEN_ERR_ASYNC_STS_SHIFT            0  /* CLKGEN_ERR_ASYNC_STS */
+#define ARIZONA_CLKGEN_ERR_ASYNC_STS_WIDTH            1  /* CLKGEN_ERR_ASYNC_STS */
+
+/*
+ * R3362 (0xD22) - Interrupt Raw Status 4
+ */
+#define ARIZONA_ASRC_CFG_ERR_STS                 0x8000  /* ASRC_CFG_ERR_STS */
+#define ARIZONA_ASRC_CFG_ERR_STS_MASK            0x8000  /* ASRC_CFG_ERR_STS */
+#define ARIZONA_ASRC_CFG_ERR_STS_SHIFT               15  /* ASRC_CFG_ERR_STS */
+#define ARIZONA_ASRC_CFG_ERR_STS_WIDTH                1  /* ASRC_CFG_ERR_STS */
+#define ARIZONA_AIF3_ERR_STS                     0x4000  /* AIF3_ERR_STS */
+#define ARIZONA_AIF3_ERR_STS_MASK                0x4000  /* AIF3_ERR_STS */
+#define ARIZONA_AIF3_ERR_STS_SHIFT                   14  /* AIF3_ERR_STS */
+#define ARIZONA_AIF3_ERR_STS_WIDTH                    1  /* AIF3_ERR_STS */
+#define ARIZONA_AIF2_ERR_STS                     0x2000  /* AIF2_ERR_STS */
+#define ARIZONA_AIF2_ERR_STS_MASK                0x2000  /* AIF2_ERR_STS */
+#define ARIZONA_AIF2_ERR_STS_SHIFT                   13  /* AIF2_ERR_STS */
+#define ARIZONA_AIF2_ERR_STS_WIDTH                    1  /* AIF2_ERR_STS */
+#define ARIZONA_AIF1_ERR_STS                     0x1000  /* AIF1_ERR_STS */
+#define ARIZONA_AIF1_ERR_STS_MASK                0x1000  /* AIF1_ERR_STS */
+#define ARIZONA_AIF1_ERR_STS_SHIFT                   12  /* AIF1_ERR_STS */
+#define ARIZONA_AIF1_ERR_STS_WIDTH                    1  /* AIF1_ERR_STS */
+#define ARIZONA_CTRLIF_ERR_STS                   0x0800  /* CTRLIF_ERR_STS */
+#define ARIZONA_CTRLIF_ERR_STS_MASK              0x0800  /* CTRLIF_ERR_STS */
+#define ARIZONA_CTRLIF_ERR_STS_SHIFT                 11  /* CTRLIF_ERR_STS */
+#define ARIZONA_CTRLIF_ERR_STS_WIDTH                  1  /* CTRLIF_ERR_STS */
+#define ARIZONA_MIXER_DROPPED_SAMPLE_STS         0x0400  /* MIXER_DROPPED_SAMPLE_STS */
+#define ARIZONA_MIXER_DROPPED_SAMPLE_STS_MASK    0x0400  /* MIXER_DROPPED_SAMPLE_STS */
+#define ARIZONA_MIXER_DROPPED_SAMPLE_STS_SHIFT       10  /* MIXER_DROPPED_SAMPLE_STS */
+#define ARIZONA_MIXER_DROPPED_SAMPLE_STS_WIDTH        1  /* MIXER_DROPPED_SAMPLE_STS */
+#define ARIZONA_ASYNC_CLK_ENA_LOW_STS            0x0200  /* ASYNC_CLK_ENA_LOW_STS */
+#define ARIZONA_ASYNC_CLK_ENA_LOW_STS_MASK       0x0200  /* ASYNC_CLK_ENA_LOW_STS */
+#define ARIZONA_ASYNC_CLK_ENA_LOW_STS_SHIFT           9  /* ASYNC_CLK_ENA_LOW_STS */
+#define ARIZONA_ASYNC_CLK_ENA_LOW_STS_WIDTH           1  /* ASYNC_CLK_ENA_LOW_STS */
+#define ARIZONA_SYSCLK_ENA_LOW_STS               0x0100  /* SYSCLK_ENA_LOW_STS */
+#define ARIZONA_SYSCLK_ENA_LOW_STS_MASK          0x0100  /* SYSCLK_ENA_LOW_STS */
+#define ARIZONA_SYSCLK_ENA_LOW_STS_SHIFT              8  /* SYSCLK_ENA_LOW_STS */
+#define ARIZONA_SYSCLK_ENA_LOW_STS_WIDTH              1  /* SYSCLK_ENA_LOW_STS */
+#define ARIZONA_ISRC1_CFG_ERR_STS                0x0080  /* ISRC1_CFG_ERR_STS */
+#define ARIZONA_ISRC1_CFG_ERR_STS_MASK           0x0080  /* ISRC1_CFG_ERR_STS */
+#define ARIZONA_ISRC1_CFG_ERR_STS_SHIFT               7  /* ISRC1_CFG_ERR_STS */
+#define ARIZONA_ISRC1_CFG_ERR_STS_WIDTH               1  /* ISRC1_CFG_ERR_STS */
+#define ARIZONA_ISRC2_CFG_ERR_STS                0x0040  /* ISRC2_CFG_ERR_STS */
+#define ARIZONA_ISRC2_CFG_ERR_STS_MASK           0x0040  /* ISRC2_CFG_ERR_STS */
+#define ARIZONA_ISRC2_CFG_ERR_STS_SHIFT               6  /* ISRC2_CFG_ERR_STS */
+#define ARIZONA_ISRC2_CFG_ERR_STS_WIDTH               1  /* ISRC2_CFG_ERR_STS */
+
+/*
+ * R3363 (0xD23) - Interrupt Raw Status 5
+ */
+#define ARIZONA_BOOT_DONE_STS                    0x0100  /* BOOT_DONE_STS */
+#define ARIZONA_BOOT_DONE_STS_MASK               0x0100  /* BOOT_DONE_STS */
+#define ARIZONA_BOOT_DONE_STS_SHIFT                   8  /* BOOT_DONE_STS */
+#define ARIZONA_BOOT_DONE_STS_WIDTH                   1  /* BOOT_DONE_STS */
+#define ARIZONA_DCS_DAC_DONE_STS                 0x0080  /* DCS_DAC_DONE_STS */
+#define ARIZONA_DCS_DAC_DONE_STS_MASK            0x0080  /* DCS_DAC_DONE_STS */
+#define ARIZONA_DCS_DAC_DONE_STS_SHIFT                7  /* DCS_DAC_DONE_STS */
+#define ARIZONA_DCS_DAC_DONE_STS_WIDTH                1  /* DCS_DAC_DONE_STS */
+#define ARIZONA_DCS_HP_DONE_STS                  0x0040  /* DCS_HP_DONE_STS */
+#define ARIZONA_DCS_HP_DONE_STS_MASK             0x0040  /* DCS_HP_DONE_STS */
+#define ARIZONA_DCS_HP_DONE_STS_SHIFT                 6  /* DCS_HP_DONE_STS */
+#define ARIZONA_DCS_HP_DONE_STS_WIDTH                 1  /* DCS_HP_DONE_STS */
+#define ARIZONA_FLL2_CLOCK_OK_STS                0x0002  /* FLL2_CLOCK_OK_STS */
+#define ARIZONA_FLL2_CLOCK_OK_STS_MASK           0x0002  /* FLL2_CLOCK_OK_STS */
+#define ARIZONA_FLL2_CLOCK_OK_STS_SHIFT               1  /* FLL2_CLOCK_OK_STS */
+#define ARIZONA_FLL2_CLOCK_OK_STS_WIDTH               1  /* FLL2_CLOCK_OK_STS */
+#define ARIZONA_FLL1_CLOCK_OK_STS                0x0001  /* FLL1_CLOCK_OK_STS */
+#define ARIZONA_FLL1_CLOCK_OK_STS_MASK           0x0001  /* FLL1_CLOCK_OK_STS */
+#define ARIZONA_FLL1_CLOCK_OK_STS_SHIFT               0  /* FLL1_CLOCK_OK_STS */
+#define ARIZONA_FLL1_CLOCK_OK_STS_WIDTH               1  /* FLL1_CLOCK_OK_STS */
+
+/*
+ * R3364 (0xD24) - Interrupt Raw Status 6
+ */
+#define ARIZONA_PWM_OVERCLOCKED_STS              0x2000  /* PWM_OVERCLOCKED_STS */
+#define ARIZONA_PWM_OVERCLOCKED_STS_MASK         0x2000  /* PWM_OVERCLOCKED_STS */
+#define ARIZONA_PWM_OVERCLOCKED_STS_SHIFT            13  /* PWM_OVERCLOCKED_STS */
+#define ARIZONA_PWM_OVERCLOCKED_STS_WIDTH             1  /* PWM_OVERCLOCKED_STS */
+#define ARIZONA_FX_CORE_OVERCLOCKED_STS          0x1000  /* FX_CORE_OVERCLOCKED_STS */
+#define ARIZONA_FX_CORE_OVERCLOCKED_STS_MASK     0x1000  /* FX_CORE_OVERCLOCKED_STS */
+#define ARIZONA_FX_CORE_OVERCLOCKED_STS_SHIFT        12  /* FX_CORE_OVERCLOCKED_STS */
+#define ARIZONA_FX_CORE_OVERCLOCKED_STS_WIDTH         1  /* FX_CORE_OVERCLOCKED_STS */
+#define ARIZONA_DAC_SYS_OVERCLOCKED_STS          0x0400  /* DAC_SYS_OVERCLOCKED_STS */
+#define ARIZONA_DAC_SYS_OVERCLOCKED_STS_MASK     0x0400  /* DAC_SYS_OVERCLOCKED_STS */
+#define ARIZONA_DAC_SYS_OVERCLOCKED_STS_SHIFT        10  /* DAC_SYS_OVERCLOCKED_STS */
+#define ARIZONA_DAC_SYS_OVERCLOCKED_STS_WIDTH         1  /* DAC_SYS_OVERCLOCKED_STS */
+#define ARIZONA_DAC_WARP_OVERCLOCKED_STS         0x0200  /* DAC_WARP_OVERCLOCKED_STS */
+#define ARIZONA_DAC_WARP_OVERCLOCKED_STS_MASK    0x0200  /* DAC_WARP_OVERCLOCKED_STS */
+#define ARIZONA_DAC_WARP_OVERCLOCKED_STS_SHIFT        9  /* DAC_WARP_OVERCLOCKED_STS */
+#define ARIZONA_DAC_WARP_OVERCLOCKED_STS_WIDTH        1  /* DAC_WARP_OVERCLOCKED_STS */
+#define ARIZONA_ADC_OVERCLOCKED_STS              0x0100  /* ADC_OVERCLOCKED_STS */
+#define ARIZONA_ADC_OVERCLOCKED_STS_MASK         0x0100  /* ADC_OVERCLOCKED_STS */
+#define ARIZONA_ADC_OVERCLOCKED_STS_SHIFT             8  /* ADC_OVERCLOCKED_STS */
+#define ARIZONA_ADC_OVERCLOCKED_STS_WIDTH             1  /* ADC_OVERCLOCKED_STS */
+#define ARIZONA_MIXER_OVERCLOCKED_STS            0x0080  /* MIXER_OVERCLOCKED_STS */
+#define ARIZONA_MIXER_OVERCLOCKED_STS_MASK       0x0080  /* MIXER_OVERCLOCKED_STS */
+#define ARIZONA_MIXER_OVERCLOCKED_STS_SHIFT           7  /* MIXER_OVERCLOCKED_STS */
+#define ARIZONA_MIXER_OVERCLOCKED_STS_WIDTH           1  /* MIXER_OVERCLOCKED_STS */
+#define ARIZONA_AIF3_ASYNC_OVERCLOCKED_STS       0x0040  /* AIF3_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF3_ASYNC_OVERCLOCKED_STS_MASK  0x0040  /* AIF3_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF3_ASYNC_OVERCLOCKED_STS_SHIFT      6  /* AIF3_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF3_ASYNC_OVERCLOCKED_STS_WIDTH      1  /* AIF3_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF2_ASYNC_OVERCLOCKED_STS       0x0020  /* AIF2_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF2_ASYNC_OVERCLOCKED_STS_MASK  0x0020  /* AIF2_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF2_ASYNC_OVERCLOCKED_STS_SHIFT      5  /* AIF2_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF2_ASYNC_OVERCLOCKED_STS_WIDTH      1  /* AIF2_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF1_ASYNC_OVERCLOCKED_STS       0x0010  /* AIF1_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF1_ASYNC_OVERCLOCKED_STS_MASK  0x0010  /* AIF1_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF1_ASYNC_OVERCLOCKED_STS_SHIFT      4  /* AIF1_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF1_ASYNC_OVERCLOCKED_STS_WIDTH      1  /* AIF1_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF3_SYNC_OVERCLOCKED_STS        0x0008  /* AIF3_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF3_SYNC_OVERCLOCKED_STS_MASK   0x0008  /* AIF3_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF3_SYNC_OVERCLOCKED_STS_SHIFT       3  /* AIF3_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF3_SYNC_OVERCLOCKED_STS_WIDTH       1  /* AIF3_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF2_SYNC_OVERCLOCKED_STS        0x0004  /* AIF2_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF2_SYNC_OVERCLOCKED_STS_MASK   0x0004  /* AIF2_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF2_SYNC_OVERCLOCKED_STS_SHIFT       2  /* AIF2_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF2_SYNC_OVERCLOCKED_STS_WIDTH       1  /* AIF2_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF1_SYNC_OVERCLOCKED_STS        0x0002  /* AIF1_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF1_SYNC_OVERCLOCKED_STS_MASK   0x0002  /* AIF1_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF1_SYNC_OVERCLOCKED_STS_SHIFT       1  /* AIF1_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_AIF1_SYNC_OVERCLOCKED_STS_WIDTH       1  /* AIF1_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_PAD_CTRL_OVERCLOCKED_STS         0x0001  /* PAD_CTRL_OVERCLOCKED_STS */
+#define ARIZONA_PAD_CTRL_OVERCLOCKED_STS_MASK    0x0001  /* PAD_CTRL_OVERCLOCKED_STS */
+#define ARIZONA_PAD_CTRL_OVERCLOCKED_STS_SHIFT        0  /* PAD_CTRL_OVERCLOCKED_STS */
+#define ARIZONA_PAD_CTRL_OVERCLOCKED_STS_WIDTH        1  /* PAD_CTRL_OVERCLOCKED_STS */
+
+/*
+ * R3365 (0xD25) - Interrupt Raw Status 7
+ */
+#define ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS   0x8000  /* SLIMBUS_SUBSYS_OVERCLOCKED_STS */
+#define ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS_MASK 0x8000  /* SLIMBUS_SUBSYS_OVERCLOCKED_STS */
+#define ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS_SHIFT     15  /* SLIMBUS_SUBSYS_OVERCLOCKED_STS */
+#define ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS_WIDTH      1  /* SLIMBUS_SUBSYS_OVERCLOCKED_STS */
+#define ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS    0x4000  /* SLIMBUS_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS_MASK 0x4000  /* SLIMBUS_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS_SHIFT     14  /* SLIMBUS_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS_WIDTH      1  /* SLIMBUS_ASYNC_OVERCLOCKED_STS */
+#define ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS     0x2000  /* SLIMBUS_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS_MASK 0x2000  /* SLIMBUS_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS_SHIFT     13  /* SLIMBUS_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS_WIDTH      1  /* SLIMBUS_SYNC_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS   0x1000  /* ASRC_ASYNC_SYS_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS_MASK 0x1000  /* ASRC_ASYNC_SYS_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS_SHIFT     12  /* ASRC_ASYNC_SYS_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS_WIDTH      1  /* ASRC_ASYNC_SYS_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS  0x0800  /* ASRC_ASYNC_WARP_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS_MASK 0x0800  /* ASRC_ASYNC_WARP_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS_SHIFT     11  /* ASRC_ASYNC_WARP_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS_WIDTH      1  /* ASRC_ASYNC_WARP_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS    0x0400  /* ASRC_SYNC_SYS_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS_MASK 0x0400  /* ASRC_SYNC_SYS_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS_SHIFT     10  /* ASRC_SYNC_SYS_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS_WIDTH      1  /* ASRC_SYNC_SYS_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS   0x0200  /* ASRC_SYNC_WARP_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS_MASK 0x0200  /* ASRC_SYNC_WARP_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS_SHIFT      9  /* ASRC_SYNC_WARP_OVERCLOCKED_STS */
+#define ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS_WIDTH      1  /* ASRC_SYNC_WARP_OVERCLOCKED_STS */
+#define ARIZONA_ADSP2_1_OVERCLOCKED_STS          0x0008  /* ADSP2_1_OVERCLOCKED_STS */
+#define ARIZONA_ADSP2_1_OVERCLOCKED_STS_MASK     0x0008  /* ADSP2_1_OVERCLOCKED_STS */
+#define ARIZONA_ADSP2_1_OVERCLOCKED_STS_SHIFT         3  /* ADSP2_1_OVERCLOCKED_STS */
+#define ARIZONA_ADSP2_1_OVERCLOCKED_STS_WIDTH         1  /* ADSP2_1_OVERCLOCKED_STS */
+#define ARIZONA_ISRC2_OVERCLOCKED_STS            0x0002  /* ISRC2_OVERCLOCKED_STS */
+#define ARIZONA_ISRC2_OVERCLOCKED_STS_MASK       0x0002  /* ISRC2_OVERCLOCKED_STS */
+#define ARIZONA_ISRC2_OVERCLOCKED_STS_SHIFT           1  /* ISRC2_OVERCLOCKED_STS */
+#define ARIZONA_ISRC2_OVERCLOCKED_STS_WIDTH           1  /* ISRC2_OVERCLOCKED_STS */
+#define ARIZONA_ISRC1_OVERCLOCKED_STS            0x0001  /* ISRC1_OVERCLOCKED_STS */
+#define ARIZONA_ISRC1_OVERCLOCKED_STS_MASK       0x0001  /* ISRC1_OVERCLOCKED_STS */
+#define ARIZONA_ISRC1_OVERCLOCKED_STS_SHIFT           0  /* ISRC1_OVERCLOCKED_STS */
+#define ARIZONA_ISRC1_OVERCLOCKED_STS_WIDTH           1  /* ISRC1_OVERCLOCKED_STS */
+
+/*
+ * R3366 (0xD26) - Interrupt Raw Status 8
+ */
+#define ARIZONA_AIF3_UNDERCLOCKED_STS            0x0400  /* AIF3_UNDERCLOCKED_STS */
+#define ARIZONA_AIF3_UNDERCLOCKED_STS_MASK       0x0400  /* AIF3_UNDERCLOCKED_STS */
+#define ARIZONA_AIF3_UNDERCLOCKED_STS_SHIFT          10  /* AIF3_UNDERCLOCKED_STS */
+#define ARIZONA_AIF3_UNDERCLOCKED_STS_WIDTH           1  /* AIF3_UNDERCLOCKED_STS */
+#define ARIZONA_AIF2_UNDERCLOCKED_STS            0x0200  /* AIF2_UNDERCLOCKED_STS */
+#define ARIZONA_AIF2_UNDERCLOCKED_STS_MASK       0x0200  /* AIF2_UNDERCLOCKED_STS */
+#define ARIZONA_AIF2_UNDERCLOCKED_STS_SHIFT           9  /* AIF2_UNDERCLOCKED_STS */
+#define ARIZONA_AIF2_UNDERCLOCKED_STS_WIDTH           1  /* AIF2_UNDERCLOCKED_STS */
+#define ARIZONA_AIF1_UNDERCLOCKED_STS            0x0100  /* AIF1_UNDERCLOCKED_STS */
+#define ARIZONA_AIF1_UNDERCLOCKED_STS_MASK       0x0100  /* AIF1_UNDERCLOCKED_STS */
+#define ARIZONA_AIF1_UNDERCLOCKED_STS_SHIFT           8  /* AIF1_UNDERCLOCKED_STS */
+#define ARIZONA_AIF1_UNDERCLOCKED_STS_WIDTH           1  /* AIF1_UNDERCLOCKED_STS */
+#define ARIZONA_ISRC2_UNDERCLOCKED_STS           0x0040  /* ISRC2_UNDERCLOCKED_STS */
+#define ARIZONA_ISRC2_UNDERCLOCKED_STS_MASK      0x0040  /* ISRC2_UNDERCLOCKED_STS */
+#define ARIZONA_ISRC2_UNDERCLOCKED_STS_SHIFT          6  /* ISRC2_UNDERCLOCKED_STS */
+#define ARIZONA_ISRC2_UNDERCLOCKED_STS_WIDTH          1  /* ISRC2_UNDERCLOCKED_STS */
+#define ARIZONA_ISRC1_UNDERCLOCKED_STS           0x0020  /* ISRC1_UNDERCLOCKED_STS */
+#define ARIZONA_ISRC1_UNDERCLOCKED_STS_MASK      0x0020  /* ISRC1_UNDERCLOCKED_STS */
+#define ARIZONA_ISRC1_UNDERCLOCKED_STS_SHIFT          5  /* ISRC1_UNDERCLOCKED_STS */
+#define ARIZONA_ISRC1_UNDERCLOCKED_STS_WIDTH          1  /* ISRC1_UNDERCLOCKED_STS */
+#define ARIZONA_FX_UNDERCLOCKED_STS              0x0010  /* FX_UNDERCLOCKED_STS */
+#define ARIZONA_FX_UNDERCLOCKED_STS_MASK         0x0010  /* FX_UNDERCLOCKED_STS */
+#define ARIZONA_FX_UNDERCLOCKED_STS_SHIFT             4  /* FX_UNDERCLOCKED_STS */
+#define ARIZONA_FX_UNDERCLOCKED_STS_WIDTH             1  /* FX_UNDERCLOCKED_STS */
+#define ARIZONA_ASRC_UNDERCLOCKED_STS            0x0008  /* ASRC_UNDERCLOCKED_STS */
+#define ARIZONA_ASRC_UNDERCLOCKED_STS_MASK       0x0008  /* ASRC_UNDERCLOCKED_STS */
+#define ARIZONA_ASRC_UNDERCLOCKED_STS_SHIFT           3  /* ASRC_UNDERCLOCKED_STS */
+#define ARIZONA_ASRC_UNDERCLOCKED_STS_WIDTH           1  /* ASRC_UNDERCLOCKED_STS */
+#define ARIZONA_DAC_UNDERCLOCKED_STS             0x0004  /* DAC_UNDERCLOCKED_STS */
+#define ARIZONA_DAC_UNDERCLOCKED_STS_MASK        0x0004  /* DAC_UNDERCLOCKED_STS */
+#define ARIZONA_DAC_UNDERCLOCKED_STS_SHIFT            2  /* DAC_UNDERCLOCKED_STS */
+#define ARIZONA_DAC_UNDERCLOCKED_STS_WIDTH            1  /* DAC_UNDERCLOCKED_STS */
+#define ARIZONA_ADC_UNDERCLOCKED_STS             0x0002  /* ADC_UNDERCLOCKED_STS */
+#define ARIZONA_ADC_UNDERCLOCKED_STS_MASK        0x0002  /* ADC_UNDERCLOCKED_STS */
+#define ARIZONA_ADC_UNDERCLOCKED_STS_SHIFT            1  /* ADC_UNDERCLOCKED_STS */
+#define ARIZONA_ADC_UNDERCLOCKED_STS_WIDTH            1  /* ADC_UNDERCLOCKED_STS */
+#define ARIZONA_MIXER_UNDERCLOCKED_STS           0x0001  /* MIXER_UNDERCLOCKED_STS */
+#define ARIZONA_MIXER_UNDERCLOCKED_STS_MASK      0x0001  /* MIXER_UNDERCLOCKED_STS */
+#define ARIZONA_MIXER_UNDERCLOCKED_STS_SHIFT          0  /* MIXER_UNDERCLOCKED_STS */
+#define ARIZONA_MIXER_UNDERCLOCKED_STS_WIDTH          1  /* MIXER_UNDERCLOCKED_STS */
+
+/*
+ * R3392 (0xD40) - IRQ Pin Status
+ */
+#define ARIZONA_IRQ2_STS                         0x0002  /* IRQ2_STS */
+#define ARIZONA_IRQ2_STS_MASK                    0x0002  /* IRQ2_STS */
+#define ARIZONA_IRQ2_STS_SHIFT                        1  /* IRQ2_STS */
+#define ARIZONA_IRQ2_STS_WIDTH                        1  /* IRQ2_STS */
+#define ARIZONA_IRQ1_STS                         0x0001  /* IRQ1_STS */
+#define ARIZONA_IRQ1_STS_MASK                    0x0001  /* IRQ1_STS */
+#define ARIZONA_IRQ1_STS_SHIFT                        0  /* IRQ1_STS */
+#define ARIZONA_IRQ1_STS_WIDTH                        1  /* IRQ1_STS */
+
+/*
+ * R3393 (0xD41) - ADSP2 IRQ0
+ */
+#define ARIZONA_DSP_IRQ2                         0x0002  /* DSP_IRQ2 */
+#define ARIZONA_DSP_IRQ2_MASK                    0x0002  /* DSP_IRQ2 */
+#define ARIZONA_DSP_IRQ2_SHIFT                        1  /* DSP_IRQ2 */
+#define ARIZONA_DSP_IRQ2_WIDTH                        1  /* DSP_IRQ2 */
+#define ARIZONA_DSP_IRQ1                         0x0001  /* DSP_IRQ1 */
+#define ARIZONA_DSP_IRQ1_MASK                    0x0001  /* DSP_IRQ1 */
+#define ARIZONA_DSP_IRQ1_SHIFT                        0  /* DSP_IRQ1 */
+#define ARIZONA_DSP_IRQ1_WIDTH                        1  /* DSP_IRQ1 */
+
+/*
+ * R3408 (0xD50) - AOD wkup and trig
+ */
+#define ARIZONA_GP5_FALL_TRIG_STS                0x0020  /* GP5_FALL_TRIG_STS */
+#define ARIZONA_GP5_FALL_TRIG_STS_MASK           0x0020  /* GP5_FALL_TRIG_STS */
+#define ARIZONA_GP5_FALL_TRIG_STS_SHIFT               5  /* GP5_FALL_TRIG_STS */
+#define ARIZONA_GP5_FALL_TRIG_STS_WIDTH               1  /* GP5_FALL_TRIG_STS */
+#define ARIZONA_GP5_RISE_TRIG_STS                0x0010  /* GP5_RISE_TRIG_STS */
+#define ARIZONA_GP5_RISE_TRIG_STS_MASK           0x0010  /* GP5_RISE_TRIG_STS */
+#define ARIZONA_GP5_RISE_TRIG_STS_SHIFT               4  /* GP5_RISE_TRIG_STS */
+#define ARIZONA_GP5_RISE_TRIG_STS_WIDTH               1  /* GP5_RISE_TRIG_STS */
+#define ARIZONA_JD1_FALL_TRIG_STS                0x0008  /* JD1_FALL_TRIG_STS */
+#define ARIZONA_JD1_FALL_TRIG_STS_MASK           0x0008  /* JD1_FALL_TRIG_STS */
+#define ARIZONA_JD1_FALL_TRIG_STS_SHIFT               3  /* JD1_FALL_TRIG_STS */
+#define ARIZONA_JD1_FALL_TRIG_STS_WIDTH               1  /* JD1_FALL_TRIG_STS */
+#define ARIZONA_JD1_RISE_TRIG_STS                0x0004  /* JD1_RISE_TRIG_STS */
+#define ARIZONA_JD1_RISE_TRIG_STS_MASK           0x0004  /* JD1_RISE_TRIG_STS */
+#define ARIZONA_JD1_RISE_TRIG_STS_SHIFT               2  /* JD1_RISE_TRIG_STS */
+#define ARIZONA_JD1_RISE_TRIG_STS_WIDTH               1  /* JD1_RISE_TRIG_STS */
+#define ARIZONA_JD2_FALL_TRIG_STS                0x0002  /* JD2_FALL_TRIG_STS */
+#define ARIZONA_JD2_FALL_TRIG_STS_MASK           0x0002  /* JD2_FALL_TRIG_STS */
+#define ARIZONA_JD2_FALL_TRIG_STS_SHIFT               1  /* JD2_FALL_TRIG_STS */
+#define ARIZONA_JD2_FALL_TRIG_STS_WIDTH               1  /* JD2_FALL_TRIG_STS */
+#define ARIZONA_JD2_RISE_TRIG_STS                0x0001  /* JD2_RISE_TRIG_STS */
+#define ARIZONA_JD2_RISE_TRIG_STS_MASK           0x0001  /* JD2_RISE_TRIG_STS */
+#define ARIZONA_JD2_RISE_TRIG_STS_SHIFT               0  /* JD2_RISE_TRIG_STS */
+#define ARIZONA_JD2_RISE_TRIG_STS_WIDTH               1  /* JD2_RISE_TRIG_STS */
+
+/*
+ * R3409 (0xD51) - AOD IRQ1
+ */
+#define ARIZONA_GP5_FALL_EINT1                   0x0020  /* GP5_FALL_EINT1 */
+#define ARIZONA_GP5_FALL_EINT1_MASK              0x0020  /* GP5_FALL_EINT1 */
+#define ARIZONA_GP5_FALL_EINT1_SHIFT                  5  /* GP5_FALL_EINT1 */
+#define ARIZONA_GP5_FALL_EINT1_WIDTH                  1  /* GP5_FALL_EINT1 */
+#define ARIZONA_GP5_RISE_EINT1                   0x0010  /* GP5_RISE_EINT1 */
+#define ARIZONA_GP5_RISE_EINT1_MASK              0x0010  /* GP5_RISE_EINT1 */
+#define ARIZONA_GP5_RISE_EINT1_SHIFT                  4  /* GP5_RISE_EINT1 */
+#define ARIZONA_GP5_RISE_EINT1_WIDTH                  1  /* GP5_RISE_EINT1 */
+#define ARIZONA_JD1_FALL_EINT1                   0x0008  /* JD1_FALL_EINT1 */
+#define ARIZONA_JD1_FALL_EINT1_MASK              0x0008  /* JD1_FALL_EINT1 */
+#define ARIZONA_JD1_FALL_EINT1_SHIFT                  3  /* JD1_FALL_EINT1 */
+#define ARIZONA_JD1_FALL_EINT1_WIDTH                  1  /* JD1_FALL_EINT1 */
+#define ARIZONA_JD1_RISE_EINT1                   0x0004  /* JD1_RISE_EINT1 */
+#define ARIZONA_JD1_RISE_EINT1_MASK              0x0004  /* JD1_RISE_EINT1 */
+#define ARIZONA_JD1_RISE_EINT1_SHIFT                  2  /* JD1_RISE_EINT1 */
+#define ARIZONA_JD1_RISE_EINT1_WIDTH                  1  /* JD1_RISE_EINT1 */
+#define ARIZONA_JD2_FALL_EINT1                   0x0002  /* JD2_FALL_EINT1 */
+#define ARIZONA_JD2_FALL_EINT1_MASK              0x0002  /* JD2_FALL_EINT1 */
+#define ARIZONA_JD2_FALL_EINT1_SHIFT                  1  /* JD2_FALL_EINT1 */
+#define ARIZONA_JD2_FALL_EINT1_WIDTH                  1  /* JD2_FALL_EINT1 */
+#define ARIZONA_JD2_RISE_EINT1                   0x0001  /* JD2_RISE_EINT1 */
+#define ARIZONA_JD2_RISE_EINT1_MASK              0x0001  /* JD2_RISE_EINT1 */
+#define ARIZONA_JD2_RISE_EINT1_SHIFT                  0  /* JD2_RISE_EINT1 */
+#define ARIZONA_JD2_RISE_EINT1_WIDTH                  1  /* JD2_RISE_EINT1 */
+
+/*
+ * R3410 (0xD52) - AOD IRQ2
+ */
+#define ARIZONA_GP5_FALL_EINT2                   0x0020  /* GP5_FALL_EINT2 */
+#define ARIZONA_GP5_FALL_EINT2_MASK              0x0020  /* GP5_FALL_EINT2 */
+#define ARIZONA_GP5_FALL_EINT2_SHIFT                  5  /* GP5_FALL_EINT2 */
+#define ARIZONA_GP5_FALL_EINT2_WIDTH                  1  /* GP5_FALL_EINT2 */
+#define ARIZONA_GP5_RISE_EINT2                   0x0010  /* GP5_RISE_EINT2 */
+#define ARIZONA_GP5_RISE_EINT2_MASK              0x0010  /* GP5_RISE_EINT2 */
+#define ARIZONA_GP5_RISE_EINT2_SHIFT                  4  /* GP5_RISE_EINT2 */
+#define ARIZONA_GP5_RISE_EINT2_WIDTH                  1  /* GP5_RISE_EINT2 */
+#define ARIZONA_JD1_FALL_EINT2                   0x0008  /* JD1_FALL_EINT2 */
+#define ARIZONA_JD1_FALL_EINT2_MASK              0x0008  /* JD1_FALL_EINT2 */
+#define ARIZONA_JD1_FALL_EINT2_SHIFT                  3  /* JD1_FALL_EINT2 */
+#define ARIZONA_JD1_FALL_EINT2_WIDTH                  1  /* JD1_FALL_EINT2 */
+#define ARIZONA_JD1_RISE_EINT2                   0x0004  /* JD1_RISE_EINT2 */
+#define ARIZONA_JD1_RISE_EINT2_MASK              0x0004  /* JD1_RISE_EINT2 */
+#define ARIZONA_JD1_RISE_EINT2_SHIFT                  2  /* JD1_RISE_EINT2 */
+#define ARIZONA_JD1_RISE_EINT2_WIDTH                  1  /* JD1_RISE_EINT2 */
+#define ARIZONA_JD2_FALL_EINT2                   0x0002  /* JD2_FALL_EINT2 */
+#define ARIZONA_JD2_FALL_EINT2_MASK              0x0002  /* JD2_FALL_EINT2 */
+#define ARIZONA_JD2_FALL_EINT2_SHIFT                  1  /* JD2_FALL_EINT2 */
+#define ARIZONA_JD2_FALL_EINT2_WIDTH                  1  /* JD2_FALL_EINT2 */
+#define ARIZONA_JD2_RISE_EINT2                   0x0001  /* JD2_RISE_EINT2 */
+#define ARIZONA_JD2_RISE_EINT2_MASK              0x0001  /* JD2_RISE_EINT2 */
+#define ARIZONA_JD2_RISE_EINT2_SHIFT                  0  /* JD2_RISE_EINT2 */
+#define ARIZONA_JD2_RISE_EINT2_WIDTH                  1  /* JD2_RISE_EINT2 */
+
+/*
+ * R3411 (0xD53) - AOD IRQ Mask IRQ1
+ */
+#define ARIZONA_IM_GP5_FALL_EINT1                0x0020  /* IM_GP5_FALL_EINT1 */
+#define ARIZONA_IM_GP5_FALL_EINT1_MASK           0x0020  /* IM_GP5_FALL_EINT1 */
+#define ARIZONA_IM_GP5_FALL_EINT1_SHIFT               5  /* IM_GP5_FALL_EINT1 */
+#define ARIZONA_IM_GP5_FALL_EINT1_WIDTH               1  /* IM_GP5_FALL_EINT1 */
+#define ARIZONA_IM_GP5_RISE_EINT1                0x0010  /* IM_GP5_RISE_EINT1 */
+#define ARIZONA_IM_GP5_RISE_EINT1_MASK           0x0010  /* IM_GP5_RISE_EINT1 */
+#define ARIZONA_IM_GP5_RISE_EINT1_SHIFT               4  /* IM_GP5_RISE_EINT1 */
+#define ARIZONA_IM_GP5_RISE_EINT1_WIDTH               1  /* IM_GP5_RISE_EINT1 */
+#define ARIZONA_IM_JD1_FALL_EINT1                0x0008  /* IM_JD1_FALL_EINT1 */
+#define ARIZONA_IM_JD1_FALL_EINT1_MASK           0x0008  /* IM_JD1_FALL_EINT1 */
+#define ARIZONA_IM_JD1_FALL_EINT1_SHIFT               3  /* IM_JD1_FALL_EINT1 */
+#define ARIZONA_IM_JD1_FALL_EINT1_WIDTH               1  /* IM_JD1_FALL_EINT1 */
+#define ARIZONA_IM_JD1_RISE_EINT1                0x0004  /* IM_JD1_RISE_EINT1 */
+#define ARIZONA_IM_JD1_RISE_EINT1_MASK           0x0004  /* IM_JD1_RISE_EINT1 */
+#define ARIZONA_IM_JD1_RISE_EINT1_SHIFT               2  /* IM_JD1_RISE_EINT1 */
+#define ARIZONA_IM_JD1_RISE_EINT1_WIDTH               1  /* IM_JD1_RISE_EINT1 */
+#define ARIZONA_IM_JD2_FALL_EINT1                0x0002  /* IM_JD2_FALL_EINT1 */
+#define ARIZONA_IM_JD2_FALL_EINT1_MASK           0x0002  /* IM_JD2_FALL_EINT1 */
+#define ARIZONA_IM_JD2_FALL_EINT1_SHIFT               1  /* IM_JD2_FALL_EINT1 */
+#define ARIZONA_IM_JD2_FALL_EINT1_WIDTH               1  /* IM_JD2_FALL_EINT1 */
+#define ARIZONA_IM_JD2_RISE_EINT1                0x0001  /* IM_JD2_RISE_EINT1 */
+#define ARIZONA_IM_JD2_RISE_EINT1_MASK           0x0001  /* IM_JD2_RISE_EINT1 */
+#define ARIZONA_IM_JD2_RISE_EINT1_SHIFT               0  /* IM_JD2_RISE_EINT1 */
+#define ARIZONA_IM_JD2_RISE_EINT1_WIDTH               1  /* IM_JD2_RISE_EINT1 */
+
+/*
+ * R3412 (0xD54) - AOD IRQ Mask IRQ2
+ */
+#define ARIZONA_IM_GP5_FALL_EINT2                0x0020  /* IM_GP5_FALL_EINT2 */
+#define ARIZONA_IM_GP5_FALL_EINT2_MASK           0x0020  /* IM_GP5_FALL_EINT2 */
+#define ARIZONA_IM_GP5_FALL_EINT2_SHIFT               5  /* IM_GP5_FALL_EINT2 */
+#define ARIZONA_IM_GP5_FALL_EINT2_WIDTH               1  /* IM_GP5_FALL_EINT2 */
+#define ARIZONA_IM_GP5_RISE_EINT2                0x0010  /* IM_GP5_RISE_EINT2 */
+#define ARIZONA_IM_GP5_RISE_EINT2_MASK           0x0010  /* IM_GP5_RISE_EINT2 */
+#define ARIZONA_IM_GP5_RISE_EINT2_SHIFT               4  /* IM_GP5_RISE_EINT2 */
+#define ARIZONA_IM_GP5_RISE_EINT2_WIDTH               1  /* IM_GP5_RISE_EINT2 */
+#define ARIZONA_IM_JD1_FALL_EINT2                0x0008  /* IM_JD1_FALL_EINT2 */
+#define ARIZONA_IM_JD1_FALL_EINT2_MASK           0x0008  /* IM_JD1_FALL_EINT2 */
+#define ARIZONA_IM_JD1_FALL_EINT2_SHIFT               3  /* IM_JD1_FALL_EINT2 */
+#define ARIZONA_IM_JD1_FALL_EINT2_WIDTH               1  /* IM_JD1_FALL_EINT2 */
+#define ARIZONA_IM_JD1_RISE_EINT2                0x0004  /* IM_JD1_RISE_EINT2 */
+#define ARIZONA_IM_JD1_RISE_EINT2_MASK           0x0004  /* IM_JD1_RISE_EINT2 */
+#define ARIZONA_IM_JD1_RISE_EINT2_SHIFT               2  /* IM_JD1_RISE_EINT2 */
+#define ARIZONA_IM_JD1_RISE_EINT2_WIDTH               1  /* IM_JD1_RISE_EINT2 */
+#define ARIZONA_IM_JD2_FALL_EINT2                0x0002  /* IM_JD2_FALL_EINT2 */
+#define ARIZONA_IM_JD2_FALL_EINT2_MASK           0x0002  /* IM_JD2_FALL_EINT2 */
+#define ARIZONA_IM_JD2_FALL_EINT2_SHIFT               1  /* IM_JD2_FALL_EINT2 */
+#define ARIZONA_IM_JD2_FALL_EINT2_WIDTH               1  /* IM_JD2_FALL_EINT2 */
+#define ARIZONA_IM_JD2_RISE_EINT2                0x0001  /* IM_JD2_RISE_EINT2 */
+#define ARIZONA_IM_JD2_RISE_EINT2_MASK           0x0001  /* IM_JD2_RISE_EINT2 */
+#define ARIZONA_IM_JD2_RISE_EINT2_SHIFT               0  /* IM_JD2_RISE_EINT2 */
+#define ARIZONA_IM_JD2_RISE_EINT2_WIDTH               1  /* IM_JD2_RISE_EINT2 */
+
+/*
+ * R3413 (0xD55) - AOD IRQ Raw Status
+ */
+#define ARIZONA_GP5_STS                          0x0004  /* GP5_STS */
+#define ARIZONA_GP5_STS_MASK                     0x0004  /* GP5_STS */
+#define ARIZONA_GP5_STS_SHIFT                         2  /* GP5_STS */
+#define ARIZONA_GP5_STS_WIDTH                         1  /* GP5_STS */
+#define ARIZONA_JD2_STS                          0x0002  /* JD2_STS */
+#define ARIZONA_JD2_STS_MASK                     0x0002  /* JD2_STS */
+#define ARIZONA_JD2_STS_SHIFT                         1  /* JD2_STS */
+#define ARIZONA_JD2_STS_WIDTH                         1  /* JD2_STS */
+#define ARIZONA_JD1_STS                          0x0001  /* JD1_STS */
+#define ARIZONA_JD1_STS_MASK                     0x0001  /* JD1_STS */
+#define ARIZONA_JD1_STS_SHIFT                         0  /* JD1_STS */
+#define ARIZONA_JD1_STS_WIDTH                         1  /* JD1_STS */
+
+/*
+ * R3414 (0xD56) - Jack detect debounce
+ */
+#define ARIZONA_JD2_DB                           0x0002  /* JD2_DB */
+#define ARIZONA_JD2_DB_MASK                      0x0002  /* JD2_DB */
+#define ARIZONA_JD2_DB_SHIFT                          1  /* JD2_DB */
+#define ARIZONA_JD2_DB_WIDTH                          1  /* JD2_DB */
+#define ARIZONA_JD1_DB                           0x0001  /* JD1_DB */
+#define ARIZONA_JD1_DB_MASK                      0x0001  /* JD1_DB */
+#define ARIZONA_JD1_DB_SHIFT                          0  /* JD1_DB */
+#define ARIZONA_JD1_DB_WIDTH                          1  /* JD1_DB */
+
+/*
+ * R3584 (0xE00) - FX_Ctrl1
+ */
+#define ARIZONA_FX_RATE_MASK                     0x7800  /* FX_RATE - [14:11] */
+#define ARIZONA_FX_RATE_SHIFT                        11  /* FX_RATE - [14:11] */
+#define ARIZONA_FX_RATE_WIDTH                         4  /* FX_RATE - [14:11] */
+
+/*
+ * R3585 (0xE01) - FX_Ctrl2
+ */
+#define ARIZONA_FX_STS_MASK                      0xFFF0  /* FX_STS - [15:4] */
+#define ARIZONA_FX_STS_SHIFT                          4  /* FX_STS - [15:4] */
+#define ARIZONA_FX_STS_WIDTH                         12  /* FX_STS - [15:4] */
+
+/*
+ * R3600 (0xE10) - EQ1_1
+ */
+#define ARIZONA_EQ1_B1_GAIN_MASK                 0xF800  /* EQ1_B1_GAIN - [15:11] */
+#define ARIZONA_EQ1_B1_GAIN_SHIFT                    11  /* EQ1_B1_GAIN - [15:11] */
+#define ARIZONA_EQ1_B1_GAIN_WIDTH                     5  /* EQ1_B1_GAIN - [15:11] */
+#define ARIZONA_EQ1_B2_GAIN_MASK                 0x07C0  /* EQ1_B2_GAIN - [10:6] */
+#define ARIZONA_EQ1_B2_GAIN_SHIFT                     6  /* EQ1_B2_GAIN - [10:6] */
+#define ARIZONA_EQ1_B2_GAIN_WIDTH                     5  /* EQ1_B2_GAIN - [10:6] */
+#define ARIZONA_EQ1_B3_GAIN_MASK                 0x003E  /* EQ1_B3_GAIN - [5:1] */
+#define ARIZONA_EQ1_B3_GAIN_SHIFT                     1  /* EQ1_B3_GAIN - [5:1] */
+#define ARIZONA_EQ1_B3_GAIN_WIDTH                     5  /* EQ1_B3_GAIN - [5:1] */
+#define ARIZONA_EQ1_ENA                          0x0001  /* EQ1_ENA */
+#define ARIZONA_EQ1_ENA_MASK                     0x0001  /* EQ1_ENA */
+#define ARIZONA_EQ1_ENA_SHIFT                         0  /* EQ1_ENA */
+#define ARIZONA_EQ1_ENA_WIDTH                         1  /* EQ1_ENA */
+
+/*
+ * R3601 (0xE11) - EQ1_2
+ */
+#define ARIZONA_EQ1_B4_GAIN_MASK                 0xF800  /* EQ1_B4_GAIN - [15:11] */
+#define ARIZONA_EQ1_B4_GAIN_SHIFT                    11  /* EQ1_B4_GAIN - [15:11] */
+#define ARIZONA_EQ1_B4_GAIN_WIDTH                     5  /* EQ1_B4_GAIN - [15:11] */
+#define ARIZONA_EQ1_B5_GAIN_MASK                 0x07C0  /* EQ1_B5_GAIN - [10:6] */
+#define ARIZONA_EQ1_B5_GAIN_SHIFT                     6  /* EQ1_B5_GAIN - [10:6] */
+#define ARIZONA_EQ1_B5_GAIN_WIDTH                     5  /* EQ1_B5_GAIN - [10:6] */
+#define ARIZONA_EQ1_B1_MODE                      0x0001  /* EQ1_B1_MODE */
+#define ARIZONA_EQ1_B1_MODE_MASK                 0x0001  /* EQ1_B1_MODE */
+#define ARIZONA_EQ1_B1_MODE_SHIFT                     0  /* EQ1_B1_MODE */
+#define ARIZONA_EQ1_B1_MODE_WIDTH                     1  /* EQ1_B1_MODE */
+
+/*
+ * R3602 (0xE12) - EQ1_3
+ */
+#define ARIZONA_EQ1_B1_A_MASK                    0xFFFF  /* EQ1_B1_A - [15:0] */
+#define ARIZONA_EQ1_B1_A_SHIFT                        0  /* EQ1_B1_A - [15:0] */
+#define ARIZONA_EQ1_B1_A_WIDTH                       16  /* EQ1_B1_A - [15:0] */
+
+/*
+ * R3603 (0xE13) - EQ1_4
+ */
+#define ARIZONA_EQ1_B1_B_MASK                    0xFFFF  /* EQ1_B1_B - [15:0] */
+#define ARIZONA_EQ1_B1_B_SHIFT                        0  /* EQ1_B1_B - [15:0] */
+#define ARIZONA_EQ1_B1_B_WIDTH                       16  /* EQ1_B1_B - [15:0] */
+
+/*
+ * R3604 (0xE14) - EQ1_5
+ */
+#define ARIZONA_EQ1_B1_PG_MASK                   0xFFFF  /* EQ1_B1_PG - [15:0] */
+#define ARIZONA_EQ1_B1_PG_SHIFT                       0  /* EQ1_B1_PG - [15:0] */
+#define ARIZONA_EQ1_B1_PG_WIDTH                      16  /* EQ1_B1_PG - [15:0] */
+
+/*
+ * R3605 (0xE15) - EQ1_6
+ */
+#define ARIZONA_EQ1_B2_A_MASK                    0xFFFF  /* EQ1_B2_A - [15:0] */
+#define ARIZONA_EQ1_B2_A_SHIFT                        0  /* EQ1_B2_A - [15:0] */
+#define ARIZONA_EQ1_B2_A_WIDTH                       16  /* EQ1_B2_A - [15:0] */
+
+/*
+ * R3606 (0xE16) - EQ1_7
+ */
+#define ARIZONA_EQ1_B2_B_MASK                    0xFFFF  /* EQ1_B2_B - [15:0] */
+#define ARIZONA_EQ1_B2_B_SHIFT                        0  /* EQ1_B2_B - [15:0] */
+#define ARIZONA_EQ1_B2_B_WIDTH                       16  /* EQ1_B2_B - [15:0] */
+
+/*
+ * R3607 (0xE17) - EQ1_8
+ */
+#define ARIZONA_EQ1_B2_C_MASK                    0xFFFF  /* EQ1_B2_C - [15:0] */
+#define ARIZONA_EQ1_B2_C_SHIFT                        0  /* EQ1_B2_C - [15:0] */
+#define ARIZONA_EQ1_B2_C_WIDTH                       16  /* EQ1_B2_C - [15:0] */
+
+/*
+ * R3608 (0xE18) - EQ1_9
+ */
+#define ARIZONA_EQ1_B2_PG_MASK                   0xFFFF  /* EQ1_B2_PG - [15:0] */
+#define ARIZONA_EQ1_B2_PG_SHIFT                       0  /* EQ1_B2_PG - [15:0] */
+#define ARIZONA_EQ1_B2_PG_WIDTH                      16  /* EQ1_B2_PG - [15:0] */
+
+/*
+ * R3609 (0xE19) - EQ1_10
+ */
+#define ARIZONA_EQ1_B3_A_MASK                    0xFFFF  /* EQ1_B3_A - [15:0] */
+#define ARIZONA_EQ1_B3_A_SHIFT                        0  /* EQ1_B3_A - [15:0] */
+#define ARIZONA_EQ1_B3_A_WIDTH                       16  /* EQ1_B3_A - [15:0] */
+
+/*
+ * R3610 (0xE1A) - EQ1_11
+ */
+#define ARIZONA_EQ1_B3_B_MASK                    0xFFFF  /* EQ1_B3_B - [15:0] */
+#define ARIZONA_EQ1_B3_B_SHIFT                        0  /* EQ1_B3_B - [15:0] */
+#define ARIZONA_EQ1_B3_B_WIDTH                       16  /* EQ1_B3_B - [15:0] */
+
+/*
+ * R3611 (0xE1B) - EQ1_12
+ */
+#define ARIZONA_EQ1_B3_C_MASK                    0xFFFF  /* EQ1_B3_C - [15:0] */
+#define ARIZONA_EQ1_B3_C_SHIFT                        0  /* EQ1_B3_C - [15:0] */
+#define ARIZONA_EQ1_B3_C_WIDTH                       16  /* EQ1_B3_C - [15:0] */
+
+/*
+ * R3612 (0xE1C) - EQ1_13
+ */
+#define ARIZONA_EQ1_B3_PG_MASK                   0xFFFF  /* EQ1_B3_PG - [15:0] */
+#define ARIZONA_EQ1_B3_PG_SHIFT                       0  /* EQ1_B3_PG - [15:0] */
+#define ARIZONA_EQ1_B3_PG_WIDTH                      16  /* EQ1_B3_PG - [15:0] */
+
+/*
+ * R3613 (0xE1D) - EQ1_14
+ */
+#define ARIZONA_EQ1_B4_A_MASK                    0xFFFF  /* EQ1_B4_A - [15:0] */
+#define ARIZONA_EQ1_B4_A_SHIFT                        0  /* EQ1_B4_A - [15:0] */
+#define ARIZONA_EQ1_B4_A_WIDTH                       16  /* EQ1_B4_A - [15:0] */
+
+/*
+ * R3614 (0xE1E) - EQ1_15
+ */
+#define ARIZONA_EQ1_B4_B_MASK                    0xFFFF  /* EQ1_B4_B - [15:0] */
+#define ARIZONA_EQ1_B4_B_SHIFT                        0  /* EQ1_B4_B - [15:0] */
+#define ARIZONA_EQ1_B4_B_WIDTH                       16  /* EQ1_B4_B - [15:0] */
+
+/*
+ * R3615 (0xE1F) - EQ1_16
+ */
+#define ARIZONA_EQ1_B4_C_MASK                    0xFFFF  /* EQ1_B4_C - [15:0] */
+#define ARIZONA_EQ1_B4_C_SHIFT                        0  /* EQ1_B4_C - [15:0] */
+#define ARIZONA_EQ1_B4_C_WIDTH                       16  /* EQ1_B4_C - [15:0] */
+
+/*
+ * R3616 (0xE20) - EQ1_17
+ */
+#define ARIZONA_EQ1_B4_PG_MASK                   0xFFFF  /* EQ1_B4_PG - [15:0] */
+#define ARIZONA_EQ1_B4_PG_SHIFT                       0  /* EQ1_B4_PG - [15:0] */
+#define ARIZONA_EQ1_B4_PG_WIDTH                      16  /* EQ1_B4_PG - [15:0] */
+
+/*
+ * R3617 (0xE21) - EQ1_18
+ */
+#define ARIZONA_EQ1_B5_A_MASK                    0xFFFF  /* EQ1_B5_A - [15:0] */
+#define ARIZONA_EQ1_B5_A_SHIFT                        0  /* EQ1_B5_A - [15:0] */
+#define ARIZONA_EQ1_B5_A_WIDTH                       16  /* EQ1_B5_A - [15:0] */
+
+/*
+ * R3618 (0xE22) - EQ1_19
+ */
+#define ARIZONA_EQ1_B5_B_MASK                    0xFFFF  /* EQ1_B5_B - [15:0] */
+#define ARIZONA_EQ1_B5_B_SHIFT                        0  /* EQ1_B5_B - [15:0] */
+#define ARIZONA_EQ1_B5_B_WIDTH                       16  /* EQ1_B5_B - [15:0] */
+
+/*
+ * R3619 (0xE23) - EQ1_20
+ */
+#define ARIZONA_EQ1_B5_PG_MASK                   0xFFFF  /* EQ1_B5_PG - [15:0] */
+#define ARIZONA_EQ1_B5_PG_SHIFT                       0  /* EQ1_B5_PG - [15:0] */
+#define ARIZONA_EQ1_B5_PG_WIDTH                      16  /* EQ1_B5_PG - [15:0] */
+
+/*
+ * R3620 (0xE24) - EQ1_21
+ */
+#define ARIZONA_EQ1_B1_C_MASK                    0xFFFF  /* EQ1_B1_C - [15:0] */
+#define ARIZONA_EQ1_B1_C_SHIFT                        0  /* EQ1_B1_C - [15:0] */
+#define ARIZONA_EQ1_B1_C_WIDTH                       16  /* EQ1_B1_C - [15:0] */
+
+/*
+ * R3622 (0xE26) - EQ2_1
+ */
+#define ARIZONA_EQ2_B1_GAIN_MASK                 0xF800  /* EQ2_B1_GAIN - [15:11] */
+#define ARIZONA_EQ2_B1_GAIN_SHIFT                    11  /* EQ2_B1_GAIN - [15:11] */
+#define ARIZONA_EQ2_B1_GAIN_WIDTH                     5  /* EQ2_B1_GAIN - [15:11] */
+#define ARIZONA_EQ2_B2_GAIN_MASK                 0x07C0  /* EQ2_B2_GAIN - [10:6] */
+#define ARIZONA_EQ2_B2_GAIN_SHIFT                     6  /* EQ2_B2_GAIN - [10:6] */
+#define ARIZONA_EQ2_B2_GAIN_WIDTH                     5  /* EQ2_B2_GAIN - [10:6] */
+#define ARIZONA_EQ2_B3_GAIN_MASK                 0x003E  /* EQ2_B3_GAIN - [5:1] */
+#define ARIZONA_EQ2_B3_GAIN_SHIFT                     1  /* EQ2_B3_GAIN - [5:1] */
+#define ARIZONA_EQ2_B3_GAIN_WIDTH                     5  /* EQ2_B3_GAIN - [5:1] */
+#define ARIZONA_EQ2_ENA                          0x0001  /* EQ2_ENA */
+#define ARIZONA_EQ2_ENA_MASK                     0x0001  /* EQ2_ENA */
+#define ARIZONA_EQ2_ENA_SHIFT                         0  /* EQ2_ENA */
+#define ARIZONA_EQ2_ENA_WIDTH                         1  /* EQ2_ENA */
+
+/*
+ * R3623 (0xE27) - EQ2_2
+ */
+#define ARIZONA_EQ2_B4_GAIN_MASK                 0xF800  /* EQ2_B4_GAIN - [15:11] */
+#define ARIZONA_EQ2_B4_GAIN_SHIFT                    11  /* EQ2_B4_GAIN - [15:11] */
+#define ARIZONA_EQ2_B4_GAIN_WIDTH                     5  /* EQ2_B4_GAIN - [15:11] */
+#define ARIZONA_EQ2_B5_GAIN_MASK                 0x07C0  /* EQ2_B5_GAIN - [10:6] */
+#define ARIZONA_EQ2_B5_GAIN_SHIFT                     6  /* EQ2_B5_GAIN - [10:6] */
+#define ARIZONA_EQ2_B5_GAIN_WIDTH                     5  /* EQ2_B5_GAIN - [10:6] */
+#define ARIZONA_EQ2_B1_MODE                      0x0001  /* EQ2_B1_MODE */
+#define ARIZONA_EQ2_B1_MODE_MASK                 0x0001  /* EQ2_B1_MODE */
+#define ARIZONA_EQ2_B1_MODE_SHIFT                     0  /* EQ2_B1_MODE */
+#define ARIZONA_EQ2_B1_MODE_WIDTH                     1  /* EQ2_B1_MODE */
+
+/*
+ * R3624 (0xE28) - EQ2_3
+ */
+#define ARIZONA_EQ2_B1_A_MASK                    0xFFFF  /* EQ2_B1_A - [15:0] */
+#define ARIZONA_EQ2_B1_A_SHIFT                        0  /* EQ2_B1_A - [15:0] */
+#define ARIZONA_EQ2_B1_A_WIDTH                       16  /* EQ2_B1_A - [15:0] */
+
+/*
+ * R3625 (0xE29) - EQ2_4
+ */
+#define ARIZONA_EQ2_B1_B_MASK                    0xFFFF  /* EQ2_B1_B - [15:0] */
+#define ARIZONA_EQ2_B1_B_SHIFT                        0  /* EQ2_B1_B - [15:0] */
+#define ARIZONA_EQ2_B1_B_WIDTH                       16  /* EQ2_B1_B - [15:0] */
+
+/*
+ * R3626 (0xE2A) - EQ2_5
+ */
+#define ARIZONA_EQ2_B1_PG_MASK                   0xFFFF  /* EQ2_B1_PG - [15:0] */
+#define ARIZONA_EQ2_B1_PG_SHIFT                       0  /* EQ2_B1_PG - [15:0] */
+#define ARIZONA_EQ2_B1_PG_WIDTH                      16  /* EQ2_B1_PG - [15:0] */
+
+/*
+ * R3627 (0xE2B) - EQ2_6
+ */
+#define ARIZONA_EQ2_B2_A_MASK                    0xFFFF  /* EQ2_B2_A - [15:0] */
+#define ARIZONA_EQ2_B2_A_SHIFT                        0  /* EQ2_B2_A - [15:0] */
+#define ARIZONA_EQ2_B2_A_WIDTH                       16  /* EQ2_B2_A - [15:0] */
+
+/*
+ * R3628 (0xE2C) - EQ2_7
+ */
+#define ARIZONA_EQ2_B2_B_MASK                    0xFFFF  /* EQ2_B2_B - [15:0] */
+#define ARIZONA_EQ2_B2_B_SHIFT                        0  /* EQ2_B2_B - [15:0] */
+#define ARIZONA_EQ2_B2_B_WIDTH                       16  /* EQ2_B2_B - [15:0] */
+
+/*
+ * R3629 (0xE2D) - EQ2_8
+ */
+#define ARIZONA_EQ2_B2_C_MASK                    0xFFFF  /* EQ2_B2_C - [15:0] */
+#define ARIZONA_EQ2_B2_C_SHIFT                        0  /* EQ2_B2_C - [15:0] */
+#define ARIZONA_EQ2_B2_C_WIDTH                       16  /* EQ2_B2_C - [15:0] */
+
+/*
+ * R3630 (0xE2E) - EQ2_9
+ */
+#define ARIZONA_EQ2_B2_PG_MASK                   0xFFFF  /* EQ2_B2_PG - [15:0] */
+#define ARIZONA_EQ2_B2_PG_SHIFT                       0  /* EQ2_B2_PG - [15:0] */
+#define ARIZONA_EQ2_B2_PG_WIDTH                      16  /* EQ2_B2_PG - [15:0] */
+
+/*
+ * R3631 (0xE2F) - EQ2_10
+ */
+#define ARIZONA_EQ2_B3_A_MASK                    0xFFFF  /* EQ2_B3_A - [15:0] */
+#define ARIZONA_EQ2_B3_A_SHIFT                        0  /* EQ2_B3_A - [15:0] */
+#define ARIZONA_EQ2_B3_A_WIDTH                       16  /* EQ2_B3_A - [15:0] */
+
+/*
+ * R3632 (0xE30) - EQ2_11
+ */
+#define ARIZONA_EQ2_B3_B_MASK                    0xFFFF  /* EQ2_B3_B - [15:0] */
+#define ARIZONA_EQ2_B3_B_SHIFT                        0  /* EQ2_B3_B - [15:0] */
+#define ARIZONA_EQ2_B3_B_WIDTH                       16  /* EQ2_B3_B - [15:0] */
+
+/*
+ * R3633 (0xE31) - EQ2_12
+ */
+#define ARIZONA_EQ2_B3_C_MASK                    0xFFFF  /* EQ2_B3_C - [15:0] */
+#define ARIZONA_EQ2_B3_C_SHIFT                        0  /* EQ2_B3_C - [15:0] */
+#define ARIZONA_EQ2_B3_C_WIDTH                       16  /* EQ2_B3_C - [15:0] */
+
+/*
+ * R3634 (0xE32) - EQ2_13
+ */
+#define ARIZONA_EQ2_B3_PG_MASK                   0xFFFF  /* EQ2_B3_PG - [15:0] */
+#define ARIZONA_EQ2_B3_PG_SHIFT                       0  /* EQ2_B3_PG - [15:0] */
+#define ARIZONA_EQ2_B3_PG_WIDTH                      16  /* EQ2_B3_PG - [15:0] */
+
+/*
+ * R3635 (0xE33) - EQ2_14
+ */
+#define ARIZONA_EQ2_B4_A_MASK                    0xFFFF  /* EQ2_B4_A - [15:0] */
+#define ARIZONA_EQ2_B4_A_SHIFT                        0  /* EQ2_B4_A - [15:0] */
+#define ARIZONA_EQ2_B4_A_WIDTH                       16  /* EQ2_B4_A - [15:0] */
+
+/*
+ * R3636 (0xE34) - EQ2_15
+ */
+#define ARIZONA_EQ2_B4_B_MASK                    0xFFFF  /* EQ2_B4_B - [15:0] */
+#define ARIZONA_EQ2_B4_B_SHIFT                        0  /* EQ2_B4_B - [15:0] */
+#define ARIZONA_EQ2_B4_B_WIDTH                       16  /* EQ2_B4_B - [15:0] */
+
+/*
+ * R3637 (0xE35) - EQ2_16
+ */
+#define ARIZONA_EQ2_B4_C_MASK                    0xFFFF  /* EQ2_B4_C - [15:0] */
+#define ARIZONA_EQ2_B4_C_SHIFT                        0  /* EQ2_B4_C - [15:0] */
+#define ARIZONA_EQ2_B4_C_WIDTH                       16  /* EQ2_B4_C - [15:0] */
+
+/*
+ * R3638 (0xE36) - EQ2_17
+ */
+#define ARIZONA_EQ2_B4_PG_MASK                   0xFFFF  /* EQ2_B4_PG - [15:0] */
+#define ARIZONA_EQ2_B4_PG_SHIFT                       0  /* EQ2_B4_PG - [15:0] */
+#define ARIZONA_EQ2_B4_PG_WIDTH                      16  /* EQ2_B4_PG - [15:0] */
+
+/*
+ * R3639 (0xE37) - EQ2_18
+ */
+#define ARIZONA_EQ2_B5_A_MASK                    0xFFFF  /* EQ2_B5_A - [15:0] */
+#define ARIZONA_EQ2_B5_A_SHIFT                        0  /* EQ2_B5_A - [15:0] */
+#define ARIZONA_EQ2_B5_A_WIDTH                       16  /* EQ2_B5_A - [15:0] */
+
+/*
+ * R3640 (0xE38) - EQ2_19
+ */
+#define ARIZONA_EQ2_B5_B_MASK                    0xFFFF  /* EQ2_B5_B - [15:0] */
+#define ARIZONA_EQ2_B5_B_SHIFT                        0  /* EQ2_B5_B - [15:0] */
+#define ARIZONA_EQ2_B5_B_WIDTH                       16  /* EQ2_B5_B - [15:0] */
+
+/*
+ * R3641 (0xE39) - EQ2_20
+ */
+#define ARIZONA_EQ2_B5_PG_MASK                   0xFFFF  /* EQ2_B5_PG - [15:0] */
+#define ARIZONA_EQ2_B5_PG_SHIFT                       0  /* EQ2_B5_PG - [15:0] */
+#define ARIZONA_EQ2_B5_PG_WIDTH                      16  /* EQ2_B5_PG - [15:0] */
+
+/*
+ * R3642 (0xE3A) - EQ2_21
+ */
+#define ARIZONA_EQ2_B1_C_MASK                    0xFFFF  /* EQ2_B1_C - [15:0] */
+#define ARIZONA_EQ2_B1_C_SHIFT                        0  /* EQ2_B1_C - [15:0] */
+#define ARIZONA_EQ2_B1_C_WIDTH                       16  /* EQ2_B1_C - [15:0] */
+
+/*
+ * R3644 (0xE3C) - EQ3_1
+ */
+#define ARIZONA_EQ3_B1_GAIN_MASK                 0xF800  /* EQ3_B1_GAIN - [15:11] */
+#define ARIZONA_EQ3_B1_GAIN_SHIFT                    11  /* EQ3_B1_GAIN - [15:11] */
+#define ARIZONA_EQ3_B1_GAIN_WIDTH                     5  /* EQ3_B1_GAIN - [15:11] */
+#define ARIZONA_EQ3_B2_GAIN_MASK                 0x07C0  /* EQ3_B2_GAIN - [10:6] */
+#define ARIZONA_EQ3_B2_GAIN_SHIFT                     6  /* EQ3_B2_GAIN - [10:6] */
+#define ARIZONA_EQ3_B2_GAIN_WIDTH                     5  /* EQ3_B2_GAIN - [10:6] */
+#define ARIZONA_EQ3_B3_GAIN_MASK                 0x003E  /* EQ3_B3_GAIN - [5:1] */
+#define ARIZONA_EQ3_B3_GAIN_SHIFT                     1  /* EQ3_B3_GAIN - [5:1] */
+#define ARIZONA_EQ3_B3_GAIN_WIDTH                     5  /* EQ3_B3_GAIN - [5:1] */
+#define ARIZONA_EQ3_ENA                          0x0001  /* EQ3_ENA */
+#define ARIZONA_EQ3_ENA_MASK                     0x0001  /* EQ3_ENA */
+#define ARIZONA_EQ3_ENA_SHIFT                         0  /* EQ3_ENA */
+#define ARIZONA_EQ3_ENA_WIDTH                         1  /* EQ3_ENA */
+
+/*
+ * R3645 (0xE3D) - EQ3_2
+ */
+#define ARIZONA_EQ3_B4_GAIN_MASK                 0xF800  /* EQ3_B4_GAIN - [15:11] */
+#define ARIZONA_EQ3_B4_GAIN_SHIFT                    11  /* EQ3_B4_GAIN - [15:11] */
+#define ARIZONA_EQ3_B4_GAIN_WIDTH                     5  /* EQ3_B4_GAIN - [15:11] */
+#define ARIZONA_EQ3_B5_GAIN_MASK                 0x07C0  /* EQ3_B5_GAIN - [10:6] */
+#define ARIZONA_EQ3_B5_GAIN_SHIFT                     6  /* EQ3_B5_GAIN - [10:6] */
+#define ARIZONA_EQ3_B5_GAIN_WIDTH                     5  /* EQ3_B5_GAIN - [10:6] */
+#define ARIZONA_EQ3_B1_MODE                      0x0001  /* EQ3_B1_MODE */
+#define ARIZONA_EQ3_B1_MODE_MASK                 0x0001  /* EQ3_B1_MODE */
+#define ARIZONA_EQ3_B1_MODE_SHIFT                     0  /* EQ3_B1_MODE */
+#define ARIZONA_EQ3_B1_MODE_WIDTH                     1  /* EQ3_B1_MODE */
+
+/*
+ * R3646 (0xE3E) - EQ3_3
+ */
+#define ARIZONA_EQ3_B1_A_MASK                    0xFFFF  /* EQ3_B1_A - [15:0] */
+#define ARIZONA_EQ3_B1_A_SHIFT                        0  /* EQ3_B1_A - [15:0] */
+#define ARIZONA_EQ3_B1_A_WIDTH                       16  /* EQ3_B1_A - [15:0] */
+
+/*
+ * R3647 (0xE3F) - EQ3_4
+ */
+#define ARIZONA_EQ3_B1_B_MASK                    0xFFFF  /* EQ3_B1_B - [15:0] */
+#define ARIZONA_EQ3_B1_B_SHIFT                        0  /* EQ3_B1_B - [15:0] */
+#define ARIZONA_EQ3_B1_B_WIDTH                       16  /* EQ3_B1_B - [15:0] */
+
+/*
+ * R3648 (0xE40) - EQ3_5
+ */
+#define ARIZONA_EQ3_B1_PG_MASK                   0xFFFF  /* EQ3_B1_PG - [15:0] */
+#define ARIZONA_EQ3_B1_PG_SHIFT                       0  /* EQ3_B1_PG - [15:0] */
+#define ARIZONA_EQ3_B1_PG_WIDTH                      16  /* EQ3_B1_PG - [15:0] */
+
+/*
+ * R3649 (0xE41) - EQ3_6
+ */
+#define ARIZONA_EQ3_B2_A_MASK                    0xFFFF  /* EQ3_B2_A - [15:0] */
+#define ARIZONA_EQ3_B2_A_SHIFT                        0  /* EQ3_B2_A - [15:0] */
+#define ARIZONA_EQ3_B2_A_WIDTH                       16  /* EQ3_B2_A - [15:0] */
+
+/*
+ * R3650 (0xE42) - EQ3_7
+ */
+#define ARIZONA_EQ3_B2_B_MASK                    0xFFFF  /* EQ3_B2_B - [15:0] */
+#define ARIZONA_EQ3_B2_B_SHIFT                        0  /* EQ3_B2_B - [15:0] */
+#define ARIZONA_EQ3_B2_B_WIDTH                       16  /* EQ3_B2_B - [15:0] */
+
+/*
+ * R3651 (0xE43) - EQ3_8
+ */
+#define ARIZONA_EQ3_B2_C_MASK                    0xFFFF  /* EQ3_B2_C - [15:0] */
+#define ARIZONA_EQ3_B2_C_SHIFT                        0  /* EQ3_B2_C - [15:0] */
+#define ARIZONA_EQ3_B2_C_WIDTH                       16  /* EQ3_B2_C - [15:0] */
+
+/*
+ * R3652 (0xE44) - EQ3_9
+ */
+#define ARIZONA_EQ3_B2_PG_MASK                   0xFFFF  /* EQ3_B2_PG - [15:0] */
+#define ARIZONA_EQ3_B2_PG_SHIFT                       0  /* EQ3_B2_PG - [15:0] */
+#define ARIZONA_EQ3_B2_PG_WIDTH                      16  /* EQ3_B2_PG - [15:0] */
+
+/*
+ * R3653 (0xE45) - EQ3_10
+ */
+#define ARIZONA_EQ3_B3_A_MASK                    0xFFFF  /* EQ3_B3_A - [15:0] */
+#define ARIZONA_EQ3_B3_A_SHIFT                        0  /* EQ3_B3_A - [15:0] */
+#define ARIZONA_EQ3_B3_A_WIDTH                       16  /* EQ3_B3_A - [15:0] */
+
+/*
+ * R3654 (0xE46) - EQ3_11
+ */
+#define ARIZONA_EQ3_B3_B_MASK                    0xFFFF  /* EQ3_B3_B - [15:0] */
+#define ARIZONA_EQ3_B3_B_SHIFT                        0  /* EQ3_B3_B - [15:0] */
+#define ARIZONA_EQ3_B3_B_WIDTH                       16  /* EQ3_B3_B - [15:0] */
+
+/*
+ * R3655 (0xE47) - EQ3_12
+ */
+#define ARIZONA_EQ3_B3_C_MASK                    0xFFFF  /* EQ3_B3_C - [15:0] */
+#define ARIZONA_EQ3_B3_C_SHIFT                        0  /* EQ3_B3_C - [15:0] */
+#define ARIZONA_EQ3_B3_C_WIDTH                       16  /* EQ3_B3_C - [15:0] */
+
+/*
+ * R3656 (0xE48) - EQ3_13
+ */
+#define ARIZONA_EQ3_B3_PG_MASK                   0xFFFF  /* EQ3_B3_PG - [15:0] */
+#define ARIZONA_EQ3_B3_PG_SHIFT                       0  /* EQ3_B3_PG - [15:0] */
+#define ARIZONA_EQ3_B3_PG_WIDTH                      16  /* EQ3_B3_PG - [15:0] */
+
+/*
+ * R3657 (0xE49) - EQ3_14
+ */
+#define ARIZONA_EQ3_B4_A_MASK                    0xFFFF  /* EQ3_B4_A - [15:0] */
+#define ARIZONA_EQ3_B4_A_SHIFT                        0  /* EQ3_B4_A - [15:0] */
+#define ARIZONA_EQ3_B4_A_WIDTH                       16  /* EQ3_B4_A - [15:0] */
+
+/*
+ * R3658 (0xE4A) - EQ3_15
+ */
+#define ARIZONA_EQ3_B4_B_MASK                    0xFFFF  /* EQ3_B4_B - [15:0] */
+#define ARIZONA_EQ3_B4_B_SHIFT                        0  /* EQ3_B4_B - [15:0] */
+#define ARIZONA_EQ3_B4_B_WIDTH                       16  /* EQ3_B4_B - [15:0] */
+
+/*
+ * R3659 (0xE4B) - EQ3_16
+ */
+#define ARIZONA_EQ3_B4_C_MASK                    0xFFFF  /* EQ3_B4_C - [15:0] */
+#define ARIZONA_EQ3_B4_C_SHIFT                        0  /* EQ3_B4_C - [15:0] */
+#define ARIZONA_EQ3_B4_C_WIDTH                       16  /* EQ3_B4_C - [15:0] */
+
+/*
+ * R3660 (0xE4C) - EQ3_17
+ */
+#define ARIZONA_EQ3_B4_PG_MASK                   0xFFFF  /* EQ3_B4_PG - [15:0] */
+#define ARIZONA_EQ3_B4_PG_SHIFT                       0  /* EQ3_B4_PG - [15:0] */
+#define ARIZONA_EQ3_B4_PG_WIDTH                      16  /* EQ3_B4_PG - [15:0] */
+
+/*
+ * R3661 (0xE4D) - EQ3_18
+ */
+#define ARIZONA_EQ3_B5_A_MASK                    0xFFFF  /* EQ3_B5_A - [15:0] */
+#define ARIZONA_EQ3_B5_A_SHIFT                        0  /* EQ3_B5_A - [15:0] */
+#define ARIZONA_EQ3_B5_A_WIDTH                       16  /* EQ3_B5_A - [15:0] */
+
+/*
+ * R3662 (0xE4E) - EQ3_19
+ */
+#define ARIZONA_EQ3_B5_B_MASK                    0xFFFF  /* EQ3_B5_B - [15:0] */
+#define ARIZONA_EQ3_B5_B_SHIFT                        0  /* EQ3_B5_B - [15:0] */
+#define ARIZONA_EQ3_B5_B_WIDTH                       16  /* EQ3_B5_B - [15:0] */
+
+/*
+ * R3663 (0xE4F) - EQ3_20
+ */
+#define ARIZONA_EQ3_B5_PG_MASK                   0xFFFF  /* EQ3_B5_PG - [15:0] */
+#define ARIZONA_EQ3_B5_PG_SHIFT                       0  /* EQ3_B5_PG - [15:0] */
+#define ARIZONA_EQ3_B5_PG_WIDTH                      16  /* EQ3_B5_PG - [15:0] */
+
+/*
+ * R3664 (0xE50) - EQ3_21
+ */
+#define ARIZONA_EQ3_B1_C_MASK                    0xFFFF  /* EQ3_B1_C - [15:0] */
+#define ARIZONA_EQ3_B1_C_SHIFT                        0  /* EQ3_B1_C - [15:0] */
+#define ARIZONA_EQ3_B1_C_WIDTH                       16  /* EQ3_B1_C - [15:0] */
+
+/*
+ * R3666 (0xE52) - EQ4_1
+ */
+#define ARIZONA_EQ4_B1_GAIN_MASK                 0xF800  /* EQ4_B1_GAIN - [15:11] */
+#define ARIZONA_EQ4_B1_GAIN_SHIFT                    11  /* EQ4_B1_GAIN - [15:11] */
+#define ARIZONA_EQ4_B1_GAIN_WIDTH                     5  /* EQ4_B1_GAIN - [15:11] */
+#define ARIZONA_EQ4_B2_GAIN_MASK                 0x07C0  /* EQ4_B2_GAIN - [10:6] */
+#define ARIZONA_EQ4_B2_GAIN_SHIFT                     6  /* EQ4_B2_GAIN - [10:6] */
+#define ARIZONA_EQ4_B2_GAIN_WIDTH                     5  /* EQ4_B2_GAIN - [10:6] */
+#define ARIZONA_EQ4_B3_GAIN_MASK                 0x003E  /* EQ4_B3_GAIN - [5:1] */
+#define ARIZONA_EQ4_B3_GAIN_SHIFT                     1  /* EQ4_B3_GAIN - [5:1] */
+#define ARIZONA_EQ4_B3_GAIN_WIDTH                     5  /* EQ4_B3_GAIN - [5:1] */
+#define ARIZONA_EQ4_ENA                          0x0001  /* EQ4_ENA */
+#define ARIZONA_EQ4_ENA_MASK                     0x0001  /* EQ4_ENA */
+#define ARIZONA_EQ4_ENA_SHIFT                         0  /* EQ4_ENA */
+#define ARIZONA_EQ4_ENA_WIDTH                         1  /* EQ4_ENA */
+
+/*
+ * R3667 (0xE53) - EQ4_2
+ */
+#define ARIZONA_EQ4_B4_GAIN_MASK                 0xF800  /* EQ4_B4_GAIN - [15:11] */
+#define ARIZONA_EQ4_B4_GAIN_SHIFT                    11  /* EQ4_B4_GAIN - [15:11] */
+#define ARIZONA_EQ4_B4_GAIN_WIDTH                     5  /* EQ4_B4_GAIN - [15:11] */
+#define ARIZONA_EQ4_B5_GAIN_MASK                 0x07C0  /* EQ4_B5_GAIN - [10:6] */
+#define ARIZONA_EQ4_B5_GAIN_SHIFT                     6  /* EQ4_B5_GAIN - [10:6] */
+#define ARIZONA_EQ4_B5_GAIN_WIDTH                     5  /* EQ4_B5_GAIN - [10:6] */
+#define ARIZONA_EQ4_B1_MODE                      0x0001  /* EQ4_B1_MODE */
+#define ARIZONA_EQ4_B1_MODE_MASK                 0x0001  /* EQ4_B1_MODE */
+#define ARIZONA_EQ4_B1_MODE_SHIFT                     0  /* EQ4_B1_MODE */
+#define ARIZONA_EQ4_B1_MODE_WIDTH                     1  /* EQ4_B1_MODE */
+
+/*
+ * R3668 (0xE54) - EQ4_3
+ */
+#define ARIZONA_EQ4_B1_A_MASK                    0xFFFF  /* EQ4_B1_A - [15:0] */
+#define ARIZONA_EQ4_B1_A_SHIFT                        0  /* EQ4_B1_A - [15:0] */
+#define ARIZONA_EQ4_B1_A_WIDTH                       16  /* EQ4_B1_A - [15:0] */
+
+/*
+ * R3669 (0xE55) - EQ4_4
+ */
+#define ARIZONA_EQ4_B1_B_MASK                    0xFFFF  /* EQ4_B1_B - [15:0] */
+#define ARIZONA_EQ4_B1_B_SHIFT                        0  /* EQ4_B1_B - [15:0] */
+#define ARIZONA_EQ4_B1_B_WIDTH                       16  /* EQ4_B1_B - [15:0] */
+
+/*
+ * R3670 (0xE56) - EQ4_5
+ */
+#define ARIZONA_EQ4_B1_PG_MASK                   0xFFFF  /* EQ4_B1_PG - [15:0] */
+#define ARIZONA_EQ4_B1_PG_SHIFT                       0  /* EQ4_B1_PG - [15:0] */
+#define ARIZONA_EQ4_B1_PG_WIDTH                      16  /* EQ4_B1_PG - [15:0] */
+
+/*
+ * R3671 (0xE57) - EQ4_6
+ */
+#define ARIZONA_EQ4_B2_A_MASK                    0xFFFF  /* EQ4_B2_A - [15:0] */
+#define ARIZONA_EQ4_B2_A_SHIFT                        0  /* EQ4_B2_A - [15:0] */
+#define ARIZONA_EQ4_B2_A_WIDTH                       16  /* EQ4_B2_A - [15:0] */
+
+/*
+ * R3672 (0xE58) - EQ4_7
+ */
+#define ARIZONA_EQ4_B2_B_MASK                    0xFFFF  /* EQ4_B2_B - [15:0] */
+#define ARIZONA_EQ4_B2_B_SHIFT                        0  /* EQ4_B2_B - [15:0] */
+#define ARIZONA_EQ4_B2_B_WIDTH                       16  /* EQ4_B2_B - [15:0] */
+
+/*
+ * R3673 (0xE59) - EQ4_8
+ */
+#define ARIZONA_EQ4_B2_C_MASK                    0xFFFF  /* EQ4_B2_C - [15:0] */
+#define ARIZONA_EQ4_B2_C_SHIFT                        0  /* EQ4_B2_C - [15:0] */
+#define ARIZONA_EQ4_B2_C_WIDTH                       16  /* EQ4_B2_C - [15:0] */
+
+/*
+ * R3674 (0xE5A) - EQ4_9
+ */
+#define ARIZONA_EQ4_B2_PG_MASK                   0xFFFF  /* EQ4_B2_PG - [15:0] */
+#define ARIZONA_EQ4_B2_PG_SHIFT                       0  /* EQ4_B2_PG - [15:0] */
+#define ARIZONA_EQ4_B2_PG_WIDTH                      16  /* EQ4_B2_PG - [15:0] */
+
+/*
+ * R3675 (0xE5B) - EQ4_10
+ */
+#define ARIZONA_EQ4_B3_A_MASK                    0xFFFF  /* EQ4_B3_A - [15:0] */
+#define ARIZONA_EQ4_B3_A_SHIFT                        0  /* EQ4_B3_A - [15:0] */
+#define ARIZONA_EQ4_B3_A_WIDTH                       16  /* EQ4_B3_A - [15:0] */
+
+/*
+ * R3676 (0xE5C) - EQ4_11
+ */
+#define ARIZONA_EQ4_B3_B_MASK                    0xFFFF  /* EQ4_B3_B - [15:0] */
+#define ARIZONA_EQ4_B3_B_SHIFT                        0  /* EQ4_B3_B - [15:0] */
+#define ARIZONA_EQ4_B3_B_WIDTH                       16  /* EQ4_B3_B - [15:0] */
+
+/*
+ * R3677 (0xE5D) - EQ4_12
+ */
+#define ARIZONA_EQ4_B3_C_MASK                    0xFFFF  /* EQ4_B3_C - [15:0] */
+#define ARIZONA_EQ4_B3_C_SHIFT                        0  /* EQ4_B3_C - [15:0] */
+#define ARIZONA_EQ4_B3_C_WIDTH                       16  /* EQ4_B3_C - [15:0] */
+
+/*
+ * R3678 (0xE5E) - EQ4_13
+ */
+#define ARIZONA_EQ4_B3_PG_MASK                   0xFFFF  /* EQ4_B3_PG - [15:0] */
+#define ARIZONA_EQ4_B3_PG_SHIFT                       0  /* EQ4_B3_PG - [15:0] */
+#define ARIZONA_EQ4_B3_PG_WIDTH                      16  /* EQ4_B3_PG - [15:0] */
+
+/*
+ * R3679 (0xE5F) - EQ4_14
+ */
+#define ARIZONA_EQ4_B4_A_MASK                    0xFFFF  /* EQ4_B4_A - [15:0] */
+#define ARIZONA_EQ4_B4_A_SHIFT                        0  /* EQ4_B4_A - [15:0] */
+#define ARIZONA_EQ4_B4_A_WIDTH                       16  /* EQ4_B4_A - [15:0] */
+
+/*
+ * R3680 (0xE60) - EQ4_15
+ */
+#define ARIZONA_EQ4_B4_B_MASK                    0xFFFF  /* EQ4_B4_B - [15:0] */
+#define ARIZONA_EQ4_B4_B_SHIFT                        0  /* EQ4_B4_B - [15:0] */
+#define ARIZONA_EQ4_B4_B_WIDTH                       16  /* EQ4_B4_B - [15:0] */
+
+/*
+ * R3681 (0xE61) - EQ4_16
+ */
+#define ARIZONA_EQ4_B4_C_MASK                    0xFFFF  /* EQ4_B4_C - [15:0] */
+#define ARIZONA_EQ4_B4_C_SHIFT                        0  /* EQ4_B4_C - [15:0] */
+#define ARIZONA_EQ4_B4_C_WIDTH                       16  /* EQ4_B4_C - [15:0] */
+
+/*
+ * R3682 (0xE62) - EQ4_17
+ */
+#define ARIZONA_EQ4_B4_PG_MASK                   0xFFFF  /* EQ4_B4_PG - [15:0] */
+#define ARIZONA_EQ4_B4_PG_SHIFT                       0  /* EQ4_B4_PG - [15:0] */
+#define ARIZONA_EQ4_B4_PG_WIDTH                      16  /* EQ4_B4_PG - [15:0] */
+
+/*
+ * R3683 (0xE63) - EQ4_18
+ */
+#define ARIZONA_EQ4_B5_A_MASK                    0xFFFF  /* EQ4_B5_A - [15:0] */
+#define ARIZONA_EQ4_B5_A_SHIFT                        0  /* EQ4_B5_A - [15:0] */
+#define ARIZONA_EQ4_B5_A_WIDTH                       16  /* EQ4_B5_A - [15:0] */
+
+/*
+ * R3684 (0xE64) - EQ4_19
+ */
+#define ARIZONA_EQ4_B5_B_MASK                    0xFFFF  /* EQ4_B5_B - [15:0] */
+#define ARIZONA_EQ4_B5_B_SHIFT                        0  /* EQ4_B5_B - [15:0] */
+#define ARIZONA_EQ4_B5_B_WIDTH                       16  /* EQ4_B5_B - [15:0] */
+
+/*
+ * R3685 (0xE65) - EQ4_20
+ */
+#define ARIZONA_EQ4_B5_PG_MASK                   0xFFFF  /* EQ4_B5_PG - [15:0] */
+#define ARIZONA_EQ4_B5_PG_SHIFT                       0  /* EQ4_B5_PG - [15:0] */
+#define ARIZONA_EQ4_B5_PG_WIDTH                      16  /* EQ4_B5_PG - [15:0] */
+
+/*
+ * R3686 (0xE66) - EQ4_21
+ */
+#define ARIZONA_EQ4_B1_C_MASK                    0xFFFF  /* EQ4_B1_C - [15:0] */
+#define ARIZONA_EQ4_B1_C_SHIFT                        0  /* EQ4_B1_C - [15:0] */
+#define ARIZONA_EQ4_B1_C_WIDTH                       16  /* EQ4_B1_C - [15:0] */
+
+/*
+ * R3712 (0xE80) - DRC1 ctrl1
+ */
+#define ARIZONA_DRC1_SIG_DET_RMS_MASK            0xF800  /* DRC1_SIG_DET_RMS - [15:11] */
+#define ARIZONA_DRC1_SIG_DET_RMS_SHIFT               11  /* DRC1_SIG_DET_RMS - [15:11] */
+#define ARIZONA_DRC1_SIG_DET_RMS_WIDTH                5  /* DRC1_SIG_DET_RMS - [15:11] */
+#define ARIZONA_DRC1_SIG_DET_PK_MASK             0x0600  /* DRC1_SIG_DET_PK - [10:9] */
+#define ARIZONA_DRC1_SIG_DET_PK_SHIFT                 9  /* DRC1_SIG_DET_PK - [10:9] */
+#define ARIZONA_DRC1_SIG_DET_PK_WIDTH                 2  /* DRC1_SIG_DET_PK - [10:9] */
+#define ARIZONA_DRC1_NG_ENA                      0x0100  /* DRC1_NG_ENA */
+#define ARIZONA_DRC1_NG_ENA_MASK                 0x0100  /* DRC1_NG_ENA */
+#define ARIZONA_DRC1_NG_ENA_SHIFT                     8  /* DRC1_NG_ENA */
+#define ARIZONA_DRC1_NG_ENA_WIDTH                     1  /* DRC1_NG_ENA */
+#define ARIZONA_DRC1_SIG_DET_MODE                0x0080  /* DRC1_SIG_DET_MODE */
+#define ARIZONA_DRC1_SIG_DET_MODE_MASK           0x0080  /* DRC1_SIG_DET_MODE */
+#define ARIZONA_DRC1_SIG_DET_MODE_SHIFT               7  /* DRC1_SIG_DET_MODE */
+#define ARIZONA_DRC1_SIG_DET_MODE_WIDTH               1  /* DRC1_SIG_DET_MODE */
+#define ARIZONA_DRC1_SIG_DET                     0x0040  /* DRC1_SIG_DET */
+#define ARIZONA_DRC1_SIG_DET_MASK                0x0040  /* DRC1_SIG_DET */
+#define ARIZONA_DRC1_SIG_DET_SHIFT                    6  /* DRC1_SIG_DET */
+#define ARIZONA_DRC1_SIG_DET_WIDTH                    1  /* DRC1_SIG_DET */
+#define ARIZONA_DRC1_KNEE2_OP_ENA                0x0020  /* DRC1_KNEE2_OP_ENA */
+#define ARIZONA_DRC1_KNEE2_OP_ENA_MASK           0x0020  /* DRC1_KNEE2_OP_ENA */
+#define ARIZONA_DRC1_KNEE2_OP_ENA_SHIFT               5  /* DRC1_KNEE2_OP_ENA */
+#define ARIZONA_DRC1_KNEE2_OP_ENA_WIDTH               1  /* DRC1_KNEE2_OP_ENA */
+#define ARIZONA_DRC1_QR                          0x0010  /* DRC1_QR */
+#define ARIZONA_DRC1_QR_MASK                     0x0010  /* DRC1_QR */
+#define ARIZONA_DRC1_QR_SHIFT                         4  /* DRC1_QR */
+#define ARIZONA_DRC1_QR_WIDTH                         1  /* DRC1_QR */
+#define ARIZONA_DRC1_ANTICLIP                    0x0008  /* DRC1_ANTICLIP */
+#define ARIZONA_DRC1_ANTICLIP_MASK               0x0008  /* DRC1_ANTICLIP */
+#define ARIZONA_DRC1_ANTICLIP_SHIFT                   3  /* DRC1_ANTICLIP */
+#define ARIZONA_DRC1_ANTICLIP_WIDTH                   1  /* DRC1_ANTICLIP */
+#define ARIZONA_DRC1L_ENA                        0x0002  /* DRC1L_ENA */
+#define ARIZONA_DRC1L_ENA_MASK                   0x0002  /* DRC1L_ENA */
+#define ARIZONA_DRC1L_ENA_SHIFT                       1  /* DRC1L_ENA */
+#define ARIZONA_DRC1L_ENA_WIDTH                       1  /* DRC1L_ENA */
+#define ARIZONA_DRC1R_ENA                        0x0001  /* DRC1R_ENA */
+#define ARIZONA_DRC1R_ENA_MASK                   0x0001  /* DRC1R_ENA */
+#define ARIZONA_DRC1R_ENA_SHIFT                       0  /* DRC1R_ENA */
+#define ARIZONA_DRC1R_ENA_WIDTH                       1  /* DRC1R_ENA */
+
+/*
+ * R3713 (0xE81) - DRC1 ctrl2
+ */
+#define ARIZONA_DRC1_ATK_MASK                    0x1E00  /* DRC1_ATK - [12:9] */
+#define ARIZONA_DRC1_ATK_SHIFT                        9  /* DRC1_ATK - [12:9] */
+#define ARIZONA_DRC1_ATK_WIDTH                        4  /* DRC1_ATK - [12:9] */
+#define ARIZONA_DRC1_DCY_MASK                    0x01E0  /* DRC1_DCY - [8:5] */
+#define ARIZONA_DRC1_DCY_SHIFT                        5  /* DRC1_DCY - [8:5] */
+#define ARIZONA_DRC1_DCY_WIDTH                        4  /* DRC1_DCY - [8:5] */
+#define ARIZONA_DRC1_MINGAIN_MASK                0x001C  /* DRC1_MINGAIN - [4:2] */
+#define ARIZONA_DRC1_MINGAIN_SHIFT                    2  /* DRC1_MINGAIN - [4:2] */
+#define ARIZONA_DRC1_MINGAIN_WIDTH                    3  /* DRC1_MINGAIN - [4:2] */
+#define ARIZONA_DRC1_MAXGAIN_MASK                0x0003  /* DRC1_MAXGAIN - [1:0] */
+#define ARIZONA_DRC1_MAXGAIN_SHIFT                    0  /* DRC1_MAXGAIN - [1:0] */
+#define ARIZONA_DRC1_MAXGAIN_WIDTH                    2  /* DRC1_MAXGAIN - [1:0] */
+
+/*
+ * R3714 (0xE82) - DRC1 ctrl3
+ */
+#define ARIZONA_DRC1_NG_MINGAIN_MASK             0xF000  /* DRC1_NG_MINGAIN - [15:12] */
+#define ARIZONA_DRC1_NG_MINGAIN_SHIFT                12  /* DRC1_NG_MINGAIN - [15:12] */
+#define ARIZONA_DRC1_NG_MINGAIN_WIDTH                 4  /* DRC1_NG_MINGAIN - [15:12] */
+#define ARIZONA_DRC1_NG_EXP_MASK                 0x0C00  /* DRC1_NG_EXP - [11:10] */
+#define ARIZONA_DRC1_NG_EXP_SHIFT                    10  /* DRC1_NG_EXP - [11:10] */
+#define ARIZONA_DRC1_NG_EXP_WIDTH                     2  /* DRC1_NG_EXP - [11:10] */
+#define ARIZONA_DRC1_QR_THR_MASK                 0x0300  /* DRC1_QR_THR - [9:8] */
+#define ARIZONA_DRC1_QR_THR_SHIFT                     8  /* DRC1_QR_THR - [9:8] */
+#define ARIZONA_DRC1_QR_THR_WIDTH                     2  /* DRC1_QR_THR - [9:8] */
+#define ARIZONA_DRC1_QR_DCY_MASK                 0x00C0  /* DRC1_QR_DCY - [7:6] */
+#define ARIZONA_DRC1_QR_DCY_SHIFT                     6  /* DRC1_QR_DCY - [7:6] */
+#define ARIZONA_DRC1_QR_DCY_WIDTH                     2  /* DRC1_QR_DCY - [7:6] */
+#define ARIZONA_DRC1_HI_COMP_MASK                0x0038  /* DRC1_HI_COMP - [5:3] */
+#define ARIZONA_DRC1_HI_COMP_SHIFT                    3  /* DRC1_HI_COMP - [5:3] */
+#define ARIZONA_DRC1_HI_COMP_WIDTH                    3  /* DRC1_HI_COMP - [5:3] */
+#define ARIZONA_DRC1_LO_COMP_MASK                0x0007  /* DRC1_LO_COMP - [2:0] */
+#define ARIZONA_DRC1_LO_COMP_SHIFT                    0  /* DRC1_LO_COMP - [2:0] */
+#define ARIZONA_DRC1_LO_COMP_WIDTH                    3  /* DRC1_LO_COMP - [2:0] */
+
+/*
+ * R3715 (0xE83) - DRC1 ctrl4
+ */
+#define ARIZONA_DRC1_KNEE_IP_MASK                0x07E0  /* DRC1_KNEE_IP - [10:5] */
+#define ARIZONA_DRC1_KNEE_IP_SHIFT                    5  /* DRC1_KNEE_IP - [10:5] */
+#define ARIZONA_DRC1_KNEE_IP_WIDTH                    6  /* DRC1_KNEE_IP - [10:5] */
+#define ARIZONA_DRC1_KNEE_OP_MASK                0x001F  /* DRC1_KNEE_OP - [4:0] */
+#define ARIZONA_DRC1_KNEE_OP_SHIFT                    0  /* DRC1_KNEE_OP - [4:0] */
+#define ARIZONA_DRC1_KNEE_OP_WIDTH                    5  /* DRC1_KNEE_OP - [4:0] */
+
+/*
+ * R3716 (0xE84) - DRC1 ctrl5
+ */
+#define ARIZONA_DRC1_KNEE2_IP_MASK               0x03E0  /* DRC1_KNEE2_IP - [9:5] */
+#define ARIZONA_DRC1_KNEE2_IP_SHIFT                   5  /* DRC1_KNEE2_IP - [9:5] */
+#define ARIZONA_DRC1_KNEE2_IP_WIDTH                   5  /* DRC1_KNEE2_IP - [9:5] */
+#define ARIZONA_DRC1_KNEE2_OP_MASK               0x001F  /* DRC1_KNEE2_OP - [4:0] */
+#define ARIZONA_DRC1_KNEE2_OP_SHIFT                   0  /* DRC1_KNEE2_OP - [4:0] */
+#define ARIZONA_DRC1_KNEE2_OP_WIDTH                   5  /* DRC1_KNEE2_OP - [4:0] */
+
+/*
+ * R3721 (0xE89) - DRC2 ctrl1
+ */
+#define ARIZONA_DRC2_SIG_DET_RMS_MASK            0xF800  /* DRC2_SIG_DET_RMS - [15:11] */
+#define ARIZONA_DRC2_SIG_DET_RMS_SHIFT               11  /* DRC2_SIG_DET_RMS - [15:11] */
+#define ARIZONA_DRC2_SIG_DET_RMS_WIDTH                5  /* DRC2_SIG_DET_RMS - [15:11] */
+#define ARIZONA_DRC2_SIG_DET_PK_MASK             0x0600  /* DRC2_SIG_DET_PK - [10:9] */
+#define ARIZONA_DRC2_SIG_DET_PK_SHIFT                 9  /* DRC2_SIG_DET_PK - [10:9] */
+#define ARIZONA_DRC2_SIG_DET_PK_WIDTH                 2  /* DRC2_SIG_DET_PK - [10:9] */
+#define ARIZONA_DRC2_NG_ENA                      0x0100  /* DRC2_NG_ENA */
+#define ARIZONA_DRC2_NG_ENA_MASK                 0x0100  /* DRC2_NG_ENA */
+#define ARIZONA_DRC2_NG_ENA_SHIFT                     8  /* DRC2_NG_ENA */
+#define ARIZONA_DRC2_NG_ENA_WIDTH                     1  /* DRC2_NG_ENA */
+#define ARIZONA_DRC2_SIG_DET_MODE                0x0080  /* DRC2_SIG_DET_MODE */
+#define ARIZONA_DRC2_SIG_DET_MODE_MASK           0x0080  /* DRC2_SIG_DET_MODE */
+#define ARIZONA_DRC2_SIG_DET_MODE_SHIFT               7  /* DRC2_SIG_DET_MODE */
+#define ARIZONA_DRC2_SIG_DET_MODE_WIDTH               1  /* DRC2_SIG_DET_MODE */
+#define ARIZONA_DRC2_SIG_DET                     0x0040  /* DRC2_SIG_DET */
+#define ARIZONA_DRC2_SIG_DET_MASK                0x0040  /* DRC2_SIG_DET */
+#define ARIZONA_DRC2_SIG_DET_SHIFT                    6  /* DRC2_SIG_DET */
+#define ARIZONA_DRC2_SIG_DET_WIDTH                    1  /* DRC2_SIG_DET */
+#define ARIZONA_DRC2_KNEE2_OP_ENA                0x0020  /* DRC2_KNEE2_OP_ENA */
+#define ARIZONA_DRC2_KNEE2_OP_ENA_MASK           0x0020  /* DRC2_KNEE2_OP_ENA */
+#define ARIZONA_DRC2_KNEE2_OP_ENA_SHIFT               5  /* DRC2_KNEE2_OP_ENA */
+#define ARIZONA_DRC2_KNEE2_OP_ENA_WIDTH               1  /* DRC2_KNEE2_OP_ENA */
+#define ARIZONA_DRC2_QR                          0x0010  /* DRC2_QR */
+#define ARIZONA_DRC2_QR_MASK                     0x0010  /* DRC2_QR */
+#define ARIZONA_DRC2_QR_SHIFT                         4  /* DRC2_QR */
+#define ARIZONA_DRC2_QR_WIDTH                         1  /* DRC2_QR */
+#define ARIZONA_DRC2_ANTICLIP                    0x0008  /* DRC2_ANTICLIP */
+#define ARIZONA_DRC2_ANTICLIP_MASK               0x0008  /* DRC2_ANTICLIP */
+#define ARIZONA_DRC2_ANTICLIP_SHIFT                   3  /* DRC2_ANTICLIP */
+#define ARIZONA_DRC2_ANTICLIP_WIDTH                   1  /* DRC2_ANTICLIP */
+#define ARIZONA_DRC2L_ENA                        0x0002  /* DRC2L_ENA */
+#define ARIZONA_DRC2L_ENA_MASK                   0x0002  /* DRC2L_ENA */
+#define ARIZONA_DRC2L_ENA_SHIFT                       1  /* DRC2L_ENA */
+#define ARIZONA_DRC2L_ENA_WIDTH                       1  /* DRC2L_ENA */
+#define ARIZONA_DRC2R_ENA                        0x0001  /* DRC2R_ENA */
+#define ARIZONA_DRC2R_ENA_MASK                   0x0001  /* DRC2R_ENA */
+#define ARIZONA_DRC2R_ENA_SHIFT                       0  /* DRC2R_ENA */
+#define ARIZONA_DRC2R_ENA_WIDTH                       1  /* DRC2R_ENA */
+
+/*
+ * R3722 (0xE8A) - DRC2 ctrl2
+ */
+#define ARIZONA_DRC2_ATK_MASK                    0x1E00  /* DRC2_ATK - [12:9] */
+#define ARIZONA_DRC2_ATK_SHIFT                        9  /* DRC2_ATK - [12:9] */
+#define ARIZONA_DRC2_ATK_WIDTH                        4  /* DRC2_ATK - [12:9] */
+#define ARIZONA_DRC2_DCY_MASK                    0x01E0  /* DRC2_DCY - [8:5] */
+#define ARIZONA_DRC2_DCY_SHIFT                        5  /* DRC2_DCY - [8:5] */
+#define ARIZONA_DRC2_DCY_WIDTH                        4  /* DRC2_DCY - [8:5] */
+#define ARIZONA_DRC2_MINGAIN_MASK                0x001C  /* DRC2_MINGAIN - [4:2] */
+#define ARIZONA_DRC2_MINGAIN_SHIFT                    2  /* DRC2_MINGAIN - [4:2] */
+#define ARIZONA_DRC2_MINGAIN_WIDTH                    3  /* DRC2_MINGAIN - [4:2] */
+#define ARIZONA_DRC2_MAXGAIN_MASK                0x0003  /* DRC2_MAXGAIN - [1:0] */
+#define ARIZONA_DRC2_MAXGAIN_SHIFT                    0  /* DRC2_MAXGAIN - [1:0] */
+#define ARIZONA_DRC2_MAXGAIN_WIDTH                    2  /* DRC2_MAXGAIN - [1:0] */
+
+/*
+ * R3723 (0xE8B) - DRC2 ctrl3
+ */
+#define ARIZONA_DRC2_NG_MINGAIN_MASK             0xF000  /* DRC2_NG_MINGAIN - [15:12] */
+#define ARIZONA_DRC2_NG_MINGAIN_SHIFT                12  /* DRC2_NG_MINGAIN - [15:12] */
+#define ARIZONA_DRC2_NG_MINGAIN_WIDTH                 4  /* DRC2_NG_MINGAIN - [15:12] */
+#define ARIZONA_DRC2_NG_EXP_MASK                 0x0C00  /* DRC2_NG_EXP - [11:10] */
+#define ARIZONA_DRC2_NG_EXP_SHIFT                    10  /* DRC2_NG_EXP - [11:10] */
+#define ARIZONA_DRC2_NG_EXP_WIDTH                     2  /* DRC2_NG_EXP - [11:10] */
+#define ARIZONA_DRC2_QR_THR_MASK                 0x0300  /* DRC2_QR_THR - [9:8] */
+#define ARIZONA_DRC2_QR_THR_SHIFT                     8  /* DRC2_QR_THR - [9:8] */
+#define ARIZONA_DRC2_QR_THR_WIDTH                     2  /* DRC2_QR_THR - [9:8] */
+#define ARIZONA_DRC2_QR_DCY_MASK                 0x00C0  /* DRC2_QR_DCY - [7:6] */
+#define ARIZONA_DRC2_QR_DCY_SHIFT                     6  /* DRC2_QR_DCY - [7:6] */
+#define ARIZONA_DRC2_QR_DCY_WIDTH                     2  /* DRC2_QR_DCY - [7:6] */
+#define ARIZONA_DRC2_HI_COMP_MASK                0x0038  /* DRC2_HI_COMP - [5:3] */
+#define ARIZONA_DRC2_HI_COMP_SHIFT                    3  /* DRC2_HI_COMP - [5:3] */
+#define ARIZONA_DRC2_HI_COMP_WIDTH                    3  /* DRC2_HI_COMP - [5:3] */
+#define ARIZONA_DRC2_LO_COMP_MASK                0x0007  /* DRC2_LO_COMP - [2:0] */
+#define ARIZONA_DRC2_LO_COMP_SHIFT                    0  /* DRC2_LO_COMP - [2:0] */
+#define ARIZONA_DRC2_LO_COMP_WIDTH                    3  /* DRC2_LO_COMP - [2:0] */
+
+/*
+ * R3724 (0xE8C) - DRC2 ctrl4
+ */
+#define ARIZONA_DRC2_KNEE_IP_MASK                0x07E0  /* DRC2_KNEE_IP - [10:5] */
+#define ARIZONA_DRC2_KNEE_IP_SHIFT                    5  /* DRC2_KNEE_IP - [10:5] */
+#define ARIZONA_DRC2_KNEE_IP_WIDTH                    6  /* DRC2_KNEE_IP - [10:5] */
+#define ARIZONA_DRC2_KNEE_OP_MASK                0x001F  /* DRC2_KNEE_OP - [4:0] */
+#define ARIZONA_DRC2_KNEE_OP_SHIFT                    0  /* DRC2_KNEE_OP - [4:0] */
+#define ARIZONA_DRC2_KNEE_OP_WIDTH                    5  /* DRC2_KNEE_OP - [4:0] */
+
+/*
+ * R3725 (0xE8D) - DRC2 ctrl5
+ */
+#define ARIZONA_DRC2_KNEE2_IP_MASK               0x03E0  /* DRC2_KNEE2_IP - [9:5] */
+#define ARIZONA_DRC2_KNEE2_IP_SHIFT                   5  /* DRC2_KNEE2_IP - [9:5] */
+#define ARIZONA_DRC2_KNEE2_IP_WIDTH                   5  /* DRC2_KNEE2_IP - [9:5] */
+#define ARIZONA_DRC2_KNEE2_OP_MASK               0x001F  /* DRC2_KNEE2_OP - [4:0] */
+#define ARIZONA_DRC2_KNEE2_OP_SHIFT                   0  /* DRC2_KNEE2_OP - [4:0] */
+#define ARIZONA_DRC2_KNEE2_OP_WIDTH                   5  /* DRC2_KNEE2_OP - [4:0] */
+
+/*
+ * R3776 (0xEC0) - HPLPF1_1
+ */
+#define ARIZONA_LHPF1_MODE                       0x0002  /* LHPF1_MODE */
+#define ARIZONA_LHPF1_MODE_MASK                  0x0002  /* LHPF1_MODE */
+#define ARIZONA_LHPF1_MODE_SHIFT                      1  /* LHPF1_MODE */
+#define ARIZONA_LHPF1_MODE_WIDTH                      1  /* LHPF1_MODE */
+#define ARIZONA_LHPF1_ENA                        0x0001  /* LHPF1_ENA */
+#define ARIZONA_LHPF1_ENA_MASK                   0x0001  /* LHPF1_ENA */
+#define ARIZONA_LHPF1_ENA_SHIFT                       0  /* LHPF1_ENA */
+#define ARIZONA_LHPF1_ENA_WIDTH                       1  /* LHPF1_ENA */
+
+/*
+ * R3777 (0xEC1) - HPLPF1_2
+ */
+#define ARIZONA_LHPF1_COEFF_MASK                 0xFFFF  /* LHPF1_COEFF - [15:0] */
+#define ARIZONA_LHPF1_COEFF_SHIFT                     0  /* LHPF1_COEFF - [15:0] */
+#define ARIZONA_LHPF1_COEFF_WIDTH                    16  /* LHPF1_COEFF - [15:0] */
+
+/*
+ * R3780 (0xEC4) - HPLPF2_1
+ */
+#define ARIZONA_LHPF2_MODE                       0x0002  /* LHPF2_MODE */
+#define ARIZONA_LHPF2_MODE_MASK                  0x0002  /* LHPF2_MODE */
+#define ARIZONA_LHPF2_MODE_SHIFT                      1  /* LHPF2_MODE */
+#define ARIZONA_LHPF2_MODE_WIDTH                      1  /* LHPF2_MODE */
+#define ARIZONA_LHPF2_ENA                        0x0001  /* LHPF2_ENA */
+#define ARIZONA_LHPF2_ENA_MASK                   0x0001  /* LHPF2_ENA */
+#define ARIZONA_LHPF2_ENA_SHIFT                       0  /* LHPF2_ENA */
+#define ARIZONA_LHPF2_ENA_WIDTH                       1  /* LHPF2_ENA */
+
+/*
+ * R3781 (0xEC5) - HPLPF2_2
+ */
+#define ARIZONA_LHPF2_COEFF_MASK                 0xFFFF  /* LHPF2_COEFF - [15:0] */
+#define ARIZONA_LHPF2_COEFF_SHIFT                     0  /* LHPF2_COEFF - [15:0] */
+#define ARIZONA_LHPF2_COEFF_WIDTH                    16  /* LHPF2_COEFF - [15:0] */
+
+/*
+ * R3784 (0xEC8) - HPLPF3_1
+ */
+#define ARIZONA_LHPF3_MODE                       0x0002  /* LHPF3_MODE */
+#define ARIZONA_LHPF3_MODE_MASK                  0x0002  /* LHPF3_MODE */
+#define ARIZONA_LHPF3_MODE_SHIFT                      1  /* LHPF3_MODE */
+#define ARIZONA_LHPF3_MODE_WIDTH                      1  /* LHPF3_MODE */
+#define ARIZONA_LHPF3_ENA                        0x0001  /* LHPF3_ENA */
+#define ARIZONA_LHPF3_ENA_MASK                   0x0001  /* LHPF3_ENA */
+#define ARIZONA_LHPF3_ENA_SHIFT                       0  /* LHPF3_ENA */
+#define ARIZONA_LHPF3_ENA_WIDTH                       1  /* LHPF3_ENA */
+
+/*
+ * R3785 (0xEC9) - HPLPF3_2
+ */
+#define ARIZONA_LHPF3_COEFF_MASK                 0xFFFF  /* LHPF3_COEFF - [15:0] */
+#define ARIZONA_LHPF3_COEFF_SHIFT                     0  /* LHPF3_COEFF - [15:0] */
+#define ARIZONA_LHPF3_COEFF_WIDTH                    16  /* LHPF3_COEFF - [15:0] */
+
+/*
+ * R3788 (0xECC) - HPLPF4_1
+ */
+#define ARIZONA_LHPF4_MODE                       0x0002  /* LHPF4_MODE */
+#define ARIZONA_LHPF4_MODE_MASK                  0x0002  /* LHPF4_MODE */
+#define ARIZONA_LHPF4_MODE_SHIFT                      1  /* LHPF4_MODE */
+#define ARIZONA_LHPF4_MODE_WIDTH                      1  /* LHPF4_MODE */
+#define ARIZONA_LHPF4_ENA                        0x0001  /* LHPF4_ENA */
+#define ARIZONA_LHPF4_ENA_MASK                   0x0001  /* LHPF4_ENA */
+#define ARIZONA_LHPF4_ENA_SHIFT                       0  /* LHPF4_ENA */
+#define ARIZONA_LHPF4_ENA_WIDTH                       1  /* LHPF4_ENA */
+
+/*
+ * R3789 (0xECD) - HPLPF4_2
+ */
+#define ARIZONA_LHPF4_COEFF_MASK                 0xFFFF  /* LHPF4_COEFF - [15:0] */
+#define ARIZONA_LHPF4_COEFF_SHIFT                     0  /* LHPF4_COEFF - [15:0] */
+#define ARIZONA_LHPF4_COEFF_WIDTH                    16  /* LHPF4_COEFF - [15:0] */
+
+/*
+ * R3808 (0xEE0) - ASRC_ENABLE
+ */
+#define ARIZONA_ASRC2L_ENA                       0x0008  /* ASRC2L_ENA */
+#define ARIZONA_ASRC2L_ENA_MASK                  0x0008  /* ASRC2L_ENA */
+#define ARIZONA_ASRC2L_ENA_SHIFT                      3  /* ASRC2L_ENA */
+#define ARIZONA_ASRC2L_ENA_WIDTH                      1  /* ASRC2L_ENA */
+#define ARIZONA_ASRC2R_ENA                       0x0004  /* ASRC2R_ENA */
+#define ARIZONA_ASRC2R_ENA_MASK                  0x0004  /* ASRC2R_ENA */
+#define ARIZONA_ASRC2R_ENA_SHIFT                      2  /* ASRC2R_ENA */
+#define ARIZONA_ASRC2R_ENA_WIDTH                      1  /* ASRC2R_ENA */
+#define ARIZONA_ASRC1L_ENA                       0x0002  /* ASRC1L_ENA */
+#define ARIZONA_ASRC1L_ENA_MASK                  0x0002  /* ASRC1L_ENA */
+#define ARIZONA_ASRC1L_ENA_SHIFT                      1  /* ASRC1L_ENA */
+#define ARIZONA_ASRC1L_ENA_WIDTH                      1  /* ASRC1L_ENA */
+#define ARIZONA_ASRC1R_ENA                       0x0001  /* ASRC1R_ENA */
+#define ARIZONA_ASRC1R_ENA_MASK                  0x0001  /* ASRC1R_ENA */
+#define ARIZONA_ASRC1R_ENA_SHIFT                      0  /* ASRC1R_ENA */
+#define ARIZONA_ASRC1R_ENA_WIDTH                      1  /* ASRC1R_ENA */
+
+/*
+ * R3810 (0xEE2) - ASRC_RATE1
+ */
+#define ARIZONA_ASRC_RATE1_MASK                  0x7800  /* ASRC_RATE1 - [14:11] */
+#define ARIZONA_ASRC_RATE1_SHIFT                     11  /* ASRC_RATE1 - [14:11] */
+#define ARIZONA_ASRC_RATE1_WIDTH                      4  /* ASRC_RATE1 - [14:11] */
+
+/*
+ * R3811 (0xEE3) - ASRC_RATE2
+ */
+#define ARIZONA_ASRC_RATE2_MASK                  0x7800  /* ASRC_RATE2 - [14:11] */
+#define ARIZONA_ASRC_RATE2_SHIFT                     11  /* ASRC_RATE2 - [14:11] */
+#define ARIZONA_ASRC_RATE2_WIDTH                      4  /* ASRC_RATE2 - [14:11] */
+
+/*
+ * R3824 (0xEF0) - ISRC 1 CTRL 1
+ */
+#define ARIZONA_ISRC1_FSH_MASK                   0x7800  /* ISRC1_FSH - [14:11] */
+#define ARIZONA_ISRC1_FSH_SHIFT                      11  /* ISRC1_FSH - [14:11] */
+#define ARIZONA_ISRC1_FSH_WIDTH                       4  /* ISRC1_FSH - [14:11] */
+#define ARIZONA_ISRC1_CLK_SEL_MASK               0x0700  /* ISRC1_CLK_SEL - [10:8] */
+#define ARIZONA_ISRC1_CLK_SEL_SHIFT                   8  /* ISRC1_CLK_SEL - [10:8] */
+#define ARIZONA_ISRC1_CLK_SEL_WIDTH                   3  /* ISRC1_CLK_SEL - [10:8] */
+
+/*
+ * R3825 (0xEF1) - ISRC 1 CTRL 2
+ */
+#define ARIZONA_ISRC1_FSL_MASK                   0x7800  /* ISRC1_FSL - [14:11] */
+#define ARIZONA_ISRC1_FSL_SHIFT                      11  /* ISRC1_FSL - [14:11] */
+#define ARIZONA_ISRC1_FSL_WIDTH                       4  /* ISRC1_FSL - [14:11] */
+
+/*
+ * R3826 (0xEF2) - ISRC 1 CTRL 3
+ */
+#define ARIZONA_ISRC1_INT0_ENA                   0x8000  /* ISRC1_INT0_ENA */
+#define ARIZONA_ISRC1_INT0_ENA_MASK              0x8000  /* ISRC1_INT0_ENA */
+#define ARIZONA_ISRC1_INT0_ENA_SHIFT                 15  /* ISRC1_INT0_ENA */
+#define ARIZONA_ISRC1_INT0_ENA_WIDTH                  1  /* ISRC1_INT0_ENA */
+#define ARIZONA_ISRC1_INT1_ENA                   0x4000  /* ISRC1_INT1_ENA */
+#define ARIZONA_ISRC1_INT1_ENA_MASK              0x4000  /* ISRC1_INT1_ENA */
+#define ARIZONA_ISRC1_INT1_ENA_SHIFT                 14  /* ISRC1_INT1_ENA */
+#define ARIZONA_ISRC1_INT1_ENA_WIDTH                  1  /* ISRC1_INT1_ENA */
+#define ARIZONA_ISRC1_INT2_ENA                   0x2000  /* ISRC1_INT2_ENA */
+#define ARIZONA_ISRC1_INT2_ENA_MASK              0x2000  /* ISRC1_INT2_ENA */
+#define ARIZONA_ISRC1_INT2_ENA_SHIFT                 13  /* ISRC1_INT2_ENA */
+#define ARIZONA_ISRC1_INT2_ENA_WIDTH                  1  /* ISRC1_INT2_ENA */
+#define ARIZONA_ISRC1_INT3_ENA                   0x1000  /* ISRC1_INT3_ENA */
+#define ARIZONA_ISRC1_INT3_ENA_MASK              0x1000  /* ISRC1_INT3_ENA */
+#define ARIZONA_ISRC1_INT3_ENA_SHIFT                 12  /* ISRC1_INT3_ENA */
+#define ARIZONA_ISRC1_INT3_ENA_WIDTH                  1  /* ISRC1_INT3_ENA */
+#define ARIZONA_ISRC1_DEC0_ENA                   0x0200  /* ISRC1_DEC0_ENA */
+#define ARIZONA_ISRC1_DEC0_ENA_MASK              0x0200  /* ISRC1_DEC0_ENA */
+#define ARIZONA_ISRC1_DEC0_ENA_SHIFT                  9  /* ISRC1_DEC0_ENA */
+#define ARIZONA_ISRC1_DEC0_ENA_WIDTH                  1  /* ISRC1_DEC0_ENA */
+#define ARIZONA_ISRC1_DEC1_ENA                   0x0100  /* ISRC1_DEC1_ENA */
+#define ARIZONA_ISRC1_DEC1_ENA_MASK              0x0100  /* ISRC1_DEC1_ENA */
+#define ARIZONA_ISRC1_DEC1_ENA_SHIFT                  8  /* ISRC1_DEC1_ENA */
+#define ARIZONA_ISRC1_DEC1_ENA_WIDTH                  1  /* ISRC1_DEC1_ENA */
+#define ARIZONA_ISRC1_DEC2_ENA                   0x0080  /* ISRC1_DEC2_ENA */
+#define ARIZONA_ISRC1_DEC2_ENA_MASK              0x0080  /* ISRC1_DEC2_ENA */
+#define ARIZONA_ISRC1_DEC2_ENA_SHIFT                  7  /* ISRC1_DEC2_ENA */
+#define ARIZONA_ISRC1_DEC2_ENA_WIDTH                  1  /* ISRC1_DEC2_ENA */
+#define ARIZONA_ISRC1_DEC3_ENA                   0x0040  /* ISRC1_DEC3_ENA */
+#define ARIZONA_ISRC1_DEC3_ENA_MASK              0x0040  /* ISRC1_DEC3_ENA */
+#define ARIZONA_ISRC1_DEC3_ENA_SHIFT                  6  /* ISRC1_DEC3_ENA */
+#define ARIZONA_ISRC1_DEC3_ENA_WIDTH                  1  /* ISRC1_DEC3_ENA */
+#define ARIZONA_ISRC1_NOTCH_ENA                  0x0001  /* ISRC1_NOTCH_ENA */
+#define ARIZONA_ISRC1_NOTCH_ENA_MASK             0x0001  /* ISRC1_NOTCH_ENA */
+#define ARIZONA_ISRC1_NOTCH_ENA_SHIFT                 0  /* ISRC1_NOTCH_ENA */
+#define ARIZONA_ISRC1_NOTCH_ENA_WIDTH                 1  /* ISRC1_NOTCH_ENA */
+
+/*
+ * R3827 (0xEF3) - ISRC 2 CTRL 1
+ */
+#define ARIZONA_ISRC2_FSH_MASK                   0x7800  /* ISRC2_FSH - [14:11] */
+#define ARIZONA_ISRC2_FSH_SHIFT                      11  /* ISRC2_FSH - [14:11] */
+#define ARIZONA_ISRC2_FSH_WIDTH                       4  /* ISRC2_FSH - [14:11] */
+#define ARIZONA_ISRC2_CLK_SEL_MASK               0x0700  /* ISRC2_CLK_SEL - [10:8] */
+#define ARIZONA_ISRC2_CLK_SEL_SHIFT                   8  /* ISRC2_CLK_SEL - [10:8] */
+#define ARIZONA_ISRC2_CLK_SEL_WIDTH                   3  /* ISRC2_CLK_SEL - [10:8] */
+
+/*
+ * R3828 (0xEF4) - ISRC 2 CTRL 2
+ */
+#define ARIZONA_ISRC2_FSL_MASK                   0x7800  /* ISRC2_FSL - [14:11] */
+#define ARIZONA_ISRC2_FSL_SHIFT                      11  /* ISRC2_FSL - [14:11] */
+#define ARIZONA_ISRC2_FSL_WIDTH                       4  /* ISRC2_FSL - [14:11] */
+
+/*
+ * R3829 (0xEF5) - ISRC 2 CTRL 3
+ */
+#define ARIZONA_ISRC2_INT0_ENA                   0x8000  /* ISRC2_INT0_ENA */
+#define ARIZONA_ISRC2_INT0_ENA_MASK              0x8000  /* ISRC2_INT0_ENA */
+#define ARIZONA_ISRC2_INT0_ENA_SHIFT                 15  /* ISRC2_INT0_ENA */
+#define ARIZONA_ISRC2_INT0_ENA_WIDTH                  1  /* ISRC2_INT0_ENA */
+#define ARIZONA_ISRC2_INT1_ENA                   0x4000  /* ISRC2_INT1_ENA */
+#define ARIZONA_ISRC2_INT1_ENA_MASK              0x4000  /* ISRC2_INT1_ENA */
+#define ARIZONA_ISRC2_INT1_ENA_SHIFT                 14  /* ISRC2_INT1_ENA */
+#define ARIZONA_ISRC2_INT1_ENA_WIDTH                  1  /* ISRC2_INT1_ENA */
+#define ARIZONA_ISRC2_INT2_ENA                   0x2000  /* ISRC2_INT2_ENA */
+#define ARIZONA_ISRC2_INT2_ENA_MASK              0x2000  /* ISRC2_INT2_ENA */
+#define ARIZONA_ISRC2_INT2_ENA_SHIFT                 13  /* ISRC2_INT2_ENA */
+#define ARIZONA_ISRC2_INT2_ENA_WIDTH                  1  /* ISRC2_INT2_ENA */
+#define ARIZONA_ISRC2_INT3_ENA                   0x1000  /* ISRC2_INT3_ENA */
+#define ARIZONA_ISRC2_INT3_ENA_MASK              0x1000  /* ISRC2_INT3_ENA */
+#define ARIZONA_ISRC2_INT3_ENA_SHIFT                 12  /* ISRC2_INT3_ENA */
+#define ARIZONA_ISRC2_INT3_ENA_WIDTH                  1  /* ISRC2_INT3_ENA */
+#define ARIZONA_ISRC2_DEC0_ENA                   0x0200  /* ISRC2_DEC0_ENA */
+#define ARIZONA_ISRC2_DEC0_ENA_MASK              0x0200  /* ISRC2_DEC0_ENA */
+#define ARIZONA_ISRC2_DEC0_ENA_SHIFT                  9  /* ISRC2_DEC0_ENA */
+#define ARIZONA_ISRC2_DEC0_ENA_WIDTH                  1  /* ISRC2_DEC0_ENA */
+#define ARIZONA_ISRC2_DEC1_ENA                   0x0100  /* ISRC2_DEC1_ENA */
+#define ARIZONA_ISRC2_DEC1_ENA_MASK              0x0100  /* ISRC2_DEC1_ENA */
+#define ARIZONA_ISRC2_DEC1_ENA_SHIFT                  8  /* ISRC2_DEC1_ENA */
+#define ARIZONA_ISRC2_DEC1_ENA_WIDTH                  1  /* ISRC2_DEC1_ENA */
+#define ARIZONA_ISRC2_DEC2_ENA                   0x0080  /* ISRC2_DEC2_ENA */
+#define ARIZONA_ISRC2_DEC2_ENA_MASK              0x0080  /* ISRC2_DEC2_ENA */
+#define ARIZONA_ISRC2_DEC2_ENA_SHIFT                  7  /* ISRC2_DEC2_ENA */
+#define ARIZONA_ISRC2_DEC2_ENA_WIDTH                  1  /* ISRC2_DEC2_ENA */
+#define ARIZONA_ISRC2_DEC3_ENA                   0x0040  /* ISRC2_DEC3_ENA */
+#define ARIZONA_ISRC2_DEC3_ENA_MASK              0x0040  /* ISRC2_DEC3_ENA */
+#define ARIZONA_ISRC2_DEC3_ENA_SHIFT                  6  /* ISRC2_DEC3_ENA */
+#define ARIZONA_ISRC2_DEC3_ENA_WIDTH                  1  /* ISRC2_DEC3_ENA */
+#define ARIZONA_ISRC2_NOTCH_ENA                  0x0001  /* ISRC2_NOTCH_ENA */
+#define ARIZONA_ISRC2_NOTCH_ENA_MASK             0x0001  /* ISRC2_NOTCH_ENA */
+#define ARIZONA_ISRC2_NOTCH_ENA_SHIFT                 0  /* ISRC2_NOTCH_ENA */
+#define ARIZONA_ISRC2_NOTCH_ENA_WIDTH                 1  /* ISRC2_NOTCH_ENA */
+
+/*
+ * R3830 (0xEF6) - ISRC 3 CTRL 1
+ */
+#define ARIZONA_ISRC3_FSH_MASK                   0x7800  /* ISRC3_FSH - [14:11] */
+#define ARIZONA_ISRC3_FSH_SHIFT                      11  /* ISRC3_FSH - [14:11] */
+#define ARIZONA_ISRC3_FSH_WIDTH                       4  /* ISRC3_FSH - [14:11] */
+#define ARIZONA_ISRC3_CLK_SEL_MASK               0x0700  /* ISRC3_CLK_SEL - [10:8] */
+#define ARIZONA_ISRC3_CLK_SEL_SHIFT                   8  /* ISRC3_CLK_SEL - [10:8] */
+#define ARIZONA_ISRC3_CLK_SEL_WIDTH                   3  /* ISRC3_CLK_SEL - [10:8] */
+
+/*
+ * R3831 (0xEF7) - ISRC 3 CTRL 2
+ */
+#define ARIZONA_ISRC3_FSL_MASK                   0x7800  /* ISRC3_FSL - [14:11] */
+#define ARIZONA_ISRC3_FSL_SHIFT                      11  /* ISRC3_FSL - [14:11] */
+#define ARIZONA_ISRC3_FSL_WIDTH                       4  /* ISRC3_FSL - [14:11] */
+
+/*
+ * R3832 (0xEF8) - ISRC 3 CTRL 3
+ */
+#define ARIZONA_ISRC3_INT0_ENA                   0x8000  /* ISRC3_INT0_ENA */
+#define ARIZONA_ISRC3_INT0_ENA_MASK              0x8000  /* ISRC3_INT0_ENA */
+#define ARIZONA_ISRC3_INT0_ENA_SHIFT                 15  /* ISRC3_INT0_ENA */
+#define ARIZONA_ISRC3_INT0_ENA_WIDTH                  1  /* ISRC3_INT0_ENA */
+#define ARIZONA_ISRC3_INT1_ENA                   0x4000  /* ISRC3_INT1_ENA */
+#define ARIZONA_ISRC3_INT1_ENA_MASK              0x4000  /* ISRC3_INT1_ENA */
+#define ARIZONA_ISRC3_INT1_ENA_SHIFT                 14  /* ISRC3_INT1_ENA */
+#define ARIZONA_ISRC3_INT1_ENA_WIDTH                  1  /* ISRC3_INT1_ENA */
+#define ARIZONA_ISRC3_INT2_ENA                   0x2000  /* ISRC3_INT2_ENA */
+#define ARIZONA_ISRC3_INT2_ENA_MASK              0x2000  /* ISRC3_INT2_ENA */
+#define ARIZONA_ISRC3_INT2_ENA_SHIFT                 13  /* ISRC3_INT2_ENA */
+#define ARIZONA_ISRC3_INT2_ENA_WIDTH                  1  /* ISRC3_INT2_ENA */
+#define ARIZONA_ISRC3_INT3_ENA                   0x1000  /* ISRC3_INT3_ENA */
+#define ARIZONA_ISRC3_INT3_ENA_MASK              0x1000  /* ISRC3_INT3_ENA */
+#define ARIZONA_ISRC3_INT3_ENA_SHIFT                 12  /* ISRC3_INT3_ENA */
+#define ARIZONA_ISRC3_INT3_ENA_WIDTH                  1  /* ISRC3_INT3_ENA */
+#define ARIZONA_ISRC3_DEC0_ENA                   0x0200  /* ISRC3_DEC0_ENA */
+#define ARIZONA_ISRC3_DEC0_ENA_MASK              0x0200  /* ISRC3_DEC0_ENA */
+#define ARIZONA_ISRC3_DEC0_ENA_SHIFT                  9  /* ISRC3_DEC0_ENA */
+#define ARIZONA_ISRC3_DEC0_ENA_WIDTH                  1  /* ISRC3_DEC0_ENA */
+#define ARIZONA_ISRC3_DEC1_ENA                   0x0100  /* ISRC3_DEC1_ENA */
+#define ARIZONA_ISRC3_DEC1_ENA_MASK              0x0100  /* ISRC3_DEC1_ENA */
+#define ARIZONA_ISRC3_DEC1_ENA_SHIFT                  8  /* ISRC3_DEC1_ENA */
+#define ARIZONA_ISRC3_DEC1_ENA_WIDTH                  1  /* ISRC3_DEC1_ENA */
+#define ARIZONA_ISRC3_DEC2_ENA                   0x0080  /* ISRC3_DEC2_ENA */
+#define ARIZONA_ISRC3_DEC2_ENA_MASK              0x0080  /* ISRC3_DEC2_ENA */
+#define ARIZONA_ISRC3_DEC2_ENA_SHIFT                  7  /* ISRC3_DEC2_ENA */
+#define ARIZONA_ISRC3_DEC2_ENA_WIDTH                  1  /* ISRC3_DEC2_ENA */
+#define ARIZONA_ISRC3_DEC3_ENA                   0x0040  /* ISRC3_DEC3_ENA */
+#define ARIZONA_ISRC3_DEC3_ENA_MASK              0x0040  /* ISRC3_DEC3_ENA */
+#define ARIZONA_ISRC3_DEC3_ENA_SHIFT                  6  /* ISRC3_DEC3_ENA */
+#define ARIZONA_ISRC3_DEC3_ENA_WIDTH                  1  /* ISRC3_DEC3_ENA */
+#define ARIZONA_ISRC3_NOTCH_ENA                  0x0001  /* ISRC3_NOTCH_ENA */
+#define ARIZONA_ISRC3_NOTCH_ENA_MASK             0x0001  /* ISRC3_NOTCH_ENA */
+#define ARIZONA_ISRC3_NOTCH_ENA_SHIFT                 0  /* ISRC3_NOTCH_ENA */
+#define ARIZONA_ISRC3_NOTCH_ENA_WIDTH                 1  /* ISRC3_NOTCH_ENA */
+
+/*
+ * R4352 (0x1100) - DSP1 Control 1
+ */
+#define ARIZONA_DSP1_RATE_MASK                   0x7800  /* DSP1_RATE - [14:11] */
+#define ARIZONA_DSP1_RATE_SHIFT                      11  /* DSP1_RATE - [14:11] */
+#define ARIZONA_DSP1_RATE_WIDTH                       4  /* DSP1_RATE - [14:11] */
+#define ARIZONA_DSP1_MEM_ENA                     0x0010  /* DSP1_MEM_ENA */
+#define ARIZONA_DSP1_MEM_ENA_MASK                0x0010  /* DSP1_MEM_ENA */
+#define ARIZONA_DSP1_MEM_ENA_SHIFT                    4  /* DSP1_MEM_ENA */
+#define ARIZONA_DSP1_MEM_ENA_WIDTH                    1  /* DSP1_MEM_ENA */
+#define ARIZONA_DSP1_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
+#define ARIZONA_DSP1_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
+#define ARIZONA_DSP1_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
+#define ARIZONA_DSP1_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
+#define ARIZONA_DSP1_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
+#define ARIZONA_DSP1_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
+#define ARIZONA_DSP1_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
+#define ARIZONA_DSP1_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
+#define ARIZONA_DSP1_START                       0x0001  /* DSP1_START */
+#define ARIZONA_DSP1_START_MASK                  0x0001  /* DSP1_START */
+#define ARIZONA_DSP1_START_SHIFT                      0  /* DSP1_START */
+#define ARIZONA_DSP1_START_WIDTH                      1  /* DSP1_START */
+
+/*
+ * R4353 (0x1101) - DSP1 Clocking 1
+ */
+#define ARIZONA_DSP1_CLK_SEL_MASK                0x0007  /* DSP1_CLK_SEL - [2:0] */
+#define ARIZONA_DSP1_CLK_SEL_SHIFT                    0  /* DSP1_CLK_SEL - [2:0] */
+#define ARIZONA_DSP1_CLK_SEL_WIDTH                    3  /* DSP1_CLK_SEL - [2:0] */
+
+/*
+ * R4356 (0x1104) - DSP1 Status 1
+ */
+#define ARIZONA_DSP1_RAM_RDY                     0x0001  /* DSP1_RAM_RDY */
+#define ARIZONA_DSP1_RAM_RDY_MASK                0x0001  /* DSP1_RAM_RDY */
+#define ARIZONA_DSP1_RAM_RDY_SHIFT                    0  /* DSP1_RAM_RDY */
+#define ARIZONA_DSP1_RAM_RDY_WIDTH                    1  /* DSP1_RAM_RDY */
+
+/*
+ * R4357 (0x1105) - DSP1 Status 2
+ */
+#define ARIZONA_DSP1_PING_FULL                   0x8000  /* DSP1_PING_FULL */
+#define ARIZONA_DSP1_PING_FULL_MASK              0x8000  /* DSP1_PING_FULL */
+#define ARIZONA_DSP1_PING_FULL_SHIFT                 15  /* DSP1_PING_FULL */
+#define ARIZONA_DSP1_PING_FULL_WIDTH                  1  /* DSP1_PING_FULL */
+#define ARIZONA_DSP1_PONG_FULL                   0x4000  /* DSP1_PONG_FULL */
+#define ARIZONA_DSP1_PONG_FULL_MASK              0x4000  /* DSP1_PONG_FULL */
+#define ARIZONA_DSP1_PONG_FULL_SHIFT                 14  /* DSP1_PONG_FULL */
+#define ARIZONA_DSP1_PONG_FULL_WIDTH                  1  /* DSP1_PONG_FULL */
+#define ARIZONA_DSP1_WDMA_ACTIVE_CHANNELS_MASK   0x00FF  /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */
+#define ARIZONA_DSP1_WDMA_ACTIVE_CHANNELS_SHIFT       0  /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */
+#define ARIZONA_DSP1_WDMA_ACTIVE_CHANNELS_WIDTH       8  /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */
+
+#endif
index 4e76163dd8624dec3dba23b7bdebb32ce8ad2d11..3a8435a8058f1cec9357b3f980efb0eae18f5569 100644 (file)
@@ -36,6 +36,11 @@ struct mfd_cell {
        /* platform data passed to the sub devices drivers */
        void                    *platform_data;
        size_t                  pdata_size;
+       /*
+        * Device Tree compatible string
+        * See: Documentation/devicetree/usage-model.txt Chapter 2.2 for details
+        */
+       const char              *of_compatible;
 
        /*
         * These resources can be specified relative to the parent device.
index b3a43b1263fead2092a4bffd7b87d878e5c0cbb5..b82f6ee66a0bb21849777fddbdffdc3585ca03d1 100644 (file)
@@ -530,7 +530,7 @@ int db8500_prcmu_stop_temp_sense(void);
 int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size);
 int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size);
 
-void prcmu_ac_wake_req(void);
+int prcmu_ac_wake_req(void);
 void prcmu_ac_sleep_req(void);
 void db8500_prcmu_modem_reset(void);
 
@@ -680,7 +680,10 @@ static inline int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
        return -ENOSYS;
 }
 
-static inline void prcmu_ac_wake_req(void) {}
+static inline int prcmu_ac_wake_req(void)
+{
+       return 0;
+}
 
 static inline void prcmu_ac_sleep_req(void) {}
 
index 5a13f93d8f1c97325ceb3d562ab0741c9ededdee..5b90e94399e1b2d8707885ee85be713b9288ab57 100644 (file)
@@ -345,7 +345,7 @@ static inline u16 prcmu_get_reset_code(void)
        return db8500_prcmu_get_reset_code();
 }
 
-void prcmu_ac_wake_req(void);
+int prcmu_ac_wake_req(void);
 void prcmu_ac_sleep_req(void);
 static inline void prcmu_modem_reset(void)
 {
@@ -533,7 +533,10 @@ static inline u16 prcmu_get_reset_code(void)
        return 0;
 }
 
-static inline void prcmu_ac_wake_req(void) {}
+static inline int prcmu_ac_wake_req(void)
+{
+       return 0;
+}
 
 static inline void prcmu_ac_sleep_req(void) {}
 
diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd/max77686-private.h
new file mode 100644 (file)
index 0000000..d327d49
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * max77686.h - Voltage regulator driver for the Maxim 77686
+ *
+ *  Copyright (C) 2012 Samsung Electrnoics
+ *  Chiwoong Byun <woong.byun@samsung.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_MFD_MAX77686_PRIV_H
+#define __LINUX_MFD_MAX77686_PRIV_H
+
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+
+#define MAX77686_REG_INVALID           (0xff)
+
+enum max77686_pmic_reg {
+       MAX77686_REG_DEVICE_ID          = 0x00,
+       MAX77686_REG_INTSRC             = 0x01,
+       MAX77686_REG_INT1               = 0x02,
+       MAX77686_REG_INT2               = 0x03,
+
+       MAX77686_REG_INT1MSK            = 0x04,
+       MAX77686_REG_INT2MSK            = 0x05,
+
+       MAX77686_REG_STATUS1            = 0x06,
+       MAX77686_REG_STATUS2            = 0x07,
+
+       MAX77686_REG_PWRON              = 0x08,
+       MAX77686_REG_ONOFF_DELAY        = 0x09,
+       MAX77686_REG_MRSTB              = 0x0A,
+       /* Reserved: 0x0B-0x0F */
+
+       MAX77686_REG_BUCK1CTRL          = 0x10,
+       MAX77686_REG_BUCK1OUT           = 0x11,
+       MAX77686_REG_BUCK2CTRL1         = 0x12,
+       MAX77686_REG_BUCK234FREQ        = 0x13,
+       MAX77686_REG_BUCK2DVS1          = 0x14,
+       MAX77686_REG_BUCK2DVS2          = 0x15,
+       MAX77686_REG_BUCK2DVS3          = 0x16,
+       MAX77686_REG_BUCK2DVS4          = 0x17,
+       MAX77686_REG_BUCK2DVS5          = 0x18,
+       MAX77686_REG_BUCK2DVS6          = 0x19,
+       MAX77686_REG_BUCK2DVS7          = 0x1A,
+       MAX77686_REG_BUCK2DVS8          = 0x1B,
+       MAX77686_REG_BUCK3CTRL1         = 0x1C,
+       /* Reserved: 0x1D */
+       MAX77686_REG_BUCK3DVS1          = 0x1E,
+       MAX77686_REG_BUCK3DVS2          = 0x1F,
+       MAX77686_REG_BUCK3DVS3          = 0x20,
+       MAX77686_REG_BUCK3DVS4          = 0x21,
+       MAX77686_REG_BUCK3DVS5          = 0x22,
+       MAX77686_REG_BUCK3DVS6          = 0x23,
+       MAX77686_REG_BUCK3DVS7          = 0x24,
+       MAX77686_REG_BUCK3DVS8          = 0x25,
+       MAX77686_REG_BUCK4CTRL1         = 0x26,
+       /* Reserved: 0x27 */
+       MAX77686_REG_BUCK4DVS1          = 0x28,
+       MAX77686_REG_BUCK4DVS2          = 0x29,
+       MAX77686_REG_BUCK4DVS3          = 0x2A,
+       MAX77686_REG_BUCK4DVS4          = 0x2B,
+       MAX77686_REG_BUCK4DVS5          = 0x2C,
+       MAX77686_REG_BUCK4DVS6          = 0x2D,
+       MAX77686_REG_BUCK4DVS7          = 0x2E,
+       MAX77686_REG_BUCK4DVS8          = 0x2F,
+       MAX77686_REG_BUCK5CTRL          = 0x30,
+       MAX77686_REG_BUCK5OUT           = 0x31,
+       MAX77686_REG_BUCK6CTRL          = 0x32,
+       MAX77686_REG_BUCK6OUT           = 0x33,
+       MAX77686_REG_BUCK7CTRL          = 0x34,
+       MAX77686_REG_BUCK7OUT           = 0x35,
+       MAX77686_REG_BUCK8CTRL          = 0x36,
+       MAX77686_REG_BUCK8OUT           = 0x37,
+       MAX77686_REG_BUCK9CTRL          = 0x38,
+       MAX77686_REG_BUCK9OUT           = 0x39,
+       /* Reserved: 0x3A-0x3F */
+
+       MAX77686_REG_LDO1CTRL1          = 0x40,
+       MAX77686_REG_LDO2CTRL1          = 0x41,
+       MAX77686_REG_LDO3CTRL1          = 0x42,
+       MAX77686_REG_LDO4CTRL1          = 0x43,
+       MAX77686_REG_LDO5CTRL1          = 0x44,
+       MAX77686_REG_LDO6CTRL1          = 0x45,
+       MAX77686_REG_LDO7CTRL1          = 0x46,
+       MAX77686_REG_LDO8CTRL1          = 0x47,
+       MAX77686_REG_LDO9CTRL1          = 0x48,
+       MAX77686_REG_LDO10CTRL1         = 0x49,
+       MAX77686_REG_LDO11CTRL1         = 0x4A,
+       MAX77686_REG_LDO12CTRL1         = 0x4B,
+       MAX77686_REG_LDO13CTRL1         = 0x4C,
+       MAX77686_REG_LDO14CTRL1         = 0x4D,
+       MAX77686_REG_LDO15CTRL1         = 0x4E,
+       MAX77686_REG_LDO16CTRL1         = 0x4F,
+       MAX77686_REG_LDO17CTRL1         = 0x50,
+       MAX77686_REG_LDO18CTRL1         = 0x51,
+       MAX77686_REG_LDO19CTRL1         = 0x52,
+       MAX77686_REG_LDO20CTRL1         = 0x53,
+       MAX77686_REG_LDO21CTRL1         = 0x54,
+       MAX77686_REG_LDO22CTRL1         = 0x55,
+       MAX77686_REG_LDO23CTRL1         = 0x56,
+       MAX77686_REG_LDO24CTRL1         = 0x57,
+       MAX77686_REG_LDO25CTRL1         = 0x58,
+       MAX77686_REG_LDO26CTRL1         = 0x59,
+       /* Reserved: 0x5A-0x5F */
+       MAX77686_REG_LDO1CTRL2          = 0x60,
+       MAX77686_REG_LDO2CTRL2          = 0x61,
+       MAX77686_REG_LDO3CTRL2          = 0x62,
+       MAX77686_REG_LDO4CTRL2          = 0x63,
+       MAX77686_REG_LDO5CTRL2          = 0x64,
+       MAX77686_REG_LDO6CTRL2          = 0x65,
+       MAX77686_REG_LDO7CTRL2          = 0x66,
+       MAX77686_REG_LDO8CTRL2          = 0x67,
+       MAX77686_REG_LDO9CTRL2          = 0x68,
+       MAX77686_REG_LDO10CTRL2         = 0x69,
+       MAX77686_REG_LDO11CTRL2         = 0x6A,
+       MAX77686_REG_LDO12CTRL2         = 0x6B,
+       MAX77686_REG_LDO13CTRL2         = 0x6C,
+       MAX77686_REG_LDO14CTRL2         = 0x6D,
+       MAX77686_REG_LDO15CTRL2         = 0x6E,
+       MAX77686_REG_LDO16CTRL2         = 0x6F,
+       MAX77686_REG_LDO17CTRL2         = 0x70,
+       MAX77686_REG_LDO18CTRL2         = 0x71,
+       MAX77686_REG_LDO19CTRL2         = 0x72,
+       MAX77686_REG_LDO20CTRL2         = 0x73,
+       MAX77686_REG_LDO21CTRL2         = 0x74,
+       MAX77686_REG_LDO22CTRL2         = 0x75,
+       MAX77686_REG_LDO23CTRL2         = 0x76,
+       MAX77686_REG_LDO24CTRL2         = 0x77,
+       MAX77686_REG_LDO25CTRL2         = 0x78,
+       MAX77686_REG_LDO26CTRL2         = 0x79,
+       /* Reserved: 0x7A-0x7D */
+
+       MAX77686_REG_BBAT_CHG           = 0x7E,
+       MAX77686_REG_32KHZ                      = 0x7F,
+
+       MAX77686_REG_PMIC_END           = 0x80,
+};
+
+enum max77686_rtc_reg {
+       MAX77686_RTC_INT                        = 0x00,
+       MAX77686_RTC_INTM                       = 0x01,
+       MAX77686_RTC_CONTROLM           = 0x02,
+       MAX77686_RTC_CONTROL            = 0x03,
+       MAX77686_RTC_UPDATE0            = 0x04,
+       /* Reserved: 0x5 */
+       MAX77686_WTSR_SMPL_CNTL         = 0x06,
+       MAX77686_RTC_SEC                        = 0x07,
+       MAX77686_RTC_MIN                        = 0x08,
+       MAX77686_RTC_HOUR                       = 0x09,
+       MAX77686_RTC_WEEKDAY            = 0x0A,
+       MAX77686_RTC_MONTH                      = 0x0B,
+       MAX77686_RTC_YEAR                       = 0x0C,
+       MAX77686_RTC_DATE                       = 0x0D,
+       MAX77686_ALARM1_SEC                     = 0x0E,
+       MAX77686_ALARM1_MIN                     = 0x0F,
+       MAX77686_ALARM1_HOUR            = 0x10,
+       MAX77686_ALARM1_WEEKDAY         = 0x11,
+       MAX77686_ALARM1_MONTH           = 0x12,
+       MAX77686_ALARM1_YEAR            = 0x13,
+       MAX77686_ALARM1_DATE            = 0x14,
+       MAX77686_ALARM2_SEC                     = 0x15,
+       MAX77686_ALARM2_MIN                     = 0x16,
+       MAX77686_ALARM2_HOUR            = 0x17,
+       MAX77686_ALARM2_WEEKDAY         = 0x18,
+       MAX77686_ALARM2_MONTH           = 0x19,
+       MAX77686_ALARM2_YEAR            = 0x1A,
+       MAX77686_ALARM2_DATE            = 0x1B,
+};
+
+#define MAX77686_IRQSRC_PMIC   (0)
+#define MAX77686_IRQSRC_RTC            (1 << 0)
+
+enum max77686_irq_source {
+       PMIC_INT1 = 0,
+       PMIC_INT2,
+       RTC_INT,
+
+       MAX77686_IRQ_GROUP_NR,
+};
+
+enum max77686_irq {
+       MAX77686_PMICIRQ_PWRONF,
+       MAX77686_PMICIRQ_PWRONR,
+       MAX77686_PMICIRQ_JIGONBF,
+       MAX77686_PMICIRQ_JIGONBR,
+       MAX77686_PMICIRQ_ACOKBF,
+       MAX77686_PMICIRQ_ACOKBR,
+       MAX77686_PMICIRQ_ONKEY1S,
+       MAX77686_PMICIRQ_MRSTB,
+
+       MAX77686_PMICIRQ_140C,
+       MAX77686_PMICIRQ_120C,
+
+       MAX77686_RTCIRQ_RTC60S,
+       MAX77686_RTCIRQ_RTCA1,
+       MAX77686_RTCIRQ_RTCA2,
+       MAX77686_RTCIRQ_SMPL,
+       MAX77686_RTCIRQ_RTC1S,
+       MAX77686_RTCIRQ_WTSR,
+
+       MAX77686_IRQ_NR,
+};
+
+struct max77686_dev {
+       struct device *dev;
+       struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */
+       struct i2c_client *rtc; /* slave addr 0x0c */
+
+       int type;
+
+       struct regmap *regmap;          /* regmap for mfd */
+       struct regmap *rtc_regmap;      /* regmap for rtc */
+
+       struct irq_domain *irq_domain;
+
+       int irq;
+       int irq_gpio;
+       bool wakeup;
+       struct mutex irqlock;
+       int irq_masks_cur[MAX77686_IRQ_GROUP_NR];
+       int irq_masks_cache[MAX77686_IRQ_GROUP_NR];
+};
+
+enum max77686_types {
+       TYPE_MAX77686,
+};
+
+extern int max77686_irq_init(struct max77686_dev *max77686);
+extern void max77686_irq_exit(struct max77686_dev *max77686);
+extern int max77686_irq_resume(struct max77686_dev *max77686);
+
+#endif /*  __LINUX_MFD_MAX77686_PRIV_H */
diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max77686.h
new file mode 100644 (file)
index 0000000..3d7ae4d
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * max77686.h - Driver for the Maxim 77686
+ *
+ *  Copyright (C) 2012 Samsung Electrnoics
+ *  Chiwoong Byun <woong.byun@samsung.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
+ *
+ * This driver is based on max8997.h
+ *
+ * MAX77686 has PMIC, RTC devices.
+ * The devices share the same I2C bus and included in
+ * this mfd driver.
+ */
+
+#ifndef __LINUX_MFD_MAX77686_H
+#define __LINUX_MFD_MAX77686_H
+
+#include <linux/regulator/consumer.h>
+
+/* MAX77686 regulator IDs */
+enum max77686_regulators {
+       MAX77686_LDO1 = 0,
+       MAX77686_LDO2,
+       MAX77686_LDO3,
+       MAX77686_LDO4,
+       MAX77686_LDO5,
+       MAX77686_LDO6,
+       MAX77686_LDO7,
+       MAX77686_LDO8,
+       MAX77686_LDO9,
+       MAX77686_LDO10,
+       MAX77686_LDO11,
+       MAX77686_LDO12,
+       MAX77686_LDO13,
+       MAX77686_LDO14,
+       MAX77686_LDO15,
+       MAX77686_LDO16,
+       MAX77686_LDO17,
+       MAX77686_LDO18,
+       MAX77686_LDO19,
+       MAX77686_LDO20,
+       MAX77686_LDO21,
+       MAX77686_LDO22,
+       MAX77686_LDO23,
+       MAX77686_LDO24,
+       MAX77686_LDO25,
+       MAX77686_LDO26,
+       MAX77686_BUCK1,
+       MAX77686_BUCK2,
+       MAX77686_BUCK3,
+       MAX77686_BUCK4,
+       MAX77686_BUCK5,
+       MAX77686_BUCK6,
+       MAX77686_BUCK7,
+       MAX77686_BUCK8,
+       MAX77686_BUCK9,
+
+       MAX77686_REG_MAX,
+};
+
+struct max77686_regulator_data {
+       int id;
+       struct regulator_init_data *initdata;
+};
+
+enum max77686_opmode {
+       MAX77686_OPMODE_NORMAL,
+       MAX77686_OPMODE_LP,
+       MAX77686_OPMODE_STANDBY,
+};
+
+struct max77686_opmode_data {
+       int id;
+       int mode;
+};
+
+struct max77686_platform_data {
+       /* IRQ */
+       int irq_gpio;
+       int ono;
+       int wakeup;
+
+       /* ---- PMIC ---- */
+       struct max77686_regulator_data *regulators;
+       int num_regulators;
+
+       struct max77686_opmode_data *opmode_data;
+
+       /*
+        * GPIO-DVS feature is not enabled with the current version of
+        * MAX77686 driver. Buck2/3/4_voltages[0] is used as the default
+        * voltage at probe. DVS/SELB gpios are set as OUTPUT-LOW.
+        */
+       int buck234_gpio_dvs[3]; /* GPIO of [0]DVS1, [1]DVS2, [2]DVS3 */
+       int buck234_gpio_selb[3]; /* [0]SELB2, [1]SELB3, [2]SELB4 */
+       unsigned int buck2_voltage[8]; /* buckx_voltage in uV */
+       unsigned int buck3_voltage[8];
+       unsigned int buck4_voltage[8];
+};
+
+#endif /* __LINUX_MFD_MAX77686_H */
index 68263c5fa53c00fd8d125c6dcaa4c7d2dfafe1f7..1eeae5c07915dea761aba5d353db41d7bf033e39 100644 (file)
@@ -190,7 +190,6 @@ struct max77693_dev {
        struct i2c_client *i2c;         /* 0xCC , PMIC, Charger, Flash LED */
        struct i2c_client *muic;        /* 0x4A , MUIC */
        struct i2c_client *haptic;      /* 0x90 , Haptic */
-       struct mutex iolock;
 
        int type;
 
index 3f4deb62d6b0d4c4c3475e074244860977fd259c..830152cfae339727ad1d70875a61dbdaa34a6c67 100644 (file)
@@ -23,6 +23,8 @@
 #define __LINUX_MFD_MAX8997_PRIV_H
 
 #include <linux/i2c.h>
+#include <linux/export.h>
+#include <linux/irqdomain.h>
 
 #define MAX8997_REG_INVALID    (0xff)
 
@@ -325,7 +327,7 @@ struct max8997_dev {
 
        int irq;
        int ono;
-       int irq_base;
+       struct irq_domain *irq_domain;
        struct mutex irqlock;
        int irq_masks_cur[MAX8997_IRQ_GROUP_NR];
        int irq_masks_cache[MAX8997_IRQ_GROUP_NR];
index b40c08cd30bc82b97fd006d4800f07345f318c2c..328d8e24b533acaf524746d01dbf1cba76656782 100644 (file)
@@ -181,7 +181,6 @@ struct max8997_led_platform_data {
 
 struct max8997_platform_data {
        /* IRQ */
-       int irq_base;
        int ono;
        int wakeup;
 
diff --git a/include/linux/mfd/s5m87xx/s5m-core.h b/include/linux/mfd/s5m87xx/s5m-core.h
deleted file mode 100644 (file)
index 0b2e0ed..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * s5m-core.h
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd
- *              http://www.samsung.com
- *
- *  This program is free software; you can redistribute  it and/or modify it
- *  under  the terms of  the GNU General  Public License as published by the
- *  Free Software Foundation;  either version 2 of the  License, or (at your
- *  option) any later version.
- *
- */
-
-#ifndef __LINUX_MFD_S5M_CORE_H
-#define __LINUX_MFD_S5M_CORE_H
-
-#define NUM_IRQ_REGS   4
-
-enum s5m_device_type {
-       S5M8751X,
-       S5M8763X,
-       S5M8767X,
-};
-
-/* S5M8767 registers */
-enum s5m8767_reg {
-       S5M8767_REG_ID,
-       S5M8767_REG_INT1,
-       S5M8767_REG_INT2,
-       S5M8767_REG_INT3,
-       S5M8767_REG_INT1M,
-       S5M8767_REG_INT2M,
-       S5M8767_REG_INT3M,
-       S5M8767_REG_STATUS1,
-       S5M8767_REG_STATUS2,
-       S5M8767_REG_STATUS3,
-       S5M8767_REG_CTRL1,
-       S5M8767_REG_CTRL2,
-       S5M8767_REG_LOWBAT1,
-       S5M8767_REG_LOWBAT2,
-       S5M8767_REG_BUCHG,
-       S5M8767_REG_DVSRAMP,
-       S5M8767_REG_DVSTIMER2 = 0x10,
-       S5M8767_REG_DVSTIMER3,
-       S5M8767_REG_DVSTIMER4,
-       S5M8767_REG_LDO1,
-       S5M8767_REG_LDO2,
-       S5M8767_REG_LDO3,
-       S5M8767_REG_LDO4,
-       S5M8767_REG_LDO5,
-       S5M8767_REG_LDO6,
-       S5M8767_REG_LDO7,
-       S5M8767_REG_LDO8,
-       S5M8767_REG_LDO9,
-       S5M8767_REG_LDO10,
-       S5M8767_REG_LDO11,
-       S5M8767_REG_LDO12,
-       S5M8767_REG_LDO13,
-       S5M8767_REG_LDO14 = 0x20,
-       S5M8767_REG_LDO15,
-       S5M8767_REG_LDO16,
-       S5M8767_REG_LDO17,
-       S5M8767_REG_LDO18,
-       S5M8767_REG_LDO19,
-       S5M8767_REG_LDO20,
-       S5M8767_REG_LDO21,
-       S5M8767_REG_LDO22,
-       S5M8767_REG_LDO23,
-       S5M8767_REG_LDO24,
-       S5M8767_REG_LDO25,
-       S5M8767_REG_LDO26,
-       S5M8767_REG_LDO27,
-       S5M8767_REG_LDO28,
-       S5M8767_REG_UVLO = 0x31,
-       S5M8767_REG_BUCK1CTRL1,
-       S5M8767_REG_BUCK1CTRL2,
-       S5M8767_REG_BUCK2CTRL,
-       S5M8767_REG_BUCK2DVS1,
-       S5M8767_REG_BUCK2DVS2,
-       S5M8767_REG_BUCK2DVS3,
-       S5M8767_REG_BUCK2DVS4,
-       S5M8767_REG_BUCK2DVS5,
-       S5M8767_REG_BUCK2DVS6,
-       S5M8767_REG_BUCK2DVS7,
-       S5M8767_REG_BUCK2DVS8,
-       S5M8767_REG_BUCK3CTRL,
-       S5M8767_REG_BUCK3DVS1,
-       S5M8767_REG_BUCK3DVS2,
-       S5M8767_REG_BUCK3DVS3,
-       S5M8767_REG_BUCK3DVS4,
-       S5M8767_REG_BUCK3DVS5,
-       S5M8767_REG_BUCK3DVS6,
-       S5M8767_REG_BUCK3DVS7,
-       S5M8767_REG_BUCK3DVS8,
-       S5M8767_REG_BUCK4CTRL,
-       S5M8767_REG_BUCK4DVS1,
-       S5M8767_REG_BUCK4DVS2,
-       S5M8767_REG_BUCK4DVS3,
-       S5M8767_REG_BUCK4DVS4,
-       S5M8767_REG_BUCK4DVS5,
-       S5M8767_REG_BUCK4DVS6,
-       S5M8767_REG_BUCK4DVS7,
-       S5M8767_REG_BUCK4DVS8,
-       S5M8767_REG_BUCK5CTRL1,
-       S5M8767_REG_BUCK5CTRL2,
-       S5M8767_REG_BUCK5CTRL3,
-       S5M8767_REG_BUCK5CTRL4,
-       S5M8767_REG_BUCK5CTRL5,
-       S5M8767_REG_BUCK6CTRL1,
-       S5M8767_REG_BUCK6CTRL2,
-       S5M8767_REG_BUCK7CTRL1,
-       S5M8767_REG_BUCK7CTRL2,
-       S5M8767_REG_BUCK8CTRL1,
-       S5M8767_REG_BUCK8CTRL2,
-       S5M8767_REG_BUCK9CTRL1,
-       S5M8767_REG_BUCK9CTRL2,
-       S5M8767_REG_LDO1CTRL,
-       S5M8767_REG_LDO2_1CTRL,
-       S5M8767_REG_LDO2_2CTRL,
-       S5M8767_REG_LDO2_3CTRL,
-       S5M8767_REG_LDO2_4CTRL,
-       S5M8767_REG_LDO3CTRL,
-       S5M8767_REG_LDO4CTRL,
-       S5M8767_REG_LDO5CTRL,
-       S5M8767_REG_LDO6CTRL,
-       S5M8767_REG_LDO7CTRL,
-       S5M8767_REG_LDO8CTRL,
-       S5M8767_REG_LDO9CTRL,
-       S5M8767_REG_LDO10CTRL,
-       S5M8767_REG_LDO11CTRL,
-       S5M8767_REG_LDO12CTRL,
-       S5M8767_REG_LDO13CTRL,
-       S5M8767_REG_LDO14CTRL,
-       S5M8767_REG_LDO15CTRL,
-       S5M8767_REG_LDO16CTRL,
-       S5M8767_REG_LDO17CTRL,
-       S5M8767_REG_LDO18CTRL,
-       S5M8767_REG_LDO19CTRL,
-       S5M8767_REG_LDO20CTRL,
-       S5M8767_REG_LDO21CTRL,
-       S5M8767_REG_LDO22CTRL,
-       S5M8767_REG_LDO23CTRL,
-       S5M8767_REG_LDO24CTRL,
-       S5M8767_REG_LDO25CTRL,
-       S5M8767_REG_LDO26CTRL,
-       S5M8767_REG_LDO27CTRL,
-       S5M8767_REG_LDO28CTRL,
-};
-
-/* S5M8763 registers */
-enum s5m8763_reg {
-       S5M8763_REG_IRQ1,
-       S5M8763_REG_IRQ2,
-       S5M8763_REG_IRQ3,
-       S5M8763_REG_IRQ4,
-       S5M8763_REG_IRQM1,
-       S5M8763_REG_IRQM2,
-       S5M8763_REG_IRQM3,
-       S5M8763_REG_IRQM4,
-       S5M8763_REG_STATUS1,
-       S5M8763_REG_STATUS2,
-       S5M8763_REG_STATUSM1,
-       S5M8763_REG_STATUSM2,
-       S5M8763_REG_CHGR1,
-       S5M8763_REG_CHGR2,
-       S5M8763_REG_LDO_ACTIVE_DISCHARGE1,
-       S5M8763_REG_LDO_ACTIVE_DISCHARGE2,
-       S5M8763_REG_BUCK_ACTIVE_DISCHARGE3,
-       S5M8763_REG_ONOFF1,
-       S5M8763_REG_ONOFF2,
-       S5M8763_REG_ONOFF3,
-       S5M8763_REG_ONOFF4,
-       S5M8763_REG_BUCK1_VOLTAGE1,
-       S5M8763_REG_BUCK1_VOLTAGE2,
-       S5M8763_REG_BUCK1_VOLTAGE3,
-       S5M8763_REG_BUCK1_VOLTAGE4,
-       S5M8763_REG_BUCK2_VOLTAGE1,
-       S5M8763_REG_BUCK2_VOLTAGE2,
-       S5M8763_REG_BUCK3,
-       S5M8763_REG_BUCK4,
-       S5M8763_REG_LDO1_LDO2,
-       S5M8763_REG_LDO3,
-       S5M8763_REG_LDO4,
-       S5M8763_REG_LDO5,
-       S5M8763_REG_LDO6,
-       S5M8763_REG_LDO7,
-       S5M8763_REG_LDO7_LDO8,
-       S5M8763_REG_LDO9_LDO10,
-       S5M8763_REG_LDO11,
-       S5M8763_REG_LDO12,
-       S5M8763_REG_LDO13,
-       S5M8763_REG_LDO14,
-       S5M8763_REG_LDO15,
-       S5M8763_REG_LDO16,
-       S5M8763_REG_BKCHR,
-       S5M8763_REG_LBCNFG1,
-       S5M8763_REG_LBCNFG2,
-};
-
-enum s5m8767_irq {
-       S5M8767_IRQ_PWRR,
-       S5M8767_IRQ_PWRF,
-       S5M8767_IRQ_PWR1S,
-       S5M8767_IRQ_JIGR,
-       S5M8767_IRQ_JIGF,
-       S5M8767_IRQ_LOWBAT2,
-       S5M8767_IRQ_LOWBAT1,
-
-       S5M8767_IRQ_MRB,
-       S5M8767_IRQ_DVSOK2,
-       S5M8767_IRQ_DVSOK3,
-       S5M8767_IRQ_DVSOK4,
-
-       S5M8767_IRQ_RTC60S,
-       S5M8767_IRQ_RTCA1,
-       S5M8767_IRQ_RTCA2,
-       S5M8767_IRQ_SMPL,
-       S5M8767_IRQ_RTC1S,
-       S5M8767_IRQ_WTSR,
-
-       S5M8767_IRQ_NR,
-};
-
-#define S5M8767_IRQ_PWRR_MASK          (1 << 0)
-#define S5M8767_IRQ_PWRF_MASK          (1 << 1)
-#define S5M8767_IRQ_PWR1S_MASK         (1 << 3)
-#define S5M8767_IRQ_JIGR_MASK          (1 << 4)
-#define S5M8767_IRQ_JIGF_MASK          (1 << 5)
-#define S5M8767_IRQ_LOWBAT2_MASK       (1 << 6)
-#define S5M8767_IRQ_LOWBAT1_MASK       (1 << 7)
-
-#define S5M8767_IRQ_MRB_MASK           (1 << 2)
-#define S5M8767_IRQ_DVSOK2_MASK                (1 << 3)
-#define S5M8767_IRQ_DVSOK3_MASK                (1 << 4)
-#define S5M8767_IRQ_DVSOK4_MASK                (1 << 5)
-
-#define S5M8767_IRQ_RTC60S_MASK                (1 << 0)
-#define S5M8767_IRQ_RTCA1_MASK         (1 << 1)
-#define S5M8767_IRQ_RTCA2_MASK         (1 << 2)
-#define S5M8767_IRQ_SMPL_MASK          (1 << 3)
-#define S5M8767_IRQ_RTC1S_MASK         (1 << 4)
-#define S5M8767_IRQ_WTSR_MASK          (1 << 5)
-
-enum s5m8763_irq {
-       S5M8763_IRQ_DCINF,
-       S5M8763_IRQ_DCINR,
-       S5M8763_IRQ_JIGF,
-       S5M8763_IRQ_JIGR,
-       S5M8763_IRQ_PWRONF,
-       S5M8763_IRQ_PWRONR,
-
-       S5M8763_IRQ_WTSREVNT,
-       S5M8763_IRQ_SMPLEVNT,
-       S5M8763_IRQ_ALARM1,
-       S5M8763_IRQ_ALARM0,
-
-       S5M8763_IRQ_ONKEY1S,
-       S5M8763_IRQ_TOPOFFR,
-       S5M8763_IRQ_DCINOVPR,
-       S5M8763_IRQ_CHGRSTF,
-       S5M8763_IRQ_DONER,
-       S5M8763_IRQ_CHGFAULT,
-
-       S5M8763_IRQ_LOBAT1,
-       S5M8763_IRQ_LOBAT2,
-
-       S5M8763_IRQ_NR,
-};
-
-#define S5M8763_IRQ_DCINF_MASK         (1 << 2)
-#define S5M8763_IRQ_DCINR_MASK         (1 << 3)
-#define S5M8763_IRQ_JIGF_MASK          (1 << 4)
-#define S5M8763_IRQ_JIGR_MASK          (1 << 5)
-#define S5M8763_IRQ_PWRONF_MASK                (1 << 6)
-#define S5M8763_IRQ_PWRONR_MASK                (1 << 7)
-
-#define S5M8763_IRQ_WTSREVNT_MASK      (1 << 0)
-#define S5M8763_IRQ_SMPLEVNT_MASK      (1 << 1)
-#define S5M8763_IRQ_ALARM1_MASK                (1 << 2)
-#define S5M8763_IRQ_ALARM0_MASK                (1 << 3)
-
-#define S5M8763_IRQ_ONKEY1S_MASK       (1 << 0)
-#define S5M8763_IRQ_TOPOFFR_MASK       (1 << 2)
-#define S5M8763_IRQ_DCINOVPR_MASK      (1 << 3)
-#define S5M8763_IRQ_CHGRSTF_MASK       (1 << 4)
-#define S5M8763_IRQ_DONER_MASK         (1 << 5)
-#define S5M8763_IRQ_CHGFAULT_MASK      (1 << 7)
-
-#define S5M8763_IRQ_LOBAT1_MASK                (1 << 0)
-#define S5M8763_IRQ_LOBAT2_MASK                (1 << 1)
-
-#define S5M8763_ENRAMP                  (1 << 4)
-
-/**
- * struct s5m87xx_dev - s5m87xx master device for sub-drivers
- * @dev: master device of the chip (can be used to access platform data)
- * @i2c: i2c client private data for regulator
- * @rtc: i2c client private data for rtc
- * @iolock: mutex for serializing io access
- * @irqlock: mutex for buslock
- * @irq_base: base IRQ number for s5m87xx, required for IRQs
- * @irq: generic IRQ number for s5m87xx
- * @ono: power onoff IRQ number for s5m87xx
- * @irq_masks_cur: currently active value
- * @irq_masks_cache: cached hardware value
- * @type: indicate which s5m87xx "variant" is used
- */
-struct s5m87xx_dev {
-       struct device *dev;
-       struct regmap *regmap;
-       struct i2c_client *i2c;
-       struct i2c_client *rtc;
-       struct mutex iolock;
-       struct mutex irqlock;
-
-       int device_type;
-       int irq_base;
-       int irq;
-       int ono;
-       u8 irq_masks_cur[NUM_IRQ_REGS];
-       u8 irq_masks_cache[NUM_IRQ_REGS];
-       int type;
-       bool wakeup;
-};
-
-int s5m_irq_init(struct s5m87xx_dev *s5m87xx);
-void s5m_irq_exit(struct s5m87xx_dev *s5m87xx);
-int s5m_irq_resume(struct s5m87xx_dev *s5m87xx);
-
-extern int s5m_reg_read(struct s5m87xx_dev *s5m87xx, u8 reg, void *dest);
-extern int s5m_bulk_read(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf);
-extern int s5m_reg_write(struct s5m87xx_dev *s5m87xx, u8 reg, u8 value);
-extern int s5m_bulk_write(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf);
-extern int s5m_reg_update(struct s5m87xx_dev *s5m87xx, u8 reg, u8 val, u8 mask);
-
-struct s5m_platform_data {
-       struct s5m_regulator_data       *regulators;
-       struct s5m_opmode_data          *opmode;
-       int                             device_type;
-       int                             num_regulators;
-
-       int                             irq_base;
-       int                             (*cfg_pmic_irq)(void);
-
-       int                             ono;
-       bool                            wakeup;
-       bool                            buck_voltage_lock;
-
-       int                             buck_gpios[3];
-       int                             buck_ds[3];
-       int                             buck2_voltage[8];
-       bool                            buck2_gpiodvs;
-       int                             buck3_voltage[8];
-       bool                            buck3_gpiodvs;
-       int                             buck4_voltage[8];
-       bool                            buck4_gpiodvs;
-
-       int                             buck_set1;
-       int                             buck_set2;
-       int                             buck_set3;
-       int                             buck2_enable;
-       int                             buck3_enable;
-       int                             buck4_enable;
-       int                             buck_default_idx;
-       int                             buck2_default_idx;
-       int                             buck3_default_idx;
-       int                             buck4_default_idx;
-
-       int                             buck_ramp_delay;
-       bool                            buck2_ramp_enable;
-       bool                            buck3_ramp_enable;
-       bool                            buck4_ramp_enable;
-
-       int                             buck2_init;
-       int                             buck3_init;
-       int                             buck4_init;
-};
-
-#endif /*  __LINUX_MFD_S5M_CORE_H */
diff --git a/include/linux/mfd/s5m87xx/s5m-pmic.h b/include/linux/mfd/s5m87xx/s5m-pmic.h
deleted file mode 100644 (file)
index 7c719f2..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/* s5m87xx.h
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- *             http://www.samsung.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 __LINUX_MFD_S5M_PMIC_H
-#define __LINUX_MFD_S5M_PMIC_H
-
-#include <linux/regulator/machine.h>
-
-/* S5M8767 regulator ids */
-enum s5m8767_regulators {
-       S5M8767_LDO1,
-       S5M8767_LDO2,
-       S5M8767_LDO3,
-       S5M8767_LDO4,
-       S5M8767_LDO5,
-       S5M8767_LDO6,
-       S5M8767_LDO7,
-       S5M8767_LDO8,
-       S5M8767_LDO9,
-       S5M8767_LDO10,
-       S5M8767_LDO11,
-       S5M8767_LDO12,
-       S5M8767_LDO13,
-       S5M8767_LDO14,
-       S5M8767_LDO15,
-       S5M8767_LDO16,
-       S5M8767_LDO17,
-       S5M8767_LDO18,
-       S5M8767_LDO19,
-       S5M8767_LDO20,
-       S5M8767_LDO21,
-       S5M8767_LDO22,
-       S5M8767_LDO23,
-       S5M8767_LDO24,
-       S5M8767_LDO25,
-       S5M8767_LDO26,
-       S5M8767_LDO27,
-       S5M8767_LDO28,
-       S5M8767_BUCK1,
-       S5M8767_BUCK2,
-       S5M8767_BUCK3,
-       S5M8767_BUCK4,
-       S5M8767_BUCK5,
-       S5M8767_BUCK6,
-       S5M8767_BUCK7,
-       S5M8767_BUCK8,
-       S5M8767_BUCK9,
-       S5M8767_AP_EN32KHZ,
-       S5M8767_CP_EN32KHZ,
-
-       S5M8767_REG_MAX,
-};
-
-#define S5M8767_ENCTRL_SHIFT  6
-
-/* S5M8763 regulator ids */
-enum s5m8763_regulators {
-       S5M8763_LDO1,
-       S5M8763_LDO2,
-       S5M8763_LDO3,
-       S5M8763_LDO4,
-       S5M8763_LDO5,
-       S5M8763_LDO6,
-       S5M8763_LDO7,
-       S5M8763_LDO8,
-       S5M8763_LDO9,
-       S5M8763_LDO10,
-       S5M8763_LDO11,
-       S5M8763_LDO12,
-       S5M8763_LDO13,
-       S5M8763_LDO14,
-       S5M8763_LDO15,
-       S5M8763_LDO16,
-       S5M8763_BUCK1,
-       S5M8763_BUCK2,
-       S5M8763_BUCK3,
-       S5M8763_BUCK4,
-       S5M8763_AP_EN32KHZ,
-       S5M8763_CP_EN32KHZ,
-       S5M8763_ENCHGVI,
-       S5M8763_ESAFEUSB1,
-       S5M8763_ESAFEUSB2,
-};
-
-/**
- * s5m87xx_regulator_data - regulator data
- * @id: regulator id
- * @initdata: regulator init data (contraints, supplies, ...)
- */
-struct s5m_regulator_data {
-       int                             id;
-       struct regulator_init_data      *initdata;
-};
-
-/*
- * s5m_opmode_data - regulator operation mode data
- * @id: regulator id
- * @mode: regulator operation mode
- */
-struct s5m_opmode_data {
-       int id;
-       int mode;
-};
-
-/*
- * s5m regulator operation mode
- * S5M_OPMODE_OFF      Regulator always OFF
- * S5M_OPMODE_ON       Regulator always ON
- * S5M_OPMODE_LOWPOWER  Regulator is on in low-power mode
- * S5M_OPMODE_SUSPEND   Regulator is changed by PWREN pin
- *                     If PWREN is high, regulator is on
- *                     If PWREN is low, regulator is off
- */
-
-enum s5m_opmode {
-       S5M_OPMODE_OFF,
-       S5M_OPMODE_ON,
-       S5M_OPMODE_LOWPOWER,
-       S5M_OPMODE_SUSPEND,
-};
-
-#endif /*  __LINUX_MFD_S5M_PMIC_H */
diff --git a/include/linux/mfd/s5m87xx/s5m-rtc.h b/include/linux/mfd/s5m87xx/s5m-rtc.h
deleted file mode 100644 (file)
index 6ce8da2..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * s5m-rtc.h
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd
- *              http://www.samsung.com
- *
- *  This program is free software; you can redistribute  it and/or modify it
- *  under  the terms of  the GNU General  Public License as published by the
- *  Free Software Foundation;  either version 2 of the  License, or (at your
- *  option) any later version.
- *
- */
-
-#ifndef __LINUX_MFD_S5M_RTC_H
-#define __LINUX_MFD_S5M_RTC_H
-
-enum s5m87xx_rtc_reg {
-       S5M87XX_RTC_SEC,
-       S5M87XX_RTC_MIN,
-       S5M87XX_RTC_HOUR,
-       S5M87XX_RTC_WEEKDAY,
-       S5M87XX_RTC_DATE,
-       S5M87XX_RTC_MONTH,
-       S5M87XX_RTC_YEAR1,
-       S5M87XX_RTC_YEAR2,
-       S5M87XX_ALARM0_SEC,
-       S5M87XX_ALARM0_MIN,
-       S5M87XX_ALARM0_HOUR,
-       S5M87XX_ALARM0_WEEKDAY,
-       S5M87XX_ALARM0_DATE,
-       S5M87XX_ALARM0_MONTH,
-       S5M87XX_ALARM0_YEAR1,
-       S5M87XX_ALARM0_YEAR2,
-       S5M87XX_ALARM1_SEC,
-       S5M87XX_ALARM1_MIN,
-       S5M87XX_ALARM1_HOUR,
-       S5M87XX_ALARM1_WEEKDAY,
-       S5M87XX_ALARM1_DATE,
-       S5M87XX_ALARM1_MONTH,
-       S5M87XX_ALARM1_YEAR1,
-       S5M87XX_ALARM1_YEAR2,
-       S5M87XX_ALARM0_CONF,
-       S5M87XX_ALARM1_CONF,
-       S5M87XX_RTC_STATUS,
-       S5M87XX_WTSR_SMPL_CNTL,
-       S5M87XX_RTC_UDR_CON,
-};
-
-#define RTC_I2C_ADDR           (0x0C >> 1)
-
-#define HOUR_12                        (1 << 7)
-#define HOUR_AMPM              (1 << 6)
-#define HOUR_PM                        (1 << 5)
-#define ALARM0_STATUS          (1 << 1)
-#define ALARM1_STATUS          (1 << 2)
-#define UPDATE_AD              (1 << 0)
-
-/* RTC Control Register */
-#define BCD_EN_SHIFT           0
-#define BCD_EN_MASK            (1 << BCD_EN_SHIFT)
-#define MODEL24_SHIFT          1
-#define MODEL24_MASK           (1 << MODEL24_SHIFT)
-/* RTC Update Register1 */
-#define RTC_UDR_SHIFT          0
-#define RTC_UDR_MASK           (1 << RTC_UDR_SHIFT)
-/* RTC Hour register */
-#define HOUR_PM_SHIFT          6
-#define HOUR_PM_MASK           (1 << HOUR_PM_SHIFT)
-/* RTC Alarm Enable */
-#define ALARM_ENABLE_SHIFT     7
-#define ALARM_ENABLE_MASK      (1 << ALARM_ENABLE_SHIFT)
-
-enum {
-       RTC_SEC = 0,
-       RTC_MIN,
-       RTC_HOUR,
-       RTC_WEEKDAY,
-       RTC_DATE,
-       RTC_MONTH,
-       RTC_YEAR1,
-       RTC_YEAR2,
-};
-
-#endif /*  __LINUX_MFD_S5M_RTC_H */
diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h
new file mode 100644 (file)
index 0000000..b50c38f
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * core.h
+ *
+ * copyright (c) 2011 Samsung Electronics Co., Ltd
+ *              http://www.samsung.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_SEC_CORE_H
+#define __LINUX_MFD_SEC_CORE_H
+
+#define NUM_IRQ_REGS   4
+
+enum sec_device_type {
+       S5M8751X,
+       S5M8763X,
+       S5M8767X,
+       S2MPS11X,
+};
+
+/**
+ * struct sec_pmic_dev - s5m87xx master device for sub-drivers
+ * @dev: master device of the chip (can be used to access platform data)
+ * @i2c: i2c client private data for regulator
+ * @rtc: i2c client private data for rtc
+ * @iolock: mutex for serializing io access
+ * @irqlock: mutex for buslock
+ * @irq_base: base IRQ number for sec-pmic, required for IRQs
+ * @irq: generic IRQ number for s5m87xx
+ * @ono: power onoff IRQ number for s5m87xx
+ * @irq_masks_cur: currently active value
+ * @irq_masks_cache: cached hardware value
+ * @type: indicate which s5m87xx "variant" is used
+ */
+struct sec_pmic_dev {
+       struct device *dev;
+       struct regmap *regmap;
+       struct i2c_client *i2c;
+       struct i2c_client *rtc;
+       struct mutex iolock;
+       struct mutex irqlock;
+
+       int device_type;
+       int irq_base;
+       int irq;
+       struct regmap_irq_chip_data *irq_data;
+
+       int ono;
+       u8 irq_masks_cur[NUM_IRQ_REGS];
+       u8 irq_masks_cache[NUM_IRQ_REGS];
+       int type;
+       bool wakeup;
+};
+
+int sec_irq_init(struct sec_pmic_dev *sec_pmic);
+void sec_irq_exit(struct sec_pmic_dev *sec_pmic);
+int sec_irq_resume(struct sec_pmic_dev *sec_pmic);
+
+extern int sec_reg_read(struct sec_pmic_dev *sec_pmic, u8 reg, void *dest);
+extern int sec_bulk_read(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf);
+extern int sec_reg_write(struct sec_pmic_dev *sec_pmic, u8 reg, u8 value);
+extern int sec_bulk_write(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf);
+extern int sec_reg_update(struct sec_pmic_dev *sec_pmic, u8 reg, u8 val, u8 mask);
+
+struct sec_platform_data {
+       struct sec_regulator_data       *regulators;
+       struct sec_opmode_data          *opmode;
+       int                             device_type;
+       int                             num_regulators;
+
+       int                             irq_base;
+       int                             (*cfg_pmic_irq)(void);
+
+       int                             ono;
+       bool                            wakeup;
+       bool                            buck_voltage_lock;
+
+       int                             buck_gpios[3];
+       int                             buck_ds[3];
+       int                             buck2_voltage[8];
+       bool                            buck2_gpiodvs;
+       int                             buck3_voltage[8];
+       bool                            buck3_gpiodvs;
+       int                             buck4_voltage[8];
+       bool                            buck4_gpiodvs;
+
+       int                             buck_set1;
+       int                             buck_set2;
+       int                             buck_set3;
+       int                             buck2_enable;
+       int                             buck3_enable;
+       int                             buck4_enable;
+       int                             buck_default_idx;
+       int                             buck2_default_idx;
+       int                             buck3_default_idx;
+       int                             buck4_default_idx;
+
+       int                             buck_ramp_delay;
+
+       int                             buck2_ramp_delay;
+       int                             buck34_ramp_delay;
+       int                             buck5_ramp_delay;
+       int                             buck16_ramp_delay;
+       int                             buck7810_ramp_delay;
+       int                             buck9_ramp_delay;
+
+       bool                            buck2_ramp_enable;
+       bool                            buck3_ramp_enable;
+       bool                            buck4_ramp_enable;
+       bool                            buck6_ramp_enable;
+
+       int                             buck2_init;
+       int                             buck3_init;
+       int                             buck4_init;
+};
+
+/**
+ * sec_regulator_data - regulator data
+ * @id: regulator id
+ * @initdata: regulator init data (contraints, supplies, ...)
+ */
+struct sec_regulator_data {
+       int                             id;
+       struct regulator_init_data      *initdata;
+};
+
+/*
+ * sec_opmode_data - regulator operation mode data
+ * @id: regulator id
+ * @mode: regulator operation mode
+ */
+struct sec_opmode_data {
+       int id;
+       int mode;
+};
+
+/*
+ * samsung regulator operation mode
+ * SEC_OPMODE_OFF      Regulator always OFF
+ * SEC_OPMODE_ON       Regulator always ON
+ * SEC_OPMODE_LOWPOWER  Regulator is on in low-power mode
+ * SEC_OPMODE_SUSPEND   Regulator is changed by PWREN pin
+ *                     If PWREN is high, regulator is on
+ *                     If PWREN is low, regulator is off
+ */
+
+enum sec_opmode {
+       SEC_OPMODE_OFF,
+       SEC_OPMODE_ON,
+       SEC_OPMODE_LOWPOWER,
+       SEC_OPMODE_SUSPEND,
+};
+
+#endif /*  __LINUX_MFD_SEC_CORE_H */
diff --git a/include/linux/mfd/samsung/irq.h b/include/linux/mfd/samsung/irq.h
new file mode 100644 (file)
index 0000000..d43b4f9
--- /dev/null
@@ -0,0 +1,152 @@
+/* irq.h
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd
+ *              http://www.samsung.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_SEC_IRQ_H
+#define __LINUX_MFD_SEC_IRQ_H
+
+enum s2mps11_irq {
+       S2MPS11_IRQ_PWRONF,
+       S2MPS11_IRQ_PWRONR,
+       S2MPS11_IRQ_JIGONBF,
+       S2MPS11_IRQ_JIGONBR,
+       S2MPS11_IRQ_ACOKBF,
+       S2MPS11_IRQ_ACOKBR,
+       S2MPS11_IRQ_PWRON1S,
+       S2MPS11_IRQ_MRB,
+
+       S2MPS11_IRQ_RTC60S,
+       S2MPS11_IRQ_RTCA1,
+       S2MPS11_IRQ_RTCA2,
+       S2MPS11_IRQ_SMPL,
+       S2MPS11_IRQ_RTC1S,
+       S2MPS11_IRQ_WTSR,
+
+       S2MPS11_IRQ_INT120C,
+       S2MPS11_IRQ_INT140C,
+
+       S2MPS11_IRQ_NR,
+};
+
+#define S2MPS11_IRQ_PWRONF_MASK                (1 << 0)
+#define S2MPS11_IRQ_PWRONR_MASK                (1 << 1)
+#define S2MPS11_IRQ_JIGONBF_MASK       (1 << 2)
+#define S2MPS11_IRQ_JIGONBR_MASK       (1 << 3)
+#define S2MPS11_IRQ_ACOKBF_MASK                (1 << 4)
+#define S2MPS11_IRQ_ACOKBR_MASK                (1 << 5)
+#define S2MPS11_IRQ_PWRON1S_MASK       (1 << 6)
+#define S2MPS11_IRQ_MRB_MASK           (1 << 7)
+
+#define S2MPS11_IRQ_RTC60S_MASK                (1 << 0)
+#define S2MPS11_IRQ_RTCA1_MASK         (1 << 1)
+#define S2MPS11_IRQ_RTCA2_MASK         (1 << 2)
+#define S2MPS11_IRQ_SMPL_MASK          (1 << 3)
+#define S2MPS11_IRQ_RTC1S_MASK         (1 << 4)
+#define S2MPS11_IRQ_WTSR_MASK          (1 << 5)
+
+#define S2MPS11_IRQ_INT120C_MASK       (1 << 0)
+#define S2MPS11_IRQ_INT140C_MASK       (1 << 1)
+
+enum s5m8767_irq {
+       S5M8767_IRQ_PWRR,
+       S5M8767_IRQ_PWRF,
+       S5M8767_IRQ_PWR1S,
+       S5M8767_IRQ_JIGR,
+       S5M8767_IRQ_JIGF,
+       S5M8767_IRQ_LOWBAT2,
+       S5M8767_IRQ_LOWBAT1,
+
+       S5M8767_IRQ_MRB,
+       S5M8767_IRQ_DVSOK2,
+       S5M8767_IRQ_DVSOK3,
+       S5M8767_IRQ_DVSOK4,
+
+       S5M8767_IRQ_RTC60S,
+       S5M8767_IRQ_RTCA1,
+       S5M8767_IRQ_RTCA2,
+       S5M8767_IRQ_SMPL,
+       S5M8767_IRQ_RTC1S,
+       S5M8767_IRQ_WTSR,
+
+       S5M8767_IRQ_NR,
+};
+
+#define S5M8767_IRQ_PWRR_MASK          (1 << 0)
+#define S5M8767_IRQ_PWRF_MASK          (1 << 1)
+#define S5M8767_IRQ_PWR1S_MASK         (1 << 3)
+#define S5M8767_IRQ_JIGR_MASK          (1 << 4)
+#define S5M8767_IRQ_JIGF_MASK          (1 << 5)
+#define S5M8767_IRQ_LOWBAT2_MASK       (1 << 6)
+#define S5M8767_IRQ_LOWBAT1_MASK       (1 << 7)
+
+#define S5M8767_IRQ_MRB_MASK           (1 << 2)
+#define S5M8767_IRQ_DVSOK2_MASK                (1 << 3)
+#define S5M8767_IRQ_DVSOK3_MASK                (1 << 4)
+#define S5M8767_IRQ_DVSOK4_MASK                (1 << 5)
+
+#define S5M8767_IRQ_RTC60S_MASK                (1 << 0)
+#define S5M8767_IRQ_RTCA1_MASK         (1 << 1)
+#define S5M8767_IRQ_RTCA2_MASK         (1 << 2)
+#define S5M8767_IRQ_SMPL_MASK          (1 << 3)
+#define S5M8767_IRQ_RTC1S_MASK         (1 << 4)
+#define S5M8767_IRQ_WTSR_MASK          (1 << 5)
+
+enum s5m8763_irq {
+       S5M8763_IRQ_DCINF,
+       S5M8763_IRQ_DCINR,
+       S5M8763_IRQ_JIGF,
+       S5M8763_IRQ_JIGR,
+       S5M8763_IRQ_PWRONF,
+       S5M8763_IRQ_PWRONR,
+
+       S5M8763_IRQ_WTSREVNT,
+       S5M8763_IRQ_SMPLEVNT,
+       S5M8763_IRQ_ALARM1,
+       S5M8763_IRQ_ALARM0,
+
+       S5M8763_IRQ_ONKEY1S,
+       S5M8763_IRQ_TOPOFFR,
+       S5M8763_IRQ_DCINOVPR,
+       S5M8763_IRQ_CHGRSTF,
+       S5M8763_IRQ_DONER,
+       S5M8763_IRQ_CHGFAULT,
+
+       S5M8763_IRQ_LOBAT1,
+       S5M8763_IRQ_LOBAT2,
+
+       S5M8763_IRQ_NR,
+};
+
+#define S5M8763_IRQ_DCINF_MASK         (1 << 2)
+#define S5M8763_IRQ_DCINR_MASK         (1 << 3)
+#define S5M8763_IRQ_JIGF_MASK          (1 << 4)
+#define S5M8763_IRQ_JIGR_MASK          (1 << 5)
+#define S5M8763_IRQ_PWRONF_MASK                (1 << 6)
+#define S5M8763_IRQ_PWRONR_MASK                (1 << 7)
+
+#define S5M8763_IRQ_WTSREVNT_MASK      (1 << 0)
+#define S5M8763_IRQ_SMPLEVNT_MASK      (1 << 1)
+#define S5M8763_IRQ_ALARM1_MASK                (1 << 2)
+#define S5M8763_IRQ_ALARM0_MASK                (1 << 3)
+
+#define S5M8763_IRQ_ONKEY1S_MASK       (1 << 0)
+#define S5M8763_IRQ_TOPOFFR_MASK       (1 << 2)
+#define S5M8763_IRQ_DCINOVPR_MASK      (1 << 3)
+#define S5M8763_IRQ_CHGRSTF_MASK       (1 << 4)
+#define S5M8763_IRQ_DONER_MASK         (1 << 5)
+#define S5M8763_IRQ_CHGFAULT_MASK      (1 << 7)
+
+#define S5M8763_IRQ_LOBAT1_MASK                (1 << 0)
+#define S5M8763_IRQ_LOBAT2_MASK                (1 << 1)
+
+#define S5M8763_ENRAMP                  (1 << 4)
+
+#endif /*  __LINUX_MFD_SEC_IRQ_H */
diff --git a/include/linux/mfd/samsung/rtc.h b/include/linux/mfd/samsung/rtc.h
new file mode 100644 (file)
index 0000000..71597e2
--- /dev/null
@@ -0,0 +1,83 @@
+/*  rtc.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ *              http://www.samsung.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_SEC_RTC_H
+#define __LINUX_MFD_SEC_RTC_H
+
+enum sec_rtc_reg {
+       SEC_RTC_SEC,
+       SEC_RTC_MIN,
+       SEC_RTC_HOUR,
+       SEC_RTC_WEEKDAY,
+       SEC_RTC_DATE,
+       SEC_RTC_MONTH,
+       SEC_RTC_YEAR1,
+       SEC_RTC_YEAR2,
+       SEC_ALARM0_SEC,
+       SEC_ALARM0_MIN,
+       SEC_ALARM0_HOUR,
+       SEC_ALARM0_WEEKDAY,
+       SEC_ALARM0_DATE,
+       SEC_ALARM0_MONTH,
+       SEC_ALARM0_YEAR1,
+       SEC_ALARM0_YEAR2,
+       SEC_ALARM1_SEC,
+       SEC_ALARM1_MIN,
+       SEC_ALARM1_HOUR,
+       SEC_ALARM1_WEEKDAY,
+       SEC_ALARM1_DATE,
+       SEC_ALARM1_MONTH,
+       SEC_ALARM1_YEAR1,
+       SEC_ALARM1_YEAR2,
+       SEC_ALARM0_CONF,
+       SEC_ALARM1_CONF,
+       SEC_RTC_STATUS,
+       SEC_WTSR_SMPL_CNTL,
+       SEC_RTC_UDR_CON,
+};
+
+#define RTC_I2C_ADDR           (0x0C >> 1)
+
+#define HOUR_12                        (1 << 7)
+#define HOUR_AMPM              (1 << 6)
+#define HOUR_PM                        (1 << 5)
+#define ALARM0_STATUS          (1 << 1)
+#define ALARM1_STATUS          (1 << 2)
+#define UPDATE_AD              (1 << 0)
+
+/* RTC Control Register */
+#define BCD_EN_SHIFT           0
+#define BCD_EN_MASK            (1 << BCD_EN_SHIFT)
+#define MODEL24_SHIFT          1
+#define MODEL24_MASK           (1 << MODEL24_SHIFT)
+/* RTC Update Register1 */
+#define RTC_UDR_SHIFT          0
+#define RTC_UDR_MASK           (1 << RTC_UDR_SHIFT)
+/* RTC Hour register */
+#define HOUR_PM_SHIFT          6
+#define HOUR_PM_MASK           (1 << HOUR_PM_SHIFT)
+/* RTC Alarm Enable */
+#define ALARM_ENABLE_SHIFT     7
+#define ALARM_ENABLE_MASK      (1 << ALARM_ENABLE_SHIFT)
+
+enum {
+       RTC_SEC = 0,
+       RTC_MIN,
+       RTC_HOUR,
+       RTC_WEEKDAY,
+       RTC_DATE,
+       RTC_MONTH,
+       RTC_YEAR1,
+       RTC_YEAR2,
+};
+
+#endif /*  __LINUX_MFD_SEC_RTC_H */
diff --git a/include/linux/mfd/samsung/s2mps11.h b/include/linux/mfd/samsung/s2mps11.h
new file mode 100644 (file)
index 0000000..ad2252f
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * s2mps11.h
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd
+ *              http://www.samsung.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_S2MPS11_H
+#define __LINUX_MFD_S2MPS11_H
+
+/* S2MPS11 registers */
+enum s2mps11_reg {
+       S2MPS11_REG_ID,
+       S2MPS11_REG_INT1,
+       S2MPS11_REG_INT2,
+       S2MPS11_REG_INT3,
+       S2MPS11_REG_INT1M,
+       S2MPS11_REG_INT2M,
+       S2MPS11_REG_INT3M,
+       S2MPS11_REG_ST1,
+       S2MPS11_REG_ST2,
+       S2MPS11_REG_OFFSRC,
+       S2MPS11_REG_PWRONSRC,
+       S2MPS11_REG_RTC_CTRL,
+       S2MPS11_REG_CTRL1,
+       S2MPS11_REG_ETC_TEST,
+       S2MPS11_REG_RSVD3,
+       S2MPS11_REG_BU_CHG,
+       S2MPS11_REG_RAMP,
+       S2MPS11_REG_RAMP_BUCK,
+       S2MPS11_REG_LDO1_8,
+       S2MPS11_REG_LDO9_16,
+       S2MPS11_REG_LDO17_24,
+       S2MPS11_REG_LDO25_32,
+       S2MPS11_REG_LDO33_38,
+       S2MPS11_REG_LDO1_8_1,
+       S2MPS11_REG_LDO9_16_1,
+       S2MPS11_REG_LDO17_24_1,
+       S2MPS11_REG_LDO25_32_1,
+       S2MPS11_REG_LDO33_38_1,
+       S2MPS11_REG_OTP_ADRL,
+       S2MPS11_REG_OTP_ADRH,
+       S2MPS11_REG_OTP_DATA,
+       S2MPS11_REG_MON1SEL,
+       S2MPS11_REG_MON2SEL,
+       S2MPS11_REG_LEE,
+       S2MPS11_REG_RSVD_NO,
+       S2MPS11_REG_UVLO,
+       S2MPS11_REG_LEE_NO,
+       S2MPS11_REG_B1CTRL1,
+       S2MPS11_REG_B1CTRL2,
+       S2MPS11_REG_B2CTRL1,
+       S2MPS11_REG_B2CTRL2,
+       S2MPS11_REG_B3CTRL1,
+       S2MPS11_REG_B3CTRL2,
+       S2MPS11_REG_B4CTRL1,
+       S2MPS11_REG_B4CTRL2,
+       S2MPS11_REG_B5CTRL1,
+       S2MPS11_REG_BUCK5_SW,
+       S2MPS11_REG_B5CTRL2,
+       S2MPS11_REG_B5CTRL3,
+       S2MPS11_REG_B5CTRL4,
+       S2MPS11_REG_B5CTRL5,
+       S2MPS11_REG_B6CTRL1,
+       S2MPS11_REG_B6CTRL2,
+       S2MPS11_REG_B7CTRL1,
+       S2MPS11_REG_B7CTRL2,
+       S2MPS11_REG_B8CTRL1,
+       S2MPS11_REG_B8CTRL2,
+       S2MPS11_REG_B9CTRL1,
+       S2MPS11_REG_B9CTRL2,
+       S2MPS11_REG_B10CTRL1,
+       S2MPS11_REG_B10CTRL2,
+       S2MPS11_REG_L1CTRL,
+       S2MPS11_REG_L2CTRL,
+       S2MPS11_REG_L3CTRL,
+       S2MPS11_REG_L4CTRL,
+       S2MPS11_REG_L5CTRL,
+       S2MPS11_REG_L6CTRL,
+       S2MPS11_REG_L7CTRL,
+       S2MPS11_REG_L8CTRL,
+       S2MPS11_REG_L9CTRL,
+       S2MPS11_REG_L10CTRL,
+       S2MPS11_REG_L11CTRL,
+       S2MPS11_REG_L12CTRL,
+       S2MPS11_REG_L13CTRL,
+       S2MPS11_REG_L14CTRL,
+       S2MPS11_REG_L15CTRL,
+       S2MPS11_REG_L16CTRL,
+       S2MPS11_REG_L17CTRL,
+       S2MPS11_REG_L18CTRL,
+       S2MPS11_REG_L19CTRL,
+       S2MPS11_REG_L20CTRL,
+       S2MPS11_REG_L21CTRL,
+       S2MPS11_REG_L22CTRL,
+       S2MPS11_REG_L23CTRL,
+       S2MPS11_REG_L24CTRL,
+       S2MPS11_REG_L25CTRL,
+       S2MPS11_REG_L26CTRL,
+       S2MPS11_REG_L27CTRL,
+       S2MPS11_REG_L28CTRL,
+       S2MPS11_REG_L29CTRL,
+       S2MPS11_REG_L30CTRL,
+       S2MPS11_REG_L31CTRL,
+       S2MPS11_REG_L32CTRL,
+       S2MPS11_REG_L33CTRL,
+       S2MPS11_REG_L34CTRL,
+       S2MPS11_REG_L35CTRL,
+       S2MPS11_REG_L36CTRL,
+       S2MPS11_REG_L37CTRL,
+       S2MPS11_REG_L38CTRL,
+};
+
+/* S2MPS11 regulator ids */
+enum s2mps11_regulators {
+       S2MPS11_LDO1,
+       S2MPS11_LDO2,
+       S2MPS11_LDO3,
+       S2MPS11_LDO4,
+       S2MPS11_LDO5,
+       S2MPS11_LDO6,
+       S2MPS11_LDO7,
+       S2MPS11_LDO8,
+       S2MPS11_LDO9,
+       S2MPS11_LDO10,
+       S2MPS11_LDO11,
+       S2MPS11_LDO12,
+       S2MPS11_LDO13,
+       S2MPS11_LDO14,
+       S2MPS11_LDO15,
+       S2MPS11_LDO16,
+       S2MPS11_LDO17,
+       S2MPS11_LDO18,
+       S2MPS11_LDO19,
+       S2MPS11_LDO20,
+       S2MPS11_LDO21,
+       S2MPS11_LDO22,
+       S2MPS11_LDO23,
+       S2MPS11_LDO24,
+       S2MPS11_LDO25,
+       S2MPS11_LDO26,
+       S2MPS11_LDO27,
+       S2MPS11_LDO28,
+       S2MPS11_LDO29,
+       S2MPS11_LDO30,
+       S2MPS11_LDO31,
+       S2MPS11_LDO32,
+       S2MPS11_LDO33,
+       S2MPS11_LDO34,
+       S2MPS11_LDO35,
+       S2MPS11_LDO36,
+       S2MPS11_LDO37,
+       S2MPS11_LDO38,
+       S2MPS11_BUCK1,
+       S2MPS11_BUCK2,
+       S2MPS11_BUCK3,
+       S2MPS11_BUCK4,
+       S2MPS11_BUCK5,
+       S2MPS11_BUCK6,
+       S2MPS11_BUCK7,
+       S2MPS11_BUCK8,
+       S2MPS11_BUCK9,
+       S2MPS11_BUCK10,
+       S2MPS11_AP_EN32KHZ,
+       S2MPS11_CP_EN32KHZ,
+       S2MPS11_BT_EN32KHZ,
+
+       S2MPS11_REG_MAX,
+};
+
+#define S2MPS11_BUCK_MIN1      600000
+#define S2MPS11_BUCK_MIN2      750000
+#define S2MPS11_BUCK_MIN3      3000000
+#define S2MPS11_LDO_MIN        800000
+#define S2MPS11_BUCK_STEP1     6250
+#define S2MPS11_BUCK_STEP2     12500
+#define S2MPS11_BUCK_STEP3     25000
+#define S2MPS11_LDO_STEP1      50000
+#define S2MPS11_LDO_STEP2      25000
+#define S2MPS11_LDO_VSEL_MASK  0x3F
+#define S2MPS11_BUCK_VSEL_MASK 0xFF
+#define S2MPS11_ENABLE_MASK    (0x03 << S2MPS11_ENABLE_SHIFT)
+#define S2MPS11_ENABLE_SHIFT   0x06
+#define S2MPS11_LDO_N_VOLTAGES (S2MPS11_LDO_VSEL_MASK + 1)
+#define S2MPS11_BUCK_N_VOLTAGES (S2MPS11_BUCK_VSEL_MASK + 1)
+
+#define S2MPS11_PMIC_EN_SHIFT  6
+#define S2MPS11_REGULATOR_MAX (S2MPS11_REG_MAX - 3)
+
+#endif /*  __LINUX_MFD_S2MPS11_H */
diff --git a/include/linux/mfd/samsung/s5m8763.h b/include/linux/mfd/samsung/s5m8763.h
new file mode 100644 (file)
index 0000000..e025418
--- /dev/null
@@ -0,0 +1,96 @@
+/*  s5m8763.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ *              http://www.samsung.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_S5M8763_H
+#define __LINUX_MFD_S5M8763_H
+
+/* S5M8763 registers */
+enum s5m8763_reg {
+       S5M8763_REG_IRQ1,
+       S5M8763_REG_IRQ2,
+       S5M8763_REG_IRQ3,
+       S5M8763_REG_IRQ4,
+       S5M8763_REG_IRQM1,
+       S5M8763_REG_IRQM2,
+       S5M8763_REG_IRQM3,
+       S5M8763_REG_IRQM4,
+       S5M8763_REG_STATUS1,
+       S5M8763_REG_STATUS2,
+       S5M8763_REG_STATUSM1,
+       S5M8763_REG_STATUSM2,
+       S5M8763_REG_CHGR1,
+       S5M8763_REG_CHGR2,
+       S5M8763_REG_LDO_ACTIVE_DISCHARGE1,
+       S5M8763_REG_LDO_ACTIVE_DISCHARGE2,
+       S5M8763_REG_BUCK_ACTIVE_DISCHARGE3,
+       S5M8763_REG_ONOFF1,
+       S5M8763_REG_ONOFF2,
+       S5M8763_REG_ONOFF3,
+       S5M8763_REG_ONOFF4,
+       S5M8763_REG_BUCK1_VOLTAGE1,
+       S5M8763_REG_BUCK1_VOLTAGE2,
+       S5M8763_REG_BUCK1_VOLTAGE3,
+       S5M8763_REG_BUCK1_VOLTAGE4,
+       S5M8763_REG_BUCK2_VOLTAGE1,
+       S5M8763_REG_BUCK2_VOLTAGE2,
+       S5M8763_REG_BUCK3,
+       S5M8763_REG_BUCK4,
+       S5M8763_REG_LDO1_LDO2,
+       S5M8763_REG_LDO3,
+       S5M8763_REG_LDO4,
+       S5M8763_REG_LDO5,
+       S5M8763_REG_LDO6,
+       S5M8763_REG_LDO7,
+       S5M8763_REG_LDO7_LDO8,
+       S5M8763_REG_LDO9_LDO10,
+       S5M8763_REG_LDO11,
+       S5M8763_REG_LDO12,
+       S5M8763_REG_LDO13,
+       S5M8763_REG_LDO14,
+       S5M8763_REG_LDO15,
+       S5M8763_REG_LDO16,
+       S5M8763_REG_BKCHR,
+       S5M8763_REG_LBCNFG1,
+       S5M8763_REG_LBCNFG2,
+};
+
+/* S5M8763 regulator ids */
+enum s5m8763_regulators {
+       S5M8763_LDO1,
+       S5M8763_LDO2,
+       S5M8763_LDO3,
+       S5M8763_LDO4,
+       S5M8763_LDO5,
+       S5M8763_LDO6,
+       S5M8763_LDO7,
+       S5M8763_LDO8,
+       S5M8763_LDO9,
+       S5M8763_LDO10,
+       S5M8763_LDO11,
+       S5M8763_LDO12,
+       S5M8763_LDO13,
+       S5M8763_LDO14,
+       S5M8763_LDO15,
+       S5M8763_LDO16,
+       S5M8763_BUCK1,
+       S5M8763_BUCK2,
+       S5M8763_BUCK3,
+       S5M8763_BUCK4,
+       S5M8763_AP_EN32KHZ,
+       S5M8763_CP_EN32KHZ,
+       S5M8763_ENCHGVI,
+       S5M8763_ESAFEUSB1,
+       S5M8763_ESAFEUSB2,
+};
+
+#define S5M8763_ENRAMP                  (1 << 4)
+#endif /* __LINUX_MFD_S5M8763_H */
diff --git a/include/linux/mfd/samsung/s5m8767.h b/include/linux/mfd/samsung/s5m8767.h
new file mode 100644 (file)
index 0000000..306a95f
--- /dev/null
@@ -0,0 +1,188 @@
+/*  s5m8767.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ *              http://www.samsung.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_S5M8767_H
+#define __LINUX_MFD_S5M8767_H
+
+/* S5M8767 registers */
+enum s5m8767_reg {
+       S5M8767_REG_ID,
+       S5M8767_REG_INT1,
+       S5M8767_REG_INT2,
+       S5M8767_REG_INT3,
+       S5M8767_REG_INT1M,
+       S5M8767_REG_INT2M,
+       S5M8767_REG_INT3M,
+       S5M8767_REG_STATUS1,
+       S5M8767_REG_STATUS2,
+       S5M8767_REG_STATUS3,
+       S5M8767_REG_CTRL1,
+       S5M8767_REG_CTRL2,
+       S5M8767_REG_LOWBAT1,
+       S5M8767_REG_LOWBAT2,
+       S5M8767_REG_BUCHG,
+       S5M8767_REG_DVSRAMP,
+       S5M8767_REG_DVSTIMER2 = 0x10,
+       S5M8767_REG_DVSTIMER3,
+       S5M8767_REG_DVSTIMER4,
+       S5M8767_REG_LDO1,
+       S5M8767_REG_LDO2,
+       S5M8767_REG_LDO3,
+       S5M8767_REG_LDO4,
+       S5M8767_REG_LDO5,
+       S5M8767_REG_LDO6,
+       S5M8767_REG_LDO7,
+       S5M8767_REG_LDO8,
+       S5M8767_REG_LDO9,
+       S5M8767_REG_LDO10,
+       S5M8767_REG_LDO11,
+       S5M8767_REG_LDO12,
+       S5M8767_REG_LDO13,
+       S5M8767_REG_LDO14 = 0x20,
+       S5M8767_REG_LDO15,
+       S5M8767_REG_LDO16,
+       S5M8767_REG_LDO17,
+       S5M8767_REG_LDO18,
+       S5M8767_REG_LDO19,
+       S5M8767_REG_LDO20,
+       S5M8767_REG_LDO21,
+       S5M8767_REG_LDO22,
+       S5M8767_REG_LDO23,
+       S5M8767_REG_LDO24,
+       S5M8767_REG_LDO25,
+       S5M8767_REG_LDO26,
+       S5M8767_REG_LDO27,
+       S5M8767_REG_LDO28,
+       S5M8767_REG_UVLO = 0x31,
+       S5M8767_REG_BUCK1CTRL1,
+       S5M8767_REG_BUCK1CTRL2,
+       S5M8767_REG_BUCK2CTRL,
+       S5M8767_REG_BUCK2DVS1,
+       S5M8767_REG_BUCK2DVS2,
+       S5M8767_REG_BUCK2DVS3,
+       S5M8767_REG_BUCK2DVS4,
+       S5M8767_REG_BUCK2DVS5,
+       S5M8767_REG_BUCK2DVS6,
+       S5M8767_REG_BUCK2DVS7,
+       S5M8767_REG_BUCK2DVS8,
+       S5M8767_REG_BUCK3CTRL,
+       S5M8767_REG_BUCK3DVS1,
+       S5M8767_REG_BUCK3DVS2,
+       S5M8767_REG_BUCK3DVS3,
+       S5M8767_REG_BUCK3DVS4,
+       S5M8767_REG_BUCK3DVS5,
+       S5M8767_REG_BUCK3DVS6,
+       S5M8767_REG_BUCK3DVS7,
+       S5M8767_REG_BUCK3DVS8,
+       S5M8767_REG_BUCK4CTRL,
+       S5M8767_REG_BUCK4DVS1,
+       S5M8767_REG_BUCK4DVS2,
+       S5M8767_REG_BUCK4DVS3,
+       S5M8767_REG_BUCK4DVS4,
+       S5M8767_REG_BUCK4DVS5,
+       S5M8767_REG_BUCK4DVS6,
+       S5M8767_REG_BUCK4DVS7,
+       S5M8767_REG_BUCK4DVS8,
+       S5M8767_REG_BUCK5CTRL1,
+       S5M8767_REG_BUCK5CTRL2,
+       S5M8767_REG_BUCK5CTRL3,
+       S5M8767_REG_BUCK5CTRL4,
+       S5M8767_REG_BUCK5CTRL5,
+       S5M8767_REG_BUCK6CTRL1,
+       S5M8767_REG_BUCK6CTRL2,
+       S5M8767_REG_BUCK7CTRL1,
+       S5M8767_REG_BUCK7CTRL2,
+       S5M8767_REG_BUCK8CTRL1,
+       S5M8767_REG_BUCK8CTRL2,
+       S5M8767_REG_BUCK9CTRL1,
+       S5M8767_REG_BUCK9CTRL2,
+       S5M8767_REG_LDO1CTRL,
+       S5M8767_REG_LDO2_1CTRL,
+       S5M8767_REG_LDO2_2CTRL,
+       S5M8767_REG_LDO2_3CTRL,
+       S5M8767_REG_LDO2_4CTRL,
+       S5M8767_REG_LDO3CTRL,
+       S5M8767_REG_LDO4CTRL,
+       S5M8767_REG_LDO5CTRL,
+       S5M8767_REG_LDO6CTRL,
+       S5M8767_REG_LDO7CTRL,
+       S5M8767_REG_LDO8CTRL,
+       S5M8767_REG_LDO9CTRL,
+       S5M8767_REG_LDO10CTRL,
+       S5M8767_REG_LDO11CTRL,
+       S5M8767_REG_LDO12CTRL,
+       S5M8767_REG_LDO13CTRL,
+       S5M8767_REG_LDO14CTRL,
+       S5M8767_REG_LDO15CTRL,
+       S5M8767_REG_LDO16CTRL,
+       S5M8767_REG_LDO17CTRL,
+       S5M8767_REG_LDO18CTRL,
+       S5M8767_REG_LDO19CTRL,
+       S5M8767_REG_LDO20CTRL,
+       S5M8767_REG_LDO21CTRL,
+       S5M8767_REG_LDO22CTRL,
+       S5M8767_REG_LDO23CTRL,
+       S5M8767_REG_LDO24CTRL,
+       S5M8767_REG_LDO25CTRL,
+       S5M8767_REG_LDO26CTRL,
+       S5M8767_REG_LDO27CTRL,
+       S5M8767_REG_LDO28CTRL,
+};
+
+/* S5M8767 regulator ids */
+enum s5m8767_regulators {
+       S5M8767_LDO1,
+       S5M8767_LDO2,
+       S5M8767_LDO3,
+       S5M8767_LDO4,
+       S5M8767_LDO5,
+       S5M8767_LDO6,
+       S5M8767_LDO7,
+       S5M8767_LDO8,
+       S5M8767_LDO9,
+       S5M8767_LDO10,
+       S5M8767_LDO11,
+       S5M8767_LDO12,
+       S5M8767_LDO13,
+       S5M8767_LDO14,
+       S5M8767_LDO15,
+       S5M8767_LDO16,
+       S5M8767_LDO17,
+       S5M8767_LDO18,
+       S5M8767_LDO19,
+       S5M8767_LDO20,
+       S5M8767_LDO21,
+       S5M8767_LDO22,
+       S5M8767_LDO23,
+       S5M8767_LDO24,
+       S5M8767_LDO25,
+       S5M8767_LDO26,
+       S5M8767_LDO27,
+       S5M8767_LDO28,
+       S5M8767_BUCK1,
+       S5M8767_BUCK2,
+       S5M8767_BUCK3,
+       S5M8767_BUCK4,
+       S5M8767_BUCK5,
+       S5M8767_BUCK6,
+       S5M8767_BUCK7,
+       S5M8767_BUCK8,
+       S5M8767_BUCK9,
+       S5M8767_AP_EN32KHZ,
+       S5M8767_CP_EN32KHZ,
+
+       S5M8767_REG_MAX,
+};
+
+#define S5M8767_ENCTRL_SHIFT  6
+
+#endif /* __LINUX_MFD_S5M8767_H */
index 6c4c478e21a4f79d33577db36faba4e9f37f3cd3..9bf8767818b450003dface8acf7585d4ce809059 100644 (file)
@@ -807,6 +807,7 @@ struct tps65910_board {
        int irq_base;
        int vmbch_threshold;
        int vmbch2_threshold;
+       bool en_ck32k_xtal;
        bool en_dev_slp;
        struct tps65910_sleep_keepon_data *slp_keepon;
        bool en_gpio_sleep[TPS6591X_MAX_NUM_GPIO];
index 6659487c31e7a010c96bcef991f05b546fa103d1..eaad49f7c130f0d148b0a7679bd4a6195194e422 100644 (file)
 #define TWL6040_CELLS                  2
 
 #define TWL6040_REV_ES1_0              0x00
-#define TWL6040_REV_ES1_1              0x01
-#define TWL6040_REV_ES1_2              0x02
+#define TWL6040_REV_ES1_1              0x01 /* Rev ES1.1 and ES1.2 */
+#define TWL6040_REV_ES1_3              0x02
+#define TWL6041_REV_ES2_0              0x10
 
 #define TWL6040_IRQ_TH                 0
 #define TWL6040_IRQ_PLUG               1
@@ -206,7 +207,6 @@ struct twl6040 {
        struct regmap *regmap;
        struct regulator_bulk_data supplies[2]; /* supplies for vio, v2v1 */
        struct mutex mutex;
-       struct mutex io_mutex;
        struct mutex irq_mutex;
        struct mfd_cell cells[TWL6040_CELLS];
        struct completion ready;
index 9192b6404a7347d5b6e665925a421cf1024d3c24..509481d9cf195b636ff3003ca89498b5697e3f00 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/mutex.h>
 #include <linux/interrupt.h>
 #include <linux/completion.h>
+#include <linux/regmap.h>
 
 #include <linux/mfd/wm8350/audio.h>
 #include <linux/mfd/wm8350/gpio.h>
@@ -66,6 +67,9 @@
 
 #define WM8350_MAX_REGISTER                     0xFF
 
+#define WM8350_UNLOCK_KEY              0x0013
+#define WM8350_LOCK_KEY                        0x0000
+
 /*
  * Field Definitions.
  */
 
 #define WM8350_NUM_IRQ_REGS 7
 
-struct wm8350_reg_access {
-       u16 readable;           /* Mask of readable bits */
-       u16 writable;           /* Mask of writable bits */
-       u16 vol;                /* Mask of volatile bits */
-};
-extern const struct wm8350_reg_access wm8350_reg_io_map[];
-extern const u16 wm8350_mode0_defaults[];
-extern const u16 wm8350_mode1_defaults[];
-extern const u16 wm8350_mode2_defaults[];
-extern const u16 wm8350_mode3_defaults[];
-extern const u16 wm8351_mode0_defaults[];
-extern const u16 wm8351_mode1_defaults[];
-extern const u16 wm8351_mode2_defaults[];
-extern const u16 wm8351_mode3_defaults[];
-extern const u16 wm8352_mode0_defaults[];
-extern const u16 wm8352_mode1_defaults[];
-extern const u16 wm8352_mode2_defaults[];
-extern const u16 wm8352_mode3_defaults[];
+extern const struct regmap_config wm8350_regmap;
 
 struct wm8350;
-struct regmap;
 
 struct wm8350_hwmon {
        struct platform_device *pdev;
@@ -614,7 +600,7 @@ struct wm8350 {
 
        /* device IO */
        struct regmap *regmap;
-       u16 *reg_cache;
+       bool unlocked;
 
        struct mutex auxadc_mutex;
        struct completion auxadc_done;
index 893267bb622932d9299f6f81f87c5d87ef0d0fe0..f0361c031927417ee9af47ef0a2be55e718e344d 100644 (file)
@@ -141,6 +141,7 @@ struct wm8994_pdata {
        struct wm8994_ldo_pdata ldo[WM8994_NUM_LDO];
 
        int irq_base;  /** Base IRQ number for WM8994, required for IRQs */
+       unsigned long irq_flags; /** user irq flags */
 
         int num_drc_cfgs;
         struct wm8994_drc_cfg *drc_cfgs;
index 855c337b20c35cb22ef0a3fa6213cfb82a3a90b0..ce7e6671968b791194df3c35d180443dbd615869 100644 (file)
@@ -15,7 +15,7 @@ extern int migrate_page(struct address_space *,
 extern int migrate_pages(struct list_head *l, new_page_t x,
                        unsigned long private, bool offlining,
                        enum migrate_mode mode);
-extern int migrate_huge_pages(struct list_head *l, new_page_t x,
+extern int migrate_huge_page(struct page *, new_page_t x,
                        unsigned long private, bool offlining,
                        enum migrate_mode mode);
 
@@ -36,7 +36,7 @@ static inline void putback_lru_pages(struct list_head *l) {}
 static inline int migrate_pages(struct list_head *l, new_page_t x,
                unsigned long private, bool offlining,
                enum migrate_mode mode) { return -ENOSYS; }
-static inline int migrate_huge_pages(struct list_head *l, new_page_t x,
+static inline int migrate_huge_page(struct page *page, new_page_t x,
                unsigned long private, bool offlining,
                enum migrate_mode mode) { return -ENOSYS; }
 
index f9f279cf5b1bd558520aa23757cb53e0928a18a3..311be906b57d8498d5c3529d0e42de0996a0c1ec 100644 (file)
@@ -805,6 +805,17 @@ static inline void *page_rmapping(struct page *page)
        return (void *)((unsigned long)page->mapping & ~PAGE_MAPPING_FLAGS);
 }
 
+extern struct address_space *__page_file_mapping(struct page *);
+
+static inline
+struct address_space *page_file_mapping(struct page *page)
+{
+       if (unlikely(PageSwapCache(page)))
+               return __page_file_mapping(page);
+
+       return page->mapping;
+}
+
 static inline int PageAnon(struct page *page)
 {
        return ((unsigned long)page->mapping & PAGE_MAPPING_ANON) != 0;
@@ -821,6 +832,20 @@ static inline pgoff_t page_index(struct page *page)
        return page->index;
 }
 
+extern pgoff_t __page_file_index(struct page *page);
+
+/*
+ * Return the file index of the page. Regular pagecache pages use ->index
+ * whereas swapcache pages use swp_offset(->private)
+ */
+static inline pgoff_t page_file_index(struct page *page)
+{
+       if (unlikely(PageSwapCache(page)))
+               return __page_file_index(page);
+
+       return page->index;
+}
+
 /*
  * Return true if this page is mapped into pagetables.
  */
@@ -994,6 +1019,10 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                        struct page **pages, struct vm_area_struct **vmas);
 int get_user_pages_fast(unsigned long start, int nr_pages, int write,
                        struct page **pages);
+struct kvec;
+int get_kernel_pages(const struct kvec *iov, int nr_pages, int write,
+                       struct page **pages);
+int get_kernel_page(unsigned long start, int write, struct page **pages);
 struct page *get_dump_page(unsigned long addr);
 
 extern int try_to_release_page(struct page * page, gfp_t gfp_mask);
@@ -1331,6 +1360,7 @@ void warn_alloc_failed(gfp_t gfp_mask, int order, const char *fmt, ...);
 extern void setup_per_cpu_pageset(void);
 
 extern void zone_pcp_update(struct zone *zone);
+extern void zone_pcp_reset(struct zone *zone);
 
 /* nommu.c */
 extern atomic_long_t mmap_pages_allocated;
@@ -1411,6 +1441,7 @@ extern void truncate_inode_pages_range(struct address_space *,
 
 /* generic vm_area_ops exported for stackable file systems */
 extern int filemap_fault(struct vm_area_struct *, struct vm_fault *);
+extern int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
 
 /* mm/page-writeback.c */
 int write_one_page(struct page *page, int wait);
@@ -1528,6 +1559,7 @@ void vm_stat_account(struct mm_struct *, unsigned long, struct file *, long);
 static inline void vm_stat_account(struct mm_struct *mm,
                        unsigned long flags, struct file *file, long pages)
 {
+       mm->total_vm += pages;
 }
 #endif /* CONFIG_PROC_FS */
 
index 704a626d94a08adc03b32b756d2d0a1c9a1f40cf..bf7867200b95b488fd960c95f7200af5837286bb 100644 (file)
@@ -53,7 +53,16 @@ struct page {
        struct {
                union {
                        pgoff_t index;          /* Our offset within mapping. */
-                       void *freelist;         /* slub first free object */
+                       void *freelist;         /* slub/slob first free object */
+                       bool pfmemalloc;        /* If set by the page allocator,
+                                                * ALLOC_NO_WATERMARKS was set
+                                                * and the low watermark was not
+                                                * met implying that the system
+                                                * is under some pressure. The
+                                                * caller should try ensure
+                                                * this page is only used to
+                                                * free other pages.
+                                                */
                };
 
                union {
@@ -91,11 +100,12 @@ struct page {
                                         */
                                        atomic_t _mapcount;
 
-                                       struct {
+                                       struct { /* SLUB */
                                                unsigned inuse:16;
                                                unsigned objects:15;
                                                unsigned frozen:1;
                                        };
+                                       int units;      /* SLOB */
                                };
                                atomic_t _count;                /* Usage count, see below. */
                        };
@@ -117,6 +127,12 @@ struct page {
                        short int pobjects;
 #endif
                };
+
+               struct list_head list;  /* slobs list of pages */
+               struct {                /* slab fields */
+                       struct kmem_cache *slab_cache;
+                       struct slab *slab_page;
+               };
        };
 
        /* Remainder is not double word aligned */
index 458988bd55a1b3322dc266f120eca7e20e3c4c91..2daa54f55db7bc1a59d894937685fead6fa01bc2 100644 (file)
@@ -201,7 +201,7 @@ struct zone_reclaim_stat {
 struct lruvec {
        struct list_head lists[NR_LRU_LISTS];
        struct zone_reclaim_stat reclaim_stat;
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
        struct zone *zone;
 #endif
 };
@@ -209,7 +209,6 @@ struct lruvec {
 /* Mask used at gathering information at once (see memcontrol.c) */
 #define LRU_ALL_FILE (BIT(LRU_INACTIVE_FILE) | BIT(LRU_ACTIVE_FILE))
 #define LRU_ALL_ANON (BIT(LRU_INACTIVE_ANON) | BIT(LRU_ACTIVE_ANON))
-#define LRU_ALL_EVICTABLE (LRU_ALL_FILE | LRU_ALL_ANON)
 #define LRU_ALL             ((1 << NR_LRU_LISTS) - 1)
 
 /* Isolate clean file */
@@ -369,6 +368,10 @@ struct zone {
         */
        spinlock_t              lock;
        int                     all_unreclaimable; /* All pages pinned */
+#if defined CONFIG_COMPACTION || defined CONFIG_CMA
+       /* pfn where the last incremental compaction isolated free pages */
+       unsigned long           compact_cached_free_pfn;
+#endif
 #ifdef CONFIG_MEMORY_HOTPLUG
        /* see spanned/present_pages for more description */
        seqlock_t               span_seqlock;
@@ -475,6 +478,14 @@ struct zone {
         * rarely used fields:
         */
        const char              *name;
+#ifdef CONFIG_MEMORY_ISOLATION
+       /*
+        * the number of MIGRATE_ISOLATE *pageblock*.
+        * We need this for free page counting. Look at zone_watermark_ok_safe.
+        * It's protected by zone->lock
+        */
+       int             nr_pageblock_isolate;
+#endif
 } ____cacheline_internodealigned_in_smp;
 
 typedef enum {
@@ -671,7 +682,7 @@ typedef struct pglist_data {
        int nr_zones;
 #ifdef CONFIG_FLAT_NODE_MEM_MAP        /* means !SPARSEMEM */
        struct page *node_mem_map;
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
        struct page_cgroup *node_page_cgroup;
 #endif
 #endif
@@ -694,6 +705,7 @@ typedef struct pglist_data {
                                             range, including holes */
        int node_id;
        wait_queue_head_t kswapd_wait;
+       wait_queue_head_t pfmemalloc_wait;
        struct task_struct *kswapd;     /* Protected by lock_memory_hotplug() */
        int kswapd_max_order;
        enum zone_type classzone_idx;
@@ -718,7 +730,7 @@ typedef struct pglist_data {
 #include <linux/memory_hotplug.h>
 
 extern struct mutex zonelists_mutex;
-void build_all_zonelists(void *data);
+void build_all_zonelists(pg_data_t *pgdat, struct zone *zone);
 void wakeup_kswapd(struct zone *zone, int order, enum zone_type classzone_idx);
 bool zone_watermark_ok(struct zone *z, int order, unsigned long mark,
                int classzone_idx, int alloc_flags);
@@ -736,7 +748,7 @@ extern void lruvec_init(struct lruvec *lruvec, struct zone *zone);
 
 static inline struct zone *lruvec_zone(struct lruvec *lruvec)
 {
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
        return lruvec->zone;
 #else
        return container_of(lruvec, struct zone, lruvec);
@@ -773,7 +785,7 @@ extern int movable_zone;
 
 static inline int zone_movable_is_highmem(void)
 {
-#if defined(CONFIG_HIGHMEM) && defined(CONFIG_HAVE_MEMBLOCK_NODE)
+#if defined(CONFIG_HIGHMEM) && defined(CONFIG_HAVE_MEMBLOCK_NODE_MAP)
        return movable_zone == ZONE_HIGHMEM;
 #else
        return 0;
@@ -1052,7 +1064,7 @@ struct mem_section {
 
        /* See declaration of similar field in struct zone */
        unsigned long *pageblock_flags;
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
        /*
         * If !SPARSEMEM, pgdat doesn't have page_cgroup pointer. We use
         * section. (see memcontrol.h/page_cgroup.h about this.)
index d2ef8b34b96722078c5ab7a3914579d5b50a8ca6..4bf19d8174ed49301470eff1c6593ad1192a6e31 100644 (file)
@@ -67,6 +67,7 @@ extern int kern_path(const char *, unsigned, struct path *);
 
 extern struct dentry *kern_path_create(int, const char *, struct path *, int);
 extern struct dentry *user_path_create(int, const char __user *, struct path *, int);
+extern void done_path_create(struct path *, struct dentry *);
 extern struct dentry *kern_path_locked(const char *, struct path *);
 extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
                           const char *, unsigned int, struct path *);
index eb06e58bed0b2dc9c7e2d6a9b166d69380556e58..1d6ab69c1f3f1f5f338a88e08566f481bf4e4995 100644 (file)
@@ -1300,6 +1300,8 @@ struct net_device {
        /* for setting kernel sock attribute on TCP connection setup */
 #define GSO_MAX_SIZE           65536
        unsigned int            gso_max_size;
+#define GSO_MAX_SEGS           65535
+       u16                     gso_max_segs;
 
 #ifdef CONFIG_DCB
        /* Data Center Bridging netlink ops */
@@ -2244,8 +2246,6 @@ extern void netif_carrier_on(struct net_device *dev);
 
 extern void netif_carrier_off(struct net_device *dev);
 
-extern void netif_notify_peers(struct net_device *dev);
-
 /**
  *     netif_dormant_on - mark device as dormant.
  *     @dev: network device
@@ -2594,8 +2594,7 @@ extern void               __dev_set_rx_mode(struct net_device *dev);
 extern int             dev_set_promiscuity(struct net_device *dev, int inc);
 extern int             dev_set_allmulti(struct net_device *dev, int inc);
 extern void            netdev_state_change(struct net_device *dev);
-extern int             netdev_bonding_change(struct net_device *dev,
-                                             unsigned long event);
+extern void            netdev_notify_peers(struct net_device *dev);
 extern void            netdev_features_change(struct net_device *dev);
 /* Load a device via the kmod */
 extern void            dev_load(struct net *net, const char *name);
index b23cfc120edb46c5b285de63edeff61fb03242ec..1f8fc7f9bcd8b8eb0d07588ba671b9327e53fe90 100644 (file)
@@ -191,7 +191,7 @@ struct nfs_inode {
        struct hlist_head       silly_list;
        wait_queue_head_t       waitqueue;
 
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
        struct nfs4_cached_acl  *nfs4_acl;
         /* NFSv4 state */
        struct list_head        open_states;
@@ -427,12 +427,8 @@ extern __be32 root_nfs_parse_addr(char *name); /*__init*/
 /*
  * linux/fs/nfs/file.c
  */
-extern const struct inode_operations nfs_file_inode_operations;
-#ifdef CONFIG_NFS_V3
-extern const struct inode_operations nfs3_file_inode_operations;
-#endif /* CONFIG_NFS_V3 */
 extern const struct file_operations nfs_file_operations;
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 extern const struct file_operations nfs4_file_operations;
 #endif /* CONFIG_NFS_V4 */
 extern const struct address_space_operations nfs_file_aops;
@@ -477,18 +473,14 @@ extern ssize_t nfs_direct_IO(int, struct kiocb *, const struct iovec *, loff_t,
                        unsigned long);
 extern ssize_t nfs_file_direct_read(struct kiocb *iocb,
                        const struct iovec *iov, unsigned long nr_segs,
-                       loff_t pos);
+                       loff_t pos, bool uio);
 extern ssize_t nfs_file_direct_write(struct kiocb *iocb,
                        const struct iovec *iov, unsigned long nr_segs,
-                       loff_t pos);
+                       loff_t pos, bool uio);
 
 /*
  * linux/fs/nfs/dir.c
  */
-extern const struct inode_operations nfs_dir_inode_operations;
-#ifdef CONFIG_NFS_V3
-extern const struct inode_operations nfs3_dir_inode_operations;
-#endif /* CONFIG_NFS_V3 */
 extern const struct file_operations nfs_dir_operations;
 extern const struct dentry_operations nfs_dentry_operations;
 
@@ -546,7 +538,7 @@ extern void nfs_writeback_done(struct rpc_task *, struct nfs_write_data *);
 extern int nfs_wb_all(struct inode *inode);
 extern int nfs_wb_page(struct inode *inode, struct page* page);
 extern int nfs_wb_page_cancel(struct inode *inode, struct page* page);
-#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
+#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4)
 extern int  nfs_commit_inode(struct inode *, int);
 extern struct nfs_commit_data *nfs_commitdata_alloc(void);
 extern void nfs_commit_free(struct nfs_commit_data *data);
index f58325a1d8fbe290fb8a7eb6e4ddc060ef553f91..310c63c8ab2cd374ff4d78c526359241cb4758cc 100644 (file)
@@ -48,11 +48,12 @@ struct nfs_client {
        struct rpc_clnt *       cl_rpcclient;
        const struct nfs_rpc_ops *rpc_ops;      /* NFS protocol vector */
        int                     cl_proto;       /* Network transport protocol */
+       struct nfs_subversion * cl_nfs_mod;     /* pointer to nfs version module */
 
        u32                     cl_minorversion;/* NFSv4 minorversion */
        struct rpc_cred         *cl_machine_cred;
 
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
        u64                     cl_clientid;    /* constant */
        nfs4_verifier           cl_confirm;     /* Clientid verifier */
        unsigned long           cl_state;
@@ -69,10 +70,9 @@ struct nfs_client {
        struct idmap *          cl_idmap;
 
        /* Our own IP address, as a null-terminated string.
-        * This is used to generate the clientid, and the callback address.
+        * This is used to generate the mv0 callback address.
         */
        char                    cl_ipaddr[48];
-       unsigned char           cl_id_uniquifier;
        u32                     cl_cb_ident;    /* v4.0 callback identifier */
        const struct nfs4_minor_version_ops *cl_mvops;
 
@@ -138,7 +138,7 @@ struct nfs_server {
 #endif
 
        u32                     pnfs_blksize;   /* layout_blksize attr */
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
        u32                     attr_bitmask[3];/* V4 bitmask representing the set
                                                   of attributes supported on this
                                                   filesystem */
@@ -201,7 +201,7 @@ struct nfs_server {
 #define NFS4_MAX_SLOT_TABLE (256U)
 #define NFS4_NO_SLOT ((u32)-1)
 
-#if defined(CONFIG_NFS_V4)
+#if IS_ENABLED(CONFIG_NFS_V4)
 
 /* Sessions */
 #define SLOT_TABLE_SZ DIV_ROUND_UP(NFS4_MAX_SLOT_TABLE, 8*sizeof(long))
index 7eed2012d288926a6317e53ead4eb7880796c7ca..ece91c57ad79d109c2c95c30d64f390201db0822 100644 (file)
@@ -69,7 +69,7 @@ struct nfs_server;
 struct nfs_fattr;
 struct nfs4_string;
 
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 int nfs_idmap_init(void);
 void nfs_idmap_quit(void);
 #else
index 88d166b555e8539fa8ec5cb896c56ecea7417bdc..880805774f9f105425a7eade9721b9965f7d43c9 100644 (file)
@@ -42,7 +42,7 @@ struct nfs_page {
                                wb_bytes;       /* Length of request */
        struct kref             wb_kref;        /* reference count */
        unsigned long           wb_flags;
-       struct nfs_writeverf    wb_verf;        /* Commit cookie */
+       struct nfs_write_verifier       wb_verf;        /* Commit cookie */
 };
 
 struct nfs_pageio_descriptor;
index d3b7c18b18f4e068997789bda6cc25be52720f8a..00485e084394d4ca1a5782b3cd54fc63e9b06acd 100644 (file)
@@ -514,9 +514,13 @@ struct nfs_writeargs {
        struct nfs4_sequence_args       seq_args;
 };
 
+struct nfs_write_verifier {
+       char                    data[8];
+};
+
 struct nfs_writeverf {
+       struct nfs_write_verifier verifier;
        enum nfs3_stable_how    committed;
-       __be32                  verifier[2];
 };
 
 struct nfs_writeres {
@@ -820,7 +824,7 @@ struct nfs3_getaclres {
        struct posix_acl *      acl_default;
 };
 
-#ifdef CONFIG_NFS_V4
+#if IS_ENABLED(CONFIG_NFS_V4)
 
 typedef u64 clientid4;
 
@@ -1349,6 +1353,10 @@ struct nfs_renamedata {
 struct nfs_access_entry;
 struct nfs_client;
 struct rpc_timeout;
+struct nfs_subversion;
+struct nfs_mount_info;
+struct nfs_client_initdata;
+struct nfs_pageio_descriptor;
 
 /*
  * RPC procedure vector for NFSv2/NFSv3 demuxing
@@ -1364,6 +1372,8 @@ struct nfs_rpc_ops {
                            struct nfs_fsinfo *);
        struct vfsmount *(*submount) (struct nfs_server *, struct dentry *,
                                      struct nfs_fh *, struct nfs_fattr *);
+       struct dentry *(*try_mount) (int, const char *, struct nfs_mount_info *,
+                                    struct nfs_subversion *);
        int     (*getattr) (struct nfs_server *, struct nfs_fh *,
                            struct nfs_fattr *);
        int     (*setattr) (struct dentry *, struct nfs_fattr *,
@@ -1402,9 +1412,13 @@ struct nfs_rpc_ops {
        int     (*set_capabilities)(struct nfs_server *, struct nfs_fh *);
        int     (*decode_dirent)(struct xdr_stream *, struct nfs_entry *, int);
        void    (*read_setup)   (struct nfs_read_data *, struct rpc_message *);
+       void    (*read_pageio_init)(struct nfs_pageio_descriptor *, struct inode *,
+                                   const struct nfs_pgio_completion_ops *);
        void    (*read_rpc_prepare)(struct rpc_task *, struct nfs_read_data *);
        int     (*read_done)  (struct rpc_task *, struct nfs_read_data *);
        void    (*write_setup)  (struct nfs_write_data *, struct rpc_message *);
+       void    (*write_pageio_init)(struct nfs_pageio_descriptor *, struct inode *, int,
+                                    const struct nfs_pgio_completion_ops *);
        void    (*write_rpc_prepare)(struct rpc_task *, struct nfs_write_data *);
        int     (*write_done)  (struct rpc_task *, struct nfs_write_data *);
        void    (*commit_setup) (struct nfs_commit_data *, struct rpc_message *);
@@ -1418,9 +1432,16 @@ struct nfs_rpc_ops {
                                struct nfs_open_context *ctx,
                                int open_flags,
                                struct iattr *iattr);
+       int (*have_delegation)(struct inode *, fmode_t);
+       int (*return_delegation)(struct inode *);
+       struct nfs_client *(*alloc_client) (const struct nfs_client_initdata *);
        struct nfs_client *
                (*init_client) (struct nfs_client *, const struct rpc_timeout *,
                                const char *, rpc_authflavor_t);
+       void    (*free_client) (struct nfs_client *);
+       struct nfs_server *(*create_server)(struct nfs_mount_info *, struct nfs_subversion *);
+       struct nfs_server *(*clone_server)(struct nfs_server *, struct nfs_fh *,
+                                          struct nfs_fattr *, rpc_authflavor_t);
 };
 
 /*
index ce4743a260152724e8609fb96883177ad92c4875..fa63048fecff82e8e5849c1487d583c49194313c 100644 (file)
@@ -143,6 +143,7 @@ typedef struct svc_fh {
        int                     fh_maxsize;     /* max size for fh_handle */
 
        unsigned char           fh_locked;      /* inode locked by us */
+       unsigned char           fh_want_write;  /* remount protection taken */
 
 #ifdef CONFIG_NFSD_V3
        unsigned char           fh_post_saved;  /* post-op attrs saved */
index 89bd4a4dcfb44e379176a11e5fe6d9516025ecaf..98755767c7b067098443397aef09ecb145f0cd3e 100644 (file)
@@ -293,7 +293,7 @@ struct nilfs_dir_entry {
        __le64  inode;                  /* Inode number */
        __le16  rec_len;                /* Directory entry length */
        __u8    name_len;               /* Name length */
-       __u8    file_type;
+       __u8    file_type;              /* Dir entry type (file, dir, etc) */
        char    name[NILFS_NAME_LEN];   /* File name */
        char    pad;
 };
@@ -395,7 +395,7 @@ union nilfs_binfo {
 };
 
 /**
- * struct nilfs_segment_summary - segment summary
+ * struct nilfs_segment_summary - segment summary header
  * @ss_datasum: checksum of data
  * @ss_sumsum: checksum of segment summary
  * @ss_magic: magic number
@@ -683,9 +683,9 @@ struct nilfs_sufile_header {
 
 /**
  * nilfs_suinfo - segment usage information
- * @sui_lastmod:
- * @sui_nblocks:
- * @sui_flags:
+ * @sui_lastmod: timestamp of last modification
+ * @sui_nblocks: number of written blocks in segment
+ * @sui_flags: segment usage flags
  */
 struct nilfs_suinfo {
        __u64 sui_lastmod;
@@ -716,9 +716,10 @@ enum {
 };
 
 /**
- * struct nilfs_cpmode -
- * @cc_cno:
- * @cc_mode:
+ * struct nilfs_cpmode - change checkpoint mode structure
+ * @cm_cno: checkpoint number
+ * @cm_mode: mode of checkpoint
+ * @cm_pad: padding
  */
 struct nilfs_cpmode {
        __u64 cm_cno;
@@ -728,11 +729,11 @@ struct nilfs_cpmode {
 
 /**
  * struct nilfs_argv - argument vector
- * @v_base:
- * @v_nmembs:
- * @v_size:
- * @v_flags:
- * @v_index:
+ * @v_base: pointer on data array from userspace
+ * @v_nmembs: number of members in data array
+ * @v_size: size of data array in bytes
+ * @v_flags: flags
+ * @v_index: start number of target data items
  */
 struct nilfs_argv {
        __u64 v_base;
@@ -743,9 +744,9 @@ struct nilfs_argv {
 };
 
 /**
- * struct nilfs_period -
- * @p_start:
- * @p_end:
+ * struct nilfs_period - period of checkpoint numbers
+ * @p_start: start checkpoint number (inclusive)
+ * @p_end: end checkpoint number (exclusive)
  */
 struct nilfs_period {
        __u64 p_start;
@@ -753,7 +754,7 @@ struct nilfs_period {
 };
 
 /**
- * struct nilfs_cpstat -
+ * struct nilfs_cpstat - checkpoint statistics
  * @cs_cno: checkpoint number
  * @cs_ncps: number of checkpoints
  * @cs_nsss: number of snapshots
@@ -765,7 +766,7 @@ struct nilfs_cpstat {
 };
 
 /**
- * struct nilfs_sustat -
+ * struct nilfs_sustat - segment usage statistics
  * @ss_nsegs: number of segments
  * @ss_ncleansegs: number of clean segments
  * @ss_ndirtysegs: number of dirty segments
@@ -784,10 +785,10 @@ struct nilfs_sustat {
 
 /**
  * struct nilfs_vinfo - virtual block number information
- * @vi_vblocknr:
- * @vi_start:
- * @vi_end:
- * @vi_blocknr:
+ * @vi_vblocknr: virtual block number
+ * @vi_start: start checkpoint number (inclusive)
+ * @vi_end: end checkpoint number (exclusive)
+ * @vi_blocknr: disk block number
  */
 struct nilfs_vinfo {
        __u64 vi_vblocknr;
@@ -797,7 +798,15 @@ struct nilfs_vinfo {
 };
 
 /**
- * struct nilfs_vdesc -
+ * struct nilfs_vdesc - descriptor of virtual block number
+ * @vd_ino: inode number
+ * @vd_cno: checkpoint number
+ * @vd_vblocknr: virtual block number
+ * @vd_period: period of checkpoint numbers
+ * @vd_blocknr: disk block number
+ * @vd_offset: logical block offset inside a file
+ * @vd_flags: flags (data or node block)
+ * @vd_pad: padding
  */
 struct nilfs_vdesc {
        __u64 vd_ino;
@@ -811,7 +820,13 @@ struct nilfs_vdesc {
 };
 
 /**
- * struct nilfs_bdesc -
+ * struct nilfs_bdesc - descriptor of disk block number
+ * @bd_ino: inode number
+ * @bd_oblocknr: disk block address (for skipping dead blocks)
+ * @bd_blocknr: disk block address
+ * @bd_offset: logical block offset inside a file
+ * @bd_level: level in the b-tree organization
+ * @bd_pad: padding
  */
 struct nilfs_bdesc {
        __u64 bd_ino;
index 0e9cf9eec08595d2432d9ae9b16f703475b54d07..5919ee33f2b7b2d930fa3f6dd877984be5ed5e0d 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/kref.h>
 #include <linux/mod_devicetable.h>
 #include <linux/spinlock.h>
+#include <linux/topology.h>
 
 #include <asm/byteorder.h>
 #include <asm/errno.h>
@@ -158,11 +159,6 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size)
 
 #define OF_BAD_ADDR    ((u64)-1)
 
-#ifndef of_node_to_nid
-static inline int of_node_to_nid(struct device_node *np) { return -1; }
-#define of_node_to_nid of_node_to_nid
-#endif
-
 static inline const char* of_node_full_name(struct device_node *np)
 {
        return np ? np->full_name : "<no-node>";
@@ -386,6 +382,13 @@ static inline int of_property_read_u64(const struct device_node *np,
        return -ENOSYS;
 }
 
+static inline int of_property_match_string(struct device_node *np,
+                                          const char *propname,
+                                          const char *string)
+{
+       return -ENOSYS;
+}
+
 static inline struct device_node *of_parse_phandle(struct device_node *np,
                                                   const char *phandle_name,
                                                   int index)
@@ -393,6 +396,15 @@ static inline struct device_node *of_parse_phandle(struct device_node *np,
        return NULL;
 }
 
+static inline int of_parse_phandle_with_args(struct device_node *np,
+                                            const char *list_name,
+                                            const char *cells_name,
+                                            int index,
+                                            struct of_phandle_args *out_args)
+{
+       return -ENOSYS;
+}
+
 static inline int of_alias_get_id(struct device_node *np, const char *stem)
 {
        return -ENOSYS;
@@ -411,6 +423,15 @@ static inline int of_machine_is_compatible(const char *compat)
        while (0)
 #endif /* CONFIG_OF */
 
+#ifndef of_node_to_nid
+static inline int of_node_to_nid(struct device_node *np)
+{
+       return numa_node_id();
+}
+
+#define of_node_to_nid of_node_to_nid
+#endif
+
 /**
  * of_property_read_bool - Findfrom a property
  * @np:                device node from which the property value is to be read.
diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h
new file mode 100644 (file)
index 0000000..eb475a8
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * OMAP DMA Engine support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __LINUX_OMAP_DMA_H
+#define __LINUX_OMAP_DMA_H
+
+struct dma_chan;
+
+#if defined(CONFIG_DMA_OMAP) || defined(CONFIG_DMA_OMAP_MODULE)
+bool omap_dma_filter_fn(struct dma_chan *, void *);
+#else
+static inline bool omap_dma_filter_fn(struct dma_chan *c, void *d)
+{
+       return false;
+}
+#endif
+
+#endif
index e4c29bc72e70297af00eb276538840e72b163eda..49a3031fda50d1156bf0f67e77fea5ae2161537f 100644 (file)
@@ -40,15 +40,36 @@ enum oom_constraint {
        CONSTRAINT_MEMCG,
 };
 
+enum oom_scan_t {
+       OOM_SCAN_OK,            /* scan thread and find its badness */
+       OOM_SCAN_CONTINUE,      /* do not consider thread for oom kill */
+       OOM_SCAN_ABORT,         /* abort the iteration and return */
+       OOM_SCAN_SELECT,        /* always select this thread first */
+};
+
 extern void compare_swap_oom_score_adj(int old_val, int new_val);
 extern int test_set_oom_score_adj(int new_val);
 
 extern unsigned long oom_badness(struct task_struct *p,
                struct mem_cgroup *memcg, const nodemask_t *nodemask,
                unsigned long totalpages);
+extern void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
+                            unsigned int points, unsigned long totalpages,
+                            struct mem_cgroup *memcg, nodemask_t *nodemask,
+                            const char *message);
+
 extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags);
 extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags);
 
+extern void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask,
+                              int order, const nodemask_t *nodemask);
+
+extern enum oom_scan_t oom_scan_process_thread(struct task_struct *task,
+               unsigned long totalpages, const nodemask_t *nodemask,
+               bool force_kill);
+extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
+                                    int order);
+
 extern void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask,
                int order, nodemask_t *mask, bool force_kill);
 extern int register_oom_notifier(struct notifier_block *nb);
diff --git a/include/linux/packet_diag.h b/include/linux/packet_diag.h
new file mode 100644 (file)
index 0000000..ea2e892
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef __PACKET_DIAG_H__
+#define __PACKET_DIAG_H__
+
+#include <linux/types.h>
+
+struct packet_diag_req {
+       __u8    sdiag_family;
+       __u8    sdiag_protocol;
+       __u16   pad;
+       __u32   pdiag_ino;
+       __u32   pdiag_show;
+       __u32   pdiag_cookie[2];
+};
+
+#define PACKET_SHOW_INFO       0x00000001 /* Basic packet_sk information */
+#define PACKET_SHOW_MCLIST     0x00000002 /* A set of packet_diag_mclist-s */
+
+struct packet_diag_msg {
+       __u8    pdiag_family;
+       __u8    pdiag_type;
+       __u16   pdiag_num;
+
+       __u32   pdiag_ino;
+       __u32   pdiag_cookie[2];
+};
+
+enum {
+       PACKET_DIAG_INFO,
+       PACKET_DIAG_MCLIST,
+
+       PACKET_DIAG_MAX,
+};
+
+struct packet_diag_info {
+       __u32   pdi_index;
+       __u32   pdi_version;
+       __u32   pdi_reserve;
+       __u32   pdi_copy_thresh;
+       __u32   pdi_tstamp;
+       __u32   pdi_flags;
+
+#define PDI_RUNNING    0x1
+#define PDI_AUXDATA    0x2
+#define PDI_ORIGDEV    0x4
+#define PDI_VNETHDR    0x8
+#define PDI_LOSS       0x10
+};
+
+struct packet_diag_mclist {
+       __u32   pdmc_index;
+       __u32   pdmc_count;
+       __u16   pdmc_type;
+       __u16   pdmc_alen;
+       __u8    pdmc_addr[MAX_ADDR_LEN];
+};
+
+#endif
index c88d2a9451af811bff67e3ee9c8d37c8ef691ef3..b5d13841604ee3614710cf6076cd0d29276bda4e 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/types.h>
 #include <linux/bug.h>
+#include <linux/mmdebug.h>
 #ifndef __GENERATING_BOUNDS_H
 #include <linux/mm_types.h>
 #include <generated/bounds.h>
@@ -453,6 +454,34 @@ static inline int PageTransTail(struct page *page)
 }
 #endif
 
+/*
+ * If network-based swap is enabled, sl*b must keep track of whether pages
+ * were allocated from pfmemalloc reserves.
+ */
+static inline int PageSlabPfmemalloc(struct page *page)
+{
+       VM_BUG_ON(!PageSlab(page));
+       return PageActive(page);
+}
+
+static inline void SetPageSlabPfmemalloc(struct page *page)
+{
+       VM_BUG_ON(!PageSlab(page));
+       SetPageActive(page);
+}
+
+static inline void __ClearPageSlabPfmemalloc(struct page *page)
+{
+       VM_BUG_ON(!PageSlab(page));
+       __ClearPageActive(page);
+}
+
+static inline void ClearPageSlabPfmemalloc(struct page *page)
+{
+       VM_BUG_ON(!PageSlab(page));
+       ClearPageActive(page);
+}
+
 #ifdef CONFIG_MMU
 #define __PG_MLOCKED           (1 << PG_mlocked)
 #else
index 3bdcab30ca4121e5067cb2eac291d039c6eee07e..105077aa7685c61dbb66cabc8473302283a0e3a5 100644 (file)
@@ -1,6 +1,11 @@
 #ifndef __LINUX_PAGEISOLATION_H
 #define __LINUX_PAGEISOLATION_H
 
+
+bool has_unmovable_pages(struct zone *zone, struct page *page, int count);
+void set_pageblock_migratetype(struct page *page, int migratetype);
+int move_freepages_block(struct zone *zone, struct page *page,
+                               int migratetype);
 /*
  * Changes migrate type in [start_pfn, end_pfn) to be MIGRATE_ISOLATE.
  * If specified range includes migrate types other than MOVABLE or CMA,
@@ -10,7 +15,7 @@
  * free all pages in the range. test_page_isolated() can be used for
  * test it.
  */
-extern int
+int
 start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
                         unsigned migratetype);
 
@@ -18,7 +23,7 @@ start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
  * Changes MIGRATE_ISOLATE to MIGRATE_MOVABLE.
  * target range is [start_pfn, end_pfn)
  */
-extern int
+int
 undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
                        unsigned migratetype);
 
@@ -30,8 +35,8 @@ int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn);
 /*
  * Internal functions. Changes pageblock's migrate type.
  */
-extern int set_migratetype_isolate(struct page *page);
-extern void unset_migratetype_isolate(struct page *page, unsigned migratetype);
+int set_migratetype_isolate(struct page *page);
+void unset_migratetype_isolate(struct page *page, unsigned migratetype);
 
 
 #endif
index a88cdba27809b490cc64b283e32d841ab7f0dd42..777a524716db9364d3f9a06f9357cacf36b2b418 100644 (file)
@@ -12,7 +12,7 @@ enum {
 #ifndef __GENERATING_BOUNDS_H
 #include <generated/bounds.h>
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
 #include <linux/bit_spinlock.h>
 
 /*
@@ -82,7 +82,7 @@ static inline void unlock_page_cgroup(struct page_cgroup *pc)
        bit_spin_unlock(PCG_LOCK, &pc->flags);
 }
 
-#else /* CONFIG_CGROUP_MEM_RES_CTLR */
+#else /* CONFIG_MEMCG */
 struct page_cgroup;
 
 static inline void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat)
@@ -102,11 +102,11 @@ static inline void __init page_cgroup_init_flatmem(void)
 {
 }
 
-#endif /* CONFIG_CGROUP_MEM_RES_CTLR */
+#endif /* CONFIG_MEMCG */
 
 #include <linux/swap.h>
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
 extern unsigned short swap_cgroup_cmpxchg(swp_entry_t ent,
                                        unsigned short old, unsigned short new);
 extern unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id);
@@ -138,7 +138,7 @@ static inline void swap_cgroup_swapoff(int type)
        return;
 }
 
-#endif /* CONFIG_CGROUP_MEM_RES_CTLR_SWAP */
+#endif /* CONFIG_MEMCG_SWAP */
 
 #endif /* !__GENERATING_BOUNDS_H */
 
index 7cfad3bbb0cc214d37c6312a86a0a317fde12edd..e42c762f0dc705aa29769b3e931c049361820b30 100644 (file)
@@ -286,6 +286,11 @@ static inline loff_t page_offset(struct page *page)
        return ((loff_t)page->index) << PAGE_CACHE_SHIFT;
 }
 
+static inline loff_t page_file_offset(struct page *page)
+{
+       return ((loff_t)page_file_index(page)) << PAGE_CACHE_SHIFT;
+}
+
 extern pgoff_t linear_hugepage_index(struct vm_area_struct *vma,
                                     unsigned long address);
 
index e11d1c0fc60fc582ef2fd988642d61796566df4c..ad1a427b5267b3faef96444289eacbaf62880f75 100644 (file)
@@ -160,4 +160,6 @@ void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *);
 long pipe_fcntl(struct file *, unsigned int, unsigned long arg);
 struct pipe_inode_info *get_pipe_info(struct file *file);
 
+int create_pipe_files(struct file **, int);
+
 #endif
diff --git a/include/linux/platform_data/i2c-nomadik.h b/include/linux/platform_data/i2c-nomadik.h
new file mode 100644 (file)
index 0000000..c2303c3
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2009 ST-Ericsson
+ *
+ * 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 __PDATA_I2C_NOMADIK_H
+#define __PDATA_I2C_NOMADIK_H
+
+enum i2c_freq_mode {
+       I2C_FREQ_MODE_STANDARD,         /* up to 100 Kb/s */
+       I2C_FREQ_MODE_FAST,             /* up to 400 Kb/s */
+       I2C_FREQ_MODE_HIGH_SPEED,       /* up to 3.4 Mb/s */
+       I2C_FREQ_MODE_FAST_PLUS,        /* up to 1 Mb/s */
+};
+
+/**
+ * struct nmk_i2c_controller - client specific controller configuration
+ * @clk_freq:  clock frequency for the operation mode
+ * @slsu:      Slave data setup time in ns.
+ *             The needed setup time for three modes of operation
+ *             are 250ns, 100ns and 10ns respectively thus leading
+ *             to the values of 14, 6, 2 for a 48 MHz i2c clk
+ * @tft:       Tx FIFO Threshold in bytes
+ * @rft:       Rx FIFO Threshold in bytes
+ * @timeout    Slave response timeout(ms)
+ * @sm:                speed mode
+ */
+struct nmk_i2c_controller {
+       unsigned long   clk_freq;
+       unsigned short  slsu;
+       unsigned char   tft;
+       unsigned char   rft;
+       int timeout;
+       enum i2c_freq_mode      sm;
+};
+
+#endif /* __PDATA_I2C_NOMADIK_H */
diff --git a/include/linux/platform_data/lp855x.h b/include/linux/platform_data/lp855x.h
new file mode 100644 (file)
index 0000000..cc76f1f
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * LP855x Backlight Driver
+ *
+ *                     Copyright (C) 2011 Texas Instruments
+ *
+ * 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 _LP855X_H
+#define _LP855X_H
+
+#define BL_CTL_SHFT    (0)
+#define BRT_MODE_SHFT  (1)
+#define BRT_MODE_MASK  (0x06)
+
+/* Enable backlight. Only valid when BRT_MODE=10(I2C only) */
+#define ENABLE_BL      (1)
+#define DISABLE_BL     (0)
+
+#define I2C_CONFIG(id) id ## _I2C_CONFIG
+#define PWM_CONFIG(id) id ## _PWM_CONFIG
+
+/* DEVICE CONTROL register - LP8550 */
+#define LP8550_PWM_CONFIG      (LP8550_PWM_ONLY << BRT_MODE_SHFT)
+#define LP8550_I2C_CONFIG      ((ENABLE_BL << BL_CTL_SHFT) | \
+                               (LP8550_I2C_ONLY << BRT_MODE_SHFT))
+
+/* DEVICE CONTROL register - LP8551 */
+#define LP8551_PWM_CONFIG      LP8550_PWM_CONFIG
+#define LP8551_I2C_CONFIG      LP8550_I2C_CONFIG
+
+/* DEVICE CONTROL register - LP8552 */
+#define LP8552_PWM_CONFIG      LP8550_PWM_CONFIG
+#define LP8552_I2C_CONFIG      LP8550_I2C_CONFIG
+
+/* DEVICE CONTROL register - LP8553 */
+#define LP8553_PWM_CONFIG      LP8550_PWM_CONFIG
+#define LP8553_I2C_CONFIG      LP8550_I2C_CONFIG
+
+/* DEVICE CONTROL register - LP8556 */
+#define LP8556_PWM_CONFIG      (LP8556_PWM_ONLY << BRT_MODE_SHFT)
+#define LP8556_COMB1_CONFIG    (LP8556_COMBINED1 << BRT_MODE_SHFT)
+#define LP8556_I2C_CONFIG      ((ENABLE_BL << BL_CTL_SHFT) | \
+                               (LP8556_I2C_ONLY << BRT_MODE_SHFT))
+#define LP8556_COMB2_CONFIG    (LP8556_COMBINED2 << BRT_MODE_SHFT)
+
+enum lp855x_chip_id {
+       LP8550,
+       LP8551,
+       LP8552,
+       LP8553,
+       LP8556,
+};
+
+enum lp855x_brightness_ctrl_mode {
+       PWM_BASED = 1,
+       REGISTER_BASED,
+};
+
+enum lp8550_brighntess_source {
+       LP8550_PWM_ONLY,
+       LP8550_I2C_ONLY = 2,
+};
+
+enum lp8551_brighntess_source {
+       LP8551_PWM_ONLY = LP8550_PWM_ONLY,
+       LP8551_I2C_ONLY = LP8550_I2C_ONLY,
+};
+
+enum lp8552_brighntess_source {
+       LP8552_PWM_ONLY = LP8550_PWM_ONLY,
+       LP8552_I2C_ONLY = LP8550_I2C_ONLY,
+};
+
+enum lp8553_brighntess_source {
+       LP8553_PWM_ONLY = LP8550_PWM_ONLY,
+       LP8553_I2C_ONLY = LP8550_I2C_ONLY,
+};
+
+enum lp8556_brightness_source {
+       LP8556_PWM_ONLY,
+       LP8556_COMBINED1,       /* pwm + i2c before the shaper block */
+       LP8556_I2C_ONLY,
+       LP8556_COMBINED2,       /* pwm + i2c after the shaper block */
+};
+
+struct lp855x_pwm_data {
+       void (*pwm_set_intensity) (int brightness, int max_brightness);
+       int (*pwm_get_intensity) (int max_brightness);
+};
+
+struct lp855x_rom_data {
+       u8 addr;
+       u8 val;
+};
+
+/**
+ * struct lp855x_platform_data
+ * @name : Backlight driver name. If it is not defined, default name is set.
+ * @mode : brightness control by pwm or lp855x register
+ * @device_control : value of DEVICE CONTROL register
+ * @initial_brightness : initial value of backlight brightness
+ * @pwm_data : platform specific pwm generation functions.
+               Only valid when mode is PWM_BASED.
+ * @load_new_rom_data :
+       0 : use default configuration data
+       1 : update values of eeprom or eprom registers on loading driver
+ * @size_program : total size of lp855x_rom_data
+ * @rom_data : list of new eeprom/eprom registers
+ */
+struct lp855x_platform_data {
+       char *name;
+       enum lp855x_brightness_ctrl_mode mode;
+       u8 device_control;
+       int initial_brightness;
+       struct lp855x_pwm_data pwm_data;
+       u8 load_new_rom_data;
+       int size_program;
+       struct lp855x_rom_data *rom_data;
+};
+
+#endif
diff --git a/include/linux/platform_data/lp8727.h b/include/linux/platform_data/lp8727.h
new file mode 100644 (file)
index 0000000..ea98c61
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * LP8727 Micro/Mini USB IC with integrated charger
+ *
+ *                     Copyright (C) 2011 Texas Instruments
+ *                     Copyright (C) 2011 National Semiconductor
+ *
+ * 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 _LP8727_H
+#define _LP8727_H
+
+enum lp8727_eoc_level {
+       EOC_5P,
+       EOC_10P,
+       EOC_16P,
+       EOC_20P,
+       EOC_25P,
+       EOC_33P,
+       EOC_50P,
+};
+
+enum lp8727_ichg {
+       ICHG_90mA,
+       ICHG_100mA,
+       ICHG_400mA,
+       ICHG_450mA,
+       ICHG_500mA,
+       ICHG_600mA,
+       ICHG_700mA,
+       ICHG_800mA,
+       ICHG_900mA,
+       ICHG_1000mA,
+};
+
+/**
+ * struct lp8727_chg_param
+ * @eoc_level : end of charge level setting
+ * @ichg : charging current
+ */
+struct lp8727_chg_param {
+       enum lp8727_eoc_level eoc_level;
+       enum lp8727_ichg ichg;
+};
+
+/**
+ * struct lp8727_platform_data
+ * @get_batt_present : check battery status - exists or not
+ * @get_batt_level : get battery voltage (mV)
+ * @get_batt_capacity : get battery capacity (%)
+ * @get_batt_temp : get battery temperature
+ * @ac, @usb : charging parameters each charger type
+ */
+struct lp8727_platform_data {
+       u8 (*get_batt_present)(void);
+       u16 (*get_batt_level)(void);
+       u8 (*get_batt_capacity)(void);
+       u8 (*get_batt_temp)(void);
+       struct lp8727_chg_param ac;
+       struct lp8727_chg_param usb;
+};
+
+#endif
index d94804aca764a24c94348f4e11bbd1ad451c725a..944b01dd103ef131b20784f6853524f9bd5812c2 100644 (file)
@@ -52,13 +52,4 @@ struct mv_usb_platform_data {
        int     (*set_vbus)(unsigned int vbus);
        int     (*private_init)(void __iomem *opregs, void __iomem *phyregs);
 };
-
-#ifndef CONFIG_HAVE_CLK
-/* Dummy stub for clk framework */
-#define clk_get(dev, id)       NULL
-#define clk_put(clock)         do {} while (0)
-#define clk_enable(clock)      do {} while (0)
-#define clk_disable(clock)     do {} while (0)
-#endif
-
 #endif
index 241065c9ce51832962f0fce4c93696d4b09dcb9d..cd22029e32aaf5adbd1b8136a3f48982705adbba 100644 (file)
@@ -16,6 +16,7 @@
 #define _CHARGER_MANAGER_H
 
 #include <linux/power_supply.h>
+#include <linux/extcon.h>
 
 enum data_source {
        CM_BATTERY_PRESENT,
@@ -64,6 +65,70 @@ struct charger_global_desc {
        bool assume_timer_stops_in_suspend;
 };
 
+/**
+ * struct charger_cable
+ * @extcon_name: the name of extcon device.
+ * @name: the name of charger cable(external connector).
+ * @extcon_dev: the extcon device.
+ * @wq: the workqueue to control charger according to the state of
+ *     charger cable. If charger cable is attached, enable charger.
+ *     But if charger cable is detached, disable charger.
+ * @nb: the notifier block to receive changed state from EXTCON
+ *     (External Connector) when charger cable is attached/detached.
+ * @attached: the state of charger cable.
+ *     true: the charger cable is attached
+ *     false: the charger cable is detached
+ * @charger: the instance of struct charger_regulator.
+ * @cm: the Charger Manager representing the battery.
+ */
+struct charger_cable {
+       const char *extcon_name;
+       const char *name;
+
+       /* The charger-manager use Exton framework*/
+       struct extcon_specific_cable_nb extcon_dev;
+       struct work_struct wq;
+       struct notifier_block nb;
+
+       /* The state of charger cable */
+       bool attached;
+
+       struct charger_regulator *charger;
+
+       /*
+        * Set min/max current of regulator to protect over-current issue
+        * according to a kind of charger cable when cable is attached.
+        */
+       int min_uA;
+       int max_uA;
+
+       struct charger_manager *cm;
+};
+
+/**
+ * struct charger_regulator
+ * @regulator_name: the name of regulator for using charger.
+ * @consumer: the regulator consumer for the charger.
+ * @cables:
+ *     the array of charger cables to enable/disable charger
+ *     and set current limit according to constratint data of
+ *     struct charger_cable if only charger cable included
+ *     in the array of charger cables is attached/detached.
+ * @num_cables: the number of charger cables.
+ */
+struct charger_regulator {
+       /* The name of regulator for charging */
+       const char *regulator_name;
+       struct regulator *consumer;
+
+       /*
+        * Store constraint information related to current limit,
+        * each cable have different condition for charging.
+        */
+       struct charger_cable *cables;
+       int num_cables;
+};
+
 /**
  * struct charger_desc
  * @psy_name: the name of power-supply-class for charger manager
@@ -109,7 +174,7 @@ struct charger_desc {
        char **psy_charger_stat;
 
        int num_charger_regulators;
-       struct regulator_bulk_data *charger_regulators;
+       struct charger_regulator *charger_regulators;
 
        char *psy_fuel_gauge;
 
index 3b912bee28d1693b8c6617f637354ed2869d306f..0bafbb15f29cf55fb76672972bdc4b947bb2fa2c 100644 (file)
@@ -109,6 +109,8 @@ enum power_supply_property {
        POWER_SUPPLY_PROP_CHARGE_NOW,
        POWER_SUPPLY_PROP_CHARGE_AVG,
        POWER_SUPPLY_PROP_CHARGE_COUNTER,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
        POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
        POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
        POWER_SUPPLY_PROP_ENERGY_FULL,
@@ -116,9 +118,15 @@ enum power_supply_property {
        POWER_SUPPLY_PROP_ENERGY_NOW,
        POWER_SUPPLY_PROP_ENERGY_AVG,
        POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
+       POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, /* in percents! */
+       POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */
        POWER_SUPPLY_PROP_CAPACITY_LEVEL,
        POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
+       POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
        POWER_SUPPLY_PROP_TEMP_AMBIENT,
+       POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN,
+       POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX,
        POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
        POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
        POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
@@ -173,6 +181,9 @@ struct power_supply {
        /* private */
        struct device *dev;
        struct work_struct changed_work;
+#ifdef CONFIG_THERMAL
+       struct thermal_zone_device *tzd;
+#endif
 
 #ifdef CONFIG_LEDS_TRIGGERS
        struct led_trigger *charging_full_trig;
@@ -236,6 +247,7 @@ static inline bool power_supply_is_amp_property(enum power_supply_property psp)
        case POWER_SUPPLY_PROP_CHARGE_NOW:
        case POWER_SUPPLY_PROP_CHARGE_AVG:
        case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
        case POWER_SUPPLY_PROP_CURRENT_MAX:
        case POWER_SUPPLY_PROP_CURRENT_NOW:
        case POWER_SUPPLY_PROP_CURRENT_AVG:
@@ -263,6 +275,7 @@ static inline bool power_supply_is_watt_property(enum power_supply_property psp)
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
        case POWER_SUPPLY_PROP_VOLTAGE_AVG:
        case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
        case POWER_SUPPLY_PROP_POWER_NOW:
                return 1;
        default:
index 1bec2f7a2d4236488af252d138188fa7c29e8369..9afc01e5a0a61ce5f4a9fe1e53ad6e0fb1b8e5bf 100644 (file)
@@ -2,27 +2,34 @@
 #define __KERNEL_PRINTK__
 
 #include <linux/init.h>
+#include <linux/kern_levels.h>
 
 extern const char linux_banner[];
 extern const char linux_proc_banner[];
 
-#define KERN_EMERG     "<0>"   /* system is unusable                   */
-#define KERN_ALERT     "<1>"   /* action must be taken immediately     */
-#define KERN_CRIT      "<2>"   /* critical conditions                  */
-#define KERN_ERR       "<3>"   /* error conditions                     */
-#define KERN_WARNING   "<4>"   /* warning conditions                   */
-#define KERN_NOTICE    "<5>"   /* normal but significant condition     */
-#define KERN_INFO      "<6>"   /* informational                        */
-#define KERN_DEBUG     "<7>"   /* debug-level messages                 */
-
-/* Use the default kernel loglevel */
-#define KERN_DEFAULT   "<d>"
-/*
- * Annotation for a "continued" line of log printout (only done after a
- * line that had no enclosing \n). Only to be used by core/arch code
- * during early bootup (a continued line is not SMP-safe otherwise).
- */
-#define KERN_CONT      "<c>"
+static inline int printk_get_level(const char *buffer)
+{
+       if (buffer[0] == KERN_SOH_ASCII && buffer[1]) {
+               switch (buffer[1]) {
+               case '0' ... '7':
+               case 'd':       /* KERN_DEFAULT */
+                       return buffer[1];
+               }
+       }
+       return 0;
+}
+
+static inline const char *printk_skip_level(const char *buffer)
+{
+       if (printk_get_level(buffer)) {
+               switch (buffer[1]) {
+               case '0' ... '7':
+               case 'd':       /* KERN_DEFAULT */
+                       return buffer + 2;
+               }
+       }
+       return buffer;
+}
 
 extern int console_printk[];
 
index 7c775751392c58003a896a0c2741cb134343ce9d..21d076c5089e9646418113fb9d27d30e4ba109f7 100644 (file)
@@ -1,7 +1,10 @@
 #ifndef __LINUX_PWM_H
 #define __LINUX_PWM_H
 
+#include <linux/of.h>
+
 struct pwm_device;
+struct seq_file;
 
 /*
  * pwm_request - request a PWM device
@@ -28,4 +31,118 @@ int pwm_enable(struct pwm_device *pwm);
  */
 void pwm_disable(struct pwm_device *pwm);
 
+#ifdef CONFIG_PWM
+struct pwm_chip;
+
+enum {
+       PWMF_REQUESTED = 1 << 0,
+       PWMF_ENABLED = 1 << 1,
+};
+
+struct pwm_device {
+       const char              *label;
+       unsigned long           flags;
+       unsigned int            hwpwm;
+       unsigned int            pwm;
+       struct pwm_chip         *chip;
+       void                    *chip_data;
+
+       unsigned int            period; /* in nanoseconds */
+};
+
+static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period)
+{
+       if (pwm)
+               pwm->period = period;
+}
+
+static inline unsigned int pwm_get_period(struct pwm_device *pwm)
+{
+       return pwm ? pwm->period : 0;
+}
+
+/**
+ * struct pwm_ops - PWM controller operations
+ * @request: optional hook for requesting a PWM
+ * @free: optional hook for freeing a PWM
+ * @config: configure duty cycles and period length for this PWM
+ * @enable: enable PWM output toggling
+ * @disable: disable PWM output toggling
+ * @dbg_show: optional routine to show contents in debugfs
+ * @owner: helps prevent removal of modules exporting active PWMs
+ */
+struct pwm_ops {
+       int                     (*request)(struct pwm_chip *chip,
+                                          struct pwm_device *pwm);
+       void                    (*free)(struct pwm_chip *chip,
+                                       struct pwm_device *pwm);
+       int                     (*config)(struct pwm_chip *chip,
+                                         struct pwm_device *pwm,
+                                         int duty_ns, int period_ns);
+       int                     (*enable)(struct pwm_chip *chip,
+                                         struct pwm_device *pwm);
+       void                    (*disable)(struct pwm_chip *chip,
+                                          struct pwm_device *pwm);
+#ifdef CONFIG_DEBUG_FS
+       void                    (*dbg_show)(struct pwm_chip *chip,
+                                           struct seq_file *s);
+#endif
+       struct module           *owner;
+};
+
+/**
+ * struct pwm_chip - abstract a PWM controller
+ * @dev: device providing the PWMs
+ * @list: list node for internal use
+ * @ops: callbacks for this PWM controller
+ * @base: number of first PWM controlled by this chip
+ * @npwm: number of PWMs controlled by this chip
+ * @pwms: array of PWM devices allocated by the framework
+ */
+struct pwm_chip {
+       struct device           *dev;
+       struct list_head        list;
+       const struct pwm_ops    *ops;
+       int                     base;
+       unsigned int            npwm;
+
+       struct pwm_device       *pwms;
+
+       struct pwm_device *     (*of_xlate)(struct pwm_chip *pc,
+                                           const struct of_phandle_args *args);
+       unsigned int            of_pwm_n_cells;
+};
+
+int pwm_set_chip_data(struct pwm_device *pwm, void *data);
+void *pwm_get_chip_data(struct pwm_device *pwm);
+
+int pwmchip_add(struct pwm_chip *chip);
+int pwmchip_remove(struct pwm_chip *chip);
+struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
+                                        unsigned int index,
+                                        const char *label);
+
+struct pwm_device *pwm_get(struct device *dev, const char *consumer);
+void pwm_put(struct pwm_device *pwm);
+
+struct pwm_lookup {
+       struct list_head list;
+       const char *provider;
+       unsigned int index;
+       const char *dev_id;
+       const char *con_id;
+};
+
+#define PWM_LOOKUP(_provider, _index, _dev_id, _con_id)        \
+       {                                               \
+               .provider = _provider,                  \
+               .index = _index,                        \
+               .dev_id = _dev_id,                      \
+               .con_id = _con_id,                      \
+       }
+
+void pwm_add_table(struct pwm_lookup *table, size_t num);
+
+#endif
+
 #endif /* __LINUX_PWM_H */
index 63d2df43e61a1999206d15b6d06f3567b9ac8f9e..56f4a866539ad7b66476fc3d1c09d15ab8ad2be1 100644 (file)
@@ -12,6 +12,7 @@ struct platform_pwm_backlight_data {
        unsigned int dft_brightness;
        unsigned int lth_brightness;
        unsigned int pwm_period_ns;
+       unsigned int *levels;
        int (*init)(struct device *dev);
        int (*notify)(struct device *dev, int brightness);
        void (*notify_after)(struct device *dev, int brightness);
index 8f74538c96db5bd8615896593c2825628a29613c..ac621ce886ca13a600a879c0132ef2650113aa15 100644 (file)
@@ -48,13 +48,13 @@ struct rnd_state {
 
 #ifdef __KERNEL__
 
-extern void rand_initialize_irq(int irq);
-
+extern void add_device_randomness(const void *, unsigned int);
 extern void add_input_randomness(unsigned int type, unsigned int code,
                                 unsigned int value);
-extern void add_interrupt_randomness(int irq);
+extern void add_interrupt_randomness(int irq, int irq_flags);
 
 extern void get_random_bytes(void *buf, int nbytes);
+extern void get_random_bytes_arch(void *buf, int nbytes);
 void generate_random_uuid(unsigned char uuid_out[16]);
 
 #ifndef MODULE
index ac9586dadfa58bdbade8fbb8391ea023d6d491aa..7b600da9a635027393368715a43e0a2a44827db0 100644 (file)
@@ -214,6 +214,10 @@ void sg_free_table(struct sg_table *);
 int __sg_alloc_table(struct sg_table *, unsigned int, unsigned int, gfp_t,
                     sg_alloc_fn *);
 int sg_alloc_table(struct sg_table *, unsigned int, gfp_t);
+int sg_alloc_table_from_pages(struct sg_table *sgt,
+       struct page **pages, unsigned int n_pages,
+       unsigned long offset, unsigned long size,
+       gfp_t gfp_mask);
 
 size_t sg_copy_from_buffer(struct scatterlist *sgl, unsigned int nents,
                           void *buf, size_t buflen);
index a721cef7e2d47eb2059540c9224fa882adaa7428..c147e7024f11f2859d8f648b1abac437bac941f1 100644 (file)
@@ -334,6 +334,14 @@ static inline void lockup_detector_init(void)
 }
 #endif
 
+#if defined(CONFIG_LOCKUP_DETECTOR) && defined(CONFIG_SUSPEND)
+void lockup_detector_bootcpu_resume(void);
+#else
+static inline void lockup_detector_bootcpu_resume(void)
+{
+}
+#endif
+
 #ifdef CONFIG_DETECT_HUNG_TASK
 extern unsigned int  sysctl_hung_task_panic;
 extern unsigned long sysctl_hung_task_check_count;
@@ -406,6 +414,11 @@ static inline void arch_pick_mmap_layout(struct mm_struct *mm) {}
 extern void set_dumpable(struct mm_struct *mm, int value);
 extern int get_dumpable(struct mm_struct *mm);
 
+/* get/set_dumpable() values */
+#define SUID_DUMPABLE_DISABLED 0
+#define SUID_DUMPABLE_ENABLED  1
+#define SUID_DUMPABLE_SAFE     2
+
 /* mm flags */
 /* dumpable bits */
 #define MMF_DUMPABLE      0  /* core dump is permitted */
@@ -1571,7 +1584,7 @@ struct task_struct {
        /* bitmask and counter of trace recursion */
        unsigned long trace_recursion;
 #endif /* CONFIG_TRACING */
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR /* memcg uses this to do batch job */
+#ifdef CONFIG_MEMCG /* memcg uses this to do batch job */
        struct memcg_batch_info {
                int do_batch;   /* incremented when batch uncharge started */
                struct mem_cgroup *memcg; /* target memcg of uncharge */
@@ -1881,6 +1894,13 @@ static inline void rcu_copy_process(struct task_struct *p)
 
 #endif
 
+static inline void tsk_restore_flags(struct task_struct *task,
+                               unsigned long orig_flags, unsigned long flags)
+{
+       task->flags &= ~flags;
+       task->flags |= orig_flags & flags;
+}
+
 #ifdef CONFIG_SMP
 extern void do_set_cpus_allowed(struct task_struct *p,
                               const struct cpumask *new_mask);
index 92808b86703ba7a63f786407144ec28f10ad13bc..edd086883ccbed5a84f2d44e0212d06b1af315a4 100644 (file)
@@ -107,12 +107,14 @@ struct shmid_kernel /* private to the kernel */
 #define SHM_NORESERVE   010000  /* don't check for reservations */
 
 #ifdef CONFIG_SYSVIPC
-long do_shmat(int shmid, char __user *shmaddr, int shmflg, unsigned long *addr);
+long do_shmat(int shmid, char __user *shmaddr, int shmflg, unsigned long *addr,
+             unsigned long shmlba);
 extern int is_file_shm_hugepages(struct file *file);
 extern void exit_shm(struct task_struct *task);
 #else
 static inline long do_shmat(int shmid, char __user *shmaddr,
-                               int shmflg, unsigned long *addr)
+                           int shmflg, unsigned long *addr,
+                           unsigned long shmlba)
 {
        return -ENOSYS;
 }
index 07ceb97d53facc505bae4b489d3cae96bd7d4021..ac6b8ee07825ee6ce99dff9faf9901a224a97b5e 100644 (file)
@@ -20,7 +20,6 @@ struct shrink_control {
  * 'nr_to_scan' entries and attempt to free them up.  It should return
  * the number of objects which remain in the cache.  If it returns -1, it means
  * it cannot do any scanning at this time (eg. there is a risk of deadlock).
- * The callback must not return -1 if nr_to_scan is zero.
  *
  * The 'gfpmask' refers to the allocation we are currently trying to
  * fulfil.
index d205c4be7f5b1da42d552790b64880a95a7fb7c8..b33a3a1f205e45c747c9125a5b5920365ca2f8be 100644 (file)
@@ -462,6 +462,7 @@ struct sk_buff {
 #ifdef CONFIG_IPV6_NDISC_NODETYPE
        __u8                    ndisc_nodetype:2;
 #endif
+       __u8                    pfmemalloc:1;
        __u8                    ooo_okay:1;
        __u8                    l4_rxhash:1;
        __u8                    wifi_acked_valid:1;
@@ -502,6 +503,15 @@ struct sk_buff {
 #include <linux/slab.h>
 
 
+#define SKB_ALLOC_FCLONE       0x01
+#define SKB_ALLOC_RX           0x02
+
+/* Returns true if the skb was allocated from PFMEMALLOC reserves */
+static inline bool skb_pfmemalloc(const struct sk_buff *skb)
+{
+       return unlikely(skb->pfmemalloc);
+}
+
 /*
  * skb might have a dst pointer attached, refcounted or not.
  * _skb_refdst low order bit is set if refcount was _not_ taken
@@ -565,7 +575,7 @@ extern bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
                             bool *fragstolen, int *delta_truesize);
 
 extern struct sk_buff *__alloc_skb(unsigned int size,
-                                  gfp_t priority, int fclone, int node);
+                                  gfp_t priority, int flags, int node);
 extern struct sk_buff *build_skb(void *data, unsigned int frag_size);
 static inline struct sk_buff *alloc_skb(unsigned int size,
                                        gfp_t priority)
@@ -576,7 +586,7 @@ static inline struct sk_buff *alloc_skb(unsigned int size,
 static inline struct sk_buff *alloc_skb_fclone(unsigned int size,
                                               gfp_t priority)
 {
-       return __alloc_skb(size, priority, 1, NUMA_NO_NODE);
+       return __alloc_skb(size, priority, SKB_ALLOC_FCLONE, NUMA_NO_NODE);
 }
 
 extern void skb_recycle(struct sk_buff *skb);
@@ -836,13 +846,16 @@ static inline int skb_shared(const struct sk_buff *skb)
  *
  *     NULL is returned on a memory allocation failure.
  */
-static inline struct sk_buff *skb_share_check(struct sk_buff *skb,
-                                             gfp_t pri)
+static inline struct sk_buff *skb_share_check(struct sk_buff *skb, gfp_t pri)
 {
        might_sleep_if(pri & __GFP_WAIT);
        if (skb_shared(skb)) {
                struct sk_buff *nskb = skb_clone(skb, pri);
-               kfree_skb(skb);
+
+               if (likely(nskb))
+                       consume_skb(skb);
+               else
+                       kfree_skb(skb);
                skb = nskb;
        }
        return skb;
@@ -1237,6 +1250,17 @@ static inline void __skb_fill_page_desc(struct sk_buff *skb, int i,
 {
        skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
 
+       /*
+        * Propagate page->pfmemalloc to the skb if we can. The problem is
+        * that not all callers have unique ownership of the page. If
+        * pfmemalloc is set, we check the mapping as a mapping implies
+        * page->index is set (index and pfmemalloc share space).
+        * If it's a valid mapping, we cannot use page->pfmemalloc but we
+        * do not lose pfmemalloc information as the pages would not be
+        * allocated using __GFP_MEMALLOC.
+        */
+       if (page->pfmemalloc && !page->mapping)
+               skb->pfmemalloc = true;
        frag->page.p              = page;
        frag->page_offset         = off;
        skb_frag_size_set(frag, size);
@@ -1753,6 +1777,61 @@ static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev,
        return __netdev_alloc_skb_ip_align(dev, length, GFP_ATOMIC);
 }
 
+/*
+ *     __skb_alloc_page - allocate pages for ps-rx on a skb and preserve pfmemalloc data
+ *     @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX
+ *     @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used
+ *     @order: size of the allocation
+ *
+ *     Allocate a new page.
+ *
+ *     %NULL is returned if there is no free memory.
+*/
+static inline struct page *__skb_alloc_pages(gfp_t gfp_mask,
+                                             struct sk_buff *skb,
+                                             unsigned int order)
+{
+       struct page *page;
+
+       gfp_mask |= __GFP_COLD;
+
+       if (!(gfp_mask & __GFP_NOMEMALLOC))
+               gfp_mask |= __GFP_MEMALLOC;
+
+       page = alloc_pages_node(NUMA_NO_NODE, gfp_mask, order);
+       if (skb && page && page->pfmemalloc)
+               skb->pfmemalloc = true;
+
+       return page;
+}
+
+/**
+ *     __skb_alloc_page - allocate a page for ps-rx for a given skb and preserve pfmemalloc data
+ *     @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX
+ *     @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used
+ *
+ *     Allocate a new page.
+ *
+ *     %NULL is returned if there is no free memory.
+ */
+static inline struct page *__skb_alloc_page(gfp_t gfp_mask,
+                                            struct sk_buff *skb)
+{
+       return __skb_alloc_pages(gfp_mask, skb, 0);
+}
+
+/**
+ *     skb_propagate_pfmemalloc - Propagate pfmemalloc if skb is allocated after RX page
+ *     @page: The page that was allocated from skb_alloc_page
+ *     @skb: The skb that may need pfmemalloc set
+ */
+static inline void skb_propagate_pfmemalloc(struct page *page,
+                                            struct sk_buff *skb)
+{
+       if (page && page->pfmemalloc)
+               skb->pfmemalloc = true;
+}
+
 /**
  * skb_frag_page - retrieve the page refered to by a paged fragment
  * @frag: the paged fragment
index 67d5d94b783a4b4ba97b53fc9d0adf9fd885af34..0dd2dfa7becadd196849e0d9ff003a7dcd7f9b45 100644 (file)
 #define ZERO_OR_NULL_PTR(x) ((unsigned long)(x) <= \
                                (unsigned long)ZERO_SIZE_PTR)
 
+/*
+ * Common fields provided in kmem_cache by all slab allocators
+ * This struct is either used directly by the allocator (SLOB)
+ * or the allocator must include definitions for all fields
+ * provided in kmem_cache_common in their definition of kmem_cache.
+ *
+ * Once we can do anonymous structs (C11 standard) we could put a
+ * anonymous struct definition in these allocators so that the
+ * separate allocations in the kmem_cache structure of SLAB and
+ * SLUB is no longer needed.
+ */
+#ifdef CONFIG_SLOB
+struct kmem_cache {
+       unsigned int object_size;/* The original size of the object */
+       unsigned int size;      /* The aligned/padded/added on size  */
+       unsigned int align;     /* Alignment as calculated */
+       unsigned long flags;    /* Active flags on the slab */
+       const char *name;       /* Slab name for sysfs */
+       int refcount;           /* Use counter */
+       void (*ctor)(void *);   /* Called on object slot creation */
+       struct list_head list;  /* List of all slab caches on the system */
+};
+#endif
+
 /*
  * struct kmem_cache related prototypes
  */
index fbd1117fdfde9fce2b059a4e9283b803d14f43d6..0c634fa376c94b213bc295fb6372d60448623d83 100644 (file)
@@ -27,7 +27,7 @@ struct kmem_cache {
        unsigned int limit;
        unsigned int shared;
 
-       unsigned int buffer_size;
+       unsigned int size;
        u32 reciprocal_buffer_size;
 /* 2) touched by every alloc & free from the backend */
 
@@ -39,7 +39,7 @@ struct kmem_cache {
        unsigned int gfporder;
 
        /* force GFP flags, e.g. GFP_DMA */
-       gfp_t gfpflags;
+       gfp_t allocflags;
 
        size_t colour;                  /* cache colouring range */
        unsigned int colour_off;        /* colour offset */
@@ -52,7 +52,10 @@ struct kmem_cache {
 
 /* 4) cache creation/removal */
        const char *name;
-       struct list_head next;
+       struct list_head list;
+       int refcount;
+       int object_size;
+       int align;
 
 /* 5) statistics */
 #ifdef CONFIG_DEBUG_SLAB
@@ -73,12 +76,11 @@ struct kmem_cache {
 
        /*
         * If debugging is enabled, then the allocator can add additional
-        * fields and/or padding to every object. buffer_size contains the total
+        * fields and/or padding to every object. size contains the total
         * object size including these internal fields, the following two
         * variables contain the offset to the user object and its size.
         */
        int obj_offset;
-       int obj_size;
 #endif /* CONFIG_DEBUG_SLAB */
 
 /* 6) per-cpu/per-node data, touched during every alloc/free */
index c2f8c8bc56edd08183dd0dd22b4f7d3b39f1c63d..df448adb7283fa534edf23656b1c90b6e27982b0 100644 (file)
@@ -48,7 +48,6 @@ struct kmem_cache_cpu {
        unsigned long tid;      /* Globally unique transaction id */
        struct page *page;      /* The slab from which we are allocating */
        struct page *partial;   /* Partially allocated frozen slabs */
-       int node;               /* The node of the page (or -1 for debug) */
 #ifdef CONFIG_SLUB_STATS
        unsigned stat[NR_SLUB_STAT_ITEMS];
 #endif
@@ -83,7 +82,7 @@ struct kmem_cache {
        unsigned long flags;
        unsigned long min_partial;
        int size;               /* The size of an object including meta data */
-       int objsize;            /* The size of an object without meta data */
+       int object_size;        /* The size of an object without meta data */
        int offset;             /* Free pointer offset. */
        int cpu_partial;        /* Number of per cpu partial objects to keep around */
        struct kmem_cache_order_objects oo;
index 00bc189cb3955b57384bbcf3e458b3b593c94774..ad6e3a6bf9fbd7e17627e07527ef3e699f447678 100644 (file)
 enum
 {
        IPSTATS_MIB_NUM = 0,
+/* frequently written fields in fast path, kept in same cache line */
        IPSTATS_MIB_INPKTS,                     /* InReceives */
+       IPSTATS_MIB_INOCTETS,                   /* InOctets */
+       IPSTATS_MIB_INDELIVERS,                 /* InDelivers */
+       IPSTATS_MIB_OUTFORWDATAGRAMS,           /* OutForwDatagrams */
+       IPSTATS_MIB_OUTPKTS,                    /* OutRequests */
+       IPSTATS_MIB_OUTOCTETS,                  /* OutOctets */
+/* other fields */
        IPSTATS_MIB_INHDRERRORS,                /* InHdrErrors */
        IPSTATS_MIB_INTOOBIGERRORS,             /* InTooBigErrors */
        IPSTATS_MIB_INNOROUTES,                 /* InNoRoutes */
@@ -26,9 +33,6 @@ enum
        IPSTATS_MIB_INUNKNOWNPROTOS,            /* InUnknownProtos */
        IPSTATS_MIB_INTRUNCATEDPKTS,            /* InTruncatedPkts */
        IPSTATS_MIB_INDISCARDS,                 /* InDiscards */
-       IPSTATS_MIB_INDELIVERS,                 /* InDelivers */
-       IPSTATS_MIB_OUTFORWDATAGRAMS,           /* OutForwDatagrams */
-       IPSTATS_MIB_OUTPKTS,                    /* OutRequests */
        IPSTATS_MIB_OUTDISCARDS,                /* OutDiscards */
        IPSTATS_MIB_OUTNOROUTES,                /* OutNoRoutes */
        IPSTATS_MIB_REASMTIMEOUT,               /* ReasmTimeout */
@@ -42,8 +46,6 @@ enum
        IPSTATS_MIB_OUTMCASTPKTS,               /* OutMcastPkts */
        IPSTATS_MIB_INBCASTPKTS,                /* InBcastPkts */
        IPSTATS_MIB_OUTBCASTPKTS,               /* OutBcastPkts */
-       IPSTATS_MIB_INOCTETS,                   /* InOctets */
-       IPSTATS_MIB_OUTOCTETS,                  /* OutOctets */
        IPSTATS_MIB_INMCASTOCTETS,              /* InMcastOctets */
        IPSTATS_MIB_OUTMCASTOCTETS,             /* OutMcastOctets */
        IPSTATS_MIB_INBCASTOCTETS,              /* InBcastOctets */
index e033564f10baec5b9b39a75c29f956f5d512e7c4..ffe0442e18d2f4e2d7b8d8eb8f22ffbc532e67e4 100644 (file)
@@ -145,4 +145,7 @@ static inline bool strstarts(const char *str, const char *prefix)
        return strncmp(str, prefix, strlen(prefix)) == 0;
 }
 #endif
+
+extern size_t memweight(const void *ptr, size_t bytes);
+
 #endif /* _LINUX_STRING_H_ */
index 492a36d72829939c21f6c4ff3f5bd412956b2a95..f25ba922baaf022dc3814f9705a5fd51749d1422 100644 (file)
@@ -101,6 +101,7 @@ struct rpc_authops {
        struct rpc_cred *       (*crcreate)(struct rpc_auth*, struct auth_cred *, int);
        int                     (*pipes_create)(struct rpc_auth *);
        void                    (*pipes_destroy)(struct rpc_auth *);
+       int                     (*list_pseudoflavors)(rpc_authflavor_t *, int);
 };
 
 struct rpc_credops {
@@ -135,6 +136,7 @@ int                 rpcauth_register(const struct rpc_authops *);
 int                    rpcauth_unregister(const struct rpc_authops *);
 struct rpc_auth *      rpcauth_create(rpc_authflavor_t, struct rpc_clnt *);
 void                   rpcauth_release(struct rpc_auth *);
+int                    rpcauth_list_flavors(rpc_authflavor_t *, int);
 struct rpc_cred *      rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int);
 void                   rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *);
 struct rpc_cred *      rpcauth_lookupcred(struct rpc_auth *, int);
index f5fd6160dbca396835773609586e65071f9c78ab..f792794f66341a5888136e6697322f445ba62197 100644 (file)
@@ -217,14 +217,32 @@ extern int qword_get(char **bpp, char *dest, int bufsize);
 static inline int get_int(char **bpp, int *anint)
 {
        char buf[50];
-       char *ep;
-       int rv;
-       int len = qword_get(bpp, buf, 50);
-       if (len < 0) return -EINVAL;
-       if (len ==0) return -ENOENT;
-       rv = simple_strtol(buf, &ep, 0);
-       if (*ep) return -EINVAL;
-       *anint = rv;
+       int len = qword_get(bpp, buf, sizeof(buf));
+
+       if (len < 0)
+               return -EINVAL;
+       if (len == 0)
+               return -ENOENT;
+
+       if (kstrtoint(buf, 0, anint))
+               return -EINVAL;
+
+       return 0;
+}
+
+static inline int get_uint(char **bpp, unsigned int *anint)
+{
+       char buf[50];
+       int len = qword_get(bpp, buf, sizeof(buf));
+
+       if (len < 0)
+               return -EINVAL;
+       if (len == 0)
+               return -ENOENT;
+
+       if (kstrtouint(buf, 0, anint))
+               return -EINVAL;
+
        return 0;
 }
 
index 332da61cf8b71fc73d802b2609210f46641a9ea1..a19e2547ae6aba4481ba4cd01f5bdbbf7545de22 100644 (file)
@@ -14,6 +14,7 @@
 
 #ifdef __KERNEL__
 #include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/msg_prot.h>
 #include <linux/uio.h>
 
 /* The mechanism-independent gss-api context: */
@@ -127,7 +128,7 @@ struct gss_api_mech *gss_mech_get_by_name(const char *);
 struct gss_api_mech *gss_mech_get_by_pseudoflavor(u32);
 
 /* Fill in an array with a list of supported pseudoflavors */
-int gss_mech_list_pseudoflavors(u32 *);
+int gss_mech_list_pseudoflavors(rpc_authflavor_t *, int);
 
 /* Just increments the mechanism's reference count and returns its input: */
 struct gss_api_mech * gss_mech_get(struct gss_api_mech *);
index 40e0a273faea3c07470e19fd23673fda89543f9b..d83db800fe02eeabbfc96f76e56a2662cce7c0cf 100644 (file)
@@ -278,6 +278,8 @@ struct svc_rqst {
        struct task_struct      *rq_task;       /* service thread */
 };
 
+#define SVC_NET(svc_rqst)      (svc_rqst->rq_xprt->xpt_net)
+
 /*
  * Rigorous type checking on sockaddr type conversions
  */
index af70af3335461ee587d23a8567a9abdb9cc23eb9..63988990bd36d5c81e8537cd1ef1ef2961c30ed6 100644 (file)
@@ -104,8 +104,6 @@ __be32 *xdr_decode_string_inplace(__be32 *p, char **sp, unsigned int *lenp,
 __be32 *xdr_encode_netobj(__be32 *p, const struct xdr_netobj *);
 __be32 *xdr_decode_netobj(__be32 *p, struct xdr_netobj *);
 
-void   xdr_encode_pages(struct xdr_buf *, struct page **, unsigned int,
-                        unsigned int);
 void   xdr_inline_pages(struct xdr_buf *, unsigned int,
                         struct page **, unsigned int, unsigned int);
 void   xdr_terminate_string(struct xdr_buf *, const u32);
@@ -205,6 +203,7 @@ struct xdr_stream {
        struct kvec *iov;       /* pointer to the current kvec */
        struct kvec scratch;    /* Scratch buffer */
        struct page **page_ptr; /* pointer to the current page */
+       unsigned int nwords;    /* Remaining decode buffer length */
 };
 
 /*
@@ -217,12 +216,13 @@ extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32
 extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
 extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
                unsigned int base, unsigned int len);
+extern unsigned int xdr_stream_pos(const struct xdr_stream *xdr);
 extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
 extern void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
                struct page **pages, unsigned int len);
 extern void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen);
 extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes);
-extern void xdr_read_pages(struct xdr_stream *xdr, unsigned int len);
+extern unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len);
 extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len);
 extern int xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len, int (*actor)(struct scatterlist *, void *), void *data);
 
index 77d278defa70667011b5a8b08ba70394b23d1d84..cff40aa7db625bbb9dafd6b5842d3dc70276c682 100644 (file)
@@ -174,6 +174,8 @@ struct rpc_xprt {
        unsigned long           state;          /* transport state */
        unsigned char           shutdown   : 1, /* being shut down */
                                resvport   : 1; /* use a reserved port */
+       unsigned int            swapper;        /* we're swapping over this
+                                                  transport */
        unsigned int            bind_index;     /* bind function index */
 
        /*
@@ -316,6 +318,7 @@ void                        xprt_release_rqst_cong(struct rpc_task *task);
 void                   xprt_disconnect_done(struct rpc_xprt *xprt);
 void                   xprt_force_disconnect(struct rpc_xprt *xprt);
 void                   xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie);
+int                    xs_swapper(struct rpc_xprt *xprt, int enable);
 
 /*
  * Reserved bit positions in xprt->state
index c84ec68eaec957b2e16a059b6feccde505f7f241..388e70601413332f6b89c97cccf8b99c0b8381ab 100644 (file)
@@ -151,6 +151,7 @@ enum {
        SWP_SOLIDSTATE  = (1 << 4),     /* blkdev seeks are cheap */
        SWP_CONTINUED   = (1 << 5),     /* swap_map has count continuation */
        SWP_BLKDEV      = (1 << 6),     /* its a block device */
+       SWP_FILE        = (1 << 7),     /* set after swap_activate success */
                                        /* add others here before... */
        SWP_SCANNING    = (1 << 8),     /* refcount in scan_swap_map */
 };
@@ -301,7 +302,7 @@ static inline void scan_unevictable_unregister_node(struct node *node)
 
 extern int kswapd_run(int nid);
 extern void kswapd_stop(int nid);
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
 extern int mem_cgroup_swappiness(struct mem_cgroup *mem);
 #else
 static inline int mem_cgroup_swappiness(struct mem_cgroup *mem)
@@ -309,7 +310,7 @@ static inline int mem_cgroup_swappiness(struct mem_cgroup *mem)
        return vm_swappiness;
 }
 #endif
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
 extern void mem_cgroup_uncharge_swap(swp_entry_t ent);
 #else
 static inline void mem_cgroup_uncharge_swap(swp_entry_t ent)
@@ -320,8 +321,14 @@ static inline void mem_cgroup_uncharge_swap(swp_entry_t ent)
 /* linux/mm/page_io.c */
 extern int swap_readpage(struct page *);
 extern int swap_writepage(struct page *page, struct writeback_control *wbc);
+extern int swap_set_page_dirty(struct page *page);
 extern void end_swap_bio_read(struct bio *bio, int err);
 
+int add_swap_extent(struct swap_info_struct *sis, unsigned long start_page,
+               unsigned long nr_pages, sector_t start_block);
+int generic_swapfile_activate(struct swap_info_struct *, struct file *,
+               sector_t *);
+
 /* linux/mm/swap_state.c */
 extern struct address_space swapper_space;
 #define total_swapcache_pages  swapper_space.nrpages
@@ -356,11 +363,12 @@ extern unsigned int count_swap_pages(int, int);
 extern sector_t map_swap_page(struct page *, struct block_device **);
 extern sector_t swapdev_block(int, pgoff_t);
 extern int page_swapcount(struct page *);
+extern struct swap_info_struct *page_swap_info(struct page *);
 extern int reuse_swap_page(struct page *);
 extern int try_to_free_swap(struct page *);
 struct backing_dev_info;
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
 extern void
 mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout);
 #else
index cfc8d908892e849a3bb8a3336a8225bae554d0eb..4b94a61955df361934984992b37e185b8d933f87 100644 (file)
@@ -151,7 +151,7 @@ enum {
 };
 #define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1)
 
-struct thermal_zone_device *thermal_zone_device_register(char *, int, int,
+struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
                void *, const struct thermal_zone_device_ops *, int tc1,
                int tc2, int passive_freq, int polling_freq);
 void thermal_zone_device_unregister(struct thermal_zone_device *);
index f46a53f060d7dd976b6767123d92a6f039a5ce0d..3b081862b9e89dcf50ab324fc77dd71ab2dfcdf6 100644 (file)
@@ -58,7 +58,8 @@ struct uvc_xu_control_mapping {
 struct uvc_xu_control_query {
        __u8 unit;
        __u8 selector;
-       __u8 query;
+       __u8 query;             /* Video Class-Specific Request Code, */
+                               /* defined in linux/usb/video.h A.8.  */
        __u16 size;
        __u8 __user *data;
 };
diff --git a/include/linux/v4l2-common.h b/include/linux/v4l2-common.h
new file mode 100644 (file)
index 0000000..0fa8b64
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * include/linux/v4l2-common.h
+ *
+ * Common V4L2 and V4L2 subdev definitions.
+ *
+ * Users are advised to #include this file either through videodev2.h
+ * (V4L2) or through v4l2-subdev.h (V4L2 subdev) rather than to refer
+ * to this file directly.
+ *
+ * Copyright (C) 2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __V4L2_COMMON__
+#define __V4L2_COMMON__
+
+/*
+ *
+ * Selection interface definitions
+ *
+ */
+
+/* Current cropping area */
+#define V4L2_SEL_TGT_CROP              0x0000
+/* Default cropping area */
+#define V4L2_SEL_TGT_CROP_DEFAULT      0x0001
+/* Cropping bounds */
+#define V4L2_SEL_TGT_CROP_BOUNDS       0x0002
+/* Current composing area */
+#define V4L2_SEL_TGT_COMPOSE           0x0100
+/* Default composing area */
+#define V4L2_SEL_TGT_COMPOSE_DEFAULT   0x0101
+/* Composing bounds */
+#define V4L2_SEL_TGT_COMPOSE_BOUNDS    0x0102
+/* Current composing area plus all padding pixels */
+#define V4L2_SEL_TGT_COMPOSE_PADDED    0x0103
+
+/* Backward compatibility target definitions --- to be removed. */
+#define V4L2_SEL_TGT_CROP_ACTIVE       V4L2_SEL_TGT_CROP
+#define V4L2_SEL_TGT_COMPOSE_ACTIVE    V4L2_SEL_TGT_COMPOSE
+#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL \
+       V4L2_SEL_TGT_CROP
+#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL \
+       V4L2_SEL_TGT_COMPOSE
+
+/* Selection flags */
+#define V4L2_SEL_FLAG_GE               (1 << 0)
+#define V4L2_SEL_FLAG_LE               (1 << 1)
+#define V4L2_SEL_FLAG_KEEP_CONFIG      (1 << 2)
+
+/* Backward compatibility flag definitions --- to be removed. */
+#define V4L2_SUBDEV_SEL_FLAG_SIZE_GE   V4L2_SEL_FLAG_GE
+#define V4L2_SUBDEV_SEL_FLAG_SIZE_LE   V4L2_SEL_FLAG_LE
+#define V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG V4L2_SEL_FLAG_KEEP_CONFIG
+
+#endif /* __V4L2_COMMON__ */
index 812019ee1e0665b58079ef90072655b3406a07a5..8c57ee9872bb429ea6e27cbe839326e9281ecd1f 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <linux/ioctl.h>
 #include <linux/types.h>
+#include <linux/v4l2-common.h>
 #include <linux/v4l2-mediabus.h>
 
 /**
@@ -123,27 +124,14 @@ struct v4l2_subdev_frame_interval_enum {
        __u32 reserved[9];
 };
 
-#define V4L2_SUBDEV_SEL_FLAG_SIZE_GE                   (1 << 0)
-#define V4L2_SUBDEV_SEL_FLAG_SIZE_LE                   (1 << 1)
-#define V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG               (1 << 2)
-
-/* active cropping area */
-#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL                        0x0000
-/* cropping bounds */
-#define V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS                        0x0002
-/* current composing area */
-#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL             0x0100
-/* composing bounds */
-#define V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS             0x0102
-
-
 /**
  * struct v4l2_subdev_selection - selection info
  *
  * @which: either V4L2_SUBDEV_FORMAT_ACTIVE or V4L2_SUBDEV_FORMAT_TRY
  * @pad: pad number, as reported by the media API
- * @target: selection target, used to choose one of possible rectangles
- * @flags: constraint flags
+ * @target: Selection target, used to choose one of possible rectangles,
+ *         defined in v4l2-common.h; V4L2_SEL_TGT_* .
+ * @flags: constraint flags, defined in v4l2-common.h; V4L2_SEL_FLAG_*.
  * @r: coordinates of the selection window
  * @reserved: for future use, set to zero for now
  *
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
new file mode 100644 (file)
index 0000000..0a4f180
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+ * VFIO API definition
+ *
+ * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
+ *     Author: Alex Williamson <alex.williamson@redhat.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 VFIO_H
+#define VFIO_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define VFIO_API_VERSION       0
+
+#ifdef __KERNEL__      /* Internal VFIO-core/bus driver API */
+
+#include <linux/iommu.h>
+#include <linux/mm.h>
+
+/**
+ * struct vfio_device_ops - VFIO bus driver device callbacks
+ *
+ * @open: Called when userspace creates new file descriptor for device
+ * @release: Called when userspace releases file descriptor for device
+ * @read: Perform read(2) on device file descriptor
+ * @write: Perform write(2) on device file descriptor
+ * @ioctl: Perform ioctl(2) on device file descriptor, supporting VFIO_DEVICE_*
+ *         operations documented below
+ * @mmap: Perform mmap(2) on a region of the device file descriptor
+ */
+struct vfio_device_ops {
+       char    *name;
+       int     (*open)(void *device_data);
+       void    (*release)(void *device_data);
+       ssize_t (*read)(void *device_data, char __user *buf,
+                       size_t count, loff_t *ppos);
+       ssize_t (*write)(void *device_data, const char __user *buf,
+                        size_t count, loff_t *size);
+       long    (*ioctl)(void *device_data, unsigned int cmd,
+                        unsigned long arg);
+       int     (*mmap)(void *device_data, struct vm_area_struct *vma);
+};
+
+extern int vfio_add_group_dev(struct device *dev,
+                             const struct vfio_device_ops *ops,
+                             void *device_data);
+
+extern void *vfio_del_group_dev(struct device *dev);
+
+/**
+ * struct vfio_iommu_driver_ops - VFIO IOMMU driver callbacks
+ */
+struct vfio_iommu_driver_ops {
+       char            *name;
+       struct module   *owner;
+       void            *(*open)(unsigned long arg);
+       void            (*release)(void *iommu_data);
+       ssize_t         (*read)(void *iommu_data, char __user *buf,
+                               size_t count, loff_t *ppos);
+       ssize_t         (*write)(void *iommu_data, const char __user *buf,
+                                size_t count, loff_t *size);
+       long            (*ioctl)(void *iommu_data, unsigned int cmd,
+                                unsigned long arg);
+       int             (*mmap)(void *iommu_data, struct vm_area_struct *vma);
+       int             (*attach_group)(void *iommu_data,
+                                       struct iommu_group *group);
+       void            (*detach_group)(void *iommu_data,
+                                       struct iommu_group *group);
+
+};
+
+extern int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops);
+
+extern void vfio_unregister_iommu_driver(
+                               const struct vfio_iommu_driver_ops *ops);
+
+/**
+ * offsetofend(TYPE, MEMBER)
+ *
+ * @TYPE: The type of the structure
+ * @MEMBER: The member within the structure to get the end offset of
+ *
+ * Simple helper macro for dealing with variable sized structures passed
+ * from user space.  This allows us to easily determine if the provided
+ * structure is sized to include various fields.
+ */
+#define offsetofend(TYPE, MEMBER) ({                           \
+       TYPE tmp;                                               \
+       offsetof(TYPE, MEMBER) + sizeof(tmp.MEMBER); })         \
+
+#endif /* __KERNEL__ */
+
+/* Kernel & User level defines for VFIO IOCTLs. */
+
+/* Extensions */
+
+#define VFIO_TYPE1_IOMMU               1
+
+/*
+ * The IOCTL interface is designed for extensibility by embedding the
+ * structure length (argsz) and flags into structures passed between
+ * kernel and userspace.  We therefore use the _IO() macro for these
+ * defines to avoid implicitly embedding a size into the ioctl request.
+ * As structure fields are added, argsz will increase to match and flag
+ * bits will be defined to indicate additional fields with valid data.
+ * It's *always* the caller's responsibility to indicate the size of
+ * the structure passed by setting argsz appropriately.
+ */
+
+#define VFIO_TYPE      (';')
+#define VFIO_BASE      100
+
+/* -------- IOCTLs for VFIO file descriptor (/dev/vfio/vfio) -------- */
+
+/**
+ * VFIO_GET_API_VERSION - _IO(VFIO_TYPE, VFIO_BASE + 0)
+ *
+ * Report the version of the VFIO API.  This allows us to bump the entire
+ * API version should we later need to add or change features in incompatible
+ * ways.
+ * Return: VFIO_API_VERSION
+ * Availability: Always
+ */
+#define VFIO_GET_API_VERSION           _IO(VFIO_TYPE, VFIO_BASE + 0)
+
+/**
+ * VFIO_CHECK_EXTENSION - _IOW(VFIO_TYPE, VFIO_BASE + 1, __u32)
+ *
+ * Check whether an extension is supported.
+ * Return: 0 if not supported, 1 (or some other positive integer) if supported.
+ * Availability: Always
+ */
+#define VFIO_CHECK_EXTENSION           _IO(VFIO_TYPE, VFIO_BASE + 1)
+
+/**
+ * VFIO_SET_IOMMU - _IOW(VFIO_TYPE, VFIO_BASE + 2, __s32)
+ *
+ * Set the iommu to the given type.  The type must be supported by an
+ * iommu driver as verified by calling CHECK_EXTENSION using the same
+ * type.  A group must be set to this file descriptor before this
+ * ioctl is available.  The IOMMU interfaces enabled by this call are
+ * specific to the value set.
+ * Return: 0 on success, -errno on failure
+ * Availability: When VFIO group attached
+ */
+#define VFIO_SET_IOMMU                 _IO(VFIO_TYPE, VFIO_BASE + 2)
+
+/* -------- IOCTLs for GROUP file descriptors (/dev/vfio/$GROUP) -------- */
+
+/**
+ * VFIO_GROUP_GET_STATUS - _IOR(VFIO_TYPE, VFIO_BASE + 3,
+ *                                             struct vfio_group_status)
+ *
+ * Retrieve information about the group.  Fills in provided
+ * struct vfio_group_info.  Caller sets argsz.
+ * Return: 0 on succes, -errno on failure.
+ * Availability: Always
+ */
+struct vfio_group_status {
+       __u32   argsz;
+       __u32   flags;
+#define VFIO_GROUP_FLAGS_VIABLE                (1 << 0)
+#define VFIO_GROUP_FLAGS_CONTAINER_SET (1 << 1)
+};
+#define VFIO_GROUP_GET_STATUS          _IO(VFIO_TYPE, VFIO_BASE + 3)
+
+/**
+ * VFIO_GROUP_SET_CONTAINER - _IOW(VFIO_TYPE, VFIO_BASE + 4, __s32)
+ *
+ * Set the container for the VFIO group to the open VFIO file
+ * descriptor provided.  Groups may only belong to a single
+ * container.  Containers may, at their discretion, support multiple
+ * groups.  Only when a container is set are all of the interfaces
+ * of the VFIO file descriptor and the VFIO group file descriptor
+ * available to the user.
+ * Return: 0 on success, -errno on failure.
+ * Availability: Always
+ */
+#define VFIO_GROUP_SET_CONTAINER       _IO(VFIO_TYPE, VFIO_BASE + 4)
+
+/**
+ * VFIO_GROUP_UNSET_CONTAINER - _IO(VFIO_TYPE, VFIO_BASE + 5)
+ *
+ * Remove the group from the attached container.  This is the
+ * opposite of the SET_CONTAINER call and returns the group to
+ * an initial state.  All device file descriptors must be released
+ * prior to calling this interface.  When removing the last group
+ * from a container, the IOMMU will be disabled and all state lost,
+ * effectively also returning the VFIO file descriptor to an initial
+ * state.
+ * Return: 0 on success, -errno on failure.
+ * Availability: When attached to container
+ */
+#define VFIO_GROUP_UNSET_CONTAINER     _IO(VFIO_TYPE, VFIO_BASE + 5)
+
+/**
+ * VFIO_GROUP_GET_DEVICE_FD - _IOW(VFIO_TYPE, VFIO_BASE + 6, char)
+ *
+ * Return a new file descriptor for the device object described by
+ * the provided string.  The string should match a device listed in
+ * the devices subdirectory of the IOMMU group sysfs entry.  The
+ * group containing the device must already be added to this context.
+ * Return: new file descriptor on success, -errno on failure.
+ * Availability: When attached to container
+ */
+#define VFIO_GROUP_GET_DEVICE_FD       _IO(VFIO_TYPE, VFIO_BASE + 6)
+
+/* --------------- IOCTLs for DEVICE file descriptors --------------- */
+
+/**
+ * VFIO_DEVICE_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 7,
+ *                                             struct vfio_device_info)
+ *
+ * Retrieve information about the device.  Fills in provided
+ * struct vfio_device_info.  Caller sets argsz.
+ * Return: 0 on success, -errno on failure.
+ */
+struct vfio_device_info {
+       __u32   argsz;
+       __u32   flags;
+#define VFIO_DEVICE_FLAGS_RESET        (1 << 0)        /* Device supports reset */
+#define VFIO_DEVICE_FLAGS_PCI  (1 << 1)        /* vfio-pci device */
+       __u32   num_regions;    /* Max region index + 1 */
+       __u32   num_irqs;       /* Max IRQ index + 1 */
+};
+#define VFIO_DEVICE_GET_INFO           _IO(VFIO_TYPE, VFIO_BASE + 7)
+
+/**
+ * VFIO_DEVICE_GET_REGION_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 8,
+ *                                    struct vfio_region_info)
+ *
+ * Retrieve information about a device region.  Caller provides
+ * struct vfio_region_info with index value set.  Caller sets argsz.
+ * Implementation of region mapping is bus driver specific.  This is
+ * intended to describe MMIO, I/O port, as well as bus specific
+ * regions (ex. PCI config space).  Zero sized regions may be used
+ * to describe unimplemented regions (ex. unimplemented PCI BARs).
+ * Return: 0 on success, -errno on failure.
+ */
+struct vfio_region_info {
+       __u32   argsz;
+       __u32   flags;
+#define VFIO_REGION_INFO_FLAG_READ     (1 << 0) /* Region supports read */
+#define VFIO_REGION_INFO_FLAG_WRITE    (1 << 1) /* Region supports write */
+#define VFIO_REGION_INFO_FLAG_MMAP     (1 << 2) /* Region supports mmap */
+       __u32   index;          /* Region index */
+       __u32   resv;           /* Reserved for alignment */
+       __u64   size;           /* Region size (bytes) */
+       __u64   offset;         /* Region offset from start of device fd */
+};
+#define VFIO_DEVICE_GET_REGION_INFO    _IO(VFIO_TYPE, VFIO_BASE + 8)
+
+/**
+ * VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9,
+ *                                 struct vfio_irq_info)
+ *
+ * Retrieve information about a device IRQ.  Caller provides
+ * struct vfio_irq_info with index value set.  Caller sets argsz.
+ * Implementation of IRQ mapping is bus driver specific.  Indexes
+ * using multiple IRQs are primarily intended to support MSI-like
+ * interrupt blocks.  Zero count irq blocks may be used to describe
+ * unimplemented interrupt types.
+ *
+ * The EVENTFD flag indicates the interrupt index supports eventfd based
+ * signaling.
+ *
+ * The MASKABLE flags indicates the index supports MASK and UNMASK
+ * actions described below.
+ *
+ * AUTOMASKED indicates that after signaling, the interrupt line is
+ * automatically masked by VFIO and the user needs to unmask the line
+ * to receive new interrupts.  This is primarily intended to distinguish
+ * level triggered interrupts.
+ *
+ * The NORESIZE flag indicates that the interrupt lines within the index
+ * are setup as a set and new subindexes cannot be enabled without first
+ * disabling the entire index.  This is used for interrupts like PCI MSI
+ * and MSI-X where the driver may only use a subset of the available
+ * indexes, but VFIO needs to enable a specific number of vectors
+ * upfront.  In the case of MSI-X, where the user can enable MSI-X and
+ * then add and unmask vectors, it's up to userspace to make the decision
+ * whether to allocate the maximum supported number of vectors or tear
+ * down setup and incrementally increase the vectors as each is enabled.
+ */
+struct vfio_irq_info {
+       __u32   argsz;
+       __u32   flags;
+#define VFIO_IRQ_INFO_EVENTFD          (1 << 0)
+#define VFIO_IRQ_INFO_MASKABLE         (1 << 1)
+#define VFIO_IRQ_INFO_AUTOMASKED       (1 << 2)
+#define VFIO_IRQ_INFO_NORESIZE         (1 << 3)
+       __u32   index;          /* IRQ index */
+       __u32   count;          /* Number of IRQs within this index */
+};
+#define VFIO_DEVICE_GET_IRQ_INFO       _IO(VFIO_TYPE, VFIO_BASE + 9)
+
+/**
+ * VFIO_DEVICE_SET_IRQS - _IOW(VFIO_TYPE, VFIO_BASE + 10, struct vfio_irq_set)
+ *
+ * Set signaling, masking, and unmasking of interrupts.  Caller provides
+ * struct vfio_irq_set with all fields set.  'start' and 'count' indicate
+ * the range of subindexes being specified.
+ *
+ * The DATA flags specify the type of data provided.  If DATA_NONE, the
+ * operation performs the specified action immediately on the specified
+ * interrupt(s).  For example, to unmask AUTOMASKED interrupt [0,0]:
+ * flags = (DATA_NONE|ACTION_UNMASK), index = 0, start = 0, count = 1.
+ *
+ * DATA_BOOL allows sparse support for the same on arrays of interrupts.
+ * For example, to mask interrupts [0,1] and [0,3] (but not [0,2]):
+ * flags = (DATA_BOOL|ACTION_MASK), index = 0, start = 1, count = 3,
+ * data = {1,0,1}
+ *
+ * DATA_EVENTFD binds the specified ACTION to the provided __s32 eventfd.
+ * A value of -1 can be used to either de-assign interrupts if already
+ * assigned or skip un-assigned interrupts.  For example, to set an eventfd
+ * to be trigger for interrupts [0,0] and [0,2]:
+ * flags = (DATA_EVENTFD|ACTION_TRIGGER), index = 0, start = 0, count = 3,
+ * data = {fd1, -1, fd2}
+ * If index [0,1] is previously set, two count = 1 ioctls calls would be
+ * required to set [0,0] and [0,2] without changing [0,1].
+ *
+ * Once a signaling mechanism is set, DATA_BOOL or DATA_NONE can be used
+ * with ACTION_TRIGGER to perform kernel level interrupt loopback testing
+ * from userspace (ie. simulate hardware triggering).
+ *
+ * Setting of an event triggering mechanism to userspace for ACTION_TRIGGER
+ * enables the interrupt index for the device.  Individual subindex interrupts
+ * can be disabled using the -1 value for DATA_EVENTFD or the index can be
+ * disabled as a whole with: flags = (DATA_NONE|ACTION_TRIGGER), count = 0.
+ *
+ * Note that ACTION_[UN]MASK specify user->kernel signaling (irqfds) while
+ * ACTION_TRIGGER specifies kernel->user signaling.
+ */
+struct vfio_irq_set {
+       __u32   argsz;
+       __u32   flags;
+#define VFIO_IRQ_SET_DATA_NONE         (1 << 0) /* Data not present */
+#define VFIO_IRQ_SET_DATA_BOOL         (1 << 1) /* Data is bool (u8) */
+#define VFIO_IRQ_SET_DATA_EVENTFD      (1 << 2) /* Data is eventfd (s32) */
+#define VFIO_IRQ_SET_ACTION_MASK       (1 << 3) /* Mask interrupt */
+#define VFIO_IRQ_SET_ACTION_UNMASK     (1 << 4) /* Unmask interrupt */
+#define VFIO_IRQ_SET_ACTION_TRIGGER    (1 << 5) /* Trigger interrupt */
+       __u32   index;
+       __u32   start;
+       __u32   count;
+       __u8    data[];
+};
+#define VFIO_DEVICE_SET_IRQS           _IO(VFIO_TYPE, VFIO_BASE + 10)
+
+#define VFIO_IRQ_SET_DATA_TYPE_MASK    (VFIO_IRQ_SET_DATA_NONE | \
+                                        VFIO_IRQ_SET_DATA_BOOL | \
+                                        VFIO_IRQ_SET_DATA_EVENTFD)
+#define VFIO_IRQ_SET_ACTION_TYPE_MASK  (VFIO_IRQ_SET_ACTION_MASK | \
+                                        VFIO_IRQ_SET_ACTION_UNMASK | \
+                                        VFIO_IRQ_SET_ACTION_TRIGGER)
+/**
+ * VFIO_DEVICE_RESET - _IO(VFIO_TYPE, VFIO_BASE + 11)
+ *
+ * Reset a device.
+ */
+#define VFIO_DEVICE_RESET              _IO(VFIO_TYPE, VFIO_BASE + 11)
+
+/*
+ * The VFIO-PCI bus driver makes use of the following fixed region and
+ * IRQ index mapping.  Unimplemented regions return a size of zero.
+ * Unimplemented IRQ types return a count of zero.
+ */
+
+enum {
+       VFIO_PCI_BAR0_REGION_INDEX,
+       VFIO_PCI_BAR1_REGION_INDEX,
+       VFIO_PCI_BAR2_REGION_INDEX,
+       VFIO_PCI_BAR3_REGION_INDEX,
+       VFIO_PCI_BAR4_REGION_INDEX,
+       VFIO_PCI_BAR5_REGION_INDEX,
+       VFIO_PCI_ROM_REGION_INDEX,
+       VFIO_PCI_CONFIG_REGION_INDEX,
+       VFIO_PCI_NUM_REGIONS
+};
+
+enum {
+       VFIO_PCI_INTX_IRQ_INDEX,
+       VFIO_PCI_MSI_IRQ_INDEX,
+       VFIO_PCI_MSIX_IRQ_INDEX,
+       VFIO_PCI_NUM_IRQS
+};
+
+/* -------- API for Type1 VFIO IOMMU -------- */
+
+/**
+ * VFIO_IOMMU_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 12, struct vfio_iommu_info)
+ *
+ * Retrieve information about the IOMMU object. Fills in provided
+ * struct vfio_iommu_info. Caller sets argsz.
+ *
+ * XXX Should we do these by CHECK_EXTENSION too?
+ */
+struct vfio_iommu_type1_info {
+       __u32   argsz;
+       __u32   flags;
+#define VFIO_IOMMU_INFO_PGSIZES (1 << 0)       /* supported page sizes info */
+       __u64   iova_pgsizes;           /* Bitmap of supported page sizes */
+};
+
+#define VFIO_IOMMU_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 12)
+
+/**
+ * VFIO_IOMMU_MAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 13, struct vfio_dma_map)
+ *
+ * Map process virtual addresses to IO virtual addresses using the
+ * provided struct vfio_dma_map. Caller sets argsz. READ &/ WRITE required.
+ */
+struct vfio_iommu_type1_dma_map {
+       __u32   argsz;
+       __u32   flags;
+#define VFIO_DMA_MAP_FLAG_READ (1 << 0)                /* readable from device */
+#define VFIO_DMA_MAP_FLAG_WRITE (1 << 1)       /* writable from device */
+       __u64   vaddr;                          /* Process virtual address */
+       __u64   iova;                           /* IO virtual address */
+       __u64   size;                           /* Size of mapping (bytes) */
+};
+
+#define VFIO_IOMMU_MAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 13)
+
+/**
+ * VFIO_IOMMU_UNMAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 14, struct vfio_dma_unmap)
+ *
+ * Unmap IO virtual addresses using the provided struct vfio_dma_unmap.
+ * Caller sets argsz.
+ */
+struct vfio_iommu_type1_dma_unmap {
+       __u32   argsz;
+       __u32   flags;
+       __u64   iova;                           /* IO virtual address */
+       __u64   size;                           /* Size of mapping (bytes) */
+};
+
+#define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14)
+
+#endif /* VFIO_H */
index 2039c5d3292e801c0abb8c770c99cdfe287ce9bf..7a147c8299ab36c5c289a8396664fa3be6fd377e 100644 (file)
@@ -64,6 +64,7 @@
 #include <linux/compiler.h>
 #include <linux/ioctl.h>
 #include <linux/types.h>
+#include <linux/v4l2-common.h>
 
 /*
  * Common stuff for both V4L1 and V4L2
@@ -273,6 +274,10 @@ struct v4l2_capability {
 #define V4L2_CAP_VIDEO_CAPTURE_MPLANE  0x00001000
 /* Is a video output device that supports multiplanar formats */
 #define V4L2_CAP_VIDEO_OUTPUT_MPLANE   0x00002000
+/* Is a video mem-to-mem device that supports multiplanar formats */
+#define V4L2_CAP_VIDEO_M2M_MPLANE      0x00004000
+/* Is a video mem-to-mem device */
+#define V4L2_CAP_VIDEO_M2M             0x00008000
 
 #define V4L2_CAP_TUNER                 0x00010000  /* has a tuner */
 #define V4L2_CAP_AUDIO                 0x00020000  /* has audio support */
@@ -657,7 +662,7 @@ struct v4l2_buffer {
                struct v4l2_plane *planes;
        } m;
        __u32                   length;
-       __u32                   input;
+       __u32                   reserved2;
        __u32                   reserved;
 };
 
@@ -671,7 +676,6 @@ struct v4l2_buffer {
 /* Buffer is ready, but the data contained within is corrupted. */
 #define V4L2_BUF_FLAG_ERROR    0x0040
 #define V4L2_BUF_FLAG_TIMECODE 0x0100  /* timecode field is valid */
-#define V4L2_BUF_FLAG_INPUT     0x0200  /* input field is valid */
 #define V4L2_BUF_FLAG_PREPARED 0x0400  /* Buffer is prepared for queuing */
 /* Cache handling flags */
 #define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE      0x0800
@@ -761,32 +765,12 @@ struct v4l2_crop {
        struct v4l2_rect        c;
 };
 
-/* Hints for adjustments of selection rectangle */
-#define V4L2_SEL_FLAG_GE       0x00000001
-#define V4L2_SEL_FLAG_LE       0x00000002
-
-/* Selection targets */
-
-/* Current cropping area */
-#define V4L2_SEL_TGT_CROP_ACTIVE       0x0000
-/* Default cropping area */
-#define V4L2_SEL_TGT_CROP_DEFAULT      0x0001
-/* Cropping bounds */
-#define V4L2_SEL_TGT_CROP_BOUNDS       0x0002
-/* Current composing area */
-#define V4L2_SEL_TGT_COMPOSE_ACTIVE    0x0100
-/* Default composing area */
-#define V4L2_SEL_TGT_COMPOSE_DEFAULT   0x0101
-/* Composing bounds */
-#define V4L2_SEL_TGT_COMPOSE_BOUNDS    0x0102
-/* Current composing area plus all padding pixels */
-#define V4L2_SEL_TGT_COMPOSE_PADDED    0x0103
-
 /**
  * struct v4l2_selection - selection info
  * @type:      buffer type (do not use *_MPLANE types)
- * @target:    selection target, used to choose one of possible rectangles
- * @flags:     constraints flags
+ * @target:    Selection target, used to choose one of possible rectangles;
+ *             defined in v4l2-common.h; V4L2_SEL_TGT_* .
+ * @flags:     constraints flags, defined in v4l2-common.h; V4L2_SEL_FLAG_*.
  * @r:         coordinates of selection window
  * @reserved:  for future use, rounds structure size to 64 bytes, set to zero
  *
@@ -2039,6 +2023,8 @@ struct v4l2_modulator {
 /*  Flags for the 'capability' field */
 #define V4L2_TUNER_CAP_LOW             0x0001
 #define V4L2_TUNER_CAP_NORM            0x0002
+#define V4L2_TUNER_CAP_HWSEEK_BOUNDED  0x0004
+#define V4L2_TUNER_CAP_HWSEEK_WRAP     0x0008
 #define V4L2_TUNER_CAP_STEREO          0x0010
 #define V4L2_TUNER_CAP_LANG2           0x0020
 #define V4L2_TUNER_CAP_SAP             0x0020
@@ -2046,6 +2032,8 @@ struct v4l2_modulator {
 #define V4L2_TUNER_CAP_RDS             0x0080
 #define V4L2_TUNER_CAP_RDS_BLOCK_IO    0x0100
 #define V4L2_TUNER_CAP_RDS_CONTROLS    0x0200
+#define V4L2_TUNER_CAP_FREQ_BANDS      0x0400
+#define V4L2_TUNER_CAP_HWSEEK_PROG_LIM 0x0800
 
 /*  Flags for the 'rxsubchans' field */
 #define V4L2_TUNER_SUB_MONO            0x0001
@@ -2064,19 +2052,36 @@ struct v4l2_modulator {
 #define V4L2_TUNER_MODE_LANG1_LANG2    0x0004
 
 struct v4l2_frequency {
-       __u32                 tuner;
-       __u32                 type;     /* enum v4l2_tuner_type */
-       __u32                 frequency;
-       __u32                 reserved[8];
+       __u32   tuner;
+       __u32   type;   /* enum v4l2_tuner_type */
+       __u32   frequency;
+       __u32   reserved[8];
+};
+
+#define V4L2_BAND_MODULATION_VSB       (1 << 1)
+#define V4L2_BAND_MODULATION_FM                (1 << 2)
+#define V4L2_BAND_MODULATION_AM                (1 << 3)
+
+struct v4l2_frequency_band {
+       __u32   tuner;
+       __u32   type;   /* enum v4l2_tuner_type */
+       __u32   index;
+       __u32   capability;
+       __u32   rangelow;
+       __u32   rangehigh;
+       __u32   modulation;
+       __u32   reserved[9];
 };
 
 struct v4l2_hw_freq_seek {
-       __u32                 tuner;
-       __u32                 type;     /* enum v4l2_tuner_type */
-       __u32                 seek_upward;
-       __u32                 wrap_around;
-       __u32                 spacing;
-       __u32                 reserved[7];
+       __u32   tuner;
+       __u32   type;   /* enum v4l2_tuner_type */
+       __u32   seek_upward;
+       __u32   wrap_around;
+       __u32   spacing;
+       __u32   rangelow;
+       __u32   rangehigh;
+       __u32   reserved[5];
 };
 
 /*
@@ -2644,6 +2649,10 @@ struct v4l2_create_buffers {
 #define VIDIOC_QUERY_DV_TIMINGS  _IOR('V', 99, struct v4l2_dv_timings)
 #define VIDIOC_DV_TIMINGS_CAP   _IOWR('V', 100, struct v4l2_dv_timings_cap)
 
+/* Experimental, this ioctl may change over the next couple of kernel
+   versions. */
+#define VIDIOC_ENUM_FREQ_BANDS _IOWR('V', 101, struct v4l2_frequency_band)
+
 /* Reminder: when adding new ioctls please add support for them to
    drivers/media/video/v4l2-compat-ioctl32.c as well! */
 
index e0edb40ca7aaca3ffeeb9261fa017eedd6548291..6d8e61c48563a41a56379643585d610cff7f096a 100644 (file)
 #define VIRTIO_BLK_F_RO                5       /* Disk is read-only */
 #define VIRTIO_BLK_F_BLK_SIZE  6       /* Block size of disk is available*/
 #define VIRTIO_BLK_F_SCSI      7       /* Supports scsi command passthru */
-#define VIRTIO_BLK_F_FLUSH     9       /* Cache flush command support */
+#define VIRTIO_BLK_F_WCE       9       /* Writeback mode enabled after reset */
 #define VIRTIO_BLK_F_TOPOLOGY  10      /* Topology information is available */
+#define VIRTIO_BLK_F_CONFIG_WCE        11      /* Writeback mode available in config */
+
+#ifndef __KERNEL__
+/* Old (deprecated) name for VIRTIO_BLK_F_WCE. */
+#define VIRTIO_BLK_F_FLUSH VIRTIO_BLK_F_WCE
+#endif
 
 #define VIRTIO_BLK_ID_BYTES    20      /* ID string length */
 
@@ -69,6 +75,8 @@ struct virtio_blk_config {
        /* optimal sustained I/O size in logical blocks. */
        __u32 opt_io_size;
 
+       /* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */
+       __u8 wce;
 } __attribute__((packed));
 
 /*
index 7529b854b7fd96ff11608f41313454e658ee25f8..270fb22c58112870fc848fac55d9102397897b65 100644 (file)
@@ -32,7 +32,7 @@
 #define VIRTIO_ID_NET          1 /* virtio net */
 #define VIRTIO_ID_BLOCK                2 /* virtio block */
 #define VIRTIO_ID_CONSOLE      3 /* virtio console */
-#define VIRTIO_ID_RNG          4 /* virtio ring */
+#define VIRTIO_ID_RNG          4 /* virtio rng */
 #define VIRTIO_ID_BALLOON      5 /* virtio balloon */
 #define VIRTIO_ID_RPMSG                7 /* virtio remote processor messaging */
 #define VIRTIO_ID_SCSI         8 /* virtio scsi */
index 06f8e38582512eb7be8713f5579887cdd559a813..57f7b1091511b3103e9850c893a7fe7fb7a37f3a 100644 (file)
@@ -30,6 +30,7 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT,
                FOR_ALL_ZONES(PGSTEAL_DIRECT),
                FOR_ALL_ZONES(PGSCAN_KSWAPD),
                FOR_ALL_ZONES(PGSCAN_DIRECT),
+               PGSCAN_DIRECT_THROTTLE,
 #ifdef CONFIG_NUMA
                PGSCAN_ZONE_RECLAIM_FAILED,
 #endif
index dcdfc2bda922eb7012d59fe7eb330aabd1a5c24e..6071e911c7f4deff2258ea404416f133c4b6ec36 100644 (file)
@@ -32,7 +32,7 @@ struct vm_struct {
        struct page             **pages;
        unsigned int            nr_pages;
        phys_addr_t             phys_addr;
-       void                    *caller;
+       const void              *caller;
 };
 
 /*
@@ -62,7 +62,7 @@ extern void *vmalloc_32_user(unsigned long size);
 extern void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot);
 extern void *__vmalloc_node_range(unsigned long size, unsigned long align,
                        unsigned long start, unsigned long end, gfp_t gfp_mask,
-                       pgprot_t prot, int node, void *caller);
+                       pgprot_t prot, int node, const void *caller);
 extern void vfree(const void *addr);
 
 extern void *vmap(struct page **pages, unsigned int count,
@@ -85,14 +85,15 @@ static inline size_t get_vm_area_size(const struct vm_struct *area)
 
 extern struct vm_struct *get_vm_area(unsigned long size, unsigned long flags);
 extern struct vm_struct *get_vm_area_caller(unsigned long size,
-                                       unsigned long flags, void *caller);
+                                       unsigned long flags, const void *caller);
 extern struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
                                        unsigned long start, unsigned long end);
 extern struct vm_struct *__get_vm_area_caller(unsigned long size,
                                        unsigned long flags,
                                        unsigned long start, unsigned long end,
-                                       void *caller);
+                                       const void *caller);
 extern struct vm_struct *remove_vm_area(const void *addr);
+extern struct vm_struct *find_vm_area(const void *addr);
 
 extern int map_vm_area(struct vm_struct *area, pgprot_t prot,
                        struct page ***pages);
index 65efb92da996cecfcd1c3648e7ee4a450fe8dd01..ad2cfd53dadce54b28ff52adb73f923c6dde2f55 100644 (file)
@@ -179,11 +179,6 @@ extern void zone_statistics(struct zone *, struct zone *, gfp_t gfp);
 #define add_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, __d)
 #define sub_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, -(__d))
 
-static inline void zap_zone_vm_stats(struct zone *zone)
-{
-       memset(zone->vm_stat, 0, sizeof(zone->vm_stat));
-}
-
 extern void inc_zone_state(struct zone *, enum zone_stat_item);
 
 #ifdef CONFIG_SMP
index 6d0a0fcd80e7fcc820042d996221fa7ad3442b10..c66fe3332d8376ab4fd226d60d7afd16e94b1a9f 100644 (file)
@@ -189,9 +189,4 @@ void tag_pages_for_writeback(struct address_space *mapping,
 
 void account_page_redirty(struct page *page);
 
-/* pdflush.c */
-extern int nr_pdflush_threads; /* Global so it can be exported to sysctl
-                                  read-only. */
-
-
 #endif         /* WRITEBACK_H */
diff --git a/include/media/adv7393.h b/include/media/adv7393.h
new file mode 100644 (file)
index 0000000..b28edf3
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * ADV7393 header file
+ *
+ * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/
+ * Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
+ *
+ * Based on ADV7343 driver,
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - 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 ADV7393_H
+#define ADV7393_H
+
+#define ADV7393_COMPOSITE_ID   (0)
+#define ADV7393_COMPONENT_ID   (1)
+#define ADV7393_SVIDEO_ID      (2)
+
+#endif                         /* End of #ifndef ADV7393_H */
index bd8217c2577c4cbcf65481056b353ca0981262b8..d8f6ab1943e4e492679b1b4ae58ac1244496cc72 100644 (file)
@@ -50,6 +50,8 @@ struct vpif_display_config {
        const char **output;
        int output_count;
        const char *card_name;
+       bool ch2_clip_en;
+       bool ch3_clip_en;
 };
 
 struct vpif_input {
index 67797bf5d432f6a648961c742d70ed1d008d0199..0142736a59db86a3dff4c9592793c6f6f6193e72 100644 (file)
 #define __GPIO_IR_RECV_H__
 
 struct gpio_ir_recv_platform_data {
-       int gpio_nr;
-       bool active_low;
+       int             gpio_nr;
+       bool            active_low;
+       u64             allowed_protos;
+       const char      *map_name;
 };
 
 #endif /* __GPIO_IR_RECV_H__ */
index e839a78bb9c58ade07ab0733b8c6ed0349e2ff6b..03fd63edd133264b99c38b650b473e4eb5b97df6 100644 (file)
@@ -3,6 +3,7 @@
 
 struct mt9t001_platform_data {
        unsigned int clk_pol:1;
+       unsigned int ext_clk;
 };
 
 #endif
index 7395c815939df242bf02aa23bd19928b04590909..58f914a40b208586db30c85cf83346703a3220fe 100644 (file)
@@ -180,6 +180,9 @@ enum {
        /* module adv7343: just ident 7343 */
        V4L2_IDENT_ADV7343 = 7343,
 
+       /* module adv7393: just ident 7393 */
+       V4L2_IDENT_ADV7393 = 7393,
+
        /* module saa7706h: just ident 7706 */
        V4L2_IDENT_SAA7706H = 7706,
 
index a056e6ee1b6820c05d87ae6e178cc0a6008dc814..5c416cdc88d5d44eae38ce5e8445d5c2de6d2c78 100644 (file)
@@ -100,6 +100,9 @@ struct video_device
        /* Control handler associated with this device node. May be NULL. */
        struct v4l2_ctrl_handler *ctrl_handler;
 
+       /* vb2_queue associated with this device node. May be NULL. */
+       struct vb2_queue *queue;
+
        /* Priority state. If NULL, then v4l2_dev->prio will be used. */
        struct v4l2_prio_state *prio;
 
index d8b76f7392f8d793c6abdf0a2a1a46b53fa115bc..e614c9c15e56ce0b396a8b03d47c81e08e197691 100644 (file)
@@ -230,6 +230,8 @@ struct v4l2_ioctl_ops {
                                        struct v4l2_frequency *a);
        int (*vidioc_s_frequency)      (struct file *file, void *fh,
                                        struct v4l2_frequency *a);
+       int (*vidioc_enum_freq_bands) (struct file *file, void *fh,
+                                   struct v4l2_frequency_band *band);
 
        /* Sliced VBI cap */
        int (*vidioc_g_sliced_vbi_cap) (struct file *file, void *fh,
@@ -295,28 +297,19 @@ struct v4l2_ioctl_ops {
 #define V4L2_DEBUG_IOCTL     0x01
 #define V4L2_DEBUG_IOCTL_ARG 0x02
 
-/* Use this macro for non-I2C drivers. Pass the driver name as the first arg. */
-#define v4l_print_ioctl(name, cmd)              \
-       do {                                     \
-               printk(KERN_DEBUG "%s: ", name); \
-               v4l_printk_ioctl(cmd);           \
-       } while (0)
-
-/* Use this macro in I2C drivers where 'client' is the struct i2c_client
-   pointer */
-#define v4l_i2c_print_ioctl(client, cmd)                  \
-       do {                                               \
-               v4l_client_printk(KERN_DEBUG, client, ""); \
-               v4l_printk_ioctl(cmd);                     \
-       } while (0)
-
 /*  Video standard functions  */
 extern const char *v4l2_norm_to_name(v4l2_std_id id);
 extern void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod);
 extern int v4l2_video_std_construct(struct v4l2_standard *vs,
                                    int id, const char *name);
-/* Prints the ioctl in a human-readable format */
-extern void v4l_printk_ioctl(unsigned int cmd);
+/* Prints the ioctl in a human-readable format. If prefix != NULL,
+   then do printk(KERN_DEBUG "%s: ", prefix) first. */
+extern void v4l_printk_ioctl(const char *prefix, unsigned int cmd);
+
+/* Internal use only: get the mutex (if any) that we need to lock for the
+   given command. */
+struct video_device;
+extern struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd);
 
 /* names for fancy debug output */
 extern const char *v4l2_field_names[];
index 90ed895e217d3b04eea44269ee9fc9d35da05f69..8c6e825940e59c0258f9e37cf685dddd750a64ce 100644 (file)
@@ -72,7 +72,6 @@ struct videobuf_buffer {
        unsigned int            height;
        unsigned int            bytesperline; /* use only if != 0 */
        unsigned long           size;
-       unsigned int            input;
        enum v4l2_field         field;
        enum videobuf_state     state;
        struct list_head        stream;  /* QBUF/DQBUF list */
@@ -142,7 +141,6 @@ struct videobuf_queue {
        wait_queue_head_t          wait; /* wait if queue is empty */
 
        enum v4l2_buf_type         type;
-       unsigned int               inputs; /* for V4L2_BUF_FLAG_INPUT */
        unsigned int               msize;
        enum v4l2_field            field;
        enum v4l2_field            last;   /* for field=V4L2_FIELD_ALTERNATE */
index a15d1f1b319effd43a404af98bcff323bfa798e5..8dd9b6cc296b57c2ee2b37612f796d6cb975aca1 100644 (file)
@@ -244,12 +244,23 @@ struct vb2_ops {
        void (*buf_queue)(struct vb2_buffer *vb);
 };
 
+struct v4l2_fh;
+
 /**
  * struct vb2_queue - a videobuf queue
  *
  * @type:      queue type (see V4L2_BUF_TYPE_* in linux/videodev2.h
  * @io_modes:  supported io methods (see vb2_io_modes enum)
  * @io_flags:  additional io flags (see vb2_fileio_flags enum)
+ * @lock:      pointer to a mutex that protects the vb2_queue struct. The
+ *             driver can set this to a mutex to let the v4l2 core serialize
+ *             the queuing ioctls. If the driver wants to handle locking
+ *             itself, then this should be set to NULL. This lock is not used
+ *             by the videobuf2 core API.
+ * @owner:     The filehandle that 'owns' the buffers, i.e. the filehandle
+ *             that called reqbufs, create_buffers or started fileio.
+ *             This field is not used by the videobuf2 core API, but it allows
+ *             drivers to easily associate an owner filehandle with the queue.
  * @ops:       driver-specific callbacks
  * @mem_ops:   memory allocator specific callbacks
  * @drv_priv:  driver private data
@@ -273,6 +284,8 @@ struct vb2_queue {
        enum v4l2_buf_type              type;
        unsigned int                    io_modes;
        unsigned int                    io_flags;
+       struct mutex                    *lock;
+       struct v4l2_fh                  *owner;
 
        const struct vb2_ops            *ops;
        const struct vb2_mem_ops        *mem_ops;
@@ -404,4 +417,45 @@ vb2_plane_size(struct vb2_buffer *vb, unsigned int plane_no)
        return 0;
 }
 
+/*
+ * The following functions are not part of the vb2 core API, but are simple
+ * helper functions that you can use in your struct v4l2_file_operations,
+ * struct v4l2_ioctl_ops and struct vb2_ops. They will serialize if vb2_queue->lock
+ * or video_device->lock is set, and they will set and test vb2_queue->owner
+ * to check if the calling filehandle is permitted to do the queuing operation.
+ */
+
+/* struct v4l2_ioctl_ops helpers */
+
+int vb2_ioctl_reqbufs(struct file *file, void *priv,
+                         struct v4l2_requestbuffers *p);
+int vb2_ioctl_create_bufs(struct file *file, void *priv,
+                         struct v4l2_create_buffers *p);
+int vb2_ioctl_prepare_buf(struct file *file, void *priv,
+                         struct v4l2_buffer *p);
+int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p);
+int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p);
+int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p);
+int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i);
+int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i);
+
+/* struct v4l2_file_operations helpers */
+
+int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma);
+int vb2_fop_release(struct file *file);
+ssize_t vb2_fop_write(struct file *file, char __user *buf,
+               size_t count, loff_t *ppos);
+ssize_t vb2_fop_read(struct file *file, char __user *buf,
+               size_t count, loff_t *ppos);
+unsigned int vb2_fop_poll(struct file *file, poll_table *wait);
+#ifndef CONFIG_MMU
+unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr,
+               unsigned long len, unsigned long pgoff, unsigned long flags);
+#endif
+
+/* struct vb2_ops helpers, only use if vq->lock is non-NULL. */
+
+void vb2_ops_wait_prepare(struct vb2_queue *vq);
+void vb2_ops_wait_finish(struct vb2_queue *vq);
+
 #endif /* _MEDIA_VIDEOBUF2_CORE_H */
index 19ae1e3505678bb32aa810823d619b06b88993d4..8197f87d6c617ccfec71b4113b01e11064e3b529 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * videobuf2-dma-coherent.h - DMA coherent memory allocator for videobuf2
+ * videobuf2-dma-contig.h - DMA contig memory allocator for videobuf2
  *
  * Copyright (C) 2010 Samsung Electronics
  *
@@ -10,8 +10,8 @@
  * the Free Software Foundation.
  */
 
-#ifndef _MEDIA_VIDEOBUF2_DMA_COHERENT_H
-#define _MEDIA_VIDEOBUF2_DMA_COHERENT_H
+#ifndef _MEDIA_VIDEOBUF2_DMA_CONTIG_H
+#define _MEDIA_VIDEOBUF2_DMA_CONTIG_H
 
 #include <media/videobuf2-core.h>
 #include <linux/dma-mapping.h>
index 7f7df93f37cd0d3b3259775ebf6ab23743b3c564..b630dae03411ae69694e842d75108a254c0584aa 100644 (file)
@@ -3,6 +3,7 @@
 #define _ARP_H
 
 #include <linux/if_arp.h>
+#include <linux/hash.h>
 #include <net/neighbour.h>
 
 
@@ -10,7 +11,7 @@ extern struct neigh_table arp_tbl;
 
 static inline u32 arp_hashfn(u32 key, const struct net_device *dev, u32 hash_rnd)
 {
-       u32 val = key ^ dev->ifindex;
+       u32 val = key ^ hash32_ptr(dev);
 
        return val * hash_rnd;
 }
index 493fa0c790051ecd10a41050e7c7a05d323a52b0..3d254e10ff30e7ab3c5a4fee2ee0b38f1309bd94 100644 (file)
@@ -96,6 +96,7 @@ enum ieee80211_band {
  *     is not permitted.
  * @IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel
  *     is not permitted.
+ * @IEEE80211_CHAN_NO_OFDM: OFDM is not allowed on this channel.
  */
 enum ieee80211_channel_flags {
        IEEE80211_CHAN_DISABLED         = 1<<0,
@@ -104,6 +105,7 @@ enum ieee80211_channel_flags {
        IEEE80211_CHAN_RADAR            = 1<<3,
        IEEE80211_CHAN_NO_HT40PLUS      = 1<<4,
        IEEE80211_CHAN_NO_HT40MINUS     = 1<<5,
+       IEEE80211_CHAN_NO_OFDM          = 1<<6,
 };
 
 #define IEEE80211_CHAN_NO_HT40 \
index baf59789006427bc7c373449b62b271e6e777d15..77f52f7dc82307baa4c6193946363385bb5511b7 100644 (file)
@@ -396,11 +396,15 @@ static inline void dst_confirm(struct dst_entry *dst)
 static inline int dst_neigh_output(struct dst_entry *dst, struct neighbour *n,
                                   struct sk_buff *skb)
 {
-       struct hh_cache *hh;
+       const struct hh_cache *hh;
+
+       if (dst->pending_confirm) {
+               unsigned long now = jiffies;
 
-       if (unlikely(dst->pending_confirm)) {
-               n->confirmed = jiffies;
                dst->pending_confirm = 0;
+               /* avoid dirtying neighbour */
+               if (n->confirmed != now)
+                       n->confirmed = now;
        }
 
        hh = &n->hh;
index 358fb86f57eb952816bc76736b7b85644dd77184..e03047f7090bb3419c2aa6f8b39f3a7ff0494f8a 100644 (file)
@@ -5,6 +5,8 @@
 #include <linux/netdevice.h>
 #include <linux/ip6_tunnel.h>
 
+#define IP6TUNNEL_ERR_TIMEO (30*HZ)
+
 /* capable of sending packets */
 #define IP6_TNL_F_CAP_XMIT 0x10000
 /* capable of receiving packets */
 /* determine capability on a per-packet basis */
 #define IP6_TNL_F_CAP_PER_PACKET 0x40000
 
-/* IPv6 tunnel */
+struct __ip6_tnl_parm {
+       char name[IFNAMSIZ];    /* name of tunnel device */
+       int link;               /* ifindex of underlying L2 interface */
+       __u8 proto;             /* tunnel protocol */
+       __u8 encap_limit;       /* encapsulation limit for tunnel */
+       __u8 hop_limit;         /* hop limit for tunnel */
+       __be32 flowinfo;        /* traffic class and flowlabel for tunnel */
+       __u32 flags;            /* tunnel flags */
+       struct in6_addr laddr;  /* local tunnel end-point address */
+       struct in6_addr raddr;  /* remote tunnel end-point address */
+
+       __be16                  i_flags;
+       __be16                  o_flags;
+       __be32                  i_key;
+       __be32                  o_key;
+};
 
+/* IPv6 tunnel */
 struct ip6_tnl {
        struct ip6_tnl __rcu *next;     /* next tunnel in list */
        struct net_device *dev; /* virtual device associated with tunnel */
-       struct ip6_tnl_parm parms;      /* tunnel configuration parameters */
+       struct __ip6_tnl_parm parms;    /* tunnel configuration parameters */
        struct flowi fl;        /* flowi template for xmit */
        struct dst_entry *dst_cache;    /* cached dst */
        u32 dst_cookie;
+
+       int err_count;
+       unsigned long err_time;
+
+       /* These fields used only by GRE */
+       __u32 i_seqno;  /* The last seen seqno  */
+       __u32 o_seqno;  /* The last output seqno */
+       int hlen;       /* Precalculated GRE header length */
+       int mlink;
 };
 
 /* Tunnel encapsulation limit destination sub-option */
@@ -31,4 +58,14 @@ struct ipv6_tlv_tnl_enc_lim {
        __u8 encap_limit;       /* tunnel encapsulation limit   */
 } __packed;
 
+struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t);
+void ip6_tnl_dst_reset(struct ip6_tnl *t);
+void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst);
+int ip6_tnl_rcv_ctl(struct ip6_tnl *t, const struct in6_addr *laddr,
+               const struct in6_addr *raddr);
+int ip6_tnl_xmit_ctl(struct ip6_tnl *t);
+__u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw);
+__u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr,
+                            const struct in6_addr *raddr);
+
 #endif
index 01c34b363a34d1f7b126d55dffd8e170963528ed..6d01fb00ff2b613f7061f16e91c6890b2c927e61 100644 (file)
@@ -34,6 +34,7 @@
 #define NEXTHDR_IPV6           41      /* IPv6 in IPv6 */
 #define NEXTHDR_ROUTING                43      /* Routing header. */
 #define NEXTHDR_FRAGMENT       44      /* Fragmentation/reassembly header. */
+#define NEXTHDR_GRE            47      /* GRE header. */
 #define NEXTHDR_ESP            50      /* Encapsulating security payload. */
 #define NEXTHDR_AUTH           51      /* Authentication header. */
 #define NEXTHDR_ICMP           58      /* ICMP for IPv6. */
index 96a3b5c03e37d965b51e9d9754af3969df6146fc..980d263765cf41059ede684d54abafb6a9e66c6c 100644 (file)
@@ -49,6 +49,7 @@ enum {
 #include <linux/types.h>
 #include <linux/if_arp.h>
 #include <linux/netdevice.h>
+#include <linux/hash.h>
 
 #include <net/neighbour.h>
 
@@ -134,7 +135,7 @@ static inline u32 ndisc_hashfn(const void *pkey, const struct net_device *dev, _
 {
        const u32 *p32 = pkey;
 
-       return (((p32[0] ^ dev->ifindex) * hash_rnd[0]) +
+       return (((p32[0] ^ hash32_ptr(dev)) * hash_rnd[0]) +
                (p32[1] * hash_rnd[1]) +
                (p32[2] * hash_rnd[2]) +
                (p32[3] * hash_rnd[3]));
index 344d8988842a527fbec3bffc674cda865f67b15c..0dab173e27da6e8e66a8eea4913f5b0230f612bd 100644 (file)
@@ -334,18 +334,22 @@ static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb)
 }
 #endif
 
-static inline int neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb)
+static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb)
 {
        unsigned int seq;
        int hh_len;
 
        do {
-               int hh_alen;
-
                seq = read_seqbegin(&hh->hh_lock);
                hh_len = hh->hh_len;
-               hh_alen = HH_DATA_ALIGN(hh_len);
-               memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);
+               if (likely(hh_len <= HH_DATA_MOD)) {
+                       /* this is inlined by gcc */
+                       memcpy(skb->data - HH_DATA_MOD, hh->hh_data, HH_DATA_MOD);
+               } else {
+                       int hh_alen = HH_DATA_ALIGN(hh_len);
+
+                       memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);
+               }
        } while (read_seqretry(&hh->hh_lock, seq));
 
        skb_push(skb, hh_len);
index ae1cd6c9ba521bb6ca9a57706b4ca77180bdcc7a..5ae57f1ab7551e556c0145dcb51206d5de08ecc6 100644 (file)
@@ -15,6 +15,7 @@
 #include <net/netns/packet.h>
 #include <net/netns/ipv4.h>
 #include <net/netns/ipv6.h>
+#include <net/netns/sctp.h>
 #include <net/netns/dccp.h>
 #include <net/netns/x_tables.h>
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
@@ -66,6 +67,7 @@ struct net {
        struct hlist_head       *dev_name_head;
        struct hlist_head       *dev_index_head;
        unsigned int            dev_base_seq;   /* protected by rtnl_mutex */
+       int                     ifindex;
 
        /* core fib_rules */
        struct list_head        rules_ops;
@@ -80,6 +82,9 @@ struct net {
 #if IS_ENABLED(CONFIG_IPV6)
        struct netns_ipv6       ipv6;
 #endif
+#if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE)
+       struct netns_sctp       sctp;
+#endif
 #if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE)
        struct netns_dccp       dccp;
 #endif
@@ -104,6 +109,13 @@ struct net {
        struct sock             *diag_nlsk;
 };
 
+/*
+ * ifindex generation is per-net namespace, and loopback is
+ * always the 1st device in ns (see net_dev_init), thus any
+ * loopback device should get ifindex 1
+ */
+
+#define LOOPBACK_IFINDEX       1
 
 #include <linux/seq_file_net.h>
 
index 785f37a3b44ee80e1336d7301bd54c6ff48b7ba7..09175d5d1fbf15e56dbf4ae7406e886f0dce1602 100644 (file)
  *   nla_put_u16(skb, type, value)     add u16 attribute to skb
  *   nla_put_u32(skb, type, value)     add u32 attribute to skb
  *   nla_put_u64(skb, type, value)     add u64 attribute to skb
+ *   nla_put_s8(skb, type, value)      add s8 attribute to skb
+ *   nla_put_s16(skb, type, value)     add s16 attribute to skb
+ *   nla_put_s32(skb, type, value)     add s32 attribute to skb
+ *   nla_put_s64(skb, type, value)     add s64 attribute to skb
  *   nla_put_string(skb, type, str)    add string attribute to skb
  *   nla_put_flag(skb, type)           add flag attribute to skb
  *   nla_put_msecs(skb, type, jiffies) add msecs attribute to skb
  *   nla_get_u16(nla)                  get payload for a u16 attribute
  *   nla_get_u32(nla)                  get payload for a u32 attribute
  *   nla_get_u64(nla)                  get payload for a u64 attribute
+ *   nla_get_s8(nla)                   get payload for a s8 attribute
+ *   nla_get_s16(nla)                  get payload for a s16 attribute
+ *   nla_get_s32(nla)                  get payload for a s32 attribute
+ *   nla_get_s64(nla)                  get payload for a s64 attribute
  *   nla_get_flag(nla)                 return 1 if flag is true
  *   nla_get_msecs(nla)                        get payload for a msecs attribute
  *
@@ -160,6 +168,10 @@ enum {
        NLA_NESTED_COMPAT,
        NLA_NUL_STRING,
        NLA_BINARY,
+       NLA_S8,
+       NLA_S16,
+       NLA_S32,
+       NLA_S64,
        __NLA_TYPE_MAX,
 };
 
@@ -183,6 +195,8 @@ enum {
  *    NLA_NESTED_COMPAT    Minimum length of structure payload
  *    NLA_U8, NLA_U16,
  *    NLA_U32, NLA_U64,
+ *    NLA_S8, NLA_S16,
+ *    NLA_S32, NLA_S64,
  *    NLA_MSECS            Leaving the length field zero will verify the
  *                         given type fits, using it verifies minimum length
  *                         just like "All other"
@@ -878,6 +892,50 @@ static inline int nla_put_le64(struct sk_buff *skb, int attrtype, __le64 value)
        return nla_put(skb, attrtype, sizeof(__le64), &value);
 }
 
+/**
+ * nla_put_s8 - Add a s8 netlink attribute to a socket buffer
+ * @skb: socket buffer to add attribute to
+ * @attrtype: attribute type
+ * @value: numeric value
+ */
+static inline int nla_put_s8(struct sk_buff *skb, int attrtype, s8 value)
+{
+       return nla_put(skb, attrtype, sizeof(s8), &value);
+}
+
+/**
+ * nla_put_s16 - Add a s16 netlink attribute to a socket buffer
+ * @skb: socket buffer to add attribute to
+ * @attrtype: attribute type
+ * @value: numeric value
+ */
+static inline int nla_put_s16(struct sk_buff *skb, int attrtype, s16 value)
+{
+       return nla_put(skb, attrtype, sizeof(s16), &value);
+}
+
+/**
+ * nla_put_s32 - Add a s32 netlink attribute to a socket buffer
+ * @skb: socket buffer to add attribute to
+ * @attrtype: attribute type
+ * @value: numeric value
+ */
+static inline int nla_put_s32(struct sk_buff *skb, int attrtype, s32 value)
+{
+       return nla_put(skb, attrtype, sizeof(s32), &value);
+}
+
+/**
+ * nla_put_s64 - Add a s64 netlink attribute to a socket buffer
+ * @skb: socket buffer to add attribute to
+ * @attrtype: attribute type
+ * @value: numeric value
+ */
+static inline int nla_put_s64(struct sk_buff *skb, int attrtype, s64 value)
+{
+       return nla_put(skb, attrtype, sizeof(s64), &value);
+}
+
 /**
  * nla_put_string - Add a string netlink attribute to a socket buffer
  * @skb: socket buffer to add attribute to
@@ -993,6 +1051,46 @@ static inline __be64 nla_get_be64(const struct nlattr *nla)
        return tmp;
 }
 
+/**
+ * nla_get_s32 - return payload of s32 attribute
+ * @nla: s32 netlink attribute
+ */
+static inline s32 nla_get_s32(const struct nlattr *nla)
+{
+       return *(s32 *) nla_data(nla);
+}
+
+/**
+ * nla_get_s16 - return payload of s16 attribute
+ * @nla: s16 netlink attribute
+ */
+static inline s16 nla_get_s16(const struct nlattr *nla)
+{
+       return *(s16 *) nla_data(nla);
+}
+
+/**
+ * nla_get_s8 - return payload of s8 attribute
+ * @nla: s8 netlink attribute
+ */
+static inline s8 nla_get_s8(const struct nlattr *nla)
+{
+       return *(s8 *) nla_data(nla);
+}
+
+/**
+ * nla_get_s64 - return payload of s64 attribute
+ * @nla: s64 netlink attribute
+ */
+static inline s64 nla_get_s64(const struct nlattr *nla)
+{
+       s64 tmp;
+
+       nla_memcpy(&tmp, nla, sizeof(tmp));
+
+       return tmp;
+}
+
 /**
  * nla_get_flag - return payload of flag attribute
  * @nla: flag netlink attribute
diff --git a/include/net/netns/sctp.h b/include/net/netns/sctp.h
new file mode 100644 (file)
index 0000000..5e5eb1f
--- /dev/null
@@ -0,0 +1,131 @@
+#ifndef __NETNS_SCTP_H__
+#define __NETNS_SCTP_H__
+
+struct sock;
+struct proc_dir_entry;
+struct sctp_mib;
+struct ctl_table_header;
+
+struct netns_sctp {
+       DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics);
+
+#ifdef CONFIG_PROC_FS
+       struct proc_dir_entry *proc_net_sctp;
+#endif
+#ifdef CONFIG_SYSCTL
+       struct ctl_table_header *sysctl_header;
+#endif
+       /* This is the global socket data structure used for responding to
+        * the Out-of-the-blue (OOTB) packets.  A control sock will be created
+        * for this socket at the initialization time.
+        */
+       struct sock *ctl_sock;
+
+       /* This is the global local address list.
+        * We actively maintain this complete list of addresses on
+        * the system by catching address add/delete events.
+        *
+        * It is a list of sctp_sockaddr_entry.
+        */
+       struct list_head local_addr_list;
+       struct list_head addr_waitq;
+       struct timer_list addr_wq_timer;
+       struct list_head auto_asconf_splist;
+       spinlock_t addr_wq_lock;
+
+       /* Lock that protects the local_addr_list writers */
+       spinlock_t local_addr_lock;
+
+       /* RFC2960 Section 14. Suggested SCTP Protocol Parameter Values
+        *
+        * The following protocol parameters are RECOMMENDED:
+        *
+        * RTO.Initial              - 3  seconds
+        * RTO.Min                  - 1  second
+        * RTO.Max                 -  60 seconds
+        * RTO.Alpha                - 1/8  (3 when converted to right shifts.)
+        * RTO.Beta                 - 1/4  (2 when converted to right shifts.)
+        */
+       unsigned int rto_initial;
+       unsigned int rto_min;
+       unsigned int rto_max;
+
+       /* Note: rto_alpha and rto_beta are really defined as inverse
+        * powers of two to facilitate integer operations.
+        */
+       int rto_alpha;
+       int rto_beta;
+
+       /* Max.Burst                - 4 */
+       int max_burst;
+
+       /* Whether Cookie Preservative is enabled(1) or not(0) */
+       int cookie_preserve_enable;
+
+       /* Valid.Cookie.Life        - 60  seconds  */
+       unsigned int valid_cookie_life;
+
+       /* Delayed SACK timeout  200ms default*/
+       unsigned int sack_timeout;
+
+       /* HB.interval              - 30 seconds  */
+       unsigned int hb_interval;
+
+       /* Association.Max.Retrans  - 10 attempts
+        * Path.Max.Retrans         - 5  attempts (per destination address)
+        * Max.Init.Retransmits     - 8  attempts
+        */
+       int max_retrans_association;
+       int max_retrans_path;
+       int max_retrans_init;
+       /* Potentially-Failed.Max.Retrans sysctl value
+        * taken from:
+        * http://tools.ietf.org/html/draft-nishida-tsvwg-sctp-failover-05
+        */
+       int pf_retrans;
+
+       /*
+        * Policy for preforming sctp/socket accounting
+        * 0   - do socket level accounting, all assocs share sk_sndbuf
+        * 1   - do sctp accounting, each asoc may use sk_sndbuf bytes
+        */
+       int sndbuf_policy;
+
+       /*
+        * Policy for preforming sctp/socket accounting
+        * 0   - do socket level accounting, all assocs share sk_rcvbuf
+        * 1   - do sctp accounting, each asoc may use sk_rcvbuf bytes
+        */
+       int rcvbuf_policy;
+
+       int default_auto_asconf;
+
+       /* Flag to indicate if addip is enabled. */
+       int addip_enable;
+       int addip_noauth;
+
+       /* Flag to indicate if PR-SCTP is enabled. */
+       int prsctp_enable;
+
+       /* Flag to idicate if SCTP-AUTH is enabled */
+       int auth_enable;
+
+       /*
+        * Policy to control SCTP IPv4 address scoping
+        * 0   - Disable IPv4 address scoping
+        * 1   - Enable IPv4 address scoping
+        * 2   - Selectively allow only IPv4 private addresses
+        * 3   - Selectively allow only IPv4 link local address
+        */
+       int scope_policy;
+
+       /* Threshold for rwnd update SACKS.  Receive buffer shifted this many
+        * bits is an indicator of when to send and window update SACK.
+        */
+       int rwnd_upd_shift;
+
+       /* Threshold for autoclose timeout, in seconds. */
+       unsigned long max_autoclose;
+};
+
+#endif /* __NETNS_SCTP_H__ */
index ff499640528b0012fd76c09141e978688f00b0a9..9c6414f553f91f2256698323ea5d8ec72db4e868 100644 (file)
 /*
  * sctp/protocol.c
  */
-extern struct sock *sctp_get_ctl_sock(void);
-extern int sctp_copy_local_addr_list(struct sctp_bind_addr *,
+extern int sctp_copy_local_addr_list(struct net *, struct sctp_bind_addr *,
                                     sctp_scope_t, gfp_t gfp,
                                     int flags);
 extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family);
 extern int sctp_register_pf(struct sctp_pf *, sa_family_t);
-extern void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *, int);
+extern void sctp_addr_wq_mgmt(struct net *, struct sctp_sockaddr_entry *, int);
 
 /*
  * sctp/socket.c
@@ -140,12 +139,12 @@ extern int sctp_asconf_mgmt(struct sctp_sock *, struct sctp_sockaddr_entry *);
 /*
  * sctp/primitive.c
  */
-int sctp_primitive_ASSOCIATE(struct sctp_association *, void *arg);
-int sctp_primitive_SHUTDOWN(struct sctp_association *, void *arg);
-int sctp_primitive_ABORT(struct sctp_association *, void *arg);
-int sctp_primitive_SEND(struct sctp_association *, void *arg);
-int sctp_primitive_REQUESTHEARTBEAT(struct sctp_association *, void *arg);
-int sctp_primitive_ASCONF(struct sctp_association *, void *arg);
+int sctp_primitive_ASSOCIATE(struct net *, struct sctp_association *, void *arg);
+int sctp_primitive_SHUTDOWN(struct net *, struct sctp_association *, void *arg);
+int sctp_primitive_ABORT(struct net *, struct sctp_association *, void *arg);
+int sctp_primitive_SEND(struct net *, struct sctp_association *, void *arg);
+int sctp_primitive_REQUESTHEARTBEAT(struct net *, struct sctp_association *, void *arg);
+int sctp_primitive_ASCONF(struct net *, struct sctp_association *, void *arg);
 
 /*
  * sctp/input.c
@@ -156,7 +155,7 @@ void sctp_hash_established(struct sctp_association *);
 void sctp_unhash_established(struct sctp_association *);
 void sctp_hash_endpoint(struct sctp_endpoint *);
 void sctp_unhash_endpoint(struct sctp_endpoint *);
-struct sock *sctp_err_lookup(int family, struct sk_buff *,
+struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *,
                             struct sctphdr *, struct sctp_association **,
                             struct sctp_transport **);
 void sctp_err_finish(struct sock *, struct sctp_association *);
@@ -173,14 +172,14 @@ void sctp_backlog_migrate(struct sctp_association *assoc,
 /*
  * sctp/proc.c
  */
-int sctp_snmp_proc_init(void);
-void sctp_snmp_proc_exit(void);
-int sctp_eps_proc_init(void);
-void sctp_eps_proc_exit(void);
-int sctp_assocs_proc_init(void);
-void sctp_assocs_proc_exit(void);
-int sctp_remaddr_proc_init(void);
-void sctp_remaddr_proc_exit(void);
+int sctp_snmp_proc_init(struct net *net);
+void sctp_snmp_proc_exit(struct net *net);
+int sctp_eps_proc_init(struct net *net);
+void sctp_eps_proc_exit(struct net *net);
+int sctp_assocs_proc_init(struct net *net);
+void sctp_assocs_proc_exit(struct net *net);
+int sctp_remaddr_proc_init(struct net *net);
+void sctp_remaddr_proc_exit(struct net *net);
 
 
 /*
@@ -222,11 +221,10 @@ extern struct kmem_cache *sctp_bucket_cachep __read_mostly;
 #define sctp_bh_unlock_sock(sk)  bh_unlock_sock(sk)
 
 /* SCTP SNMP MIB stats handlers */
-DECLARE_SNMP_STAT(struct sctp_mib, sctp_statistics);
-#define SCTP_INC_STATS(field)      SNMP_INC_STATS(sctp_statistics, field)
-#define SCTP_INC_STATS_BH(field)   SNMP_INC_STATS_BH(sctp_statistics, field)
-#define SCTP_INC_STATS_USER(field) SNMP_INC_STATS_USER(sctp_statistics, field)
-#define SCTP_DEC_STATS(field)      SNMP_DEC_STATS(sctp_statistics, field)
+#define SCTP_INC_STATS(net, field)      SNMP_INC_STATS((net)->sctp.sctp_statistics, field)
+#define SCTP_INC_STATS_BH(net, field)   SNMP_INC_STATS_BH((net)->sctp.sctp_statistics, field)
+#define SCTP_INC_STATS_USER(net, field) SNMP_INC_STATS_USER((net)->sctp.sctp_statistics, field)
+#define SCTP_DEC_STATS(net, field)      SNMP_DEC_STATS((net)->sctp.sctp_statistics, field)
 
 #endif /* !TEST_FRAME */
 
@@ -361,25 +359,29 @@ atomic_t sctp_dbg_objcnt_## name = ATOMIC_INIT(0)
 #define SCTP_DBG_OBJCNT_ENTRY(name) \
 {.label= #name, .counter= &sctp_dbg_objcnt_## name}
 
-void sctp_dbg_objcnt_init(void);
-void sctp_dbg_objcnt_exit(void);
+void sctp_dbg_objcnt_init(struct net *);
+void sctp_dbg_objcnt_exit(struct net *);
 
 #else
 
 #define SCTP_DBG_OBJCNT_INC(name)
 #define SCTP_DBG_OBJCNT_DEC(name)
 
-static inline void sctp_dbg_objcnt_init(void) { return; }
-static inline void sctp_dbg_objcnt_exit(void) { return; }
+static inline void sctp_dbg_objcnt_init(struct net *net) { return; }
+static inline void sctp_dbg_objcnt_exit(struct net *net) { return; }
 
 #endif /* CONFIG_SCTP_DBG_OBJCOUNT */
 
 #if defined CONFIG_SYSCTL
 void sctp_sysctl_register(void);
 void sctp_sysctl_unregister(void);
+int sctp_sysctl_net_register(struct net *net);
+void sctp_sysctl_net_unregister(struct net *net);
 #else
 static inline void sctp_sysctl_register(void) { return; }
 static inline void sctp_sysctl_unregister(void) { return; }
+static inline int sctp_sysctl_net_register(struct net *net) { return 0; }
+static inline void sctp_sysctl_net_unregister(struct net *net) { return; }
 #endif
 
 /* Size of Supported Address Parameter for 'x' address types. */
@@ -586,7 +588,6 @@ for (pos = chunk->subh.fwdtsn_hdr->skip;\
 
 extern struct proto sctp_prot;
 extern struct proto sctpv6_prot;
-extern struct proc_dir_entry *proc_net_sctp;
 void sctp_put_port(struct sock *sk);
 
 extern struct idr sctp_assocs_id;
@@ -632,21 +633,21 @@ static inline int sctp_sanity_check(void)
 
 /* Warning: The following hash functions assume a power of two 'size'. */
 /* This is the hash function for the SCTP port hash table. */
-static inline int sctp_phashfn(__u16 lport)
+static inline int sctp_phashfn(struct net *net, __u16 lport)
 {
-       return lport & (sctp_port_hashsize - 1);
+       return (net_hash_mix(net) + lport) & (sctp_port_hashsize - 1);
 }
 
 /* This is the hash function for the endpoint hash table. */
-static inline int sctp_ep_hashfn(__u16 lport)
+static inline int sctp_ep_hashfn(struct net *net, __u16 lport)
 {
-       return lport & (sctp_ep_hashsize - 1);
+       return (net_hash_mix(net) + lport) & (sctp_ep_hashsize - 1);
 }
 
 /* This is the hash function for the association hash table. */
-static inline int sctp_assoc_hashfn(__u16 lport, __u16 rport)
+static inline int sctp_assoc_hashfn(struct net *net, __u16 lport, __u16 rport)
 {
-       int h = (lport << 16) + rport;
+       int h = (lport << 16) + rport + net_hash_mix(net);
        h ^= h>>8;
        return h & (sctp_assoc_hashsize - 1);
 }
index 9148632b820467ff3e64ec911c63429e56272539..b5887e1677e4e421479919399b945844133595af 100644 (file)
@@ -77,7 +77,8 @@ typedef struct {
        int action;
 } sctp_sm_command_t;
 
-typedef sctp_disposition_t (sctp_state_fn_t) (const struct sctp_endpoint *,
+typedef sctp_disposition_t (sctp_state_fn_t) (struct net *,
+                                             const struct sctp_endpoint *,
                                              const struct sctp_association *,
                                              const sctp_subtype_t type,
                                              void *arg,
@@ -178,7 +179,8 @@ sctp_state_fn_t sctp_sf_autoclose_timer_expire;
 
 /* Prototypes for utility support functions.  */
 __u8 sctp_get_chunk_type(struct sctp_chunk *chunk);
-const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t,
+const sctp_sm_table_entry_t *sctp_sm_lookup_event(struct net *,
+                                           sctp_event_t,
                                            sctp_state_t,
                                            sctp_subtype_t);
 int sctp_chunk_iif(const struct sctp_chunk *);
@@ -268,7 +270,7 @@ void sctp_chunk_assign_ssn(struct sctp_chunk *);
 
 /* Prototypes for statetable processing. */
 
-int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype,
+int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype,
               sctp_state_t state,
                struct sctp_endpoint *,
                struct sctp_association *asoc,
index fc5e60016e37422e9408d9ca0c0b00136aaf2bb6..0fef00f5d3ce1fe65e2e6483ba1502f69615597b 100644 (file)
@@ -102,6 +102,7 @@ struct sctp_bind_bucket {
        unsigned short  fastreuse;
        struct hlist_node       node;
        struct hlist_head       owner;
+       struct net      *net;
 };
 
 struct sctp_bind_hashbucket {
@@ -118,69 +119,6 @@ struct sctp_hashbucket {
 
 /* The SCTP globals structure. */
 extern struct sctp_globals {
-       /* RFC2960 Section 14. Suggested SCTP Protocol Parameter Values
-        *
-        * The following protocol parameters are RECOMMENDED:
-        *
-        * RTO.Initial              - 3  seconds
-        * RTO.Min                  - 1  second
-        * RTO.Max                 -  60 seconds
-        * RTO.Alpha                - 1/8  (3 when converted to right shifts.)
-        * RTO.Beta                 - 1/4  (2 when converted to right shifts.)
-        */
-       unsigned int rto_initial;
-       unsigned int rto_min;
-       unsigned int rto_max;
-
-       /* Note: rto_alpha and rto_beta are really defined as inverse
-        * powers of two to facilitate integer operations.
-        */
-       int rto_alpha;
-       int rto_beta;
-
-       /* Max.Burst                - 4 */
-       int max_burst;
-
-       /* Whether Cookie Preservative is enabled(1) or not(0) */
-       int cookie_preserve_enable;
-
-       /* Valid.Cookie.Life        - 60  seconds  */
-       unsigned int valid_cookie_life;
-
-       /* Delayed SACK timeout  200ms default*/
-       unsigned int sack_timeout;
-
-       /* HB.interval              - 30 seconds  */
-       unsigned int hb_interval;
-
-       /* Association.Max.Retrans  - 10 attempts
-        * Path.Max.Retrans         - 5  attempts (per destination address)
-        * Max.Init.Retransmits     - 8  attempts
-        */
-       int max_retrans_association;
-       int max_retrans_path;
-       int max_retrans_init;
-
-       /* Potentially-Failed.Max.Retrans sysctl value
-        * taken from:
-        * http://tools.ietf.org/html/draft-nishida-tsvwg-sctp-failover-05
-        */
-       int pf_retrans;
-
-       /*
-        * Policy for preforming sctp/socket accounting
-        * 0   - do socket level accounting, all assocs share sk_sndbuf
-        * 1   - do sctp accounting, each asoc may use sk_sndbuf bytes
-        */
-       int sndbuf_policy;
-
-       /*
-        * Policy for preforming sctp/socket accounting
-        * 0   - do socket level accounting, all assocs share sk_rcvbuf
-        * 1   - do sctp accounting, each asoc may use sk_rcvbuf bytes
-        */
-       int rcvbuf_policy;
-
        /* The following variables are implementation specific.  */
 
        /* Default initialization values to be applied to new associations. */
@@ -204,70 +142,11 @@ extern struct sctp_globals {
        int port_hashsize;
        struct sctp_bind_hashbucket *port_hashtable;
 
-       /* This is the global local address list.
-        * We actively maintain this complete list of addresses on
-        * the system by catching address add/delete events.
-        *
-        * It is a list of sctp_sockaddr_entry.
-        */
-       struct list_head local_addr_list;
-       int default_auto_asconf;
-       struct list_head addr_waitq;
-       struct timer_list addr_wq_timer;
-       struct list_head auto_asconf_splist;
-       spinlock_t addr_wq_lock;
-
-       /* Lock that protects the local_addr_list writers */
-       spinlock_t addr_list_lock;
-       
-       /* Flag to indicate if addip is enabled. */
-       int addip_enable;
-       int addip_noauth_enable;
-
-       /* Flag to indicate if PR-SCTP is enabled. */
-       int prsctp_enable;
-
-       /* Flag to idicate if SCTP-AUTH is enabled */
-       int auth_enable;
-
-       /*
-        * Policy to control SCTP IPv4 address scoping
-        * 0   - Disable IPv4 address scoping
-        * 1   - Enable IPv4 address scoping
-        * 2   - Selectively allow only IPv4 private addresses
-        * 3   - Selectively allow only IPv4 link local address
-        */
-       int ipv4_scope_policy;
-
        /* Flag to indicate whether computing and verifying checksum
         * is disabled. */
         bool checksum_disable;
-
-       /* Threshold for rwnd update SACKS.  Receive buffer shifted this many
-        * bits is an indicator of when to send and window update SACK.
-        */
-       int rwnd_update_shift;
-
-       /* Threshold for autoclose timeout, in seconds. */
-       unsigned long max_autoclose;
 } sctp_globals;
 
-#define sctp_rto_initial               (sctp_globals.rto_initial)
-#define sctp_rto_min                   (sctp_globals.rto_min)
-#define sctp_rto_max                   (sctp_globals.rto_max)
-#define sctp_rto_alpha                 (sctp_globals.rto_alpha)
-#define sctp_rto_beta                  (sctp_globals.rto_beta)
-#define sctp_max_burst                 (sctp_globals.max_burst)
-#define sctp_valid_cookie_life         (sctp_globals.valid_cookie_life)
-#define sctp_cookie_preserve_enable    (sctp_globals.cookie_preserve_enable)
-#define sctp_max_retrans_association   (sctp_globals.max_retrans_association)
-#define sctp_sndbuf_policy             (sctp_globals.sndbuf_policy)
-#define sctp_rcvbuf_policy             (sctp_globals.rcvbuf_policy)
-#define sctp_max_retrans_path          (sctp_globals.max_retrans_path)
-#define sctp_pf_retrans                        (sctp_globals.pf_retrans)
-#define sctp_max_retrans_init          (sctp_globals.max_retrans_init)
-#define sctp_sack_timeout              (sctp_globals.sack_timeout)
-#define sctp_hb_interval               (sctp_globals.hb_interval)
 #define sctp_max_instreams             (sctp_globals.max_instreams)
 #define sctp_max_outstreams            (sctp_globals.max_outstreams)
 #define sctp_address_families          (sctp_globals.address_families)
@@ -277,21 +156,7 @@ extern struct sctp_globals {
 #define sctp_assoc_hashtable           (sctp_globals.assoc_hashtable)
 #define sctp_port_hashsize             (sctp_globals.port_hashsize)
 #define sctp_port_hashtable            (sctp_globals.port_hashtable)
-#define sctp_local_addr_list           (sctp_globals.local_addr_list)
-#define sctp_local_addr_lock           (sctp_globals.addr_list_lock)
-#define sctp_auto_asconf_splist                (sctp_globals.auto_asconf_splist)
-#define sctp_addr_waitq                        (sctp_globals.addr_waitq)
-#define sctp_addr_wq_timer             (sctp_globals.addr_wq_timer)
-#define sctp_addr_wq_lock              (sctp_globals.addr_wq_lock)
-#define sctp_default_auto_asconf       (sctp_globals.default_auto_asconf)
-#define sctp_scope_policy              (sctp_globals.ipv4_scope_policy)
-#define sctp_addip_enable              (sctp_globals.addip_enable)
-#define sctp_addip_noauth              (sctp_globals.addip_noauth_enable)
-#define sctp_prsctp_enable             (sctp_globals.prsctp_enable)
-#define sctp_auth_enable               (sctp_globals.auth_enable)
 #define sctp_checksum_disable          (sctp_globals.checksum_disable)
-#define sctp_rwnd_upd_shift            (sctp_globals.rwnd_update_shift)
-#define sctp_max_autoclose             (sctp_globals.max_autoclose)
 
 /* SCTP Socket type: UDP or TCP style. */
 typedef enum {
@@ -1085,7 +950,7 @@ struct sctp_transport {
        __u64 hb_nonce;
 };
 
-struct sctp_transport *sctp_transport_new(const union sctp_addr *,
+struct sctp_transport *sctp_transport_new(struct net *, const union sctp_addr *,
                                          gfp_t);
 void sctp_transport_set_owner(struct sctp_transport *,
                              struct sctp_association *);
@@ -1240,7 +1105,7 @@ struct sctp_bind_addr {
 
 void sctp_bind_addr_init(struct sctp_bind_addr *, __u16 port);
 void sctp_bind_addr_free(struct sctp_bind_addr *);
-int sctp_bind_addr_copy(struct sctp_bind_addr *dest,
+int sctp_bind_addr_copy(struct net *net, struct sctp_bind_addr *dest,
                        const struct sctp_bind_addr *src,
                        sctp_scope_t scope, gfp_t gfp,
                        int flags);
@@ -1267,7 +1132,7 @@ int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw, int len,
                           __u16 port, gfp_t gfp);
 
 sctp_scope_t sctp_scope(const union sctp_addr *);
-int sctp_in_scope(const union sctp_addr *addr, const sctp_scope_t scope);
+int sctp_in_scope(struct net *net, const union sctp_addr *addr, const sctp_scope_t scope);
 int sctp_is_any(struct sock *sk, const union sctp_addr *addr);
 int sctp_addr_is_valid(const union sctp_addr *addr);
 int sctp_is_ep_boundall(struct sock *sk);
@@ -1425,13 +1290,13 @@ struct sctp_association *sctp_endpoint_lookup_assoc(
 int sctp_endpoint_is_peeled_off(struct sctp_endpoint *,
                                const union sctp_addr *);
 struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *,
-                                       const union sctp_addr *);
-int sctp_has_association(const union sctp_addr *laddr,
+                                       struct net *, const union sctp_addr *);
+int sctp_has_association(struct net *net, const union sctp_addr *laddr,
                         const union sctp_addr *paddr);
 
-int sctp_verify_init(const struct sctp_association *asoc, sctp_cid_t,
-                    sctp_init_chunk_t *peer_init, struct sctp_chunk *chunk,
-                    struct sctp_chunk **err_chunk);
+int sctp_verify_init(struct net *net, const struct sctp_association *asoc,
+                    sctp_cid_t, sctp_init_chunk_t *peer_init,
+                    struct sctp_chunk *chunk, struct sctp_chunk **err_chunk);
 int sctp_process_init(struct sctp_association *, struct sctp_chunk *chunk,
                      const union sctp_addr *peer,
                      sctp_init_chunk_t *init, gfp_t gfp);
@@ -2013,6 +1878,7 @@ void sctp_assoc_control_transport(struct sctp_association *,
                                  sctp_transport_cmd_t, sctp_sn_error_t);
 struct sctp_transport *sctp_assoc_lookup_tsn(struct sctp_association *, __u32);
 struct sctp_transport *sctp_assoc_is_match(struct sctp_association *,
+                                          struct net *,
                                           const union sctp_addr *,
                                           const union sctp_addr *);
 void sctp_assoc_migrate(struct sctp_association *, struct sock *);
index 0147b901e79c4d6b43eef9dbe45f76eef0fc1720..71596261fa997ec7014b77f0bbee9b47b6146493 100644 (file)
@@ -154,13 +154,15 @@ struct linux_xfrm_mib {
  */
 #define SNMP_UPD_PO_STATS(mib, basefield, addend)      \
        do { \
-               this_cpu_inc(mib[0]->mibs[basefield##PKTS]);            \
-               this_cpu_add(mib[0]->mibs[basefield##OCTETS], addend);  \
+               __typeof__(*mib[0]->mibs) *ptr = mib[0]->mibs;  \
+               this_cpu_inc(ptr[basefield##PKTS]);             \
+               this_cpu_add(ptr[basefield##OCTETS], addend);   \
        } while (0)
 #define SNMP_UPD_PO_STATS_BH(mib, basefield, addend)   \
        do { \
-               __this_cpu_inc(mib[0]->mibs[basefield##PKTS]);          \
-               __this_cpu_add(mib[0]->mibs[basefield##OCTETS], addend);        \
+               __typeof__(*mib[0]->mibs) *ptr = mib[0]->mibs;  \
+               __this_cpu_inc(ptr[basefield##PKTS]);           \
+               __this_cpu_add(ptr[basefield##OCTETS], addend); \
        } while (0)
 
 
index e067f8c18f88a3d16a0d28b9bac84d53a236bba0..72132aef53fc61a721ce23c8fbc4fe17cb7a9699 100644 (file)
@@ -218,6 +218,7 @@ struct cg_proto;
   *    @sk_route_nocaps: forbidden route capabilities (e.g NETIF_F_GSO_MASK)
   *    @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4)
   *    @sk_gso_max_size: Maximum GSO segment size to build
+  *    @sk_gso_max_segs: Maximum number of GSO segments
   *    @sk_lingertime: %SO_LINGER l_linger setting
   *    @sk_backlog: always used with the per-socket spinlock held
   *    @sk_callback_lock: used with the callbacks in the end of this struct
@@ -338,6 +339,7 @@ struct sock {
        netdev_features_t       sk_route_nocaps;
        int                     sk_gso_type;
        unsigned int            sk_gso_max_size;
+       u16                     sk_gso_max_segs;
        int                     sk_rcvlowat;
        unsigned long           sk_lingertime;
        struct sk_buff_head     sk_error_queue;
@@ -621,6 +623,7 @@ enum sock_flags {
        SOCK_RCVTSTAMPNS, /* %SO_TIMESTAMPNS setting */
        SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */
        SOCK_QUEUE_SHRUNK, /* write queue has been shrunk recently */
+       SOCK_MEMALLOC, /* VM depends on this socket for swapping */
        SOCK_TIMESTAMPING_TX_HARDWARE,  /* %SOF_TIMESTAMPING_TX_HARDWARE */
        SOCK_TIMESTAMPING_TX_SOFTWARE,  /* %SOF_TIMESTAMPING_TX_SOFTWARE */
        SOCK_TIMESTAMPING_RX_HARDWARE,  /* %SOF_TIMESTAMPING_RX_HARDWARE */
@@ -658,6 +661,26 @@ static inline bool sock_flag(const struct sock *sk, enum sock_flags flag)
        return test_bit(flag, &sk->sk_flags);
 }
 
+#ifdef CONFIG_NET
+extern struct static_key memalloc_socks;
+static inline int sk_memalloc_socks(void)
+{
+       return static_key_false(&memalloc_socks);
+}
+#else
+
+static inline int sk_memalloc_socks(void)
+{
+       return 0;
+}
+
+#endif
+
+static inline gfp_t sk_gfp_atomic(struct sock *sk, gfp_t gfp_mask)
+{
+       return GFP_ATOMIC | (sk->sk_allocation & __GFP_MEMALLOC);
+}
+
 static inline void sk_acceptq_removed(struct sock *sk)
 {
        sk->sk_ack_backlog--;
@@ -733,8 +756,13 @@ static inline __must_check int sk_add_backlog(struct sock *sk, struct sk_buff *s
        return 0;
 }
 
+extern int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb);
+
 static inline int sk_backlog_rcv(struct sock *sk, struct sk_buff *skb)
 {
+       if (sk_memalloc_socks() && skb_pfmemalloc(skb))
+               return __sk_backlog_rcv(sk, skb);
+
        return sk->sk_backlog_rcv(sk, skb);
 }
 
@@ -798,6 +826,8 @@ extern int sk_stream_wait_memory(struct sock *sk, long *timeo_p);
 extern void sk_stream_wait_close(struct sock *sk, long timeo_p);
 extern int sk_stream_error(struct sock *sk, int flags, int err);
 extern void sk_stream_kill_queues(struct sock *sk);
+extern void sk_set_memalloc(struct sock *sk);
+extern void sk_clear_memalloc(struct sock *sk);
 
 extern int sk_wait_data(struct sock *sk, long *timeo);
 
@@ -913,7 +943,7 @@ struct proto {
 #ifdef SOCK_REFCNT_DEBUG
        atomic_t                socks;
 #endif
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
        /*
         * cgroup specific init/deinit functions. Called once for all
         * protocols that implement it, from cgroups populate function.
@@ -994,7 +1024,7 @@ inline void sk_refcnt_debug_release(const struct sock *sk)
 #define sk_refcnt_debug_release(sk) do { } while (0)
 #endif /* SOCK_REFCNT_DEBUG */
 
-#if defined(CONFIG_CGROUP_MEM_RES_CTLR_KMEM) && defined(CONFIG_NET)
+#if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_NET)
 extern struct static_key memcg_socket_limit_enabled;
 static inline struct cg_proto *parent_cg_proto(struct proto *proto,
                                               struct cg_proto *cg_proto)
@@ -1301,12 +1331,14 @@ static inline bool sk_wmem_schedule(struct sock *sk, int size)
                __sk_mem_schedule(sk, size, SK_MEM_SEND);
 }
 
-static inline bool sk_rmem_schedule(struct sock *sk, int size)
+static inline bool
+sk_rmem_schedule(struct sock *sk, struct sk_buff *skb, unsigned int size)
 {
        if (!sk_has_account(sk))
                return true;
-       return size <= sk->sk_forward_alloc ||
-               __sk_mem_schedule(sk, size, SK_MEM_RECV);
+       return size<= sk->sk_forward_alloc ||
+               __sk_mem_schedule(sk, size, SK_MEM_RECV) ||
+               skb_pfmemalloc(skb);
 }
 
 static inline void sk_mem_reclaim(struct sock *sk)
index d9509eb29b80f989eb47531877279ae93f31f85c..62b619e82a905354d521f62703c933faf5846c39 100644 (file)
@@ -213,6 +213,9 @@ struct xfrm_state {
        struct xfrm_lifetime_cur curlft;
        struct tasklet_hrtimer  mtimer;
 
+       /* used to fix curlft->add_time when changing date */
+       long            saved_tmo;
+
        /* Last used time */
        unsigned long           lastused;
 
@@ -238,6 +241,7 @@ static inline struct net *xs_net(struct xfrm_state *x)
 
 /* xflags - make enum if more show up */
 #define XFRM_TIME_DEFER        1
+#define XFRM_SOFT_EXPIRE 2
 
 enum {
        XFRM_STATE_VOID,
diff --git a/include/ras/ras_event.h b/include/ras/ras_event.h
new file mode 100644 (file)
index 0000000..260470e
--- /dev/null
@@ -0,0 +1,102 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ras
+#define TRACE_INCLUDE_FILE ras_event
+
+#if !defined(_TRACE_HW_EVENT_MC_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_HW_EVENT_MC_H
+
+#include <linux/tracepoint.h>
+#include <linux/edac.h>
+#include <linux/ktime.h>
+
+/*
+ * Hardware Events Report
+ *
+ * Those events are generated when hardware detected a corrected or
+ * uncorrected event, and are meant to replace the current API to report
+ * errors defined on both EDAC and MCE subsystems.
+ *
+ * FIXME: Add events for handling memory errors originated from the
+ *        MCE subsystem.
+ */
+
+/*
+ * Hardware-independent Memory Controller specific events
+ */
+
+/*
+ * Default error mechanisms for Memory Controller errors (CE and UE)
+ */
+TRACE_EVENT(mc_event,
+
+       TP_PROTO(const unsigned int err_type,
+                const char *error_msg,
+                const char *label,
+                const int error_count,
+                const u8 mc_index,
+                const s8 top_layer,
+                const s8 mid_layer,
+                const s8 low_layer,
+                unsigned long address,
+                const u8 grain_bits,
+                unsigned long syndrome,
+                const char *driver_detail),
+
+       TP_ARGS(err_type, error_msg, label, error_count, mc_index,
+               top_layer, mid_layer, low_layer, address, grain_bits,
+               syndrome, driver_detail),
+
+       TP_STRUCT__entry(
+               __field(        unsigned int,   error_type              )
+               __string(       msg,            error_msg               )
+               __string(       label,          label                   )
+               __field(        u16,            error_count             )
+               __field(        u8,             mc_index                )
+               __field(        s8,             top_layer               )
+               __field(        s8,             middle_layer            )
+               __field(        s8,             lower_layer             )
+               __field(        long,           address                 )
+               __field(        u8,             grain_bits              )
+               __field(        long,           syndrome                )
+               __string(       driver_detail,  driver_detail           )
+       ),
+
+       TP_fast_assign(
+               __entry->error_type             = err_type;
+               __assign_str(msg, error_msg);
+               __assign_str(label, label);
+               __entry->error_count            = error_count;
+               __entry->mc_index               = mc_index;
+               __entry->top_layer              = top_layer;
+               __entry->middle_layer           = mid_layer;
+               __entry->lower_layer            = low_layer;
+               __entry->address                = address;
+               __entry->grain_bits             = grain_bits;
+               __entry->syndrome               = syndrome;
+               __assign_str(driver_detail, driver_detail);
+       ),
+
+       TP_printk("%d %s error%s:%s%s on %s (mc:%d location:%d:%d:%d address:0x%08lx grain:%d syndrome:0x%08lx%s%s)",
+                 __entry->error_count,
+                 (__entry->error_type == HW_EVENT_ERR_CORRECTED) ? "Corrected" :
+                       ((__entry->error_type == HW_EVENT_ERR_FATAL) ?
+                       "Fatal" : "Uncorrected"),
+                 __entry->error_count > 1 ? "s" : "",
+                 ((char *)__get_str(msg))[0] ? " " : "",
+                 __get_str(msg),
+                 __get_str(label),
+                 __entry->mc_index,
+                 __entry->top_layer,
+                 __entry->middle_layer,
+                 __entry->lower_layer,
+                 __entry->address,
+                 1 << __entry->grain_bits,
+                 __entry->syndrome,
+                 ((char *)__get_str(driver_detail))[0] ? " " : "",
+                 __get_str(driver_detail))
+);
+
+#endif /* _TRACE_HW_EVENT_MC_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 3ec7ecbe250220720ae906a396c1490e8beee67d..f752dd33dfaf81ff43da9473b7ce2f30d67f198d 100644 (file)
@@ -29,6 +29,7 @@
 #define ES1688_HW_AUTO         0x0000
 #define ES1688_HW_688          0x0001
 #define ES1688_HW_1688         0x0002
+#define ES1688_HW_UNDEF        0x0003
 
 struct snd_es1688 {
        unsigned long port;             /* port of ESS chip */
index 0c3c2fb0f9395be596699830a62939cc83dd1609..fe8590cac5c2653b414ec4517e8ea0f35bcafb4a 100644 (file)
 struct snd_tea575x;
 
 struct snd_tea575x_ops {
+       /* Drivers using snd_tea575x must either define read_ and write_val */
+       void (*write_val)(struct snd_tea575x *tea, u32 val);
+       u32 (*read_val)(struct snd_tea575x *tea);
+       /* Or define the 3 pin functions */
        void (*set_pins)(struct snd_tea575x *tea, u8 pins);
        u8 (*get_pins)(struct snd_tea575x *tea);
        void (*set_direction)(struct snd_tea575x *tea, bool output);
@@ -49,6 +53,7 @@ struct snd_tea575x {
        int radio_nr;                   /* radio_nr */
        bool tea5759;                   /* 5759 chip is present */
        bool cannot_read_data;          /* Device cannot read the data pin */
+       bool cannot_mute;               /* Device cannot mute */
        bool mute;                      /* Device is muted? */
        bool stereo;                    /* receiving stereo */
        bool tuned;                     /* tuned to a station */
index 9fe3a36646e9ce47886bd7049684ce68d05808e4..d6fd8e5b14b76c41bfd532c3fa86255e4e92b0f3 100644 (file)
@@ -30,6 +30,7 @@
        {(unsigned long)__GFP_COMP,             "GFP_COMP"},            \
        {(unsigned long)__GFP_ZERO,             "GFP_ZERO"},            \
        {(unsigned long)__GFP_NOMEMALLOC,       "GFP_NOMEMALLOC"},      \
+       {(unsigned long)__GFP_MEMALLOC,         "GFP_MEMALLOC"},        \
        {(unsigned long)__GFP_HARDWALL,         "GFP_HARDWALL"},        \
        {(unsigned long)__GFP_THISNODE,         "GFP_THISNODE"},        \
        {(unsigned long)__GFP_RECLAIMABLE,      "GFP_RECLAIMABLE"},     \
diff --git a/include/trace/events/random.h b/include/trace/events/random.h
new file mode 100644 (file)
index 0000000..422df19
--- /dev/null
@@ -0,0 +1,134 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM random
+
+#if !defined(_TRACE_RANDOM_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_RANDOM_H
+
+#include <linux/writeback.h>
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(random__mix_pool_bytes,
+       TP_PROTO(const char *pool_name, int bytes, unsigned long IP),
+
+       TP_ARGS(pool_name, bytes, IP),
+
+       TP_STRUCT__entry(
+               __field( const char *,  pool_name               )
+               __field(          int,  bytes                   )
+               __field(unsigned long,  IP                      )
+       ),
+
+       TP_fast_assign(
+               __entry->pool_name      = pool_name;
+               __entry->bytes          = bytes;
+               __entry->IP             = IP;
+       ),
+
+       TP_printk("%s pool: bytes %d caller %pF",
+                 __entry->pool_name, __entry->bytes, (void *)__entry->IP)
+);
+
+DEFINE_EVENT(random__mix_pool_bytes, mix_pool_bytes,
+       TP_PROTO(const char *pool_name, int bytes, unsigned long IP),
+
+       TP_ARGS(pool_name, bytes, IP)
+);
+
+DEFINE_EVENT(random__mix_pool_bytes, mix_pool_bytes_nolock,
+       TP_PROTO(const char *pool_name, int bytes, unsigned long IP),
+
+       TP_ARGS(pool_name, bytes, IP)
+);
+
+TRACE_EVENT(credit_entropy_bits,
+       TP_PROTO(const char *pool_name, int bits, int entropy_count,
+                int entropy_total, unsigned long IP),
+
+       TP_ARGS(pool_name, bits, entropy_count, entropy_total, IP),
+
+       TP_STRUCT__entry(
+               __field( const char *,  pool_name               )
+               __field(          int,  bits                    )
+               __field(          int,  entropy_count           )
+               __field(          int,  entropy_total           )
+               __field(unsigned long,  IP                      )
+       ),
+
+       TP_fast_assign(
+               __entry->pool_name      = pool_name;
+               __entry->bits           = bits;
+               __entry->entropy_count  = entropy_count;
+               __entry->entropy_total  = entropy_total;
+               __entry->IP             = IP;
+       ),
+
+       TP_printk("%s pool: bits %d entropy_count %d entropy_total %d "
+                 "caller %pF", __entry->pool_name, __entry->bits,
+                 __entry->entropy_count, __entry->entropy_total,
+                 (void *)__entry->IP)
+);
+
+TRACE_EVENT(get_random_bytes,
+       TP_PROTO(int nbytes, unsigned long IP),
+
+       TP_ARGS(nbytes, IP),
+
+       TP_STRUCT__entry(
+               __field(          int,  nbytes                  )
+               __field(unsigned long,  IP                      )
+       ),
+
+       TP_fast_assign(
+               __entry->nbytes         = nbytes;
+               __entry->IP             = IP;
+       ),
+
+       TP_printk("nbytes %d caller %pF", __entry->nbytes, (void *)__entry->IP)
+);
+
+DECLARE_EVENT_CLASS(random__extract_entropy,
+       TP_PROTO(const char *pool_name, int nbytes, int entropy_count,
+                unsigned long IP),
+
+       TP_ARGS(pool_name, nbytes, entropy_count, IP),
+
+       TP_STRUCT__entry(
+               __field( const char *,  pool_name               )
+               __field(          int,  nbytes                  )
+               __field(          int,  entropy_count           )
+               __field(unsigned long,  IP                      )
+       ),
+
+       TP_fast_assign(
+               __entry->pool_name      = pool_name;
+               __entry->nbytes         = nbytes;
+               __entry->entropy_count  = entropy_count;
+               __entry->IP             = IP;
+       ),
+
+       TP_printk("%s pool: nbytes %d entropy_count %d caller %pF",
+                 __entry->pool_name, __entry->nbytes, __entry->entropy_count,
+                 (void *)__entry->IP)
+);
+
+
+DEFINE_EVENT(random__extract_entropy, extract_entropy,
+       TP_PROTO(const char *pool_name, int nbytes, int entropy_count,
+                unsigned long IP),
+
+       TP_ARGS(pool_name, nbytes, entropy_count, IP)
+);
+
+DEFINE_EVENT(random__extract_entropy, extract_entropy_user,
+       TP_PROTO(const char *pool_name, int nbytes, int entropy_count,
+                unsigned long IP),
+
+       TP_ARGS(pool_name, nbytes, entropy_count, IP)
+);
+
+
+
+#endif /* _TRACE_RANDOM_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 89d43b3d4cb9e3fe713fe5b8eaeb2de0616eb9fa..5a0e4f9efb53a31cb4c1ba74e0257a2881fcde3e 100644 (file)
@@ -82,6 +82,9 @@ struct lcd_ctrl_config {
 
        /* Raster Data Order Select: 1=Most-to-least 0=Least-to-most */
        unsigned char raster_order;
+
+       /* DMA FIFO threshold */
+       int fifo_th;
 };
 
 struct lcd_sync_arg {
index c8e59b4a3364264df1719090162f19cf0b250dd8..a6267a2d292bfa07a815003410060348656392ce 100644 (file)
 #define DISPC_IRQ_FRAMEDONEWB          (1 << 23)
 #define DISPC_IRQ_FRAMEDONETV          (1 << 24)
 #define DISPC_IRQ_WBBUFFEROVERFLOW     (1 << 25)
+#define DISPC_IRQ_FRAMEDONE3           (1 << 26)
+#define DISPC_IRQ_VSYNC3               (1 << 27)
+#define DISPC_IRQ_ACBIAS_COUNT_STAT3   (1 << 28)
+#define DISPC_IRQ_SYNC_LOST3           (1 << 29)
 
 struct omap_dss_device;
 struct omap_overlay_manager;
@@ -75,6 +79,7 @@ enum omap_channel {
        OMAP_DSS_CHANNEL_LCD    = 0,
        OMAP_DSS_CHANNEL_DIGIT  = 1,
        OMAP_DSS_CHANNEL_LCD2   = 2,
+       OMAP_DSS_CHANNEL_LCD3   = 3,
 };
 
 enum omap_color_mode {
@@ -99,11 +104,6 @@ enum omap_color_mode {
        OMAP_DSS_COLOR_XRGB16_1555      = 1 << 18, /* xRGB16 - 1555 */
 };
 
-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,
@@ -121,15 +121,15 @@ enum omap_rfbi_te_mode {
        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,
+enum omap_dss_signal_level {
+       OMAPDSS_SIG_ACTIVE_HIGH = 0,
+       OMAPDSS_SIG_ACTIVE_LOW  = 1,
+};
 
-       OMAP_DSS_LCD_TFT                = 1<<20,
+enum omap_dss_signal_edge {
+       OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+       OMAPDSS_DRIVE_SIG_RISING_EDGE,
+       OMAPDSS_DRIVE_SIG_FALLING_EDGE,
 };
 
 enum omap_dss_venc_type {
@@ -167,13 +167,6 @@ enum omap_dss_audio_state {
        OMAP_DSS_AUDIO_PLAYING,
 };
 
-/* XXX perhaps this should be removed */
-enum omap_dss_overlay_managers {
-       OMAP_DSS_OVL_MGR_LCD,
-       OMAP_DSS_OVL_MGR_TV,
-       OMAP_DSS_OVL_MGR_LCD2,
-};
-
 enum omap_dss_rotation_type {
        OMAP_DSS_ROT_DMA        = 1 << 0,
        OMAP_DSS_ROT_VRFB       = 1 << 1,
@@ -268,9 +261,6 @@ struct omap_dss_dsi_videomode_data {
        int hfp_blanking_mode;
 
        /* Video port sync events */
-       int vp_de_pol;
-       int vp_hsync_pol;
-       int vp_vsync_pol;
        bool vp_vsync_end;
        bool vp_hsync_end;
 
@@ -346,6 +336,19 @@ struct omap_video_timings {
        u16 vfp;        /* Vertical front porch */
        /* Unit: line clocks */
        u16 vbp;        /* Vertical back porch */
+
+       /* Vsync logic level */
+       enum omap_dss_signal_level vsync_level;
+       /* Hsync logic level */
+       enum omap_dss_signal_level hsync_level;
+       /* Interlaced or Progressive timings */
+       bool interlace;
+       /* Pixel clock edge to drive LCD data */
+       enum omap_dss_signal_edge data_pclk_edge;
+       /* Data enable logic level */
+       enum omap_dss_signal_level de_level;
+       /* Pixel clock edges to drive HSYNC and VSYNC signals */
+       enum omap_dss_signal_edge sync_pclk_edge;
 };
 
 #ifdef CONFIG_OMAP2_DSS_VENC
@@ -559,8 +562,6 @@ struct omap_dss_device {
                /* Unit: line clocks */
                int acb;        /* ac-bias pin frequency */
 
-               enum omap_panel_config config;
-
                enum omap_dss_dsi_pixel_format dsi_pix_fmt;
                enum omap_dss_dsi_mode dsi_mode;
                struct omap_dss_dsi_videomode_data dsi_vm_data;
index 7571b27a0ba102a1077ee7bee5f317bb78353573..ff43ffc1aab29a111a9f7893ac08b93864d1be0f 100644 (file)
@@ -166,6 +166,12 @@ struct sh_mobile_lcdc_bl_info {
        int (*get_brightness)(void);
 };
 
+struct sh_mobile_lcdc_overlay_cfg {
+       int fourcc;
+       unsigned int max_xres;
+       unsigned int max_yres;
+};
+
 struct sh_mobile_lcdc_chan_cfg {
        int chan;
        int fourcc;
@@ -186,6 +192,7 @@ struct sh_mobile_lcdc_chan_cfg {
 struct sh_mobile_lcdc_info {
        int clock_source;
        struct sh_mobile_lcdc_chan_cfg ch[2];
+       struct sh_mobile_lcdc_overlay_cfg overlays[4];
        struct sh_mobile_meram_info *meram_dev;
 };
 
index 29b2fd3b147e8773cadcd08f5f107fad0d7a7000..062e6e7f955c42ebfc8d8d98bfc17e8e2755d031 100644 (file)
@@ -15,7 +15,6 @@ enum {
 
 
 struct sh_mobile_meram_priv;
-struct sh_mobile_meram_ops;
 
 /*
  * struct sh_mobile_meram_info - MERAM platform data
@@ -24,7 +23,6 @@ struct sh_mobile_meram_ops;
 struct sh_mobile_meram_info {
        int                             addr_mode;
        u32                             reserved_icbs;
-       struct sh_mobile_meram_ops      *ops;
        struct sh_mobile_meram_priv     *priv;
        struct platform_device          *pdev;
 };
@@ -38,26 +36,59 @@ struct sh_mobile_meram_cfg {
        struct sh_mobile_meram_icb_cfg icb[2];
 };
 
-struct module;
-struct sh_mobile_meram_ops {
-       struct module   *module;
-       /* register usage of meram */
-       void *(*meram_register)(struct sh_mobile_meram_info *meram_dev,
-                               const struct sh_mobile_meram_cfg *cfg,
-                               unsigned int xres, unsigned int yres,
-                               unsigned int pixelformat,
-                               unsigned int *pitch);
-
-       /* unregister usage of meram */
-       void (*meram_unregister)(struct sh_mobile_meram_info *meram_dev,
-                                void *data);
-
-       /* update meram settings */
-       void (*meram_update)(struct sh_mobile_meram_info *meram_dev, void *data,
+#if defined(CONFIG_FB_SH_MOBILE_MERAM) || \
+    defined(CONFIG_FB_SH_MOBILE_MERAM_MODULE)
+unsigned long sh_mobile_meram_alloc(struct sh_mobile_meram_info *meram_dev,
+                                   size_t size);
+void sh_mobile_meram_free(struct sh_mobile_meram_info *meram_dev,
+                         unsigned long mem, size_t size);
+void *sh_mobile_meram_cache_alloc(struct sh_mobile_meram_info *dev,
+                                 const struct sh_mobile_meram_cfg *cfg,
+                                 unsigned int xres, unsigned int yres,
+                                 unsigned int pixelformat,
+                                 unsigned int *pitch);
+void sh_mobile_meram_cache_free(struct sh_mobile_meram_info *dev, void *data);
+void sh_mobile_meram_cache_update(struct sh_mobile_meram_info *dev, void *data,
+                                 unsigned long base_addr_y,
+                                 unsigned long base_addr_c,
+                                 unsigned long *icb_addr_y,
+                                 unsigned long *icb_addr_c);
+#else
+static inline unsigned long
+sh_mobile_meram_alloc(struct sh_mobile_meram_info *meram_dev, size_t size)
+{
+       return 0;
+}
+
+static inline void
+sh_mobile_meram_free(struct sh_mobile_meram_info *meram_dev,
+                    unsigned long mem, size_t size)
+{
+}
+
+static inline void *
+sh_mobile_meram_cache_alloc(struct sh_mobile_meram_info *dev,
+                           const struct sh_mobile_meram_cfg *cfg,
+                           unsigned int xres, unsigned int yres,
+                           unsigned int pixelformat,
+                           unsigned int *pitch)
+{
+       return ERR_PTR(-ENODEV);
+}
+
+static inline void
+sh_mobile_meram_cache_free(struct sh_mobile_meram_info *dev, void *data)
+{
+}
+
+static inline void
+sh_mobile_meram_cache_update(struct sh_mobile_meram_info *dev, void *data,
                             unsigned long base_addr_y,
                             unsigned long base_addr_c,
                             unsigned long *icb_addr_y,
-                            unsigned long *icb_addr_c);
-};
+                            unsigned long *icb_addr_c)
+{
+}
+#endif
 
 #endif /* __VIDEO_SH_MOBILE_MERAM_H__  */
index b3f55f15e1074ca90bfd1cacbba5cd0c68be9ad7..af6c7f8ba019ae25519f858850b2637b1fc76c8e 100644 (file)
@@ -686,7 +686,7 @@ config RESOURCE_COUNTERS
          This option enables controller independent resource accounting
          infrastructure that works with cgroups.
 
-config CGROUP_MEM_RES_CTLR
+config MEMCG
        bool "Memory Resource Controller for Control Groups"
        depends on RESOURCE_COUNTERS
        select MM_OWNER
@@ -709,9 +709,9 @@ config CGROUP_MEM_RES_CTLR
          This config option also selects MM_OWNER config option, which
          could in turn add some fork/exit overhead.
 
-config CGROUP_MEM_RES_CTLR_SWAP
+config MEMCG_SWAP
        bool "Memory Resource Controller Swap Extension"
-       depends on CGROUP_MEM_RES_CTLR && SWAP
+       depends on MEMCG && SWAP
        help
          Add swap management feature to memory resource controller. When you
          enable this, you can limit mem+swap usage per cgroup. In other words,
@@ -726,9 +726,9 @@ config CGROUP_MEM_RES_CTLR_SWAP
          if boot option "swapaccount=0" is set, swap will not be accounted.
          Now, memory usage of swap_cgroup is 2 bytes per entry. If swap page
          size is 4096bytes, 512k per 1Gbytes of swap.
-config CGROUP_MEM_RES_CTLR_SWAP_ENABLED
+config MEMCG_SWAP_ENABLED
        bool "Memory Resource Controller Swap Extension enabled by default"
-       depends on CGROUP_MEM_RES_CTLR_SWAP
+       depends on MEMCG_SWAP
        default y
        help
          Memory Resource Controller Swap Extension comes with its price in
@@ -739,9 +739,9 @@ config CGROUP_MEM_RES_CTLR_SWAP_ENABLED
          For those who want to have the feature enabled by default should
          select this option (if, for some reason, they need to disable it
          then swapaccount=0 does the trick).
-config CGROUP_MEM_RES_CTLR_KMEM
+config MEMCG_KMEM
        bool "Memory Resource Controller Kernel Memory accounting (EXPERIMENTAL)"
-       depends on CGROUP_MEM_RES_CTLR && EXPERIMENTAL
+       depends on MEMCG && EXPERIMENTAL
        default n
        help
          The Kernel Memory extension for Memory Resource Controller can limit
@@ -751,6 +751,21 @@ config CGROUP_MEM_RES_CTLR_KMEM
          the kmem extension can use it to guarantee that no group of processes
          will ever exhaust kernel resources alone.
 
+config CGROUP_HUGETLB
+       bool "HugeTLB Resource Controller for Control Groups"
+       depends on RESOURCE_COUNTERS && HUGETLB_PAGE && EXPERIMENTAL
+       default n
+       help
+         Provides a cgroup Resource Controller for HugeTLB pages.
+         When you enable this, you can put a per cgroup limit on HugeTLB usage.
+         The limit is enforced during page fault. Since HugeTLB doesn't
+         support page reclaim, enforcing the limit at page fault time implies
+         that, the application will get SIGBUS signal if it tries to access
+         HugeTLB pages beyond its limit. This requires the application to know
+         beforehand how much HugeTLB pages it would require for its use. The
+         control group is tracked in the third page lru pointer. This means
+         that we cannot use the controller with huge page less than 3 pages.
+
 config CGROUP_PERF
        bool "Enable perf_event per-cpu per-container group (cgroup) monitoring"
        depends on PERF_EVENTS && CGROUPS
index 95316a1b4a76abcc4763422eacdd7f4c646499ab..e60679de61c3a050640554966d34542fcc13a7bd 100644 (file)
@@ -506,7 +506,7 @@ asmlinkage void __init start_kernel(void)
        setup_per_cpu_areas();
        smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
 
-       build_all_zonelists(NULL);
+       build_all_zonelists(NULL, NULL);
        page_alloc_init();
 
        printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
index a6df704f521e01f628def526e97b38cca92bb3ed..ad9518eb26e06e648c6044b95de41d39e9b076d0 100644 (file)
@@ -118,7 +118,7 @@ extern int sem_ctls[];
 
 static inline int compat_ipc_parse_version(int *cmd)
 {
-#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC
+#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION
        int version = *cmd & IPC_64;
 
        /* this is tricky: architectures that have support for the old
@@ -373,21 +373,21 @@ long compat_sys_semctl(int semid, int semnum, int cmd, int arg)
 }
 
 long compat_sys_msgsnd(int msqid, struct compat_msgbuf __user *msgp,
-                      size_t msgsz, int msgflg)
+                      compat_ssize_t msgsz, int msgflg)
 {
        compat_long_t mtype;
 
        if (get_user(mtype, &msgp->mtype))
                return -EFAULT;
-       return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg);
+       return do_msgsnd(msqid, mtype, msgp->mtext, (ssize_t)msgsz, msgflg);
 }
 
 long compat_sys_msgrcv(int msqid, struct compat_msgbuf __user *msgp,
-                      size_t msgsz, long msgtyp, int msgflg)
+                      compat_ssize_t msgsz, long msgtyp, int msgflg)
 {
        long err, mtype;
 
-       err =  do_msgrcv(msqid, &mtype, msgp->mtext, msgsz, msgtyp, msgflg);
+       err =  do_msgrcv(msqid, &mtype, msgp->mtext, (ssize_t)msgsz, msgtyp, msgflg);
        if (err < 0)
                goto out;
 
@@ -514,6 +514,10 @@ long compat_sys_msgctl(int first, int second, void __user *uptr)
        return err;
 }
 
+#ifndef COMPAT_SHMLBA
+#define COMPAT_SHMLBA  SHMLBA
+#endif
+
 #ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC
 long compat_sys_shmat(int first, int second, compat_uptr_t third, int version,
                        void __user *uptr)
@@ -524,7 +528,7 @@ long compat_sys_shmat(int first, int second, compat_uptr_t third, int version,
 
        if (version == 1)
                return -EINVAL;
-       err = do_shmat(first, uptr, second, &raddr);
+       err = do_shmat(first, uptr, second, &raddr, COMPAT_SHMLBA);
        if (err < 0)
                return err;
        uaddr = compat_ptr(third);
@@ -536,7 +540,7 @@ long compat_sys_shmat(int shmid, compat_uptr_t shmaddr, int shmflg)
        unsigned long ret;
        long err;
 
-       err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret);
+       err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret, COMPAT_SHMLBA);
        if (err)
                return err;
        force_successful_syscall_return();
index 41c1285d697a6f19a3c9b945691346c9b45ade47..00faa05cf72adf914bc40a41113182da964eec31 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -953,7 +953,8 @@ out:
  * "raddr" thing points to kernel space, and there has to be a wrapper around
  * this.
  */
-long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
+long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr,
+             unsigned long shmlba)
 {
        struct shmid_kernel *shp;
        unsigned long addr;
@@ -973,9 +974,9 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
        if (shmid < 0)
                goto out;
        else if ((addr = (ulong)shmaddr)) {
-               if (addr & (SHMLBA-1)) {
+               if (addr & (shmlba - 1)) {
                        if (shmflg & SHM_RND)
-                               addr &= ~(SHMLBA-1);       /* round down */
+                               addr &= ~(shmlba - 1);     /* round down */
                        else
 #ifndef __ARCH_FORCE_SHMLBA
                                if (addr & ~PAGE_MASK)
@@ -1107,7 +1108,7 @@ SYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg)
        unsigned long ret;
        long err;
 
-       err = do_shmat(shmid, shmaddr, shmflg, &ret);
+       err = do_shmat(shmid, shmaddr, shmflg, &ret, SHMLBA);
        if (err)
                return err;
        force_successful_syscall_return();
index 1d6f53f6b562441bcf5ef99c85067275eecf6c5a..0d1e32ce048eeb0ab0d8341519aedd7ead76db62 100644 (file)
@@ -73,7 +73,7 @@ SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second,
                default: {
                        unsigned long raddr;
                        ret = do_shmat(first, (char __user *)ptr,
-                                      second, &raddr);
+                                      second, &raddr, SHMLBA);
                        if (ret)
                                return ret;
                        return put_user(raddr, (unsigned long __user *) third);
index 75261a31d48da6ce865ffacb3e180b2708906678..eb07fd356f2759c74a74206a441bf3928ba95f5b 100644 (file)
@@ -804,7 +804,7 @@ out_up:
        return ERR_PTR(err);
 }
 
-#ifdef __ARCH_WANT_IPC_PARSE_VERSION
+#ifdef CONFIG_ARCH_WANT_IPC_PARSE_VERSION
 
 
 /**
@@ -826,7 +826,7 @@ int ipc_parse_version (int *cmd)
        }
 }
 
-#endif /* __ARCH_WANT_IPC_PARSE_VERSION */
+#endif /* CONFIG_ARCH_WANT_IPC_PARSE_VERSION */
 
 #ifdef CONFIG_PROC_FS
 struct ipc_proc_iter {
index 6f5c20bedaab21ff1dd0e05ad90ae0ecadeb82aa..850ef3e962cb36a13b2cdcd560f6bbccb8fcb489 100644 (file)
@@ -130,7 +130,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,
                                      struct ipc_ids *ids, int id, int cmd,
                                      struct ipc64_perm *perm, int extra_perm);
 
-#ifndef __ARCH_WANT_IPC_PARSE_VERSION
+#ifndef CONFIG_ARCH_WANT_IPC_PARSE_VERSION
   /* On IA-64, we always use the "64-bit version" of the IPC structures.  */ 
 # define ipc_parse_version(cmd)        IPC_64
 #else
index 4a3f28d2ca65239db79eee58b0e85366aa98669f..ea3b7b6191c7af3347dce055a88af200c55d1f5d 100644 (file)
@@ -1455,6 +1455,27 @@ void audit_log_key(struct audit_buffer *ab, char *key)
                audit_log_format(ab, "(null)");
 }
 
+/**
+ * audit_log_link_denied - report a link restriction denial
+ * @operation: specific link opreation
+ * @link: the path that triggered the restriction
+ */
+void audit_log_link_denied(const char *operation, struct path *link)
+{
+       struct audit_buffer *ab;
+
+       ab = audit_log_start(current->audit_context, GFP_KERNEL,
+                            AUDIT_ANOM_LINK);
+       audit_log_format(ab, "op=%s action=denied", operation);
+       audit_log_format(ab, " pid=%d comm=", current->pid);
+       audit_log_untrustedstring(ab, current->comm);
+       audit_log_d_path(ab, " path=", link);
+       audit_log_format(ab, " dev=");
+       audit_log_untrustedstring(ab, link->dentry->d_inode->i_sb->s_id);
+       audit_log_format(ab, " ino=%lu", link->dentry->d_inode->i_ino);
+       audit_log_end(ab);
+}
+
 /**
  * audit_log_end - end one audit record
  * @ab: the audit_buffer
index a4eb5227a19e482eaf2b821c94de845228381bdd..14d32588cccdb3c6d2d64dcd451b5f2efa8e9a19 100644 (file)
@@ -416,7 +416,7 @@ int __cpuinit cpu_up(unsigned int cpu)
 
        if (pgdat->node_zonelists->_zonerefs->zone == NULL) {
                mutex_lock(&zonelists_mutex);
-               build_all_zonelists(NULL);
+               build_all_zonelists(NULL, NULL);
                mutex_unlock(&zonelists_mutex);
        }
 #endif
index f93532748bca38340f0f2d196b54324a03590480..c08a22d02f7268ffd5e5516fb9d67182d22e5de8 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/swap.h>                /* try_to_free_swap */
 #include <linux/ptrace.h>      /* user_enable_single_step */
 #include <linux/kdebug.h>      /* notifier mechanism */
+#include "../../mm/internal.h" /* munlock_vma_page */
 
 #include <linux/uprobes.h>
 
@@ -112,14 +113,14 @@ static bool valid_vma(struct vm_area_struct *vma, bool is_register)
        return false;
 }
 
-static loff_t vma_address(struct vm_area_struct *vma, loff_t offset)
+static unsigned long offset_to_vaddr(struct vm_area_struct *vma, loff_t offset)
 {
-       loff_t vaddr;
-
-       vaddr = vma->vm_start + offset;
-       vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+       return vma->vm_start + offset - ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
+}
 
-       return vaddr;
+static loff_t vaddr_to_offset(struct vm_area_struct *vma, unsigned long vaddr)
+{
+       return ((loff_t)vma->vm_pgoff << PAGE_SHIFT) + (vaddr - vma->vm_start);
 }
 
 /**
@@ -127,25 +128,27 @@ static loff_t vma_address(struct vm_area_struct *vma, loff_t offset)
  * based on replace_page in mm/ksm.c
  *
  * @vma:      vma that holds the pte pointing to page
+ * @addr:     address the old @page is mapped at
  * @page:     the cowed page we are replacing by kpage
  * @kpage:    the modified page we replace page by
  *
  * Returns 0 on success, -EFAULT on failure.
  */
-static int __replace_page(struct vm_area_struct *vma, struct page *page, struct page *kpage)
+static int __replace_page(struct vm_area_struct *vma, unsigned long addr,
+                               struct page *page, struct page *kpage)
 {
        struct mm_struct *mm = vma->vm_mm;
-       unsigned long addr;
        spinlock_t *ptl;
        pte_t *ptep;
+       int err;
 
-       addr = page_address_in_vma(page, vma);
-       if (addr == -EFAULT)
-               return -EFAULT;
+       /* For try_to_free_swap() and munlock_vma_page() below */
+       lock_page(page);
 
+       err = -EAGAIN;
        ptep = page_check_address(page, mm, addr, &ptl, 0);
        if (!ptep)
-               return -EAGAIN;
+               goto unlock;
 
        get_page(kpage);
        page_add_new_anon_rmap(kpage, vma, addr);
@@ -162,10 +165,16 @@ static int __replace_page(struct vm_area_struct *vma, struct page *page, struct
        page_remove_rmap(page);
        if (!page_mapped(page))
                try_to_free_swap(page);
-       put_page(page);
        pte_unmap_unlock(ptep, ptl);
 
-       return 0;
+       if (vma->vm_flags & VM_LOCKED)
+               munlock_vma_page(page);
+       put_page(page);
+
+       err = 0;
+ unlock:
+       unlock_page(page);
+       return err;
 }
 
 /**
@@ -206,45 +215,23 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
                        unsigned long vaddr, uprobe_opcode_t opcode)
 {
        struct page *old_page, *new_page;
-       struct address_space *mapping;
        void *vaddr_old, *vaddr_new;
        struct vm_area_struct *vma;
-       struct uprobe *uprobe;
        int ret;
+
 retry:
        /* Read the page with vaddr into memory */
        ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &old_page, &vma);
        if (ret <= 0)
                return ret;
 
-       ret = -EINVAL;
-
-       /*
-        * We are interested in text pages only. Our pages of interest
-        * should be mapped for read and execute only. We desist from
-        * adding probes in write mapped pages since the breakpoints
-        * might end up in the file copy.
-        */
-       if (!valid_vma(vma, is_swbp_insn(&opcode)))
-               goto put_out;
-
-       uprobe = container_of(auprobe, struct uprobe, arch);
-       mapping = uprobe->inode->i_mapping;
-       if (mapping != vma->vm_file->f_mapping)
-               goto put_out;
-
        ret = -ENOMEM;
        new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr);
        if (!new_page)
-               goto put_out;
+               goto put_old;
 
        __SetPageUptodate(new_page);
 
-       /*
-        * lock page will serialize against do_wp_page()'s
-        * PageAnon() handling
-        */
-       lock_page(old_page);
        /* copy the page now that we've got it stable */
        vaddr_old = kmap_atomic(old_page);
        vaddr_new = kmap_atomic(new_page);
@@ -257,17 +244,13 @@ retry:
 
        ret = anon_vma_prepare(vma);
        if (ret)
-               goto unlock_out;
+               goto put_new;
 
-       lock_page(new_page);
-       ret = __replace_page(vma, old_page, new_page);
-       unlock_page(new_page);
+       ret = __replace_page(vma, vaddr, old_page, new_page);
 
-unlock_out:
-       unlock_page(old_page);
+put_new:
        page_cache_release(new_page);
-
-put_out:
+put_old:
        put_page(old_page);
 
        if (unlikely(ret == -EAGAIN))
@@ -791,7 +774,7 @@ build_map_info(struct address_space *mapping, loff_t offset, bool is_register)
                curr = info;
 
                info->mm = vma->vm_mm;
-               info->vaddr = vma_address(vma, offset);
+               info->vaddr = offset_to_vaddr(vma, offset);
        }
        mutex_unlock(&mapping->i_mmap_mutex);
 
@@ -839,12 +822,13 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
                        goto free;
 
                down_write(&mm->mmap_sem);
-               vma = find_vma(mm, (unsigned long)info->vaddr);
-               if (!vma || !valid_vma(vma, is_register))
+               vma = find_vma(mm, info->vaddr);
+               if (!vma || !valid_vma(vma, is_register) ||
+                   vma->vm_file->f_mapping->host != uprobe->inode)
                        goto unlock;
 
-               if (vma->vm_file->f_mapping->host != uprobe->inode ||
-                   vma_address(vma, uprobe->offset) != info->vaddr)
+               if (vma->vm_start > info->vaddr ||
+                   vaddr_to_offset(vma, info->vaddr) != uprobe->offset)
                        goto unlock;
 
                if (is_register) {
@@ -960,59 +944,66 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consume
                put_uprobe(uprobe);
 }
 
-/*
- * Of all the nodes that correspond to the given inode, return the node
- * with the least offset.
- */
-static struct rb_node *find_least_offset_node(struct inode *inode)
+static struct rb_node *
+find_node_in_range(struct inode *inode, loff_t min, loff_t max)
 {
-       struct uprobe u = { .inode = inode, .offset = 0};
        struct rb_node *n = uprobes_tree.rb_node;
-       struct rb_node *close_node = NULL;
-       struct uprobe *uprobe;
-       int match;
 
        while (n) {
-               uprobe = rb_entry(n, struct uprobe, rb_node);
-               match = match_uprobe(&u, uprobe);
-
-               if (uprobe->inode == inode)
-                       close_node = n;
-
-               if (!match)
-                       return close_node;
+               struct uprobe *u = rb_entry(n, struct uprobe, rb_node);
 
-               if (match < 0)
+               if (inode < u->inode) {
                        n = n->rb_left;
-               else
+               } else if (inode > u->inode) {
                        n = n->rb_right;
+               } else {
+                       if (max < u->offset)
+                               n = n->rb_left;
+                       else if (min > u->offset)
+                               n = n->rb_right;
+                       else
+                               break;
+               }
        }
 
-       return close_node;
+       return n;
 }
 
 /*
- * For a given inode, build a list of probes that need to be inserted.
+ * For a given range in vma, build a list of probes that need to be inserted.
  */
-static void build_probe_list(struct inode *inode, struct list_head *head)
+static void build_probe_list(struct inode *inode,
+                               struct vm_area_struct *vma,
+                               unsigned long start, unsigned long end,
+                               struct list_head *head)
 {
-       struct uprobe *uprobe;
+       loff_t min, max;
        unsigned long flags;
-       struct rb_node *n;
-
-       spin_lock_irqsave(&uprobes_treelock, flags);
-
-       n = find_least_offset_node(inode);
+       struct rb_node *n, *t;
+       struct uprobe *u;
 
-       for (; n; n = rb_next(n)) {
-               uprobe = rb_entry(n, struct uprobe, rb_node);
-               if (uprobe->inode != inode)
-                       break;
+       INIT_LIST_HEAD(head);
+       min = vaddr_to_offset(vma, start);
+       max = min + (end - start) - 1;
 
-               list_add(&uprobe->pending_list, head);
-               atomic_inc(&uprobe->ref);
+       spin_lock_irqsave(&uprobes_treelock, flags);
+       n = find_node_in_range(inode, min, max);
+       if (n) {
+               for (t = n; t; t = rb_prev(t)) {
+                       u = rb_entry(t, struct uprobe, rb_node);
+                       if (u->inode != inode || u->offset < min)
+                               break;
+                       list_add(&u->pending_list, head);
+                       atomic_inc(&u->ref);
+               }
+               for (t = n; (t = rb_next(t)); ) {
+                       u = rb_entry(t, struct uprobe, rb_node);
+                       if (u->inode != inode || u->offset > max)
+                               break;
+                       list_add(&u->pending_list, head);
+                       atomic_inc(&u->ref);
+               }
        }
-
        spin_unlock_irqrestore(&uprobes_treelock, flags);
 }
 
@@ -1031,7 +1022,7 @@ static void build_probe_list(struct inode *inode, struct list_head *head)
 int uprobe_mmap(struct vm_area_struct *vma)
 {
        struct list_head tmp_list;
-       struct uprobe *uprobe;
+       struct uprobe *uprobe, *u;
        struct inode *inode;
        int ret, count;
 
@@ -1042,21 +1033,15 @@ int uprobe_mmap(struct vm_area_struct *vma)
        if (!inode)
                return 0;
 
-       INIT_LIST_HEAD(&tmp_list);
        mutex_lock(uprobes_mmap_hash(inode));
-       build_probe_list(inode, &tmp_list);
+       build_probe_list(inode, vma, vma->vm_start, vma->vm_end, &tmp_list);
 
        ret = 0;
        count = 0;
 
-       list_for_each_entry(uprobe, &tmp_list, pending_list) {
+       list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
                if (!ret) {
-                       loff_t vaddr = vma_address(vma, uprobe->offset);
-
-                       if (vaddr < vma->vm_start || vaddr >= vma->vm_end) {
-                               put_uprobe(uprobe);
-                               continue;
-                       }
+                       unsigned long vaddr = offset_to_vaddr(vma, uprobe->offset);
 
                        ret = install_breakpoint(uprobe, vma->vm_mm, vma, vaddr);
                        /*
@@ -1097,12 +1082,15 @@ int uprobe_mmap(struct vm_area_struct *vma)
 void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end)
 {
        struct list_head tmp_list;
-       struct uprobe *uprobe;
+       struct uprobe *uprobe, *u;
        struct inode *inode;
 
        if (!atomic_read(&uprobe_events) || !valid_vma(vma, false))
                return;
 
+       if (!atomic_read(&vma->vm_mm->mm_users)) /* called by mmput() ? */
+               return;
+
        if (!atomic_read(&vma->vm_mm->uprobes_state.count))
                return;
 
@@ -1110,21 +1098,17 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lon
        if (!inode)
                return;
 
-       INIT_LIST_HEAD(&tmp_list);
        mutex_lock(uprobes_mmap_hash(inode));
-       build_probe_list(inode, &tmp_list);
-
-       list_for_each_entry(uprobe, &tmp_list, pending_list) {
-               loff_t vaddr = vma_address(vma, uprobe->offset);
-
-               if (vaddr >= start && vaddr < end) {
-                       /*
-                        * An unregister could have removed the probe before
-                        * unmap. So check before we decrement the count.
-                        */
-                       if (is_swbp_at_addr(vma->vm_mm, vaddr) == 1)
-                               atomic_dec(&vma->vm_mm->uprobes_state.count);
-               }
+       build_probe_list(inode, vma, start, end, &tmp_list);
+
+       list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
+               unsigned long vaddr = offset_to_vaddr(vma, uprobe->offset);
+               /*
+                * An unregister could have removed the probe before
+                * unmap. So check before we decrement the count.
+                */
+               if (is_swbp_at_addr(vma->vm_mm, vaddr) == 1)
+                       atomic_dec(&vma->vm_mm->uprobes_state.count);
                put_uprobe(uprobe);
        }
        mutex_unlock(uprobes_mmap_hash(inode));
@@ -1463,12 +1447,9 @@ static struct uprobe *find_active_uprobe(unsigned long bp_vaddr, int *is_swbp)
        vma = find_vma(mm, bp_vaddr);
        if (vma && vma->vm_start <= bp_vaddr) {
                if (valid_vma(vma, false)) {
-                       struct inode *inode;
-                       loff_t offset;
+                       struct inode *inode = vma->vm_file->f_mapping->host;
+                       loff_t offset = vaddr_to_offset(vma, bp_vaddr);
 
-                       inode = vma->vm_file->f_mapping->host;
-                       offset = bp_vaddr - vma->vm_start;
-                       offset += (vma->vm_pgoff << PAGE_SHIFT);
                        uprobe = find_uprobe(inode, offset);
                }
 
index ff1cad3b7bdc5eda7d525c165c6aaa4cfe659aec..3bd2280d79f6b5507537c3e294e05c77a69d678f 100644 (file)
@@ -114,6 +114,10 @@ int nr_processes(void)
        return total;
 }
 
+void __weak arch_release_task_struct(struct task_struct *tsk)
+{
+}
+
 #ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR
 static struct kmem_cache *task_struct_cachep;
 
@@ -122,17 +126,17 @@ static inline struct task_struct *alloc_task_struct_node(int node)
        return kmem_cache_alloc_node(task_struct_cachep, GFP_KERNEL, node);
 }
 
-void __weak arch_release_task_struct(struct task_struct *tsk) { }
-
 static inline void free_task_struct(struct task_struct *tsk)
 {
-       arch_release_task_struct(tsk);
        kmem_cache_free(task_struct_cachep, tsk);
 }
 #endif
 
+void __weak arch_release_thread_info(struct thread_info *ti)
+{
+}
+
 #ifndef CONFIG_ARCH_THREAD_INFO_ALLOCATOR
-void __weak arch_release_thread_info(struct thread_info *ti) { }
 
 /*
  * Allocate pages if THREAD_SIZE is >= PAGE_SIZE, otherwise use a
@@ -150,7 +154,6 @@ static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
 
 static inline void free_thread_info(struct thread_info *ti)
 {
-       arch_release_thread_info(ti);
        free_pages((unsigned long)ti, THREAD_SIZE_ORDER);
 }
 # else
@@ -164,7 +167,6 @@ static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
 
 static void free_thread_info(struct thread_info *ti)
 {
-       arch_release_thread_info(ti);
        kmem_cache_free(thread_info_cache, ti);
 }
 
@@ -205,10 +207,12 @@ static void account_kernel_stack(struct thread_info *ti, int account)
 void free_task(struct task_struct *tsk)
 {
        account_kernel_stack(tsk->stack, -1);
+       arch_release_thread_info(tsk->stack);
        free_thread_info(tsk->stack);
        rt_mutex_debug_task_free(tsk);
        ftrace_graph_exit_task(tsk);
        put_seccomp_filter(tsk);
+       arch_release_task_struct(tsk);
        free_task_struct(tsk);
 }
 EXPORT_SYMBOL(free_task);
@@ -298,23 +302,16 @@ static struct task_struct *dup_task_struct(struct task_struct *orig)
                return NULL;
 
        ti = alloc_thread_info_node(tsk, node);
-       if (!ti) {
-               free_task_struct(tsk);
-               return NULL;
-       }
+       if (!ti)
+               goto free_tsk;
 
        err = arch_dup_task_struct(tsk, orig);
+       if (err)
+               goto free_ti;
 
-       /*
-        * We defer looking at err, because we will need this setup
-        * for the clean up path to work correctly.
-        */
        tsk->stack = ti;
-       setup_thread_stack(tsk, orig);
-
-       if (err)
-               goto out;
 
+       setup_thread_stack(tsk, orig);
        clear_user_return_notifier(tsk);
        clear_tsk_need_resched(tsk);
        stackend = end_of_stack(tsk);
@@ -338,8 +335,9 @@ static struct task_struct *dup_task_struct(struct task_struct *orig)
 
        return tsk;
 
-out:
+free_ti:
        free_thread_info(ti);
+free_tsk:
        free_task_struct(tsk);
        return NULL;
 }
@@ -383,16 +381,14 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
                struct file *file;
 
                if (mpnt->vm_flags & VM_DONTCOPY) {
-                       long pages = vma_pages(mpnt);
-                       mm->total_vm -= pages;
                        vm_stat_account(mm, mpnt->vm_flags, mpnt->vm_file,
-                                                               -pages);
+                                                       -vma_pages(mpnt));
                        continue;
                }
                charge = 0;
                if (mpnt->vm_flags & VM_ACCOUNT) {
-                       unsigned long len;
-                       len = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
+                       unsigned long len = vma_pages(mpnt);
+
                        if (security_vm_enough_memory_mm(oldmm, len)) /* sic */
                                goto fail_nomem;
                        charge = len;
@@ -1310,7 +1306,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 #ifdef CONFIG_DEBUG_MUTEXES
        p->blocked_on = NULL; /* not blocked yet */
 #endif
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
        p->memcg_batch.do_batch = 0;
        p->memcg_batch.memcg = NULL;
 #endif
index bdb1803255513ee8ee53c3683541348c6c9a4e0b..131ca176b4973c9b5f40ff8aa1ab471587ac71ae 100644 (file)
@@ -133,7 +133,7 @@ irqreturn_t
 handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
 {
        irqreturn_t retval = IRQ_NONE;
-       unsigned int random = 0, irq = desc->irq_data.irq;
+       unsigned int flags = 0, irq = desc->irq_data.irq;
 
        do {
                irqreturn_t res;
@@ -161,7 +161,7 @@ handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
 
                        /* Fall through to add to randomness */
                case IRQ_HANDLED:
-                       random |= action->flags;
+                       flags |= action->flags;
                        break;
 
                default:
@@ -172,8 +172,7 @@ handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
                action = action->next;
        } while (action);
 
-       if (random & IRQF_SAMPLE_RANDOM)
-               add_interrupt_randomness(irq);
+       add_interrupt_randomness(irq, flags);
 
        if (!noirqdebug)
                note_interrupt(irq, desc, retval);
index 38c5eb839c92fe28232dea59a507cf75ce78187f..49a77727db42603e8e0ac963416d0f6b57823636 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/topology.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
 #include <linux/smp.h>
@@ -45,7 +46,8 @@ static struct irq_domain *irq_domain_alloc(struct device_node *of_node,
 {
        struct irq_domain *domain;
 
-       domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+       domain = kzalloc_node(sizeof(*domain), GFP_KERNEL,
+                             of_node_to_nid(of_node));
        if (WARN_ON(!domain))
                return NULL;
 
@@ -137,6 +139,36 @@ static unsigned int irq_domain_legacy_revmap(struct irq_domain *domain,
        return hwirq - first_hwirq + domain->revmap_data.legacy.first_irq;
 }
 
+/**
+ * irq_domain_add_simple() - Allocate and register a simple irq_domain.
+ * @of_node: pointer to interrupt controller's device tree node.
+ * @size: total number of irqs in mapping
+ * @first_irq: first number of irq block assigned to the domain
+ * @ops: map/unmap domain callbacks
+ * @host_data: Controller private data pointer
+ *
+ * Allocates a legacy irq_domain if irq_base is positive or a linear
+ * domain otherwise.
+ *
+ * This is intended to implement the expected behaviour for most
+ * interrupt controllers which is that a linear mapping should
+ * normally be used unless the system requires a legacy mapping in
+ * order to support supplying interrupt numbers during non-DT
+ * registration of devices.
+ */
+struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
+                                        unsigned int size,
+                                        unsigned int first_irq,
+                                        const struct irq_domain_ops *ops,
+                                        void *host_data)
+{
+       if (first_irq > 0)
+               return irq_domain_add_legacy(of_node, size, first_irq, 0,
+                                            ops, host_data);
+       else
+               return irq_domain_add_linear(of_node, size, ops, host_data);
+}
+
 /**
  * irq_domain_add_legacy() - Allocate and register a legacy revmap irq_domain.
  * @of_node: pointer to interrupt controller's device tree node.
@@ -203,7 +235,8 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
                 * one can then use irq_create_mapping() to
                 * explicitly change them
                 */
-               ops->map(domain, irq, hwirq);
+               if (ops->map)
+                       ops->map(domain, irq, hwirq);
 
                /* Clear norequest flags */
                irq_clear_status_flags(irq, IRQ_NOREQUEST);
@@ -215,7 +248,7 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
 EXPORT_SYMBOL_GPL(irq_domain_add_legacy);
 
 /**
- * irq_domain_add_linear() - Allocate and register a legacy revmap irq_domain.
+ * irq_domain_add_linear() - Allocate and register a linear revmap irq_domain.
  * @of_node: pointer to interrupt controller's device tree node.
  * @size: Number of interrupts in the domain.
  * @ops: map/unmap domain callbacks
@@ -229,7 +262,8 @@ struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
        struct irq_domain *domain;
        unsigned int *revmap;
 
-       revmap = kzalloc(sizeof(*revmap) * size, GFP_KERNEL);
+       revmap = kzalloc_node(sizeof(*revmap) * size, GFP_KERNEL,
+                             of_node_to_nid(of_node));
        if (WARN_ON(!revmap))
                return NULL;
 
@@ -330,24 +364,112 @@ void irq_set_default_host(struct irq_domain *domain)
 }
 EXPORT_SYMBOL_GPL(irq_set_default_host);
 
-static int irq_setup_virq(struct irq_domain *domain, unsigned int virq,
-                           irq_hw_number_t hwirq)
+static void irq_domain_disassociate_many(struct irq_domain *domain,
+                                        unsigned int irq_base, int count)
 {
-       struct irq_data *irq_data = irq_get_irq_data(virq);
+       /*
+        * disassociate in reverse order;
+        * not strictly necessary, but nice for unwinding
+        */
+       while (count--) {
+               int irq = irq_base + count;
+               struct irq_data *irq_data = irq_get_irq_data(irq);
+               irq_hw_number_t hwirq = irq_data->hwirq;
+
+               if (WARN_ON(!irq_data || irq_data->domain != domain))
+                       continue;
+
+               irq_set_status_flags(irq, IRQ_NOREQUEST);
+
+               /* remove chip and handler */
+               irq_set_chip_and_handler(irq, NULL, NULL);
+
+               /* Make sure it's completed */
+               synchronize_irq(irq);
+
+               /* Tell the PIC about it */
+               if (domain->ops->unmap)
+                       domain->ops->unmap(domain, irq);
+               smp_mb();
 
-       irq_data->hwirq = hwirq;
-       irq_data->domain = domain;
-       if (domain->ops->map(domain, virq, hwirq)) {
-               pr_debug("irq-%i==>hwirq-0x%lx mapping failed\n", virq, hwirq);
                irq_data->domain = NULL;
                irq_data->hwirq = 0;
-               return -1;
+
+               /* Clear reverse map */
+               switch(domain->revmap_type) {
+               case IRQ_DOMAIN_MAP_LINEAR:
+                       if (hwirq < domain->revmap_data.linear.size)
+                               domain->revmap_data.linear.revmap[hwirq] = 0;
+                       break;
+               case IRQ_DOMAIN_MAP_TREE:
+                       mutex_lock(&revmap_trees_mutex);
+                       radix_tree_delete(&domain->revmap_data.tree, hwirq);
+                       mutex_unlock(&revmap_trees_mutex);
+                       break;
+               }
        }
+}
+
+int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
+                             irq_hw_number_t hwirq_base, int count)
+{
+       unsigned int virq = irq_base;
+       irq_hw_number_t hwirq = hwirq_base;
+       int i, ret;
+
+       pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
+               of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
+
+       for (i = 0; i < count; i++) {
+               struct irq_data *irq_data = irq_get_irq_data(virq + i);
+
+               if (WARN(!irq_data, "error: irq_desc not allocated; "
+                        "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
+                       return -EINVAL;
+               if (WARN(irq_data->domain, "error: irq_desc already associated; "
+                        "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
+                       return -EINVAL;
+       };
+
+       for (i = 0; i < count; i++, virq++, hwirq++) {
+               struct irq_data *irq_data = irq_get_irq_data(virq);
+
+               irq_data->hwirq = hwirq;
+               irq_data->domain = domain;
+               if (domain->ops->map) {
+                       ret = domain->ops->map(domain, virq, hwirq);
+                       if (ret != 0) {
+                               pr_err("irq-%i==>hwirq-0x%lx mapping failed: %d\n",
+                                      virq, hwirq, ret);
+                               WARN_ON(1);
+                               irq_data->domain = NULL;
+                               irq_data->hwirq = 0;
+                               goto err_unmap;
+                       }
+               }
 
-       irq_clear_status_flags(virq, IRQ_NOREQUEST);
+               switch (domain->revmap_type) {
+               case IRQ_DOMAIN_MAP_LINEAR:
+                       if (hwirq < domain->revmap_data.linear.size)
+                               domain->revmap_data.linear.revmap[hwirq] = virq;
+                       break;
+               case IRQ_DOMAIN_MAP_TREE:
+                       mutex_lock(&revmap_trees_mutex);
+                       radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data);
+                       mutex_unlock(&revmap_trees_mutex);
+                       break;
+               }
+
+               irq_clear_status_flags(virq, IRQ_NOREQUEST);
+       }
 
        return 0;
+
+ err_unmap:
+       irq_domain_disassociate_many(domain, irq_base, i);
+       return -EINVAL;
 }
+EXPORT_SYMBOL_GPL(irq_domain_associate_many);
 
 /**
  * irq_create_direct_mapping() - Allocate an irq for direct mapping
@@ -364,10 +486,10 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain)
        if (domain == NULL)
                domain = irq_default_domain;
 
-       BUG_ON(domain == NULL);
-       WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_NOMAP);
+       if (WARN_ON(!domain || domain->revmap_type != IRQ_DOMAIN_MAP_NOMAP))
+               return 0;
 
-       virq = irq_alloc_desc_from(1, 0);
+       virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
        if (!virq) {
                pr_debug("create_direct virq allocation failed\n");
                return 0;
@@ -380,7 +502,7 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain)
        }
        pr_debug("create_direct obtained virq %d\n", virq);
 
-       if (irq_setup_virq(domain, virq, virq)) {
+       if (irq_domain_associate(domain, virq, virq)) {
                irq_free_desc(virq);
                return 0;
        }
@@ -433,17 +555,16 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
        hint = hwirq % nr_irqs;
        if (hint == 0)
                hint++;
-       virq = irq_alloc_desc_from(hint, 0);
+       virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node));
        if (virq <= 0)
-               virq = irq_alloc_desc_from(1, 0);
+               virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
        if (virq <= 0) {
                pr_debug("-> virq allocation failed\n");
                return 0;
        }
 
-       if (irq_setup_virq(domain, virq, hwirq)) {
-               if (domain->revmap_type != IRQ_DOMAIN_MAP_LEGACY)
-                       irq_free_desc(virq);
+       if (irq_domain_associate(domain, virq, hwirq)) {
+               irq_free_desc(virq);
                return 0;
        }
 
@@ -454,6 +575,44 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
 }
 EXPORT_SYMBOL_GPL(irq_create_mapping);
 
+/**
+ * irq_create_strict_mappings() - Map a range of hw irqs to fixed linux irqs
+ * @domain: domain owning the interrupt range
+ * @irq_base: beginning of linux IRQ range
+ * @hwirq_base: beginning of hardware IRQ range
+ * @count: Number of interrupts to map
+ *
+ * This routine is used for allocating and mapping a range of hardware
+ * irqs to linux irqs where the linux irq numbers are at pre-defined
+ * locations. For use by controllers that already have static mappings
+ * to insert in to the domain.
+ *
+ * Non-linear users can use irq_create_identity_mapping() for IRQ-at-a-time
+ * domain insertion.
+ *
+ * 0 is returned upon success, while any failure to establish a static
+ * mapping is treated as an error.
+ */
+int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
+                              irq_hw_number_t hwirq_base, int count)
+{
+       int ret;
+
+       ret = irq_alloc_descs(irq_base, irq_base, count,
+                             of_node_to_nid(domain->of_node));
+       if (unlikely(ret < 0))
+               return ret;
+
+       ret = irq_domain_associate_many(domain, irq_base, hwirq_base, count);
+       if (unlikely(ret < 0)) {
+               irq_free_descs(irq_base, count);
+               return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(irq_create_strict_mappings);
+
 unsigned int irq_create_of_mapping(struct device_node *controller,
                                   const u32 *intspec, unsigned int intsize)
 {
@@ -511,7 +670,6 @@ void irq_dispose_mapping(unsigned int virq)
 {
        struct irq_data *irq_data = irq_get_irq_data(virq);
        struct irq_domain *domain;
-       irq_hw_number_t hwirq;
 
        if (!virq || !irq_data)
                return;
@@ -524,33 +682,7 @@ void irq_dispose_mapping(unsigned int virq)
        if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
                return;
 
-       irq_set_status_flags(virq, IRQ_NOREQUEST);
-
-       /* remove chip and handler */
-       irq_set_chip_and_handler(virq, NULL, NULL);
-
-       /* Make sure it's completed */
-       synchronize_irq(virq);
-
-       /* Tell the PIC about it */
-       if (domain->ops->unmap)
-               domain->ops->unmap(domain, virq);
-       smp_mb();
-
-       /* Clear reverse map */
-       hwirq = irq_data->hwirq;
-       switch(domain->revmap_type) {
-       case IRQ_DOMAIN_MAP_LINEAR:
-               if (hwirq < domain->revmap_data.linear.size)
-                       domain->revmap_data.linear.revmap[hwirq] = 0;
-               break;
-       case IRQ_DOMAIN_MAP_TREE:
-               mutex_lock(&revmap_trees_mutex);
-               radix_tree_delete(&domain->revmap_data.tree, hwirq);
-               mutex_unlock(&revmap_trees_mutex);
-               break;
-       }
-
+       irq_domain_disassociate_many(domain, virq, 1);
        irq_free_desc(virq);
 }
 EXPORT_SYMBOL_GPL(irq_dispose_mapping);
@@ -559,16 +691,11 @@ EXPORT_SYMBOL_GPL(irq_dispose_mapping);
  * irq_find_mapping() - Find a linux irq from an hw irq number.
  * @domain: domain owning this hardware interrupt
  * @hwirq: hardware irq number in that domain space
- *
- * This is a slow path, for use by generic code. It's expected that an
- * irq controller implementation directly calls the appropriate low level
- * mapping function.
  */
 unsigned int irq_find_mapping(struct irq_domain *domain,
                              irq_hw_number_t hwirq)
 {
-       unsigned int i;
-       unsigned int hint = hwirq % nr_irqs;
+       struct irq_data *data;
 
        /* Look for default domain if nececssary */
        if (domain == NULL)
@@ -576,115 +703,47 @@ unsigned int irq_find_mapping(struct irq_domain *domain,
        if (domain == NULL)
                return 0;
 
-       /* legacy -> bail early */
-       if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
+       switch (domain->revmap_type) {
+       case IRQ_DOMAIN_MAP_LEGACY:
                return irq_domain_legacy_revmap(domain, hwirq);
-
-       /* Slow path does a linear search of the map */
-       if (hint == 0)
-               hint = 1;
-       i = hint;
-       do {
-               struct irq_data *data = irq_get_irq_data(i);
+       case IRQ_DOMAIN_MAP_LINEAR:
+               return irq_linear_revmap(domain, hwirq);
+       case IRQ_DOMAIN_MAP_TREE:
+               rcu_read_lock();
+               data = radix_tree_lookup(&domain->revmap_data.tree, hwirq);
+               rcu_read_unlock();
+               if (data)
+                       return data->irq;
+               break;
+       case IRQ_DOMAIN_MAP_NOMAP:
+               data = irq_get_irq_data(hwirq);
                if (data && (data->domain == domain) && (data->hwirq == hwirq))
-                       return i;
-               i++;
-               if (i >= nr_irqs)
-                       i = 1;
-       } while(i != hint);
+                       return hwirq;
+               break;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(irq_find_mapping);
 
-/**
- * irq_radix_revmap_lookup() - Find a linux irq from a hw irq number.
- * @domain: domain owning this hardware interrupt
- * @hwirq: hardware irq number in that domain space
- *
- * This is a fast path, for use by irq controller code that uses radix tree
- * revmaps
- */
-unsigned int irq_radix_revmap_lookup(struct irq_domain *domain,
-                                    irq_hw_number_t hwirq)
-{
-       struct irq_data *irq_data;
-
-       if (WARN_ON_ONCE(domain->revmap_type != IRQ_DOMAIN_MAP_TREE))
-               return irq_find_mapping(domain, hwirq);
-
-       /*
-        * Freeing an irq can delete nodes along the path to
-        * do the lookup via call_rcu.
-        */
-       rcu_read_lock();
-       irq_data = radix_tree_lookup(&domain->revmap_data.tree, hwirq);
-       rcu_read_unlock();
-
-       /*
-        * If found in radix tree, then fine.
-        * Else fallback to linear lookup - this should not happen in practice
-        * as it means that we failed to insert the node in the radix tree.
-        */
-       return irq_data ? irq_data->irq : irq_find_mapping(domain, hwirq);
-}
-EXPORT_SYMBOL_GPL(irq_radix_revmap_lookup);
-
-/**
- * irq_radix_revmap_insert() - Insert a hw irq to linux irq number mapping.
- * @domain: domain owning this hardware interrupt
- * @virq: linux irq number
- * @hwirq: hardware irq number in that domain space
- *
- * This is for use by irq controllers that use a radix tree reverse
- * mapping for fast lookup.
- */
-void irq_radix_revmap_insert(struct irq_domain *domain, unsigned int virq,
-                            irq_hw_number_t hwirq)
-{
-       struct irq_data *irq_data = irq_get_irq_data(virq);
-
-       if (WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_TREE))
-               return;
-
-       if (virq) {
-               mutex_lock(&revmap_trees_mutex);
-               radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data);
-               mutex_unlock(&revmap_trees_mutex);
-       }
-}
-EXPORT_SYMBOL_GPL(irq_radix_revmap_insert);
-
 /**
  * irq_linear_revmap() - Find a linux irq from a hw irq number.
  * @domain: domain owning this hardware interrupt
  * @hwirq: hardware irq number in that domain space
  *
- * This is a fast path, for use by irq controller code that uses linear
- * revmaps. It does fallback to the slow path if the revmap doesn't exist
- * yet and will create the revmap entry with appropriate locking
+ * This is a fast path that can be called directly by irq controller code to
+ * save a handful of instructions.
  */
 unsigned int irq_linear_revmap(struct irq_domain *domain,
                               irq_hw_number_t hwirq)
 {
-       unsigned int *revmap;
-
-       if (WARN_ON_ONCE(domain->revmap_type != IRQ_DOMAIN_MAP_LINEAR))
-               return irq_find_mapping(domain, hwirq);
+       BUG_ON(domain->revmap_type != IRQ_DOMAIN_MAP_LINEAR);
 
-       /* Check revmap bounds */
-       if (unlikely(hwirq >= domain->revmap_data.linear.size))
-               return irq_find_mapping(domain, hwirq);
-
-       /* Check if revmap was allocated */
-       revmap = domain->revmap_data.linear.revmap;
-       if (unlikely(revmap == NULL))
-               return irq_find_mapping(domain, hwirq);
-
-       /* Fill up revmap with slow path if no mapping found */
-       if (unlikely(!revmap[hwirq]))
-               revmap[hwirq] = irq_find_mapping(domain, hwirq);
+       /* Check revmap bounds; complain if exceeded */
+       if (WARN_ON(hwirq >= domain->revmap_data.linear.size))
+               return 0;
 
-       return revmap[hwirq];
+       return domain->revmap_data.linear.revmap[hwirq];
 }
 EXPORT_SYMBOL_GPL(irq_linear_revmap);
 
@@ -761,12 +820,6 @@ static int __init irq_debugfs_init(void)
 __initcall(irq_debugfs_init);
 #endif /* CONFIG_IRQ_DOMAIN_DEBUG */
 
-static int irq_domain_simple_map(struct irq_domain *d, unsigned int irq,
-                                irq_hw_number_t hwirq)
-{
-       return 0;
-}
-
 /**
  * irq_domain_xlate_onecell() - Generic xlate for direct one cell bindings
  *
@@ -829,7 +882,6 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d,
 EXPORT_SYMBOL_GPL(irq_domain_xlate_onetwocell);
 
 const struct irq_domain_ops irq_domain_simple_ops = {
-       .map = irq_domain_simple_map,
        .xlate = irq_domain_xlate_onetwocell,
 };
 EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
index 814c9ef6bba14d7e212d082c659fe83f0f361ce8..0a8e8f059627dec4ee56bab79849e0f86372d3b5 100644 (file)
@@ -893,22 +893,6 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                return -ENOSYS;
        if (!try_module_get(desc->owner))
                return -ENODEV;
-       /*
-        * Some drivers like serial.c use request_irq() heavily,
-        * so we have to be careful not to interfere with a
-        * running system.
-        */
-       if (new->flags & IRQF_SAMPLE_RANDOM) {
-               /*
-                * This function might sleep, we want to call it first,
-                * outside of the atomic block.
-                * Yes, this might clear the entropy pool if the wrong
-                * driver is attempted to be loaded, without actually
-                * installing a new handler, but is this really a problem,
-                * only the sysadmin is able to do this.
-                */
-               rand_initialize_irq(irq);
-       }
 
        /*
         * Check whether the interrupt nests into another interrupt
@@ -1354,7 +1338,6 @@ EXPORT_SYMBOL(free_irq);
  *     Flags:
  *
  *     IRQF_SHARED             Interrupt is shared
- *     IRQF_SAMPLE_RANDOM      The interrupt can be used for entropy
  *     IRQF_TRIGGER_*          Specify active edge(s) or level
  *
  */
index 4e2e472f6aeb35e5fb78e9a6ccdcc4c08fae57e3..0668d58d6413e8eeb8736b234be6d8d7378a4bea 100644 (file)
@@ -1424,7 +1424,7 @@ static void update_vmcoreinfo_note(void)
 
 void crash_save_vmcoreinfo(void)
 {
-       vmcoreinfo_append_str("CRASHTIME=%ld", get_seconds());
+       vmcoreinfo_append_str("CRASHTIME=%ld\n", get_seconds());
        update_vmcoreinfo_note();
 }
 
index ff2c7cb86d770aaf51712e330dc0f1e8a72a26e6..6f99aead66c6b897fbe14b9fb03b2a77bd17bc47 100644 (file)
@@ -45,6 +45,13 @@ extern int max_threads;
 
 static struct workqueue_struct *khelper_wq;
 
+/*
+ * kmod_thread_locker is used for deadlock avoidance.  There is no explicit
+ * locking to protect this global - it is private to the singleton khelper
+ * thread and should only ever be modified by that thread.
+ */
+static const struct task_struct *kmod_thread_locker;
+
 #define CAP_BSET       (void *)1
 #define CAP_PI         (void *)2
 
@@ -221,6 +228,13 @@ fail:
        return 0;
 }
 
+static int call_helper(void *data)
+{
+       /* Worker thread started blocking khelper thread. */
+       kmod_thread_locker = current;
+       return ____call_usermodehelper(data);
+}
+
 static void call_usermodehelper_freeinfo(struct subprocess_info *info)
 {
        if (info->cleanup)
@@ -295,9 +309,12 @@ static void __call_usermodehelper(struct work_struct *work)
        if (wait == UMH_WAIT_PROC)
                pid = kernel_thread(wait_for_helper, sub_info,
                                    CLONE_FS | CLONE_FILES | SIGCHLD);
-       else
-               pid = kernel_thread(____call_usermodehelper, sub_info,
+       else {
+               pid = kernel_thread(call_helper, sub_info,
                                    CLONE_VFORK | SIGCHLD);
+               /* Worker thread stopped blocking khelper thread. */
+               kmod_thread_locker = NULL;
+       }
 
        switch (wait) {
        case UMH_NO_WAIT:
@@ -548,6 +565,16 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
                retval = -EBUSY;
                goto out;
        }
+       /*
+        * Worker thread must not wait for khelper thread at below
+        * wait_for_completion() if the thread was created with CLONE_VFORK
+        * flag, for khelper thread is already waiting for the thread at
+        * wait_for_completion() in do_fork().
+        */
+       if (wait != UMH_NO_WAIT && current == kmod_thread_locker) {
+               retval = -EBUSY;
+               goto out;
+       }
 
        sub_info->complete = &done;
        sub_info->wait = wait;
@@ -577,6 +604,12 @@ unlock:
        return retval;
 }
 
+/*
+ * call_usermodehelper_fns() will not run the caller-provided cleanup function
+ * if a memory allocation failure is experienced.  So the caller might need to
+ * check the call_usermodehelper_fns() return value: if it is -ENOMEM, perform
+ * the necessaary cleanup within the caller.
+ */
 int call_usermodehelper_fns(
        char *path, char **argv, char **envp, int wait,
        int (*init)(struct subprocess_info *info, struct cred *new),
index d2a5f4ecc6ddd2ebc2da68764da646c7b96d9fc9..e1b2822fff97b8164ffae96e8a17ace02060a0c1 100644 (file)
@@ -74,6 +74,14 @@ void panic(const char *fmt, ...)
        long i, i_next = 0;
        int state = 0;
 
+       /*
+        * Disable local interrupts. This will prevent panic_smp_self_stop
+        * from deadlocking the first cpu that invokes the panic, since
+        * there is nothing to prevent an interrupt handler (that runs
+        * after the panic_lock is acquired) from invoking panic again.
+        */
+       local_irq_disable();
+
        /*
         * It's possible to come here directly from a panic-assertion and
         * not have preempt disabled. Some functions called from here want
index c8b7446b27dfc21add10f610e1692a2f3acee7ed..1da39ea248fdf3bee6f16f536693b0daad10afbe 100644 (file)
@@ -178,6 +178,9 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
        arch_suspend_enable_irqs();
        BUG_ON(irqs_disabled());
 
+       /* Kick the lockup detector */
+       lockup_detector_bootcpu_resume();
+
  Enable_cpus:
        enable_nonboot_cpus();
 
index 50c96b5651b696766c915b57dec92b5629a78866..6a76ab9d4476466b5ba3b3e37d38aadbb2e02644 100644 (file)
@@ -389,8 +389,10 @@ static ssize_t devkmsg_writev(struct kiocb *iocb, const struct iovec *iv,
 
        line = buf;
        for (i = 0; i < count; i++) {
-               if (copy_from_user(line, iv[i].iov_base, iv[i].iov_len))
+               if (copy_from_user(line, iv[i].iov_base, iv[i].iov_len)) {
+                       ret = -EFAULT;
                        goto out;
+               }
                line += iv[i].iov_len;
        }
 
@@ -1540,17 +1542,23 @@ asmlinkage int vprintk_emit(int facility, int level,
                lflags |= LOG_NEWLINE;
        }
 
-       /* strip syslog prefix and extract log level or control flags */
-       if (text[0] == '<' && text[1] && text[2] == '>') {
-               switch (text[1]) {
-               case '0' ... '7':
-                       if (level == -1)
-                               level = text[1] - '0';
-               case 'd':       /* KERN_DEFAULT */
-                       lflags |= LOG_PREFIX;
-               case 'c':       /* KERN_CONT */
-                       text += 3;
-                       text_len -= 3;
+       /* strip kernel syslog prefix and extract log level or control flags */
+       if (facility == 0) {
+               int kern_level = printk_get_level(text);
+
+               if (kern_level) {
+                       const char *end_of_header = printk_skip_level(text);
+                       switch (kern_level) {
+                       case '0' ... '7':
+                               if (level == -1)
+                                       level = kern_level - '0';
+                       case 'd':       /* KERN_DEFAULT */
+                               lflags |= LOG_PREFIX;
+                       case 'c':       /* KERN_CONT */
+                               break;
+                       }
+                       text_len -= end_of_header - text;
+                       text = (char *)end_of_header;
                }
        }
 
index dc8b477644436730bea0165a1ca6fd7f179b3144..34d45886ee8429acc7197c8ceaca18ef215fb2ae 100644 (file)
@@ -7,6 +7,8 @@
  * Arbitrary resource management.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/export.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
@@ -791,8 +793,28 @@ void __init reserve_region_with_split(struct resource *root,
                resource_size_t start, resource_size_t end,
                const char *name)
 {
+       int abort = 0;
+
        write_lock(&resource_lock);
-       __reserve_region_with_split(root, start, end, name);
+       if (root->start > start || root->end < end) {
+               pr_err("requested range [0x%llx-0x%llx] not in root %pr\n",
+                      (unsigned long long)start, (unsigned long long)end,
+                      root);
+               if (start > root->end || end < root->start)
+                       abort = 1;
+               else {
+                       if (end > root->end)
+                               end = root->end;
+                       if (start < root->start)
+                               start = root->start;
+                       pr_err("fixing request to [0x%llx-0x%llx]\n",
+                              (unsigned long long)start,
+                              (unsigned long long)end);
+               }
+               dump_stack();
+       }
+       if (!abort)
+               __reserve_region_with_split(root, start, end, name);
        write_unlock(&resource_lock);
 }
 
index 5d011ef4c0df37ee859ac3d3871fcabd2164316a..d325c4b2dcbb0c5995a903d76e3035129d1590c3 100644 (file)
@@ -1910,12 +1910,12 @@ static inline void
 prepare_task_switch(struct rq *rq, struct task_struct *prev,
                    struct task_struct *next)
 {
+       trace_sched_switch(prev, next);
        sched_info_switch(prev, next);
        perf_event_task_sched_out(prev, next);
        fire_sched_out_preempt_notifiers(prev, next);
        prepare_lock_switch(rq, next);
        prepare_arch_switch(next);
-       trace_sched_switch(prev, next);
 }
 
 /**
index 671f9594e3681e028c18b3e7456b1e58de2930c2..b73e681df09ea23e951b04672ca41227e9e0787f 100644 (file)
@@ -210,6 +210,14 @@ asmlinkage void __do_softirq(void)
        __u32 pending;
        int max_restart = MAX_SOFTIRQ_RESTART;
        int cpu;
+       unsigned long old_flags = current->flags;
+
+       /*
+        * Mask out PF_MEMALLOC s current task context is borrowed for the
+        * softirq. A softirq handled such as network RX might set PF_MEMALLOC
+        * again if the socket is related to swap
+        */
+       current->flags &= ~PF_MEMALLOC;
 
        pending = local_softirq_pending();
        account_system_vtime(current);
@@ -265,6 +273,7 @@ restart:
 
        account_system_vtime(current);
        __local_bh_enable(SOFTIRQ_OFFSET);
+       tsk_restore_flags(current, old_flags, PF_MEMALLOC);
 }
 
 #ifndef __ARCH_HAS_DO_SOFTIRQ
index 2d39a84cd8575e6295f666421cee01b41652b3b1..241507f23eca097871bec58f5976476f352f3d75 100644 (file)
@@ -2015,7 +2015,6 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                                break;
                        }
                        me->pdeath_signal = arg2;
-                       error = 0;
                        break;
                case PR_GET_PDEATHSIG:
                        error = put_user(me->pdeath_signal, (int __user *)arg2);
@@ -2029,7 +2028,6 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                                break;
                        }
                        set_dumpable(me->mm, arg2);
-                       error = 0;
                        break;
 
                case PR_SET_UNALIGN:
@@ -2056,10 +2054,7 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                case PR_SET_TIMING:
                        if (arg2 != PR_TIMING_STATISTICAL)
                                error = -EINVAL;
-                       else
-                               error = 0;
                        break;
-
                case PR_SET_NAME:
                        comm[sizeof(me->comm)-1] = 0;
                        if (strncpy_from_user(comm, (char __user *)arg2,
@@ -2067,20 +2062,19 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                                return -EFAULT;
                        set_task_comm(me, comm);
                        proc_comm_connector(me);
-                       return 0;
+                       break;
                case PR_GET_NAME:
                        get_task_comm(comm, me);
                        if (copy_to_user((char __user *)arg2, comm,
                                         sizeof(comm)))
                                return -EFAULT;
-                       return 0;
+                       break;
                case PR_GET_ENDIAN:
                        error = GET_ENDIAN(me, arg2);
                        break;
                case PR_SET_ENDIAN:
                        error = SET_ENDIAN(me, arg2);
                        break;
-
                case PR_GET_SECCOMP:
                        error = prctl_get_seccomp();
                        break;
@@ -2108,7 +2102,6 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                                        current->default_timer_slack_ns;
                        else
                                current->timer_slack_ns = arg2;
-                       error = 0;
                        break;
                case PR_MCE_KILL:
                        if (arg4 | arg5)
@@ -2134,7 +2127,6 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                        default:
                                return -EINVAL;
                        }
-                       error = 0;
                        break;
                case PR_MCE_KILL_GET:
                        if (arg2 | arg3 | arg4 | arg5)
@@ -2153,7 +2145,6 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                        break;
                case PR_SET_CHILD_SUBREAPER:
                        me->signal->is_child_subreaper = !!arg2;
-                       error = 0;
                        break;
                case PR_GET_CHILD_SUBREAPER:
                        error = put_user(me->signal->is_child_subreaper,
@@ -2195,46 +2186,52 @@ static void argv_cleanup(struct subprocess_info *info)
        argv_free(info->argv);
 }
 
-/**
- * orderly_poweroff - Trigger an orderly system poweroff
- * @force: force poweroff if command execution fails
- *
- * This may be called from any context to trigger a system shutdown.
- * If the orderly shutdown fails, it will force an immediate shutdown.
- */
-int orderly_poweroff(bool force)
+static int __orderly_poweroff(void)
 {
        int argc;
-       char **argv = argv_split(GFP_ATOMIC, poweroff_cmd, &argc);
+       char **argv;
        static char *envp[] = {
                "HOME=/",
                "PATH=/sbin:/bin:/usr/sbin:/usr/bin",
                NULL
        };
-       int ret = -ENOMEM;
+       int ret;
 
+       argv = argv_split(GFP_ATOMIC, poweroff_cmd, &argc);
        if (argv == NULL) {
                printk(KERN_WARNING "%s failed to allocate memory for \"%s\"\n",
                       __func__, poweroff_cmd);
-               goto out;
+               return -ENOMEM;
        }
 
        ret = call_usermodehelper_fns(argv[0], argv, envp, UMH_NO_WAIT,
                                      NULL, argv_cleanup, NULL);
-out:
-       if (likely(!ret))
-               return 0;
-
        if (ret == -ENOMEM)
                argv_free(argv);
 
-       if (force) {
+       return ret;
+}
+
+/**
+ * orderly_poweroff - Trigger an orderly system poweroff
+ * @force: force poweroff if command execution fails
+ *
+ * This may be called from any context to trigger a system shutdown.
+ * If the orderly shutdown fails, it will force an immediate shutdown.
+ */
+int orderly_poweroff(bool force)
+{
+       int ret = __orderly_poweroff();
+
+       if (ret && force) {
                printk(KERN_WARNING "Failed to start orderly shutdown: "
                       "forcing the issue\n");
 
-               /* I guess this should try to kick off some daemon to
-                  sync and poweroff asap.  Or not even bother syncing
-                  if we're doing an emergency shutdown? */
+               /*
+                * I guess this should try to kick off some daemon to sync and
+                * poweroff asap.  Or not even bother syncing if we're doing an
+                * emergency shutdown?
+                */
                emergency_sync();
                kernel_power_off();
        }
index 4ab11879aeb4d15baa3d8d25fa753725084ed809..87174ef59161eb32ce298f5ef9c2b0e9feb1b3f1 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/security.h>
 #include <linux/ctype.h>
 #include <linux/kmemcheck.h>
+#include <linux/kmemleak.h>
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -174,6 +175,11 @@ static int proc_dointvec_minmax_sysadmin(struct ctl_table *table, int write,
                                void __user *buffer, size_t *lenp, loff_t *ppos);
 #endif
 
+static int proc_dointvec_minmax_coredump(struct ctl_table *table, int write,
+               void __user *buffer, size_t *lenp, loff_t *ppos);
+static int proc_dostring_coredump(struct ctl_table *table, int write,
+               void __user *buffer, size_t *lenp, loff_t *ppos);
+
 #ifdef CONFIG_MAGIC_SYSRQ
 /* Note: sysrq code uses it's own private copy */
 static int __sysrq_enabled = SYSRQ_DEFAULT_ENABLE;
@@ -410,7 +416,7 @@ static struct ctl_table kern_table[] = {
                .data           = core_pattern,
                .maxlen         = CORENAME_MAX_SIZE,
                .mode           = 0644,
-               .proc_handler   = proc_dostring,
+               .proc_handler   = proc_dostring_coredump,
        },
        {
                .procname       = "core_pipe_limit",
@@ -1095,11 +1101,9 @@ static struct ctl_table vm_table[] = {
                .extra1         = &zero,
        },
        {
-               .procname       = "nr_pdflush_threads",
-               .data           = &nr_pdflush_threads,
-               .maxlen         = sizeof nr_pdflush_threads,
-               .mode           = 0444 /* read-only*/,
-               .proc_handler   = proc_dointvec,
+               .procname       = "nr_pdflush_threads",
+               .mode           = 0444 /* read-only */,
+               .proc_handler   = pdflush_proc_obsolete,
        },
        {
                .procname       = "swappiness",
@@ -1493,12 +1497,30 @@ static struct ctl_table fs_table[] = {
        },
 #endif
 #endif
+       {
+               .procname       = "protected_symlinks",
+               .data           = &sysctl_protected_symlinks,
+               .maxlen         = sizeof(int),
+               .mode           = 0600,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &zero,
+               .extra2         = &one,
+       },
+       {
+               .procname       = "protected_hardlinks",
+               .data           = &sysctl_protected_hardlinks,
+               .maxlen         = sizeof(int),
+               .mode           = 0600,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &zero,
+               .extra2         = &one,
+       },
        {
                .procname       = "suid_dumpable",
                .data           = &suid_dumpable,
                .maxlen         = sizeof(int),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
+               .proc_handler   = proc_dointvec_minmax_coredump,
                .extra1         = &zero,
                .extra2         = &two,
        },
@@ -1551,7 +1573,10 @@ static struct ctl_table dev_table[] = {
 
 int __init sysctl_init(void)
 {
-       register_sysctl_table(sysctl_base_table);
+       struct ctl_table_header *hdr;
+
+       hdr = register_sysctl_table(sysctl_base_table);
+       kmemleak_not_leak(hdr);
        return 0;
 }
 
@@ -2009,6 +2034,34 @@ int proc_dointvec_minmax(struct ctl_table *table, int write,
                                do_proc_dointvec_minmax_conv, &param);
 }
 
+static void validate_coredump_safety(void)
+{
+       if (suid_dumpable == SUID_DUMPABLE_SAFE &&
+           core_pattern[0] != '/' && core_pattern[0] != '|') {
+               printk(KERN_WARNING "Unsafe core_pattern used with "\
+                       "suid_dumpable=2. Pipe handler or fully qualified "\
+                       "core dump path required.\n");
+       }
+}
+
+static int proc_dointvec_minmax_coredump(struct ctl_table *table, int write,
+               void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       int error = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+       if (!error)
+               validate_coredump_safety();
+       return error;
+}
+
+static int proc_dostring_coredump(struct ctl_table *table, int write,
+                 void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       int error = proc_dostring(table, write, buffer, lenp, ppos);
+       if (!error)
+               validate_coredump_safety();
+       return error;
+}
+
 static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write,
                                     void __user *buffer,
                                     size_t *lenp, loff_t *ppos,
index a650694883a180e93c5ec1d6414e45ba904fcff3..65bdcf198d4e1c3f0727176a439aca4f22800136 100644 (file)
@@ -147,7 +147,7 @@ static const struct bin_table bin_vm_table[] = {
        { CTL_INT,      VM_DIRTY_RATIO,                 "dirty_ratio" },
        /* VM_DIRTY_WB_CS "dirty_writeback_centisecs" no longer used */
        /* VM_DIRTY_EXPIRE_CS "dirty_expire_centisecs" no longer used */
-       { CTL_INT,      VM_NR_PDFLUSH_THREADS,          "nr_pdflush_threads" },
+       /* VM_NR_PDFLUSH_THREADS "nr_pdflush_threads" no longer used */
        { CTL_INT,      VM_OVERCOMMIT_RATIO,            "overcommit_ratio" },
        /* VM_PAGEBUF unused */
        /* VM_HUGETLB_PAGES "nr_hugepages" no longer used */
index e66046456f4ffebab2ec0300e0537a1fb1911e56..d0a32796550fcdf81e40d18cda31a3b353d332d5 100644 (file)
@@ -436,6 +436,11 @@ static int cgroupstats_user_cmd(struct sk_buff *skb, struct genl_info *info)
 
        na = nla_reserve(rep_skb, CGROUPSTATS_TYPE_CGROUP_STATS,
                                sizeof(struct cgroupstats));
+       if (na == NULL) {
+               rc = -EMSGSIZE;
+               goto err;
+       }
+
        stats = nla_data(na);
        memset(stats, 0, sizeof(*stats));
 
index 4b1dfba70f7cf8ae7397623656a9b695028f702a..69add8a9da686a326be90e45aa09ab8427a24875 100644 (file)
@@ -575,7 +575,7 @@ out:
 /*
  * Create/destroy watchdog threads as CPUs come and go:
  */
-static int __cpuinit
+static int
 cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
 {
        int hotcpu = (unsigned long)hcpu;
@@ -610,10 +610,27 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
        return NOTIFY_OK;
 }
 
-static struct notifier_block __cpuinitdata cpu_nfb = {
+static struct notifier_block cpu_nfb = {
        .notifier_call = cpu_callback
 };
 
+#ifdef CONFIG_SUSPEND
+/*
+ * On exit from suspend we force an offline->online transition on the boot CPU
+ * so that the PMU state that was lost while in suspended state gets set up
+ * properly for the boot CPU.  This information is required for restarting the
+ * NMI watchdog.
+ */
+void lockup_detector_bootcpu_resume(void)
+{
+       void *cpu = (void *)(long)smp_processor_id();
+
+       cpu_callback(&cpu_nfb, CPU_DEAD_FROZEN, cpu);
+       cpu_callback(&cpu_nfb, CPU_UP_PREPARE_FROZEN, cpu);
+       cpu_callback(&cpu_nfb, CPU_ONLINE_FROZEN, cpu);
+}
+#endif
+
 void __init lockup_detector_init(void)
 {
        void *cpu = (void *)(long)smp_processor_id();
index 8269d56dcdaa38a2af895777bfb1a2d873df5f84..bb94c1ba616a4213bc11521adef4310387032353 100644 (file)
@@ -340,6 +340,9 @@ config NLATTR
 config GENERIC_ATOMIC64
        bool
 
+config ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
+       def_bool y if GENERIC_ATOMIC64
+
 config LRU_CACHE
        tristate
 
@@ -387,4 +390,10 @@ config SIGNATURE
          Digital signature verification. Currently only RSA is supported.
          Implementation is done using GnuPG MPI library
 
+#
+# libfdt files, only selected if needed.
+#
+config LIBFDT
+       bool
+
 endmenu
index 4a186508bf8b98de56872d254cc405f3a47c0cb2..2403a63b5da5bcf4e978d5dc2dfe8682cebef453 100644 (file)
@@ -1084,18 +1084,105 @@ config LKDTM
        Documentation on how to use the module can be found in
        Documentation/fault-injection/provoke-crashes.txt
 
+config NOTIFIER_ERROR_INJECTION
+       tristate "Notifier error injection"
+       depends on DEBUG_KERNEL
+       select DEBUG_FS
+       help
+         This option provides the ability to inject artifical errors to
+         specified notifier chain callbacks. It is useful to test the error
+         handling of notifier call chain failures.
+
+         Say N if unsure.
+
 config CPU_NOTIFIER_ERROR_INJECT
        tristate "CPU notifier error injection module"
-       depends on HOTPLUG_CPU && DEBUG_KERNEL
+       depends on HOTPLUG_CPU && NOTIFIER_ERROR_INJECTION
        help
          This option provides a kernel module that can be used to test
-         the error handling of the cpu notifiers
+         the error handling of the cpu notifiers by injecting artifical
+         errors to CPU notifier chain callbacks.  It is controlled through
+         debugfs interface under /sys/kernel/debug/notifier-error-inject/cpu
+
+         If the notifier call chain should be failed with some events
+         notified, write the error code to "actions/<notifier event>/error".
+
+         Example: Inject CPU offline error (-1 == -EPERM)
+
+         # cd /sys/kernel/debug/notifier-error-inject/cpu
+         # echo -1 > actions/CPU_DOWN_PREPARE/error
+         # echo 0 > /sys/devices/system/cpu/cpu1/online
+         bash: echo: write error: Operation not permitted
 
          To compile this code as a module, choose M here: the module will
          be called cpu-notifier-error-inject.
 
          If unsure, say N.
 
+config PM_NOTIFIER_ERROR_INJECT
+       tristate "PM notifier error injection module"
+       depends on PM && NOTIFIER_ERROR_INJECTION
+       default m if PM_DEBUG
+       help
+         This option provides the ability to inject artifical errors to
+         PM notifier chain callbacks.  It is controlled through debugfs
+         interface /sys/kernel/debug/notifier-error-inject/pm
+
+         If the notifier call chain should be failed with some events
+         notified, write the error code to "actions/<notifier event>/error".
+
+         Example: Inject PM suspend error (-12 = -ENOMEM)
+
+         # cd /sys/kernel/debug/notifier-error-inject/pm/
+         # echo -12 > actions/PM_SUSPEND_PREPARE/error
+         # echo mem > /sys/power/state
+         bash: echo: write error: Cannot allocate memory
+
+         To compile this code as a module, choose M here: the module will
+         be called pm-notifier-error-inject.
+
+         If unsure, say N.
+
+config MEMORY_NOTIFIER_ERROR_INJECT
+       tristate "Memory hotplug notifier error injection module"
+       depends on MEMORY_HOTPLUG_SPARSE && NOTIFIER_ERROR_INJECTION
+       help
+         This option provides the ability to inject artifical errors to
+         memory hotplug notifier chain callbacks.  It is controlled through
+         debugfs interface under /sys/kernel/debug/notifier-error-inject/memory
+
+         If the notifier call chain should be failed with some events
+         notified, write the error code to "actions/<notifier event>/error".
+
+         Example: Inject memory hotplug offline error (-12 == -ENOMEM)
+
+         # cd /sys/kernel/debug/notifier-error-inject/memory
+         # echo -12 > actions/MEM_GOING_OFFLINE/error
+         # echo offline > /sys/devices/system/memory/memoryXXX/state
+         bash: echo: write error: Cannot allocate memory
+
+         To compile this code as a module, choose M here: the module will
+         be called pSeries-reconfig-notifier-error-inject.
+
+         If unsure, say N.
+
+config PSERIES_RECONFIG_NOTIFIER_ERROR_INJECT
+       tristate "pSeries reconfig notifier error injection module"
+       depends on PPC_PSERIES && NOTIFIER_ERROR_INJECTION
+       help
+         This option provides the ability to inject artifical errors to
+         pSeries reconfig notifier chain callbacks.  It is controlled
+         through debugfs interface under
+         /sys/kernel/debug/notifier-error-inject/pSeries-reconfig/
+
+         If the notifier call chain should be failed with some events
+         notified, write the error code to "actions/<notifier event>/error".
+
+         To compile this code as a module, choose M here: the module will
+         be called memory-notifier-error-inject.
+
+         If unsure, say N.
+
 config FAULT_INJECTION
        bool "Fault-injection framework"
        depends on DEBUG_KERNEL
index 8c31a0cb75e97746af1cd02906ac149105250cc2..42d283edc4d3157cc81f897c51fd4ee20a615b62 100644 (file)
@@ -11,7 +11,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
         rbtree.o radix-tree.o dump_stack.o timerqueue.o\
         idr.o int_sqrt.o extable.o prio_tree.o \
         sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \
-        proportions.o prio_heap.o ratelimit.o show_mem.o \
+        proportions.o flex_proportions.o prio_heap.o ratelimit.o show_mem.o \
         is_single_threaded.o plist.o decompress.o
 
 lib-$(CONFIG_MMU) += ioremap.o
@@ -22,7 +22,7 @@ lib-y += kobject.o klist.o
 obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
         bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
         string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o \
-        bsearch.o find_last_bit.o find_next_bit.o llist.o
+        bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o
 obj-y += kstrtox.o
 obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o
 
@@ -90,7 +90,12 @@ obj-$(CONFIG_AUDIT_GENERIC) += audit.o
 obj-$(CONFIG_SWIOTLB) += swiotlb.o
 obj-$(CONFIG_IOMMU_HELPER) += iommu-helper.o
 obj-$(CONFIG_FAULT_INJECTION) += fault-inject.o
+obj-$(CONFIG_NOTIFIER_ERROR_INJECTION) += notifier-error-inject.o
 obj-$(CONFIG_CPU_NOTIFIER_ERROR_INJECT) += cpu-notifier-error-inject.o
+obj-$(CONFIG_PM_NOTIFIER_ERROR_INJECT) += pm-notifier-error-inject.o
+obj-$(CONFIG_MEMORY_NOTIFIER_ERROR_INJECT) += memory-notifier-error-inject.o
+obj-$(CONFIG_PSERIES_RECONFIG_NOTIFIER_ERROR_INJECT) += \
+       pSeries-reconfig-notifier-error-inject.o
 
 lib-$(CONFIG_GENERIC_BUG) += bug.o
 
@@ -130,6 +135,11 @@ obj-$(CONFIG_GENERIC_STRNLEN_USER) += strnlen_user.o
 
 obj-$(CONFIG_STMP_DEVICE) += stmp_device.o
 
+libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o
+$(foreach file, $(libfdt_files), \
+       $(eval CFLAGS_$(file) = -I$(src)/../scripts/dtc/libfdt))
+lib-$(CONFIG_LIBFDT) += $(libfdt_files)
+
 hostprogs-y    := gen_crc32table
 clean-files    := crc32table.h
 
index cb99b91c3a1d3af01b012fc421d69c8935733556..00bca223d1e1e6552bbdd0dd7c69fef2ff48c60d 100644 (file)
@@ -114,8 +114,7 @@ static __init int test_atomic64(void)
        r += one;
        BUG_ON(v.counter != r);
 
-#if defined(CONFIG_X86) || defined(CONFIG_MIPS) || defined(CONFIG_PPC) || \
-    defined(CONFIG_S390) || defined(_ASM_GENERIC_ATOMIC64_H) || defined(CONFIG_ARM)
+#ifdef CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
        INIT(onestwos);
        BUG_ON(atomic64_dec_if_positive(&v) != (onestwos - 1));
        r -= one;
@@ -129,7 +128,7 @@ static __init int test_atomic64(void)
        BUG_ON(atomic64_dec_if_positive(&v) != (-one - one));
        BUG_ON(v.counter != r);
 #else
-#warning Please implement atomic64_dec_if_positive for your architecture, and add it to the IF above
+#warning Please implement atomic64_dec_if_positive for your architecture and select the above Kconfig symbol
 #endif
 
        INIT(onestwos);
index 4dc20321b0d59e41cd14059eadf76e035df7a8f3..707ca24f7b187f4c2f18f590e1eee298fcdadcb3 100644 (file)
@@ -1,58 +1,45 @@
 #include <linux/kernel.h>
-#include <linux/cpu.h>
 #include <linux/module.h>
-#include <linux/notifier.h>
+#include <linux/cpu.h>
 
-static int priority;
-static int cpu_up_prepare_error;
-static int cpu_down_prepare_error;
+#include "notifier-error-inject.h"
 
+static int priority;
 module_param(priority, int, 0);
 MODULE_PARM_DESC(priority, "specify cpu notifier priority");
 
-module_param(cpu_up_prepare_error, int, 0644);
-MODULE_PARM_DESC(cpu_up_prepare_error,
-               "specify error code to inject CPU_UP_PREPARE action");
-
-module_param(cpu_down_prepare_error, int, 0644);
-MODULE_PARM_DESC(cpu_down_prepare_error,
-               "specify error code to inject CPU_DOWN_PREPARE action");
-
-static int err_inject_cpu_callback(struct notifier_block *nfb,
-                               unsigned long action, void *hcpu)
-{
-       int err = 0;
-
-       switch (action) {
-       case CPU_UP_PREPARE:
-       case CPU_UP_PREPARE_FROZEN:
-               err = cpu_up_prepare_error;
-               break;
-       case CPU_DOWN_PREPARE:
-       case CPU_DOWN_PREPARE_FROZEN:
-               err = cpu_down_prepare_error;
-               break;
+static struct notifier_err_inject cpu_notifier_err_inject = {
+       .actions = {
+               { NOTIFIER_ERR_INJECT_ACTION(CPU_UP_PREPARE) },
+               { NOTIFIER_ERR_INJECT_ACTION(CPU_UP_PREPARE_FROZEN) },
+               { NOTIFIER_ERR_INJECT_ACTION(CPU_DOWN_PREPARE) },
+               { NOTIFIER_ERR_INJECT_ACTION(CPU_DOWN_PREPARE_FROZEN) },
+               {}
        }
-       if (err)
-               printk(KERN_INFO "Injecting error (%d) at cpu notifier\n", err);
-
-       return notifier_from_errno(err);
-}
-
-static struct notifier_block err_inject_cpu_notifier = {
-       .notifier_call = err_inject_cpu_callback,
 };
 
+static struct dentry *dir;
+
 static int err_inject_init(void)
 {
-       err_inject_cpu_notifier.priority = priority;
+       int err;
+
+       dir = notifier_err_inject_init("cpu", notifier_err_inject_dir,
+                                       &cpu_notifier_err_inject, priority);
+       if (IS_ERR(dir))
+               return PTR_ERR(dir);
+
+       err = register_hotcpu_notifier(&cpu_notifier_err_inject.nb);
+       if (err)
+               debugfs_remove_recursive(dir);
 
-       return register_hotcpu_notifier(&err_inject_cpu_notifier);
+       return err;
 }
 
 static void err_inject_exit(void)
 {
-       unregister_hotcpu_notifier(&err_inject_cpu_notifier);
+       unregister_hotcpu_notifier(&cpu_notifier_err_inject.nb);
+       debugfs_remove_recursive(dir);
 }
 
 module_init(err_inject_init);
index b0d278fb1d911a5112db498873071cd64c368ad4..61774b8db4de6911453ccd383df7d38dd2f2b42f 100644 (file)
@@ -74,7 +74,9 @@ crc32_body(u32 crc, unsigned char const *buf, size_t len, const u32 (*tab)[256])
        size_t i;
 # endif
        const u32 *t0=tab[0], *t1=tab[1], *t2=tab[2], *t3=tab[3];
+# if CRC_LE_BITS != 32
        const u32 *t4 = tab[4], *t5 = tab[5], *t6 = tab[6], *t7 = tab[7];
+# endif
        u32 q;
 
        /* Align it */
diff --git a/lib/fdt.c b/lib/fdt.c
new file mode 100644 (file)
index 0000000..97f2006
--- /dev/null
+++ b/lib/fdt.c
@@ -0,0 +1,2 @@
+#include <linux/libfdt_env.h>
+#include "../scripts/dtc/libfdt/fdt.c"
diff --git a/lib/fdt_ro.c b/lib/fdt_ro.c
new file mode 100644 (file)
index 0000000..f73c04e
--- /dev/null
@@ -0,0 +1,2 @@
+#include <linux/libfdt_env.h>
+#include "../scripts/dtc/libfdt/fdt_ro.c"
diff --git a/lib/fdt_rw.c b/lib/fdt_rw.c
new file mode 100644 (file)
index 0000000..0c1f0f4
--- /dev/null
@@ -0,0 +1,2 @@
+#include <linux/libfdt_env.h>
+#include "../scripts/dtc/libfdt/fdt_rw.c"
diff --git a/lib/fdt_strerror.c b/lib/fdt_strerror.c
new file mode 100644 (file)
index 0000000..8713e3f
--- /dev/null
@@ -0,0 +1,2 @@
+#include <linux/libfdt_env.h>
+#include "../scripts/dtc/libfdt/fdt_strerror.c"
diff --git a/lib/fdt_sw.c b/lib/fdt_sw.c
new file mode 100644 (file)
index 0000000..9ac7e50
--- /dev/null
@@ -0,0 +1,2 @@
+#include <linux/libfdt_env.h>
+#include "../scripts/dtc/libfdt/fdt_sw.c"
diff --git a/lib/fdt_wip.c b/lib/fdt_wip.c
new file mode 100644 (file)
index 0000000..45b3fc3
--- /dev/null
@@ -0,0 +1,2 @@
+#include <linux/libfdt_env.h>
+#include "../scripts/dtc/libfdt/fdt_wip.c"
diff --git a/lib/flex_proportions.c b/lib/flex_proportions.c
new file mode 100644 (file)
index 0000000..c785554
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ *  Floating proportions with flexible aging period
+ *
+ *   Copyright (C) 2011, SUSE, Jan Kara <jack@suse.cz>
+ *
+ * The goal of this code is: Given different types of event, measure proportion
+ * of each type of event over time. The proportions are measured with
+ * exponentially decaying history to give smooth transitions. A formula
+ * expressing proportion of event of type 'j' is:
+ *
+ *   p_{j} = (\Sum_{i>=0} x_{i,j}/2^{i+1})/(\Sum_{i>=0} x_i/2^{i+1})
+ *
+ * Where x_{i,j} is j's number of events in i-th last time period and x_i is
+ * total number of events in i-th last time period.
+ *
+ * Note that p_{j}'s are normalised, i.e.
+ *
+ *   \Sum_{j} p_{j} = 1,
+ *
+ * This formula can be straightforwardly computed by maintaing denominator
+ * (let's call it 'd') and for each event type its numerator (let's call it
+ * 'n_j'). When an event of type 'j' happens, we simply need to do:
+ *   n_j++; d++;
+ *
+ * When a new period is declared, we could do:
+ *   d /= 2
+ *   for each j
+ *     n_j /= 2
+ *
+ * To avoid iteration over all event types, we instead shift numerator of event
+ * j lazily when someone asks for a proportion of event j or when event j
+ * occurs. This can bit trivially implemented by remembering last period in
+ * which something happened with proportion of type j.
+ */
+#include <linux/flex_proportions.h>
+
+int fprop_global_init(struct fprop_global *p)
+{
+       int err;
+
+       p->period = 0;
+       /* Use 1 to avoid dealing with periods with 0 events... */
+       err = percpu_counter_init(&p->events, 1);
+       if (err)
+               return err;
+       seqcount_init(&p->sequence);
+       return 0;
+}
+
+void fprop_global_destroy(struct fprop_global *p)
+{
+       percpu_counter_destroy(&p->events);
+}
+
+/*
+ * Declare @periods new periods. It is upto the caller to make sure period
+ * transitions cannot happen in parallel.
+ *
+ * The function returns true if the proportions are still defined and false
+ * if aging zeroed out all events. This can be used to detect whether declaring
+ * further periods has any effect.
+ */
+bool fprop_new_period(struct fprop_global *p, int periods)
+{
+       u64 events;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       events = percpu_counter_sum(&p->events);
+       /*
+        * Don't do anything if there are no events.
+        */
+       if (events <= 1) {
+               local_irq_restore(flags);
+               return false;
+       }
+       write_seqcount_begin(&p->sequence);
+       if (periods < 64)
+               events -= events >> periods;
+       /* Use addition to avoid losing events happening between sum and set */
+       percpu_counter_add(&p->events, -events);
+       p->period += periods;
+       write_seqcount_end(&p->sequence);
+       local_irq_restore(flags);
+
+       return true;
+}
+
+/*
+ * ---- SINGLE ----
+ */
+
+int fprop_local_init_single(struct fprop_local_single *pl)
+{
+       pl->events = 0;
+       pl->period = 0;
+       raw_spin_lock_init(&pl->lock);
+       return 0;
+}
+
+void fprop_local_destroy_single(struct fprop_local_single *pl)
+{
+}
+
+static void fprop_reflect_period_single(struct fprop_global *p,
+                                       struct fprop_local_single *pl)
+{
+       unsigned int period = p->period;
+       unsigned long flags;
+
+       /* Fast path - period didn't change */
+       if (pl->period == period)
+               return;
+       raw_spin_lock_irqsave(&pl->lock, flags);
+       /* Someone updated pl->period while we were spinning? */
+       if (pl->period >= period) {
+               raw_spin_unlock_irqrestore(&pl->lock, flags);
+               return;
+       }
+       /* Aging zeroed our fraction? */
+       if (period - pl->period < BITS_PER_LONG)
+               pl->events >>= period - pl->period;
+       else
+               pl->events = 0;
+       pl->period = period;
+       raw_spin_unlock_irqrestore(&pl->lock, flags);
+}
+
+/* Event of type pl happened */
+void __fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl)
+{
+       fprop_reflect_period_single(p, pl);
+       pl->events++;
+       percpu_counter_add(&p->events, 1);
+}
+
+/* Return fraction of events of type pl */
+void fprop_fraction_single(struct fprop_global *p,
+                          struct fprop_local_single *pl,
+                          unsigned long *numerator, unsigned long *denominator)
+{
+       unsigned int seq;
+       s64 num, den;
+
+       do {
+               seq = read_seqcount_begin(&p->sequence);
+               fprop_reflect_period_single(p, pl);
+               num = pl->events;
+               den = percpu_counter_read_positive(&p->events);
+       } while (read_seqcount_retry(&p->sequence, seq));
+
+       /*
+        * Make fraction <= 1 and denominator > 0 even in presence of percpu
+        * counter errors
+        */
+       if (den <= num) {
+               if (num)
+                       den = num;
+               else
+                       den = 1;
+       }
+       *denominator = den;
+       *numerator = num;
+}
+
+/*
+ * ---- PERCPU ----
+ */
+#define PROP_BATCH (8*(1+ilog2(nr_cpu_ids)))
+
+int fprop_local_init_percpu(struct fprop_local_percpu *pl)
+{
+       int err;
+
+       err = percpu_counter_init(&pl->events, 0);
+       if (err)
+               return err;
+       pl->period = 0;
+       raw_spin_lock_init(&pl->lock);
+       return 0;
+}
+
+void fprop_local_destroy_percpu(struct fprop_local_percpu *pl)
+{
+       percpu_counter_destroy(&pl->events);
+}
+
+static void fprop_reflect_period_percpu(struct fprop_global *p,
+                                       struct fprop_local_percpu *pl)
+{
+       unsigned int period = p->period;
+       unsigned long flags;
+
+       /* Fast path - period didn't change */
+       if (pl->period == period)
+               return;
+       raw_spin_lock_irqsave(&pl->lock, flags);
+       /* Someone updated pl->period while we were spinning? */
+       if (pl->period >= period) {
+               raw_spin_unlock_irqrestore(&pl->lock, flags);
+               return;
+       }
+       /* Aging zeroed our fraction? */
+       if (period - pl->period < BITS_PER_LONG) {
+               s64 val = percpu_counter_read(&pl->events);
+
+               if (val < (nr_cpu_ids * PROP_BATCH))
+                       val = percpu_counter_sum(&pl->events);
+
+               __percpu_counter_add(&pl->events,
+                       -val + (val >> (period-pl->period)), PROP_BATCH);
+       } else
+               percpu_counter_set(&pl->events, 0);
+       pl->period = period;
+       raw_spin_unlock_irqrestore(&pl->lock, flags);
+}
+
+/* Event of type pl happened */
+void __fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl)
+{
+       fprop_reflect_period_percpu(p, pl);
+       __percpu_counter_add(&pl->events, 1, PROP_BATCH);
+       percpu_counter_add(&p->events, 1);
+}
+
+void fprop_fraction_percpu(struct fprop_global *p,
+                          struct fprop_local_percpu *pl,
+                          unsigned long *numerator, unsigned long *denominator)
+{
+       unsigned int seq;
+       s64 num, den;
+
+       do {
+               seq = read_seqcount_begin(&p->sequence);
+               fprop_reflect_period_percpu(p, pl);
+               num = percpu_counter_read_positive(&pl->events);
+               den = percpu_counter_read_positive(&p->events);
+       } while (read_seqcount_retry(&p->sequence, seq));
+
+       /*
+        * Make fraction <= 1 and denominator > 0 even in presence of percpu
+        * counter errors
+        */
+       if (den <= num) {
+               if (num)
+                       den = num;
+               else
+                       den = 1;
+       }
+       *denominator = den;
+       *numerator = num;
+}
+
+/*
+ * Like __fprop_inc_percpu() except that event is counted only if the given
+ * type has fraction smaller than @max_frac/FPROP_FRAC_BASE
+ */
+void __fprop_inc_percpu_max(struct fprop_global *p,
+                           struct fprop_local_percpu *pl, int max_frac)
+{
+       if (unlikely(max_frac < FPROP_FRAC_BASE)) {
+               unsigned long numerator, denominator;
+
+               fprop_fraction_percpu(p, pl, &numerator, &denominator);
+               if (numerator >
+                   (((u64)denominator) * max_frac) >> FPROP_FRAC_SHIFT)
+                       return;
+       } else
+               fprop_reflect_period_percpu(p, pl);
+       __percpu_counter_add(&pl->events, 1, PROP_BATCH);
+       percpu_counter_add(&p->events, 1);
+}
diff --git a/lib/memory-notifier-error-inject.c b/lib/memory-notifier-error-inject.c
new file mode 100644 (file)
index 0000000..e6239bf
--- /dev/null
@@ -0,0 +1,48 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/memory.h>
+
+#include "notifier-error-inject.h"
+
+static int priority;
+module_param(priority, int, 0);
+MODULE_PARM_DESC(priority, "specify memory notifier priority");
+
+static struct notifier_err_inject memory_notifier_err_inject = {
+       .actions = {
+               { NOTIFIER_ERR_INJECT_ACTION(MEM_GOING_ONLINE) },
+               { NOTIFIER_ERR_INJECT_ACTION(MEM_GOING_OFFLINE) },
+               {}
+       }
+};
+
+static struct dentry *dir;
+
+static int err_inject_init(void)
+{
+       int err;
+
+       dir = notifier_err_inject_init("memory", notifier_err_inject_dir,
+                                       &memory_notifier_err_inject, priority);
+       if (IS_ERR(dir))
+               return PTR_ERR(dir);
+
+       err = register_memory_notifier(&memory_notifier_err_inject.nb);
+       if (err)
+               debugfs_remove_recursive(dir);
+
+       return err;
+}
+
+static void err_inject_exit(void)
+{
+       unregister_memory_notifier(&memory_notifier_err_inject.nb);
+       debugfs_remove_recursive(dir);
+}
+
+module_init(err_inject_init);
+module_exit(err_inject_exit);
+
+MODULE_DESCRIPTION("memory notifier error injection module");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
diff --git a/lib/memweight.c b/lib/memweight.c
new file mode 100644 (file)
index 0000000..e35fc87
--- /dev/null
@@ -0,0 +1,38 @@
+#include <linux/export.h>
+#include <linux/bug.h>
+#include <linux/bitmap.h>
+
+/**
+ * memweight - count the total number of bits set in memory area
+ * @ptr: pointer to the start of the area
+ * @bytes: the size of the area
+ */
+size_t memweight(const void *ptr, size_t bytes)
+{
+       size_t ret = 0;
+       size_t longs;
+       const unsigned char *bitmap = ptr;
+
+       for (; bytes > 0 && ((unsigned long)bitmap) % sizeof(long);
+                       bytes--, bitmap++)
+               ret += hweight8(*bitmap);
+
+       longs = bytes / sizeof(long);
+       if (longs) {
+               BUG_ON(longs >= INT_MAX / BITS_PER_LONG);
+               ret += bitmap_weight((unsigned long *)bitmap,
+                               longs * BITS_PER_LONG);
+               bytes -= longs * sizeof(long);
+               bitmap += longs * sizeof(long);
+       }
+       /*
+        * The reason that this last loop is distinct from the preceding
+        * bitmap_weight() call is to compute 1-bits in the last region smaller
+        * than sizeof(long) properly on big-endian systems.
+        */
+       for (; bytes > 0; bytes--, bitmap++)
+               ret += hweight8(*bitmap);
+
+       return ret;
+}
+EXPORT_SYMBOL(memweight);
diff --git a/lib/notifier-error-inject.c b/lib/notifier-error-inject.c
new file mode 100644 (file)
index 0000000..44b92cb
--- /dev/null
@@ -0,0 +1,112 @@
+#include <linux/module.h>
+
+#include "notifier-error-inject.h"
+
+static int debugfs_errno_set(void *data, u64 val)
+{
+       *(int *)data = clamp_t(int, val, -MAX_ERRNO, 0);
+       return 0;
+}
+
+static int debugfs_errno_get(void *data, u64 *val)
+{
+       *val = *(int *)data;
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_errno, debugfs_errno_get, debugfs_errno_set,
+                       "%lld\n");
+
+static struct dentry *debugfs_create_errno(const char *name, mode_t mode,
+                               struct dentry *parent, int *value)
+{
+       return debugfs_create_file(name, mode, parent, value, &fops_errno);
+}
+
+static int notifier_err_inject_callback(struct notifier_block *nb,
+                               unsigned long val, void *p)
+{
+       int err = 0;
+       struct notifier_err_inject *err_inject =
+               container_of(nb, struct notifier_err_inject, nb);
+       struct notifier_err_inject_action *action;
+
+       for (action = err_inject->actions; action->name; action++) {
+               if (action->val == val) {
+                       err = action->error;
+                       break;
+               }
+       }
+       if (err)
+               pr_info("Injecting error (%d) to %s\n", err, action->name);
+
+       return notifier_from_errno(err);
+}
+
+struct dentry *notifier_err_inject_dir;
+EXPORT_SYMBOL_GPL(notifier_err_inject_dir);
+
+struct dentry *notifier_err_inject_init(const char *name, struct dentry *parent,
+                       struct notifier_err_inject *err_inject, int priority)
+{
+       struct notifier_err_inject_action *action;
+       mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
+       struct dentry *dir;
+       struct dentry *actions_dir;
+
+       err_inject->nb.notifier_call = notifier_err_inject_callback;
+       err_inject->nb.priority = priority;
+
+       dir = debugfs_create_dir(name, parent);
+       if (!dir)
+               return ERR_PTR(-ENOMEM);
+
+       actions_dir = debugfs_create_dir("actions", dir);
+       if (!actions_dir)
+               goto fail;
+
+       for (action = err_inject->actions; action->name; action++) {
+               struct dentry *action_dir;
+
+               action_dir = debugfs_create_dir(action->name, actions_dir);
+               if (!action_dir)
+                       goto fail;
+
+               /*
+                * Create debugfs r/w file containing action->error. If
+                * notifier call chain is called with action->val, it will
+                * fail with the error code
+                */
+               if (!debugfs_create_errno("error", mode, action_dir,
+                                       &action->error))
+                       goto fail;
+       }
+       return dir;
+fail:
+       debugfs_remove_recursive(dir);
+       return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL_GPL(notifier_err_inject_init);
+
+static int __init err_inject_init(void)
+{
+       notifier_err_inject_dir =
+               debugfs_create_dir("notifier-error-inject", NULL);
+
+       if (!notifier_err_inject_dir)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void __exit err_inject_exit(void)
+{
+       debugfs_remove_recursive(notifier_err_inject_dir);
+}
+
+module_init(err_inject_init);
+module_exit(err_inject_exit);
+
+MODULE_DESCRIPTION("Notifier error injection module");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
diff --git a/lib/notifier-error-inject.h b/lib/notifier-error-inject.h
new file mode 100644 (file)
index 0000000..99b3b6f
--- /dev/null
@@ -0,0 +1,24 @@
+#include <linux/atomic.h>
+#include <linux/debugfs.h>
+#include <linux/notifier.h>
+
+struct notifier_err_inject_action {
+       unsigned long val;
+       int error;
+       const char *name;
+};
+
+#define NOTIFIER_ERR_INJECT_ACTION(action)     \
+       .name = #action, .val = (action),
+
+struct notifier_err_inject {
+       struct notifier_block nb;
+       struct notifier_err_inject_action actions[];
+       /* The last slot must be terminated with zero sentinel */
+};
+
+extern struct dentry *notifier_err_inject_dir;
+
+extern struct dentry *notifier_err_inject_init(const char *name,
+               struct dentry *parent, struct notifier_err_inject *err_inject,
+               int priority);
diff --git a/lib/pSeries-reconfig-notifier-error-inject.c b/lib/pSeries-reconfig-notifier-error-inject.c
new file mode 100644 (file)
index 0000000..7f7c98d
--- /dev/null
@@ -0,0 +1,51 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <asm/pSeries_reconfig.h>
+
+#include "notifier-error-inject.h"
+
+static int priority;
+module_param(priority, int, 0);
+MODULE_PARM_DESC(priority, "specify pSeries reconfig notifier priority");
+
+static struct notifier_err_inject reconfig_err_inject = {
+       .actions = {
+               { NOTIFIER_ERR_INJECT_ACTION(PSERIES_RECONFIG_ADD) },
+               { NOTIFIER_ERR_INJECT_ACTION(PSERIES_RECONFIG_REMOVE) },
+               { NOTIFIER_ERR_INJECT_ACTION(PSERIES_DRCONF_MEM_ADD) },
+               { NOTIFIER_ERR_INJECT_ACTION(PSERIES_DRCONF_MEM_REMOVE) },
+               {}
+       }
+};
+
+static struct dentry *dir;
+
+static int err_inject_init(void)
+{
+       int err;
+
+       dir = notifier_err_inject_init("pSeries-reconfig",
+               notifier_err_inject_dir, &reconfig_err_inject, priority);
+       if (IS_ERR(dir))
+               return PTR_ERR(dir);
+
+       err = pSeries_reconfig_notifier_register(&reconfig_err_inject.nb);
+       if (err)
+               debugfs_remove_recursive(dir);
+
+       return err;
+}
+
+static void err_inject_exit(void)
+{
+       pSeries_reconfig_notifier_unregister(&reconfig_err_inject.nb);
+       debugfs_remove_recursive(dir);
+}
+
+module_init(err_inject_init);
+module_exit(err_inject_exit);
+
+MODULE_DESCRIPTION("pSeries reconfig notifier error injection module");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
index f8a3f1a829b8a9d767bed39c5138c7015380196b..ba6085d9c7411f33bbb6aa0077423e822ad1c3dc 100644 (file)
@@ -12,7 +12,7 @@
 
 #ifdef CONFIG_HOTPLUG_CPU
 static LIST_HEAD(percpu_counters);
-static DEFINE_MUTEX(percpu_counters_lock);
+static DEFINE_SPINLOCK(percpu_counters_lock);
 #endif
 
 #ifdef CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER
@@ -123,9 +123,9 @@ int __percpu_counter_init(struct percpu_counter *fbc, s64 amount,
 
 #ifdef CONFIG_HOTPLUG_CPU
        INIT_LIST_HEAD(&fbc->list);
-       mutex_lock(&percpu_counters_lock);
+       spin_lock(&percpu_counters_lock);
        list_add(&fbc->list, &percpu_counters);
-       mutex_unlock(&percpu_counters_lock);
+       spin_unlock(&percpu_counters_lock);
 #endif
        return 0;
 }
@@ -139,9 +139,9 @@ void percpu_counter_destroy(struct percpu_counter *fbc)
        debug_percpu_counter_deactivate(fbc);
 
 #ifdef CONFIG_HOTPLUG_CPU
-       mutex_lock(&percpu_counters_lock);
+       spin_lock(&percpu_counters_lock);
        list_del(&fbc->list);
-       mutex_unlock(&percpu_counters_lock);
+       spin_unlock(&percpu_counters_lock);
 #endif
        free_percpu(fbc->counters);
        fbc->counters = NULL;
@@ -170,7 +170,7 @@ static int __cpuinit percpu_counter_hotcpu_callback(struct notifier_block *nb,
                return NOTIFY_OK;
 
        cpu = (unsigned long)hcpu;
-       mutex_lock(&percpu_counters_lock);
+       spin_lock(&percpu_counters_lock);
        list_for_each_entry(fbc, &percpu_counters, list) {
                s32 *pcount;
                unsigned long flags;
@@ -181,7 +181,7 @@ static int __cpuinit percpu_counter_hotcpu_callback(struct notifier_block *nb,
                *pcount = 0;
                raw_spin_unlock_irqrestore(&fbc->lock, flags);
        }
-       mutex_unlock(&percpu_counters_lock);
+       spin_unlock(&percpu_counters_lock);
 #endif
        return NOTIFY_OK;
 }
diff --git a/lib/pm-notifier-error-inject.c b/lib/pm-notifier-error-inject.c
new file mode 100644 (file)
index 0000000..c094b2d
--- /dev/null
@@ -0,0 +1,49 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/suspend.h>
+
+#include "notifier-error-inject.h"
+
+static int priority;
+module_param(priority, int, 0);
+MODULE_PARM_DESC(priority, "specify PM notifier priority");
+
+static struct notifier_err_inject pm_notifier_err_inject = {
+       .actions = {
+               { NOTIFIER_ERR_INJECT_ACTION(PM_HIBERNATION_PREPARE) },
+               { NOTIFIER_ERR_INJECT_ACTION(PM_SUSPEND_PREPARE) },
+               { NOTIFIER_ERR_INJECT_ACTION(PM_RESTORE_PREPARE) },
+               {}
+       }
+};
+
+static struct dentry *dir;
+
+static int err_inject_init(void)
+{
+       int err;
+
+       dir = notifier_err_inject_init("pm", notifier_err_inject_dir,
+                                       &pm_notifier_err_inject, priority);
+       if (IS_ERR(dir))
+               return PTR_ERR(dir);
+
+       err = register_pm_notifier(&pm_notifier_err_inject.nb);
+       if (err)
+               debugfs_remove_recursive(dir);
+
+       return err;
+}
+
+static void err_inject_exit(void)
+{
+       unregister_pm_notifier(&pm_notifier_err_inject.nb);
+       debugfs_remove_recursive(dir);
+}
+
+module_init(err_inject_init);
+module_exit(err_inject_exit);
+
+MODULE_DESCRIPTION("PM notifier error injection module");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
index 6096e89bee552ea79ed1ae5410f3b3ee9327cc0f..fadae774a20cc6abb0226ee4c3bfbc6cb4cd4eeb 100644 (file)
@@ -279,14 +279,6 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents,
                if (!left)
                        sg_mark_end(&sg[sg_size - 1]);
 
-               /*
-                * only really needed for mempool backed sg allocations (like
-                * SCSI), a possible improvement here would be to pass the
-                * table pointer into the allocator and let that clear these
-                * flags
-                */
-               gfp_mask &= ~__GFP_WAIT;
-               gfp_mask |= __GFP_HIGH;
                prv = sg;
        } while (left);
 
@@ -318,6 +310,70 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
 }
 EXPORT_SYMBOL(sg_alloc_table);
 
+/**
+ * sg_alloc_table_from_pages - Allocate and initialize an sg table from
+ *                            an array of pages
+ * @sgt:       The sg table header to use
+ * @pages:     Pointer to an array of page pointers
+ * @n_pages:   Number of pages in the pages array
+ * @offset:     Offset from start of the first page to the start of a buffer
+ * @size:       Number of valid bytes in the buffer (after offset)
+ * @gfp_mask:  GFP allocation mask
+ *
+ *  Description:
+ *    Allocate and initialize an sg table from a list of pages. Contiguous
+ *    ranges of the pages are squashed into a single scatterlist node. A user
+ *    may provide an offset at a start and a size of valid data in a buffer
+ *    specified by the page array. The returned sg table is released by
+ *    sg_free_table.
+ *
+ * Returns:
+ *   0 on success, negative error on failure
+ */
+int sg_alloc_table_from_pages(struct sg_table *sgt,
+       struct page **pages, unsigned int n_pages,
+       unsigned long offset, unsigned long size,
+       gfp_t gfp_mask)
+{
+       unsigned int chunks;
+       unsigned int i;
+       unsigned int cur_page;
+       int ret;
+       struct scatterlist *s;
+
+       /* compute number of contiguous chunks */
+       chunks = 1;
+       for (i = 1; i < n_pages; ++i)
+               if (page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1)
+                       ++chunks;
+
+       ret = sg_alloc_table(sgt, chunks, gfp_mask);
+       if (unlikely(ret))
+               return ret;
+
+       /* merging chunks and putting them into the scatterlist */
+       cur_page = 0;
+       for_each_sg(sgt->sgl, s, sgt->orig_nents, i) {
+               unsigned long chunk_size;
+               unsigned int j;
+
+               /* look for the end of the current chunk */
+               for (j = cur_page + 1; j < n_pages; ++j)
+                       if (page_to_pfn(pages[j]) !=
+                           page_to_pfn(pages[j - 1]) + 1)
+                               break;
+
+               chunk_size = ((j - cur_page) << PAGE_SHIFT) - offset;
+               sg_set_page(s, pages[cur_page], min(size, chunk_size), offset);
+               size -= chunk_size;
+               offset = 0;
+               cur_page = j;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(sg_alloc_table_from_pages);
+
 /**
  * sg_miter_start - start mapping iteration over a sg list
  * @miter: sg mapping iter to be started
index e91fbc23fff121915217a8e5c53c29a2b52aeba4..eb10578ae055947d2cd39c9e4c8e9f7c1c76477e 100644 (file)
@@ -58,7 +58,7 @@ static void spin_dump(raw_spinlock_t *lock, const char *msg)
        printk(KERN_EMERG "BUG: spinlock %s on CPU#%d, %s/%d\n",
                msg, raw_smp_processor_id(),
                current->comm, task_pid_nr(current));
-       printk(KERN_EMERG " lock: %ps, .magic: %08x, .owner: %s/%d, "
+       printk(KERN_EMERG " lock: %pS, .magic: %08x, .owner: %s/%d, "
                        ".owner_cpu: %d\n",
                lock, lock->magic,
                owner ? owner->comm : "<none>",
index c3f36d415bdf43034415c801b8e3f922f5e5b928..0e337541f005d8f29a36434dae555b7477d12164 100644 (file)
@@ -654,6 +654,50 @@ char *resource_string(char *buf, char *end, struct resource *res,
        return string(buf, end, sym, spec);
 }
 
+static noinline_for_stack
+char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
+                const char *fmt)
+{
+       int i, len = 1;         /* if we pass '%ph[CDN]', field witdh remains
+                                  negative value, fallback to the default */
+       char separator;
+
+       if (spec.field_width == 0)
+               /* nothing to print */
+               return buf;
+
+       if (ZERO_OR_NULL_PTR(addr))
+               /* NULL pointer */
+               return string(buf, end, NULL, spec);
+
+       switch (fmt[1]) {
+       case 'C':
+               separator = ':';
+               break;
+       case 'D':
+               separator = '-';
+               break;
+       case 'N':
+               separator = 0;
+               break;
+       default:
+               separator = ' ';
+               break;
+       }
+
+       if (spec.field_width > 0)
+               len = min_t(int, spec.field_width, 64);
+
+       for (i = 0; i < len && buf < end - 1; i++) {
+               buf = hex_byte_pack(buf, addr[i]);
+
+               if (buf < end && separator && i != len - 1)
+                       *buf++ = separator;
+       }
+
+       return buf;
+}
+
 static noinline_for_stack
 char *mac_address_string(char *buf, char *end, u8 *addr,
                         struct printf_spec spec, const char *fmt)
@@ -662,15 +706,28 @@ char *mac_address_string(char *buf, char *end, u8 *addr,
        char *p = mac_addr;
        int i;
        char separator;
+       bool reversed = false;
 
-       if (fmt[1] == 'F') {            /* FDDI canonical format */
+       switch (fmt[1]) {
+       case 'F':
                separator = '-';
-       } else {
+               break;
+
+       case 'R':
+               reversed = true;
+               /* fall through */
+
+       default:
                separator = ':';
+               break;
        }
 
        for (i = 0; i < 6; i++) {
-               p = hex_byte_pack(p, addr[i]);
+               if (reversed)
+                       p = hex_byte_pack(p, addr[5 - i]);
+               else
+                       p = hex_byte_pack(p, addr[i]);
+
                if (fmt[0] == 'M' && i != 5)
                        *p++ = separator;
        }
@@ -933,6 +990,7 @@ int kptr_restrict __read_mostly;
  * - 'm' For a 6-byte MAC address, it prints the hex address without colons
  * - 'MF' For a 6-byte MAC FDDI address, it prints the address
  *       with a dash-separated hex notation
+ * - '[mM]R For a 6-byte MAC address, Reverse order (Bluetooth)
  * - 'I' [46] for IPv4/IPv6 addresses printed in the usual way
  *       IPv4 uses dot-separated decimal without leading 0's (1.2.3.4)
  *       IPv6 uses colon separated network-order 16 bit hex with leading 0's
@@ -960,6 +1018,13 @@ int kptr_restrict __read_mostly;
  *       correctness of the format string and va_list arguments.
  * - 'K' For a kernel pointer that should be hidden from unprivileged users
  * - 'NF' For a netdev_features_t
+ * - 'h[CDN]' For a variable-length buffer, it prints it as a hex string with
+ *            a certain separator (' ' by default):
+ *              C colon
+ *              D dash
+ *              N no separator
+ *            The maximum supported length is 64 bytes of the input. Consider
+ *            to use print_hex_dump() for the larger input.
  *
  * Note: The difference between 'S' and 'F' is that on ia64 and ppc64
  * function pointers are really function descriptors, which contain a
@@ -993,9 +1058,12 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
        case 'R':
        case 'r':
                return resource_string(buf, end, ptr, spec, fmt);
+       case 'h':
+               return hex_string(buf, end, ptr, spec, fmt);
        case 'M':                       /* Colon separated: 00:01:02:03:04:05 */
        case 'm':                       /* Contiguous: 000102030405 */
-                                       /* [mM]F (FDDI, bit reversed) */
+                                       /* [mM]F (FDDI) */
+                                       /* [mM]R (Reverse order; Bluetooth) */
                return mac_address_string(buf, end, ptr, spec, fmt);
        case 'I':                       /* Formatted IP supported
                                         * 4:   1.2.3.4
@@ -1030,7 +1098,8 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
                 * %pK cannot be used in IRQ context because its test
                 * for CAP_SYSLOG would be meaningless.
                 */
-               if (in_irq() || in_serving_softirq() || in_nmi()) {
+               if (kptr_restrict && (in_irq() || in_serving_softirq() ||
+                                     in_nmi())) {
                        if (spec.field_width == -1)
                                spec.field_width = default_width;
                        return string(buf, end, "pK-error", spec);
@@ -1280,8 +1349,12 @@ qualifier:
  * %pI6c print an IPv6 address as specified by RFC 5952
  * %pU[bBlL] print a UUID/GUID in big or little endian using lower or upper
  *   case.
+ * %*ph[CDN] a variable-length hex string with a separator (supports up to 64
+ *           bytes of the input)
  * %n is ignored
  *
+ * ** Please update Documentation/printk-formats.txt when making changes **
+ *
  * The return value is the number of characters which would
  * be generated for the given input, excluding the trailing
  * '\0', as per ISO C99. If you want to have the exact
index 82fed4eb2b6fe39cfd0476afe46e9589e02b386f..d5c8019c662793886224b872cc494e1ae3d00842 100644 (file)
@@ -140,9 +140,13 @@ config ARCH_DISCARD_MEMBLOCK
 config NO_BOOTMEM
        boolean
 
+config MEMORY_ISOLATION
+       boolean
+
 # eventually, we can have this option just 'select SPARSEMEM'
 config MEMORY_HOTPLUG
        bool "Allow for memory hot-add"
+       select MEMORY_ISOLATION
        depends on SPARSEMEM || X86_64_ACPI_NUMA
        depends on HOTPLUG && ARCH_ENABLE_MEMORY_HOTPLUG
        depends on (IA64 || X86 || PPC_BOOK3S_64 || SUPERH || S390)
@@ -272,6 +276,7 @@ config MEMORY_FAILURE
        depends on MMU
        depends on ARCH_SUPPORTS_MEMORY_FAILURE
        bool "Enable recovery from hardware memory errors"
+       select MEMORY_ISOLATION
        help
          Enables code to recover from some memory failures on systems
          with MCA recovery. This allows a system to continue running
index 2e2fbbefb99fa94c97be13aa8fa71da823455409..92753e2d82dac41fe9a8c2f4633189ad723bbd07 100644 (file)
@@ -15,8 +15,9 @@ obj-y                 := filemap.o mempool.o oom_kill.o fadvise.o \
                           maccess.o page_alloc.o page-writeback.o \
                           readahead.o swap.o truncate.o vmscan.o shmem.o \
                           prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \
-                          page_isolation.o mm_init.o mmu_context.o percpu.o \
+                          mm_init.o mmu_context.o percpu.o slab_common.o \
                           compaction.o $(mmu-y)
+
 obj-y += init-mm.o
 
 ifdef CONFIG_NO_BOOTMEM
@@ -48,9 +49,11 @@ obj-$(CONFIG_FS_XIP) += filemap_xip.o
 obj-$(CONFIG_MIGRATION) += migrate.o
 obj-$(CONFIG_QUICKLIST) += quicklist.o
 obj-$(CONFIG_TRANSPARENT_HUGEPAGE) += huge_memory.o
-obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o
+obj-$(CONFIG_MEMCG) += memcontrol.o page_cgroup.o
+obj-$(CONFIG_CGROUP_HUGETLB) += hugetlb_cgroup.o
 obj-$(CONFIG_MEMORY_FAILURE) += memory-failure.o
 obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o
 obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
 obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
 obj-$(CONFIG_CLEANCACHE) += cleancache.o
+obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
index dd8e2aafb07e1aecae5e42ac403d9915caa7e698..6b4718e2ee341d7fd7f068d83e31da014d1a5ef0 100644 (file)
@@ -677,7 +677,7 @@ int bdi_init(struct backing_dev_info *bdi)
 
        bdi->min_ratio = 0;
        bdi->max_ratio = 100;
-       bdi->max_prop_frac = PROP_FRAC_BASE;
+       bdi->max_prop_frac = FPROP_FRAC_BASE;
        spin_lock_init(&bdi->wb_lock);
        INIT_LIST_HEAD(&bdi->bdi_list);
        INIT_LIST_HEAD(&bdi->work_list);
@@ -700,7 +700,7 @@ int bdi_init(struct backing_dev_info *bdi)
        bdi->write_bandwidth = INIT_BW;
        bdi->avg_write_bandwidth = INIT_BW;
 
-       err = prop_local_init_percpu(&bdi->completions);
+       err = fprop_local_init_percpu(&bdi->completions);
 
        if (err) {
 err:
@@ -744,7 +744,7 @@ void bdi_destroy(struct backing_dev_info *bdi)
        for (i = 0; i < NR_BDI_STAT_ITEMS; i++)
                percpu_counter_destroy(&bdi->bdi_stat[i]);
 
-       prop_local_destroy_percpu(&bdi->completions);
+       fprop_local_destroy_percpu(&bdi->completions);
 }
 EXPORT_SYMBOL(bdi_destroy);
 
@@ -886,3 +886,23 @@ out:
        return ret;
 }
 EXPORT_SYMBOL(wait_iff_congested);
+
+int pdflush_proc_obsolete(struct ctl_table *table, int write,
+                       void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       char kbuf[] = "0\n";
+
+       if (*ppos) {
+               *lenp = 0;
+               return 0;
+       }
+
+       if (copy_to_user(buffer, kbuf, sizeof(kbuf)))
+               return -EFAULT;
+       printk_once(KERN_WARNING "%s exported in /proc is scheduled for removal\n",
+                       table->procname);
+
+       *lenp = 2;
+       *ppos += *lenp;
+       return 2;
+}
index 2f42d952853970b5dd1f95a149d8750e7d570357..e78cb968842163ff65154346e6721aac91070319 100644 (file)
@@ -422,6 +422,17 @@ static void isolate_freepages(struct zone *zone,
                                        pfn -= pageblock_nr_pages) {
                unsigned long isolated;
 
+               /*
+                * Skip ahead if another thread is compacting in the area
+                * simultaneously. If we wrapped around, we can only skip
+                * ahead if zone->compact_cached_free_pfn also wrapped to
+                * above our starting point.
+                */
+               if (cc->order > 0 && (!cc->wrapped ||
+                                     zone->compact_cached_free_pfn >
+                                     cc->start_free_pfn))
+                       pfn = min(pfn, zone->compact_cached_free_pfn);
+
                if (!pfn_valid(pfn))
                        continue;
 
@@ -461,8 +472,11 @@ static void isolate_freepages(struct zone *zone,
                 * looking for free pages, the search will restart here as
                 * page migration may have returned some pages to the allocator
                 */
-               if (isolated)
+               if (isolated) {
                        high_pfn = max(high_pfn, pfn);
+                       if (cc->order > 0)
+                               zone->compact_cached_free_pfn = high_pfn;
+               }
        }
 
        /* split_free_page does not map the pages */
@@ -556,6 +570,20 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone,
        return ISOLATE_SUCCESS;
 }
 
+/*
+ * Returns the start pfn of the last page block in a zone.  This is the starting
+ * point for full compaction of a zone.  Compaction searches for free pages from
+ * the end of each zone, while isolate_freepages_block scans forward inside each
+ * page block.
+ */
+static unsigned long start_free_pfn(struct zone *zone)
+{
+       unsigned long free_pfn;
+       free_pfn = zone->zone_start_pfn + zone->spanned_pages;
+       free_pfn &= ~(pageblock_nr_pages-1);
+       return free_pfn;
+}
+
 static int compact_finished(struct zone *zone,
                            struct compact_control *cc)
 {
@@ -565,8 +593,26 @@ static int compact_finished(struct zone *zone,
        if (fatal_signal_pending(current))
                return COMPACT_PARTIAL;
 
-       /* Compaction run completes if the migrate and free scanner meet */
-       if (cc->free_pfn <= cc->migrate_pfn)
+       /*
+        * A full (order == -1) compaction run starts at the beginning and
+        * end of a zone; it completes when the migrate and free scanner meet.
+        * A partial (order > 0) compaction can start with the free scanner
+        * at a random point in the zone, and may have to restart.
+        */
+       if (cc->free_pfn <= cc->migrate_pfn) {
+               if (cc->order > 0 && !cc->wrapped) {
+                       /* We started partway through; restart at the end. */
+                       unsigned long free_pfn = start_free_pfn(zone);
+                       zone->compact_cached_free_pfn = free_pfn;
+                       cc->free_pfn = free_pfn;
+                       cc->wrapped = 1;
+                       return COMPACT_CONTINUE;
+               }
+               return COMPACT_COMPLETE;
+       }
+
+       /* We wrapped around and ended up where we started. */
+       if (cc->wrapped && cc->free_pfn <= cc->start_free_pfn)
                return COMPACT_COMPLETE;
 
        /*
@@ -664,8 +710,15 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
 
        /* Setup to move all movable pages to the end of the zone */
        cc->migrate_pfn = zone->zone_start_pfn;
-       cc->free_pfn = cc->migrate_pfn + zone->spanned_pages;
-       cc->free_pfn &= ~(pageblock_nr_pages-1);
+
+       if (cc->order > 0) {
+               /* Incremental compaction. Start where the last one stopped. */
+               cc->free_pfn = zone->compact_cached_free_pfn;
+               cc->start_free_pfn = cc->free_pfn;
+       } else {
+               /* Order == -1 starts at the end of the zone. */
+               cc->free_pfn = start_free_pfn(zone);
+       }
 
        migrate_prep_local();
 
index 469491e0af79fed994d48fd4ada817f8e50c6d6a..9b75a045dbf4bf63e250372daad3f878909ecfdd 100644 (file)
@@ -93,11 +93,6 @@ SYSCALL_DEFINE(fadvise64_64)(int fd, loff_t offset, loff_t len, int advice)
                spin_unlock(&file->f_lock);
                break;
        case POSIX_FADV_WILLNEED:
-               if (!mapping->a_ops->readpage) {
-                       ret = -EINVAL;
-                       break;
-               }
-
                /* First and last PARTIAL page! */
                start_index = offset >> PAGE_CACHE_SHIFT;
                end_index = endbyte >> PAGE_CACHE_SHIFT;
@@ -106,12 +101,13 @@ SYSCALL_DEFINE(fadvise64_64)(int fd, loff_t offset, loff_t len, int advice)
                nrpages = end_index - start_index + 1;
                if (!nrpages)
                        nrpages = ~0UL;
-               
-               ret = force_page_cache_readahead(mapping, file,
-                               start_index,
-                               nrpages);
-               if (ret > 0)
-                       ret = 0;
+
+               /*
+                * Ignore return value because fadvise() shall return
+                * success even if filesystem can't retrieve a hint,
+                */
+               force_page_cache_readahead(mapping, file, start_index,
+                                          nrpages);
                break;
        case POSIX_FADV_NOREUSE:
                break;
index a4a5260b0279b77b37738540b1e8c24fb446a3e5..fa5ca304148e7b4756bf2e52cd6afde1dba2b5c3 100644 (file)
@@ -1712,8 +1712,35 @@ page_not_uptodate:
 }
 EXPORT_SYMBOL(filemap_fault);
 
+int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+       struct page *page = vmf->page;
+       struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
+       int ret = VM_FAULT_LOCKED;
+
+       sb_start_pagefault(inode->i_sb);
+       file_update_time(vma->vm_file);
+       lock_page(page);
+       if (page->mapping != inode->i_mapping) {
+               unlock_page(page);
+               ret = VM_FAULT_NOPAGE;
+               goto out;
+       }
+       /*
+        * We mark the page dirty already here so that when freeze is in
+        * progress, we are guaranteed that writeback during freezing will
+        * see the dirty page and writeprotect it again.
+        */
+       set_page_dirty(page);
+out:
+       sb_end_pagefault(inode->i_sb);
+       return ret;
+}
+EXPORT_SYMBOL(filemap_page_mkwrite);
+
 const struct vm_operations_struct generic_file_vm_ops = {
        .fault          = filemap_fault,
+       .page_mkwrite   = filemap_page_mkwrite,
 };
 
 /* This is used for a general mmap of a disk file */
@@ -2407,8 +2434,6 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
        count = ocount;
        pos = *ppos;
 
-       vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
-
        /* We can write back this queue in page reclaim */
        current->backing_dev_info = mapping->backing_dev_info;
        written = 0;
@@ -2507,6 +2532,7 @@ ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
 
        BUG_ON(iocb->ki_pos != pos);
 
+       sb_start_write(inode->i_sb);
        mutex_lock(&inode->i_mutex);
        blk_start_plug(&plug);
        ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
@@ -2520,6 +2546,7 @@ ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
                        ret = err;
        }
        blk_finish_plug(&plug);
+       sb_end_write(inode->i_sb);
        return ret;
 }
 EXPORT_SYMBOL(generic_file_aio_write);
index 213ca1f5340980e1ce6fad8d4f12e50858d61397..13e013b1270c6c240ba19eadb6e091e8c6afb13d 100644 (file)
@@ -304,6 +304,7 @@ out:
 
 static const struct vm_operations_struct xip_file_vm_ops = {
        .fault  = xip_file_fault,
+       .page_mkwrite   = filemap_page_mkwrite,
 };
 
 int xip_file_mmap(struct file * file, struct vm_area_struct * vma)
@@ -401,6 +402,8 @@ xip_file_write(struct file *filp, const char __user *buf, size_t len,
        loff_t pos;
        ssize_t ret;
 
+       sb_start_write(inode->i_sb);
+
        mutex_lock(&inode->i_mutex);
 
        if (!access_ok(VERIFY_READ, buf, len)) {
@@ -411,8 +414,6 @@ xip_file_write(struct file *filp, const char __user *buf, size_t len,
        pos = *ppos;
        count = len;
 
-       vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
-
        /* We can write back this queue in page reclaim */
        current->backing_dev_info = mapping->backing_dev_info;
 
@@ -436,6 +437,7 @@ xip_file_write(struct file *filp, const char __user *buf, size_t len,
        current->backing_dev_info = NULL;
  out_up:
        mutex_unlock(&inode->i_mutex);
+       sb_end_write(inode->i_sb);
        return ret;
 }
 EXPORT_SYMBOL_GPL(xip_file_write);
index 57d82c6250c308e36089f81a8e902b729391335e..d517cd16a6eb91e8df18f3cbd79cd67621b27d3f 100644 (file)
@@ -94,6 +94,18 @@ static DECLARE_WAIT_QUEUE_HEAD(pkmap_map_wait);
                do { spin_unlock(&kmap_lock); (void)(flags); } while (0)
 #endif
 
+struct page *kmap_to_page(void *vaddr)
+{
+       unsigned long addr = (unsigned long)vaddr;
+
+       if (addr >= PKMAP_ADDR(0) && addr <= PKMAP_ADDR(LAST_PKMAP)) {
+               int i = (addr - PKMAP_ADDR(0)) >> PAGE_SHIFT;
+               return pte_page(pkmap_page_table[i]);
+       }
+
+       return virt_to_page(addr);
+}
+
 static void flush_all_zero_pkmaps(void)
 {
        int i;
index e198831276a3eab77b4a89fc0e1457a5a45d025d..bc727122dd44de6c4ae9307c618e58ddf4da3c87 100644 (file)
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
-#include <linux/io.h>
+#include <asm/tlb.h>
 
+#include <linux/io.h>
 #include <linux/hugetlb.h>
+#include <linux/hugetlb_cgroup.h>
 #include <linux/node.h>
+#include <linux/hugetlb_cgroup.h>
 #include "internal.h"
 
 const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL;
 static gfp_t htlb_alloc_mask = GFP_HIGHUSER;
 unsigned long hugepages_treat_as_movable;
 
-static int max_hstate;
+int hugetlb_max_hstate __read_mostly;
 unsigned int default_hstate_idx;
 struct hstate hstates[HUGE_MAX_HSTATE];
 
@@ -45,13 +48,10 @@ static struct hstate * __initdata parsed_hstate;
 static unsigned long __initdata default_hstate_max_huge_pages;
 static unsigned long __initdata default_hstate_size;
 
-#define for_each_hstate(h) \
-       for ((h) = hstates; (h) < &hstates[max_hstate]; (h)++)
-
 /*
  * Protects updates to hugepage_freelists, nr_huge_pages, and free_huge_pages
  */
-static DEFINE_SPINLOCK(hugetlb_lock);
+DEFINE_SPINLOCK(hugetlb_lock);
 
 static inline void unlock_or_release_subpool(struct hugepage_subpool *spool)
 {
@@ -509,7 +509,7 @@ void copy_huge_page(struct page *dst, struct page *src)
 static void enqueue_huge_page(struct hstate *h, struct page *page)
 {
        int nid = page_to_nid(page);
-       list_add(&page->lru, &h->hugepage_freelists[nid]);
+       list_move(&page->lru, &h->hugepage_freelists[nid]);
        h->free_huge_pages++;
        h->free_huge_pages_node[nid]++;
 }
@@ -521,7 +521,7 @@ static struct page *dequeue_huge_page_node(struct hstate *h, int nid)
        if (list_empty(&h->hugepage_freelists[nid]))
                return NULL;
        page = list_entry(h->hugepage_freelists[nid].next, struct page, lru);
-       list_del(&page->lru);
+       list_move(&page->lru, &h->hugepage_activelist);
        set_page_refcounted(page);
        h->free_huge_pages--;
        h->free_huge_pages_node[nid]--;
@@ -593,6 +593,7 @@ static void update_and_free_page(struct hstate *h, struct page *page)
                                1 << PG_active | 1 << PG_reserved |
                                1 << PG_private | 1 << PG_writeback);
        }
+       VM_BUG_ON(hugetlb_cgroup_from_page(page));
        set_compound_page_dtor(page, NULL);
        set_page_refcounted(page);
        arch_release_hugepage(page);
@@ -625,10 +626,13 @@ static void free_huge_page(struct page *page)
        page->mapping = NULL;
        BUG_ON(page_count(page));
        BUG_ON(page_mapcount(page));
-       INIT_LIST_HEAD(&page->lru);
 
        spin_lock(&hugetlb_lock);
+       hugetlb_cgroup_uncharge_page(hstate_index(h),
+                                    pages_per_huge_page(h), page);
        if (h->surplus_huge_pages_node[nid] && huge_page_order(h) < MAX_ORDER) {
+               /* remove the page from active list */
+               list_del(&page->lru);
                update_and_free_page(h, page);
                h->surplus_huge_pages--;
                h->surplus_huge_pages_node[nid]--;
@@ -641,8 +645,10 @@ static void free_huge_page(struct page *page)
 
 static void prep_new_huge_page(struct hstate *h, struct page *page, int nid)
 {
+       INIT_LIST_HEAD(&page->lru);
        set_compound_page_dtor(page, free_huge_page);
        spin_lock(&hugetlb_lock);
+       set_hugetlb_cgroup(page, NULL);
        h->nr_huge_pages++;
        h->nr_huge_pages_node[nid]++;
        spin_unlock(&hugetlb_lock);
@@ -889,8 +895,10 @@ static struct page *alloc_buddy_huge_page(struct hstate *h, int nid)
 
        spin_lock(&hugetlb_lock);
        if (page) {
+               INIT_LIST_HEAD(&page->lru);
                r_nid = page_to_nid(page);
                set_compound_page_dtor(page, free_huge_page);
+               set_hugetlb_cgroup(page, NULL);
                /*
                 * We incremented the global counters already
                 */
@@ -993,7 +1001,6 @@ retry:
        list_for_each_entry_safe(page, tmp, &surplus_list, lru) {
                if ((--needed) < 0)
                        break;
-               list_del(&page->lru);
                /*
                 * This page is now managed by the hugetlb allocator and has
                 * no users -- drop the buddy allocator's reference.
@@ -1008,7 +1015,6 @@ free:
        /* Free unnecessary surplus pages to the buddy allocator */
        if (!list_empty(&surplus_list)) {
                list_for_each_entry_safe(page, tmp, &surplus_list, lru) {
-                       list_del(&page->lru);
                        put_page(page);
                }
        }
@@ -1112,7 +1118,10 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma,
        struct hstate *h = hstate_vma(vma);
        struct page *page;
        long chg;
+       int ret, idx;
+       struct hugetlb_cgroup *h_cg;
 
+       idx = hstate_index(h);
        /*
         * Processes that did not create the mapping will have no
         * reserves and will not have accounted against subpool
@@ -1123,27 +1132,43 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma,
         */
        chg = vma_needs_reservation(h, vma, addr);
        if (chg < 0)
-               return ERR_PTR(-VM_FAULT_OOM);
+               return ERR_PTR(-ENOMEM);
        if (chg)
                if (hugepage_subpool_get_pages(spool, chg))
-                       return ERR_PTR(-VM_FAULT_SIGBUS);
+                       return ERR_PTR(-ENOSPC);
 
+       ret = hugetlb_cgroup_charge_cgroup(idx, pages_per_huge_page(h), &h_cg);
+       if (ret) {
+               hugepage_subpool_put_pages(spool, chg);
+               return ERR_PTR(-ENOSPC);
+       }
        spin_lock(&hugetlb_lock);
        page = dequeue_huge_page_vma(h, vma, addr, avoid_reserve);
-       spin_unlock(&hugetlb_lock);
-
-       if (!page) {
+       if (page) {
+               /* update page cgroup details */
+               hugetlb_cgroup_commit_charge(idx, pages_per_huge_page(h),
+                                            h_cg, page);
+               spin_unlock(&hugetlb_lock);
+       } else {
+               spin_unlock(&hugetlb_lock);
                page = alloc_buddy_huge_page(h, NUMA_NO_NODE);
                if (!page) {
+                       hugetlb_cgroup_uncharge_cgroup(idx,
+                                                      pages_per_huge_page(h),
+                                                      h_cg);
                        hugepage_subpool_put_pages(spool, chg);
-                       return ERR_PTR(-VM_FAULT_SIGBUS);
+                       return ERR_PTR(-ENOSPC);
                }
+               spin_lock(&hugetlb_lock);
+               hugetlb_cgroup_commit_charge(idx, pages_per_huge_page(h),
+                                            h_cg, page);
+               list_move(&page->lru, &h->hugepage_activelist);
+               spin_unlock(&hugetlb_lock);
        }
 
        set_page_private(page, (unsigned long)spool);
 
        vma_commit_reservation(h, vma, addr);
-
        return page;
 }
 
@@ -1646,7 +1671,7 @@ static int hugetlb_sysfs_add_hstate(struct hstate *h, struct kobject *parent,
                                    struct attribute_group *hstate_attr_group)
 {
        int retval;
-       int hi = h - hstates;
+       int hi = hstate_index(h);
 
        hstate_kobjs[hi] = kobject_create_and_add(h->name, parent);
        if (!hstate_kobjs[hi])
@@ -1741,11 +1766,13 @@ void hugetlb_unregister_node(struct node *node)
        if (!nhs->hugepages_kobj)
                return;         /* no hstate attributes */
 
-       for_each_hstate(h)
-               if (nhs->hstate_kobjs[h - hstates]) {
-                       kobject_put(nhs->hstate_kobjs[h - hstates]);
-                       nhs->hstate_kobjs[h - hstates] = NULL;
+       for_each_hstate(h) {
+               int idx = hstate_index(h);
+               if (nhs->hstate_kobjs[idx]) {
+                       kobject_put(nhs->hstate_kobjs[idx]);
+                       nhs->hstate_kobjs[idx] = NULL;
                }
+       }
 
        kobject_put(nhs->hugepages_kobj);
        nhs->hugepages_kobj = NULL;
@@ -1848,7 +1875,7 @@ static void __exit hugetlb_exit(void)
        hugetlb_unregister_all_nodes();
 
        for_each_hstate(h) {
-               kobject_put(hstate_kobjs[h - hstates]);
+               kobject_put(hstate_kobjs[hstate_index(h)]);
        }
 
        kobject_put(hugepages_kobj);
@@ -1869,7 +1896,7 @@ static int __init hugetlb_init(void)
                if (!size_to_hstate(default_hstate_size))
                        hugetlb_add_hstate(HUGETLB_PAGE_ORDER);
        }
-       default_hstate_idx = size_to_hstate(default_hstate_size) - hstates;
+       default_hstate_idx = hstate_index(size_to_hstate(default_hstate_size));
        if (default_hstate_max_huge_pages)
                default_hstate.max_huge_pages = default_hstate_max_huge_pages;
 
@@ -1897,19 +1924,27 @@ void __init hugetlb_add_hstate(unsigned order)
                printk(KERN_WARNING "hugepagesz= specified twice, ignoring\n");
                return;
        }
-       BUG_ON(max_hstate >= HUGE_MAX_HSTATE);
+       BUG_ON(hugetlb_max_hstate >= HUGE_MAX_HSTATE);
        BUG_ON(order == 0);
-       h = &hstates[max_hstate++];
+       h = &hstates[hugetlb_max_hstate++];
        h->order = order;
        h->mask = ~((1ULL << (order + PAGE_SHIFT)) - 1);
        h->nr_huge_pages = 0;
        h->free_huge_pages = 0;
        for (i = 0; i < MAX_NUMNODES; ++i)
                INIT_LIST_HEAD(&h->hugepage_freelists[i]);
+       INIT_LIST_HEAD(&h->hugepage_activelist);
        h->next_nid_to_alloc = first_node(node_states[N_HIGH_MEMORY]);
        h->next_nid_to_free = first_node(node_states[N_HIGH_MEMORY]);
        snprintf(h->name, HSTATE_NAME_LEN, "hugepages-%lukB",
                                        huge_page_size(h)/1024);
+       /*
+        * Add cgroup control files only if the huge page consists
+        * of more than two normal pages. This is because we use
+        * page[2].lru.next for storing cgoup details.
+        */
+       if (order >= HUGETLB_CGROUP_MIN_ORDER)
+               hugetlb_cgroup_file_init(hugetlb_max_hstate - 1);
 
        parsed_hstate = h;
 }
@@ -1920,10 +1955,10 @@ static int __init hugetlb_nrpages_setup(char *s)
        static unsigned long *last_mhp;
 
        /*
-        * !max_hstate means we haven't parsed a hugepagesz= parameter yet,
+        * !hugetlb_max_hstate means we haven't parsed a hugepagesz= parameter yet,
         * so this hugepages= parameter goes to the "default hstate".
         */
-       if (!max_hstate)
+       if (!hugetlb_max_hstate)
                mhp = &default_hstate_max_huge_pages;
        else
                mhp = &parsed_hstate->max_huge_pages;
@@ -1942,7 +1977,7 @@ static int __init hugetlb_nrpages_setup(char *s)
         * But we need to allocate >= MAX_ORDER hstates here early to still
         * use the bootmem allocator.
         */
-       if (max_hstate && parsed_hstate->order >= MAX_ORDER)
+       if (hugetlb_max_hstate && parsed_hstate->order >= MAX_ORDER)
                hugetlb_hstate_alloc_pages(parsed_hstate);
 
        last_mhp = mhp;
@@ -2308,30 +2343,26 @@ static int is_hugetlb_entry_hwpoisoned(pte_t pte)
                return 0;
 }
 
-void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
-                           unsigned long end, struct page *ref_page)
+void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+                           unsigned long start, unsigned long end,
+                           struct page *ref_page)
 {
+       int force_flush = 0;
        struct mm_struct *mm = vma->vm_mm;
        unsigned long address;
        pte_t *ptep;
        pte_t pte;
        struct page *page;
-       struct page *tmp;
        struct hstate *h = hstate_vma(vma);
        unsigned long sz = huge_page_size(h);
 
-       /*
-        * A page gathering list, protected by per file i_mmap_mutex. The
-        * lock is used to avoid list corruption from multiple unmapping
-        * of the same page since we are using page->lru.
-        */
-       LIST_HEAD(page_list);
-
        WARN_ON(!is_vm_hugetlb_page(vma));
        BUG_ON(start & ~huge_page_mask(h));
        BUG_ON(end & ~huge_page_mask(h));
 
+       tlb_start_vma(tlb, vma);
        mmu_notifier_invalidate_range_start(mm, start, end);
+again:
        spin_lock(&mm->page_table_lock);
        for (address = start; address < end; address += sz) {
                ptep = huge_pte_offset(mm, address);
@@ -2370,30 +2401,64 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
                }
 
                pte = huge_ptep_get_and_clear(mm, address, ptep);
+               tlb_remove_tlb_entry(tlb, ptep, address);
                if (pte_dirty(pte))
                        set_page_dirty(page);
-               list_add(&page->lru, &page_list);
 
+               page_remove_rmap(page);
+               force_flush = !__tlb_remove_page(tlb, page);
+               if (force_flush)
+                       break;
                /* Bail out after unmapping reference page if supplied */
                if (ref_page)
                        break;
        }
-       flush_tlb_range(vma, start, end);
        spin_unlock(&mm->page_table_lock);
-       mmu_notifier_invalidate_range_end(mm, start, end);
-       list_for_each_entry_safe(page, tmp, &page_list, lru) {
-               page_remove_rmap(page);
-               list_del(&page->lru);
-               put_page(page);
+       /*
+        * mmu_gather ran out of room to batch pages, we break out of
+        * the PTE lock to avoid doing the potential expensive TLB invalidate
+        * and page-free while holding it.
+        */
+       if (force_flush) {
+               force_flush = 0;
+               tlb_flush_mmu(tlb);
+               if (address < end && !ref_page)
+                       goto again;
        }
+       mmu_notifier_invalidate_range_end(mm, start, end);
+       tlb_end_vma(tlb, vma);
+}
+
+void __unmap_hugepage_range_final(struct mmu_gather *tlb,
+                         struct vm_area_struct *vma, unsigned long start,
+                         unsigned long end, struct page *ref_page)
+{
+       __unmap_hugepage_range(tlb, vma, start, end, ref_page);
+
+       /*
+        * Clear this flag so that x86's huge_pmd_share page_table_shareable
+        * test will fail on a vma being torn down, and not grab a page table
+        * on its way out.  We're lucky that the flag has such an appropriate
+        * name, and can in fact be safely cleared here. We could clear it
+        * before the __unmap_hugepage_range above, but all that's necessary
+        * is to clear it before releasing the i_mmap_mutex. This works
+        * because in the context this is called, the VMA is about to be
+        * destroyed and the i_mmap_mutex is held.
+        */
+       vma->vm_flags &= ~VM_MAYSHARE;
 }
 
 void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
                          unsigned long end, struct page *ref_page)
 {
-       mutex_lock(&vma->vm_file->f_mapping->i_mmap_mutex);
-       __unmap_hugepage_range(vma, start, end, ref_page);
-       mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex);
+       struct mm_struct *mm;
+       struct mmu_gather tlb;
+
+       mm = vma->vm_mm;
+
+       tlb_gather_mmu(&tlb, mm, 0);
+       __unmap_hugepage_range(&tlb, vma, start, end, ref_page);
+       tlb_finish_mmu(&tlb, start, end);
 }
 
 /*
@@ -2438,9 +2503,8 @@ static int unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma,
                 * from the time of fork. This would look like data corruption
                 */
                if (!is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER))
-                       __unmap_hugepage_range(iter_vma,
-                               address, address + huge_page_size(h),
-                               page);
+                       unmap_hugepage_range(iter_vma, address,
+                                            address + huge_page_size(h), page);
        }
        mutex_unlock(&mapping->i_mmap_mutex);
 
@@ -2496,6 +2560,7 @@ retry_avoidcopy:
        new_page = alloc_huge_page(vma, address, outside_reserve);
 
        if (IS_ERR(new_page)) {
+               long err = PTR_ERR(new_page);
                page_cache_release(old_page);
 
                /*
@@ -2524,7 +2589,10 @@ retry_avoidcopy:
 
                /* Caller expects lock to be held */
                spin_lock(&mm->page_table_lock);
-               return -PTR_ERR(new_page);
+               if (err == -ENOMEM)
+                       return VM_FAULT_OOM;
+               else
+                       return VM_FAULT_SIGBUS;
        }
 
        /*
@@ -2642,7 +2710,11 @@ retry:
                        goto out;
                page = alloc_huge_page(vma, address, 0);
                if (IS_ERR(page)) {
-                       ret = -PTR_ERR(page);
+                       ret = PTR_ERR(page);
+                       if (ret == -ENOMEM)
+                               ret = VM_FAULT_OOM;
+                       else
+                               ret = VM_FAULT_SIGBUS;
                        goto out;
                }
                clear_huge_page(page, address, pages_per_huge_page(h));
@@ -2679,7 +2751,7 @@ retry:
                 */
                if (unlikely(PageHWPoison(page))) {
                        ret = VM_FAULT_HWPOISON |
-                             VM_FAULT_SET_HINDEX(h - hstates);
+                               VM_FAULT_SET_HINDEX(hstate_index(h));
                        goto backout_unlocked;
                }
        }
@@ -2752,7 +2824,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
                        return 0;
                } else if (unlikely(is_hugetlb_entry_hwpoisoned(entry)))
                        return VM_FAULT_HWPOISON_LARGE |
-                              VM_FAULT_SET_HINDEX(h - hstates);
+                               VM_FAULT_SET_HINDEX(hstate_index(h));
        }
 
        ptep = huge_pte_alloc(mm, address, huge_page_size(h));
@@ -2959,9 +3031,14 @@ void hugetlb_change_protection(struct vm_area_struct *vma,
                }
        }
        spin_unlock(&mm->page_table_lock);
-       mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex);
-
+       /*
+        * Must flush TLB before releasing i_mmap_mutex: x86's huge_pmd_unshare
+        * may have cleared our pud entry and done put_page on the page table:
+        * once we release i_mmap_mutex, another task can do the final put_page
+        * and that page table be reused and filled with junk.
+        */
        flush_tlb_range(vma, start, end);
+       mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex);
 }
 
 int hugetlb_reserve_pages(struct inode *inode,
diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c
new file mode 100644 (file)
index 0000000..a3f358f
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ *
+ * Copyright IBM Corporation, 2012
+ * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <linux/cgroup.h>
+#include <linux/slab.h>
+#include <linux/hugetlb.h>
+#include <linux/hugetlb_cgroup.h>
+
+struct hugetlb_cgroup {
+       struct cgroup_subsys_state css;
+       /*
+        * the counter to account for hugepages from hugetlb.
+        */
+       struct res_counter hugepage[HUGE_MAX_HSTATE];
+};
+
+#define MEMFILE_PRIVATE(x, val)        (((x) << 16) | (val))
+#define MEMFILE_IDX(val)       (((val) >> 16) & 0xffff)
+#define MEMFILE_ATTR(val)      ((val) & 0xffff)
+
+struct cgroup_subsys hugetlb_subsys __read_mostly;
+static struct hugetlb_cgroup *root_h_cgroup __read_mostly;
+
+static inline
+struct hugetlb_cgroup *hugetlb_cgroup_from_css(struct cgroup_subsys_state *s)
+{
+       return container_of(s, struct hugetlb_cgroup, css);
+}
+
+static inline
+struct hugetlb_cgroup *hugetlb_cgroup_from_cgroup(struct cgroup *cgroup)
+{
+       return hugetlb_cgroup_from_css(cgroup_subsys_state(cgroup,
+                                                          hugetlb_subsys_id));
+}
+
+static inline
+struct hugetlb_cgroup *hugetlb_cgroup_from_task(struct task_struct *task)
+{
+       return hugetlb_cgroup_from_css(task_subsys_state(task,
+                                                        hugetlb_subsys_id));
+}
+
+static inline bool hugetlb_cgroup_is_root(struct hugetlb_cgroup *h_cg)
+{
+       return (h_cg == root_h_cgroup);
+}
+
+static inline struct hugetlb_cgroup *parent_hugetlb_cgroup(struct cgroup *cg)
+{
+       if (!cg->parent)
+               return NULL;
+       return hugetlb_cgroup_from_cgroup(cg->parent);
+}
+
+static inline bool hugetlb_cgroup_have_usage(struct cgroup *cg)
+{
+       int idx;
+       struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cg);
+
+       for (idx = 0; idx < hugetlb_max_hstate; idx++) {
+               if ((res_counter_read_u64(&h_cg->hugepage[idx], RES_USAGE)) > 0)
+                       return true;
+       }
+       return false;
+}
+
+static struct cgroup_subsys_state *hugetlb_cgroup_create(struct cgroup *cgroup)
+{
+       int idx;
+       struct cgroup *parent_cgroup;
+       struct hugetlb_cgroup *h_cgroup, *parent_h_cgroup;
+
+       h_cgroup = kzalloc(sizeof(*h_cgroup), GFP_KERNEL);
+       if (!h_cgroup)
+               return ERR_PTR(-ENOMEM);
+
+       parent_cgroup = cgroup->parent;
+       if (parent_cgroup) {
+               parent_h_cgroup = hugetlb_cgroup_from_cgroup(parent_cgroup);
+               for (idx = 0; idx < HUGE_MAX_HSTATE; idx++)
+                       res_counter_init(&h_cgroup->hugepage[idx],
+                                        &parent_h_cgroup->hugepage[idx]);
+       } else {
+               root_h_cgroup = h_cgroup;
+               for (idx = 0; idx < HUGE_MAX_HSTATE; idx++)
+                       res_counter_init(&h_cgroup->hugepage[idx], NULL);
+       }
+       return &h_cgroup->css;
+}
+
+static void hugetlb_cgroup_destroy(struct cgroup *cgroup)
+{
+       struct hugetlb_cgroup *h_cgroup;
+
+       h_cgroup = hugetlb_cgroup_from_cgroup(cgroup);
+       kfree(h_cgroup);
+}
+
+
+/*
+ * Should be called with hugetlb_lock held.
+ * Since we are holding hugetlb_lock, pages cannot get moved from
+ * active list or uncharged from the cgroup, So no need to get
+ * page reference and test for page active here. This function
+ * cannot fail.
+ */
+static void hugetlb_cgroup_move_parent(int idx, struct cgroup *cgroup,
+                                      struct page *page)
+{
+       int csize;
+       struct res_counter *counter;
+       struct res_counter *fail_res;
+       struct hugetlb_cgroup *page_hcg;
+       struct hugetlb_cgroup *h_cg   = hugetlb_cgroup_from_cgroup(cgroup);
+       struct hugetlb_cgroup *parent = parent_hugetlb_cgroup(cgroup);
+
+       page_hcg = hugetlb_cgroup_from_page(page);
+       /*
+        * We can have pages in active list without any cgroup
+        * ie, hugepage with less than 3 pages. We can safely
+        * ignore those pages.
+        */
+       if (!page_hcg || page_hcg != h_cg)
+               goto out;
+
+       csize = PAGE_SIZE << compound_order(page);
+       if (!parent) {
+               parent = root_h_cgroup;
+               /* root has no limit */
+               res_counter_charge_nofail(&parent->hugepage[idx],
+                                         csize, &fail_res);
+       }
+       counter = &h_cg->hugepage[idx];
+       res_counter_uncharge_until(counter, counter->parent, csize);
+
+       set_hugetlb_cgroup(page, parent);
+out:
+       return;
+}
+
+/*
+ * Force the hugetlb cgroup to empty the hugetlb resources by moving them to
+ * the parent cgroup.
+ */
+static int hugetlb_cgroup_pre_destroy(struct cgroup *cgroup)
+{
+       struct hstate *h;
+       struct page *page;
+       int ret = 0, idx = 0;
+
+       do {
+               if (cgroup_task_count(cgroup) ||
+                   !list_empty(&cgroup->children)) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+               for_each_hstate(h) {
+                       spin_lock(&hugetlb_lock);
+                       list_for_each_entry(page, &h->hugepage_activelist, lru)
+                               hugetlb_cgroup_move_parent(idx, cgroup, page);
+
+                       spin_unlock(&hugetlb_lock);
+                       idx++;
+               }
+               cond_resched();
+       } while (hugetlb_cgroup_have_usage(cgroup));
+out:
+       return ret;
+}
+
+int hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,
+                                struct hugetlb_cgroup **ptr)
+{
+       int ret = 0;
+       struct res_counter *fail_res;
+       struct hugetlb_cgroup *h_cg = NULL;
+       unsigned long csize = nr_pages * PAGE_SIZE;
+
+       if (hugetlb_cgroup_disabled())
+               goto done;
+       /*
+        * We don't charge any cgroup if the compound page have less
+        * than 3 pages.
+        */
+       if (huge_page_order(&hstates[idx]) < HUGETLB_CGROUP_MIN_ORDER)
+               goto done;
+again:
+       rcu_read_lock();
+       h_cg = hugetlb_cgroup_from_task(current);
+       if (!css_tryget(&h_cg->css)) {
+               rcu_read_unlock();
+               goto again;
+       }
+       rcu_read_unlock();
+
+       ret = res_counter_charge(&h_cg->hugepage[idx], csize, &fail_res);
+       css_put(&h_cg->css);
+done:
+       *ptr = h_cg;
+       return ret;
+}
+
+/* Should be called with hugetlb_lock held */
+void hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages,
+                                 struct hugetlb_cgroup *h_cg,
+                                 struct page *page)
+{
+       if (hugetlb_cgroup_disabled() || !h_cg)
+               return;
+
+       set_hugetlb_cgroup(page, h_cg);
+       return;
+}
+
+/*
+ * Should be called with hugetlb_lock held
+ */
+void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages,
+                                 struct page *page)
+{
+       struct hugetlb_cgroup *h_cg;
+       unsigned long csize = nr_pages * PAGE_SIZE;
+
+       if (hugetlb_cgroup_disabled())
+               return;
+       VM_BUG_ON(!spin_is_locked(&hugetlb_lock));
+       h_cg = hugetlb_cgroup_from_page(page);
+       if (unlikely(!h_cg))
+               return;
+       set_hugetlb_cgroup(page, NULL);
+       res_counter_uncharge(&h_cg->hugepage[idx], csize);
+       return;
+}
+
+void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages,
+                                   struct hugetlb_cgroup *h_cg)
+{
+       unsigned long csize = nr_pages * PAGE_SIZE;
+
+       if (hugetlb_cgroup_disabled() || !h_cg)
+               return;
+
+       if (huge_page_order(&hstates[idx]) < HUGETLB_CGROUP_MIN_ORDER)
+               return;
+
+       res_counter_uncharge(&h_cg->hugepage[idx], csize);
+       return;
+}
+
+static ssize_t hugetlb_cgroup_read(struct cgroup *cgroup, struct cftype *cft,
+                                  struct file *file, char __user *buf,
+                                  size_t nbytes, loff_t *ppos)
+{
+       u64 val;
+       char str[64];
+       int idx, name, len;
+       struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup);
+
+       idx = MEMFILE_IDX(cft->private);
+       name = MEMFILE_ATTR(cft->private);
+
+       val = res_counter_read_u64(&h_cg->hugepage[idx], name);
+       len = scnprintf(str, sizeof(str), "%llu\n", (unsigned long long)val);
+       return simple_read_from_buffer(buf, nbytes, ppos, str, len);
+}
+
+static int hugetlb_cgroup_write(struct cgroup *cgroup, struct cftype *cft,
+                               const char *buffer)
+{
+       int idx, name, ret;
+       unsigned long long val;
+       struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup);
+
+       idx = MEMFILE_IDX(cft->private);
+       name = MEMFILE_ATTR(cft->private);
+
+       switch (name) {
+       case RES_LIMIT:
+               if (hugetlb_cgroup_is_root(h_cg)) {
+                       /* Can't set limit on root */
+                       ret = -EINVAL;
+                       break;
+               }
+               /* This function does all necessary parse...reuse it */
+               ret = res_counter_memparse_write_strategy(buffer, &val);
+               if (ret)
+                       break;
+               ret = res_counter_set_limit(&h_cg->hugepage[idx], val);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int hugetlb_cgroup_reset(struct cgroup *cgroup, unsigned int event)
+{
+       int idx, name, ret = 0;
+       struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup);
+
+       idx = MEMFILE_IDX(event);
+       name = MEMFILE_ATTR(event);
+
+       switch (name) {
+       case RES_MAX_USAGE:
+               res_counter_reset_max(&h_cg->hugepage[idx]);
+               break;
+       case RES_FAILCNT:
+               res_counter_reset_failcnt(&h_cg->hugepage[idx]);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static char *mem_fmt(char *buf, int size, unsigned long hsize)
+{
+       if (hsize >= (1UL << 30))
+               snprintf(buf, size, "%luGB", hsize >> 30);
+       else if (hsize >= (1UL << 20))
+               snprintf(buf, size, "%luMB", hsize >> 20);
+       else
+               snprintf(buf, size, "%luKB", hsize >> 10);
+       return buf;
+}
+
+int __init hugetlb_cgroup_file_init(int idx)
+{
+       char buf[32];
+       struct cftype *cft;
+       struct hstate *h = &hstates[idx];
+
+       /* format the size */
+       mem_fmt(buf, 32, huge_page_size(h));
+
+       /* Add the limit file */
+       cft = &h->cgroup_files[0];
+       snprintf(cft->name, MAX_CFTYPE_NAME, "%s.limit_in_bytes", buf);
+       cft->private = MEMFILE_PRIVATE(idx, RES_LIMIT);
+       cft->read = hugetlb_cgroup_read;
+       cft->write_string = hugetlb_cgroup_write;
+
+       /* Add the usage file */
+       cft = &h->cgroup_files[1];
+       snprintf(cft->name, MAX_CFTYPE_NAME, "%s.usage_in_bytes", buf);
+       cft->private = MEMFILE_PRIVATE(idx, RES_USAGE);
+       cft->read = hugetlb_cgroup_read;
+
+       /* Add the MAX usage file */
+       cft = &h->cgroup_files[2];
+       snprintf(cft->name, MAX_CFTYPE_NAME, "%s.max_usage_in_bytes", buf);
+       cft->private = MEMFILE_PRIVATE(idx, RES_MAX_USAGE);
+       cft->trigger = hugetlb_cgroup_reset;
+       cft->read = hugetlb_cgroup_read;
+
+       /* Add the failcntfile */
+       cft = &h->cgroup_files[3];
+       snprintf(cft->name, MAX_CFTYPE_NAME, "%s.failcnt", buf);
+       cft->private  = MEMFILE_PRIVATE(idx, RES_FAILCNT);
+       cft->trigger  = hugetlb_cgroup_reset;
+       cft->read = hugetlb_cgroup_read;
+
+       /* NULL terminate the last cft */
+       cft = &h->cgroup_files[4];
+       memset(cft, 0, sizeof(*cft));
+
+       WARN_ON(cgroup_add_cftypes(&hugetlb_subsys, h->cgroup_files));
+
+       return 0;
+}
+
+/*
+ * hugetlb_lock will make sure a parallel cgroup rmdir won't happen
+ * when we migrate hugepages
+ */
+void hugetlb_cgroup_migrate(struct page *oldhpage, struct page *newhpage)
+{
+       struct hugetlb_cgroup *h_cg;
+       struct hstate *h = page_hstate(oldhpage);
+
+       if (hugetlb_cgroup_disabled())
+               return;
+
+       VM_BUG_ON(!PageHuge(oldhpage));
+       spin_lock(&hugetlb_lock);
+       h_cg = hugetlb_cgroup_from_page(oldhpage);
+       set_hugetlb_cgroup(oldhpage, NULL);
+
+       /* move the h_cg details to new cgroup */
+       set_hugetlb_cgroup(newhpage, h_cg);
+       list_move(&newhpage->lru, &h->hugepage_activelist);
+       spin_unlock(&hugetlb_lock);
+       return;
+}
+
+struct cgroup_subsys hugetlb_subsys = {
+       .name = "hugetlb",
+       .create     = hugetlb_cgroup_create,
+       .pre_destroy = hugetlb_cgroup_pre_destroy,
+       .destroy    = hugetlb_cgroup_destroy,
+       .subsys_id  = hugetlb_subsys_id,
+};
index cc448bb983babf65bd0e643047353a32f787b5e6..3a61efc518d56964460af63ca31ba26bc375ad47 100644 (file)
@@ -123,7 +123,7 @@ static int pfn_inject_init(void)
        if (!dentry)
                goto fail;
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
        dentry = debugfs_create_u64("corrupt-filter-memcg", 0600,
                                    hwpoison_dir, &hwpoison_filter_memcg);
        if (!dentry)
index 2ba87fbfb75b9755e279d39af93359693afe66fd..3314f79d775a5f90c84e3250d8b93b7bd6f99933 100644 (file)
@@ -118,8 +118,14 @@ struct compact_control {
        unsigned long nr_freepages;     /* Number of isolated free pages */
        unsigned long nr_migratepages;  /* Number of pages to migrate */
        unsigned long free_pfn;         /* isolate_freepages search base */
+       unsigned long start_free_pfn;   /* where we started the search */
        unsigned long migrate_pfn;      /* isolate_migratepages search base */
        bool sync;                      /* Synchronous migration */
+       bool wrapped;                   /* Order > 0 compactions are
+                                          incremental, once free_pfn
+                                          and migrate_pfn meet, we restart
+                                          from the top of the zone;
+                                          remember we wrapped around. */
 
        int order;                      /* order a direct compactor needs */
        int migratetype;                /* MOVABLE, RECLAIMABLE etc */
@@ -347,3 +353,5 @@ extern u32 hwpoison_filter_enable;
 extern unsigned long vm_mmap_pgoff(struct file *, unsigned long,
         unsigned long, unsigned long,
         unsigned long, unsigned long);
+
+extern void set_pageblock_order(void);
index 5cc6731b00ccd05ff2f5b610627a0a2848dc5544..4d9393c7edc9072ff929175eec6e611788da124b 100644 (file)
@@ -222,13 +222,13 @@ static int __init_memblock memblock_double_array(struct memblock_type *type,
        /* Try to find some space for it.
         *
         * WARNING: We assume that either slab_is_available() and we use it or
-        * we use MEMBLOCK for allocations. That means that this is unsafe to use
-        * when bootmem is currently active (unless bootmem itself is implemented
-        * on top of MEMBLOCK which isn't the case yet)
+        * we use MEMBLOCK for allocations. That means that this is unsafe to
+        * use when bootmem is currently active (unless bootmem itself is
+        * implemented on top of MEMBLOCK which isn't the case yet)
         *
         * This should however not be an issue for now, as we currently only
-        * call into MEMBLOCK while it's still active, or much later when slab is
-        * active for memory hotplug operations
+        * call into MEMBLOCK while it's still active, or much later when slab
+        * is active for memory hotplug operations
         */
        if (use_slab) {
                new_array = kmalloc(new_size, GFP_KERNEL);
@@ -243,8 +243,8 @@ static int __init_memblock memblock_double_array(struct memblock_type *type,
                                                new_alloc_size, PAGE_SIZE);
                if (!addr && new_area_size)
                        addr = memblock_find_in_range(0,
-                                       min(new_area_start, memblock.current_limit),
-                                       new_alloc_size, PAGE_SIZE);
+                               min(new_area_start, memblock.current_limit),
+                               new_alloc_size, PAGE_SIZE);
 
                new_array = addr ? __va(addr) : 0;
        }
@@ -254,12 +254,14 @@ static int __init_memblock memblock_double_array(struct memblock_type *type,
                return -1;
        }
 
-       memblock_dbg("memblock: %s array is doubled to %ld at [%#010llx-%#010llx]",
-                memblock_type_name(type), type->max * 2, (u64)addr, (u64)addr + new_size - 1);
+       memblock_dbg("memblock: %s is doubled to %ld at [%#010llx-%#010llx]",
+                       memblock_type_name(type), type->max * 2, (u64)addr,
+                       (u64)addr + new_size - 1);
 
-       /* Found space, we now need to move the array over before
-        * we add the reserved region since it may be our reserved
-        * array itself that is full.
+       /*
+        * Found space, we now need to move the array over before we add the
+        * reserved region since it may be our reserved array itself that is
+        * full.
         */
        memcpy(new_array, type->regions, old_size);
        memset(new_array + type->max, 0, old_size);
@@ -267,17 +269,16 @@ static int __init_memblock memblock_double_array(struct memblock_type *type,
        type->regions = new_array;
        type->max <<= 1;
 
-       /* Free old array. We needn't free it if the array is the
-        * static one
-        */
+       /* Free old array. We needn't free it if the array is the static one */
        if (*in_slab)
                kfree(old_array);
        else if (old_array != memblock_memory_init_regions &&
                 old_array != memblock_reserved_init_regions)
                memblock_free(__pa(old_array), old_alloc_size);
 
-       /* Reserve the new array if that comes from the memblock.
-        * Otherwise, we needn't do it
+       /*
+        * Reserve the new array if that comes from the memblock.  Otherwise, we
+        * needn't do it
         */
        if (!use_slab)
                BUG_ON(memblock_reserve(addr, new_alloc_size));
index f72b5e52451a7d8e62648dbfe211e5ff11c7c34f..795e525afaba8914f3f8863de6a7299d84898ada 100644 (file)
@@ -61,12 +61,12 @@ struct cgroup_subsys mem_cgroup_subsys __read_mostly;
 #define MEM_CGROUP_RECLAIM_RETRIES     5
 static struct mem_cgroup *root_mem_cgroup __read_mostly;
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
 /* Turned on only when memory cgroup is enabled && really_do_swap_account = 1 */
 int do_swap_account __read_mostly;
 
 /* for remember boot option*/
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED
+#ifdef CONFIG_MEMCG_SWAP_ENABLED
 static int really_do_swap_account __initdata = 1;
 #else
 static int really_do_swap_account __initdata = 0;
@@ -87,7 +87,7 @@ enum mem_cgroup_stat_index {
        MEM_CGROUP_STAT_CACHE,     /* # of pages charged as cache */
        MEM_CGROUP_STAT_RSS,       /* # of pages charged as anon rss */
        MEM_CGROUP_STAT_FILE_MAPPED,  /* # of pages charged as file rss */
-       MEM_CGROUP_STAT_SWAPOUT, /* # of pages, swapped out */
+       MEM_CGROUP_STAT_SWAP, /* # of pages, swapped out */
        MEM_CGROUP_STAT_NSTATS,
 };
 
@@ -378,9 +378,7 @@ static bool move_file(void)
 
 enum charge_type {
        MEM_CGROUP_CHARGE_TYPE_CACHE = 0,
-       MEM_CGROUP_CHARGE_TYPE_MAPPED,
-       MEM_CGROUP_CHARGE_TYPE_SHMEM,   /* used by page migration of shmem */
-       MEM_CGROUP_CHARGE_TYPE_FORCE,   /* used by force_empty */
+       MEM_CGROUP_CHARGE_TYPE_ANON,
        MEM_CGROUP_CHARGE_TYPE_SWAPOUT, /* for accounting swapcache */
        MEM_CGROUP_CHARGE_TYPE_DROP,    /* a page was unused swap cache */
        NR_CHARGE_TYPE,
@@ -407,8 +405,14 @@ enum charge_type {
 static void mem_cgroup_get(struct mem_cgroup *memcg);
 static void mem_cgroup_put(struct mem_cgroup *memcg);
 
+static inline
+struct mem_cgroup *mem_cgroup_from_css(struct cgroup_subsys_state *s)
+{
+       return container_of(s, struct mem_cgroup, css);
+}
+
 /* Writing them here to avoid exposing memcg's inner layout */
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
 #include <net/sock.h>
 #include <net/ip.h>
 
@@ -467,9 +471,9 @@ struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg)
 }
 EXPORT_SYMBOL(tcp_proto_cgroup);
 #endif /* CONFIG_INET */
-#endif /* CONFIG_CGROUP_MEM_RES_CTLR_KMEM */
+#endif /* CONFIG_MEMCG_KMEM */
 
-#if defined(CONFIG_INET) && defined(CONFIG_CGROUP_MEM_RES_CTLR_KMEM)
+#if defined(CONFIG_INET) && defined(CONFIG_MEMCG_KMEM)
 static void disarm_sock_keys(struct mem_cgroup *memcg)
 {
        if (!memcg_proto_activated(&memcg->tcp_mem.cg_proto))
@@ -703,7 +707,7 @@ static void mem_cgroup_swap_statistics(struct mem_cgroup *memcg,
                                         bool charge)
 {
        int val = (charge) ? 1 : -1;
-       this_cpu_add(memcg->stat->count[MEM_CGROUP_STAT_SWAPOUT], val);
+       this_cpu_add(memcg->stat->count[MEM_CGROUP_STAT_SWAP], val);
 }
 
 static unsigned long mem_cgroup_read_events(struct mem_cgroup *memcg,
@@ -864,9 +868,8 @@ static void memcg_check_events(struct mem_cgroup *memcg, struct page *page)
 
 struct mem_cgroup *mem_cgroup_from_cont(struct cgroup *cont)
 {
-       return container_of(cgroup_subsys_state(cont,
-                               mem_cgroup_subsys_id), struct mem_cgroup,
-                               css);
+       return mem_cgroup_from_css(
+               cgroup_subsys_state(cont, mem_cgroup_subsys_id));
 }
 
 struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p)
@@ -879,8 +882,7 @@ struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p)
        if (unlikely(!p))
                return NULL;
 
-       return container_of(task_subsys_state(p, mem_cgroup_subsys_id),
-                               struct mem_cgroup, css);
+       return mem_cgroup_from_css(task_subsys_state(p, mem_cgroup_subsys_id));
 }
 
 struct mem_cgroup *try_get_mem_cgroup_from_mm(struct mm_struct *mm)
@@ -966,8 +968,7 @@ struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root,
                css = css_get_next(&mem_cgroup_subsys, id + 1, &root->css, &id);
                if (css) {
                        if (css == &root->css || css_tryget(css))
-                               memcg = container_of(css,
-                                                    struct mem_cgroup, css);
+                               memcg = mem_cgroup_from_css(css);
                } else
                        id = 0;
                rcu_read_unlock();
@@ -1454,7 +1455,7 @@ static int mem_cgroup_count_children(struct mem_cgroup *memcg)
 /*
  * Return the memory (and swap, if configured) limit for a memcg.
  */
-u64 mem_cgroup_get_limit(struct mem_cgroup *memcg)
+static u64 mem_cgroup_get_limit(struct mem_cgroup *memcg)
 {
        u64 limit;
        u64 memsw;
@@ -1470,6 +1471,73 @@ u64 mem_cgroup_get_limit(struct mem_cgroup *memcg)
        return min(limit, memsw);
 }
 
+void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
+                             int order)
+{
+       struct mem_cgroup *iter;
+       unsigned long chosen_points = 0;
+       unsigned long totalpages;
+       unsigned int points = 0;
+       struct task_struct *chosen = NULL;
+
+       /*
+        * If current has a pending SIGKILL, then automatically select it.  The
+        * goal is to allow it to allocate so that it may quickly exit and free
+        * its memory.
+        */
+       if (fatal_signal_pending(current)) {
+               set_thread_flag(TIF_MEMDIE);
+               return;
+       }
+
+       check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL);
+       totalpages = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT ? : 1;
+       for_each_mem_cgroup_tree(iter, memcg) {
+               struct cgroup *cgroup = iter->css.cgroup;
+               struct cgroup_iter it;
+               struct task_struct *task;
+
+               cgroup_iter_start(cgroup, &it);
+               while ((task = cgroup_iter_next(cgroup, &it))) {
+                       switch (oom_scan_process_thread(task, totalpages, NULL,
+                                                       false)) {
+                       case OOM_SCAN_SELECT:
+                               if (chosen)
+                                       put_task_struct(chosen);
+                               chosen = task;
+                               chosen_points = ULONG_MAX;
+                               get_task_struct(chosen);
+                               /* fall through */
+                       case OOM_SCAN_CONTINUE:
+                               continue;
+                       case OOM_SCAN_ABORT:
+                               cgroup_iter_end(cgroup, &it);
+                               mem_cgroup_iter_break(memcg, iter);
+                               if (chosen)
+                                       put_task_struct(chosen);
+                               return;
+                       case OOM_SCAN_OK:
+                               break;
+                       };
+                       points = oom_badness(task, memcg, NULL, totalpages);
+                       if (points > chosen_points) {
+                               if (chosen)
+                                       put_task_struct(chosen);
+                               chosen = task;
+                               chosen_points = points;
+                               get_task_struct(chosen);
+                       }
+               }
+               cgroup_iter_end(cgroup, &it);
+       }
+
+       if (!chosen)
+               return;
+       points = chosen_points * 1000 / totalpages;
+       oom_kill_process(chosen, gfp_mask, order, points, totalpages, memcg,
+                        NULL, "Memory cgroup out of memory");
+}
+
 static unsigned long mem_cgroup_reclaim(struct mem_cgroup *memcg,
                                        gfp_t gfp_mask,
                                        unsigned long flags)
@@ -1899,7 +1967,7 @@ again:
                return;
        /*
         * If this memory cgroup is not under account moving, we don't
-        * need to take move_lock_page_cgroup(). Because we already hold
+        * need to take move_lock_mem_cgroup(). Because we already hold
         * rcu_read_lock(), any calls to move_account will be delayed until
         * rcu_read_unlock() if mem_cgroup_stolen() == true.
         */
@@ -1921,7 +1989,7 @@ void __mem_cgroup_end_update_page_stat(struct page *page, unsigned long *flags)
        /*
         * It's guaranteed that pc->mem_cgroup never changes while
         * lock is held because a routine modifies pc->mem_cgroup
-        * should take move_lock_page_cgroup().
+        * should take move_lock_mem_cgroup().
         */
        move_unlock_mem_cgroup(pc->mem_cgroup, flags);
 }
@@ -2268,7 +2336,7 @@ static int __mem_cgroup_try_charge(struct mm_struct *mm,
         * We always charge the cgroup the mm_struct belongs to.
         * The mm_struct's mem_cgroup changes on task migration if the
         * thread group leader migrates. It's possible that mm is not
-        * set, if so charge the init_mm (happens for pagecache usage).
+        * set, if so charge the root memcg (happens for pagecache usage).
         */
        if (!*ptr && !mm)
                *ptr = root_mem_cgroup;
@@ -2429,7 +2497,7 @@ static struct mem_cgroup *mem_cgroup_lookup(unsigned short id)
        css = css_lookup(&mem_cgroup_subsys, id);
        if (!css)
                return NULL;
-       return container_of(css, struct mem_cgroup, css);
+       return mem_cgroup_from_css(css);
 }
 
 struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page)
@@ -2473,11 +2541,7 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg,
        bool anon;
 
        lock_page_cgroup(pc);
-       if (unlikely(PageCgroupUsed(pc))) {
-               unlock_page_cgroup(pc);
-               __mem_cgroup_cancel_charge(memcg, nr_pages);
-               return;
-       }
+       VM_BUG_ON(PageCgroupUsed(pc));
        /*
         * we don't need page_cgroup_lock about tail pages, becase they are not
         * accessed by any other context at this point.
@@ -2519,7 +2583,7 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg,
                spin_unlock_irq(&zone->lru_lock);
        }
 
-       if (ctype == MEM_CGROUP_CHARGE_TYPE_MAPPED)
+       if (ctype == MEM_CGROUP_CHARGE_TYPE_ANON)
                anon = true;
        else
                anon = false;
@@ -2644,8 +2708,7 @@ out:
 
 static int mem_cgroup_move_parent(struct page *page,
                                  struct page_cgroup *pc,
-                                 struct mem_cgroup *child,
-                                 gfp_t gfp_mask)
+                                 struct mem_cgroup *child)
 {
        struct mem_cgroup *parent;
        unsigned int nr_pages;
@@ -2728,38 +2791,7 @@ int mem_cgroup_newpage_charge(struct page *page,
        VM_BUG_ON(page->mapping && !PageAnon(page));
        VM_BUG_ON(!mm);
        return mem_cgroup_charge_common(page, mm, gfp_mask,
-                                       MEM_CGROUP_CHARGE_TYPE_MAPPED);
-}
-
-static void
-__mem_cgroup_commit_charge_swapin(struct page *page, struct mem_cgroup *ptr,
-                                       enum charge_type ctype);
-
-int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm,
-                               gfp_t gfp_mask)
-{
-       struct mem_cgroup *memcg = NULL;
-       enum charge_type type = MEM_CGROUP_CHARGE_TYPE_CACHE;
-       int ret;
-
-       if (mem_cgroup_disabled())
-               return 0;
-       if (PageCompound(page))
-               return 0;
-
-       if (unlikely(!mm))
-               mm = &init_mm;
-       if (!page_is_file_cache(page))
-               type = MEM_CGROUP_CHARGE_TYPE_SHMEM;
-
-       if (!PageSwapCache(page))
-               ret = mem_cgroup_charge_common(page, mm, gfp_mask, type);
-       else { /* page is swapcache/shmem */
-               ret = mem_cgroup_try_charge_swapin(mm, page, gfp_mask, &memcg);
-               if (!ret)
-                       __mem_cgroup_commit_charge_swapin(page, memcg, type);
-       }
-       return ret;
+                                       MEM_CGROUP_CHARGE_TYPE_ANON);
 }
 
 /*
@@ -2768,27 +2800,26 @@ int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm,
  * 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,
-                                struct page *page,
-                                gfp_t mask, struct mem_cgroup **memcgp)
+static int __mem_cgroup_try_charge_swapin(struct mm_struct *mm,
+                                         struct page *page,
+                                         gfp_t mask,
+                                         struct mem_cgroup **memcgp)
 {
        struct mem_cgroup *memcg;
+       struct page_cgroup *pc;
        int ret;
 
-       *memcgp = NULL;
-
-       if (mem_cgroup_disabled())
-               return 0;
-
-       if (!do_swap_account)
-               goto charge_cur_mm;
+       pc = lookup_page_cgroup(page);
        /*
-        * A racing thread's fault, or swapoff, may have already updated
-        * the pte, and even removed page from swap cache: in those cases
-        * do_swap_page()'s pte_same() test will fail; but there's also a
-        * KSM case which does need to charge the page.
+        * Every swap fault against a single page tries to charge the
+        * page, bail as early as possible.  shmem_unuse() encounters
+        * already charged pages, too.  The USED bit is protected by
+        * the page lock, which serializes swap cache removal, which
+        * in turn serializes uncharging.
         */
-       if (!PageSwapCache(page))
+       if (PageCgroupUsed(pc))
+               return 0;
+       if (!do_swap_account)
                goto charge_cur_mm;
        memcg = try_get_mem_cgroup_from_page(page);
        if (!memcg)
@@ -2800,14 +2831,44 @@ int mem_cgroup_try_charge_swapin(struct mm_struct *mm,
                ret = 0;
        return ret;
 charge_cur_mm:
-       if (unlikely(!mm))
-               mm = &init_mm;
        ret = __mem_cgroup_try_charge(mm, mask, 1, memcgp, true);
        if (ret == -EINTR)
                ret = 0;
        return ret;
 }
 
+int mem_cgroup_try_charge_swapin(struct mm_struct *mm, struct page *page,
+                                gfp_t gfp_mask, struct mem_cgroup **memcgp)
+{
+       *memcgp = NULL;
+       if (mem_cgroup_disabled())
+               return 0;
+       /*
+        * A racing thread's fault, or swapoff, may have already
+        * updated the pte, and even removed page from swap cache: in
+        * those cases unuse_pte()'s pte_same() test will fail; but
+        * there's also a KSM case which does need to charge the page.
+        */
+       if (!PageSwapCache(page)) {
+               int ret;
+
+               ret = __mem_cgroup_try_charge(mm, gfp_mask, 1, memcgp, true);
+               if (ret == -EINTR)
+                       ret = 0;
+               return ret;
+       }
+       return __mem_cgroup_try_charge_swapin(mm, page, gfp_mask, memcgp);
+}
+
+void mem_cgroup_cancel_charge_swapin(struct mem_cgroup *memcg)
+{
+       if (mem_cgroup_disabled())
+               return;
+       if (!memcg)
+               return;
+       __mem_cgroup_cancel_charge(memcg, 1);
+}
+
 static void
 __mem_cgroup_commit_charge_swapin(struct page *page, struct mem_cgroup *memcg,
                                        enum charge_type ctype)
@@ -2842,16 +2903,30 @@ void mem_cgroup_commit_charge_swapin(struct page *page,
                                     struct mem_cgroup *memcg)
 {
        __mem_cgroup_commit_charge_swapin(page, memcg,
-                                         MEM_CGROUP_CHARGE_TYPE_MAPPED);
+                                         MEM_CGROUP_CHARGE_TYPE_ANON);
 }
 
-void mem_cgroup_cancel_charge_swapin(struct mem_cgroup *memcg)
+int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm,
+                               gfp_t gfp_mask)
 {
+       struct mem_cgroup *memcg = NULL;
+       enum charge_type type = MEM_CGROUP_CHARGE_TYPE_CACHE;
+       int ret;
+
        if (mem_cgroup_disabled())
-               return;
-       if (!memcg)
-               return;
-       __mem_cgroup_cancel_charge(memcg, 1);
+               return 0;
+       if (PageCompound(page))
+               return 0;
+
+       if (!PageSwapCache(page))
+               ret = mem_cgroup_charge_common(page, mm, gfp_mask, type);
+       else { /* page is swapcache/shmem */
+               ret = __mem_cgroup_try_charge_swapin(mm, page,
+                                                    gfp_mask, &memcg);
+               if (!ret)
+                       __mem_cgroup_commit_charge_swapin(page, memcg, type);
+       }
+       return ret;
 }
 
 static void mem_cgroup_do_uncharge(struct mem_cgroup *memcg,
@@ -2911,7 +2986,8 @@ direct_uncharge:
  * uncharge if !page_mapped(page)
  */
 static struct mem_cgroup *
-__mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
+__mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype,
+                            bool end_migration)
 {
        struct mem_cgroup *memcg = NULL;
        unsigned int nr_pages = 1;
@@ -2921,8 +2997,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
        if (mem_cgroup_disabled())
                return NULL;
 
-       if (PageSwapCache(page))
-               return NULL;
+       VM_BUG_ON(PageSwapCache(page));
 
        if (PageTransHuge(page)) {
                nr_pages <<= compound_order(page);
@@ -2945,7 +3020,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
        anon = PageAnon(page);
 
        switch (ctype) {
-       case MEM_CGROUP_CHARGE_TYPE_MAPPED:
+       case MEM_CGROUP_CHARGE_TYPE_ANON:
                /*
                 * Generally PageAnon tells if it's the anon statistics to be
                 * updated; but sometimes e.g. mem_cgroup_uncharge_page() is
@@ -2955,7 +3030,16 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
                /* fallthrough */
        case MEM_CGROUP_CHARGE_TYPE_DROP:
                /* See mem_cgroup_prepare_migration() */
-               if (page_mapped(page) || PageCgroupMigration(pc))
+               if (page_mapped(page))
+                       goto unlock_out;
+               /*
+                * Pages under migration may not be uncharged.  But
+                * end_migration() /must/ be the one uncharging the
+                * unused post-migration page and so it has to call
+                * here with the migration bit still set.  See the
+                * res_counter handling below.
+                */
+               if (!end_migration && PageCgroupMigration(pc))
                        goto unlock_out;
                break;
        case MEM_CGROUP_CHARGE_TYPE_SWAPOUT:
@@ -2989,7 +3073,12 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
                mem_cgroup_swap_statistics(memcg, true);
                mem_cgroup_get(memcg);
        }
-       if (!mem_cgroup_is_root(memcg))
+       /*
+        * Migration does not charge the res_counter for the
+        * replacement page, so leave it alone when phasing out the
+        * page that is unused after the migration.
+        */
+       if (!end_migration && !mem_cgroup_is_root(memcg))
                mem_cgroup_do_uncharge(memcg, nr_pages, ctype);
 
        return memcg;
@@ -3005,14 +3094,16 @@ void mem_cgroup_uncharge_page(struct page *page)
        if (page_mapped(page))
                return;
        VM_BUG_ON(page->mapping && !PageAnon(page));
-       __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_MAPPED);
+       if (PageSwapCache(page))
+               return;
+       __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_ANON, false);
 }
 
 void mem_cgroup_uncharge_cache_page(struct page *page)
 {
        VM_BUG_ON(page_mapped(page));
        VM_BUG_ON(page->mapping);
-       __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_CACHE);
+       __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_CACHE, false);
 }
 
 /*
@@ -3076,7 +3167,7 @@ mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout)
        if (!swapout) /* this was a swap cache but the swap is unused ! */
                ctype = MEM_CGROUP_CHARGE_TYPE_DROP;
 
-       memcg = __mem_cgroup_uncharge_common(page, ctype);
+       memcg = __mem_cgroup_uncharge_common(page, ctype, false);
 
        /*
         * record memcg information,  if swapout && memcg != NULL,
@@ -3087,7 +3178,7 @@ mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout)
 }
 #endif
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
 /*
  * called from swap_entry_free(). remove record in swap_cgroup and
  * uncharge "memsw" account.
@@ -3166,19 +3257,18 @@ static inline int mem_cgroup_move_swap_account(swp_entry_t entry,
  * Before starting migration, account PAGE_SIZE to mem_cgroup that the old
  * page belongs to.
  */
-int mem_cgroup_prepare_migration(struct page *page,
-       struct page *newpage, struct mem_cgroup **memcgp, gfp_t gfp_mask)
+void mem_cgroup_prepare_migration(struct page *page, struct page *newpage,
+                                 struct mem_cgroup **memcgp)
 {
        struct mem_cgroup *memcg = NULL;
        struct page_cgroup *pc;
        enum charge_type ctype;
-       int ret = 0;
 
        *memcgp = NULL;
 
        VM_BUG_ON(PageTransHuge(page));
        if (mem_cgroup_disabled())
-               return 0;
+               return;
 
        pc = lookup_page_cgroup(page);
        lock_page_cgroup(pc);
@@ -3223,24 +3313,9 @@ int mem_cgroup_prepare_migration(struct page *page,
         * we return here.
         */
        if (!memcg)
-               return 0;
+               return;
 
        *memcgp = memcg;
-       ret = __mem_cgroup_try_charge(NULL, gfp_mask, 1, memcgp, false);
-       css_put(&memcg->css);/* drop extra refcnt */
-       if (ret) {
-               if (PageAnon(page)) {
-                       lock_page_cgroup(pc);
-                       ClearPageCgroupMigration(pc);
-                       unlock_page_cgroup(pc);
-                       /*
-                        * The old page may be fully unmapped while we kept it.
-                        */
-                       mem_cgroup_uncharge_page(page);
-               }
-               /* we'll need to revisit this error code (we have -EINTR) */
-               return -ENOMEM;
-       }
        /*
         * We charge new page before it's used/mapped. So, even if unlock_page()
         * is called before end_migration, we can catch all events on this new
@@ -3248,13 +3323,15 @@ int mem_cgroup_prepare_migration(struct page *page,
         * mapcount will be finally 0 and we call uncharge in end_migration().
         */
        if (PageAnon(page))
-               ctype = MEM_CGROUP_CHARGE_TYPE_MAPPED;
-       else if (page_is_file_cache(page))
-               ctype = MEM_CGROUP_CHARGE_TYPE_CACHE;
+               ctype = MEM_CGROUP_CHARGE_TYPE_ANON;
        else
-               ctype = MEM_CGROUP_CHARGE_TYPE_SHMEM;
+               ctype = MEM_CGROUP_CHARGE_TYPE_CACHE;
+       /*
+        * The page is committed to the memcg, but it's not actually
+        * charged to the res_counter since we plan on replacing the
+        * old one and only one page is going to be left afterwards.
+        */
        __mem_cgroup_commit_charge(memcg, newpage, 1, ctype, false);
-       return ret;
 }
 
 /* remove redundant charge if migration failed*/
@@ -3276,6 +3353,12 @@ void mem_cgroup_end_migration(struct mem_cgroup *memcg,
                used = newpage;
                unused = oldpage;
        }
+       anon = PageAnon(used);
+       __mem_cgroup_uncharge_common(unused,
+                                    anon ? MEM_CGROUP_CHARGE_TYPE_ANON
+                                    : MEM_CGROUP_CHARGE_TYPE_CACHE,
+                                    true);
+       css_put(&memcg->css);
        /*
         * We disallowed uncharge of pages under migration because mapcount
         * of the page goes down to zero, temporarly.
@@ -3285,10 +3368,6 @@ void mem_cgroup_end_migration(struct mem_cgroup *memcg,
        lock_page_cgroup(pc);
        ClearPageCgroupMigration(pc);
        unlock_page_cgroup(pc);
-       anon = PageAnon(used);
-       __mem_cgroup_uncharge_common(unused,
-               anon ? MEM_CGROUP_CHARGE_TYPE_MAPPED
-                    : MEM_CGROUP_CHARGE_TYPE_CACHE);
 
        /*
         * If a page is a file cache, radix-tree replacement is very atomic
@@ -3340,10 +3419,6 @@ void mem_cgroup_replace_page_cache(struct page *oldpage,
         */
        if (!memcg)
                return;
-
-       if (PageSwapBacked(oldpage))
-               type = MEM_CGROUP_CHARGE_TYPE_SHMEM;
-
        /*
         * Even if newpage->mapping was NULL before starting replacement,
         * the newpage may be on LRU(or pagevec for LRU) already. We lock
@@ -3418,7 +3493,7 @@ static int mem_cgroup_resize_limit(struct mem_cgroup *memcg,
                /*
                 * Rather than hide all in some function, I do this in
                 * open coded manner. You see what this really does.
-                * We have to guarantee memcg->res.limit < memcg->memsw.limit.
+                * We have to guarantee memcg->res.limit <= memcg->memsw.limit.
                 */
                mutex_lock(&set_limit_mutex);
                memswlimit = res_counter_read_u64(&memcg->memsw, RES_LIMIT);
@@ -3479,7 +3554,7 @@ static int mem_cgroup_resize_memsw_limit(struct mem_cgroup *memcg,
                /*
                 * Rather than hide all in some function, I do this in
                 * open coded manner. You see what this really does.
-                * We have to guarantee memcg->res.limit < memcg->memsw.limit.
+                * We have to guarantee memcg->res.limit <= memcg->memsw.limit.
                 */
                mutex_lock(&set_limit_mutex);
                memlimit = res_counter_read_u64(&memcg->res, RES_LIMIT);
@@ -3611,10 +3686,12 @@ unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
 }
 
 /*
- * This routine traverse page_cgroup in given list and drop them all.
- * *And* this routine doesn't reclaim page itself, just removes page_cgroup.
+ * Traverse a specified page_cgroup list and try to drop them all.  This doesn't
+ * reclaim the pages page themselves - it just removes the page_cgroups.
+ * Returns true if some page_cgroups were not freed, indicating that the caller
+ * must retry this operation.
  */
-static int mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
+static bool mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
                                int node, int zid, enum lru_list lru)
 {
        struct mem_cgroup_per_zone *mz;
@@ -3622,7 +3699,6 @@ static int mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
        struct list_head *list;
        struct page *busy;
        struct zone *zone;
-       int ret = 0;
 
        zone = &NODE_DATA(node)->node_zones[zid];
        mz = mem_cgroup_zoneinfo(memcg, node, zid);
@@ -3636,7 +3712,6 @@ static int mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
                struct page_cgroup *pc;
                struct page *page;
 
-               ret = 0;
                spin_lock_irqsave(&zone->lru_lock, flags);
                if (list_empty(list)) {
                        spin_unlock_irqrestore(&zone->lru_lock, flags);
@@ -3653,21 +3728,14 @@ static int mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
 
                pc = lookup_page_cgroup(page);
 
-               ret = mem_cgroup_move_parent(page, pc, memcg, GFP_KERNEL);
-               if (ret == -ENOMEM || ret == -EINTR)
-                       break;
-
-               if (ret == -EBUSY || ret == -EINVAL) {
+               if (mem_cgroup_move_parent(page, pc, memcg)) {
                        /* found lock contention or "pc" is obsolete. */
                        busy = page;
                        cond_resched();
                } else
                        busy = NULL;
        }
-
-       if (!ret && !list_empty(list))
-               return -EBUSY;
-       return ret;
+       return !list_empty(list);
 }
 
 /*
@@ -3692,9 +3760,6 @@ move_account:
                ret = -EBUSY;
                if (cgroup_task_count(cgrp) || !list_empty(&cgrp->children))
                        goto out;
-               ret = -EINTR;
-               if (signal_pending(current))
-                       goto out;
                /* This is for making all *used* pages to be on LRU. */
                lru_add_drain_all();
                drain_all_stock_sync(memcg);
@@ -3715,9 +3780,6 @@ move_account:
                }
                mem_cgroup_end_move(memcg);
                memcg_oom_recover(memcg);
-               /* it seems parent cgroup doesn't have enough mem */
-               if (ret == -ENOMEM)
-                       goto try_to_free;
                cond_resched();
        /* "ret" should also be checked to ensure all lists are empty. */
        } while (res_counter_read_u64(&memcg->res, RES_USAGE) > 0 || ret);
@@ -3779,6 +3841,10 @@ static int mem_cgroup_hierarchy_write(struct cgroup *cont, struct cftype *cft,
                parent_memcg = mem_cgroup_from_cont(parent);
 
        cgroup_lock();
+
+       if (memcg->use_hierarchy == val)
+               goto out;
+
        /*
         * 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
@@ -3795,6 +3861,8 @@ static int mem_cgroup_hierarchy_write(struct cgroup *cont, struct cftype *cft,
                        retval = -EBUSY;
        } else
                retval = -EINVAL;
+
+out:
        cgroup_unlock();
 
        return retval;
@@ -3831,7 +3899,7 @@ static inline u64 mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)
        val += mem_cgroup_recursive_stat(memcg, MEM_CGROUP_STAT_RSS);
 
        if (swap)
-               val += mem_cgroup_recursive_stat(memcg, MEM_CGROUP_STAT_SWAPOUT);
+               val += mem_cgroup_recursive_stat(memcg, MEM_CGROUP_STAT_SWAP);
 
        return val << PAGE_SHIFT;
 }
@@ -4015,7 +4083,7 @@ static int mem_cgroup_move_charge_write(struct cgroup *cgrp,
 #endif
 
 #ifdef CONFIG_NUMA
-static int mem_control_numa_stat_show(struct cgroup *cont, struct cftype *cft,
+static int memcg_numa_stat_show(struct cgroup *cont, struct cftype *cft,
                                      struct seq_file *m)
 {
        int nid;
@@ -4074,7 +4142,7 @@ static inline void mem_cgroup_lru_names_not_uptodate(void)
        BUILD_BUG_ON(ARRAY_SIZE(mem_cgroup_lru_names) != NR_LRU_LISTS);
 }
 
-static int mem_control_stat_show(struct cgroup *cont, struct cftype *cft,
+static int memcg_stat_show(struct cgroup *cont, struct cftype *cft,
                                 struct seq_file *m)
 {
        struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
@@ -4082,7 +4150,7 @@ static int mem_control_stat_show(struct cgroup *cont, struct cftype *cft,
        unsigned int i;
 
        for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
-               if (i == MEM_CGROUP_STAT_SWAPOUT && !do_swap_account)
+               if (i == MEM_CGROUP_STAT_SWAP && !do_swap_account)
                        continue;
                seq_printf(m, "%s %ld\n", mem_cgroup_stat_names[i],
                           mem_cgroup_read_stat(memcg, i) * PAGE_SIZE);
@@ -4109,7 +4177,7 @@ static int mem_control_stat_show(struct cgroup *cont, struct cftype *cft,
        for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
                long long val = 0;
 
-               if (i == MEM_CGROUP_STAT_SWAPOUT && !do_swap_account)
+               if (i == MEM_CGROUP_STAT_SWAP && !do_swap_account)
                        continue;
                for_each_mem_cgroup_tree(mi, memcg)
                        val += mem_cgroup_read_stat(mi, i) * PAGE_SIZE;
@@ -4533,7 +4601,7 @@ static int mem_cgroup_oom_control_write(struct cgroup *cgrp,
        return 0;
 }
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
 static int memcg_init_kmem(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
 {
        return mem_cgroup_sockets_init(memcg, ss);
@@ -4588,7 +4656,7 @@ static struct cftype mem_cgroup_files[] = {
        },
        {
                .name = "stat",
-               .read_seq_string = mem_control_stat_show,
+               .read_seq_string = memcg_stat_show,
        },
        {
                .name = "force_empty",
@@ -4620,10 +4688,10 @@ static struct cftype mem_cgroup_files[] = {
 #ifdef CONFIG_NUMA
        {
                .name = "numa_stat",
-               .read_seq_string = mem_control_numa_stat_show,
+               .read_seq_string = memcg_numa_stat_show,
        },
 #endif
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
        {
                .name = "memsw.usage_in_bytes",
                .private = MEMFILE_PRIVATE(_MEMSWAP, RES_USAGE),
@@ -4810,7 +4878,7 @@ struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *memcg)
 }
 EXPORT_SYMBOL(parent_mem_cgroup);
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
 static void __init enable_swap_cgroup(void)
 {
        if (!mem_cgroup_disabled() && really_do_swap_account)
@@ -5541,7 +5609,7 @@ struct cgroup_subsys mem_cgroup_subsys = {
        .__DEPRECATED_clear_css_refs = true,
 };
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
 static int __init enable_swap_account(char *s)
 {
        /* consider enabled if no parameter or 1 is given */
index de4ce70584508edfa95eec5827271511052af102..a6e2141a6610bfd4fab325e343149a25fadc8835 100644 (file)
@@ -128,7 +128,7 @@ static int hwpoison_filter_flags(struct page *p)
  * can only guarantee that the page either belongs to the memcg tasks, or is
  * a freed page.
  */
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
 u64 hwpoison_filter_memcg;
 EXPORT_SYMBOL_GPL(hwpoison_filter_memcg);
 static int hwpoison_filter_task(struct page *p)
@@ -1416,7 +1416,6 @@ static int soft_offline_huge_page(struct page *page, int flags)
        int ret;
        unsigned long pfn = page_to_pfn(page);
        struct page *hpage = compound_head(page);
-       LIST_HEAD(pagelist);
 
        ret = get_any_page(page, pfn, flags);
        if (ret < 0)
@@ -1431,24 +1430,18 @@ static int soft_offline_huge_page(struct page *page, int flags)
        }
 
        /* Keep page count to indicate a given hugepage is isolated. */
-
-       list_add(&hpage->lru, &pagelist);
-       ret = migrate_huge_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL, 0,
-                               true);
+       ret = migrate_huge_page(hpage, new_page, MPOL_MF_MOVE_ALL, false,
+                               MIGRATE_SYNC);
+       put_page(hpage);
        if (ret) {
-               struct page *page1, *page2;
-               list_for_each_entry_safe(page1, page2, &pagelist, lru)
-                       put_page(page1);
-
                pr_info("soft offline: %#lx: migration failed %d, type %lx\n",
                        pfn, ret, page->flags);
-               if (ret > 0)
-                       ret = -EIO;
                return ret;
        }
 done:
        if (!PageHWPoison(hpage))
-               atomic_long_add(1 << compound_trans_order(hpage), &mce_bad_pages);
+               atomic_long_add(1 << compound_trans_order(hpage),
+                               &mce_bad_pages);
        set_page_hwpoison_huge_page(hpage);
        dequeue_hwpoisoned_huge_page(hpage);
        /* keep elevated page count for bad page */
@@ -1563,7 +1556,7 @@ int soft_offline_page(struct page *page, int flags)
                                            page_is_file_cache(page));
                list_add(&page->lru, &pagelist);
                ret = migrate_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL,
-                                                       0, MIGRATE_SYNC);
+                                                       false, MIGRATE_SYNC);
                if (ret) {
                        putback_lru_pages(&pagelist);
                        pr_info("soft offline: %#lx: migration failed %d, type %lx\n",
index 91f69459d3e8b3bf8075574e145788b0b40c4a6f..57361708d1a57d7bc11c8f34d269a35c50317dbb 100644 (file)
@@ -1343,8 +1343,11 @@ static void unmap_single_vma(struct mmu_gather *tlb,
                         * Since no pte has actually been setup, it is
                         * safe to do nothing in this case.
                         */
-                       if (vma->vm_file)
-                               unmap_hugepage_range(vma, start, end, NULL);
+                       if (vma->vm_file) {
+                               mutex_lock(&vma->vm_file->f_mapping->i_mmap_mutex);
+                               __unmap_hugepage_range_final(tlb, vma, start, end, NULL);
+                               mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex);
+                       }
                } else
                        unmap_page_range(tlb, vma, start, end, details);
        }
@@ -2647,6 +2650,9 @@ reuse:
                if (!page_mkwrite) {
                        wait_on_page_locked(dirty_page);
                        set_page_dirty_balance(dirty_page, page_mkwrite);
+                       /* file_update_time outside page_lock */
+                       if (vma->vm_file)
+                               file_update_time(vma->vm_file);
                }
                put_page(dirty_page);
                if (page_mkwrite) {
@@ -2664,10 +2670,6 @@ reuse:
                        }
                }
 
-               /* file_update_time outside page_lock */
-               if (vma->vm_file)
-                       file_update_time(vma->vm_file);
-
                return ret;
        }
 
@@ -3336,12 +3338,13 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,
 
        if (dirty_page) {
                struct address_space *mapping = page->mapping;
+               int dirtied = 0;
 
                if (set_page_dirty(dirty_page))
-                       page_mkwrite = 1;
+                       dirtied = 1;
                unlock_page(dirty_page);
                put_page(dirty_page);
-               if (page_mkwrite && mapping) {
+               if ((dirtied || page_mkwrite) && mapping) {
                        /*
                         * Some device drivers do not set page.mapping but still
                         * dirty their pages
@@ -3350,7 +3353,7 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,
                }
 
                /* file_update_time outside page_lock */
-               if (vma->vm_file)
+               if (vma->vm_file && !page_mkwrite)
                        file_update_time(vma->vm_file);
        } else {
                unlock_page(vmf.page);
@@ -3938,7 +3941,7 @@ void print_vma_addr(char *prefix, unsigned long ip)
                        free_page((unsigned long)buf);
                }
        }
-       up_read(&current->mm->mmap_sem);
+       up_read(&mm->mmap_sem);
 }
 
 #ifdef CONFIG_PROVE_LOCKING
index 427bb291dd0fdeeb6db93c86c66df6a6f92c9b28..3ad25f9d1fc134dc09e9672fad40f508e12e9502 100644 (file)
@@ -512,19 +512,20 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages)
 
        zone->present_pages += onlined_pages;
        zone->zone_pgdat->node_present_pages += onlined_pages;
-       if (need_zonelists_rebuild)
-               build_all_zonelists(zone);
-       else
-               zone_pcp_update(zone);
+       if (onlined_pages) {
+               node_set_state(zone_to_nid(zone), N_HIGH_MEMORY);
+               if (need_zonelists_rebuild)
+                       build_all_zonelists(NULL, zone);
+               else
+                       zone_pcp_update(zone);
+       }
 
        mutex_unlock(&zonelists_mutex);
 
        init_per_zone_wmark_min();
 
-       if (onlined_pages) {
+       if (onlined_pages)
                kswapd_run(zone_to_nid(zone));
-               node_set_state(zone_to_nid(zone), N_HIGH_MEMORY);
-       }
 
        vm_total_pages = nr_free_pagecache_pages();
 
@@ -562,7 +563,7 @@ static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start)
         * to access not-initialized zonelist, build here.
         */
        mutex_lock(&zonelists_mutex);
-       build_all_zonelists(NULL);
+       build_all_zonelists(pgdat, NULL);
        mutex_unlock(&zonelists_mutex);
 
        return pgdat;
@@ -965,6 +966,9 @@ repeat:
 
        init_per_zone_wmark_min();
 
+       if (!populated_zone(zone))
+               zone_pcp_reset(zone);
+
        if (!node_present_pages(node)) {
                node_clear_state(node, N_HIGH_MEMORY);
                kswapd_stop(node);
index 1d771e4200d222eea46458bdd6089279a78ffeb5..bd92431d4c49a8e29f4b46d50d6d0e66c69098d5 100644 (file)
@@ -1602,8 +1602,14 @@ static unsigned interleave_nodes(struct mempolicy *policy)
  * task can change it's policy.  The system default policy requires no
  * such protection.
  */
-unsigned slab_node(struct mempolicy *policy)
+unsigned slab_node(void)
 {
+       struct mempolicy *policy;
+
+       if (in_interrupt())
+               return numa_node_id();
+
+       policy = current->mempolicy;
        if (!policy || policy->flags & MPOL_F_LOCAL)
                return numa_node_id();
 
index d9049811f3521bc690ff1535831b87dc3b682fe4..54990476c049b2fa60c5e740a0533ea70df1f856 100644 (file)
@@ -63,19 +63,21 @@ EXPORT_SYMBOL(mempool_destroy);
 mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn,
                                mempool_free_t *free_fn, void *pool_data)
 {
-       return  mempool_create_node(min_nr,alloc_fn,free_fn, pool_data,-1);
+       return mempool_create_node(min_nr,alloc_fn,free_fn, pool_data,
+                                  GFP_KERNEL, NUMA_NO_NODE);
 }
 EXPORT_SYMBOL(mempool_create);
 
 mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn,
-                       mempool_free_t *free_fn, void *pool_data, int node_id)
+                              mempool_free_t *free_fn, void *pool_data,
+                              gfp_t gfp_mask, int node_id)
 {
        mempool_t *pool;
-       pool = kmalloc_node(sizeof(*pool), GFP_KERNEL | __GFP_ZERO, node_id);
+       pool = kmalloc_node(sizeof(*pool), gfp_mask | __GFP_ZERO, node_id);
        if (!pool)
                return NULL;
        pool->elements = kmalloc_node(min_nr * sizeof(void *),
-                                       GFP_KERNEL, node_id);
+                                     gfp_mask, node_id);
        if (!pool->elements) {
                kfree(pool);
                return NULL;
@@ -93,7 +95,7 @@ mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn,
        while (pool->curr_nr < pool->min_nr) {
                void *element;
 
-               element = pool->alloc(GFP_KERNEL, pool->pool_data);
+               element = pool->alloc(gfp_mask, pool->pool_data);
                if (unlikely(!element)) {
                        mempool_destroy(pool);
                        return NULL;
index be26d5cbe56b34d63f8c8ac8b799782f9ef6424b..77ed2d7737056eea742eb29601373e7996b14432 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/memcontrol.h>
 #include <linux/syscalls.h>
 #include <linux/hugetlb.h>
+#include <linux/hugetlb_cgroup.h>
 #include <linux/gfp.h>
 
 #include <asm/tlbflush.h>
@@ -682,7 +683,6 @@ static int __unmap_and_move(struct page *page, struct page *newpage,
 {
        int rc = -EAGAIN;
        int remap_swapcache = 1;
-       int charge = 0;
        struct mem_cgroup *mem;
        struct anon_vma *anon_vma = NULL;
 
@@ -724,12 +724,7 @@ static int __unmap_and_move(struct page *page, struct page *newpage,
        }
 
        /* charge against new page */
-       charge = mem_cgroup_prepare_migration(page, newpage, &mem, GFP_KERNEL);
-       if (charge == -ENOMEM) {
-               rc = -ENOMEM;
-               goto unlock;
-       }
-       BUG_ON(charge);
+       mem_cgroup_prepare_migration(page, newpage, &mem);
 
        if (PageWriteback(page)) {
                /*
@@ -819,8 +814,7 @@ skip_unmap:
                put_anon_vma(anon_vma);
 
 uncharge:
-       if (!charge)
-               mem_cgroup_end_migration(mem, page, newpage, rc == 0);
+       mem_cgroup_end_migration(mem, page, newpage, rc == 0);
 unlock:
        unlock_page(page);
 out:
@@ -931,16 +925,13 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
 
        if (anon_vma)
                put_anon_vma(anon_vma);
-       unlock_page(hpage);
 
-out:
-       if (rc != -EAGAIN) {
-               list_del(&hpage->lru);
-               put_page(hpage);
-       }
+       if (!rc)
+               hugetlb_cgroup_migrate(hpage, new_hpage);
 
+       unlock_page(hpage);
+out:
        put_page(new_hpage);
-
        if (result) {
                if (rc)
                        *result = rc;
@@ -1016,48 +1007,32 @@ out:
        return nr_failed + retry;
 }
 
-int migrate_huge_pages(struct list_head *from,
-               new_page_t get_new_page, unsigned long private, bool offlining,
-               enum migrate_mode mode)
+int migrate_huge_page(struct page *hpage, new_page_t get_new_page,
+                     unsigned long private, bool offlining,
+                     enum migrate_mode mode)
 {
-       int retry = 1;
-       int nr_failed = 0;
-       int pass = 0;
-       struct page *page;
-       struct page *page2;
-       int rc;
-
-       for (pass = 0; pass < 10 && retry; pass++) {
-               retry = 0;
-
-               list_for_each_entry_safe(page, page2, from, lru) {
+       int pass, rc;
+
+       for (pass = 0; pass < 10; pass++) {
+               rc = unmap_and_move_huge_page(get_new_page,
+                                             private, hpage, pass > 2, offlining,
+                                             mode);
+               switch (rc) {
+               case -ENOMEM:
+                       goto out;
+               case -EAGAIN:
+                       /* try again */
                        cond_resched();
-
-                       rc = unmap_and_move_huge_page(get_new_page,
-                                       private, page, pass > 2, offlining,
-                                       mode);
-
-                       switch(rc) {
-                       case -ENOMEM:
-                               goto out;
-                       case -EAGAIN:
-                               retry++;
-                               break;
-                       case 0:
-                               break;
-                       default:
-                               /* Permanent failure */
-                               nr_failed++;
-                               break;
-                       }
+                       break;
+               case 0:
+                       goto out;
+               default:
+                       rc = -EIO;
+                       goto out;
                }
        }
-       rc = 0;
 out:
-       if (rc)
-               return rc;
-
-       return nr_failed + retry;
+       return rc;
 }
 
 #ifdef CONFIG_NUMA
index 3edfcdfa42d9f27a5238780065220ec3b4fc702a..e3e86914f11ad7d664ec73e08c98ee5670ef8e16 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -943,6 +943,8 @@ void vm_stat_account(struct mm_struct *mm, unsigned long flags,
        const unsigned long stack_flags
                = VM_STACK_FLAGS & (VM_GROWSUP|VM_GROWSDOWN);
 
+       mm->total_vm += pages;
+
        if (file) {
                mm->shared_vm += pages;
                if ((flags & (VM_EXEC|VM_WRITE)) == VM_EXEC)
@@ -1347,7 +1349,6 @@ munmap_back:
 out:
        perf_event_mmap(vma);
 
-       mm->total_vm += len >> PAGE_SHIFT;
        vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT);
        if (vm_flags & VM_LOCKED) {
                if (!mlock_vma_pages_range(vma, addr, addr + len))
@@ -1707,7 +1708,6 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns
                return -ENOMEM;
 
        /* Ok, everything looks good - let it rip */
-       mm->total_vm += grow;
        if (vma->vm_flags & VM_LOCKED)
                mm->locked_vm += grow;
        vm_stat_account(mm, vma->vm_flags, vma->vm_file, grow);
@@ -1889,7 +1889,6 @@ static void remove_vma_list(struct mm_struct *mm, struct vm_area_struct *vma)
 
                if (vma->vm_flags & VM_ACCOUNT)
                        nr_accounted += nrpages;
-               mm->total_vm -= nrpages;
                vm_stat_account(mm, vma->vm_flags, vma->vm_file, -nrpages);
                vma = remove_vma(vma);
        } while (vma);
@@ -2345,9 +2344,6 @@ int insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma)
             security_vm_enough_memory_mm(mm, vma_pages(vma)))
                return -ENOMEM;
 
-       if (vma->vm_file && uprobe_mmap(vma))
-               return -EINVAL;
-
        vma_link(mm, vma, prev, rb_link, rb_parent);
        return 0;
 }
@@ -2418,9 +2414,6 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
                        if (new_vma->vm_file) {
                                get_file(new_vma->vm_file);
 
-                               if (uprobe_mmap(new_vma))
-                                       goto out_free_mempol;
-
                                if (vma->vm_flags & VM_EXECUTABLE)
                                        added_exe_file_vma(mm);
                        }
index 9a611d3a18486031d63dda0585079f66c2f7499c..862b60822d9f8c4761448dee6165974da8405ecb 100644 (file)
 void __mmu_notifier_release(struct mm_struct *mm)
 {
        struct mmu_notifier *mn;
+       struct hlist_node *n;
+
+       /*
+        * RCU here will block mmu_notifier_unregister until
+        * ->release returns.
+        */
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(mn, n, &mm->mmu_notifier_mm->list, hlist)
+               /*
+                * if ->release runs before mmu_notifier_unregister it
+                * must be handled as it's the only way for the driver
+                * to flush all existing sptes and stop the driver
+                * from establishing any more sptes before all the
+                * pages in the mm are freed.
+                */
+               if (mn->ops->release)
+                       mn->ops->release(mn, mm);
+       rcu_read_unlock();
 
        spin_lock(&mm->mmu_notifier_mm->lock);
        while (unlikely(!hlist_empty(&mm->mmu_notifier_mm->list))) {
@@ -46,23 +64,6 @@ void __mmu_notifier_release(struct mm_struct *mm)
                 * mmu_notifier_unregister to return.
                 */
                hlist_del_init_rcu(&mn->hlist);
-               /*
-                * RCU here will block mmu_notifier_unregister until
-                * ->release returns.
-                */
-               rcu_read_lock();
-               spin_unlock(&mm->mmu_notifier_mm->lock);
-               /*
-                * if ->release runs before mmu_notifier_unregister it
-                * must be handled as it's the only way for the driver
-                * to flush all existing sptes and stop the driver
-                * from establishing any more sptes before all the
-                * pages in the mm are freed.
-                */
-               if (mn->ops->release)
-                       mn->ops->release(mn, mm);
-               rcu_read_unlock();
-               spin_lock(&mm->mmu_notifier_mm->lock);
        }
        spin_unlock(&mm->mmu_notifier_mm->lock);
 
@@ -284,16 +285,13 @@ void mmu_notifier_unregister(struct mmu_notifier *mn, struct mm_struct *mm)
 {
        BUG_ON(atomic_read(&mm->mm_count) <= 0);
 
-       spin_lock(&mm->mmu_notifier_mm->lock);
        if (!hlist_unhashed(&mn->hlist)) {
-               hlist_del_rcu(&mn->hlist);
-
                /*
                 * RCU here will force exit_mmap to wait ->release to finish
                 * before freeing the pages.
                 */
                rcu_read_lock();
-               spin_unlock(&mm->mmu_notifier_mm->lock);
+
                /*
                 * exit_mmap will block in mmu_notifier_release to
                 * guarantee ->release is called before freeing the
@@ -302,8 +300,11 @@ void mmu_notifier_unregister(struct mmu_notifier *mn, struct mm_struct *mm)
                if (mn->ops->release)
                        mn->ops->release(mn, mm);
                rcu_read_unlock();
-       } else
+
+               spin_lock(&mm->mmu_notifier_mm->lock);
+               hlist_del_rcu(&mn->hlist);
                spin_unlock(&mm->mmu_notifier_mm->lock);
+       }
 
        /*
         * Wait any running method to finish, of course including
index 6830eab5bf09c5c5432801ed7ad23d4bad054779..3cef80f6ac79ff2e8b5fef7bcd373679e91df2c1 100644 (file)
@@ -96,7 +96,7 @@ void lruvec_init(struct lruvec *lruvec, struct zone *zone)
        for_each_lru(lru)
                INIT_LIST_HEAD(&lruvec->lists[lru]);
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
        lruvec->zone = zone;
 #endif
 }
index 21fed202ddad865bb3ee70d07d8ebce17fd37493..cc06d0e48d050dc0a2ec47cf62fd8d3b22b4a2be 100644 (file)
@@ -260,7 +260,6 @@ static unsigned long move_vma(struct vm_area_struct *vma,
         * If this were a serious issue, we'd add a flag to do_munmap().
         */
        hiwater_vm = mm->hiwater_vm;
-       mm->total_vm += new_len >> PAGE_SHIFT;
        vm_stat_account(mm, vma->vm_flags, vma->vm_file, new_len>>PAGE_SHIFT);
 
        if (do_munmap(mm, old_addr, old_len) < 0) {
@@ -497,7 +496,6 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
                                goto out;
                        }
 
-                       mm->total_vm += pages;
                        vm_stat_account(mm, vma->vm_flags, vma->vm_file, pages);
                        if (vma->vm_flags & VM_LOCKED) {
                                mm->locked_vm += pages;
index ac300c99baf644824f9c6532627a99fdb26020b1..198600861638b9833ef6603198f980bf2b9fe67f 100644 (file)
@@ -288,76 +288,93 @@ static enum oom_constraint constrained_alloc(struct zonelist *zonelist,
 }
 #endif
 
+enum oom_scan_t oom_scan_process_thread(struct task_struct *task,
+               unsigned long totalpages, const nodemask_t *nodemask,
+               bool force_kill)
+{
+       if (task->exit_state)
+               return OOM_SCAN_CONTINUE;
+       if (oom_unkillable_task(task, NULL, nodemask))
+               return OOM_SCAN_CONTINUE;
+
+       /*
+        * This task already has access to memory reserves and is being killed.
+        * Don't allow any other task to have access to the reserves.
+        */
+       if (test_tsk_thread_flag(task, TIF_MEMDIE)) {
+               if (unlikely(frozen(task)))
+                       __thaw_task(task);
+               if (!force_kill)
+                       return OOM_SCAN_ABORT;
+       }
+       if (!task->mm)
+               return OOM_SCAN_CONTINUE;
+
+       if (task->flags & PF_EXITING) {
+               /*
+                * If task is current and is in the process of releasing memory,
+                * allow the "kill" to set TIF_MEMDIE, which will allow it to
+                * access memory reserves.  Otherwise, it may stall forever.
+                *
+                * The iteration isn't broken here, however, in case other
+                * threads are found to have already been oom killed.
+                */
+               if (task == current)
+                       return OOM_SCAN_SELECT;
+               else if (!force_kill) {
+                       /*
+                        * If this task is not being ptraced on exit, then wait
+                        * for it to finish before killing some other task
+                        * unnecessarily.
+                        */
+                       if (!(task->group_leader->ptrace & PT_TRACE_EXIT))
+                               return OOM_SCAN_ABORT;
+               }
+       }
+       return OOM_SCAN_OK;
+}
+
 /*
  * Simple selection loop. We chose the process with the highest
- * number of 'points'. We expect the caller will lock the tasklist.
+ * number of 'points'.
  *
  * (not docbooked, we don't want this one cluttering up the manual)
  */
 static struct task_struct *select_bad_process(unsigned int *ppoints,
-               unsigned long totalpages, struct mem_cgroup *memcg,
-               const nodemask_t *nodemask, bool force_kill)
+               unsigned long totalpages, const nodemask_t *nodemask,
+               bool force_kill)
 {
        struct task_struct *g, *p;
        struct task_struct *chosen = NULL;
        unsigned long chosen_points = 0;
 
+       rcu_read_lock();
        do_each_thread(g, p) {
                unsigned int points;
 
-               if (p->exit_state)
-                       continue;
-               if (oom_unkillable_task(p, memcg, nodemask))
-                       continue;
-
-               /*
-                * This task already has access to memory reserves and is
-                * being killed. Don't allow any other task access to the
-                * memory reserve.
-                *
-                * Note: this may have a chance of deadlock if it gets
-                * blocked waiting for another task which itself is waiting
-                * for memory. Is there a better alternative?
-                */
-               if (test_tsk_thread_flag(p, TIF_MEMDIE)) {
-                       if (unlikely(frozen(p)))
-                               __thaw_task(p);
-                       if (!force_kill)
-                               return ERR_PTR(-1UL);
-               }
-               if (!p->mm)
+               switch (oom_scan_process_thread(p, totalpages, nodemask,
+                                               force_kill)) {
+               case OOM_SCAN_SELECT:
+                       chosen = p;
+                       chosen_points = ULONG_MAX;
+                       /* fall through */
+               case OOM_SCAN_CONTINUE:
                        continue;
-
-               if (p->flags & PF_EXITING) {
-                       /*
-                        * If p is the current task and is in the process of
-                        * releasing memory, we allow the "kill" to set
-                        * TIF_MEMDIE, which will allow it to gain access to
-                        * memory reserves.  Otherwise, it may stall forever.
-                        *
-                        * The loop isn't broken here, however, in case other
-                        * threads are found to have already been oom killed.
-                        */
-                       if (p == current) {
-                               chosen = p;
-                               chosen_points = ULONG_MAX;
-                       } else if (!force_kill) {
-                               /*
-                                * If this task is not being ptraced on exit,
-                                * then wait for it to finish before killing
-                                * some other task unnecessarily.
-                                */
-                               if (!(p->group_leader->ptrace & PT_TRACE_EXIT))
-                                       return ERR_PTR(-1UL);
-                       }
-               }
-
-               points = oom_badness(p, memcg, nodemask, totalpages);
+               case OOM_SCAN_ABORT:
+                       rcu_read_unlock();
+                       return ERR_PTR(-1UL);
+               case OOM_SCAN_OK:
+                       break;
+               };
+               points = oom_badness(p, NULL, nodemask, totalpages);
                if (points > chosen_points) {
                        chosen = p;
                        chosen_points = points;
                }
        } while_each_thread(g, p);
+       if (chosen)
+               get_task_struct(chosen);
+       rcu_read_unlock();
 
        *ppoints = chosen_points * 1000 / totalpages;
        return chosen;
@@ -371,17 +388,16 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
  * Dumps the current memory state of all eligible tasks.  Tasks not in the same
  * memcg, not in the same cpuset, or bound to a disjoint set of mempolicy nodes
  * are not shown.
- * State information includes task's pid, uid, tgid, vm size, rss, cpu, oom_adj
- * value, oom_score_adj value, and name.
- *
- * Call with tasklist_lock read-locked.
+ * State information includes task's pid, uid, tgid, vm size, rss, nr_ptes,
+ * swapents, oom_score_adj value, and name.
  */
 static void dump_tasks(const struct mem_cgroup *memcg, const nodemask_t *nodemask)
 {
        struct task_struct *p;
        struct task_struct *task;
 
-       pr_info("[ pid ]   uid  tgid total_vm      rss cpu oom_adj oom_score_adj name\n");
+       pr_info("[ pid ]   uid  tgid total_vm      rss nr_ptes swapents oom_score_adj name\n");
+       rcu_read_lock();
        for_each_process(p) {
                if (oom_unkillable_task(p, memcg, nodemask))
                        continue;
@@ -396,13 +412,15 @@ static void dump_tasks(const struct mem_cgroup *memcg, const nodemask_t *nodemas
                        continue;
                }
 
-               pr_info("[%5d] %5d %5d %8lu %8lu %3u     %3d         %5d %s\n",
+               pr_info("[%5d] %5d %5d %8lu %8lu %7lu %8lu         %5d %s\n",
                        task->pid, from_kuid(&init_user_ns, task_uid(task)),
                        task->tgid, task->mm->total_vm, get_mm_rss(task->mm),
-                       task_cpu(task), task->signal->oom_adj,
+                       task->mm->nr_ptes,
+                       get_mm_counter(task->mm, MM_SWAPENTS),
                        task->signal->oom_score_adj, task->comm);
                task_unlock(task);
        }
+       rcu_read_unlock();
 }
 
 static void dump_header(struct task_struct *p, gfp_t gfp_mask, int order,
@@ -423,10 +441,14 @@ static void dump_header(struct task_struct *p, gfp_t gfp_mask, int order,
 }
 
 #define K(x) ((x) << (PAGE_SHIFT-10))
-static void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
-                            unsigned int points, unsigned long totalpages,
-                            struct mem_cgroup *memcg, nodemask_t *nodemask,
-                            const char *message)
+/*
+ * Must be called while holding a reference to p, which will be released upon
+ * returning.
+ */
+void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
+                     unsigned int points, unsigned long totalpages,
+                     struct mem_cgroup *memcg, nodemask_t *nodemask,
+                     const char *message)
 {
        struct task_struct *victim = p;
        struct task_struct *child;
@@ -442,6 +464,7 @@ static void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
         */
        if (p->flags & PF_EXITING) {
                set_tsk_thread_flag(p, TIF_MEMDIE);
+               put_task_struct(p);
                return;
        }
 
@@ -459,6 +482,7 @@ static void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
         * parent.  This attempts to lose the minimal amount of work done while
         * still freeing memory.
         */
+       read_lock(&tasklist_lock);
        do {
                list_for_each_entry(child, &t->children, sibling) {
                        unsigned int child_points;
@@ -471,15 +495,26 @@ static void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
                        child_points = oom_badness(child, memcg, nodemask,
                                                                totalpages);
                        if (child_points > victim_points) {
+                               put_task_struct(victim);
                                victim = child;
                                victim_points = child_points;
+                               get_task_struct(victim);
                        }
                }
        } while_each_thread(p, t);
+       read_unlock(&tasklist_lock);
 
-       victim = find_lock_task_mm(victim);
-       if (!victim)
+       rcu_read_lock();
+       p = find_lock_task_mm(victim);
+       if (!p) {
+               rcu_read_unlock();
+               put_task_struct(victim);
                return;
+       } else if (victim != p) {
+               get_task_struct(p);
+               put_task_struct(victim);
+               victim = p;
+       }
 
        /* mm cannot safely be dereferenced after task_unlock(victim) */
        mm = victim->mm;
@@ -510,17 +545,19 @@ static void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
                        task_unlock(p);
                        do_send_sig_info(SIGKILL, SEND_SIG_FORCED, p, true);
                }
+       rcu_read_unlock();
 
        set_tsk_thread_flag(victim, TIF_MEMDIE);
        do_send_sig_info(SIGKILL, SEND_SIG_FORCED, victim, true);
+       put_task_struct(victim);
 }
 #undef K
 
 /*
  * Determines whether the kernel must panic because of the panic_on_oom sysctl.
  */
-static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask,
-                               int order, const nodemask_t *nodemask)
+void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask,
+                       int order, const nodemask_t *nodemask)
 {
        if (likely(!sysctl_panic_on_oom))
                return;
@@ -533,42 +570,11 @@ static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask,
                if (constraint != CONSTRAINT_NONE)
                        return;
        }
-       read_lock(&tasklist_lock);
        dump_header(NULL, gfp_mask, order, NULL, nodemask);
-       read_unlock(&tasklist_lock);
        panic("Out of memory: %s panic_on_oom is enabled\n",
                sysctl_panic_on_oom == 2 ? "compulsory" : "system-wide");
 }
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
-void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
-                             int order)
-{
-       unsigned long limit;
-       unsigned int points = 0;
-       struct task_struct *p;
-
-       /*
-        * If current has a pending SIGKILL, then automatically select it.  The
-        * goal is to allow it to allocate so that it may quickly exit and free
-        * its memory.
-        */
-       if (fatal_signal_pending(current)) {
-               set_thread_flag(TIF_MEMDIE);
-               return;
-       }
-
-       check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL);
-       limit = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT ? : 1;
-       read_lock(&tasklist_lock);
-       p = select_bad_process(&points, limit, memcg, NULL, false);
-       if (p && PTR_ERR(p) != -1UL)
-               oom_kill_process(p, gfp_mask, order, points, limit, memcg, NULL,
-                                "Memory cgroup out of memory");
-       read_unlock(&tasklist_lock);
-}
-#endif
-
 static BLOCKING_NOTIFIER_HEAD(oom_notify_list);
 
 int register_oom_notifier(struct notifier_block *nb)
@@ -690,7 +696,7 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask,
        struct task_struct *p;
        unsigned long totalpages;
        unsigned long freed = 0;
-       unsigned int points;
+       unsigned int uninitialized_var(points);
        enum oom_constraint constraint = CONSTRAINT_NONE;
        int killed = 0;
 
@@ -718,22 +724,20 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask,
        mpol_mask = (constraint == CONSTRAINT_MEMORY_POLICY) ? nodemask : NULL;
        check_panic_on_oom(constraint, gfp_mask, order, mpol_mask);
 
-       read_lock(&tasklist_lock);
-       if (sysctl_oom_kill_allocating_task &&
+       if (sysctl_oom_kill_allocating_task && current->mm &&
            !oom_unkillable_task(current, NULL, nodemask) &&
-           current->mm) {
+           current->signal->oom_score_adj != OOM_SCORE_ADJ_MIN) {
+               get_task_struct(current);
                oom_kill_process(current, gfp_mask, order, 0, totalpages, NULL,
                                 nodemask,
                                 "Out of memory (oom_kill_allocating_task)");
                goto out;
        }
 
-       p = select_bad_process(&points, totalpages, NULL, mpol_mask,
-                              force_kill);
+       p = select_bad_process(&points, totalpages, mpol_mask, force_kill);
        /* Found nothing?!?! Either we hang forever, or we panic. */
        if (!p) {
                dump_header(NULL, gfp_mask, order, NULL, mpol_mask);
-               read_unlock(&tasklist_lock);
                panic("Out of memory and no killable processes...\n");
        }
        if (PTR_ERR(p) != -1UL) {
@@ -742,14 +746,12 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask,
                killed = 1;
        }
 out:
-       read_unlock(&tasklist_lock);
-
        /*
-        * Give "p" a good chance of killing itself before we
-        * retry to allocate memory unless "p" is current
+        * Give the killed threads a good chance of exiting before trying to
+        * allocate memory again.
         */
-       if (killed && !test_thread_flag(TIF_MEMDIE))
-               schedule_timeout_uninterruptible(1);
+       if (killed)
+               schedule_timeout_killable(1);
 }
 
 /*
@@ -764,6 +766,5 @@ void pagefault_out_of_memory(void)
                out_of_memory(NULL, 0, 0, NULL, false);
                clear_system_oom();
        }
-       if (!test_thread_flag(TIF_MEMDIE))
-               schedule_timeout_uninterruptible(1);
+       schedule_timeout_killable(1);
 }
index 93d8d2f7108ccb8b3c5a73de42361f23d98049c1..e5363f34e0250656c357d965a2e8949577cfb607 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/syscalls.h>
 #include <linux/buffer_head.h> /* __set_page_dirty_buffers */
 #include <linux/pagevec.h>
+#include <linux/timer.h>
 #include <trace/events/writeback.h>
 
 /*
@@ -135,7 +136,20 @@ unsigned long global_dirty_limit;
  * measured in page writeback completions.
  *
  */
-static struct prop_descriptor vm_completions;
+static struct fprop_global writeout_completions;
+
+static void writeout_period(unsigned long t);
+/* Timer for aging of writeout_completions */
+static struct timer_list writeout_period_timer =
+               TIMER_DEFERRED_INITIALIZER(writeout_period, 0, 0);
+static unsigned long writeout_period_time = 0;
+
+/*
+ * Length of period for aging writeout fractions of bdis. This is an
+ * arbitrarily chosen number. The longer the period, the slower fractions will
+ * reflect changes in current writeout rate.
+ */
+#define VM_COMPLETIONS_PERIOD_LEN (3*HZ)
 
 /*
  * Work out the current dirty-memory clamping and background writeout
@@ -322,34 +336,6 @@ bool zone_dirty_ok(struct zone *zone)
               zone_page_state(zone, NR_WRITEBACK) <= limit;
 }
 
-/*
- * couple the period to the dirty_ratio:
- *
- *   period/2 ~ roundup_pow_of_two(dirty limit)
- */
-static int calc_period_shift(void)
-{
-       unsigned long dirty_total;
-
-       if (vm_dirty_bytes)
-               dirty_total = vm_dirty_bytes / PAGE_SIZE;
-       else
-               dirty_total = (vm_dirty_ratio * global_dirtyable_memory()) /
-                               100;
-       return 2 + ilog2(dirty_total - 1);
-}
-
-/*
- * update the period when the dirty threshold changes.
- */
-static void update_completion_period(void)
-{
-       int shift = calc_period_shift();
-       prop_change_shift(&vm_completions, shift);
-
-       writeback_set_ratelimit();
-}
-
 int dirty_background_ratio_handler(struct ctl_table *table, int write,
                void __user *buffer, size_t *lenp,
                loff_t *ppos)
@@ -383,7 +369,7 @@ int dirty_ratio_handler(struct ctl_table *table, int write,
 
        ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
        if (ret == 0 && write && vm_dirty_ratio != old_ratio) {
-               update_completion_period();
+               writeback_set_ratelimit();
                vm_dirty_bytes = 0;
        }
        return ret;
@@ -398,12 +384,21 @@ int dirty_bytes_handler(struct ctl_table *table, int write,
 
        ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
        if (ret == 0 && write && vm_dirty_bytes != old_bytes) {
-               update_completion_period();
+               writeback_set_ratelimit();
                vm_dirty_ratio = 0;
        }
        return ret;
 }
 
+static unsigned long wp_next_time(unsigned long cur_time)
+{
+       cur_time += VM_COMPLETIONS_PERIOD_LEN;
+       /* 0 has a special meaning... */
+       if (!cur_time)
+               return 1;
+       return cur_time;
+}
+
 /*
  * Increment the BDI's writeout completion count and the global writeout
  * completion count. Called from test_clear_page_writeback().
@@ -411,8 +406,19 @@ int dirty_bytes_handler(struct ctl_table *table, int write,
 static inline void __bdi_writeout_inc(struct backing_dev_info *bdi)
 {
        __inc_bdi_stat(bdi, BDI_WRITTEN);
-       __prop_inc_percpu_max(&vm_completions, &bdi->completions,
-                             bdi->max_prop_frac);
+       __fprop_inc_percpu_max(&writeout_completions, &bdi->completions,
+                              bdi->max_prop_frac);
+       /* First event after period switching was turned off? */
+       if (!unlikely(writeout_period_time)) {
+               /*
+                * We can race with other __bdi_writeout_inc calls here but
+                * it does not cause any harm since the resulting time when
+                * timer will fire and what is in writeout_period_time will be
+                * roughly the same.
+                */
+               writeout_period_time = wp_next_time(jiffies);
+               mod_timer(&writeout_period_timer, writeout_period_time);
+       }
 }
 
 void bdi_writeout_inc(struct backing_dev_info *bdi)
@@ -431,10 +437,32 @@ EXPORT_SYMBOL_GPL(bdi_writeout_inc);
 static void bdi_writeout_fraction(struct backing_dev_info *bdi,
                long *numerator, long *denominator)
 {
-       prop_fraction_percpu(&vm_completions, &bdi->completions,
+       fprop_fraction_percpu(&writeout_completions, &bdi->completions,
                                numerator, denominator);
 }
 
+/*
+ * On idle system, we can be called long after we scheduled because we use
+ * deferred timers so count with missed periods.
+ */
+static void writeout_period(unsigned long t)
+{
+       int miss_periods = (jiffies - writeout_period_time) /
+                                                VM_COMPLETIONS_PERIOD_LEN;
+
+       if (fprop_new_period(&writeout_completions, miss_periods + 1)) {
+               writeout_period_time = wp_next_time(writeout_period_time +
+                               miss_periods * VM_COMPLETIONS_PERIOD_LEN);
+               mod_timer(&writeout_period_timer, writeout_period_time);
+       } else {
+               /*
+                * Aging has zeroed all fractions. Stop wasting CPU on period
+                * updates.
+                */
+               writeout_period_time = 0;
+       }
+}
+
 /*
  * bdi_min_ratio keeps the sum of the minimum dirty shares of all
  * registered backing devices, which, for obvious reasons, can not
@@ -475,7 +503,7 @@ int bdi_set_max_ratio(struct backing_dev_info *bdi, unsigned max_ratio)
                ret = -EINVAL;
        } else {
                bdi->max_ratio = max_ratio;
-               bdi->max_prop_frac = (PROP_FRAC_BASE * max_ratio) / 100;
+               bdi->max_prop_frac = (FPROP_FRAC_BASE * max_ratio) / 100;
        }
        spin_unlock_bh(&bdi_lock);
 
@@ -918,7 +946,7 @@ static void bdi_update_dirty_ratelimit(struct backing_dev_info *bdi,
         *      bdi->dirty_ratelimit = balanced_dirty_ratelimit;
         *
         * However to get a more stable dirty_ratelimit, the below elaborated
-        * code makes use of task_ratelimit to filter out sigular points and
+        * code makes use of task_ratelimit to filter out singular points and
         * limit the step size.
         *
         * The below code essentially only uses the relative value of
@@ -941,7 +969,7 @@ static void bdi_update_dirty_ratelimit(struct backing_dev_info *bdi,
         * feel and care are stable dirty rate and small position error.
         *
         * |task_ratelimit - dirty_ratelimit| is used to limit the step size
-        * and filter out the sigular points of balanced_dirty_ratelimit. Which
+        * and filter out the singular points of balanced_dirty_ratelimit. Which
         * keeps jumping around randomly and can even leap far away at times
         * due to the small 200ms estimation period of dirty_rate (we want to
         * keep that period small to reduce time lags).
@@ -1606,13 +1634,10 @@ static struct notifier_block __cpuinitdata ratelimit_nb = {
  */
 void __init page_writeback_init(void)
 {
-       int shift;
-
        writeback_set_ratelimit();
        register_cpu_notifier(&ratelimit_nb);
 
-       shift = calc_period_shift();
-       prop_descriptor_init(&vm_completions, shift);
+       fprop_global_init(&writeout_completions);
 }
 
 /**
index 4a4f9219683f63d8594c143fa24f27d4daa6a8ff..889532b8e6c10342d7355c8868cf5812ec8de500 100644 (file)
@@ -51,7 +51,6 @@
 #include <linux/page_cgroup.h>
 #include <linux/debugobjects.h>
 #include <linux/kmemleak.h>
-#include <linux/memory.h>
 #include <linux/compaction.h>
 #include <trace/events/kmem.h>
 #include <linux/ftrace_event.h>
@@ -219,7 +218,12 @@ EXPORT_SYMBOL(nr_online_nodes);
 
 int page_group_by_mobility_disabled __read_mostly;
 
-static void set_pageblock_migratetype(struct page *page, int migratetype)
+/*
+ * NOTE:
+ * Don't use set_pageblock_migratetype(page, MIGRATE_ISOLATE) directly.
+ * Instead, use {un}set_pageblock_isolate.
+ */
+void set_pageblock_migratetype(struct page *page, int migratetype)
 {
 
        if (unlikely(page_group_by_mobility_disabled))
@@ -954,7 +958,7 @@ static int move_freepages(struct zone *zone,
        return pages_moved;
 }
 
-static int move_freepages_block(struct zone *zone, struct page *page,
+int move_freepages_block(struct zone *zone, struct page *page,
                                int migratetype)
 {
        unsigned long start_pfn, end_pfn;
@@ -1158,8 +1162,10 @@ void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp)
                to_drain = pcp->batch;
        else
                to_drain = pcp->count;
-       free_pcppages_bulk(zone, to_drain, pcp);
-       pcp->count -= to_drain;
+       if (to_drain > 0) {
+               free_pcppages_bulk(zone, to_drain, pcp);
+               pcp->count -= to_drain;
+       }
        local_irq_restore(flags);
 }
 #endif
@@ -1529,16 +1535,16 @@ static int __init setup_fail_page_alloc(char *str)
 }
 __setup("fail_page_alloc=", setup_fail_page_alloc);
 
-static int should_fail_alloc_page(gfp_t gfp_mask, unsigned int order)
+static bool should_fail_alloc_page(gfp_t gfp_mask, unsigned int order)
 {
        if (order < fail_page_alloc.min_order)
-               return 0;
+               return false;
        if (gfp_mask & __GFP_NOFAIL)
-               return 0;
+               return false;
        if (fail_page_alloc.ignore_gfp_highmem && (gfp_mask & __GFP_HIGHMEM))
-               return 0;
+               return false;
        if (fail_page_alloc.ignore_gfp_wait && (gfp_mask & __GFP_WAIT))
-               return 0;
+               return false;
 
        return should_fail(&fail_page_alloc.attr, 1 << order);
 }
@@ -1578,9 +1584,9 @@ late_initcall(fail_page_alloc_debugfs);
 
 #else /* CONFIG_FAIL_PAGE_ALLOC */
 
-static inline int should_fail_alloc_page(gfp_t gfp_mask, unsigned int order)
+static inline bool should_fail_alloc_page(gfp_t gfp_mask, unsigned int order)
 {
-       return 0;
+       return false;
 }
 
 #endif /* CONFIG_FAIL_PAGE_ALLOC */
@@ -1594,6 +1600,7 @@ static bool __zone_watermark_ok(struct zone *z, int order, unsigned long mark,
 {
        /* free_pages my go negative - that's OK */
        long min = mark;
+       long lowmem_reserve = z->lowmem_reserve[classzone_idx];
        int o;
 
        free_pages -= (1 << order) - 1;
@@ -1602,7 +1609,7 @@ static bool __zone_watermark_ok(struct zone *z, int order, unsigned long mark,
        if (alloc_flags & ALLOC_HARDER)
                min -= min / 4;
 
-       if (free_pages <= min + z->lowmem_reserve[classzone_idx])
+       if (free_pages <= min + lowmem_reserve)
                return false;
        for (o = 0; o < order; o++) {
                /* At the next order, this order's pages become unavailable */
@@ -1617,6 +1624,20 @@ static bool __zone_watermark_ok(struct zone *z, int order, unsigned long mark,
        return true;
 }
 
+#ifdef CONFIG_MEMORY_ISOLATION
+static inline unsigned long nr_zone_isolate_freepages(struct zone *zone)
+{
+       if (unlikely(zone->nr_pageblock_isolate))
+               return zone->nr_pageblock_isolate * pageblock_nr_pages;
+       return 0;
+}
+#else
+static inline unsigned long nr_zone_isolate_freepages(struct zone *zone)
+{
+       return 0;
+}
+#endif
+
 bool zone_watermark_ok(struct zone *z, int order, unsigned long mark,
                      int classzone_idx, int alloc_flags)
 {
@@ -1632,6 +1653,14 @@ bool zone_watermark_ok_safe(struct zone *z, int order, unsigned long mark,
        if (z->percpu_drift_mark && free_pages < z->percpu_drift_mark)
                free_pages = zone_page_state_snapshot(z, NR_FREE_PAGES);
 
+       /*
+        * If the zone has MIGRATE_ISOLATE type free pages, we should consider
+        * it.  nr_zone_isolate_freepages is never accurate so kswapd might not
+        * sleep although it could do so.  But this is more desirable for memory
+        * hotplug than sleeping which can cause a livelock in the direct
+        * reclaim path.
+        */
+       free_pages -= nr_zone_isolate_freepages(z);
        return __zone_watermark_ok(z, order, mark, classzone_idx, alloc_flags,
                                                                free_pages);
 }
@@ -2087,8 +2116,8 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
 
                page = get_page_from_freelist(gfp_mask, nodemask,
                                order, zonelist, high_zoneidx,
-                               alloc_flags, preferred_zone,
-                               migratetype);
+                               alloc_flags & ~ALLOC_NO_WATERMARKS,
+                               preferred_zone, migratetype);
                if (page) {
                        preferred_zone->compact_considered = 0;
                        preferred_zone->compact_defer_shift = 0;
@@ -2180,8 +2209,8 @@ __alloc_pages_direct_reclaim(gfp_t gfp_mask, unsigned int order,
 retry:
        page = get_page_from_freelist(gfp_mask, nodemask, order,
                                        zonelist, high_zoneidx,
-                                       alloc_flags, preferred_zone,
-                                       migratetype);
+                                       alloc_flags & ~ALLOC_NO_WATERMARKS,
+                                       preferred_zone, migratetype);
 
        /*
         * If an allocation failed after direct reclaim, it could be because
@@ -2265,15 +2294,24 @@ gfp_to_alloc_flags(gfp_t gfp_mask)
                alloc_flags |= ALLOC_HARDER;
 
        if (likely(!(gfp_mask & __GFP_NOMEMALLOC))) {
-               if (!in_interrupt() &&
-                   ((current->flags & PF_MEMALLOC) ||
-                    unlikely(test_thread_flag(TIF_MEMDIE))))
+               if (gfp_mask & __GFP_MEMALLOC)
+                       alloc_flags |= ALLOC_NO_WATERMARKS;
+               else if (in_serving_softirq() && (current->flags & PF_MEMALLOC))
+                       alloc_flags |= ALLOC_NO_WATERMARKS;
+               else if (!in_interrupt() &&
+                               ((current->flags & PF_MEMALLOC) ||
+                                unlikely(test_thread_flag(TIF_MEMDIE))))
                        alloc_flags |= ALLOC_NO_WATERMARKS;
        }
 
        return alloc_flags;
 }
 
+bool gfp_pfmemalloc_allowed(gfp_t gfp_mask)
+{
+       return !!(gfp_to_alloc_flags(gfp_mask) & ALLOC_NO_WATERMARKS);
+}
+
 static inline struct page *
 __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
        struct zonelist *zonelist, enum zone_type high_zoneidx,
@@ -2340,11 +2378,27 @@ rebalance:
 
        /* Allocate without watermarks if the context allows */
        if (alloc_flags & ALLOC_NO_WATERMARKS) {
+               /*
+                * Ignore mempolicies if ALLOC_NO_WATERMARKS on the grounds
+                * the allocation is high priority and these type of
+                * allocations are system rather than user orientated
+                */
+               zonelist = node_zonelist(numa_node_id(), gfp_mask);
+
                page = __alloc_pages_high_priority(gfp_mask, order,
                                zonelist, high_zoneidx, nodemask,
                                preferred_zone, migratetype);
-               if (page)
+               if (page) {
+                       /*
+                        * page->pfmemalloc is set when ALLOC_NO_WATERMARKS was
+                        * necessary to allocate the page. The expectation is
+                        * that the caller is taking steps that will free more
+                        * memory. The caller should avoid the page being used
+                        * for !PFMEMALLOC purposes.
+                        */
+                       page->pfmemalloc = true;
                        goto got_pg;
+               }
        }
 
        /* Atomic allocations - we can't balance anything */
@@ -2463,8 +2517,8 @@ nopage:
 got_pg:
        if (kmemcheck_enabled)
                kmemcheck_pagealloc_alloc(page, order, gfp_mask);
-       return page;
 
+       return page;
 }
 
 /*
@@ -2515,6 +2569,8 @@ retry_cpuset:
                page = __alloc_pages_slowpath(gfp_mask, order,
                                zonelist, high_zoneidx, nodemask,
                                preferred_zone, migratetype);
+       else
+               page->pfmemalloc = false;
 
        trace_mm_page_alloc(page, order, gfp_mask, migratetype);
 
@@ -3030,7 +3086,7 @@ int numa_zonelist_order_handler(ctl_table *table, int write,
                        user_zonelist_order = oldval;
                } else if (oldval != user_zonelist_order) {
                        mutex_lock(&zonelists_mutex);
-                       build_all_zonelists(NULL);
+                       build_all_zonelists(NULL, NULL);
                        mutex_unlock(&zonelists_mutex);
                }
        }
@@ -3409,14 +3465,21 @@ static void setup_zone_pageset(struct zone *zone);
 DEFINE_MUTEX(zonelists_mutex);
 
 /* return values int ....just for stop_machine() */
-static __init_refok int __build_all_zonelists(void *data)
+static int __build_all_zonelists(void *data)
 {
        int nid;
        int cpu;
+       pg_data_t *self = data;
 
 #ifdef CONFIG_NUMA
        memset(node_load, 0, sizeof(node_load));
 #endif
+
+       if (self && !node_online(self->node_id)) {
+               build_zonelists(self);
+               build_zonelist_cache(self);
+       }
+
        for_each_online_node(nid) {
                pg_data_t *pgdat = NODE_DATA(nid);
 
@@ -3461,7 +3524,7 @@ static __init_refok int __build_all_zonelists(void *data)
  * Called with zonelists_mutex held always
  * unless system_state == SYSTEM_BOOTING.
  */
-void __ref build_all_zonelists(void *data)
+void __ref build_all_zonelists(pg_data_t *pgdat, struct zone *zone)
 {
        set_zonelist_order();
 
@@ -3473,10 +3536,10 @@ void __ref build_all_zonelists(void *data)
                /* we have to stop all cpus to guarantee there is no user
                   of zonelist */
 #ifdef CONFIG_MEMORY_HOTPLUG
-               if (data)
-                       setup_zone_pageset((struct zone *)data);
+               if (zone)
+                       setup_zone_pageset(zone);
 #endif
-               stop_machine(__build_all_zonelists, NULL, NULL);
+               stop_machine(__build_all_zonelists, pgdat, NULL);
                /* cpuset refresh routine should be here */
        }
        vm_total_pages = nr_free_pagecache_pages();
@@ -3746,7 +3809,7 @@ static void __meminit zone_init_free_lists(struct zone *zone)
        memmap_init_zone((size), (nid), (zone), (start_pfn), MEMMAP_EARLY)
 #endif
 
-static int zone_batchsize(struct zone *zone)
+static int __meminit zone_batchsize(struct zone *zone)
 {
 #ifdef CONFIG_MMU
        int batch;
@@ -3828,7 +3891,7 @@ static void setup_pagelist_highmark(struct per_cpu_pageset *p,
                pcp->batch = PAGE_SHIFT * 8;
 }
 
-static void setup_zone_pageset(struct zone *zone)
+static void __meminit setup_zone_pageset(struct zone *zone)
 {
        int cpu;
 
@@ -3901,32 +3964,6 @@ int zone_wait_table_init(struct zone *zone, unsigned long zone_size_pages)
        return 0;
 }
 
-static int __zone_pcp_update(void *data)
-{
-       struct zone *zone = data;
-       int cpu;
-       unsigned long batch = zone_batchsize(zone), flags;
-
-       for_each_possible_cpu(cpu) {
-               struct per_cpu_pageset *pset;
-               struct per_cpu_pages *pcp;
-
-               pset = per_cpu_ptr(zone->pageset, cpu);
-               pcp = &pset->pcp;
-
-               local_irq_save(flags);
-               free_pcppages_bulk(zone, pcp->count, pcp);
-               setup_pageset(pset, batch);
-               local_irq_restore(flags);
-       }
-       return 0;
-}
-
-void zone_pcp_update(struct zone *zone)
-{
-       stop_machine(__zone_pcp_update, zone, NULL);
-}
-
 static __meminit void zone_pcp_init(struct zone *zone)
 {
        /*
@@ -3942,7 +3979,7 @@ static __meminit void zone_pcp_init(struct zone *zone)
                                         zone_batchsize(zone));
 }
 
-__meminit int init_currently_empty_zone(struct zone *zone,
+int __meminit init_currently_empty_zone(struct zone *zone,
                                        unsigned long zone_start_pfn,
                                        unsigned long size,
                                        enum memmap_context context)
@@ -4301,7 +4338,7 @@ static inline void setup_usemap(struct pglist_data *pgdat,
 #ifdef CONFIG_HUGETLB_PAGE_SIZE_VARIABLE
 
 /* Initialise the number of pages represented by NR_PAGEBLOCK_BITS */
-static inline void __init set_pageblock_order(void)
+void __init set_pageblock_order(void)
 {
        unsigned int order;
 
@@ -4329,7 +4366,7 @@ static inline void __init set_pageblock_order(void)
  * include/linux/pageblock-flags.h for the values of pageblock_order based on
  * the kernel config
  */
-static inline void set_pageblock_order(void)
+void __init set_pageblock_order(void)
 {
 }
 
@@ -4340,6 +4377,8 @@ static inline void set_pageblock_order(void)
  *   - mark all pages reserved
  *   - mark all memory queues empty
  *   - clear the memory bitmaps
+ *
+ * NOTE: pgdat should get zeroed by caller.
  */
 static void __paginginit free_area_init_core(struct pglist_data *pgdat,
                unsigned long *zones_size, unsigned long *zholes_size)
@@ -4350,9 +4389,8 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
        int ret;
 
        pgdat_resize_init(pgdat);
-       pgdat->nr_zones = 0;
        init_waitqueue_head(&pgdat->kswapd_wait);
-       pgdat->kswapd_max_order = 0;
+       init_waitqueue_head(&pgdat->pfmemalloc_wait);
        pgdat_page_cgroup_init(pgdat);
 
        for (j = 0; j < MAX_NR_ZONES; j++) {
@@ -4394,6 +4432,11 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
 
                zone->spanned_pages = size;
                zone->present_pages = realsize;
+#if defined CONFIG_COMPACTION || defined CONFIG_CMA
+               zone->compact_cached_free_pfn = zone->zone_start_pfn +
+                                               zone->spanned_pages;
+               zone->compact_cached_free_pfn &= ~(pageblock_nr_pages-1);
+#endif
 #ifdef CONFIG_NUMA
                zone->node = nid;
                zone->min_unmapped_pages = (realsize*sysctl_min_unmapped_ratio)
@@ -4408,8 +4451,6 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
 
                zone_pcp_init(zone);
                lruvec_init(&zone->lruvec, zone);
-               zap_zone_vm_stats(zone);
-               zone->flags = 0;
                if (!size)
                        continue;
 
@@ -4469,6 +4510,9 @@ void __paginginit free_area_init_node(int nid, unsigned long *zones_size,
 {
        pg_data_t *pgdat = NODE_DATA(nid);
 
+       /* pg_data_t should be reset to zero when it's allocated */
+       WARN_ON(pgdat->nr_zones || pgdat->node_start_pfn || pgdat->classzone_idx);
+
        pgdat->node_id = nid;
        pgdat->node_start_pfn = node_start_pfn;
        calculate_node_totalpages(pgdat, zones_size, zholes_size);
@@ -4750,7 +4794,7 @@ out:
 }
 
 /* Any regular memory on that node ? */
-static void check_for_regular_memory(pg_data_t *pgdat)
+static void __init check_for_regular_memory(pg_data_t *pgdat)
 {
 #ifdef CONFIG_HIGHMEM
        enum zone_type zone_type;
@@ -5468,26 +5512,27 @@ void set_pageblock_flags_group(struct page *page, unsigned long flags,
 }
 
 /*
- * This is designed as sub function...plz see page_isolation.c also.
- * set/clear page block's type to be ISOLATE.
- * page allocater never alloc memory from ISOLATE block.
+ * This function checks whether pageblock includes unmovable pages or not.
+ * If @count is not zero, it is okay to include less @count unmovable pages
+ *
+ * PageLRU check wihtout isolation or lru_lock could race so that
+ * MIGRATE_MOVABLE block might include unmovable pages. It means you can't
+ * expect this function should be exact.
  */
-
-static int
-__count_immobile_pages(struct zone *zone, struct page *page, int count)
+bool has_unmovable_pages(struct zone *zone, struct page *page, int count)
 {
        unsigned long pfn, iter, found;
        int mt;
 
        /*
         * For avoiding noise data, lru_add_drain_all() should be called
-        * If ZONE_MOVABLE, the zone never contains immobile pages
+        * If ZONE_MOVABLE, the zone never contains unmovable pages
         */
        if (zone_idx(zone) == ZONE_MOVABLE)
-               return true;
+               return false;
        mt = get_pageblock_migratetype(page);
        if (mt == MIGRATE_MOVABLE || is_migrate_cma(mt))
-               return true;
+               return false;
 
        pfn = page_to_pfn(page);
        for (found = 0, iter = 0; iter < pageblock_nr_pages; iter++) {
@@ -5497,11 +5542,18 @@ __count_immobile_pages(struct zone *zone, struct page *page, int count)
                        continue;
 
                page = pfn_to_page(check);
-               if (!page_count(page)) {
+               /*
+                * We can't use page_count without pin a page
+                * because another CPU can free compound page.
+                * This check already skips compound tails of THP
+                * because their page->_count is zero at all time.
+                */
+               if (!atomic_read(&page->_count)) {
                        if (PageBuddy(page))
                                iter += (1 << page_order(page)) - 1;
                        continue;
                }
+
                if (!PageLRU(page))
                        found++;
                /*
@@ -5518,9 +5570,9 @@ __count_immobile_pages(struct zone *zone, struct page *page, int count)
                 * page at boot.
                 */
                if (found > count)
-                       return false;
+                       return true;
        }
-       return true;
+       return false;
 }
 
 bool is_pageblock_removable_nolock(struct page *page)
@@ -5544,77 +5596,7 @@ bool is_pageblock_removable_nolock(struct page *page)
                        zone->zone_start_pfn + zone->spanned_pages <= pfn)
                return false;
 
-       return __count_immobile_pages(zone, page, 0);
-}
-
-int set_migratetype_isolate(struct page *page)
-{
-       struct zone *zone;
-       unsigned long flags, pfn;
-       struct memory_isolate_notify arg;
-       int notifier_ret;
-       int ret = -EBUSY;
-
-       zone = page_zone(page);
-
-       spin_lock_irqsave(&zone->lock, flags);
-
-       pfn = page_to_pfn(page);
-       arg.start_pfn = pfn;
-       arg.nr_pages = pageblock_nr_pages;
-       arg.pages_found = 0;
-
-       /*
-        * It may be possible to isolate a pageblock even if the
-        * migratetype is not MIGRATE_MOVABLE. The memory isolation
-        * notifier chain is used by balloon drivers to return the
-        * number of pages in a range that are held by the balloon
-        * driver to shrink memory. If all the pages are accounted for
-        * by balloons, are free, or on the LRU, isolation can continue.
-        * Later, for example, when memory hotplug notifier runs, these
-        * pages reported as "can be isolated" should be isolated(freed)
-        * by the balloon driver through the memory notifier chain.
-        */
-       notifier_ret = memory_isolate_notify(MEM_ISOLATE_COUNT, &arg);
-       notifier_ret = notifier_to_errno(notifier_ret);
-       if (notifier_ret)
-               goto out;
-       /*
-        * FIXME: Now, memory hotplug doesn't call shrink_slab() by itself.
-        * We just check MOVABLE pages.
-        */
-       if (__count_immobile_pages(zone, page, arg.pages_found))
-               ret = 0;
-
-       /*
-        * immobile means "not-on-lru" paes. If immobile is larger than
-        * removable-by-driver pages reported by notifier, we'll fail.
-        */
-
-out:
-       if (!ret) {
-               set_pageblock_migratetype(page, MIGRATE_ISOLATE);
-               move_freepages_block(zone, page, MIGRATE_ISOLATE);
-       }
-
-       spin_unlock_irqrestore(&zone->lock, flags);
-       if (!ret)
-               drain_all_pages();
-       return ret;
-}
-
-void unset_migratetype_isolate(struct page *page, unsigned migratetype)
-{
-       struct zone *zone;
-       unsigned long flags;
-       zone = page_zone(page);
-       spin_lock_irqsave(&zone->lock, flags);
-       if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE)
-               goto out;
-       set_pageblock_migratetype(page, migratetype);
-       move_freepages_block(zone, page, migratetype);
-out:
-       spin_unlock_irqrestore(&zone->lock, flags);
+       return !has_unmovable_pages(zone, page, 0);
 }
 
 #ifdef CONFIG_CMA
@@ -5869,7 +5851,49 @@ void free_contig_range(unsigned long pfn, unsigned nr_pages)
 }
 #endif
 
+#ifdef CONFIG_MEMORY_HOTPLUG
+static int __meminit __zone_pcp_update(void *data)
+{
+       struct zone *zone = data;
+       int cpu;
+       unsigned long batch = zone_batchsize(zone), flags;
+
+       for_each_possible_cpu(cpu) {
+               struct per_cpu_pageset *pset;
+               struct per_cpu_pages *pcp;
+
+               pset = per_cpu_ptr(zone->pageset, cpu);
+               pcp = &pset->pcp;
+
+               local_irq_save(flags);
+               if (pcp->count > 0)
+                       free_pcppages_bulk(zone, pcp->count, pcp);
+               setup_pageset(pset, batch);
+               local_irq_restore(flags);
+       }
+       return 0;
+}
+
+void __meminit zone_pcp_update(struct zone *zone)
+{
+       stop_machine(__zone_pcp_update, zone, NULL);
+}
+#endif
+
 #ifdef CONFIG_MEMORY_HOTREMOVE
+void zone_pcp_reset(struct zone *zone)
+{
+       unsigned long flags;
+
+       /* avoid races with drain_pages()  */
+       local_irq_save(flags);
+       if (zone->pageset != &boot_pageset) {
+               free_percpu(zone->pageset);
+               zone->pageset = &boot_pageset;
+       }
+       local_irq_restore(flags);
+}
+
 /*
  * All pages in the range must be isolated before calling this.
  */
index eb750f851395b4e726c06f3926d42b472ab1bdf9..5ddad0c6daa6c6c23c3e0f03b55e13c79d061a59 100644 (file)
@@ -317,7 +317,7 @@ void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat)
 #endif
 
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
 
 static DEFINE_MUTEX(swap_cgroup_mutex);
 struct swap_cgroup_ctrl {
index 34f02923744c921fa2d990ec68f220a698049362..78eee32ee4860b17155413bb37c211bbd8b9b60a 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/swap.h>
 #include <linux/bio.h>
 #include <linux/swapops.h>
+#include <linux/buffer_head.h>
 #include <linux/writeback.h>
 #include <linux/frontswap.h>
 #include <asm/pgtable.h>
@@ -86,6 +87,98 @@ void end_swap_bio_read(struct bio *bio, int err)
        bio_put(bio);
 }
 
+int generic_swapfile_activate(struct swap_info_struct *sis,
+                               struct file *swap_file,
+                               sector_t *span)
+{
+       struct address_space *mapping = swap_file->f_mapping;
+       struct inode *inode = mapping->host;
+       unsigned blocks_per_page;
+       unsigned long page_no;
+       unsigned blkbits;
+       sector_t probe_block;
+       sector_t last_block;
+       sector_t lowest_block = -1;
+       sector_t highest_block = 0;
+       int nr_extents = 0;
+       int ret;
+
+       blkbits = inode->i_blkbits;
+       blocks_per_page = PAGE_SIZE >> blkbits;
+
+       /*
+        * Map all the blocks into the extent list.  This code doesn't try
+        * to be very smart.
+        */
+       probe_block = 0;
+       page_no = 0;
+       last_block = i_size_read(inode) >> blkbits;
+       while ((probe_block + blocks_per_page) <= last_block &&
+                       page_no < sis->max) {
+               unsigned block_in_page;
+               sector_t first_block;
+
+               first_block = bmap(inode, probe_block);
+               if (first_block == 0)
+                       goto bad_bmap;
+
+               /*
+                * It must be PAGE_SIZE aligned on-disk
+                */
+               if (first_block & (blocks_per_page - 1)) {
+                       probe_block++;
+                       goto reprobe;
+               }
+
+               for (block_in_page = 1; block_in_page < blocks_per_page;
+                                       block_in_page++) {
+                       sector_t block;
+
+                       block = bmap(inode, probe_block + block_in_page);
+                       if (block == 0)
+                               goto bad_bmap;
+                       if (block != first_block + block_in_page) {
+                               /* Discontiguity */
+                               probe_block++;
+                               goto reprobe;
+                       }
+               }
+
+               first_block >>= (PAGE_SHIFT - blkbits);
+               if (page_no) {  /* exclude the header page */
+                       if (first_block < lowest_block)
+                               lowest_block = first_block;
+                       if (first_block > highest_block)
+                               highest_block = first_block;
+               }
+
+               /*
+                * We found a PAGE_SIZE-length, PAGE_SIZE-aligned run of blocks
+                */
+               ret = add_swap_extent(sis, page_no, 1, first_block);
+               if (ret < 0)
+                       goto out;
+               nr_extents += ret;
+               page_no++;
+               probe_block += blocks_per_page;
+reprobe:
+               continue;
+       }
+       ret = nr_extents;
+       *span = 1 + highest_block - lowest_block;
+       if (page_no == 0)
+               page_no = 1;    /* force Empty message */
+       sis->max = page_no;
+       sis->pages = page_no - 1;
+       sis->highest_bit = page_no - 1;
+out:
+       return ret;
+bad_bmap:
+       printk(KERN_ERR "swapon: swapfile has holes\n");
+       ret = -EINVAL;
+       goto out;
+}
+
 /*
  * We may have stale swap cache pages in memory: notice
  * them here and get rid of the unnecessary final write.
@@ -94,6 +187,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc)
 {
        struct bio *bio;
        int ret = 0, rw = WRITE;
+       struct swap_info_struct *sis = page_swap_info(page);
 
        if (try_to_free_swap(page)) {
                unlock_page(page);
@@ -105,6 +199,33 @@ int swap_writepage(struct page *page, struct writeback_control *wbc)
                end_page_writeback(page);
                goto out;
        }
+
+       if (sis->flags & SWP_FILE) {
+               struct kiocb kiocb;
+               struct file *swap_file = sis->swap_file;
+               struct address_space *mapping = swap_file->f_mapping;
+               struct iovec iov = {
+                       .iov_base = kmap(page),
+                       .iov_len  = PAGE_SIZE,
+               };
+
+               init_sync_kiocb(&kiocb, swap_file);
+               kiocb.ki_pos = page_file_offset(page);
+               kiocb.ki_left = PAGE_SIZE;
+               kiocb.ki_nbytes = PAGE_SIZE;
+
+               unlock_page(page);
+               ret = mapping->a_ops->direct_IO(KERNEL_WRITE,
+                                               &kiocb, &iov,
+                                               kiocb.ki_pos, 1);
+               kunmap(page);
+               if (ret == PAGE_SIZE) {
+                       count_vm_event(PSWPOUT);
+                       ret = 0;
+               }
+               return ret;
+       }
+
        bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write);
        if (bio == NULL) {
                set_page_dirty(page);
@@ -126,6 +247,7 @@ int swap_readpage(struct page *page)
 {
        struct bio *bio;
        int ret = 0;
+       struct swap_info_struct *sis = page_swap_info(page);
 
        VM_BUG_ON(!PageLocked(page));
        VM_BUG_ON(PageUptodate(page));
@@ -134,6 +256,17 @@ int swap_readpage(struct page *page)
                unlock_page(page);
                goto out;
        }
+
+       if (sis->flags & SWP_FILE) {
+               struct file *swap_file = sis->swap_file;
+               struct address_space *mapping = swap_file->f_mapping;
+
+               ret = mapping->a_ops->readpage(swap_file, page);
+               if (!ret)
+                       count_vm_event(PSWPIN);
+               return ret;
+       }
+
        bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read);
        if (bio == NULL) {
                unlock_page(page);
@@ -145,3 +278,15 @@ int swap_readpage(struct page *page)
 out:
        return ret;
 }
+
+int swap_set_page_dirty(struct page *page)
+{
+       struct swap_info_struct *sis = page_swap_info(page);
+
+       if (sis->flags & SWP_FILE) {
+               struct address_space *mapping = sis->swap_file->f_mapping;
+               return mapping->a_ops->set_page_dirty(page);
+       } else {
+               return __set_page_dirty_no_writeback(page);
+       }
+}
index c9f04774f2b8117354bc6f38230bf94e645f701b..247d1f175739247718d087aee8bba3c810daaa46 100644 (file)
@@ -5,8 +5,101 @@
 #include <linux/mm.h>
 #include <linux/page-isolation.h>
 #include <linux/pageblock-flags.h>
+#include <linux/memory.h>
 #include "internal.h"
 
+/* called while holding zone->lock */
+static void set_pageblock_isolate(struct page *page)
+{
+       if (get_pageblock_migratetype(page) == MIGRATE_ISOLATE)
+               return;
+
+       set_pageblock_migratetype(page, MIGRATE_ISOLATE);
+       page_zone(page)->nr_pageblock_isolate++;
+}
+
+/* called while holding zone->lock */
+static void restore_pageblock_isolate(struct page *page, int migratetype)
+{
+       struct zone *zone = page_zone(page);
+       if (WARN_ON(get_pageblock_migratetype(page) != MIGRATE_ISOLATE))
+               return;
+
+       BUG_ON(zone->nr_pageblock_isolate <= 0);
+       set_pageblock_migratetype(page, migratetype);
+       zone->nr_pageblock_isolate--;
+}
+
+int set_migratetype_isolate(struct page *page)
+{
+       struct zone *zone;
+       unsigned long flags, pfn;
+       struct memory_isolate_notify arg;
+       int notifier_ret;
+       int ret = -EBUSY;
+
+       zone = page_zone(page);
+
+       spin_lock_irqsave(&zone->lock, flags);
+
+       pfn = page_to_pfn(page);
+       arg.start_pfn = pfn;
+       arg.nr_pages = pageblock_nr_pages;
+       arg.pages_found = 0;
+
+       /*
+        * It may be possible to isolate a pageblock even if the
+        * migratetype is not MIGRATE_MOVABLE. The memory isolation
+        * notifier chain is used by balloon drivers to return the
+        * number of pages in a range that are held by the balloon
+        * driver to shrink memory. If all the pages are accounted for
+        * by balloons, are free, or on the LRU, isolation can continue.
+        * Later, for example, when memory hotplug notifier runs, these
+        * pages reported as "can be isolated" should be isolated(freed)
+        * by the balloon driver through the memory notifier chain.
+        */
+       notifier_ret = memory_isolate_notify(MEM_ISOLATE_COUNT, &arg);
+       notifier_ret = notifier_to_errno(notifier_ret);
+       if (notifier_ret)
+               goto out;
+       /*
+        * FIXME: Now, memory hotplug doesn't call shrink_slab() by itself.
+        * We just check MOVABLE pages.
+        */
+       if (!has_unmovable_pages(zone, page, arg.pages_found))
+               ret = 0;
+
+       /*
+        * immobile means "not-on-lru" paes. If immobile is larger than
+        * removable-by-driver pages reported by notifier, we'll fail.
+        */
+
+out:
+       if (!ret) {
+               set_pageblock_isolate(page);
+               move_freepages_block(zone, page, MIGRATE_ISOLATE);
+       }
+
+       spin_unlock_irqrestore(&zone->lock, flags);
+       if (!ret)
+               drain_all_pages();
+       return ret;
+}
+
+void unset_migratetype_isolate(struct page *page, unsigned migratetype)
+{
+       struct zone *zone;
+       unsigned long flags;
+       zone = page_zone(page);
+       spin_lock_irqsave(&zone->lock, flags);
+       if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE)
+               goto out;
+       move_freepages_block(zone, page, migratetype);
+       restore_pageblock_isolate(page, migratetype);
+out:
+       spin_unlock_irqrestore(&zone->lock, flags);
+}
+
 static inline struct page *
 __first_valid_page(unsigned long pfn, unsigned long nr_pages)
 {
index c15b998e5a860f9d3c375ad31ce9d5f34fbb5ea0..d4e184e2a38ea590350e5f31073c1ed8ad6690e0 100644 (file)
@@ -929,7 +929,8 @@ static struct page *shmem_swapin(swp_entry_t swap, gfp_t gfp,
 
        /* Create a pseudo vma that just contains the policy */
        pvma.vm_start = 0;
-       pvma.vm_pgoff = index;
+       /* Bias interleave by inode number to distribute better across nodes */
+       pvma.vm_pgoff = index + info->vfs_inode.i_ino;
        pvma.vm_ops = NULL;
        pvma.vm_policy = spol;
        return swapin_readahead(swap, gfp, &pvma, 0);
@@ -942,7 +943,8 @@ static struct page *shmem_alloc_page(gfp_t gfp,
 
        /* Create a pseudo vma that just contains the policy */
        pvma.vm_start = 0;
-       pvma.vm_pgoff = index;
+       /* Bias interleave by inode number to distribute better across nodes */
+       pvma.vm_pgoff = index + info->vfs_inode.i_ino;
        pvma.vm_ops = NULL;
        pvma.vm_policy = mpol_shared_policy_lookup(&info->policy, index);
 
index e901a36e2520c2b7aa5123ddcd0553415b764397..f8b0d539b4822af7812c8f9edcb51224450f3f64 100644 (file)
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -68,7 +68,7 @@
  * Further notes from the original documentation:
  *
  * 11 April '97.  Started multi-threading - markhe
- *     The global cache-chain is protected by the mutex 'cache_chain_mutex'.
+ *     The global cache-chain is protected by the mutex 'slab_mutex'.
  *     The sem is only needed when accessing/extending the cache-chain, which
  *     can never happen inside an interrupt (kmem_cache_create(),
  *     kmem_cache_shrink() and kmem_cache_reap()).
@@ -87,6 +87,7 @@
  */
 
 #include       <linux/slab.h>
+#include       "slab.h"
 #include       <linux/mm.h>
 #include       <linux/poison.h>
 #include       <linux/swap.h>
 #include       <linux/memory.h>
 #include       <linux/prefetch.h>
 
+#include       <net/sock.h>
+
 #include       <asm/cacheflush.h>
 #include       <asm/tlbflush.h>
 #include       <asm/page.h>
 
 #include <trace/events/kmem.h>
 
+#include       "internal.h"
+
 /*
  * DEBUG       - 1 for kmem_cache_create() to honour; SLAB_RED_ZONE & SLAB_POISON.
  *               0 for faster, smaller code (especially in the critical paths).
 #define ARCH_KMALLOC_FLAGS SLAB_HWCACHE_ALIGN
 #endif
 
+/*
+ * true if a page was allocated from pfmemalloc reserves for network-based
+ * swap
+ */
+static bool pfmemalloc_active __read_mostly;
+
 /* Legal flag mask for kmem_cache_create(). */
 #if DEBUG
 # define CREATE_MASK   (SLAB_RED_ZONE | \
@@ -256,9 +267,30 @@ struct array_cache {
                         * Must have this definition in here for the proper
                         * alignment of array_cache. Also simplifies accessing
                         * the entries.
+                        *
+                        * Entries should not be directly dereferenced as
+                        * entries belonging to slabs marked pfmemalloc will
+                        * have the lower bits set SLAB_OBJ_PFMEMALLOC
                         */
 };
 
+#define SLAB_OBJ_PFMEMALLOC    1
+static inline bool is_obj_pfmemalloc(void *objp)
+{
+       return (unsigned long)objp & SLAB_OBJ_PFMEMALLOC;
+}
+
+static inline void set_obj_pfmemalloc(void **objp)
+{
+       *objp = (void *)((unsigned long)*objp | SLAB_OBJ_PFMEMALLOC);
+       return;
+}
+
+static inline void clear_obj_pfmemalloc(void **objp)
+{
+       *objp = (void *)((unsigned long)*objp & ~SLAB_OBJ_PFMEMALLOC);
+}
+
 /*
  * bootstrap: The caches do not work without cpuarrays anymore, but the
  * cpuarrays are allocated from the generic caches...
@@ -424,8 +456,8 @@ static void kmem_list3_init(struct kmem_list3 *parent)
  * cachep->obj_offset - BYTES_PER_WORD .. cachep->obj_offset - 1:
  *             redzone word.
  * cachep->obj_offset: The real object.
- * cachep->buffer_size - 2* BYTES_PER_WORD: redzone word [BYTES_PER_WORD long]
- * cachep->buffer_size - 1* BYTES_PER_WORD: last caller address
+ * cachep->size - 2* BYTES_PER_WORD: redzone word [BYTES_PER_WORD long]
+ * cachep->size - 1* BYTES_PER_WORD: last caller address
  *                                     [BYTES_PER_WORD long]
  */
 static int obj_offset(struct kmem_cache *cachep)
@@ -433,11 +465,6 @@ static int obj_offset(struct kmem_cache *cachep)
        return cachep->obj_offset;
 }
 
-static int obj_size(struct kmem_cache *cachep)
-{
-       return cachep->obj_size;
-}
-
 static unsigned long long *dbg_redzone1(struct kmem_cache *cachep, void *objp)
 {
        BUG_ON(!(cachep->flags & SLAB_RED_ZONE));
@@ -449,23 +476,22 @@ static unsigned long long *dbg_redzone2(struct kmem_cache *cachep, void *objp)
 {
        BUG_ON(!(cachep->flags & SLAB_RED_ZONE));
        if (cachep->flags & SLAB_STORE_USER)
-               return (unsigned long long *)(objp + cachep->buffer_size -
+               return (unsigned long long *)(objp + cachep->size -
                                              sizeof(unsigned long long) -
                                              REDZONE_ALIGN);
-       return (unsigned long long *) (objp + cachep->buffer_size -
+       return (unsigned long long *) (objp + cachep->size -
                                       sizeof(unsigned long long));
 }
 
 static void **dbg_userword(struct kmem_cache *cachep, void *objp)
 {
        BUG_ON(!(cachep->flags & SLAB_STORE_USER));
-       return (void **)(objp + cachep->buffer_size - BYTES_PER_WORD);
+       return (void **)(objp + cachep->size - BYTES_PER_WORD);
 }
 
 #else
 
 #define obj_offset(x)                  0
-#define obj_size(cachep)               (cachep->buffer_size)
 #define dbg_redzone1(cachep, objp)     ({BUG(); (unsigned long long *)NULL;})
 #define dbg_redzone2(cachep, objp)     ({BUG(); (unsigned long long *)NULL;})
 #define dbg_userword(cachep, objp)     ({BUG(); (void **)NULL;})
@@ -475,7 +501,7 @@ static void **dbg_userword(struct kmem_cache *cachep, void *objp)
 #ifdef CONFIG_TRACING
 size_t slab_buffer_size(struct kmem_cache *cachep)
 {
-       return cachep->buffer_size;
+       return cachep->size;
 }
 EXPORT_SYMBOL(slab_buffer_size);
 #endif
@@ -489,56 +515,37 @@ EXPORT_SYMBOL(slab_buffer_size);
 static int slab_max_order = SLAB_MAX_ORDER_LO;
 static bool slab_max_order_set __initdata;
 
-/*
- * Functions for storing/retrieving the cachep and or slab from the page
- * allocator.  These are used to find the slab an obj belongs to.  With kfree(),
- * these are used to find the cache which an obj belongs to.
- */
-static inline void page_set_cache(struct page *page, struct kmem_cache *cache)
-{
-       page->lru.next = (struct list_head *)cache;
-}
-
 static inline struct kmem_cache *page_get_cache(struct page *page)
 {
        page = compound_head(page);
        BUG_ON(!PageSlab(page));
-       return (struct kmem_cache *)page->lru.next;
-}
-
-static inline void page_set_slab(struct page *page, struct slab *slab)
-{
-       page->lru.prev = (struct list_head *)slab;
-}
-
-static inline struct slab *page_get_slab(struct page *page)
-{
-       BUG_ON(!PageSlab(page));
-       return (struct slab *)page->lru.prev;
+       return page->slab_cache;
 }
 
 static inline struct kmem_cache *virt_to_cache(const void *obj)
 {
        struct page *page = virt_to_head_page(obj);
-       return page_get_cache(page);
+       return page->slab_cache;
 }
 
 static inline struct slab *virt_to_slab(const void *obj)
 {
        struct page *page = virt_to_head_page(obj);
-       return page_get_slab(page);
+
+       VM_BUG_ON(!PageSlab(page));
+       return page->slab_page;
 }
 
 static inline void *index_to_obj(struct kmem_cache *cache, struct slab *slab,
                                 unsigned int idx)
 {
-       return slab->s_mem + cache->buffer_size * idx;
+       return slab->s_mem + cache->size * idx;
 }
 
 /*
- * We want to avoid an expensive divide : (offset / cache->buffer_size)
- *   Using the fact that buffer_size is a constant for a particular cache,
- *   we can replace (offset / cache->buffer_size) by
+ * We want to avoid an expensive divide : (offset / cache->size)
+ *   Using the fact that size is a constant for a particular cache,
+ *   we can replace (offset / cache->size) by
  *   reciprocal_divide(offset, cache->reciprocal_buffer_size)
  */
 static inline unsigned int obj_to_index(const struct kmem_cache *cache,
@@ -584,33 +591,12 @@ static struct kmem_cache cache_cache = {
        .batchcount = 1,
        .limit = BOOT_CPUCACHE_ENTRIES,
        .shared = 1,
-       .buffer_size = sizeof(struct kmem_cache),
+       .size = sizeof(struct kmem_cache),
        .name = "kmem_cache",
 };
 
 #define BAD_ALIEN_MAGIC 0x01020304ul
 
-/*
- * chicken and egg problem: delay the per-cpu array allocation
- * until the general caches are up.
- */
-static enum {
-       NONE,
-       PARTIAL_AC,
-       PARTIAL_L3,
-       EARLY,
-       LATE,
-       FULL
-} g_cpucache_up;
-
-/*
- * used by boot code to determine if it can use slab based allocator
- */
-int slab_is_available(void)
-{
-       return g_cpucache_up >= EARLY;
-}
-
 #ifdef CONFIG_LOCKDEP
 
 /*
@@ -676,7 +662,7 @@ static void init_node_lock_keys(int q)
 {
        struct cache_sizes *s = malloc_sizes;
 
-       if (g_cpucache_up < LATE)
+       if (slab_state < UP)
                return;
 
        for (s = malloc_sizes; s->cs_size != ULONG_MAX; s++) {
@@ -716,12 +702,6 @@ static void slab_set_debugobj_lock_classes(struct kmem_cache *cachep)
 }
 #endif
 
-/*
- * Guard access to the cache-chain.
- */
-static DEFINE_MUTEX(cache_chain_mutex);
-static struct list_head cache_chain;
-
 static DEFINE_PER_CPU(struct delayed_work, slab_reap_work);
 
 static inline struct array_cache *cpu_cache_get(struct kmem_cache *cachep)
@@ -951,6 +931,124 @@ static struct array_cache *alloc_arraycache(int node, int entries,
        return nc;
 }
 
+static inline bool is_slab_pfmemalloc(struct slab *slabp)
+{
+       struct page *page = virt_to_page(slabp->s_mem);
+
+       return PageSlabPfmemalloc(page);
+}
+
+/* Clears pfmemalloc_active if no slabs have pfmalloc set */
+static void recheck_pfmemalloc_active(struct kmem_cache *cachep,
+                                               struct array_cache *ac)
+{
+       struct kmem_list3 *l3 = cachep->nodelists[numa_mem_id()];
+       struct slab *slabp;
+       unsigned long flags;
+
+       if (!pfmemalloc_active)
+               return;
+
+       spin_lock_irqsave(&l3->list_lock, flags);
+       list_for_each_entry(slabp, &l3->slabs_full, list)
+               if (is_slab_pfmemalloc(slabp))
+                       goto out;
+
+       list_for_each_entry(slabp, &l3->slabs_partial, list)
+               if (is_slab_pfmemalloc(slabp))
+                       goto out;
+
+       list_for_each_entry(slabp, &l3->slabs_free, list)
+               if (is_slab_pfmemalloc(slabp))
+                       goto out;
+
+       pfmemalloc_active = false;
+out:
+       spin_unlock_irqrestore(&l3->list_lock, flags);
+}
+
+static void *__ac_get_obj(struct kmem_cache *cachep, struct array_cache *ac,
+                                               gfp_t flags, bool force_refill)
+{
+       int i;
+       void *objp = ac->entry[--ac->avail];
+
+       /* Ensure the caller is allowed to use objects from PFMEMALLOC slab */
+       if (unlikely(is_obj_pfmemalloc(objp))) {
+               struct kmem_list3 *l3;
+
+               if (gfp_pfmemalloc_allowed(flags)) {
+                       clear_obj_pfmemalloc(&objp);
+                       return objp;
+               }
+
+               /* The caller cannot use PFMEMALLOC objects, find another one */
+               for (i = 1; i < ac->avail; i++) {
+                       /* If a !PFMEMALLOC object is found, swap them */
+                       if (!is_obj_pfmemalloc(ac->entry[i])) {
+                               objp = ac->entry[i];
+                               ac->entry[i] = ac->entry[ac->avail];
+                               ac->entry[ac->avail] = objp;
+                               return objp;
+                       }
+               }
+
+               /*
+                * If there are empty slabs on the slabs_free list and we are
+                * being forced to refill the cache, mark this one !pfmemalloc.
+                */
+               l3 = cachep->nodelists[numa_mem_id()];
+               if (!list_empty(&l3->slabs_free) && force_refill) {
+                       struct slab *slabp = virt_to_slab(objp);
+                       ClearPageSlabPfmemalloc(virt_to_page(slabp->s_mem));
+                       clear_obj_pfmemalloc(&objp);
+                       recheck_pfmemalloc_active(cachep, ac);
+                       return objp;
+               }
+
+               /* No !PFMEMALLOC objects available */
+               ac->avail++;
+               objp = NULL;
+       }
+
+       return objp;
+}
+
+static inline void *ac_get_obj(struct kmem_cache *cachep,
+                       struct array_cache *ac, gfp_t flags, bool force_refill)
+{
+       void *objp;
+
+       if (unlikely(sk_memalloc_socks()))
+               objp = __ac_get_obj(cachep, ac, flags, force_refill);
+       else
+               objp = ac->entry[--ac->avail];
+
+       return objp;
+}
+
+static void *__ac_put_obj(struct kmem_cache *cachep, struct array_cache *ac,
+                                                               void *objp)
+{
+       if (unlikely(pfmemalloc_active)) {
+               /* Some pfmemalloc slabs exist, check if this is one */
+               struct page *page = virt_to_page(objp);
+               if (PageSlabPfmemalloc(page))
+                       set_obj_pfmemalloc(&objp);
+       }
+
+       return objp;
+}
+
+static inline void ac_put_obj(struct kmem_cache *cachep, struct array_cache *ac,
+                                                               void *objp)
+{
+       if (unlikely(sk_memalloc_socks()))
+               objp = __ac_put_obj(cachep, ac, objp);
+
+       ac->entry[ac->avail++] = objp;
+}
+
 /*
  * Transfer objects in one arraycache to another.
  * Locking must be handled by the caller.
@@ -1127,7 +1225,7 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp)
                        STATS_INC_ACOVERFLOW(cachep);
                        __drain_alien_cache(cachep, alien, nodeid);
                }
-               alien->entry[alien->avail++] = objp;
+               ac_put_obj(cachep, alien, objp);
                spin_unlock(&alien->lock);
        } else {
                spin_lock(&(cachep->nodelists[nodeid])->list_lock);
@@ -1145,7 +1243,7 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp)
  * When hotplugging memory or a cpu, existing nodelists are not replaced if
  * already in use.
  *
- * Must hold cache_chain_mutex.
+ * Must hold slab_mutex.
  */
 static int init_cache_nodelists_node(int node)
 {
@@ -1153,7 +1251,7 @@ static int init_cache_nodelists_node(int node)
        struct kmem_list3 *l3;
        const int memsize = sizeof(struct kmem_list3);
 
-       list_for_each_entry(cachep, &cache_chain, next) {
+       list_for_each_entry(cachep, &slab_caches, list) {
                /*
                 * Set up the size64 kmemlist for cpu before we can
                 * begin anything. Make sure some other cpu on this
@@ -1169,7 +1267,7 @@ static int init_cache_nodelists_node(int node)
 
                        /*
                         * The l3s don't come and go as CPUs come and
-                        * go.  cache_chain_mutex is sufficient
+                        * go.  slab_mutex is sufficient
                         * protection here.
                         */
                        cachep->nodelists[node] = l3;
@@ -1191,7 +1289,7 @@ static void __cpuinit cpuup_canceled(long cpu)
        int node = cpu_to_mem(cpu);
        const struct cpumask *mask = cpumask_of_node(node);
 
-       list_for_each_entry(cachep, &cache_chain, next) {
+       list_for_each_entry(cachep, &slab_caches, list) {
                struct array_cache *nc;
                struct array_cache *shared;
                struct array_cache **alien;
@@ -1241,7 +1339,7 @@ free_array_cache:
         * the respective cache's slabs,  now we can go ahead and
         * shrink each nodelist to its limit.
         */
-       list_for_each_entry(cachep, &cache_chain, next) {
+       list_for_each_entry(cachep, &slab_caches, list) {
                l3 = cachep->nodelists[node];
                if (!l3)
                        continue;
@@ -1270,7 +1368,7 @@ static int __cpuinit cpuup_prepare(long cpu)
         * Now we can go ahead with allocating the shared arrays and
         * array caches
         */
-       list_for_each_entry(cachep, &cache_chain, next) {
+       list_for_each_entry(cachep, &slab_caches, list) {
                struct array_cache *nc;
                struct array_cache *shared = NULL;
                struct array_cache **alien = NULL;
@@ -1338,9 +1436,9 @@ static int __cpuinit cpuup_callback(struct notifier_block *nfb,
        switch (action) {
        case CPU_UP_PREPARE:
        case CPU_UP_PREPARE_FROZEN:
-               mutex_lock(&cache_chain_mutex);
+               mutex_lock(&slab_mutex);
                err = cpuup_prepare(cpu);
-               mutex_unlock(&cache_chain_mutex);
+               mutex_unlock(&slab_mutex);
                break;
        case CPU_ONLINE:
        case CPU_ONLINE_FROZEN:
@@ -1350,7 +1448,7 @@ static int __cpuinit cpuup_callback(struct notifier_block *nfb,
        case CPU_DOWN_PREPARE:
        case CPU_DOWN_PREPARE_FROZEN:
                /*
-                * Shutdown cache reaper. Note that the cache_chain_mutex is
+                * Shutdown cache reaper. Note that the slab_mutex is
                 * held so that if cache_reap() is invoked it cannot do
                 * anything expensive but will only modify reap_work
                 * and reschedule the timer.
@@ -1377,9 +1475,9 @@ static int __cpuinit cpuup_callback(struct notifier_block *nfb,
 #endif
        case CPU_UP_CANCELED:
        case CPU_UP_CANCELED_FROZEN:
-               mutex_lock(&cache_chain_mutex);
+               mutex_lock(&slab_mutex);
                cpuup_canceled(cpu);
-               mutex_unlock(&cache_chain_mutex);
+               mutex_unlock(&slab_mutex);
                break;
        }
        return notifier_from_errno(err);
@@ -1395,14 +1493,14 @@ static struct notifier_block __cpuinitdata cpucache_notifier = {
  * Returns -EBUSY if all objects cannot be drained so that the node is not
  * removed.
  *
- * Must hold cache_chain_mutex.
+ * Must hold slab_mutex.
  */
 static int __meminit drain_cache_nodelists_node(int node)
 {
        struct kmem_cache *cachep;
        int ret = 0;
 
-       list_for_each_entry(cachep, &cache_chain, next) {
+       list_for_each_entry(cachep, &slab_caches, list) {
                struct kmem_list3 *l3;
 
                l3 = cachep->nodelists[node];
@@ -1433,14 +1531,14 @@ static int __meminit slab_memory_callback(struct notifier_block *self,
 
        switch (action) {
        case MEM_GOING_ONLINE:
-               mutex_lock(&cache_chain_mutex);
+               mutex_lock(&slab_mutex);
                ret = init_cache_nodelists_node(nid);
-               mutex_unlock(&cache_chain_mutex);
+               mutex_unlock(&slab_mutex);
                break;
        case MEM_GOING_OFFLINE:
-               mutex_lock(&cache_chain_mutex);
+               mutex_lock(&slab_mutex);
                ret = drain_cache_nodelists_node(nid);
-               mutex_unlock(&cache_chain_mutex);
+               mutex_unlock(&slab_mutex);
                break;
        case MEM_ONLINE:
        case MEM_OFFLINE:
@@ -1544,8 +1642,8 @@ void __init kmem_cache_init(void)
        node = numa_mem_id();
 
        /* 1) create the cache_cache */
-       INIT_LIST_HEAD(&cache_chain);
-       list_add(&cache_cache.next, &cache_chain);
+       INIT_LIST_HEAD(&slab_caches);
+       list_add(&cache_cache.list, &slab_caches);
        cache_cache.colour_off = cache_line_size();
        cache_cache.array[smp_processor_id()] = &initarray_cache.cache;
        cache_cache.nodelists[node] = &initkmem_list3[CACHE_CACHE + node];
@@ -1553,18 +1651,16 @@ void __init kmem_cache_init(void)
        /*
         * struct kmem_cache size depends on nr_node_ids & nr_cpu_ids
         */
-       cache_cache.buffer_size = offsetof(struct kmem_cache, array[nr_cpu_ids]) +
+       cache_cache.size = offsetof(struct kmem_cache, array[nr_cpu_ids]) +
                                  nr_node_ids * sizeof(struct kmem_list3 *);
-#if DEBUG
-       cache_cache.obj_size = cache_cache.buffer_size;
-#endif
-       cache_cache.buffer_size = ALIGN(cache_cache.buffer_size,
+       cache_cache.object_size = cache_cache.size;
+       cache_cache.size = ALIGN(cache_cache.size,
                                        cache_line_size());
        cache_cache.reciprocal_buffer_size =
-               reciprocal_value(cache_cache.buffer_size);
+               reciprocal_value(cache_cache.size);
 
        for (order = 0; order < MAX_ORDER; order++) {
-               cache_estimate(order, cache_cache.buffer_size,
+               cache_estimate(order, cache_cache.size,
                        cache_line_size(), 0, &left_over, &cache_cache.num);
                if (cache_cache.num)
                        break;
@@ -1585,7 +1681,7 @@ void __init kmem_cache_init(void)
         * bug.
         */
 
-       sizes[INDEX_AC].cs_cachep = kmem_cache_create(names[INDEX_AC].name,
+       sizes[INDEX_AC].cs_cachep = __kmem_cache_create(names[INDEX_AC].name,
                                        sizes[INDEX_AC].cs_size,
                                        ARCH_KMALLOC_MINALIGN,
                                        ARCH_KMALLOC_FLAGS|SLAB_PANIC,
@@ -1593,7 +1689,7 @@ void __init kmem_cache_init(void)
 
        if (INDEX_AC != INDEX_L3) {
                sizes[INDEX_L3].cs_cachep =
-                       kmem_cache_create(names[INDEX_L3].name,
+                       __kmem_cache_create(names[INDEX_L3].name,
                                sizes[INDEX_L3].cs_size,
                                ARCH_KMALLOC_MINALIGN,
                                ARCH_KMALLOC_FLAGS|SLAB_PANIC,
@@ -1611,14 +1707,14 @@ void __init kmem_cache_init(void)
                 * allow tighter packing of the smaller caches.
                 */
                if (!sizes->cs_cachep) {
-                       sizes->cs_cachep = kmem_cache_create(names->name,
+                       sizes->cs_cachep = __kmem_cache_create(names->name,
                                        sizes->cs_size,
                                        ARCH_KMALLOC_MINALIGN,
                                        ARCH_KMALLOC_FLAGS|SLAB_PANIC,
                                        NULL);
                }
 #ifdef CONFIG_ZONE_DMA
-               sizes->cs_dmacachep = kmem_cache_create(
+               sizes->cs_dmacachep = __kmem_cache_create(
                                        names->name_dma,
                                        sizes->cs_size,
                                        ARCH_KMALLOC_MINALIGN,
@@ -1676,27 +1772,27 @@ void __init kmem_cache_init(void)
                }
        }
 
-       g_cpucache_up = EARLY;
+       slab_state = UP;
 }
 
 void __init kmem_cache_init_late(void)
 {
        struct kmem_cache *cachep;
 
-       g_cpucache_up = LATE;
+       slab_state = UP;
 
        /* Annotate slab for lockdep -- annotate the malloc caches */
        init_lock_keys();
 
        /* 6) resize the head arrays to their final sizes */
-       mutex_lock(&cache_chain_mutex);
-       list_for_each_entry(cachep, &cache_chain, next)
+       mutex_lock(&slab_mutex);
+       list_for_each_entry(cachep, &slab_caches, list)
                if (enable_cpucache(cachep, GFP_NOWAIT))
                        BUG();
-       mutex_unlock(&cache_chain_mutex);
+       mutex_unlock(&slab_mutex);
 
        /* Done! */
-       g_cpucache_up = FULL;
+       slab_state = FULL;
 
        /*
         * Register a cpu startup notifier callback that initializes
@@ -1727,6 +1823,9 @@ static int __init cpucache_init(void)
         */
        for_each_online_cpu(cpu)
                start_cpu_timer(cpu);
+
+       /* Done! */
+       slab_state = FULL;
        return 0;
 }
 __initcall(cpucache_init);
@@ -1743,7 +1842,7 @@ slab_out_of_memory(struct kmem_cache *cachep, gfp_t gfpflags, int nodeid)
                "SLAB: Unable to allocate memory on node %d (gfp=0x%x)\n",
                nodeid, gfpflags);
        printk(KERN_WARNING "  cache: %s, object size: %d, order: %d\n",
-               cachep->name, cachep->buffer_size, cachep->gfporder);
+               cachep->name, cachep->size, cachep->gfporder);
 
        for_each_online_node(node) {
                unsigned long active_objs = 0, num_objs = 0, free_objects = 0;
@@ -1798,7 +1897,7 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
        flags |= __GFP_COMP;
 #endif
 
-       flags |= cachep->gfpflags;
+       flags |= cachep->allocflags;
        if (cachep->flags & SLAB_RECLAIM_ACCOUNT)
                flags |= __GFP_RECLAIMABLE;
 
@@ -1809,6 +1908,10 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
                return NULL;
        }
 
+       /* Record if ALLOC_NO_WATERMARKS was set when allocating the slab */
+       if (unlikely(page->pfmemalloc))
+               pfmemalloc_active = true;
+
        nr_pages = (1 << cachep->gfporder);
        if (cachep->flags & SLAB_RECLAIM_ACCOUNT)
                add_zone_page_state(page_zone(page),
@@ -1816,9 +1919,13 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
        else
                add_zone_page_state(page_zone(page),
                        NR_SLAB_UNRECLAIMABLE, nr_pages);
-       for (i = 0; i < nr_pages; i++)
+       for (i = 0; i < nr_pages; i++) {
                __SetPageSlab(page + i);
 
+               if (page->pfmemalloc)
+                       SetPageSlabPfmemalloc(page + i);
+       }
+
        if (kmemcheck_enabled && !(cachep->flags & SLAB_NOTRACK)) {
                kmemcheck_alloc_shadow(page, cachep->gfporder, flags, nodeid);
 
@@ -1850,6 +1957,7 @@ static void kmem_freepages(struct kmem_cache *cachep, void *addr)
                                NR_SLAB_UNRECLAIMABLE, nr_freed);
        while (i--) {
                BUG_ON(!PageSlab(page));
+               __ClearPageSlabPfmemalloc(page);
                __ClearPageSlab(page);
                page++;
        }
@@ -1874,7 +1982,7 @@ static void kmem_rcu_free(struct rcu_head *head)
 static void store_stackinfo(struct kmem_cache *cachep, unsigned long *addr,
                            unsigned long caller)
 {
-       int size = obj_size(cachep);
+       int size = cachep->object_size;
 
        addr = (unsigned long *)&((char *)addr)[obj_offset(cachep)];
 
@@ -1906,7 +2014,7 @@ static void store_stackinfo(struct kmem_cache *cachep, unsigned long *addr,
 
 static void poison_obj(struct kmem_cache *cachep, void *addr, unsigned char val)
 {
-       int size = obj_size(cachep);
+       int size = cachep->object_size;
        addr = &((char *)addr)[obj_offset(cachep)];
 
        memset(addr, val, size);
@@ -1966,7 +2074,7 @@ static void print_objinfo(struct kmem_cache *cachep, void *objp, int lines)
                printk("\n");
        }
        realobj = (char *)objp + obj_offset(cachep);
-       size = obj_size(cachep);
+       size = cachep->object_size;
        for (i = 0; i < size && lines; i += 16, lines--) {
                int limit;
                limit = 16;
@@ -1983,7 +2091,7 @@ static void check_poison_obj(struct kmem_cache *cachep, void *objp)
        int lines = 0;
 
        realobj = (char *)objp + obj_offset(cachep);
-       size = obj_size(cachep);
+       size = cachep->object_size;
 
        for (i = 0; i < size; i++) {
                char exp = POISON_FREE;
@@ -2047,10 +2155,10 @@ static void slab_destroy_debugcheck(struct kmem_cache *cachep, struct slab *slab
 
                if (cachep->flags & SLAB_POISON) {
 #ifdef CONFIG_DEBUG_PAGEALLOC
-                       if (cachep->buffer_size % PAGE_SIZE == 0 &&
+                       if (cachep->size % PAGE_SIZE == 0 &&
                                        OFF_SLAB(cachep))
                                kernel_map_pages(virt_to_page(objp),
-                                       cachep->buffer_size / PAGE_SIZE, 1);
+                                       cachep->size / PAGE_SIZE, 1);
                        else
                                check_poison_obj(cachep, objp);
 #else
@@ -2194,10 +2302,10 @@ static size_t calculate_slab_order(struct kmem_cache *cachep,
 
 static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp)
 {
-       if (g_cpucache_up == FULL)
+       if (slab_state >= FULL)
                return enable_cpucache(cachep, gfp);
 
-       if (g_cpucache_up == NONE) {
+       if (slab_state == DOWN) {
                /*
                 * Note: the first kmem_cache_create must create the cache
                 * that's used by kmalloc(24), otherwise the creation of
@@ -2212,16 +2320,16 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp)
                 */
                set_up_list3s(cachep, SIZE_AC);
                if (INDEX_AC == INDEX_L3)
-                       g_cpucache_up = PARTIAL_L3;
+                       slab_state = PARTIAL_L3;
                else
-                       g_cpucache_up = PARTIAL_AC;
+                       slab_state = PARTIAL_ARRAYCACHE;
        } else {
                cachep->array[smp_processor_id()] =
                        kmalloc(sizeof(struct arraycache_init), gfp);
 
-               if (g_cpucache_up == PARTIAL_AC) {
+               if (slab_state == PARTIAL_ARRAYCACHE) {
                        set_up_list3s(cachep, SIZE_L3);
-                       g_cpucache_up = PARTIAL_L3;
+                       slab_state = PARTIAL_L3;
                } else {
                        int node;
                        for_each_online_node(node) {
@@ -2247,7 +2355,7 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp)
 }
 
 /**
- * kmem_cache_create - Create a cache.
+ * __kmem_cache_create - Create a cache.
  * @name: A string which is used in /proc/slabinfo to identify this cache.
  * @size: The size of objects to be created in this cache.
  * @align: The required alignment for the objects.
@@ -2274,59 +2382,14 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp)
  * as davem.
  */
 struct kmem_cache *
-kmem_cache_create (const char *name, size_t size, size_t align,
+__kmem_cache_create (const char *name, size_t size, size_t align,
        unsigned long flags, void (*ctor)(void *))
 {
        size_t left_over, slab_size, ralign;
-       struct kmem_cache *cachep = NULL, *pc;
+       struct kmem_cache *cachep = NULL;
        gfp_t gfp;
 
-       /*
-        * Sanity checks... these are all serious usage bugs.
-        */
-       if (!name || in_interrupt() || (size < BYTES_PER_WORD) ||
-           size > KMALLOC_MAX_SIZE) {
-               printk(KERN_ERR "%s: Early error in slab %s\n", __func__,
-                               name);
-               BUG();
-       }
-
-       /*
-        * We use cache_chain_mutex to ensure a consistent view of
-        * cpu_online_mask as well.  Please see cpuup_callback
-        */
-       if (slab_is_available()) {
-               get_online_cpus();
-               mutex_lock(&cache_chain_mutex);
-       }
-
-       list_for_each_entry(pc, &cache_chain, next) {
-               char tmp;
-               int res;
-
-               /*
-                * This happens when the module gets unloaded and doesn't
-                * destroy its slab cache and no-one else reuses the vmalloc
-                * area of the module.  Print a warning.
-                */
-               res = probe_kernel_address(pc->name, tmp);
-               if (res) {
-                       printk(KERN_ERR
-                              "SLAB: cache with size %d has lost its name\n",
-                              pc->buffer_size);
-                       continue;
-               }
-
-               if (!strcmp(pc->name, name)) {
-                       printk(KERN_ERR
-                              "kmem_cache_create: duplicate cache %s\n", name);
-                       dump_stack();
-                       goto oops;
-               }
-       }
-
 #if DEBUG
-       WARN_ON(strchr(name, ' '));     /* It confuses parsers */
 #if FORCED_DEBUG
        /*
         * Enable redzoning and last user accounting, except for caches with
@@ -2415,11 +2478,12 @@ kmem_cache_create (const char *name, size_t size, size_t align,
        /* Get cache's description obj. */
        cachep = kmem_cache_zalloc(&cache_cache, gfp);
        if (!cachep)
-               goto oops;
+               return NULL;
 
        cachep->nodelists = (struct kmem_list3 **)&cachep->array[nr_cpu_ids];
+       cachep->object_size = size;
+       cachep->align = align;
 #if DEBUG
-       cachep->obj_size = size;
 
        /*
         * Both debugging options require word-alignment which is calculated
@@ -2442,7 +2506,7 @@ kmem_cache_create (const char *name, size_t size, size_t align,
        }
 #if FORCED_DEBUG && defined(CONFIG_DEBUG_PAGEALLOC)
        if (size >= malloc_sizes[INDEX_L3 + 1].cs_size
-           && cachep->obj_size > cache_line_size() && ALIGN(size, align) < PAGE_SIZE) {
+           && cachep->object_size > cache_line_size() && ALIGN(size, align) < PAGE_SIZE) {
                cachep->obj_offset += PAGE_SIZE - ALIGN(size, align);
                size = PAGE_SIZE;
        }
@@ -2471,8 +2535,7 @@ kmem_cache_create (const char *name, size_t size, size_t align,
                printk(KERN_ERR
                       "kmem_cache_create: couldn't create cache %s.\n", name);
                kmem_cache_free(&cache_cache, cachep);
-               cachep = NULL;
-               goto oops;
+               return NULL;
        }
        slab_size = ALIGN(cachep->num * sizeof(kmem_bufctl_t)
                          + sizeof(struct slab), align);
@@ -2508,10 +2571,10 @@ kmem_cache_create (const char *name, size_t size, size_t align,
        cachep->colour = left_over / cachep->colour_off;
        cachep->slab_size = slab_size;
        cachep->flags = flags;
-       cachep->gfpflags = 0;
+       cachep->allocflags = 0;
        if (CONFIG_ZONE_DMA_FLAG && (flags & SLAB_CACHE_DMA))
-               cachep->gfpflags |= GFP_DMA;
-       cachep->buffer_size = size;
+               cachep->allocflags |= GFP_DMA;
+       cachep->size = size;
        cachep->reciprocal_buffer_size = reciprocal_value(size);
 
        if (flags & CFLGS_OFF_SLAB) {
@@ -2530,8 +2593,7 @@ kmem_cache_create (const char *name, size_t size, size_t align,
 
        if (setup_cpu_cache(cachep, gfp)) {
                __kmem_cache_destroy(cachep);
-               cachep = NULL;
-               goto oops;
+               return NULL;
        }
 
        if (flags & SLAB_DEBUG_OBJECTS) {
@@ -2545,18 +2607,9 @@ kmem_cache_create (const char *name, size_t size, size_t align,
        }
 
        /* cache setup completed, link it into the list */
-       list_add(&cachep->next, &cache_chain);
-oops:
-       if (!cachep && (flags & SLAB_PANIC))
-               panic("kmem_cache_create(): failed to create slab `%s'\n",
-                     name);
-       if (slab_is_available()) {
-               mutex_unlock(&cache_chain_mutex);
-               put_online_cpus();
-       }
+       list_add(&cachep->list, &slab_caches);
        return cachep;
 }
-EXPORT_SYMBOL(kmem_cache_create);
 
 #if DEBUG
 static void check_irq_off(void)
@@ -2671,7 +2724,7 @@ out:
        return nr_freed;
 }
 
-/* Called with cache_chain_mutex held to protect against cpu hotplug */
+/* Called with slab_mutex held to protect against cpu hotplug */
 static int __cache_shrink(struct kmem_cache *cachep)
 {
        int ret = 0, i = 0;
@@ -2706,9 +2759,9 @@ int kmem_cache_shrink(struct kmem_cache *cachep)
        BUG_ON(!cachep || in_interrupt());
 
        get_online_cpus();
-       mutex_lock(&cache_chain_mutex);
+       mutex_lock(&slab_mutex);
        ret = __cache_shrink(cachep);
-       mutex_unlock(&cache_chain_mutex);
+       mutex_unlock(&slab_mutex);
        put_online_cpus();
        return ret;
 }
@@ -2736,15 +2789,15 @@ void kmem_cache_destroy(struct kmem_cache *cachep)
 
        /* Find the cache in the chain of caches. */
        get_online_cpus();
-       mutex_lock(&cache_chain_mutex);
+       mutex_lock(&slab_mutex);
        /*
         * the chain is never empty, cache_cache is never destroyed
         */
-       list_del(&cachep->next);
+       list_del(&cachep->list);
        if (__cache_shrink(cachep)) {
                slab_error(cachep, "Can't free all objects");
-               list_add(&cachep->next, &cache_chain);
-               mutex_unlock(&cache_chain_mutex);
+               list_add(&cachep->list, &slab_caches);
+               mutex_unlock(&slab_mutex);
                put_online_cpus();
                return;
        }
@@ -2753,7 +2806,7 @@ void kmem_cache_destroy(struct kmem_cache *cachep)
                rcu_barrier();
 
        __kmem_cache_destroy(cachep);
-       mutex_unlock(&cache_chain_mutex);
+       mutex_unlock(&slab_mutex);
        put_online_cpus();
 }
 EXPORT_SYMBOL(kmem_cache_destroy);
@@ -2840,10 +2893,10 @@ static void cache_init_objs(struct kmem_cache *cachep,
                                slab_error(cachep, "constructor overwrote the"
                                           " start of an object");
                }
-               if ((cachep->buffer_size % PAGE_SIZE) == 0 &&
+               if ((cachep->size % PAGE_SIZE) == 0 &&
                            OFF_SLAB(cachep) && cachep->flags & SLAB_POISON)
                        kernel_map_pages(virt_to_page(objp),
-                                        cachep->buffer_size / PAGE_SIZE, 0);
+                                        cachep->size / PAGE_SIZE, 0);
 #else
                if (cachep->ctor)
                        cachep->ctor(objp);
@@ -2857,9 +2910,9 @@ static void kmem_flagcheck(struct kmem_cache *cachep, gfp_t flags)
 {
        if (CONFIG_ZONE_DMA_FLAG) {
                if (flags & GFP_DMA)
-                       BUG_ON(!(cachep->gfpflags & GFP_DMA));
+                       BUG_ON(!(cachep->allocflags & GFP_DMA));
                else
-                       BUG_ON(cachep->gfpflags & GFP_DMA);
+                       BUG_ON(cachep->allocflags & GFP_DMA);
        }
 }
 
@@ -2918,8 +2971,8 @@ static void slab_map_pages(struct kmem_cache *cache, struct slab *slab,
                nr_pages <<= cache->gfporder;
 
        do {
-               page_set_cache(page, cache);
-               page_set_slab(page, slab);
+               page->slab_cache = cache;
+               page->slab_page = slab;
                page++;
        } while (--nr_pages);
 }
@@ -3057,7 +3110,7 @@ static void *cache_free_debugcheck(struct kmem_cache *cachep, void *objp,
        kfree_debugcheck(objp);
        page = virt_to_head_page(objp);
 
-       slabp = page_get_slab(page);
+       slabp = page->slab_page;
 
        if (cachep->flags & SLAB_RED_ZONE) {
                verify_redzone_free(cachep, objp);
@@ -3077,10 +3130,10 @@ static void *cache_free_debugcheck(struct kmem_cache *cachep, void *objp,
 #endif
        if (cachep->flags & SLAB_POISON) {
 #ifdef CONFIG_DEBUG_PAGEALLOC
-               if ((cachep->buffer_size % PAGE_SIZE)==0 && OFF_SLAB(cachep)) {
+               if ((cachep->size % PAGE_SIZE)==0 && OFF_SLAB(cachep)) {
                        store_stackinfo(cachep, objp, (unsigned long)caller);
                        kernel_map_pages(virt_to_page(objp),
-                                        cachep->buffer_size / PAGE_SIZE, 0);
+                                        cachep->size / PAGE_SIZE, 0);
                } else {
                        poison_obj(cachep, objp, POISON_FREE);
                }
@@ -3120,16 +3173,19 @@ bad:
 #define check_slabp(x,y) do { } while(0)
 #endif
 
-static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags)
+static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags,
+                                                       bool force_refill)
 {
        int batchcount;
        struct kmem_list3 *l3;
        struct array_cache *ac;
        int node;
 
-retry:
        check_irq_off();
        node = numa_mem_id();
+       if (unlikely(force_refill))
+               goto force_grow;
+retry:
        ac = cpu_cache_get(cachep);
        batchcount = ac->batchcount;
        if (!ac->touched && batchcount > BATCHREFILL_LIMIT) {
@@ -3179,8 +3235,8 @@ retry:
                        STATS_INC_ACTIVE(cachep);
                        STATS_SET_HIGH(cachep);
 
-                       ac->entry[ac->avail++] = slab_get_obj(cachep, slabp,
-                                                           node);
+                       ac_put_obj(cachep, ac, slab_get_obj(cachep, slabp,
+                                                                       node));
                }
                check_slabp(cachep, slabp);
 
@@ -3199,18 +3255,22 @@ alloc_done:
 
        if (unlikely(!ac->avail)) {
                int x;
+force_grow:
                x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL);
 
                /* cache_grow can reenable interrupts, then ac could change. */
                ac = cpu_cache_get(cachep);
-               if (!x && ac->avail == 0)       /* no objects in sight? abort */
+
+               /* no objects in sight? abort */
+               if (!x && (ac->avail == 0 || force_refill))
                        return NULL;
 
                if (!ac->avail)         /* objects refilled by interrupt? */
                        goto retry;
        }
        ac->touched = 1;
-       return ac->entry[--ac->avail];
+
+       return ac_get_obj(cachep, ac, flags, force_refill);
 }
 
 static inline void cache_alloc_debugcheck_before(struct kmem_cache *cachep,
@@ -3230,9 +3290,9 @@ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep,
                return objp;
        if (cachep->flags & SLAB_POISON) {
 #ifdef CONFIG_DEBUG_PAGEALLOC
-               if ((cachep->buffer_size % PAGE_SIZE) == 0 && OFF_SLAB(cachep))
+               if ((cachep->size % PAGE_SIZE) == 0 && OFF_SLAB(cachep))
                        kernel_map_pages(virt_to_page(objp),
-                                        cachep->buffer_size / PAGE_SIZE, 1);
+                                        cachep->size / PAGE_SIZE, 1);
                else
                        check_poison_obj(cachep, objp);
 #else
@@ -3261,8 +3321,8 @@ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep,
                struct slab *slabp;
                unsigned objnr;
 
-               slabp = page_get_slab(virt_to_head_page(objp));
-               objnr = (unsigned)(objp - slabp->s_mem) / cachep->buffer_size;
+               slabp = virt_to_head_page(objp)->slab_page;
+               objnr = (unsigned)(objp - slabp->s_mem) / cachep->size;
                slab_bufctl(slabp)[objnr] = BUFCTL_ACTIVE;
        }
 #endif
@@ -3285,30 +3345,42 @@ static bool slab_should_failslab(struct kmem_cache *cachep, gfp_t flags)
        if (cachep == &cache_cache)
                return false;
 
-       return should_failslab(obj_size(cachep), flags, cachep->flags);
+       return should_failslab(cachep->object_size, flags, cachep->flags);
 }
 
 static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
 {
        void *objp;
        struct array_cache *ac;
+       bool force_refill = false;
 
        check_irq_off();
 
        ac = cpu_cache_get(cachep);
        if (likely(ac->avail)) {
-               STATS_INC_ALLOCHIT(cachep);
                ac->touched = 1;
-               objp = ac->entry[--ac->avail];
-       } else {
-               STATS_INC_ALLOCMISS(cachep);
-               objp = cache_alloc_refill(cachep, flags);
+               objp = ac_get_obj(cachep, ac, flags, false);
+
                /*
-                * the 'ac' may be updated by cache_alloc_refill(),
-                * and kmemleak_erase() requires its correct value.
+                * Allow for the possibility all avail objects are not allowed
+                * by the current flags
                 */
-               ac = cpu_cache_get(cachep);
+               if (objp) {
+                       STATS_INC_ALLOCHIT(cachep);
+                       goto out;
+               }
+               force_refill = true;
        }
+
+       STATS_INC_ALLOCMISS(cachep);
+       objp = cache_alloc_refill(cachep, flags, force_refill);
+       /*
+        * the 'ac' may be updated by cache_alloc_refill(),
+        * and kmemleak_erase() requires its correct value.
+        */
+       ac = cpu_cache_get(cachep);
+
+out:
        /*
         * To avoid a false negative, if an object that is in one of the
         * per-CPU caches is leaked, we need to make sure kmemleak doesn't
@@ -3336,7 +3408,7 @@ static void *alternate_node_alloc(struct kmem_cache *cachep, gfp_t flags)
        if (cpuset_do_slab_mem_spread() && (cachep->flags & SLAB_MEM_SPREAD))
                nid_alloc = cpuset_slab_spread_node();
        else if (current->mempolicy)
-               nid_alloc = slab_node(current->mempolicy);
+               nid_alloc = slab_node();
        if (nid_alloc != nid_here)
                return ____cache_alloc_node(cachep, flags, nid_alloc);
        return NULL;
@@ -3368,7 +3440,7 @@ static void *fallback_alloc(struct kmem_cache *cache, gfp_t flags)
 
 retry_cpuset:
        cpuset_mems_cookie = get_mems_allowed();
-       zonelist = node_zonelist(slab_node(current->mempolicy), flags);
+       zonelist = node_zonelist(slab_node(), flags);
 
 retry:
        /*
@@ -3545,14 +3617,14 @@ __cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid,
   out:
        local_irq_restore(save_flags);
        ptr = cache_alloc_debugcheck_after(cachep, flags, ptr, caller);
-       kmemleak_alloc_recursive(ptr, obj_size(cachep), 1, cachep->flags,
+       kmemleak_alloc_recursive(ptr, cachep->object_size, 1, cachep->flags,
                                 flags);
 
        if (likely(ptr))
-               kmemcheck_slab_alloc(cachep, flags, ptr, obj_size(cachep));
+               kmemcheck_slab_alloc(cachep, flags, ptr, cachep->object_size);
 
        if (unlikely((flags & __GFP_ZERO) && ptr))
-               memset(ptr, 0, obj_size(cachep));
+               memset(ptr, 0, cachep->object_size);
 
        return ptr;
 }
@@ -3607,15 +3679,15 @@ __cache_alloc(struct kmem_cache *cachep, gfp_t flags, void *caller)
        objp = __do_cache_alloc(cachep, flags);
        local_irq_restore(save_flags);
        objp = cache_alloc_debugcheck_after(cachep, flags, objp, caller);
-       kmemleak_alloc_recursive(objp, obj_size(cachep), 1, cachep->flags,
+       kmemleak_alloc_recursive(objp, cachep->object_size, 1, cachep->flags,
                                 flags);
        prefetchw(objp);
 
        if (likely(objp))
-               kmemcheck_slab_alloc(cachep, flags, objp, obj_size(cachep));
+               kmemcheck_slab_alloc(cachep, flags, objp, cachep->object_size);
 
        if (unlikely((flags & __GFP_ZERO) && objp))
-               memset(objp, 0, obj_size(cachep));
+               memset(objp, 0, cachep->object_size);
 
        return objp;
 }
@@ -3630,9 +3702,12 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
        struct kmem_list3 *l3;
 
        for (i = 0; i < nr_objects; i++) {
-               void *objp = objpp[i];
+               void *objp;
                struct slab *slabp;
 
+               clear_obj_pfmemalloc(&objpp[i]);
+               objp = objpp[i];
+
                slabp = virt_to_slab(objp);
                l3 = cachep->nodelists[node];
                list_del(&slabp->list);
@@ -3731,7 +3806,7 @@ static inline void __cache_free(struct kmem_cache *cachep, void *objp,
        kmemleak_free_recursive(objp, cachep->flags);
        objp = cache_free_debugcheck(cachep, objp, caller);
 
-       kmemcheck_slab_free(cachep, objp, obj_size(cachep));
+       kmemcheck_slab_free(cachep, objp, cachep->object_size);
 
        /*
         * Skip calling cache_free_alien() when the platform is not numa.
@@ -3750,7 +3825,7 @@ static inline void __cache_free(struct kmem_cache *cachep, void *objp,
                cache_flusharray(cachep, ac);
        }
 
-       ac->entry[ac->avail++] = objp;
+       ac_put_obj(cachep, ac, objp);
 }
 
 /**
@@ -3766,7 +3841,7 @@ void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags)
        void *ret = __cache_alloc(cachep, flags, __builtin_return_address(0));
 
        trace_kmem_cache_alloc(_RET_IP_, ret,
-                              obj_size(cachep), cachep->buffer_size, flags);
+                              cachep->object_size, cachep->size, flags);
 
        return ret;
 }
@@ -3794,7 +3869,7 @@ void *kmem_cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid)
                                       __builtin_return_address(0));
 
        trace_kmem_cache_alloc_node(_RET_IP_, ret,
-                                   obj_size(cachep), cachep->buffer_size,
+                                   cachep->object_size, cachep->size,
                                    flags, nodeid);
 
        return ret;
@@ -3876,7 +3951,7 @@ static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,
        ret = __cache_alloc(cachep, flags, caller);
 
        trace_kmalloc((unsigned long) caller, ret,
-                     size, cachep->buffer_size, flags);
+                     size, cachep->size, flags);
 
        return ret;
 }
@@ -3916,9 +3991,9 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp)
        unsigned long flags;
 
        local_irq_save(flags);
-       debug_check_no_locks_freed(objp, obj_size(cachep));
+       debug_check_no_locks_freed(objp, cachep->object_size);
        if (!(cachep->flags & SLAB_DEBUG_OBJECTS))
-               debug_check_no_obj_freed(objp, obj_size(cachep));
+               debug_check_no_obj_freed(objp, cachep->object_size);
        __cache_free(cachep, objp, __builtin_return_address(0));
        local_irq_restore(flags);
 
@@ -3947,8 +4022,9 @@ void kfree(const void *objp)
        local_irq_save(flags);
        kfree_debugcheck(objp);
        c = virt_to_cache(objp);
-       debug_check_no_locks_freed(objp, obj_size(c));
-       debug_check_no_obj_freed(objp, obj_size(c));
+       debug_check_no_locks_freed(objp, c->object_size);
+
+       debug_check_no_obj_freed(objp, c->object_size);
        __cache_free(c, (void *)objp, __builtin_return_address(0));
        local_irq_restore(flags);
 }
@@ -3956,7 +4032,7 @@ EXPORT_SYMBOL(kfree);
 
 unsigned int kmem_cache_size(struct kmem_cache *cachep)
 {
-       return obj_size(cachep);
+       return cachep->object_size;
 }
 EXPORT_SYMBOL(kmem_cache_size);
 
@@ -4030,7 +4106,7 @@ static int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp)
        return 0;
 
 fail:
-       if (!cachep->next.next) {
+       if (!cachep->list.next) {
                /* Cache is not active yet. Roll back what we did */
                node--;
                while (node >= 0) {
@@ -4065,7 +4141,7 @@ static void do_ccupdate_local(void *info)
        new->new[smp_processor_id()] = old;
 }
 
-/* Always called with the cache_chain_mutex held */
+/* Always called with the slab_mutex held */
 static int do_tune_cpucache(struct kmem_cache *cachep, int limit,
                                int batchcount, int shared, gfp_t gfp)
 {
@@ -4109,7 +4185,7 @@ static int do_tune_cpucache(struct kmem_cache *cachep, int limit,
        return alloc_kmemlist(cachep, gfp);
 }
 
-/* Called with cache_chain_mutex held always */
+/* Called with slab_mutex held always */
 static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp)
 {
        int err;
@@ -4124,13 +4200,13 @@ static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp)
         * The numbers are guessed, we should auto-tune as described by
         * Bonwick.
         */
-       if (cachep->buffer_size > 131072)
+       if (cachep->size > 131072)
                limit = 1;
-       else if (cachep->buffer_size > PAGE_SIZE)
+       else if (cachep->size > PAGE_SIZE)
                limit = 8;
-       else if (cachep->buffer_size > 1024)
+       else if (cachep->size > 1024)
                limit = 24;
-       else if (cachep->buffer_size > 256)
+       else if (cachep->size > 256)
                limit = 54;
        else
                limit = 120;
@@ -4145,7 +4221,7 @@ static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp)
         * to a larger limit. Thus disabled by default.
         */
        shared = 0;
-       if (cachep->buffer_size <= PAGE_SIZE && num_possible_cpus() > 1)
+       if (cachep->size <= PAGE_SIZE && num_possible_cpus() > 1)
                shared = 8;
 
 #if DEBUG
@@ -4211,11 +4287,11 @@ static void cache_reap(struct work_struct *w)
        int node = numa_mem_id();
        struct delayed_work *work = to_delayed_work(w);
 
-       if (!mutex_trylock(&cache_chain_mutex))
+       if (!mutex_trylock(&slab_mutex))
                /* Give up. Setup the next iteration. */
                goto out;
 
-       list_for_each_entry(searchp, &cache_chain, next) {
+       list_for_each_entry(searchp, &slab_caches, list) {
                check_irq_on();
 
                /*
@@ -4253,7 +4329,7 @@ next:
                cond_resched();
        }
        check_irq_on();
-       mutex_unlock(&cache_chain_mutex);
+       mutex_unlock(&slab_mutex);
        next_reap_node();
 out:
        /* Set up the next iteration */
@@ -4289,26 +4365,26 @@ static void *s_start(struct seq_file *m, loff_t *pos)
 {
        loff_t n = *pos;
 
-       mutex_lock(&cache_chain_mutex);
+       mutex_lock(&slab_mutex);
        if (!n)
                print_slabinfo_header(m);
 
-       return seq_list_start(&cache_chain, *pos);
+       return seq_list_start(&slab_caches, *pos);
 }
 
 static void *s_next(struct seq_file *m, void *p, loff_t *pos)
 {
-       return seq_list_next(p, &cache_chain, pos);
+       return seq_list_next(p, &slab_caches, pos);
 }
 
 static void s_stop(struct seq_file *m, void *p)
 {
-       mutex_unlock(&cache_chain_mutex);
+       mutex_unlock(&slab_mutex);
 }
 
 static int s_show(struct seq_file *m, void *p)
 {
-       struct kmem_cache *cachep = list_entry(p, struct kmem_cache, next);
+       struct kmem_cache *cachep = list_entry(p, struct kmem_cache, list);
        struct slab *slabp;
        unsigned long active_objs;
        unsigned long num_objs;
@@ -4364,7 +4440,7 @@ static int s_show(struct seq_file *m, void *p)
                printk(KERN_ERR "slab: cache %s error: %s\n", name, error);
 
        seq_printf(m, "%-17s %6lu %6lu %6u %4u %4d",
-                  name, active_objs, num_objs, cachep->buffer_size,
+                  name, active_objs, num_objs, cachep->size,
                   cachep->num, (1 << cachep->gfporder));
        seq_printf(m, " : tunables %4u %4u %4u",
                   cachep->limit, cachep->batchcount, cachep->shared);
@@ -4454,9 +4530,9 @@ static ssize_t slabinfo_write(struct file *file, const char __user *buffer,
                return -EINVAL;
 
        /* Find the cache in the chain of caches. */
-       mutex_lock(&cache_chain_mutex);
+       mutex_lock(&slab_mutex);
        res = -EINVAL;
-       list_for_each_entry(cachep, &cache_chain, next) {
+       list_for_each_entry(cachep, &slab_caches, list) {
                if (!strcmp(cachep->name, kbuf)) {
                        if (limit < 1 || batchcount < 1 ||
                                        batchcount > limit || shared < 0) {
@@ -4469,7 +4545,7 @@ static ssize_t slabinfo_write(struct file *file, const char __user *buffer,
                        break;
                }
        }
-       mutex_unlock(&cache_chain_mutex);
+       mutex_unlock(&slab_mutex);
        if (res >= 0)
                res = count;
        return res;
@@ -4492,8 +4568,8 @@ static const struct file_operations proc_slabinfo_operations = {
 
 static void *leaks_start(struct seq_file *m, loff_t *pos)
 {
-       mutex_lock(&cache_chain_mutex);
-       return seq_list_start(&cache_chain, *pos);
+       mutex_lock(&slab_mutex);
+       return seq_list_start(&slab_caches, *pos);
 }
 
 static inline int add_caller(unsigned long *n, unsigned long v)
@@ -4532,7 +4608,7 @@ static void handle_slab(unsigned long *n, struct kmem_cache *c, struct slab *s)
        int i;
        if (n[0] == n[1])
                return;
-       for (i = 0, p = s->s_mem; i < c->num; i++, p += c->buffer_size) {
+       for (i = 0, p = s->s_mem; i < c->num; i++, p += c->size) {
                if (slab_bufctl(s)[i] != BUFCTL_ACTIVE)
                        continue;
                if (!add_caller(n, (unsigned long)*dbg_userword(c, p)))
@@ -4558,7 +4634,7 @@ static void show_symbol(struct seq_file *m, unsigned long address)
 
 static int leaks_show(struct seq_file *m, void *p)
 {
-       struct kmem_cache *cachep = list_entry(p, struct kmem_cache, next);
+       struct kmem_cache *cachep = list_entry(p, struct kmem_cache, list);
        struct slab *slabp;
        struct kmem_list3 *l3;
        const char *name;
@@ -4592,17 +4668,17 @@ static int leaks_show(struct seq_file *m, void *p)
        name = cachep->name;
        if (n[0] == n[1]) {
                /* Increase the buffer size */
-               mutex_unlock(&cache_chain_mutex);
+               mutex_unlock(&slab_mutex);
                m->private = kzalloc(n[0] * 4 * sizeof(unsigned long), GFP_KERNEL);
                if (!m->private) {
                        /* Too bad, we are really out */
                        m->private = n;
-                       mutex_lock(&cache_chain_mutex);
+                       mutex_lock(&slab_mutex);
                        return -ENOMEM;
                }
                *(unsigned long *)m->private = n[0] * 2;
                kfree(n);
-               mutex_lock(&cache_chain_mutex);
+               mutex_lock(&slab_mutex);
                /* Now make sure this entry will be retried */
                m->count = m->size;
                return 0;
@@ -4677,6 +4753,6 @@ size_t ksize(const void *objp)
        if (unlikely(objp == ZERO_SIZE_PTR))
                return 0;
 
-       return obj_size(virt_to_cache(objp));
+       return virt_to_cache(objp)->object_size;
 }
 EXPORT_SYMBOL(ksize);
diff --git a/mm/slab.h b/mm/slab.h
new file mode 100644 (file)
index 0000000..db7848c
--- /dev/null
+++ b/mm/slab.h
@@ -0,0 +1,33 @@
+#ifndef MM_SLAB_H
+#define MM_SLAB_H
+/*
+ * Internal slab definitions
+ */
+
+/*
+ * State of the slab allocator.
+ *
+ * This is used to describe the states of the allocator during bootup.
+ * Allocators use this to gradually bootstrap themselves. Most allocators
+ * have the problem that the structures used for managing slab caches are
+ * allocated from slab caches themselves.
+ */
+enum slab_state {
+       DOWN,                   /* No slab functionality yet */
+       PARTIAL,                /* SLUB: kmem_cache_node available */
+       PARTIAL_ARRAYCACHE,     /* SLAB: kmalloc size for arraycache available */
+       PARTIAL_L3,             /* SLAB: kmalloc size for l3 struct available */
+       UP,                     /* Slab caches usable but not all extras yet */
+       FULL                    /* Everything is working */
+};
+
+extern enum slab_state slab_state;
+
+/* The slab cache mutex protects the management structures during changes */
+extern struct mutex slab_mutex;
+extern struct list_head slab_caches;
+
+struct kmem_cache *__kmem_cache_create(const char *name, size_t size,
+       size_t align, unsigned long flags, void (*ctor)(void *));
+
+#endif
diff --git a/mm/slab_common.c b/mm/slab_common.c
new file mode 100644 (file)
index 0000000..aa3ca5b
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Slab allocator functions that are independent of the allocator strategy
+ *
+ * (C) 2012 Christoph Lameter <cl@linux.com>
+ */
+#include <linux/slab.h>
+
+#include <linux/mm.h>
+#include <linux/poison.h>
+#include <linux/interrupt.h>
+#include <linux/memory.h>
+#include <linux/compiler.h>
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/uaccess.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <asm/page.h>
+
+#include "slab.h"
+
+enum slab_state slab_state;
+LIST_HEAD(slab_caches);
+DEFINE_MUTEX(slab_mutex);
+
+/*
+ * kmem_cache_create - Create a cache.
+ * @name: A string which is used in /proc/slabinfo to identify this cache.
+ * @size: The size of objects to be created in this cache.
+ * @align: The required alignment for the objects.
+ * @flags: SLAB flags
+ * @ctor: A constructor for the objects.
+ *
+ * Returns a ptr to the cache on success, NULL on failure.
+ * Cannot be called within a interrupt, but can be interrupted.
+ * The @ctor is run when new pages are allocated by the cache.
+ *
+ * The flags are
+ *
+ * %SLAB_POISON - Poison the slab with a known test pattern (a5a5a5a5)
+ * to catch references to uninitialised memory.
+ *
+ * %SLAB_RED_ZONE - Insert `Red' zones around the allocated memory to check
+ * for buffer overruns.
+ *
+ * %SLAB_HWCACHE_ALIGN - Align the objects in this cache to a hardware
+ * cacheline.  This can be beneficial if you're counting cycles as closely
+ * as davem.
+ */
+
+struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align,
+               unsigned long flags, void (*ctor)(void *))
+{
+       struct kmem_cache *s = NULL;
+
+#ifdef CONFIG_DEBUG_VM
+       if (!name || in_interrupt() || size < sizeof(void *) ||
+               size > KMALLOC_MAX_SIZE) {
+               printk(KERN_ERR "kmem_cache_create(%s) integrity check"
+                       " failed\n", name);
+               goto out;
+       }
+#endif
+
+       get_online_cpus();
+       mutex_lock(&slab_mutex);
+
+#ifdef CONFIG_DEBUG_VM
+       list_for_each_entry(s, &slab_caches, list) {
+               char tmp;
+               int res;
+
+               /*
+                * This happens when the module gets unloaded and doesn't
+                * destroy its slab cache and no-one else reuses the vmalloc
+                * area of the module.  Print a warning.
+                */
+               res = probe_kernel_address(s->name, tmp);
+               if (res) {
+                       printk(KERN_ERR
+                              "Slab cache with size %d has lost its name\n",
+                              s->object_size);
+                       continue;
+               }
+
+               if (!strcmp(s->name, name)) {
+                       printk(KERN_ERR "kmem_cache_create(%s): Cache name"
+                               " already exists.\n",
+                               name);
+                       dump_stack();
+                       s = NULL;
+                       goto oops;
+               }
+       }
+
+       WARN_ON(strchr(name, ' '));     /* It confuses parsers */
+#endif
+
+       s = __kmem_cache_create(name, size, align, flags, ctor);
+
+#ifdef CONFIG_DEBUG_VM
+oops:
+#endif
+       mutex_unlock(&slab_mutex);
+       put_online_cpus();
+
+#ifdef CONFIG_DEBUG_VM
+out:
+#endif
+       if (!s && (flags & SLAB_PANIC))
+               panic("kmem_cache_create: Failed to create slab '%s'\n", name);
+
+       return s;
+}
+EXPORT_SYMBOL(kmem_cache_create);
+
+int slab_is_available(void)
+{
+       return slab_state >= UP;
+}
index 8105be42cad13b9ba6d231de8fad5bf29af6de2f..45d4ca79933a84eec7e9e824f5d3117f3dc4dc64 100644 (file)
--- a/mm/slob.c
+++ b/mm/slob.c
@@ -59,6 +59,8 @@
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
+#include "slab.h"
+
 #include <linux/mm.h>
 #include <linux/swap.h> /* struct reclaim_state */
 #include <linux/cache.h>
@@ -91,36 +93,6 @@ struct slob_block {
 };
 typedef struct slob_block slob_t;
 
-/*
- * We use struct page fields to manage some slob allocation aspects,
- * however to avoid the horrible mess in include/linux/mm_types.h, we'll
- * just define our own struct page type variant here.
- */
-struct slob_page {
-       union {
-               struct {
-                       unsigned long flags;    /* mandatory */
-                       atomic_t _count;        /* mandatory */
-                       slobidx_t units;        /* free units left in page */
-                       unsigned long pad[2];
-                       slob_t *free;           /* first free slob_t in page */
-                       struct list_head list;  /* linked list of free pages */
-               };
-               struct page page;
-       };
-};
-static inline void struct_slob_page_wrong_size(void)
-{ BUILD_BUG_ON(sizeof(struct slob_page) != sizeof(struct page)); }
-
-/*
- * free_slob_page: call before a slob_page is returned to the page allocator.
- */
-static inline void free_slob_page(struct slob_page *sp)
-{
-       reset_page_mapcount(&sp->page);
-       sp->page.mapping = NULL;
-}
-
 /*
  * All partially free slob pages go on these lists.
  */
@@ -130,47 +102,24 @@ static LIST_HEAD(free_slob_small);
 static LIST_HEAD(free_slob_medium);
 static LIST_HEAD(free_slob_large);
 
-/*
- * is_slob_page: True for all slob pages (false for bigblock pages)
- */
-static inline int is_slob_page(struct slob_page *sp)
-{
-       return PageSlab((struct page *)sp);
-}
-
-static inline void set_slob_page(struct slob_page *sp)
-{
-       __SetPageSlab((struct page *)sp);
-}
-
-static inline void clear_slob_page(struct slob_page *sp)
-{
-       __ClearPageSlab((struct page *)sp);
-}
-
-static inline struct slob_page *slob_page(const void *addr)
-{
-       return (struct slob_page *)virt_to_page(addr);
-}
-
 /*
  * slob_page_free: true for pages on free_slob_pages list.
  */
-static inline int slob_page_free(struct slob_page *sp)
+static inline int slob_page_free(struct page *sp)
 {
-       return PageSlobFree((struct page *)sp);
+       return PageSlobFree(sp);
 }
 
-static void set_slob_page_free(struct slob_page *sp, struct list_head *list)
+static void set_slob_page_free(struct page *sp, struct list_head *list)
 {
        list_add(&sp->list, list);
-       __SetPageSlobFree((struct page *)sp);
+       __SetPageSlobFree(sp);
 }
 
-static inline void clear_slob_page_free(struct slob_page *sp)
+static inline void clear_slob_page_free(struct page *sp)
 {
        list_del(&sp->list);
-       __ClearPageSlobFree((struct page *)sp);
+       __ClearPageSlobFree(sp);
 }
 
 #define SLOB_UNIT sizeof(slob_t)
@@ -267,12 +216,12 @@ static void slob_free_pages(void *b, int order)
 /*
  * Allocate a slob block within a given slob_page sp.
  */
-static void *slob_page_alloc(struct slob_page *sp, size_t size, int align)
+static void *slob_page_alloc(struct page *sp, size_t size, int align)
 {
        slob_t *prev, *cur, *aligned = NULL;
        int delta = 0, units = SLOB_UNITS(size);
 
-       for (prev = NULL, cur = sp->free; ; prev = cur, cur = slob_next(cur)) {
+       for (prev = NULL, cur = sp->freelist; ; prev = cur, cur = slob_next(cur)) {
                slobidx_t avail = slob_units(cur);
 
                if (align) {
@@ -296,12 +245,12 @@ static void *slob_page_alloc(struct slob_page *sp, size_t size, int align)
                                if (prev)
                                        set_slob(prev, slob_units(prev), next);
                                else
-                                       sp->free = next;
+                                       sp->freelist = next;
                        } else { /* fragment */
                                if (prev)
                                        set_slob(prev, slob_units(prev), cur + units);
                                else
-                                       sp->free = cur + units;
+                                       sp->freelist = cur + units;
                                set_slob(cur + units, avail - units, next);
                        }
 
@@ -320,7 +269,7 @@ static void *slob_page_alloc(struct slob_page *sp, size_t size, int align)
  */
 static void *slob_alloc(size_t size, gfp_t gfp, int align, int node)
 {
-       struct slob_page *sp;
+       struct page *sp;
        struct list_head *prev;
        struct list_head *slob_list;
        slob_t *b = NULL;
@@ -341,7 +290,7 @@ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node)
                 * If there's a node specification, search for a partial
                 * page with a matching node id in the freelist.
                 */
-               if (node != -1 && page_to_nid(&sp->page) != node)
+               if (node != -1 && page_to_nid(sp) != node)
                        continue;
 #endif
                /* Enough room on this page? */
@@ -369,12 +318,12 @@ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node)
                b = slob_new_pages(gfp & ~__GFP_ZERO, 0, node);
                if (!b)
                        return NULL;
-               sp = slob_page(b);
-               set_slob_page(sp);
+               sp = virt_to_page(b);
+               __SetPageSlab(sp);
 
                spin_lock_irqsave(&slob_lock, flags);
                sp->units = SLOB_UNITS(PAGE_SIZE);
-               sp->free = b;
+               sp->freelist = b;
                INIT_LIST_HEAD(&sp->list);
                set_slob(b, SLOB_UNITS(PAGE_SIZE), b + SLOB_UNITS(PAGE_SIZE));
                set_slob_page_free(sp, slob_list);
@@ -392,7 +341,7 @@ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node)
  */
 static void slob_free(void *block, int size)
 {
-       struct slob_page *sp;
+       struct page *sp;
        slob_t *prev, *next, *b = (slob_t *)block;
        slobidx_t units;
        unsigned long flags;
@@ -402,7 +351,7 @@ static void slob_free(void *block, int size)
                return;
        BUG_ON(!size);
 
-       sp = slob_page(block);
+       sp = virt_to_page(block);
        units = SLOB_UNITS(size);
 
        spin_lock_irqsave(&slob_lock, flags);
@@ -412,8 +361,8 @@ static void slob_free(void *block, int size)
                if (slob_page_free(sp))
                        clear_slob_page_free(sp);
                spin_unlock_irqrestore(&slob_lock, flags);
-               clear_slob_page(sp);
-               free_slob_page(sp);
+               __ClearPageSlab(sp);
+               reset_page_mapcount(sp);
                slob_free_pages(b, 0);
                return;
        }
@@ -421,7 +370,7 @@ static void slob_free(void *block, int size)
        if (!slob_page_free(sp)) {
                /* This slob page is about to become partially free. Easy! */
                sp->units = units;
-               sp->free = b;
+               sp->freelist = b;
                set_slob(b, units,
                        (void *)((unsigned long)(b +
                                        SLOB_UNITS(PAGE_SIZE)) & PAGE_MASK));
@@ -441,15 +390,15 @@ static void slob_free(void *block, int size)
         */
        sp->units += units;
 
-       if (b < sp->free) {
-               if (b + units == sp->free) {
-                       units += slob_units(sp->free);
-                       sp->free = slob_next(sp->free);
+       if (b < (slob_t *)sp->freelist) {
+               if (b + units == sp->freelist) {
+                       units += slob_units(sp->freelist);
+                       sp->freelist = slob_next(sp->freelist);
                }
-               set_slob(b, units, sp->free);
-               sp->free = b;
+               set_slob(b, units, sp->freelist);
+               sp->freelist = b;
        } else {
-               prev = sp->free;
+               prev = sp->freelist;
                next = slob_next(prev);
                while (b > next) {
                        prev = next;
@@ -522,7 +471,7 @@ EXPORT_SYMBOL(__kmalloc_node);
 
 void kfree(const void *block)
 {
-       struct slob_page *sp;
+       struct page *sp;
 
        trace_kfree(_RET_IP_, block);
 
@@ -530,43 +479,36 @@ void kfree(const void *block)
                return;
        kmemleak_free(block);
 
-       sp = slob_page(block);
-       if (is_slob_page(sp)) {
+       sp = virt_to_page(block);
+       if (PageSlab(sp)) {
                int align = max(ARCH_KMALLOC_MINALIGN, ARCH_SLAB_MINALIGN);
                unsigned int *m = (unsigned int *)(block - align);
                slob_free(m, *m + align);
        } else
-               put_page(&sp->page);
+               put_page(sp);
 }
 EXPORT_SYMBOL(kfree);
 
 /* can't use ksize for kmem_cache_alloc memory, only kmalloc */
 size_t ksize(const void *block)
 {
-       struct slob_page *sp;
+       struct page *sp;
 
        BUG_ON(!block);
        if (unlikely(block == ZERO_SIZE_PTR))
                return 0;
 
-       sp = slob_page(block);
-       if (is_slob_page(sp)) {
+       sp = virt_to_page(block);
+       if (PageSlab(sp)) {
                int align = max(ARCH_KMALLOC_MINALIGN, ARCH_SLAB_MINALIGN);
                unsigned int *m = (unsigned int *)(block - align);
                return SLOB_UNITS(*m) * SLOB_UNIT;
        } else
-               return sp->page.private;
+               return sp->private;
 }
 EXPORT_SYMBOL(ksize);
 
-struct kmem_cache {
-       unsigned int size, align;
-       unsigned long flags;
-       const char *name;
-       void (*ctor)(void *);
-};
-
-struct kmem_cache *kmem_cache_create(const char *name, size_t size,
+struct kmem_cache *__kmem_cache_create(const char *name, size_t size,
        size_t align, unsigned long flags, void (*ctor)(void *))
 {
        struct kmem_cache *c;
@@ -589,13 +531,12 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size,
                        c->align = ARCH_SLAB_MINALIGN;
                if (c->align < align)
                        c->align = align;
-       } else if (flags & SLAB_PANIC)
-               panic("Cannot create slab cache %s\n", name);
 
-       kmemleak_alloc(c, sizeof(struct kmem_cache), 1, GFP_KERNEL);
+               kmemleak_alloc(c, sizeof(struct kmem_cache), 1, GFP_KERNEL);
+               c->refcount = 1;
+       }
        return c;
 }
-EXPORT_SYMBOL(kmem_cache_create);
 
 void kmem_cache_destroy(struct kmem_cache *c)
 {
@@ -678,19 +619,12 @@ int kmem_cache_shrink(struct kmem_cache *d)
 }
 EXPORT_SYMBOL(kmem_cache_shrink);
 
-static unsigned int slob_ready __read_mostly;
-
-int slab_is_available(void)
-{
-       return slob_ready;
-}
-
 void __init kmem_cache_init(void)
 {
-       slob_ready = 1;
+       slab_state = UP;
 }
 
 void __init kmem_cache_init_late(void)
 {
-       /* Nothing to do */
+       slab_state = FULL;
 }
index 8c691fa1cf3c78a91fa301bcd0cd323df1b28a1e..8f78e25770317e63a37c741d66c2761f71afa2e9 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -16,6 +16,7 @@
 #include <linux/interrupt.h>
 #include <linux/bitops.h>
 #include <linux/slab.h>
+#include "slab.h"
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/kmemcheck.h>
 
 #include <trace/events/kmem.h>
 
+#include "internal.h"
+
 /*
  * Lock order:
- *   1. slub_lock (Global Semaphore)
+ *   1. slab_mutex (Global Mutex)
  *   2. node->list_lock
  *   3. slab_lock(page) (Only on some arches and for debugging)
  *
- *   slub_lock
+ *   slab_mutex
  *
- *   The role of the slub_lock is to protect the list of all the slabs
+ *   The role of the slab_mutex is to protect the list of all the slabs
  *   and to synchronize major metadata changes to slab cache structures.
  *
  *   The slab_lock is only used for debugging and on arches that do not
@@ -182,17 +185,6 @@ static int kmem_size = sizeof(struct kmem_cache);
 static struct notifier_block slab_notifier;
 #endif
 
-static enum {
-       DOWN,           /* No slab functionality available */
-       PARTIAL,        /* Kmem_cache_node works */
-       UP,             /* Everything works but does not show up in sysfs */
-       SYSFS           /* Sysfs up */
-} slab_state = DOWN;
-
-/* A list of all slab caches on the system */
-static DECLARE_RWSEM(slub_lock);
-static LIST_HEAD(slab_caches);
-
 /*
  * Tracking user of a slab.
  */
@@ -237,11 +229,6 @@ static inline void stat(const struct kmem_cache *s, enum stat_item si)
  *                     Core slab cache functions
  *******************************************************************/
 
-int slab_is_available(void)
-{
-       return slab_state >= UP;
-}
-
 static inline struct kmem_cache_node *get_node(struct kmem_cache *s, int node)
 {
        return s->node[node];
@@ -311,7 +298,7 @@ static inline size_t slab_ksize(const struct kmem_cache *s)
         * and whatever may come after it.
         */
        if (s->flags & (SLAB_RED_ZONE | SLAB_POISON))
-               return s->objsize;
+               return s->object_size;
 
 #endif
        /*
@@ -609,11 +596,11 @@ static void print_trailer(struct kmem_cache *s, struct page *page, u8 *p)
        if (p > addr + 16)
                print_section("Bytes b4 ", p - 16, 16);
 
-       print_section("Object ", p, min_t(unsigned long, s->objsize,
+       print_section("Object ", p, min_t(unsigned long, s->object_size,
                                PAGE_SIZE));
        if (s->flags & SLAB_RED_ZONE)
-               print_section("Redzone ", p + s->objsize,
-                       s->inuse - s->objsize);
+               print_section("Redzone ", p + s->object_size,
+                       s->inuse - s->object_size);
 
        if (s->offset)
                off = s->offset + sizeof(void *);
@@ -655,12 +642,12 @@ static void init_object(struct kmem_cache *s, void *object, u8 val)
        u8 *p = object;
 
        if (s->flags & __OBJECT_POISON) {
-               memset(p, POISON_FREE, s->objsize - 1);
-               p[s->objsize - 1] = POISON_END;
+               memset(p, POISON_FREE, s->object_size - 1);
+               p[s->object_size - 1] = POISON_END;
        }
 
        if (s->flags & SLAB_RED_ZONE)
-               memset(p + s->objsize, val, s->inuse - s->objsize);
+               memset(p + s->object_size, val, s->inuse - s->object_size);
 }
 
 static void restore_bytes(struct kmem_cache *s, char *message, u8 data,
@@ -705,10 +692,10 @@ static int check_bytes_and_report(struct kmem_cache *s, struct page *page,
  *     Poisoning uses 0x6b (POISON_FREE) and the last byte is
  *     0xa5 (POISON_END)
  *
- * object + s->objsize
+ * object + s->object_size
  *     Padding to reach word boundary. This is also used for Redzoning.
  *     Padding is extended by another word if Redzoning is enabled and
- *     objsize == inuse.
+ *     object_size == inuse.
  *
  *     We fill with 0xbb (RED_INACTIVE) for inactive objects and with
  *     0xcc (RED_ACTIVE) for objects in use.
@@ -727,7 +714,7 @@ static int check_bytes_and_report(struct kmem_cache *s, struct page *page,
  * object + s->size
  *     Nothing is used beyond s->size.
  *
- * If slabcaches are merged then the objsize and inuse boundaries are mostly
+ * If slabcaches are merged then the object_size and inuse boundaries are mostly
  * ignored. And therefore no slab options that rely on these boundaries
  * may be used with merged slabcaches.
  */
@@ -787,25 +774,25 @@ static int check_object(struct kmem_cache *s, struct page *page,
                                        void *object, u8 val)
 {
        u8 *p = object;
-       u8 *endobject = object + s->objsize;
+       u8 *endobject = object + s->object_size;
 
        if (s->flags & SLAB_RED_ZONE) {
                if (!check_bytes_and_report(s, page, object, "Redzone",
-                       endobject, val, s->inuse - s->objsize))
+                       endobject, val, s->inuse - s->object_size))
                        return 0;
        } else {
-               if ((s->flags & SLAB_POISON) && s->objsize < s->inuse) {
+               if ((s->flags & SLAB_POISON) && s->object_size < s->inuse) {
                        check_bytes_and_report(s, page, p, "Alignment padding",
-                               endobject, POISON_INUSE, s->inuse - s->objsize);
+                               endobject, POISON_INUSE, s->inuse - s->object_size);
                }
        }
 
        if (s->flags & SLAB_POISON) {
                if (val != SLUB_RED_ACTIVE && (s->flags & __OBJECT_POISON) &&
                        (!check_bytes_and_report(s, page, p, "Poison", p,
-                                       POISON_FREE, s->objsize - 1) ||
+                                       POISON_FREE, s->object_size - 1) ||
                         !check_bytes_and_report(s, page, p, "Poison",
-                               p + s->objsize - 1, POISON_END, 1)))
+                               p + s->object_size - 1, POISON_END, 1)))
                        return 0;
                /*
                 * check_pad_bytes cleans up on its own.
@@ -926,7 +913,7 @@ static void trace(struct kmem_cache *s, struct page *page, void *object,
                        page->freelist);
 
                if (!alloc)
-                       print_section("Object ", (void *)object, s->objsize);
+                       print_section("Object ", (void *)object, s->object_size);
 
                dump_stack();
        }
@@ -942,14 +929,14 @@ static inline int slab_pre_alloc_hook(struct kmem_cache *s, gfp_t flags)
        lockdep_trace_alloc(flags);
        might_sleep_if(flags & __GFP_WAIT);
 
-       return should_failslab(s->objsize, flags, s->flags);
+       return should_failslab(s->object_size, flags, s->flags);
 }
 
 static inline void slab_post_alloc_hook(struct kmem_cache *s, gfp_t flags, void *object)
 {
        flags &= gfp_allowed_mask;
        kmemcheck_slab_alloc(s, flags, object, slab_ksize(s));
-       kmemleak_alloc_recursive(object, s->objsize, 1, s->flags, flags);
+       kmemleak_alloc_recursive(object, s->object_size, 1, s->flags, flags);
 }
 
 static inline void slab_free_hook(struct kmem_cache *s, void *x)
@@ -966,13 +953,13 @@ static inline void slab_free_hook(struct kmem_cache *s, void *x)
                unsigned long flags;
 
                local_irq_save(flags);
-               kmemcheck_slab_free(s, x, s->objsize);
-               debug_check_no_locks_freed(x, s->objsize);
+               kmemcheck_slab_free(s, x, s->object_size);
+               debug_check_no_locks_freed(x, s->object_size);
                local_irq_restore(flags);
        }
 #endif
        if (!(s->flags & SLAB_DEBUG_OBJECTS))
-               debug_check_no_obj_freed(x, s->objsize);
+               debug_check_no_obj_freed(x, s->object_size);
 }
 
 /*
@@ -1207,7 +1194,7 @@ out:
 
 __setup("slub_debug", setup_slub_debug);
 
-static unsigned long kmem_cache_flags(unsigned long objsize,
+static unsigned long kmem_cache_flags(unsigned long object_size,
        unsigned long flags, const char *name,
        void (*ctor)(void *))
 {
@@ -1237,7 +1224,7 @@ static inline int check_object(struct kmem_cache *s, struct page *page,
 static inline void add_full(struct kmem_cache *s, struct kmem_cache_node *n,
                                        struct page *page) {}
 static inline void remove_full(struct kmem_cache *s, struct page *page) {}
-static inline unsigned long kmem_cache_flags(unsigned long objsize,
+static inline unsigned long kmem_cache_flags(unsigned long object_size,
        unsigned long flags, const char *name,
        void (*ctor)(void *))
 {
@@ -1314,13 +1301,7 @@ static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
                        stat(s, ORDER_FALLBACK);
        }
 
-       if (flags & __GFP_WAIT)
-               local_irq_disable();
-
-       if (!page)
-               return NULL;
-
-       if (kmemcheck_enabled
+       if (kmemcheck_enabled && page
                && !(s->flags & (SLAB_NOTRACK | DEBUG_DEFAULT_FLAGS))) {
                int pages = 1 << oo_order(oo);
 
@@ -1336,6 +1317,11 @@ static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
                        kmemcheck_mark_unallocated_pages(page, pages);
        }
 
+       if (flags & __GFP_WAIT)
+               local_irq_disable();
+       if (!page)
+               return NULL;
+
        page->objects = oo_objects(oo);
        mod_zone_page_state(page_zone(page),
                (s->flags & SLAB_RECLAIM_ACCOUNT) ?
@@ -1370,6 +1356,8 @@ static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node)
        inc_slabs_node(s, page_to_nid(page), page->objects);
        page->slab = s;
        __SetPageSlab(page);
+       if (page->pfmemalloc)
+               SetPageSlabPfmemalloc(page);
 
        start = page_address(page);
 
@@ -1413,6 +1401,7 @@ static void __free_slab(struct kmem_cache *s, struct page *page)
                NR_SLAB_RECLAIMABLE : NR_SLAB_UNRECLAIMABLE,
                -pages);
 
+       __ClearPageSlabPfmemalloc(page);
        __ClearPageSlab(page);
        reset_page_mapcount(page);
        if (current->reclaim_state)
@@ -1490,12 +1479,12 @@ static inline void remove_partial(struct kmem_cache_node *n,
 }
 
 /*
- * Lock slab, remove from the partial list and put the object into the
- * per cpu freelist.
+ * Remove slab from the partial list, freeze it and
+ * return the pointer to the freelist.
  *
  * Returns a list of objects or NULL if it fails.
  *
- * Must hold list_lock.
+ * Must hold list_lock since we modify the partial list.
  */
 static inline void *acquire_slab(struct kmem_cache *s,
                struct kmem_cache_node *n, struct page *page,
@@ -1510,26 +1499,27 @@ static inline void *acquire_slab(struct kmem_cache *s,
         * The old freelist is the list of objects for the
         * per cpu allocation list.
         */
-       do {
-               freelist = page->freelist;
-               counters = page->counters;
-               new.counters = counters;
-               if (mode) {
-                       new.inuse = page->objects;
-                       new.freelist = NULL;
-               } else {
-                       new.freelist = freelist;
-               }
+       freelist = page->freelist;
+       counters = page->counters;
+       new.counters = counters;
+       if (mode) {
+               new.inuse = page->objects;
+               new.freelist = NULL;
+       } else {
+               new.freelist = freelist;
+       }
 
-               VM_BUG_ON(new.frozen);
-               new.frozen = 1;
+       VM_BUG_ON(new.frozen);
+       new.frozen = 1;
 
-       } while (!__cmpxchg_double_slab(s, page,
+       if (!__cmpxchg_double_slab(s, page,
                        freelist, counters,
                        new.freelist, new.counters,
-                       "lock and freeze"));
+                       "acquire_slab"))
+               return NULL;
 
        remove_partial(n, page);
+       WARN_ON(!freelist);
        return freelist;
 }
 
@@ -1563,7 +1553,6 @@ static void *get_partial_node(struct kmem_cache *s,
 
                if (!object) {
                        c->page = page;
-                       c->node = page_to_nid(page);
                        stat(s, ALLOC_FROM_PARTIAL);
                        object = t;
                        available =  page->objects - page->inuse;
@@ -1617,7 +1606,7 @@ static void *get_any_partial(struct kmem_cache *s, gfp_t flags,
 
        do {
                cpuset_mems_cookie = get_mems_allowed();
-               zonelist = node_zonelist(slab_node(current->mempolicy), flags);
+               zonelist = node_zonelist(slab_node(), flags);
                for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) {
                        struct kmem_cache_node *n;
 
@@ -1731,14 +1720,12 @@ void init_kmem_cache_cpus(struct kmem_cache *s)
 /*
  * Remove the cpu slab
  */
-static void deactivate_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)
+static void deactivate_slab(struct kmem_cache *s, struct page *page, void *freelist)
 {
        enum slab_modes { M_NONE, M_PARTIAL, M_FULL, M_FREE };
-       struct page *page = c->page;
        struct kmem_cache_node *n = get_node(s, page_to_nid(page));
        int lock = 0;
        enum slab_modes l = M_NONE, m = M_NONE;
-       void *freelist;
        void *nextfree;
        int tail = DEACTIVATE_TO_HEAD;
        struct page new;
@@ -1749,11 +1736,6 @@ static void deactivate_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)
                tail = DEACTIVATE_TO_TAIL;
        }
 
-       c->tid = next_tid(c->tid);
-       c->page = NULL;
-       freelist = c->freelist;
-       c->freelist = NULL;
-
        /*
         * Stage one: Free all available per cpu objects back
         * to the page freelist while it is still frozen. Leave the
@@ -1879,21 +1861,31 @@ redo:
        }
 }
 
-/* Unfreeze all the cpu partial slabs */
+/*
+ * Unfreeze all the cpu partial slabs.
+ *
+ * This function must be called with interrupt disabled.
+ */
 static void unfreeze_partials(struct kmem_cache *s)
 {
-       struct kmem_cache_node *n = NULL;
+       struct kmem_cache_node *n = NULL, *n2 = NULL;
        struct kmem_cache_cpu *c = this_cpu_ptr(s->cpu_slab);
        struct page *page, *discard_page = NULL;
 
        while ((page = c->partial)) {
-               enum slab_modes { M_PARTIAL, M_FREE };
-               enum slab_modes l, m;
                struct page new;
                struct page old;
 
                c->partial = page->next;
-               l = M_FREE;
+
+               n2 = get_node(s, page_to_nid(page));
+               if (n != n2) {
+                       if (n)
+                               spin_unlock(&n->list_lock);
+
+                       n = n2;
+                       spin_lock(&n->list_lock);
+               }
 
                do {
 
@@ -1906,43 +1898,17 @@ static void unfreeze_partials(struct kmem_cache *s)
 
                        new.frozen = 0;
 
-                       if (!new.inuse && (!n || n->nr_partial > s->min_partial))
-                               m = M_FREE;
-                       else {
-                               struct kmem_cache_node *n2 = get_node(s,
-                                                       page_to_nid(page));
-
-                               m = M_PARTIAL;
-                               if (n != n2) {
-                                       if (n)
-                                               spin_unlock(&n->list_lock);
-
-                                       n = n2;
-                                       spin_lock(&n->list_lock);
-                               }
-                       }
-
-                       if (l != m) {
-                               if (l == M_PARTIAL) {
-                                       remove_partial(n, page);
-                                       stat(s, FREE_REMOVE_PARTIAL);
-                               } else {
-                                       add_partial(n, page,
-                                               DEACTIVATE_TO_TAIL);
-                                       stat(s, FREE_ADD_PARTIAL);
-                               }
-
-                               l = m;
-                       }
-
-               } while (!cmpxchg_double_slab(s, page,
+               } while (!__cmpxchg_double_slab(s, page,
                                old.freelist, old.counters,
                                new.freelist, new.counters,
                                "unfreezing slab"));
 
-               if (m == M_FREE) {
+               if (unlikely(!new.inuse && n->nr_partial > s->min_partial)) {
                        page->next = discard_page;
                        discard_page = page;
+               } else {
+                       add_partial(n, page, DEACTIVATE_TO_TAIL);
+                       stat(s, FREE_ADD_PARTIAL);
                }
        }
 
@@ -2011,7 +1977,11 @@ int put_cpu_partial(struct kmem_cache *s, struct page *page, int drain)
 static inline void flush_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)
 {
        stat(s, CPUSLAB_FLUSH);
-       deactivate_slab(s, c);
+       deactivate_slab(s, c->page, c->freelist);
+
+       c->tid = next_tid(c->tid);
+       c->page = NULL;
+       c->freelist = NULL;
 }
 
 /*
@@ -2055,10 +2025,10 @@ static void flush_all(struct kmem_cache *s)
  * Check if the objects in a per cpu structure fit numa
  * locality expectations.
  */
-static inline int node_match(struct kmem_cache_cpu *c, int node)
+static inline int node_match(struct page *page, int node)
 {
 #ifdef CONFIG_NUMA
-       if (node != NUMA_NO_NODE && c->node != node)
+       if (node != NUMA_NO_NODE && page_to_nid(page) != node)
                return 0;
 #endif
        return 1;
@@ -2101,10 +2071,10 @@ slab_out_of_memory(struct kmem_cache *s, gfp_t gfpflags, int nid)
                "SLUB: Unable to allocate memory on node %d (gfp=0x%x)\n",
                nid, gfpflags);
        printk(KERN_WARNING "  cache: %s, object size: %d, buffer size: %d, "
-               "default order: %d, min order: %d\n", s->name, s->objsize,
+               "default order: %d, min order: %d\n", s->name, s->object_size,
                s->size, oo_order(s->oo), oo_order(s->min));
 
-       if (oo_order(s->min) > get_order(s->objsize))
+       if (oo_order(s->min) > get_order(s->object_size))
                printk(KERN_WARNING "  %s debugging increased min order, use "
                       "slub_debug=O to disable.\n", s->name);
 
@@ -2130,10 +2100,16 @@ slab_out_of_memory(struct kmem_cache *s, gfp_t gfpflags, int nid)
 static inline void *new_slab_objects(struct kmem_cache *s, gfp_t flags,
                        int node, struct kmem_cache_cpu **pc)
 {
-       void *object;
-       struct kmem_cache_cpu *c;
-       struct page *page = new_slab(s, flags, node);
+       void *freelist;
+       struct kmem_cache_cpu *c = *pc;
+       struct page *page;
 
+       freelist = get_partial(s, flags, node, c);
+
+       if (freelist)
+               return freelist;
+
+       page = new_slab(s, flags, node);
        if (page) {
                c = __this_cpu_ptr(s->cpu_slab);
                if (c->page)
@@ -2143,17 +2119,24 @@ static inline void *new_slab_objects(struct kmem_cache *s, gfp_t flags,
                 * No other reference to the page yet so we can
                 * muck around with it freely without cmpxchg
                 */
-               object = page->freelist;
+               freelist = page->freelist;
                page->freelist = NULL;
 
                stat(s, ALLOC_SLAB);
-               c->node = page_to_nid(page);
                c->page = page;
                *pc = c;
        } else
-               object = NULL;
+               freelist = NULL;
 
-       return object;
+       return freelist;
+}
+
+static inline bool pfmemalloc_match(struct page *page, gfp_t gfpflags)
+{
+       if (unlikely(PageSlabPfmemalloc(page)))
+               return gfp_pfmemalloc_allowed(gfpflags);
+
+       return true;
 }
 
 /*
@@ -2163,6 +2146,8 @@ static inline void *new_slab_objects(struct kmem_cache *s, gfp_t flags,
  * The page is still frozen if the return value is not NULL.
  *
  * If this function returns NULL then the page has been unfrozen.
+ *
+ * This function must be called with interrupt disabled.
  */
 static inline void *get_freelist(struct kmem_cache *s, struct page *page)
 {
@@ -2173,13 +2158,14 @@ static inline void *get_freelist(struct kmem_cache *s, struct page *page)
        do {
                freelist = page->freelist;
                counters = page->counters;
+
                new.counters = counters;
                VM_BUG_ON(!new.frozen);
 
                new.inuse = page->objects;
                new.frozen = freelist != NULL;
 
-       } while (!cmpxchg_double_slab(s, page,
+       } while (!__cmpxchg_double_slab(s, page,
                freelist, counters,
                NULL, new.counters,
                "get_freelist"));
@@ -2206,7 +2192,8 @@ static inline void *get_freelist(struct kmem_cache *s, struct page *page)
 static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
                          unsigned long addr, struct kmem_cache_cpu *c)
 {
-       void **object;
+       void *freelist;
+       struct page *page;
        unsigned long flags;
 
        local_irq_save(flags);
@@ -2219,25 +2206,41 @@ static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
        c = this_cpu_ptr(s->cpu_slab);
 #endif
 
-       if (!c->page)
+       page = c->page;
+       if (!page)
                goto new_slab;
 redo:
-       if (unlikely(!node_match(c, node))) {
+
+       if (unlikely(!node_match(page, node))) {
                stat(s, ALLOC_NODE_MISMATCH);
-               deactivate_slab(s, c);
+               deactivate_slab(s, page, c->freelist);
+               c->page = NULL;
+               c->freelist = NULL;
+               goto new_slab;
+       }
+
+       /*
+        * By rights, we should be searching for a slab page that was
+        * PFMEMALLOC but right now, we are losing the pfmemalloc
+        * information when the page leaves the per-cpu allocator
+        */
+       if (unlikely(!pfmemalloc_match(page, gfpflags))) {
+               deactivate_slab(s, page, c->freelist);
+               c->page = NULL;
+               c->freelist = NULL;
                goto new_slab;
        }
 
        /* must check again c->freelist in case of cpu migration or IRQ */
-       object = c->freelist;
-       if (object)
+       freelist = c->freelist;
+       if (freelist)
                goto load_freelist;
 
        stat(s, ALLOC_SLOWPATH);
 
-       object = get_freelist(s, c->page);
+       freelist = get_freelist(s, page);
 
-       if (!object) {
+       if (!freelist) {
                c->page = NULL;
                stat(s, DEACTIVATE_BYPASS);
                goto new_slab;
@@ -2246,50 +2249,50 @@ redo:
        stat(s, ALLOC_REFILL);
 
 load_freelist:
-       c->freelist = get_freepointer(s, object);
+       /*
+        * freelist is pointing to the list of objects to be used.
+        * page is pointing to the page from which the objects are obtained.
+        * That page must be frozen for per cpu allocations to work.
+        */
+       VM_BUG_ON(!c->page->frozen);
+       c->freelist = get_freepointer(s, freelist);
        c->tid = next_tid(c->tid);
        local_irq_restore(flags);
-       return object;
+       return freelist;
 
 new_slab:
 
        if (c->partial) {
-               c->page = c->partial;
-               c->partial = c->page->next;
-               c->node = page_to_nid(c->page);
+               page = c->page = c->partial;
+               c->partial = page->next;
                stat(s, CPU_PARTIAL_ALLOC);
                c->freelist = NULL;
                goto redo;
        }
 
-       /* Then do expensive stuff like retrieving pages from the partial lists */
-       object = get_partial(s, gfpflags, node, c);
-
-       if (unlikely(!object)) {
+       freelist = new_slab_objects(s, gfpflags, node, &c);
 
-               object = new_slab_objects(s, gfpflags, node, &c);
+       if (unlikely(!freelist)) {
+               if (!(gfpflags & __GFP_NOWARN) && printk_ratelimit())
+                       slab_out_of_memory(s, gfpflags, node);
 
-               if (unlikely(!object)) {
-                       if (!(gfpflags & __GFP_NOWARN) && printk_ratelimit())
-                               slab_out_of_memory(s, gfpflags, node);
-
-                       local_irq_restore(flags);
-                       return NULL;
-               }
+               local_irq_restore(flags);
+               return NULL;
        }
 
-       if (likely(!kmem_cache_debug(s)))
+       page = c->page;
+       if (likely(!kmem_cache_debug(s) && pfmemalloc_match(page, gfpflags)))
                goto load_freelist;
 
        /* Only entered in the debug case */
-       if (!alloc_debug_processing(s, c->page, object, addr))
+       if (kmem_cache_debug(s) && !alloc_debug_processing(s, page, freelist, addr))
                goto new_slab;  /* Slab failed checks. Next slab needed */
 
-       c->freelist = get_freepointer(s, object);
-       deactivate_slab(s, c);
-       c->node = NUMA_NO_NODE;
+       deactivate_slab(s, page, get_freepointer(s, freelist));
+       c->page = NULL;
+       c->freelist = NULL;
        local_irq_restore(flags);
-       return object;
+       return freelist;
 }
 
 /*
@@ -2307,6 +2310,7 @@ static __always_inline void *slab_alloc(struct kmem_cache *s,
 {
        void **object;
        struct kmem_cache_cpu *c;
+       struct page *page;
        unsigned long tid;
 
        if (slab_pre_alloc_hook(s, gfpflags))
@@ -2332,8 +2336,8 @@ redo:
        barrier();
 
        object = c->freelist;
-       if (unlikely(!object || !node_match(c, node)))
-
+       page = c->page;
+       if (unlikely(!object || !node_match(page, node)))
                object = __slab_alloc(s, gfpflags, node, addr, c);
 
        else {
@@ -2364,7 +2368,7 @@ redo:
        }
 
        if (unlikely(gfpflags & __GFP_ZERO) && object)
-               memset(object, 0, s->objsize);
+               memset(object, 0, s->object_size);
 
        slab_post_alloc_hook(s, gfpflags, object);
 
@@ -2375,7 +2379,7 @@ void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags)
 {
        void *ret = slab_alloc(s, gfpflags, NUMA_NO_NODE, _RET_IP_);
 
-       trace_kmem_cache_alloc(_RET_IP_, ret, s->objsize, s->size, gfpflags);
+       trace_kmem_cache_alloc(_RET_IP_, ret, s->object_size, s->size, gfpflags);
 
        return ret;
 }
@@ -2405,7 +2409,7 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
        void *ret = slab_alloc(s, gfpflags, node, _RET_IP_);
 
        trace_kmem_cache_alloc_node(_RET_IP_, ret,
-                                   s->objsize, s->size, gfpflags, node);
+                                   s->object_size, s->size, gfpflags, node);
 
        return ret;
 }
@@ -2900,7 +2904,7 @@ static void set_min_partial(struct kmem_cache *s, unsigned long min)
 static int calculate_sizes(struct kmem_cache *s, int forced_order)
 {
        unsigned long flags = s->flags;
-       unsigned long size = s->objsize;
+       unsigned long size = s->object_size;
        unsigned long align = s->align;
        int order;
 
@@ -2929,7 +2933,7 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order)
         * end of the object and the free pointer. If not then add an
         * additional word to have some bytes to store Redzone information.
         */
-       if ((flags & SLAB_RED_ZONE) && size == s->objsize)
+       if ((flags & SLAB_RED_ZONE) && size == s->object_size)
                size += sizeof(void *);
 #endif
 
@@ -2977,7 +2981,7 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order)
         * user specified and the dynamic determination of cache line size
         * on bootup.
         */
-       align = calculate_alignment(flags, align, s->objsize);
+       align = calculate_alignment(flags, align, s->object_size);
        s->align = align;
 
        /*
@@ -3025,7 +3029,7 @@ static int kmem_cache_open(struct kmem_cache *s,
        memset(s, 0, kmem_size);
        s->name = name;
        s->ctor = ctor;
-       s->objsize = size;
+       s->object_size = size;
        s->align = align;
        s->flags = kmem_cache_flags(size, flags, name, ctor);
        s->reserved = 0;
@@ -3040,7 +3044,7 @@ static int kmem_cache_open(struct kmem_cache *s,
                 * Disable debugging flags that store metadata if the min slab
                 * order increased.
                 */
-               if (get_order(s->size) > get_order(s->objsize)) {
+               if (get_order(s->size) > get_order(s->object_size)) {
                        s->flags &= ~DEBUG_METADATA_FLAGS;
                        s->offset = 0;
                        if (!calculate_sizes(s, -1))
@@ -3114,7 +3118,7 @@ error:
  */
 unsigned int kmem_cache_size(struct kmem_cache *s)
 {
-       return s->objsize;
+       return s->object_size;
 }
 EXPORT_SYMBOL(kmem_cache_size);
 
@@ -3192,11 +3196,11 @@ static inline int kmem_cache_close(struct kmem_cache *s)
  */
 void kmem_cache_destroy(struct kmem_cache *s)
 {
-       down_write(&slub_lock);
+       mutex_lock(&slab_mutex);
        s->refcount--;
        if (!s->refcount) {
                list_del(&s->list);
-               up_write(&slub_lock);
+               mutex_unlock(&slab_mutex);
                if (kmem_cache_close(s)) {
                        printk(KERN_ERR "SLUB %s: %s called for cache that "
                                "still has objects.\n", s->name, __func__);
@@ -3206,7 +3210,7 @@ void kmem_cache_destroy(struct kmem_cache *s)
                        rcu_barrier();
                sysfs_slab_remove(s);
        } else
-               up_write(&slub_lock);
+               mutex_unlock(&slab_mutex);
 }
 EXPORT_SYMBOL(kmem_cache_destroy);
 
@@ -3268,7 +3272,7 @@ static struct kmem_cache *__init create_kmalloc_cache(const char *name,
 
        /*
         * This function is called with IRQs disabled during early-boot on
-        * single CPU so there's no need to take slub_lock here.
+        * single CPU so there's no need to take slab_mutex here.
         */
        if (!kmem_cache_open(s, name, size, ARCH_KMALLOC_MINALIGN,
                                                                flags, NULL))
@@ -3553,10 +3557,10 @@ static int slab_mem_going_offline_callback(void *arg)
 {
        struct kmem_cache *s;
 
-       down_read(&slub_lock);
+       mutex_lock(&slab_mutex);
        list_for_each_entry(s, &slab_caches, list)
                kmem_cache_shrink(s);
-       up_read(&slub_lock);
+       mutex_unlock(&slab_mutex);
 
        return 0;
 }
@@ -3577,7 +3581,7 @@ static void slab_mem_offline_callback(void *arg)
        if (offline_node < 0)
                return;
 
-       down_read(&slub_lock);
+       mutex_lock(&slab_mutex);
        list_for_each_entry(s, &slab_caches, list) {
                n = get_node(s, offline_node);
                if (n) {
@@ -3593,7 +3597,7 @@ static void slab_mem_offline_callback(void *arg)
                        kmem_cache_free(kmem_cache_node, n);
                }
        }
-       up_read(&slub_lock);
+       mutex_unlock(&slab_mutex);
 }
 
 static int slab_mem_going_online_callback(void *arg)
@@ -3616,7 +3620,7 @@ static int slab_mem_going_online_callback(void *arg)
         * allocate a kmem_cache_node structure in order to bring the node
         * online.
         */
-       down_read(&slub_lock);
+       mutex_lock(&slab_mutex);
        list_for_each_entry(s, &slab_caches, list) {
                /*
                 * XXX: kmem_cache_alloc_node will fallback to other nodes
@@ -3632,7 +3636,7 @@ static int slab_mem_going_online_callback(void *arg)
                s->node[nid] = n;
        }
 out:
-       up_read(&slub_lock);
+       mutex_unlock(&slab_mutex);
        return ret;
 }
 
@@ -3843,11 +3847,11 @@ void __init kmem_cache_init(void)
 
                if (s && s->size) {
                        char *name = kasprintf(GFP_NOWAIT,
-                                "dma-kmalloc-%d", s->objsize);
+                                "dma-kmalloc-%d", s->object_size);
 
                        BUG_ON(!name);
                        kmalloc_dma_caches[i] = create_kmalloc_cache(name,
-                               s->objsize, SLAB_CACHE_DMA);
+                               s->object_size, SLAB_CACHE_DMA);
                }
        }
 #endif
@@ -3924,16 +3928,12 @@ static struct kmem_cache *find_mergeable(size_t size,
        return NULL;
 }
 
-struct kmem_cache *kmem_cache_create(const char *name, size_t size,
+struct kmem_cache *__kmem_cache_create(const char *name, size_t size,
                size_t align, unsigned long flags, void (*ctor)(void *))
 {
        struct kmem_cache *s;
        char *n;
 
-       if (WARN_ON(!name))
-               return NULL;
-
-       down_write(&slub_lock);
        s = find_mergeable(size, align, flags, name, ctor);
        if (s) {
                s->refcount++;
@@ -3941,49 +3941,42 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size,
                 * Adjust the object sizes so that we clear
                 * the complete object on kzalloc.
                 */
-               s->objsize = max(s->objsize, (int)size);
+               s->object_size = max(s->object_size, (int)size);
                s->inuse = max_t(int, s->inuse, ALIGN(size, sizeof(void *)));
 
                if (sysfs_slab_alias(s, name)) {
                        s->refcount--;
-                       goto err;
+                       return NULL;
                }
-               up_write(&slub_lock);
                return s;
        }
 
        n = kstrdup(name, GFP_KERNEL);
        if (!n)
-               goto err;
+               return NULL;
 
        s = kmalloc(kmem_size, GFP_KERNEL);
        if (s) {
                if (kmem_cache_open(s, n,
                                size, align, flags, ctor)) {
+                       int r;
+
                        list_add(&s->list, &slab_caches);
-                       up_write(&slub_lock);
-                       if (sysfs_slab_add(s)) {
-                               down_write(&slub_lock);
-                               list_del(&s->list);
-                               kfree(n);
-                               kfree(s);
-                               goto err;
-                       }
-                       return s;
+                       mutex_unlock(&slab_mutex);
+                       r = sysfs_slab_add(s);
+                       mutex_lock(&slab_mutex);
+
+                       if (!r)
+                               return s;
+
+                       list_del(&s->list);
+                       kmem_cache_close(s);
                }
                kfree(s);
        }
        kfree(n);
-err:
-       up_write(&slub_lock);
-
-       if (flags & SLAB_PANIC)
-               panic("Cannot create slabcache %s\n", name);
-       else
-               s = NULL;
-       return s;
+       return NULL;
 }
-EXPORT_SYMBOL(kmem_cache_create);
 
 #ifdef CONFIG_SMP
 /*
@@ -4002,13 +3995,13 @@ static int __cpuinit slab_cpuup_callback(struct notifier_block *nfb,
        case CPU_UP_CANCELED_FROZEN:
        case CPU_DEAD:
        case CPU_DEAD_FROZEN:
-               down_read(&slub_lock);
+               mutex_lock(&slab_mutex);
                list_for_each_entry(s, &slab_caches, list) {
                        local_irq_save(flags);
                        __flush_cpu_slab(s, cpu);
                        local_irq_restore(flags);
                }
-               up_read(&slub_lock);
+               mutex_unlock(&slab_mutex);
                break;
        default:
                break;
@@ -4500,30 +4493,31 @@ static ssize_t show_slab_objects(struct kmem_cache *s,
 
                for_each_possible_cpu(cpu) {
                        struct kmem_cache_cpu *c = per_cpu_ptr(s->cpu_slab, cpu);
-                       int node = ACCESS_ONCE(c->node);
+                       int node;
                        struct page *page;
 
-                       if (node < 0)
-                               continue;
                        page = ACCESS_ONCE(c->page);
-                       if (page) {
-                               if (flags & SO_TOTAL)
-                                       x = page->objects;
-                               else if (flags & SO_OBJECTS)
-                                       x = page->inuse;
-                               else
-                                       x = 1;
+                       if (!page)
+                               continue;
 
-                               total += x;
-                               nodes[node] += x;
-                       }
-                       page = c->partial;
+                       node = page_to_nid(page);
+                       if (flags & SO_TOTAL)
+                               x = page->objects;
+                       else if (flags & SO_OBJECTS)
+                               x = page->inuse;
+                       else
+                               x = 1;
 
+                       total += x;
+                       nodes[node] += x;
+
+                       page = ACCESS_ONCE(c->partial);
                        if (page) {
                                x = page->pobjects;
                                total += x;
                                nodes[node] += x;
                        }
+
                        per_cpu[node]++;
                }
        }
@@ -4623,7 +4617,7 @@ SLAB_ATTR_RO(align);
 
 static ssize_t object_size_show(struct kmem_cache *s, char *buf)
 {
-       return sprintf(buf, "%d\n", s->objsize);
+       return sprintf(buf, "%d\n", s->object_size);
 }
 SLAB_ATTR_RO(object_size);
 
@@ -5286,7 +5280,7 @@ static int sysfs_slab_add(struct kmem_cache *s)
        const char *name;
        int unmergeable;
 
-       if (slab_state < SYSFS)
+       if (slab_state < FULL)
                /* Defer until later */
                return 0;
 
@@ -5331,7 +5325,7 @@ static int sysfs_slab_add(struct kmem_cache *s)
 
 static void sysfs_slab_remove(struct kmem_cache *s)
 {
-       if (slab_state < SYSFS)
+       if (slab_state < FULL)
                /*
                 * Sysfs has not been setup yet so no need to remove the
                 * cache from sysfs.
@@ -5359,7 +5353,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name)
 {
        struct saved_alias *al;
 
-       if (slab_state == SYSFS) {
+       if (slab_state == FULL) {
                /*
                 * If we have a leftover link then remove it.
                 */
@@ -5383,16 +5377,16 @@ static int __init slab_sysfs_init(void)
        struct kmem_cache *s;
        int err;
 
-       down_write(&slub_lock);
+       mutex_lock(&slab_mutex);
 
        slab_kset = kset_create_and_add("slab", &slab_uevent_ops, kernel_kobj);
        if (!slab_kset) {
-               up_write(&slub_lock);
+               mutex_unlock(&slab_mutex);
                printk(KERN_ERR "Cannot register slab subsystem.\n");
                return -ENOSYS;
        }
 
-       slab_state = SYSFS;
+       slab_state = FULL;
 
        list_for_each_entry(s, &slab_caches, list) {
                err = sysfs_slab_add(s);
@@ -5408,11 +5402,11 @@ static int __init slab_sysfs_init(void)
                err = sysfs_slab_alias(al->s, al->name);
                if (err)
                        printk(KERN_ERR "SLUB: Unable to add boot slab alias"
-                                       " %s to sysfs\n", s->name);
+                                       " %s to sysfs\n", al->name);
                kfree(al);
        }
 
-       up_write(&slub_lock);
+       mutex_unlock(&slab_mutex);
        resiliency_test();
        return 0;
 }
@@ -5427,7 +5421,7 @@ __initcall(slab_sysfs_init);
 static void print_slabinfo_header(struct seq_file *m)
 {
        seq_puts(m, "slabinfo - version: 2.1\n");
-       seq_puts(m, "# name            <active_objs> <num_objs> <objsize> "
+       seq_puts(m, "# name            <active_objs> <num_objs> <object_size> "
                 "<objperslab> <pagesperslab>");
        seq_puts(m, " : tunables <limit> <batchcount> <sharedfactor>");
        seq_puts(m, " : slabdata <active_slabs> <num_slabs> <sharedavail>");
@@ -5438,7 +5432,7 @@ static void *s_start(struct seq_file *m, loff_t *pos)
 {
        loff_t n = *pos;
 
-       down_read(&slub_lock);
+       mutex_lock(&slab_mutex);
        if (!n)
                print_slabinfo_header(m);
 
@@ -5452,7 +5446,7 @@ static void *s_next(struct seq_file *m, void *p, loff_t *pos)
 
 static void s_stop(struct seq_file *m, void *p)
 {
-       up_read(&slub_lock);
+       mutex_unlock(&slab_mutex);
 }
 
 static int s_show(struct seq_file *m, void *p)
index c7bb952400c83c9114a401f647955cb18dd2ea44..fac95f2888f2d1fbe923fd83ff7d43786ac89c58 100644 (file)
@@ -65,21 +65,18 @@ static struct mem_section noinline __init_refok *sparse_index_alloc(int nid)
 
        if (slab_is_available()) {
                if (node_state(nid, N_HIGH_MEMORY))
-                       section = kmalloc_node(array_size, GFP_KERNEL, nid);
+                       section = kzalloc_node(array_size, GFP_KERNEL, nid);
                else
-                       section = kmalloc(array_size, GFP_KERNEL);
-       } else
+                       section = kzalloc(array_size, GFP_KERNEL);
+       } else {
                section = alloc_bootmem_node(NODE_DATA(nid), array_size);
-
-       if (section)
-               memset(section, 0, array_size);
+       }
 
        return section;
 }
 
 static int __meminit sparse_index_init(unsigned long section_nr, int nid)
 {
-       static DEFINE_SPINLOCK(index_init_lock);
        unsigned long root = SECTION_NR_TO_ROOT(section_nr);
        struct mem_section *section;
        int ret = 0;
@@ -90,20 +87,9 @@ static int __meminit sparse_index_init(unsigned long section_nr, int nid)
        section = sparse_index_alloc(nid);
        if (!section)
                return -ENOMEM;
-       /*
-        * This lock keeps two different sections from
-        * reallocating for the same index
-        */
-       spin_lock(&index_init_lock);
-
-       if (mem_section[root]) {
-               ret = -EEXIST;
-               goto out;
-       }
 
        mem_section[root] = section;
-out:
-       spin_unlock(&index_init_lock);
+
        return ret;
 }
 #else /* !SPARSEMEM_EXTREME */
@@ -132,6 +118,8 @@ int __section_nr(struct mem_section* ms)
                     break;
        }
 
+       VM_BUG_ON(root_nr == NR_SECTION_ROOTS);
+
        return (root_nr * SECTIONS_PER_ROOT) + (ms - root);
 }
 
@@ -493,6 +481,9 @@ void __init sparse_init(void)
        struct page **map_map;
 #endif
 
+       /* Setup pageblock_order for HUGETLB_PAGE_SIZE_VARIABLE */
+       set_pageblock_order();
+
        /*
         * map is using big page (aka 2M in x86 64 bit)
         * usemap is less one page (aka 24 bytes)
index 4e7e2ec67078f783750eb43e8dc45db5600b1b94..77825883298f1f1843e068c2e5ad0d55706cf873 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -236,6 +236,58 @@ void put_pages_list(struct list_head *pages)
 }
 EXPORT_SYMBOL(put_pages_list);
 
+/*
+ * get_kernel_pages() - pin kernel pages in memory
+ * @kiov:      An array of struct kvec structures
+ * @nr_segs:   number of segments to pin
+ * @write:     pinning for read/write, currently ignored
+ * @pages:     array that receives pointers to the pages pinned.
+ *             Should be at least nr_segs long.
+ *
+ * Returns number of pages pinned. This may be fewer than the number
+ * requested. If nr_pages is 0 or negative, returns 0. If no pages
+ * were pinned, returns -errno. Each page returned must be released
+ * with a put_page() call when it is finished with.
+ */
+int get_kernel_pages(const struct kvec *kiov, int nr_segs, int write,
+               struct page **pages)
+{
+       int seg;
+
+       for (seg = 0; seg < nr_segs; seg++) {
+               if (WARN_ON(kiov[seg].iov_len != PAGE_SIZE))
+                       return seg;
+
+               pages[seg] = kmap_to_page(kiov[seg].iov_base);
+               page_cache_get(pages[seg]);
+       }
+
+       return seg;
+}
+EXPORT_SYMBOL_GPL(get_kernel_pages);
+
+/*
+ * get_kernel_page() - pin a kernel page in memory
+ * @start:     starting kernel address
+ * @write:     pinning for read/write, currently ignored
+ * @pages:     array that receives pointer to the page pinned.
+ *             Must be at least nr_segs long.
+ *
+ * Returns 1 if page is pinned. If the page was not pinned, returns
+ * -errno. The page returned must be released with a put_page() call
+ * when it is finished with.
+ */
+int get_kernel_page(unsigned long start, int write, struct page **pages)
+{
+       const struct kvec kiov = {
+               .iov_base = (void *)start,
+               .iov_len = PAGE_SIZE
+       };
+
+       return get_kernel_pages(&kiov, 1, write, pages);
+}
+EXPORT_SYMBOL_GPL(get_kernel_page);
+
 static void pagevec_lru_move_fn(struct pagevec *pvec,
        void (*move_fn)(struct page *page, struct lruvec *lruvec, void *arg),
        void *arg)
index 4c5ff7f284d9b299d7ea9cf3b7bfe374550ce5ca..0cb36fb1f61cc539baa143319c40da30ada3d04e 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/init.h>
 #include <linux/pagemap.h>
 #include <linux/backing-dev.h>
+#include <linux/blkdev.h>
 #include <linux/pagevec.h>
 #include <linux/migrate.h>
 #include <linux/page_cgroup.h>
@@ -26,7 +27,7 @@
  */
 static const struct address_space_operations swap_aops = {
        .writepage      = swap_writepage,
-       .set_page_dirty = __set_page_dirty_no_writeback,
+       .set_page_dirty = swap_set_page_dirty,
        .migratepage    = migrate_page,
 };
 
@@ -376,6 +377,7 @@ struct page *swapin_readahead(swp_entry_t entry, gfp_t gfp_mask,
        unsigned long offset = swp_offset(entry);
        unsigned long start_offset, end_offset;
        unsigned long mask = (1UL << page_cluster) - 1;
+       struct blk_plug plug;
 
        /* Read a page_cluster sized and aligned cluster around offset. */
        start_offset = offset & ~mask;
@@ -383,6 +385,7 @@ struct page *swapin_readahead(swp_entry_t entry, gfp_t gfp_mask,
        if (!start_offset)      /* First page is swap header. */
                start_offset++;
 
+       blk_start_plug(&plug);
        for (offset = start_offset; offset <= end_offset ; offset++) {
                /* Ok, do the async read-ahead now */
                page = read_swap_cache_async(swp_entry(swp_type(entry), offset),
@@ -391,6 +394,8 @@ struct page *swapin_readahead(swp_entry_t entry, gfp_t gfp_mask,
                        continue;
                page_cache_release(page);
        }
+       blk_finish_plug(&plug);
+
        lru_add_drain();        /* Push any new pages onto the LRU now */
        return read_swap_cache_async(entry, gfp_mask, vma, addr);
 }
index 71373d03fcee99d57c29d24a25db888228bf3417..14e254c768fc5090e9c2276085b3c6f96c6255f6 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/oom.h>
 #include <linux/frontswap.h>
 #include <linux/swapfile.h>
+#include <linux/export.h>
 
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
@@ -548,7 +549,6 @@ static unsigned char swap_entry_free(struct swap_info_struct *p,
 
        /* free if no reference */
        if (!usage) {
-               struct gendisk *disk = p->bdev->bd_disk;
                if (offset < p->lowest_bit)
                        p->lowest_bit = offset;
                if (offset > p->highest_bit)
@@ -559,9 +559,12 @@ static unsigned char swap_entry_free(struct swap_info_struct *p,
                nr_swap_pages++;
                p->inuse_pages--;
                frontswap_invalidate_page(p->type, offset);
-               if ((p->flags & SWP_BLKDEV) &&
-                               disk->fops->swap_slot_free_notify)
-                       disk->fops->swap_slot_free_notify(p->bdev, offset);
+               if (p->flags & SWP_BLKDEV) {
+                       struct gendisk *disk = p->bdev->bd_disk;
+                       if (disk->fops->swap_slot_free_notify)
+                               disk->fops->swap_slot_free_notify(p->bdev,
+                                                                 offset);
+               }
        }
 
        return usage;
@@ -832,8 +835,7 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
 
        pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
        if (unlikely(!pte_same(*pte, swp_entry_to_pte(entry)))) {
-               if (ret > 0)
-                       mem_cgroup_cancel_charge_swapin(memcg);
+               mem_cgroup_cancel_charge_swapin(memcg);
                ret = 0;
                goto out;
        }
@@ -1328,6 +1330,14 @@ static void destroy_swap_extents(struct swap_info_struct *sis)
                list_del(&se->list);
                kfree(se);
        }
+
+       if (sis->flags & SWP_FILE) {
+               struct file *swap_file = sis->swap_file;
+               struct address_space *mapping = swap_file->f_mapping;
+
+               sis->flags &= ~SWP_FILE;
+               mapping->a_ops->swap_deactivate(swap_file);
+       }
 }
 
 /*
@@ -1336,7 +1346,7 @@ static void destroy_swap_extents(struct swap_info_struct *sis)
  *
  * This function rather assumes that it is called in ascending page order.
  */
-static int
+int
 add_swap_extent(struct swap_info_struct *sis, unsigned long start_page,
                unsigned long nr_pages, sector_t start_block)
 {
@@ -1409,98 +1419,28 @@ add_swap_extent(struct swap_info_struct *sis, unsigned long start_page,
  */
 static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span)
 {
-       struct inode *inode;
-       unsigned blocks_per_page;
-       unsigned long page_no;
-       unsigned blkbits;
-       sector_t probe_block;
-       sector_t last_block;
-       sector_t lowest_block = -1;
-       sector_t highest_block = 0;
-       int nr_extents = 0;
+       struct file *swap_file = sis->swap_file;
+       struct address_space *mapping = swap_file->f_mapping;
+       struct inode *inode = mapping->host;
        int ret;
 
-       inode = sis->swap_file->f_mapping->host;
        if (S_ISBLK(inode->i_mode)) {
                ret = add_swap_extent(sis, 0, sis->max, 0);
                *span = sis->pages;
-               goto out;
+               return ret;
        }
 
-       blkbits = inode->i_blkbits;
-       blocks_per_page = PAGE_SIZE >> blkbits;
-
-       /*
-        * Map all the blocks into the extent list.  This code doesn't try
-        * to be very smart.
-        */
-       probe_block = 0;
-       page_no = 0;
-       last_block = i_size_read(inode) >> blkbits;
-       while ((probe_block + blocks_per_page) <= last_block &&
-                       page_no < sis->max) {
-               unsigned block_in_page;
-               sector_t first_block;
-
-               first_block = bmap(inode, probe_block);
-               if (first_block == 0)
-                       goto bad_bmap;
-
-               /*
-                * It must be PAGE_SIZE aligned on-disk
-                */
-               if (first_block & (blocks_per_page - 1)) {
-                       probe_block++;
-                       goto reprobe;
-               }
-
-               for (block_in_page = 1; block_in_page < blocks_per_page;
-                                       block_in_page++) {
-                       sector_t block;
-
-                       block = bmap(inode, probe_block + block_in_page);
-                       if (block == 0)
-                               goto bad_bmap;
-                       if (block != first_block + block_in_page) {
-                               /* Discontiguity */
-                               probe_block++;
-                               goto reprobe;
-                       }
-               }
-
-               first_block >>= (PAGE_SHIFT - blkbits);
-               if (page_no) {  /* exclude the header page */
-                       if (first_block < lowest_block)
-                               lowest_block = first_block;
-                       if (first_block > highest_block)
-                               highest_block = first_block;
+       if (mapping->a_ops->swap_activate) {
+               ret = mapping->a_ops->swap_activate(sis, swap_file, span);
+               if (!ret) {
+                       sis->flags |= SWP_FILE;
+                       ret = add_swap_extent(sis, 0, sis->max, 0);
+                       *span = sis->pages;
                }
+               return ret;
+       }
 
-               /*
-                * We found a PAGE_SIZE-length, PAGE_SIZE-aligned run of blocks
-                */
-               ret = add_swap_extent(sis, page_no, 1, first_block);
-               if (ret < 0)
-                       goto out;
-               nr_extents += ret;
-               page_no++;
-               probe_block += blocks_per_page;
-reprobe:
-               continue;
-       }
-       ret = nr_extents;
-       *span = 1 + highest_block - lowest_block;
-       if (page_no == 0)
-               page_no = 1;    /* force Empty message */
-       sis->max = page_no;
-       sis->pages = page_no - 1;
-       sis->highest_bit = page_no - 1;
-out:
-       return ret;
-bad_bmap:
-       printk(KERN_ERR "swapon: swapfile has holes\n");
-       ret = -EINVAL;
-       goto out;
+       return generic_swapfile_activate(sis, swap_file, span);
 }
 
 static void enable_swap_info(struct swap_info_struct *p, int prio,
@@ -2285,6 +2225,31 @@ int swapcache_prepare(swp_entry_t entry)
        return __swap_duplicate(entry, SWAP_HAS_CACHE);
 }
 
+struct swap_info_struct *page_swap_info(struct page *page)
+{
+       swp_entry_t swap = { .val = page_private(page) };
+       BUG_ON(!PageSwapCache(page));
+       return swap_info[swp_type(swap)];
+}
+
+/*
+ * out-of-line __page_file_ methods to avoid include hell.
+ */
+struct address_space *__page_file_mapping(struct page *page)
+{
+       VM_BUG_ON(!PageSwapCache(page));
+       return page_swap_info(page)->swap_file->f_mapping;
+}
+EXPORT_SYMBOL_GPL(__page_file_mapping);
+
+pgoff_t __page_file_index(struct page *page)
+{
+       swp_entry_t swap = { .val = page_private(page) };
+       VM_BUG_ON(!PageSwapCache(page));
+       return swp_offset(swap);
+}
+EXPORT_SYMBOL_GPL(__page_file_index);
+
 /*
  * add_swap_count_continuation - called when a swap count is duplicated
  * beyond SWAP_MAP_MAX, it allocates a new page and links that to the entry's
index c7ac8e1b3ac76480cd05fcfaeab6703e9a2610dd..2bb90b1d241cc872da1e13dd80b449b2d323e812 100644 (file)
@@ -413,11 +413,11 @@ nocache:
                if (addr + size - 1 < addr)
                        goto overflow;
 
-               n = rb_next(&first->rb_node);
-               if (n)
-                       first = rb_entry(n, struct vmap_area, rb_node);
-               else
+               if (list_is_last(&first->list, &vmap_area_list))
                        goto found;
+
+               first = list_entry(first->list.next,
+                               struct vmap_area, list);
        }
 
 found:
@@ -904,6 +904,14 @@ static void *vb_alloc(unsigned long size, gfp_t gfp_mask)
 
        BUG_ON(size & ~PAGE_MASK);
        BUG_ON(size > PAGE_SIZE*VMAP_MAX_ALLOC);
+       if (WARN_ON(size == 0)) {
+               /*
+                * Allocating 0 bytes isn't what caller wants since
+                * get_order(0) returns funny result. Just warn and terminate
+                * early.
+                */
+               return NULL;
+       }
        order = get_order(size);
 
 again:
@@ -1280,7 +1288,7 @@ DEFINE_RWLOCK(vmlist_lock);
 struct vm_struct *vmlist;
 
 static void setup_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va,
-                             unsigned long flags, void *caller)
+                             unsigned long flags, const void *caller)
 {
        vm->flags = flags;
        vm->addr = (void *)va->va_start;
@@ -1306,7 +1314,7 @@ static void insert_vmalloc_vmlist(struct vm_struct *vm)
 }
 
 static void insert_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va,
-                             unsigned long flags, void *caller)
+                             unsigned long flags, const void *caller)
 {
        setup_vmalloc_vm(vm, va, flags, caller);
        insert_vmalloc_vmlist(vm);
@@ -1314,7 +1322,7 @@ static void insert_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va,
 
 static struct vm_struct *__get_vm_area_node(unsigned long size,
                unsigned long align, unsigned long flags, unsigned long start,
-               unsigned long end, int node, gfp_t gfp_mask, void *caller)
+               unsigned long end, int node, gfp_t gfp_mask, const void *caller)
 {
        struct vmap_area *va;
        struct vm_struct *area;
@@ -1375,7 +1383,7 @@ EXPORT_SYMBOL_GPL(__get_vm_area);
 
 struct vm_struct *__get_vm_area_caller(unsigned long size, unsigned long flags,
                                       unsigned long start, unsigned long end,
-                                      void *caller)
+                                      const void *caller)
 {
        return __get_vm_area_node(size, 1, flags, start, end, -1, GFP_KERNEL,
                                  caller);
@@ -1397,13 +1405,21 @@ struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)
 }
 
 struct vm_struct *get_vm_area_caller(unsigned long size, unsigned long flags,
-                               void *caller)
+                               const void *caller)
 {
        return __get_vm_area_node(size, 1, flags, VMALLOC_START, VMALLOC_END,
                                                -1, GFP_KERNEL, caller);
 }
 
-static struct vm_struct *find_vm_area(const void *addr)
+/**
+ *     find_vm_area  -  find a continuous kernel virtual area
+ *     @addr:          base address
+ *
+ *     Search for the kernel VM area starting at @addr, and return it.
+ *     It is up to the caller to do all required locking to keep the returned
+ *     pointer valid.
+ */
+struct vm_struct *find_vm_area(const void *addr)
 {
        struct vmap_area *va;
 
@@ -1568,9 +1584,9 @@ EXPORT_SYMBOL(vmap);
 
 static void *__vmalloc_node(unsigned long size, unsigned long align,
                            gfp_t gfp_mask, pgprot_t prot,
-                           int node, void *caller);
+                           int node, const void *caller);
 static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
-                                pgprot_t prot, int node, void *caller)
+                                pgprot_t prot, int node, const void *caller)
 {
        const int order = 0;
        struct page **pages;
@@ -1643,7 +1659,7 @@ fail:
  */
 void *__vmalloc_node_range(unsigned long size, unsigned long align,
                        unsigned long start, unsigned long end, gfp_t gfp_mask,
-                       pgprot_t prot, int node, void *caller)
+                       pgprot_t prot, int node, const void *caller)
 {
        struct vm_struct *area;
        void *addr;
@@ -1699,7 +1715,7 @@ fail:
  */
 static void *__vmalloc_node(unsigned long size, unsigned long align,
                            gfp_t gfp_mask, pgprot_t prot,
-                           int node, void *caller)
+                           int node, const void *caller)
 {
        return __vmalloc_node_range(size, align, VMALLOC_START, VMALLOC_END,
                                gfp_mask, prot, node, caller);
index 347b3ff2a478c91e6ffd98866eeb107aac0f2ddf..8d01243d9560e0ea8d8a04cf51cd4087ed8b280d 100644 (file)
@@ -133,7 +133,7 @@ long vm_total_pages;        /* The total number of pages which the VM controls */
 static LIST_HEAD(shrinker_list);
 static DECLARE_RWSEM(shrinker_rwsem);
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
 static bool global_reclaim(struct scan_control *sc)
 {
        return !sc->target_mem_cgroup;
@@ -687,6 +687,7 @@ static unsigned long shrink_page_list(struct list_head *page_list,
 
        cond_resched();
 
+       mem_cgroup_uncharge_start();
        while (!list_empty(page_list)) {
                enum page_references references;
                struct address_space *mapping;
@@ -720,9 +721,41 @@ static unsigned long shrink_page_list(struct list_head *page_list,
                        (PageSwapCache(page) && (sc->gfp_mask & __GFP_IO));
 
                if (PageWriteback(page)) {
-                       nr_writeback++;
-                       unlock_page(page);
-                       goto keep;
+                       /*
+                        * memcg doesn't have any dirty pages throttling so we
+                        * could easily OOM just because too many pages are in
+                        * writeback and there is nothing else to reclaim.
+                        *
+                        * Check __GFP_IO, certainly because a loop driver
+                        * thread might enter reclaim, and deadlock if it waits
+                        * on a page for which it is needed to do the write
+                        * (loop masks off __GFP_IO|__GFP_FS for this reason);
+                        * but more thought would probably show more reasons.
+                        *
+                        * Don't require __GFP_FS, since we're not going into
+                        * the FS, just waiting on its writeback completion.
+                        * Worryingly, ext4 gfs2 and xfs allocate pages with
+                        * grab_cache_page_write_begin(,,AOP_FLAG_NOFS), so
+                        * testing may_enter_fs here is liable to OOM on them.
+                        */
+                       if (global_reclaim(sc) ||
+                           !PageReclaim(page) || !(sc->gfp_mask & __GFP_IO)) {
+                               /*
+                                * This is slightly racy - end_page_writeback()
+                                * might have just cleared PageReclaim, then
+                                * setting PageReclaim here end up interpreted
+                                * as PageReadahead - but that does not matter
+                                * enough to care.  What we do want is for this
+                                * page to have PageReclaim set next time memcg
+                                * reclaim reaches the tests above, so it will
+                                * then wait_on_page_writeback() to avoid OOM;
+                                * and it's also appropriate in global reclaim.
+                                */
+                               SetPageReclaim(page);
+                               nr_writeback++;
+                               goto keep_locked;
+                       }
+                       wait_on_page_writeback(page);
                }
 
                references = page_check_references(page, sc);
@@ -921,6 +954,7 @@ keep:
 
        list_splice(&ret_pages, page_list);
        count_vm_events(PGACTIVATE, pgactivate);
+       mem_cgroup_uncharge_end();
        *ret_nr_dirty += nr_dirty;
        *ret_nr_writeback += nr_writeback;
        return nr_reclaimed;
@@ -2112,6 +2146,83 @@ out:
        return 0;
 }
 
+static bool pfmemalloc_watermark_ok(pg_data_t *pgdat)
+{
+       struct zone *zone;
+       unsigned long pfmemalloc_reserve = 0;
+       unsigned long free_pages = 0;
+       int i;
+       bool wmark_ok;
+
+       for (i = 0; i <= ZONE_NORMAL; i++) {
+               zone = &pgdat->node_zones[i];
+               pfmemalloc_reserve += min_wmark_pages(zone);
+               free_pages += zone_page_state(zone, NR_FREE_PAGES);
+       }
+
+       wmark_ok = free_pages > pfmemalloc_reserve / 2;
+
+       /* kswapd must be awake if processes are being throttled */
+       if (!wmark_ok && waitqueue_active(&pgdat->kswapd_wait)) {
+               pgdat->classzone_idx = min(pgdat->classzone_idx,
+                                               (enum zone_type)ZONE_NORMAL);
+               wake_up_interruptible(&pgdat->kswapd_wait);
+       }
+
+       return wmark_ok;
+}
+
+/*
+ * Throttle direct reclaimers if backing storage is backed by the network
+ * and the PFMEMALLOC reserve for the preferred node is getting dangerously
+ * depleted. kswapd will continue to make progress and wake the processes
+ * when the low watermark is reached
+ */
+static void throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist,
+                                       nodemask_t *nodemask)
+{
+       struct zone *zone;
+       int high_zoneidx = gfp_zone(gfp_mask);
+       pg_data_t *pgdat;
+
+       /*
+        * Kernel threads should not be throttled as they may be indirectly
+        * responsible for cleaning pages necessary for reclaim to make forward
+        * progress. kjournald for example may enter direct reclaim while
+        * committing a transaction where throttling it could forcing other
+        * processes to block on log_wait_commit().
+        */
+       if (current->flags & PF_KTHREAD)
+               return;
+
+       /* Check if the pfmemalloc reserves are ok */
+       first_zones_zonelist(zonelist, high_zoneidx, NULL, &zone);
+       pgdat = zone->zone_pgdat;
+       if (pfmemalloc_watermark_ok(pgdat))
+               return;
+
+       /* Account for the throttling */
+       count_vm_event(PGSCAN_DIRECT_THROTTLE);
+
+       /*
+        * If the caller cannot enter the filesystem, it's possible that it
+        * is due to the caller holding an FS lock or performing a journal
+        * transaction in the case of a filesystem like ext[3|4]. In this case,
+        * it is not safe to block on pfmemalloc_wait as kswapd could be
+        * blocked waiting on the same lock. Instead, throttle for up to a
+        * second before continuing.
+        */
+       if (!(gfp_mask & __GFP_FS)) {
+               wait_event_interruptible_timeout(pgdat->pfmemalloc_wait,
+                       pfmemalloc_watermark_ok(pgdat), HZ);
+               return;
+       }
+
+       /* Throttle until kswapd wakes the process */
+       wait_event_killable(zone->zone_pgdat->pfmemalloc_wait,
+               pfmemalloc_watermark_ok(pgdat));
+}
+
 unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
                                gfp_t gfp_mask, nodemask_t *nodemask)
 {
@@ -2131,6 +2242,15 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
                .gfp_mask = sc.gfp_mask,
        };
 
+       throttle_direct_reclaim(gfp_mask, zonelist, nodemask);
+
+       /*
+        * Do not enter reclaim if fatal signal is pending. 1 is returned so
+        * that the page allocator does not consider triggering OOM
+        */
+       if (fatal_signal_pending(current))
+               return 1;
+
        trace_mm_vmscan_direct_reclaim_begin(order,
                                sc.may_writepage,
                                gfp_mask);
@@ -2142,7 +2262,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
        return nr_reclaimed;
 }
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
 
 unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *memcg,
                                                gfp_t gfp_mask, bool noswap,
@@ -2275,8 +2395,13 @@ static bool pgdat_balanced(pg_data_t *pgdat, unsigned long balanced_pages,
        return balanced_pages >= (present_pages >> 2);
 }
 
-/* is kswapd sleeping prematurely? */
-static bool sleeping_prematurely(pg_data_t *pgdat, int order, long remaining,
+/*
+ * Prepare kswapd for sleeping. This verifies that there are no processes
+ * waiting in throttle_direct_reclaim() and that watermarks have been met.
+ *
+ * Returns true if kswapd is ready to sleep
+ */
+static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, long remaining,
                                        int classzone_idx)
 {
        int i;
@@ -2285,7 +2410,21 @@ static bool sleeping_prematurely(pg_data_t *pgdat, int order, long remaining,
 
        /* If a direct reclaimer woke kswapd within HZ/10, it's premature */
        if (remaining)
-               return true;
+               return false;
+
+       /*
+        * There is a potential race between when kswapd checks its watermarks
+        * and a process gets throttled. There is also a potential race if
+        * processes get throttled, kswapd wakes, a large process exits therby
+        * balancing the zones that causes kswapd to miss a wakeup. If kswapd
+        * is going to sleep, no process should be sleeping on pfmemalloc_wait
+        * so wake them now if necessary. If necessary, processes will wake
+        * kswapd and get throttled again
+        */
+       if (waitqueue_active(&pgdat->pfmemalloc_wait)) {
+               wake_up(&pgdat->pfmemalloc_wait);
+               return false;
+       }
 
        /* Check the watermark levels */
        for (i = 0; i <= classzone_idx; i++) {
@@ -2318,9 +2457,9 @@ static bool sleeping_prematurely(pg_data_t *pgdat, int order, long remaining,
         * must be balanced
         */
        if (order)
-               return !pgdat_balanced(pgdat, balanced, classzone_idx);
+               return pgdat_balanced(pgdat, balanced, classzone_idx);
        else
-               return !all_zones_ok;
+               return all_zones_ok;
 }
 
 /*
@@ -2546,6 +2685,16 @@ loop_again:
                        }
 
                }
+
+               /*
+                * If the low watermark is met there is no need for processes
+                * to be throttled on pfmemalloc_wait as they should not be
+                * able to safely make forward progress. Wake them
+                */
+               if (waitqueue_active(&pgdat->pfmemalloc_wait) &&
+                               pfmemalloc_watermark_ok(pgdat))
+                       wake_up(&pgdat->pfmemalloc_wait);
+
                if (all_zones_ok || (order && pgdat_balanced(pgdat, balanced, *classzone_idx)))
                        break;          /* kswapd: all done */
                /*
@@ -2647,7 +2796,7 @@ out:
        }
 
        /*
-        * Return the order we were reclaiming at so sleeping_prematurely()
+        * Return the order we were reclaiming at so prepare_kswapd_sleep()
         * makes a decision on the order we were last reclaiming at. However,
         * if another caller entered the allocator slow path while kswapd
         * was awake, order will remain at the higher level
@@ -2667,7 +2816,7 @@ static void kswapd_try_to_sleep(pg_data_t *pgdat, int order, int classzone_idx)
        prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);
 
        /* Try to sleep for a short interval */
-       if (!sleeping_prematurely(pgdat, order, remaining, classzone_idx)) {
+       if (prepare_kswapd_sleep(pgdat, order, remaining, classzone_idx)) {
                remaining = schedule_timeout(HZ/10);
                finish_wait(&pgdat->kswapd_wait, &wait);
                prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);
@@ -2677,7 +2826,7 @@ static void kswapd_try_to_sleep(pg_data_t *pgdat, int order, int classzone_idx)
         * After a short sleep, check if it was a premature sleep. If not, then
         * go fully to sleep until explicitly woken up.
         */
-       if (!sleeping_prematurely(pgdat, order, remaining, classzone_idx)) {
+       if (prepare_kswapd_sleep(pgdat, order, remaining, classzone_idx)) {
                trace_mm_vmscan_kswapd_sleep(pgdat->node_id);
 
                /*
index 1bbbbd9776ade1962a277c4e9c6d2161a2963c97..df7a6748231d67e524e2cea0c0a663d91f5d63d2 100644 (file)
@@ -745,6 +745,7 @@ const char * const vmstat_text[] = {
        TEXTS_FOR_ZONES("pgsteal_direct")
        TEXTS_FOR_ZONES("pgscan_kswapd")
        TEXTS_FOR_ZONES("pgscan_direct")
+       "pgscan_direct_throttle",
 
 #ifdef CONFIG_NUMA
        "zone_reclaim_failed",
index d21f32383517f6b66a3b0c43ea534beb3840307c..9ce430b4657c2781efa84b136122b1292516f53d 100644 (file)
@@ -312,7 +312,7 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf,
 
                        fe->is_local = f->is_local;
                        if (!f->is_static)
-                               fe->ageing_timer_value = jiffies_to_clock_t(jiffies - f->updated);
+                               fe->ageing_timer_value = jiffies_delta_to_clock_t(jiffies - f->updated);
                        ++fe;
                        ++num;
                }
index a6747e673426e3f29c734f11596c1be328e7b558..c3530a81a33bf40c9162cb28a6f931ea937baf54 100644 (file)
@@ -170,5 +170,5 @@ void br_stp_port_timer_init(struct net_bridge_port *p)
 unsigned long br_timer_value(const struct timer_list *timer)
 {
        return timer_pending(timer)
-               ? jiffies_to_clock_t(timer->expires - jiffies) : 0;
+               ? jiffies_delta_to_clock_t(timer->expires - jiffies) : 0;
 }
index 78f1cdad5b332b91401570aeccbc12af5bd33d1e..095259f839023a99a2127d1d4173bb29737471df 100644 (file)
@@ -141,7 +141,7 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
        err = sk_filter(sk, skb);
        if (err)
                return err;
-       if (!sk_rmem_schedule(sk, skb->truesize) && rx_flow_is_on(cf_sk)) {
+       if (!sk_rmem_schedule(sk, skb, skb->truesize) && rx_flow_is_on(cf_sk)) {
                set_rx_flow_off(cf_sk);
                net_dbg_ratelimited("sending flow OFF due to rmem_schedule\n");
                caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ);
index ba4323bce0e92beff34d13dcdc45b63a1b14dce7..69e38db28e5f3efcda5ce871ac5682408eb5aa9e 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/string.h>
 
 
+#include <linux/ceph/ceph_features.h>
 #include <linux/ceph/libceph.h>
 #include <linux/ceph/debugfs.h>
 #include <linux/ceph/decode.h>
@@ -460,27 +461,23 @@ struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private,
        client->auth_err = 0;
 
        client->extra_mon_dispatch = NULL;
-       client->supported_features = CEPH_FEATURE_SUPPORTED_DEFAULT |
+       client->supported_features = CEPH_FEATURES_SUPPORTED_DEFAULT |
                supported_features;
-       client->required_features = CEPH_FEATURE_REQUIRED_DEFAULT |
+       client->required_features = CEPH_FEATURES_REQUIRED_DEFAULT |
                required_features;
 
        /* msgr */
        if (ceph_test_opt(client, MYIP))
                myaddr = &client->options->my_addr;
-       client->msgr = ceph_messenger_create(myaddr,
-                                            client->supported_features,
-                                            client->required_features);
-       if (IS_ERR(client->msgr)) {
-               err = PTR_ERR(client->msgr);
-               goto fail;
-       }
-       client->msgr->nocrc = ceph_test_opt(client, NOCRC);
+       ceph_messenger_init(&client->msgr, myaddr,
+               client->supported_features,
+               client->required_features,
+               ceph_test_opt(client, NOCRC));
 
        /* subsystems */
        err = ceph_monc_init(&client->monc, client);
        if (err < 0)
-               goto fail_msgr;
+               goto fail;
        err = ceph_osdc_init(&client->osdc, client);
        if (err < 0)
                goto fail_monc;
@@ -489,8 +486,6 @@ struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private,
 
 fail_monc:
        ceph_monc_stop(&client->monc);
-fail_msgr:
-       ceph_messenger_destroy(client->msgr);
 fail:
        kfree(client);
        return ERR_PTR(err);
@@ -501,6 +496,8 @@ void ceph_destroy_client(struct ceph_client *client)
 {
        dout("destroy_client %p\n", client);
 
+       atomic_set(&client->msgr.stopping, 1);
+
        /* unmount */
        ceph_osdc_stop(&client->osdc);
 
@@ -508,8 +505,6 @@ void ceph_destroy_client(struct ceph_client *client)
 
        ceph_debugfs_client_cleanup(client);
 
-       ceph_messenger_destroy(client->msgr);
-
        ceph_destroy_options(client->options);
 
        kfree(client);
index d7edc24333b84d5aab17da2d983878ff5044b2bb..35fce755ce103528071d422ca6e9ca2e0faf6a27 100644 (file)
@@ -306,7 +306,6 @@ static int crush_choose(const struct crush_map *map,
        int item = 0;
        int itemtype;
        int collide, reject;
-       const unsigned int orig_tries = 5; /* attempts before we fall back to search */
 
        dprintk("CHOOSE%s bucket %d x %d outpos %d numrep %d\n", recurse_to_leaf ? "_LEAF" : "",
                bucket->id, x, outpos, numrep);
@@ -351,8 +350,9 @@ static int crush_choose(const struct crush_map *map,
                                        reject = 1;
                                        goto reject;
                                }
-                               if (flocal >= (in->size>>1) &&
-                                   flocal > orig_tries)
+                               if (map->choose_local_fallback_tries > 0 &&
+                                   flocal >= (in->size>>1) &&
+                                   flocal > map->choose_local_fallback_tries)
                                        item = bucket_perm_choose(in, x, r);
                                else
                                        item = crush_bucket_choose(in, x, r);
@@ -422,13 +422,14 @@ reject:
                                        ftotal++;
                                        flocal++;
 
-                                       if (collide && flocal < 3)
+                                       if (collide && flocal <= map->choose_local_tries)
                                                /* retry locally a few times */
                                                retry_bucket = 1;
-                                       else if (flocal <= in->size + orig_tries)
+                                       else if (map->choose_local_fallback_tries > 0 &&
+                                                flocal <= in->size + map->choose_local_fallback_tries)
                                                /* exhaustive bucket search */
                                                retry_bucket = 1;
-                                       else if (ftotal < 20)
+                                       else if (ftotal <= map->choose_total_tries)
                                                /* then retry descent */
                                                retry_descent = 1;
                                        else
index 10255e81be79d84c5428504c2a79be3180626f79..b9796750034afc093c172dc9ac92b2e864d87271 100644 (file)
  * the sender.
  */
 
+/*
+ * We track the state of the socket on a given connection using
+ * values defined below.  The transition to a new socket state is
+ * handled by a function which verifies we aren't coming from an
+ * unexpected state.
+ *
+ *      --------
+ *      | NEW* |  transient initial state
+ *      --------
+ *          | con_sock_state_init()
+ *          v
+ *      ----------
+ *      | CLOSED |  initialized, but no socket (and no
+ *      ----------  TCP connection)
+ *       ^      \
+ *       |       \ con_sock_state_connecting()
+ *       |        ----------------------
+ *       |                              \
+ *       + con_sock_state_closed()       \
+ *       |+---------------------------    \
+ *       | \                          \    \
+ *       |  -----------                \    \
+ *       |  | CLOSING |  socket event;  \    \
+ *       |  -----------  await close     \    \
+ *       |       ^                        \   |
+ *       |       |                         \  |
+ *       |       + con_sock_state_closing() \ |
+ *       |      / \                         | |
+ *       |     /   ---------------          | |
+ *       |    /                   \         v v
+ *       |   /                    --------------
+ *       |  /    -----------------| CONNECTING |  socket created, TCP
+ *       |  |   /                 --------------  connect initiated
+ *       |  |   | con_sock_state_connected()
+ *       |  |   v
+ *      -------------
+ *      | CONNECTED |  TCP connection established
+ *      -------------
+ *
+ * State values for ceph_connection->sock_state; NEW is assumed to be 0.
+ */
+
+#define CON_SOCK_STATE_NEW             0       /* -> CLOSED */
+#define CON_SOCK_STATE_CLOSED          1       /* -> CONNECTING */
+#define CON_SOCK_STATE_CONNECTING      2       /* -> CONNECTED or -> CLOSING */
+#define CON_SOCK_STATE_CONNECTED       3       /* -> CLOSING or -> CLOSED */
+#define CON_SOCK_STATE_CLOSING         4       /* -> CLOSED */
+
+/*
+ * connection states
+ */
+#define CON_STATE_CLOSED        1  /* -> PREOPEN */
+#define CON_STATE_PREOPEN       2  /* -> CONNECTING, CLOSED */
+#define CON_STATE_CONNECTING    3  /* -> NEGOTIATING, CLOSED */
+#define CON_STATE_NEGOTIATING   4  /* -> OPEN, CLOSED */
+#define CON_STATE_OPEN          5  /* -> STANDBY, CLOSED */
+#define CON_STATE_STANDBY       6  /* -> PREOPEN, CLOSED */
+
+/*
+ * ceph_connection flag bits
+ */
+#define CON_FLAG_LOSSYTX           0  /* we can close channel or drop
+                                      * messages on errors */
+#define CON_FLAG_KEEPALIVE_PENDING 1  /* we need to send a keepalive */
+#define CON_FLAG_WRITE_PENDING    2  /* we have data ready to send */
+#define CON_FLAG_SOCK_CLOSED      3  /* socket state changed to closed */
+#define CON_FLAG_BACKOFF           4  /* need to retry queuing delayed work */
+
 /* static tag bytes (protocol control messages) */
 static char tag_msg = CEPH_MSGR_TAG_MSG;
 static char tag_ack = CEPH_MSGR_TAG_ACK;
@@ -147,72 +215,130 @@ void ceph_msgr_flush(void)
 }
 EXPORT_SYMBOL(ceph_msgr_flush);
 
+/* Connection socket state transition functions */
+
+static void con_sock_state_init(struct ceph_connection *con)
+{
+       int old_state;
+
+       old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CLOSED);
+       if (WARN_ON(old_state != CON_SOCK_STATE_NEW))
+               printk("%s: unexpected old state %d\n", __func__, old_state);
+       dout("%s con %p sock %d -> %d\n", __func__, con, old_state,
+            CON_SOCK_STATE_CLOSED);
+}
+
+static void con_sock_state_connecting(struct ceph_connection *con)
+{
+       int old_state;
+
+       old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CONNECTING);
+       if (WARN_ON(old_state != CON_SOCK_STATE_CLOSED))
+               printk("%s: unexpected old state %d\n", __func__, old_state);
+       dout("%s con %p sock %d -> %d\n", __func__, con, old_state,
+            CON_SOCK_STATE_CONNECTING);
+}
+
+static void con_sock_state_connected(struct ceph_connection *con)
+{
+       int old_state;
+
+       old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CONNECTED);
+       if (WARN_ON(old_state != CON_SOCK_STATE_CONNECTING))
+               printk("%s: unexpected old state %d\n", __func__, old_state);
+       dout("%s con %p sock %d -> %d\n", __func__, con, old_state,
+            CON_SOCK_STATE_CONNECTED);
+}
+
+static void con_sock_state_closing(struct ceph_connection *con)
+{
+       int old_state;
+
+       old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CLOSING);
+       if (WARN_ON(old_state != CON_SOCK_STATE_CONNECTING &&
+                       old_state != CON_SOCK_STATE_CONNECTED &&
+                       old_state != CON_SOCK_STATE_CLOSING))
+               printk("%s: unexpected old state %d\n", __func__, old_state);
+       dout("%s con %p sock %d -> %d\n", __func__, con, old_state,
+            CON_SOCK_STATE_CLOSING);
+}
+
+static void con_sock_state_closed(struct ceph_connection *con)
+{
+       int old_state;
+
+       old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CLOSED);
+       if (WARN_ON(old_state != CON_SOCK_STATE_CONNECTED &&
+                   old_state != CON_SOCK_STATE_CLOSING &&
+                   old_state != CON_SOCK_STATE_CONNECTING &&
+                   old_state != CON_SOCK_STATE_CLOSED))
+               printk("%s: unexpected old state %d\n", __func__, old_state);
+       dout("%s con %p sock %d -> %d\n", __func__, con, old_state,
+            CON_SOCK_STATE_CLOSED);
+}
 
 /*
  * socket callback functions
  */
 
 /* data available on socket, or listen socket received a connect */
-static void ceph_data_ready(struct sock *sk, int count_unused)
+static void ceph_sock_data_ready(struct sock *sk, int count_unused)
 {
        struct ceph_connection *con = sk->sk_user_data;
+       if (atomic_read(&con->msgr->stopping)) {
+               return;
+       }
 
        if (sk->sk_state != TCP_CLOSE_WAIT) {
-               dout("ceph_data_ready on %p state = %lu, queueing work\n",
+               dout("%s on %p state = %lu, queueing work\n", __func__,
                     con, con->state);
                queue_con(con);
        }
 }
 
 /* socket has buffer space for writing */
-static void ceph_write_space(struct sock *sk)
+static void ceph_sock_write_space(struct sock *sk)
 {
        struct ceph_connection *con = sk->sk_user_data;
 
        /* only queue to workqueue if there is data we want to write,
         * and there is sufficient space in the socket buffer to accept
-        * more data.  clear SOCK_NOSPACE so that ceph_write_space()
+        * more data.  clear SOCK_NOSPACE so that ceph_sock_write_space()
         * doesn't get called again until try_write() fills the socket
         * buffer. See net/ipv4/tcp_input.c:tcp_check_space()
         * and net/core/stream.c:sk_stream_write_space().
         */
-       if (test_bit(WRITE_PENDING, &con->state)) {
+       if (test_bit(CON_FLAG_WRITE_PENDING, &con->flags)) {
                if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) {
-                       dout("ceph_write_space %p queueing write work\n", con);
+                       dout("%s %p queueing write work\n", __func__, con);
                        clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
                        queue_con(con);
                }
        } else {
-               dout("ceph_write_space %p nothing to write\n", con);
+               dout("%s %p nothing to write\n", __func__, con);
        }
 }
 
 /* socket's state has changed */
-static void ceph_state_change(struct sock *sk)
+static void ceph_sock_state_change(struct sock *sk)
 {
        struct ceph_connection *con = sk->sk_user_data;
 
-       dout("ceph_state_change %p state = %lu sk_state = %u\n",
+       dout("%s %p state = %lu sk_state = %u\n", __func__,
             con, con->state, sk->sk_state);
 
-       if (test_bit(CLOSED, &con->state))
-               return;
-
        switch (sk->sk_state) {
        case TCP_CLOSE:
-               dout("ceph_state_change TCP_CLOSE\n");
+               dout("%s TCP_CLOSE\n", __func__);
        case TCP_CLOSE_WAIT:
-               dout("ceph_state_change TCP_CLOSE_WAIT\n");
-               if (test_and_set_bit(SOCK_CLOSED, &con->state) == 0) {
-                       if (test_bit(CONNECTING, &con->state))
-                               con->error_msg = "connection failed";
-                       else
-                               con->error_msg = "socket closed";
-                       queue_con(con);
-               }
+               dout("%s TCP_CLOSE_WAIT\n", __func__);
+               con_sock_state_closing(con);
+               set_bit(CON_FLAG_SOCK_CLOSED, &con->flags);
+               queue_con(con);
                break;
        case TCP_ESTABLISHED:
-               dout("ceph_state_change TCP_ESTABLISHED\n");
+               dout("%s TCP_ESTABLISHED\n", __func__);
+               con_sock_state_connected(con);
                queue_con(con);
                break;
        default:        /* Everything else is uninteresting */
@@ -228,9 +354,9 @@ static void set_sock_callbacks(struct socket *sock,
 {
        struct sock *sk = sock->sk;
        sk->sk_user_data = con;
-       sk->sk_data_ready = ceph_data_ready;
-       sk->sk_write_space = ceph_write_space;
-       sk->sk_state_change = ceph_state_change;
+       sk->sk_data_ready = ceph_sock_data_ready;
+       sk->sk_write_space = ceph_sock_write_space;
+       sk->sk_state_change = ceph_sock_state_change;
 }
 
 
@@ -262,6 +388,7 @@ static int ceph_tcp_connect(struct ceph_connection *con)
 
        dout("connect %s\n", ceph_pr_addr(&con->peer_addr.in_addr));
 
+       con_sock_state_connecting(con);
        ret = sock->ops->connect(sock, (struct sockaddr *)paddr, sizeof(*paddr),
                                 O_NONBLOCK);
        if (ret == -EINPROGRESS) {
@@ -277,7 +404,6 @@ static int ceph_tcp_connect(struct ceph_connection *con)
                return ret;
        }
        con->sock = sock;
-
        return 0;
 }
 
@@ -333,16 +459,24 @@ static int ceph_tcp_sendpage(struct socket *sock, struct page *page,
  */
 static int con_close_socket(struct ceph_connection *con)
 {
-       int rc;
+       int rc = 0;
 
        dout("con_close_socket on %p sock %p\n", con, con->sock);
-       if (!con->sock)
-               return 0;
-       set_bit(SOCK_CLOSED, &con->state);
-       rc = con->sock->ops->shutdown(con->sock, SHUT_RDWR);
-       sock_release(con->sock);
-       con->sock = NULL;
-       clear_bit(SOCK_CLOSED, &con->state);
+       if (con->sock) {
+               rc = con->sock->ops->shutdown(con->sock, SHUT_RDWR);
+               sock_release(con->sock);
+               con->sock = NULL;
+       }
+
+       /*
+        * Forcibly clear the SOCK_CLOSED flag.  It gets set
+        * independent of the connection mutex, and we could have
+        * received a socket close event before we had the chance to
+        * shut the socket down.
+        */
+       clear_bit(CON_FLAG_SOCK_CLOSED, &con->flags);
+
+       con_sock_state_closed(con);
        return rc;
 }
 
@@ -353,6 +487,10 @@ static int con_close_socket(struct ceph_connection *con)
 static void ceph_msg_remove(struct ceph_msg *msg)
 {
        list_del_init(&msg->list_head);
+       BUG_ON(msg->con == NULL);
+       msg->con->ops->put(msg->con);
+       msg->con = NULL;
+
        ceph_msg_put(msg);
 }
 static void ceph_msg_remove_list(struct list_head *head)
@@ -372,8 +510,11 @@ static void reset_connection(struct ceph_connection *con)
        ceph_msg_remove_list(&con->out_sent);
 
        if (con->in_msg) {
+               BUG_ON(con->in_msg->con != con);
+               con->in_msg->con = NULL;
                ceph_msg_put(con->in_msg);
                con->in_msg = NULL;
+               con->ops->put(con);
        }
 
        con->connect_seq = 0;
@@ -391,32 +532,44 @@ static void reset_connection(struct ceph_connection *con)
  */
 void ceph_con_close(struct ceph_connection *con)
 {
+       mutex_lock(&con->mutex);
        dout("con_close %p peer %s\n", con,
             ceph_pr_addr(&con->peer_addr.in_addr));
-       set_bit(CLOSED, &con->state);  /* in case there's queued work */
-       clear_bit(STANDBY, &con->state);  /* avoid connect_seq bump */
-       clear_bit(LOSSYTX, &con->state);  /* so we retry next connect */
-       clear_bit(KEEPALIVE_PENDING, &con->state);
-       clear_bit(WRITE_PENDING, &con->state);
-       mutex_lock(&con->mutex);
+       con->state = CON_STATE_CLOSED;
+
+       clear_bit(CON_FLAG_LOSSYTX, &con->flags); /* so we retry next connect */
+       clear_bit(CON_FLAG_KEEPALIVE_PENDING, &con->flags);
+       clear_bit(CON_FLAG_WRITE_PENDING, &con->flags);
+       clear_bit(CON_FLAG_KEEPALIVE_PENDING, &con->flags);
+       clear_bit(CON_FLAG_BACKOFF, &con->flags);
+
        reset_connection(con);
        con->peer_global_seq = 0;
        cancel_delayed_work(&con->work);
+       con_close_socket(con);
        mutex_unlock(&con->mutex);
-       queue_con(con);
 }
 EXPORT_SYMBOL(ceph_con_close);
 
 /*
  * Reopen a closed connection, with a new peer address.
  */
-void ceph_con_open(struct ceph_connection *con, struct ceph_entity_addr *addr)
+void ceph_con_open(struct ceph_connection *con,
+                  __u8 entity_type, __u64 entity_num,
+                  struct ceph_entity_addr *addr)
 {
+       mutex_lock(&con->mutex);
        dout("con_open %p %s\n", con, ceph_pr_addr(&addr->in_addr));
-       set_bit(OPENING, &con->state);
-       clear_bit(CLOSED, &con->state);
+
+       BUG_ON(con->state != CON_STATE_CLOSED);
+       con->state = CON_STATE_PREOPEN;
+
+       con->peer_name.type = (__u8) entity_type;
+       con->peer_name.num = cpu_to_le64(entity_num);
+
        memcpy(&con->peer_addr, addr, sizeof(*addr));
        con->delay = 0;      /* reset backoff memory */
+       mutex_unlock(&con->mutex);
        queue_con(con);
 }
 EXPORT_SYMBOL(ceph_con_open);
@@ -429,43 +582,27 @@ bool ceph_con_opened(struct ceph_connection *con)
        return con->connect_seq > 0;
 }
 
-/*
- * generic get/put
- */
-struct ceph_connection *ceph_con_get(struct ceph_connection *con)
-{
-       int nref = __atomic_add_unless(&con->nref, 1, 0);
-
-       dout("con_get %p nref = %d -> %d\n", con, nref, nref + 1);
-
-       return nref ? con : NULL;
-}
-
-void ceph_con_put(struct ceph_connection *con)
-{
-       int nref = atomic_dec_return(&con->nref);
-
-       BUG_ON(nref < 0);
-       if (nref == 0) {
-               BUG_ON(con->sock);
-               kfree(con);
-       }
-       dout("con_put %p nref = %d -> %d\n", con, nref + 1, nref);
-}
-
 /*
  * initialize a new connection.
  */
-void ceph_con_init(struct ceph_messenger *msgr, struct ceph_connection *con)
+void ceph_con_init(struct ceph_connection *con, void *private,
+       const struct ceph_connection_operations *ops,
+       struct ceph_messenger *msgr)
 {
        dout("con_init %p\n", con);
        memset(con, 0, sizeof(*con));
-       atomic_set(&con->nref, 1);
+       con->private = private;
+       con->ops = ops;
        con->msgr = msgr;
+
+       con_sock_state_init(con);
+
        mutex_init(&con->mutex);
        INIT_LIST_HEAD(&con->out_queue);
        INIT_LIST_HEAD(&con->out_sent);
        INIT_DELAYED_WORK(&con->work, con_work);
+
+       con->state = CON_STATE_CLOSED;
 }
 EXPORT_SYMBOL(ceph_con_init);
 
@@ -486,14 +623,14 @@ static u32 get_global_seq(struct ceph_messenger *msgr, u32 gt)
        return ret;
 }
 
-static void ceph_con_out_kvec_reset(struct ceph_connection *con)
+static void con_out_kvec_reset(struct ceph_connection *con)
 {
        con->out_kvec_left = 0;
        con->out_kvec_bytes = 0;
        con->out_kvec_cur = &con->out_kvec[0];
 }
 
-static void ceph_con_out_kvec_add(struct ceph_connection *con,
+static void con_out_kvec_add(struct ceph_connection *con,
                                size_t size, void *data)
 {
        int index;
@@ -507,6 +644,53 @@ static void ceph_con_out_kvec_add(struct ceph_connection *con,
        con->out_kvec_bytes += size;
 }
 
+#ifdef CONFIG_BLOCK
+static void init_bio_iter(struct bio *bio, struct bio **iter, int *seg)
+{
+       if (!bio) {
+               *iter = NULL;
+               *seg = 0;
+               return;
+       }
+       *iter = bio;
+       *seg = bio->bi_idx;
+}
+
+static void iter_bio_next(struct bio **bio_iter, int *seg)
+{
+       if (*bio_iter == NULL)
+               return;
+
+       BUG_ON(*seg >= (*bio_iter)->bi_vcnt);
+
+       (*seg)++;
+       if (*seg == (*bio_iter)->bi_vcnt)
+               init_bio_iter((*bio_iter)->bi_next, bio_iter, seg);
+}
+#endif
+
+static void prepare_write_message_data(struct ceph_connection *con)
+{
+       struct ceph_msg *msg = con->out_msg;
+
+       BUG_ON(!msg);
+       BUG_ON(!msg->hdr.data_len);
+
+       /* initialize page iterator */
+       con->out_msg_pos.page = 0;
+       if (msg->pages)
+               con->out_msg_pos.page_pos = msg->page_alignment;
+       else
+               con->out_msg_pos.page_pos = 0;
+#ifdef CONFIG_BLOCK
+       if (msg->bio)
+               init_bio_iter(msg->bio, &msg->bio_iter, &msg->bio_seg);
+#endif
+       con->out_msg_pos.data_pos = 0;
+       con->out_msg_pos.did_page_crc = false;
+       con->out_more = 1;  /* data + footer will follow */
+}
+
 /*
  * Prepare footer for currently outgoing message, and finish things
  * off.  Assumes out_kvec* are already valid.. we just add on to the end.
@@ -516,6 +700,8 @@ static void prepare_write_message_footer(struct ceph_connection *con)
        struct ceph_msg *m = con->out_msg;
        int v = con->out_kvec_left;
 
+       m->footer.flags |= CEPH_MSG_FOOTER_COMPLETE;
+
        dout("prepare_write_message_footer %p\n", con);
        con->out_kvec_is_msg = true;
        con->out_kvec[v].iov_base = &m->footer;
@@ -534,7 +720,7 @@ static void prepare_write_message(struct ceph_connection *con)
        struct ceph_msg *m;
        u32 crc;
 
-       ceph_con_out_kvec_reset(con);
+       con_out_kvec_reset(con);
        con->out_kvec_is_msg = true;
        con->out_msg_done = false;
 
@@ -542,14 +728,16 @@ static void prepare_write_message(struct ceph_connection *con)
         * TCP packet that's a good thing. */
        if (con->in_seq > con->in_seq_acked) {
                con->in_seq_acked = con->in_seq;
-               ceph_con_out_kvec_add(con, sizeof (tag_ack), &tag_ack);
+               con_out_kvec_add(con, sizeof (tag_ack), &tag_ack);
                con->out_temp_ack = cpu_to_le64(con->in_seq_acked);
-               ceph_con_out_kvec_add(con, sizeof (con->out_temp_ack),
+               con_out_kvec_add(con, sizeof (con->out_temp_ack),
                        &con->out_temp_ack);
        }
 
+       BUG_ON(list_empty(&con->out_queue));
        m = list_first_entry(&con->out_queue, struct ceph_msg, list_head);
        con->out_msg = m;
+       BUG_ON(m->con != con);
 
        /* put message on sent list */
        ceph_msg_get(m);
@@ -576,18 +764,18 @@ static void prepare_write_message(struct ceph_connection *con)
        BUG_ON(le32_to_cpu(m->hdr.front_len) != m->front.iov_len);
 
        /* tag + hdr + front + middle */
-       ceph_con_out_kvec_add(con, sizeof (tag_msg), &tag_msg);
-       ceph_con_out_kvec_add(con, sizeof (m->hdr), &m->hdr);
-       ceph_con_out_kvec_add(con, m->front.iov_len, m->front.iov_base);
+       con_out_kvec_add(con, sizeof (tag_msg), &tag_msg);
+       con_out_kvec_add(con, sizeof (m->hdr), &m->hdr);
+       con_out_kvec_add(con, m->front.iov_len, m->front.iov_base);
 
        if (m->middle)
-               ceph_con_out_kvec_add(con, m->middle->vec.iov_len,
+               con_out_kvec_add(con, m->middle->vec.iov_len,
                        m->middle->vec.iov_base);
 
        /* fill in crc (except data pages), footer */
        crc = crc32c(0, &m->hdr, offsetof(struct ceph_msg_header, crc));
        con->out_msg->hdr.crc = cpu_to_le32(crc);
-       con->out_msg->footer.flags = CEPH_MSG_FOOTER_COMPLETE;
+       con->out_msg->footer.flags = 0;
 
        crc = crc32c(0, m->front.iov_base, m->front.iov_len);
        con->out_msg->footer.front_crc = cpu_to_le32(crc);
@@ -597,28 +785,19 @@ static void prepare_write_message(struct ceph_connection *con)
                con->out_msg->footer.middle_crc = cpu_to_le32(crc);
        } else
                con->out_msg->footer.middle_crc = 0;
-       con->out_msg->footer.data_crc = 0;
-       dout("prepare_write_message front_crc %u data_crc %u\n",
+       dout("%s front_crc %u middle_crc %u\n", __func__,
             le32_to_cpu(con->out_msg->footer.front_crc),
             le32_to_cpu(con->out_msg->footer.middle_crc));
 
        /* is there a data payload? */
-       if (le32_to_cpu(m->hdr.data_len) > 0) {
-               /* initialize page iterator */
-               con->out_msg_pos.page = 0;
-               if (m->pages)
-                       con->out_msg_pos.page_pos = m->page_alignment;
-               else
-                       con->out_msg_pos.page_pos = 0;
-               con->out_msg_pos.data_pos = 0;
-               con->out_msg_pos.did_page_crc = false;
-               con->out_more = 1;  /* data + footer will follow */
-       } else {
+       con->out_msg->footer.data_crc = 0;
+       if (m->hdr.data_len)
+               prepare_write_message_data(con);
+       else
                /* no, queue up footer too and be done */
                prepare_write_message_footer(con);
-       }
 
-       set_bit(WRITE_PENDING, &con->state);
+       set_bit(CON_FLAG_WRITE_PENDING, &con->flags);
 }
 
 /*
@@ -630,16 +809,16 @@ static void prepare_write_ack(struct ceph_connection *con)
             con->in_seq_acked, con->in_seq);
        con->in_seq_acked = con->in_seq;
 
-       ceph_con_out_kvec_reset(con);
+       con_out_kvec_reset(con);
 
-       ceph_con_out_kvec_add(con, sizeof (tag_ack), &tag_ack);
+       con_out_kvec_add(con, sizeof (tag_ack), &tag_ack);
 
        con->out_temp_ack = cpu_to_le64(con->in_seq_acked);
-       ceph_con_out_kvec_add(con, sizeof (con->out_temp_ack),
+       con_out_kvec_add(con, sizeof (con->out_temp_ack),
                                &con->out_temp_ack);
 
        con->out_more = 1;  /* more will follow.. eventually.. */
-       set_bit(WRITE_PENDING, &con->state);
+       set_bit(CON_FLAG_WRITE_PENDING, &con->flags);
 }
 
 /*
@@ -648,9 +827,9 @@ static void prepare_write_ack(struct ceph_connection *con)
 static void prepare_write_keepalive(struct ceph_connection *con)
 {
        dout("prepare_write_keepalive %p\n", con);
-       ceph_con_out_kvec_reset(con);
-       ceph_con_out_kvec_add(con, sizeof (tag_keepalive), &tag_keepalive);
-       set_bit(WRITE_PENDING, &con->state);
+       con_out_kvec_reset(con);
+       con_out_kvec_add(con, sizeof (tag_keepalive), &tag_keepalive);
+       set_bit(CON_FLAG_WRITE_PENDING, &con->flags);
 }
 
 /*
@@ -665,27 +844,21 @@ static struct ceph_auth_handshake *get_connect_authorizer(struct ceph_connection
        if (!con->ops->get_authorizer) {
                con->out_connect.authorizer_protocol = CEPH_AUTH_UNKNOWN;
                con->out_connect.authorizer_len = 0;
-
                return NULL;
        }
 
        /* Can't hold the mutex while getting authorizer */
-
        mutex_unlock(&con->mutex);
-
        auth = con->ops->get_authorizer(con, auth_proto, con->auth_retry);
-
        mutex_lock(&con->mutex);
 
        if (IS_ERR(auth))
                return auth;
-       if (test_bit(CLOSED, &con->state) || test_bit(OPENING, &con->state))
+       if (con->state != CON_STATE_NEGOTIATING)
                return ERR_PTR(-EAGAIN);
 
        con->auth_reply_buf = auth->authorizer_reply_buf;
        con->auth_reply_buf_len = auth->authorizer_reply_buf_len;
-
-
        return auth;
 }
 
@@ -694,12 +867,12 @@ static struct ceph_auth_handshake *get_connect_authorizer(struct ceph_connection
  */
 static void prepare_write_banner(struct ceph_connection *con)
 {
-       ceph_con_out_kvec_add(con, strlen(CEPH_BANNER), CEPH_BANNER);
-       ceph_con_out_kvec_add(con, sizeof (con->msgr->my_enc_addr),
+       con_out_kvec_add(con, strlen(CEPH_BANNER), CEPH_BANNER);
+       con_out_kvec_add(con, sizeof (con->msgr->my_enc_addr),
                                        &con->msgr->my_enc_addr);
 
        con->out_more = 0;
-       set_bit(WRITE_PENDING, &con->state);
+       set_bit(CON_FLAG_WRITE_PENDING, &con->flags);
 }
 
 static int prepare_write_connect(struct ceph_connection *con)
@@ -742,14 +915,15 @@ static int prepare_write_connect(struct ceph_connection *con)
        con->out_connect.authorizer_len = auth ?
                cpu_to_le32(auth->authorizer_buf_len) : 0;
 
-       ceph_con_out_kvec_add(con, sizeof (con->out_connect),
+       con_out_kvec_reset(con);
+       con_out_kvec_add(con, sizeof (con->out_connect),
                                        &con->out_connect);
        if (auth && auth->authorizer_buf_len)
-               ceph_con_out_kvec_add(con, auth->authorizer_buf_len,
+               con_out_kvec_add(con, auth->authorizer_buf_len,
                                        auth->authorizer_buf);
 
        con->out_more = 0;
-       set_bit(WRITE_PENDING, &con->state);
+       set_bit(CON_FLAG_WRITE_PENDING, &con->flags);
 
        return 0;
 }
@@ -797,30 +971,34 @@ out:
        return ret;  /* done! */
 }
 
-#ifdef CONFIG_BLOCK
-static void init_bio_iter(struct bio *bio, struct bio **iter, int *seg)
+static void out_msg_pos_next(struct ceph_connection *con, struct page *page,
+                       size_t len, size_t sent, bool in_trail)
 {
-       if (!bio) {
-               *iter = NULL;
-               *seg = 0;
-               return;
-       }
-       *iter = bio;
-       *seg = bio->bi_idx;
-}
+       struct ceph_msg *msg = con->out_msg;
 
-static void iter_bio_next(struct bio **bio_iter, int *seg)
-{
-       if (*bio_iter == NULL)
-               return;
+       BUG_ON(!msg);
+       BUG_ON(!sent);
 
-       BUG_ON(*seg >= (*bio_iter)->bi_vcnt);
+       con->out_msg_pos.data_pos += sent;
+       con->out_msg_pos.page_pos += sent;
+       if (sent < len)
+               return;
 
-       (*seg)++;
-       if (*seg == (*bio_iter)->bi_vcnt)
-               init_bio_iter((*bio_iter)->bi_next, bio_iter, seg);
-}
+       BUG_ON(sent != len);
+       con->out_msg_pos.page_pos = 0;
+       con->out_msg_pos.page++;
+       con->out_msg_pos.did_page_crc = false;
+       if (in_trail)
+               list_move_tail(&page->lru,
+                              &msg->trail->head);
+       else if (msg->pagelist)
+               list_move_tail(&page->lru,
+                              &msg->pagelist->head);
+#ifdef CONFIG_BLOCK
+       else if (msg->bio)
+               iter_bio_next(&msg->bio_iter, &msg->bio_seg);
 #endif
+}
 
 /*
  * Write as much message data payload as we can.  If we finish, queue
@@ -837,41 +1015,36 @@ static int write_partial_msg_pages(struct ceph_connection *con)
        bool do_datacrc = !con->msgr->nocrc;
        int ret;
        int total_max_write;
-       int in_trail = 0;
-       size_t trail_len = (msg->trail ? msg->trail->length : 0);
+       bool in_trail = false;
+       const size_t trail_len = (msg->trail ? msg->trail->length : 0);
+       const size_t trail_off = data_len - trail_len;
 
        dout("write_partial_msg_pages %p msg %p page %d/%d offset %d\n",
-            con, con->out_msg, con->out_msg_pos.page, con->out_msg->nr_pages,
+            con, msg, con->out_msg_pos.page, msg->nr_pages,
             con->out_msg_pos.page_pos);
 
-#ifdef CONFIG_BLOCK
-       if (msg->bio && !msg->bio_iter)
-               init_bio_iter(msg->bio, &msg->bio_iter, &msg->bio_seg);
-#endif
-
+       /*
+        * Iterate through each page that contains data to be
+        * written, and send as much as possible for each.
+        *
+        * If we are calculating the data crc (the default), we will
+        * need to map the page.  If we have no pages, they have
+        * been revoked, so use the zero page.
+        */
        while (data_len > con->out_msg_pos.data_pos) {
                struct page *page = NULL;
                int max_write = PAGE_SIZE;
                int bio_offset = 0;
 
-               total_max_write = data_len - trail_len -
-                       con->out_msg_pos.data_pos;
-
-               /*
-                * if we are calculating the data crc (the default), we need
-                * to map the page.  if our pages[] has been revoked, use the
-                * zero page.
-                */
-
-               /* have we reached the trail part of the data? */
-               if (con->out_msg_pos.data_pos >= data_len - trail_len) {
-                       in_trail = 1;
+               in_trail = in_trail || con->out_msg_pos.data_pos >= trail_off;
+               if (!in_trail)
+                       total_max_write = trail_off - con->out_msg_pos.data_pos;
 
+               if (in_trail) {
                        total_max_write = data_len - con->out_msg_pos.data_pos;
 
                        page = list_first_entry(&msg->trail->head,
                                                struct page, lru);
-                       max_write = PAGE_SIZE;
                } else if (msg->pages) {
                        page = msg->pages[con->out_msg_pos.page];
                } else if (msg->pagelist) {
@@ -894,15 +1067,14 @@ static int write_partial_msg_pages(struct ceph_connection *con)
 
                if (do_datacrc && !con->out_msg_pos.did_page_crc) {
                        void *base;
-                       u32 crc;
-                       u32 tmpcrc = le32_to_cpu(con->out_msg->footer.data_crc);
+                       u32 crc = le32_to_cpu(msg->footer.data_crc);
                        char *kaddr;
 
                        kaddr = kmap(page);
                        BUG_ON(kaddr == NULL);
                        base = kaddr + con->out_msg_pos.page_pos + bio_offset;
-                       crc = crc32c(tmpcrc, base, len);
-                       con->out_msg->footer.data_crc = cpu_to_le32(crc);
+                       crc = crc32c(crc, base, len);
+                       msg->footer.data_crc = cpu_to_le32(crc);
                        con->out_msg_pos.did_page_crc = true;
                }
                ret = ceph_tcp_sendpage(con->sock, page,
@@ -915,31 +1087,15 @@ static int write_partial_msg_pages(struct ceph_connection *con)
                if (ret <= 0)
                        goto out;
 
-               con->out_msg_pos.data_pos += ret;
-               con->out_msg_pos.page_pos += ret;
-               if (ret == len) {
-                       con->out_msg_pos.page_pos = 0;
-                       con->out_msg_pos.page++;
-                       con->out_msg_pos.did_page_crc = false;
-                       if (in_trail)
-                               list_move_tail(&page->lru,
-                                              &msg->trail->head);
-                       else if (msg->pagelist)
-                               list_move_tail(&page->lru,
-                                              &msg->pagelist->head);
-#ifdef CONFIG_BLOCK
-                       else if (msg->bio)
-                               iter_bio_next(&msg->bio_iter, &msg->bio_seg);
-#endif
-               }
+               out_msg_pos_next(con, page, len, (size_t) ret, in_trail);
        }
 
        dout("write_partial_msg_pages %p msg %p done\n", con, msg);
 
        /* prepare and queue up footer, too */
        if (!do_datacrc)
-               con->out_msg->footer.flags |= CEPH_MSG_FOOTER_NOCRC;
-       ceph_con_out_kvec_reset(con);
+               msg->footer.flags |= CEPH_MSG_FOOTER_NOCRC;
+       con_out_kvec_reset(con);
        prepare_write_message_footer(con);
        ret = 1;
 out:
@@ -1351,20 +1507,14 @@ static int process_banner(struct ceph_connection *con)
                     ceph_pr_addr(&con->msgr->inst.addr.in_addr));
        }
 
-       set_bit(NEGOTIATING, &con->state);
-       prepare_read_connect(con);
        return 0;
 }
 
 static void fail_protocol(struct ceph_connection *con)
 {
        reset_connection(con);
-       set_bit(CLOSED, &con->state);  /* in case there's queued work */
-
-       mutex_unlock(&con->mutex);
-       if (con->ops->bad_proto)
-               con->ops->bad_proto(con);
-       mutex_lock(&con->mutex);
+       BUG_ON(con->state != CON_STATE_NEGOTIATING);
+       con->state = CON_STATE_CLOSED;
 }
 
 static int process_connect(struct ceph_connection *con)
@@ -1407,7 +1557,6 @@ static int process_connect(struct ceph_connection *con)
                        return -1;
                }
                con->auth_retry = 1;
-               ceph_con_out_kvec_reset(con);
                ret = prepare_write_connect(con);
                if (ret < 0)
                        return ret;
@@ -1428,7 +1577,6 @@ static int process_connect(struct ceph_connection *con)
                       ENTITY_NAME(con->peer_name),
                       ceph_pr_addr(&con->peer_addr.in_addr));
                reset_connection(con);
-               ceph_con_out_kvec_reset(con);
                ret = prepare_write_connect(con);
                if (ret < 0)
                        return ret;
@@ -1440,8 +1588,7 @@ static int process_connect(struct ceph_connection *con)
                if (con->ops->peer_reset)
                        con->ops->peer_reset(con);
                mutex_lock(&con->mutex);
-               if (test_bit(CLOSED, &con->state) ||
-                   test_bit(OPENING, &con->state))
+               if (con->state != CON_STATE_NEGOTIATING)
                        return -EAGAIN;
                break;
 
@@ -1454,7 +1601,6 @@ static int process_connect(struct ceph_connection *con)
                     le32_to_cpu(con->out_connect.connect_seq),
                     le32_to_cpu(con->in_reply.connect_seq));
                con->connect_seq = le32_to_cpu(con->in_reply.connect_seq);
-               ceph_con_out_kvec_reset(con);
                ret = prepare_write_connect(con);
                if (ret < 0)
                        return ret;
@@ -1471,7 +1617,6 @@ static int process_connect(struct ceph_connection *con)
                     le32_to_cpu(con->in_reply.global_seq));
                get_global_seq(con->msgr,
                               le32_to_cpu(con->in_reply.global_seq));
-               ceph_con_out_kvec_reset(con);
                ret = prepare_write_connect(con);
                if (ret < 0)
                        return ret;
@@ -1489,7 +1634,10 @@ static int process_connect(struct ceph_connection *con)
                        fail_protocol(con);
                        return -1;
                }
-               clear_bit(CONNECTING, &con->state);
+
+               BUG_ON(con->state != CON_STATE_NEGOTIATING);
+               con->state = CON_STATE_OPEN;
+
                con->peer_global_seq = le32_to_cpu(con->in_reply.global_seq);
                con->connect_seq++;
                con->peer_features = server_feat;
@@ -1501,7 +1649,9 @@ static int process_connect(struct ceph_connection *con)
                        le32_to_cpu(con->in_reply.connect_seq));
 
                if (con->in_reply.flags & CEPH_MSG_CONNECT_LOSSY)
-                       set_bit(LOSSYTX, &con->state);
+                       set_bit(CON_FLAG_LOSSYTX, &con->flags);
+
+               con->delay = 0;      /* reset backoff memory */
 
                prepare_read_tag(con);
                break;
@@ -1587,10 +1737,7 @@ static int read_partial_message_section(struct ceph_connection *con,
        return 1;
 }
 
-static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con,
-                               struct ceph_msg_header *hdr,
-                               int *skip);
-
+static int ceph_con_in_msg_alloc(struct ceph_connection *con, int *skip);
 
 static int read_partial_message_pages(struct ceph_connection *con,
                                      struct page **pages,
@@ -1633,9 +1780,6 @@ static int read_partial_message_bio(struct ceph_connection *con,
        void *p;
        int ret, left;
 
-       if (IS_ERR(bv))
-               return PTR_ERR(bv);
-
        left = min((int)(data_len - con->in_msg_pos.data_pos),
                   (int)(bv->bv_len - con->in_msg_pos.page_pos));
 
@@ -1672,7 +1816,6 @@ static int read_partial_message(struct ceph_connection *con)
        int ret;
        unsigned int front_len, middle_len, data_len;
        bool do_datacrc = !con->msgr->nocrc;
-       int skip;
        u64 seq;
        u32 crc;
 
@@ -1723,10 +1866,13 @@ static int read_partial_message(struct ceph_connection *con)
 
        /* allocate message? */
        if (!con->in_msg) {
+               int skip = 0;
+
                dout("got hdr type %d front %d data %d\n", con->in_hdr.type,
                     con->in_hdr.front_len, con->in_hdr.data_len);
-               skip = 0;
-               con->in_msg = ceph_alloc_msg(con, &con->in_hdr, &skip);
+               ret = ceph_con_in_msg_alloc(con, &skip);
+               if (ret < 0)
+                       return ret;
                if (skip) {
                        /* skip this message */
                        dout("alloc_msg said skip message\n");
@@ -1737,11 +1883,9 @@ static int read_partial_message(struct ceph_connection *con)
                        con->in_seq++;
                        return 0;
                }
-               if (!con->in_msg) {
-                       con->error_msg =
-                               "error allocating memory for incoming message";
-                       return -ENOMEM;
-               }
+
+               BUG_ON(!con->in_msg);
+               BUG_ON(con->in_msg->con != con);
                m = con->in_msg;
                m->front.iov_len = 0;    /* haven't read it yet */
                if (m->middle)
@@ -1753,6 +1897,11 @@ static int read_partial_message(struct ceph_connection *con)
                else
                        con->in_msg_pos.page_pos = 0;
                con->in_msg_pos.data_pos = 0;
+
+#ifdef CONFIG_BLOCK
+               if (m->bio)
+                       init_bio_iter(m->bio, &m->bio_iter, &m->bio_seg);
+#endif
        }
 
        /* front */
@@ -1769,10 +1918,6 @@ static int read_partial_message(struct ceph_connection *con)
                if (ret <= 0)
                        return ret;
        }
-#ifdef CONFIG_BLOCK
-       if (m->bio && !m->bio_iter)
-               init_bio_iter(m->bio, &m->bio_iter, &m->bio_seg);
-#endif
 
        /* (page) data */
        while (con->in_msg_pos.data_pos < data_len) {
@@ -1783,7 +1928,7 @@ static int read_partial_message(struct ceph_connection *con)
                                return ret;
 #ifdef CONFIG_BLOCK
                } else if (m->bio) {
-
+                       BUG_ON(!m->bio_iter);
                        ret = read_partial_message_bio(con,
                                                 &m->bio_iter, &m->bio_seg,
                                                 data_len, do_datacrc);
@@ -1837,8 +1982,11 @@ static void process_message(struct ceph_connection *con)
 {
        struct ceph_msg *msg;
 
+       BUG_ON(con->in_msg->con != con);
+       con->in_msg->con = NULL;
        msg = con->in_msg;
        con->in_msg = NULL;
+       con->ops->put(con);
 
        /* if first message, set peer_name */
        if (con->peer_name.type == 0)
@@ -1858,7 +2006,6 @@ static void process_message(struct ceph_connection *con)
        con->ops->dispatch(con, msg);
 
        mutex_lock(&con->mutex);
-       prepare_read_tag(con);
 }
 
 
@@ -1870,22 +2017,19 @@ static int try_write(struct ceph_connection *con)
 {
        int ret = 1;
 
-       dout("try_write start %p state %lu nref %d\n", con, con->state,
-            atomic_read(&con->nref));
+       dout("try_write start %p state %lu\n", con, con->state);
 
 more:
        dout("try_write out_kvec_bytes %d\n", con->out_kvec_bytes);
 
        /* open the socket first? */
-       if (con->sock == NULL) {
-               ceph_con_out_kvec_reset(con);
+       if (con->state == CON_STATE_PREOPEN) {
+               BUG_ON(con->sock);
+               con->state = CON_STATE_CONNECTING;
+
+               con_out_kvec_reset(con);
                prepare_write_banner(con);
-               ret = prepare_write_connect(con);
-               if (ret < 0)
-                       goto out;
                prepare_read_banner(con);
-               set_bit(CONNECTING, &con->state);
-               clear_bit(NEGOTIATING, &con->state);
 
                BUG_ON(con->in_msg);
                con->in_tag = CEPH_MSGR_TAG_READY;
@@ -1932,7 +2076,7 @@ more_kvec:
        }
 
 do_next:
-       if (!test_bit(CONNECTING, &con->state)) {
+       if (con->state == CON_STATE_OPEN) {
                /* is anything else pending? */
                if (!list_empty(&con->out_queue)) {
                        prepare_write_message(con);
@@ -1942,14 +2086,15 @@ do_next:
                        prepare_write_ack(con);
                        goto more;
                }
-               if (test_and_clear_bit(KEEPALIVE_PENDING, &con->state)) {
+               if (test_and_clear_bit(CON_FLAG_KEEPALIVE_PENDING,
+                                      &con->flags)) {
                        prepare_write_keepalive(con);
                        goto more;
                }
        }
 
        /* Nothing to do! */
-       clear_bit(WRITE_PENDING, &con->state);
+       clear_bit(CON_FLAG_WRITE_PENDING, &con->flags);
        dout("try_write nothing else to write.\n");
        ret = 0;
 out:
@@ -1966,38 +2111,42 @@ static int try_read(struct ceph_connection *con)
 {
        int ret = -1;
 
-       if (!con->sock)
-               return 0;
-
-       if (test_bit(STANDBY, &con->state))
+more:
+       dout("try_read start on %p state %lu\n", con, con->state);
+       if (con->state != CON_STATE_CONNECTING &&
+           con->state != CON_STATE_NEGOTIATING &&
+           con->state != CON_STATE_OPEN)
                return 0;
 
-       dout("try_read start on %p\n", con);
+       BUG_ON(!con->sock);
 
-more:
        dout("try_read tag %d in_base_pos %d\n", (int)con->in_tag,
             con->in_base_pos);
 
-       /*
-        * process_connect and process_message drop and re-take
-        * con->mutex.  make sure we handle a racing close or reopen.
-        */
-       if (test_bit(CLOSED, &con->state) ||
-           test_bit(OPENING, &con->state)) {
-               ret = -EAGAIN;
+       if (con->state == CON_STATE_CONNECTING) {
+               dout("try_read connecting\n");
+               ret = read_partial_banner(con);
+               if (ret <= 0)
+                       goto out;
+               ret = process_banner(con);
+               if (ret < 0)
+                       goto out;
+
+               BUG_ON(con->state != CON_STATE_CONNECTING);
+               con->state = CON_STATE_NEGOTIATING;
+
+               /* Banner is good, exchange connection info */
+               ret = prepare_write_connect(con);
+               if (ret < 0)
+                       goto out;
+               prepare_read_connect(con);
+
+               /* Send connection info before awaiting response */
                goto out;
        }
 
-       if (test_bit(CONNECTING, &con->state)) {
-               if (!test_bit(NEGOTIATING, &con->state)) {
-                       dout("try_read connecting\n");
-                       ret = read_partial_banner(con);
-                       if (ret <= 0)
-                               goto out;
-                       ret = process_banner(con);
-                       if (ret < 0)
-                               goto out;
-               }
+       if (con->state == CON_STATE_NEGOTIATING) {
+               dout("try_read negotiating\n");
                ret = read_partial_connect(con);
                if (ret <= 0)
                        goto out;
@@ -2007,6 +2156,8 @@ more:
                goto more;
        }
 
+       BUG_ON(con->state != CON_STATE_OPEN);
+
        if (con->in_base_pos < 0) {
                /*
                 * skipping + discarding content.
@@ -2040,7 +2191,8 @@ more:
                        prepare_read_ack(con);
                        break;
                case CEPH_MSGR_TAG_CLOSE:
-                       set_bit(CLOSED, &con->state);   /* fixme */
+                       con_close_socket(con);
+                       con->state = CON_STATE_CLOSED;
                        goto out;
                default:
                        goto bad_tag;
@@ -2063,6 +2215,8 @@ more:
                if (con->in_tag == CEPH_MSGR_TAG_READY)
                        goto more;
                process_message(con);
+               if (con->state == CON_STATE_OPEN)
+                       prepare_read_tag(con);
                goto more;
        }
        if (con->in_tag == CEPH_MSGR_TAG_ACK) {
@@ -2091,12 +2245,6 @@ bad_tag:
  */
 static void queue_con(struct ceph_connection *con)
 {
-       if (test_bit(DEAD, &con->state)) {
-               dout("queue_con %p ignoring: DEAD\n",
-                    con);
-               return;
-       }
-
        if (!con->ops->get(con)) {
                dout("queue_con %p ref count 0\n", con);
                return;
@@ -2121,7 +2269,26 @@ static void con_work(struct work_struct *work)
 
        mutex_lock(&con->mutex);
 restart:
-       if (test_and_clear_bit(BACKOFF, &con->state)) {
+       if (test_and_clear_bit(CON_FLAG_SOCK_CLOSED, &con->flags)) {
+               switch (con->state) {
+               case CON_STATE_CONNECTING:
+                       con->error_msg = "connection failed";
+                       break;
+               case CON_STATE_NEGOTIATING:
+                       con->error_msg = "negotiation failed";
+                       break;
+               case CON_STATE_OPEN:
+                       con->error_msg = "socket closed";
+                       break;
+               default:
+                       dout("unrecognized con state %d\n", (int)con->state);
+                       con->error_msg = "unrecognized con state";
+                       BUG();
+               }
+               goto fault;
+       }
+
+       if (test_and_clear_bit(CON_FLAG_BACKOFF, &con->flags)) {
                dout("con_work %p backing off\n", con);
                if (queue_delayed_work(ceph_msgr_wq, &con->work,
                                       round_jiffies_relative(con->delay))) {
@@ -2135,35 +2302,35 @@ restart:
                }
        }
 
-       if (test_bit(STANDBY, &con->state)) {
+       if (con->state == CON_STATE_STANDBY) {
                dout("con_work %p STANDBY\n", con);
                goto done;
        }
-       if (test_bit(CLOSED, &con->state)) { /* e.g. if we are replaced */
-               dout("con_work CLOSED\n");
-               con_close_socket(con);
+       if (con->state == CON_STATE_CLOSED) {
+               dout("con_work %p CLOSED\n", con);
+               BUG_ON(con->sock);
                goto done;
        }
-       if (test_and_clear_bit(OPENING, &con->state)) {
-               /* reopen w/ new peer */
+       if (con->state == CON_STATE_PREOPEN) {
                dout("con_work OPENING\n");
-               con_close_socket(con);
+               BUG_ON(con->sock);
        }
 
-       if (test_and_clear_bit(SOCK_CLOSED, &con->state))
-               goto fault;
-
        ret = try_read(con);
        if (ret == -EAGAIN)
                goto restart;
-       if (ret < 0)
+       if (ret < 0) {
+               con->error_msg = "socket error on read";
                goto fault;
+       }
 
        ret = try_write(con);
        if (ret == -EAGAIN)
                goto restart;
-       if (ret < 0)
+       if (ret < 0) {
+               con->error_msg = "socket error on write";
                goto fault;
+       }
 
 done:
        mutex_unlock(&con->mutex);
@@ -2172,7 +2339,6 @@ done_unlocked:
        return;
 
 fault:
-       mutex_unlock(&con->mutex);
        ceph_fault(con);     /* error/fault path */
        goto done_unlocked;
 }
@@ -2183,26 +2349,31 @@ fault:
  * exponential backoff
  */
 static void ceph_fault(struct ceph_connection *con)
+       __releases(con->mutex)
 {
        pr_err("%s%lld %s %s\n", ENTITY_NAME(con->peer_name),
               ceph_pr_addr(&con->peer_addr.in_addr), con->error_msg);
        dout("fault %p state %lu to peer %s\n",
             con, con->state, ceph_pr_addr(&con->peer_addr.in_addr));
 
-       if (test_bit(LOSSYTX, &con->state)) {
-               dout("fault on LOSSYTX channel\n");
-               goto out;
-       }
-
-       mutex_lock(&con->mutex);
-       if (test_bit(CLOSED, &con->state))
-               goto out_unlock;
+       BUG_ON(con->state != CON_STATE_CONNECTING &&
+              con->state != CON_STATE_NEGOTIATING &&
+              con->state != CON_STATE_OPEN);
 
        con_close_socket(con);
 
+       if (test_bit(CON_FLAG_LOSSYTX, &con->flags)) {
+               dout("fault on LOSSYTX channel, marking CLOSED\n");
+               con->state = CON_STATE_CLOSED;
+               goto out_unlock;
+       }
+
        if (con->in_msg) {
+               BUG_ON(con->in_msg->con != con);
+               con->in_msg->con = NULL;
                ceph_msg_put(con->in_msg);
                con->in_msg = NULL;
+               con->ops->put(con);
        }
 
        /* Requeue anything that hasn't been acked */
@@ -2211,12 +2382,13 @@ static void ceph_fault(struct ceph_connection *con)
        /* If there are no messages queued or keepalive pending, place
         * the connection in a STANDBY state */
        if (list_empty(&con->out_queue) &&
-           !test_bit(KEEPALIVE_PENDING, &con->state)) {
+           !test_bit(CON_FLAG_KEEPALIVE_PENDING, &con->flags)) {
                dout("fault %p setting STANDBY clearing WRITE_PENDING\n", con);
-               clear_bit(WRITE_PENDING, &con->state);
-               set_bit(STANDBY, &con->state);
+               clear_bit(CON_FLAG_WRITE_PENDING, &con->flags);
+               con->state = CON_STATE_STANDBY;
        } else {
                /* retry after a delay. */
+               con->state = CON_STATE_PREOPEN;
                if (con->delay == 0)
                        con->delay = BASE_DELAY_INTERVAL;
                else if (con->delay < MAX_DELAY_INTERVAL)
@@ -2237,13 +2409,12 @@ static void ceph_fault(struct ceph_connection *con)
                         * that when con_work restarts we schedule the
                         * delay then.
                         */
-                       set_bit(BACKOFF, &con->state);
+                       set_bit(CON_FLAG_BACKOFF, &con->flags);
                }
        }
 
 out_unlock:
        mutex_unlock(&con->mutex);
-out:
        /*
         * in case we faulted due to authentication, invalidate our
         * current tickets so that we can get new ones.
@@ -2260,18 +2431,14 @@ out:
 
 
 /*
- * create a new messenger instance
+ * initialize a new messenger instance
  */
-struct ceph_messenger *ceph_messenger_create(struct ceph_entity_addr *myaddr,
-                                            u32 supported_features,
-                                            u32 required_features)
+void ceph_messenger_init(struct ceph_messenger *msgr,
+                       struct ceph_entity_addr *myaddr,
+                       u32 supported_features,
+                       u32 required_features,
+                       bool nocrc)
 {
-       struct ceph_messenger *msgr;
-
-       msgr = kzalloc(sizeof(*msgr), GFP_KERNEL);
-       if (msgr == NULL)
-               return ERR_PTR(-ENOMEM);
-
        msgr->supported_features = supported_features;
        msgr->required_features = required_features;
 
@@ -2284,30 +2451,23 @@ struct ceph_messenger *ceph_messenger_create(struct ceph_entity_addr *myaddr,
        msgr->inst.addr.type = 0;
        get_random_bytes(&msgr->inst.addr.nonce, sizeof(msgr->inst.addr.nonce));
        encode_my_addr(msgr);
+       msgr->nocrc = nocrc;
 
-       dout("messenger_create %p\n", msgr);
-       return msgr;
-}
-EXPORT_SYMBOL(ceph_messenger_create);
+       atomic_set(&msgr->stopping, 0);
 
-void ceph_messenger_destroy(struct ceph_messenger *msgr)
-{
-       dout("destroy %p\n", msgr);
-       kfree(msgr);
-       dout("destroyed messenger %p\n", msgr);
+       dout("%s %p\n", __func__, msgr);
 }
-EXPORT_SYMBOL(ceph_messenger_destroy);
+EXPORT_SYMBOL(ceph_messenger_init);
 
 static void clear_standby(struct ceph_connection *con)
 {
        /* come back from STANDBY? */
-       if (test_and_clear_bit(STANDBY, &con->state)) {
-               mutex_lock(&con->mutex);
+       if (con->state == CON_STATE_STANDBY) {
                dout("clear_standby %p and ++connect_seq\n", con);
+               con->state = CON_STATE_PREOPEN;
                con->connect_seq++;
-               WARN_ON(test_bit(WRITE_PENDING, &con->state));
-               WARN_ON(test_bit(KEEPALIVE_PENDING, &con->state));
-               mutex_unlock(&con->mutex);
+               WARN_ON(test_bit(CON_FLAG_WRITE_PENDING, &con->flags));
+               WARN_ON(test_bit(CON_FLAG_KEEPALIVE_PENDING, &con->flags));
        }
 }
 
@@ -2316,21 +2476,24 @@ static void clear_standby(struct ceph_connection *con)
  */
 void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg)
 {
-       if (test_bit(CLOSED, &con->state)) {
-               dout("con_send %p closed, dropping %p\n", con, msg);
-               ceph_msg_put(msg);
-               return;
-       }
-
        /* set src+dst */
        msg->hdr.src = con->msgr->inst.name;
-
        BUG_ON(msg->front.iov_len != le32_to_cpu(msg->hdr.front_len));
-
        msg->needs_out_seq = true;
 
-       /* queue */
        mutex_lock(&con->mutex);
+
+       if (con->state == CON_STATE_CLOSED) {
+               dout("con_send %p closed, dropping %p\n", con, msg);
+               ceph_msg_put(msg);
+               mutex_unlock(&con->mutex);
+               return;
+       }
+
+       BUG_ON(msg->con != NULL);
+       msg->con = con->ops->get(con);
+       BUG_ON(msg->con == NULL);
+
        BUG_ON(!list_empty(&msg->list_head));
        list_add_tail(&msg->list_head, &con->out_queue);
        dout("----- %p to %s%lld %d=%s len %d+%d+%d -----\n", msg,
@@ -2339,12 +2502,13 @@ void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg)
             le32_to_cpu(msg->hdr.front_len),
             le32_to_cpu(msg->hdr.middle_len),
             le32_to_cpu(msg->hdr.data_len));
+
+       clear_standby(con);
        mutex_unlock(&con->mutex);
 
        /* if there wasn't anything waiting to send before, queue
         * new work */
-       clear_standby(con);
-       if (test_and_set_bit(WRITE_PENDING, &con->state) == 0)
+       if (test_and_set_bit(CON_FLAG_WRITE_PENDING, &con->flags) == 0)
                queue_con(con);
 }
 EXPORT_SYMBOL(ceph_con_send);
@@ -2352,24 +2516,34 @@ EXPORT_SYMBOL(ceph_con_send);
 /*
  * Revoke a message that was previously queued for send
  */
-void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg)
+void ceph_msg_revoke(struct ceph_msg *msg)
 {
+       struct ceph_connection *con = msg->con;
+
+       if (!con)
+               return;         /* Message not in our possession */
+
        mutex_lock(&con->mutex);
        if (!list_empty(&msg->list_head)) {
-               dout("con_revoke %p msg %p - was on queue\n", con, msg);
+               dout("%s %p msg %p - was on queue\n", __func__, con, msg);
                list_del_init(&msg->list_head);
-               ceph_msg_put(msg);
+               BUG_ON(msg->con == NULL);
+               msg->con->ops->put(msg->con);
+               msg->con = NULL;
                msg->hdr.seq = 0;
+
+               ceph_msg_put(msg);
        }
        if (con->out_msg == msg) {
-               dout("con_revoke %p msg %p - was sending\n", con, msg);
+               dout("%s %p msg %p - was sending\n", __func__, con, msg);
                con->out_msg = NULL;
                if (con->out_kvec_is_msg) {
                        con->out_skip = con->out_kvec_bytes;
                        con->out_kvec_is_msg = false;
                }
-               ceph_msg_put(msg);
                msg->hdr.seq = 0;
+
+               ceph_msg_put(msg);
        }
        mutex_unlock(&con->mutex);
 }
@@ -2377,17 +2551,27 @@ void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg)
 /*
  * Revoke a message that we may be reading data into
  */
-void ceph_con_revoke_message(struct ceph_connection *con, struct ceph_msg *msg)
+void ceph_msg_revoke_incoming(struct ceph_msg *msg)
 {
+       struct ceph_connection *con;
+
+       BUG_ON(msg == NULL);
+       if (!msg->con) {
+               dout("%s msg %p null con\n", __func__, msg);
+
+               return;         /* Message not in our possession */
+       }
+
+       con = msg->con;
        mutex_lock(&con->mutex);
-       if (con->in_msg && con->in_msg == msg) {
+       if (con->in_msg == msg) {
                unsigned int front_len = le32_to_cpu(con->in_hdr.front_len);
                unsigned int middle_len = le32_to_cpu(con->in_hdr.middle_len);
                unsigned int data_len = le32_to_cpu(con->in_hdr.data_len);
 
                /* skip rest of message */
-               dout("con_revoke_pages %p msg %p revoked\n", con, msg);
-                       con->in_base_pos = con->in_base_pos -
+               dout("%s %p msg %p revoked\n", __func__, con, msg);
+               con->in_base_pos = con->in_base_pos -
                                sizeof(struct ceph_msg_header) -
                                front_len -
                                middle_len -
@@ -2398,8 +2582,8 @@ void ceph_con_revoke_message(struct ceph_connection *con, struct ceph_msg *msg)
                con->in_tag = CEPH_MSGR_TAG_READY;
                con->in_seq++;
        } else {
-               dout("con_revoke_pages %p msg %p pages %p no-op\n",
-                    con, con->in_msg, msg);
+               dout("%s %p in_msg %p msg %p no-op\n",
+                    __func__, con, con->in_msg, msg);
        }
        mutex_unlock(&con->mutex);
 }
@@ -2410,9 +2594,11 @@ void ceph_con_revoke_message(struct ceph_connection *con, struct ceph_msg *msg)
 void ceph_con_keepalive(struct ceph_connection *con)
 {
        dout("con_keepalive %p\n", con);
+       mutex_lock(&con->mutex);
        clear_standby(con);
-       if (test_and_set_bit(KEEPALIVE_PENDING, &con->state) == 0 &&
-           test_and_set_bit(WRITE_PENDING, &con->state) == 0)
+       mutex_unlock(&con->mutex);
+       if (test_and_set_bit(CON_FLAG_KEEPALIVE_PENDING, &con->flags) == 0 &&
+           test_and_set_bit(CON_FLAG_WRITE_PENDING, &con->flags) == 0)
                queue_con(con);
 }
 EXPORT_SYMBOL(ceph_con_keepalive);
@@ -2431,6 +2617,8 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags,
        if (m == NULL)
                goto out;
        kref_init(&m->kref);
+
+       m->con = NULL;
        INIT_LIST_HEAD(&m->list_head);
 
        m->hdr.tid = 0;
@@ -2526,46 +2714,77 @@ static int ceph_alloc_middle(struct ceph_connection *con, struct ceph_msg *msg)
 }
 
 /*
- * Generic message allocator, for incoming messages.
+ * Allocate a message for receiving an incoming message on a
+ * connection, and save the result in con->in_msg.  Uses the
+ * connection's private alloc_msg op if available.
+ *
+ * Returns 0 on success, or a negative error code.
+ *
+ * On success, if we set *skip = 1:
+ *  - the next message should be skipped and ignored.
+ *  - con->in_msg == NULL
+ * or if we set *skip = 0:
+ *  - con->in_msg is non-null.
+ * On error (ENOMEM, EAGAIN, ...),
+ *  - con->in_msg == NULL
  */
-static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con,
-                               struct ceph_msg_header *hdr,
-                               int *skip)
+static int ceph_con_in_msg_alloc(struct ceph_connection *con, int *skip)
 {
+       struct ceph_msg_header *hdr = &con->in_hdr;
        int type = le16_to_cpu(hdr->type);
        int front_len = le32_to_cpu(hdr->front_len);
        int middle_len = le32_to_cpu(hdr->middle_len);
-       struct ceph_msg *msg = NULL;
-       int ret;
+       int ret = 0;
+
+       BUG_ON(con->in_msg != NULL);
 
        if (con->ops->alloc_msg) {
+               struct ceph_msg *msg;
+
                mutex_unlock(&con->mutex);
                msg = con->ops->alloc_msg(con, hdr, skip);
                mutex_lock(&con->mutex);
-               if (!msg || *skip)
-                       return NULL;
+               if (con->state != CON_STATE_OPEN) {
+                       ceph_msg_put(msg);
+                       return -EAGAIN;
+               }
+               con->in_msg = msg;
+               if (con->in_msg) {
+                       con->in_msg->con = con->ops->get(con);
+                       BUG_ON(con->in_msg->con == NULL);
+               }
+               if (*skip) {
+                       con->in_msg = NULL;
+                       return 0;
+               }
+               if (!con->in_msg) {
+                       con->error_msg =
+                               "error allocating memory for incoming message";
+                       return -ENOMEM;
+               }
        }
-       if (!msg) {
-               *skip = 0;
-               msg = ceph_msg_new(type, front_len, GFP_NOFS, false);
-               if (!msg) {
+       if (!con->in_msg) {
+               con->in_msg = ceph_msg_new(type, front_len, GFP_NOFS, false);
+               if (!con->in_msg) {
                        pr_err("unable to allocate msg type %d len %d\n",
                               type, front_len);
-                       return NULL;
+                       return -ENOMEM;
                }
-               msg->page_alignment = le16_to_cpu(hdr->data_off);
+               con->in_msg->con = con->ops->get(con);
+               BUG_ON(con->in_msg->con == NULL);
+               con->in_msg->page_alignment = le16_to_cpu(hdr->data_off);
        }
-       memcpy(&msg->hdr, &con->in_hdr, sizeof(con->in_hdr));
+       memcpy(&con->in_msg->hdr, &con->in_hdr, sizeof(con->in_hdr));
 
-       if (middle_len && !msg->middle) {
-               ret = ceph_alloc_middle(con, msg);
+       if (middle_len && !con->in_msg->middle) {
+               ret = ceph_alloc_middle(con, con->in_msg);
                if (ret < 0) {
-                       ceph_msg_put(msg);
-                       return NULL;
+                       ceph_msg_put(con->in_msg);
+                       con->in_msg = NULL;
                }
        }
 
-       return msg;
+       return ret;
 }
 
 
index d0649a9655be3b7bbf1baaa03220548c74156ff0..105d533b55f3bcc3e8a2e7a82b80a6459cbb9363 100644 (file)
@@ -106,9 +106,9 @@ static void __send_prepared_auth_request(struct ceph_mon_client *monc, int len)
        monc->pending_auth = 1;
        monc->m_auth->front.iov_len = len;
        monc->m_auth->hdr.front_len = cpu_to_le32(len);
-       ceph_con_revoke(monc->con, monc->m_auth);
+       ceph_msg_revoke(monc->m_auth);
        ceph_msg_get(monc->m_auth);  /* keep our ref */
-       ceph_con_send(monc->con, monc->m_auth);
+       ceph_con_send(&monc->con, monc->m_auth);
 }
 
 /*
@@ -117,8 +117,11 @@ static void __send_prepared_auth_request(struct ceph_mon_client *monc, int len)
 static void __close_session(struct ceph_mon_client *monc)
 {
        dout("__close_session closing mon%d\n", monc->cur_mon);
-       ceph_con_revoke(monc->con, monc->m_auth);
-       ceph_con_close(monc->con);
+       ceph_msg_revoke(monc->m_auth);
+       ceph_msg_revoke_incoming(monc->m_auth_reply);
+       ceph_msg_revoke(monc->m_subscribe);
+       ceph_msg_revoke_incoming(monc->m_subscribe_ack);
+       ceph_con_close(&monc->con);
        monc->cur_mon = -1;
        monc->pending_auth = 0;
        ceph_auth_reset(monc->auth);
@@ -142,9 +145,8 @@ static int __open_session(struct ceph_mon_client *monc)
                monc->want_next_osdmap = !!monc->want_next_osdmap;
 
                dout("open_session mon%d opening\n", monc->cur_mon);
-               monc->con->peer_name.type = CEPH_ENTITY_TYPE_MON;
-               monc->con->peer_name.num = cpu_to_le64(monc->cur_mon);
-               ceph_con_open(monc->con,
+               ceph_con_open(&monc->con,
+                             CEPH_ENTITY_TYPE_MON, monc->cur_mon,
                              &monc->monmap->mon_inst[monc->cur_mon].addr);
 
                /* initiatiate authentication handshake */
@@ -226,8 +228,8 @@ static void __send_subscribe(struct ceph_mon_client *monc)
 
                msg->front.iov_len = p - msg->front.iov_base;
                msg->hdr.front_len = cpu_to_le32(msg->front.iov_len);
-               ceph_con_revoke(monc->con, msg);
-               ceph_con_send(monc->con, ceph_msg_get(msg));
+               ceph_msg_revoke(msg);
+               ceph_con_send(&monc->con, ceph_msg_get(msg));
 
                monc->sub_sent = jiffies | 1;  /* never 0 */
        }
@@ -247,7 +249,7 @@ static void handle_subscribe_ack(struct ceph_mon_client *monc,
        if (monc->hunting) {
                pr_info("mon%d %s session established\n",
                        monc->cur_mon,
-                       ceph_pr_addr(&monc->con->peer_addr.in_addr));
+                       ceph_pr_addr(&monc->con.peer_addr.in_addr));
                monc->hunting = false;
        }
        dout("handle_subscribe_ack after %d seconds\n", seconds);
@@ -439,6 +441,7 @@ static struct ceph_msg *get_generic_reply(struct ceph_connection *con,
                m = NULL;
        } else {
                dout("get_generic_reply %lld got %p\n", tid, req->reply);
+               *skip = 0;
                m = ceph_msg_get(req->reply);
                /*
                 * we don't need to track the connection reading into
@@ -461,7 +464,7 @@ static int do_generic_request(struct ceph_mon_client *monc,
        req->request->hdr.tid = cpu_to_le64(req->tid);
        __insert_generic_request(monc, req);
        monc->num_generic_requests++;
-       ceph_con_send(monc->con, ceph_msg_get(req->request));
+       ceph_con_send(&monc->con, ceph_msg_get(req->request));
        mutex_unlock(&monc->mutex);
 
        err = wait_for_completion_interruptible(&req->completion);
@@ -684,8 +687,9 @@ static void __resend_generic_request(struct ceph_mon_client *monc)
 
        for (p = rb_first(&monc->generic_request_tree); p; p = rb_next(p)) {
                req = rb_entry(p, struct ceph_mon_generic_request, node);
-               ceph_con_revoke(monc->con, req->request);
-               ceph_con_send(monc->con, ceph_msg_get(req->request));
+               ceph_msg_revoke(req->request);
+               ceph_msg_revoke_incoming(req->reply);
+               ceph_con_send(&monc->con, ceph_msg_get(req->request));
        }
 }
 
@@ -705,7 +709,7 @@ static void delayed_work(struct work_struct *work)
                __close_session(monc);
                __open_session(monc);  /* continue hunting */
        } else {
-               ceph_con_keepalive(monc->con);
+               ceph_con_keepalive(&monc->con);
 
                __validate_auth(monc);
 
@@ -760,19 +764,12 @@ int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl)
                goto out;
 
        /* connection */
-       monc->con = kmalloc(sizeof(*monc->con), GFP_KERNEL);
-       if (!monc->con)
-               goto out_monmap;
-       ceph_con_init(monc->client->msgr, monc->con);
-       monc->con->private = monc;
-       monc->con->ops = &mon_con_ops;
-
        /* authentication */
        monc->auth = ceph_auth_init(cl->options->name,
                                    cl->options->key);
        if (IS_ERR(monc->auth)) {
                err = PTR_ERR(monc->auth);
-               goto out_con;
+               goto out_monmap;
        }
        monc->auth->want_keys =
                CEPH_ENTITY_TYPE_AUTH | CEPH_ENTITY_TYPE_MON |
@@ -801,6 +798,9 @@ int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl)
        if (!monc->m_auth)
                goto out_auth_reply;
 
+       ceph_con_init(&monc->con, monc, &mon_con_ops,
+                     &monc->client->msgr);
+
        monc->cur_mon = -1;
        monc->hunting = true;
        monc->sub_renew_after = jiffies;
@@ -824,8 +824,6 @@ out_subscribe_ack:
        ceph_msg_put(monc->m_subscribe_ack);
 out_auth:
        ceph_auth_destroy(monc->auth);
-out_con:
-       monc->con->ops->put(monc->con);
 out_monmap:
        kfree(monc->monmap);
 out:
@@ -841,10 +839,6 @@ void ceph_monc_stop(struct ceph_mon_client *monc)
        mutex_lock(&monc->mutex);
        __close_session(monc);
 
-       monc->con->private = NULL;
-       monc->con->ops->put(monc->con);
-       monc->con = NULL;
-
        mutex_unlock(&monc->mutex);
 
        /*
@@ -888,8 +882,8 @@ static void handle_auth_reply(struct ceph_mon_client *monc,
        } else if (!was_auth && monc->auth->ops->is_authenticated(monc->auth)) {
                dout("authenticated, starting session\n");
 
-               monc->client->msgr->inst.name.type = CEPH_ENTITY_TYPE_CLIENT;
-               monc->client->msgr->inst.name.num =
+               monc->client->msgr.inst.name.type = CEPH_ENTITY_TYPE_CLIENT;
+               monc->client->msgr.inst.name.num =
                                        cpu_to_le64(monc->auth->global_id);
 
                __send_subscribe(monc);
@@ -1000,6 +994,8 @@ static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con,
        case CEPH_MSG_MDS_MAP:
        case CEPH_MSG_OSD_MAP:
                m = ceph_msg_new(type, front_len, GFP_NOFS, false);
+               if (!m)
+                       return NULL;    /* ENOMEM--return skip == 0 */
                break;
        }
 
@@ -1029,7 +1025,7 @@ static void mon_fault(struct ceph_connection *con)
        if (!monc->hunting)
                pr_info("mon%d %s session lost, "
                        "hunting for new mon\n", monc->cur_mon,
-                       ceph_pr_addr(&monc->con->peer_addr.in_addr));
+                       ceph_pr_addr(&monc->con.peer_addr.in_addr));
 
        __close_session(monc);
        if (!monc->hunting) {
@@ -1044,9 +1040,23 @@ out:
        mutex_unlock(&monc->mutex);
 }
 
+/*
+ * We can ignore refcounting on the connection struct, as all references
+ * will come from the messenger workqueue, which is drained prior to
+ * mon_client destruction.
+ */
+static struct ceph_connection *con_get(struct ceph_connection *con)
+{
+       return con;
+}
+
+static void con_put(struct ceph_connection *con)
+{
+}
+
 static const struct ceph_connection_operations mon_con_ops = {
-       .get = ceph_con_get,
-       .put = ceph_con_put,
+       .get = con_get,
+       .put = con_put,
        .dispatch = dispatch,
        .fault = mon_fault,
        .alloc_msg = mon_alloc_msg,
index 11d5f4196a73cfeef3492389c76f9f285c50188d..ddec1c10ac80fc3b993cdc7ba5524e317382bd04 100644 (file)
@@ -12,7 +12,7 @@ static void *msgpool_alloc(gfp_t gfp_mask, void *arg)
        struct ceph_msgpool *pool = arg;
        struct ceph_msg *msg;
 
-       msg = ceph_msg_new(0, pool->front_len, gfp_mask, true);
+       msg = ceph_msg_new(pool->type, pool->front_len, gfp_mask, true);
        if (!msg) {
                dout("msgpool_alloc %s failed\n", pool->name);
        } else {
@@ -32,10 +32,11 @@ static void msgpool_free(void *element, void *arg)
        ceph_msg_put(msg);
 }
 
-int ceph_msgpool_init(struct ceph_msgpool *pool,
+int ceph_msgpool_init(struct ceph_msgpool *pool, int type,
                      int front_len, int size, bool blocking, const char *name)
 {
        dout("msgpool %s init\n", name);
+       pool->type = type;
        pool->front_len = front_len;
        pool->pool = mempool_create(size, msgpool_alloc, msgpool_free, pool);
        if (!pool->pool)
@@ -61,7 +62,7 @@ struct ceph_msg *ceph_msgpool_get(struct ceph_msgpool *pool,
                WARN_ON(1);
 
                /* try to alloc a fresh message */
-               return ceph_msg_new(0, front_len, GFP_NOFS, false);
+               return ceph_msg_new(pool->type, front_len, GFP_NOFS, false);
        }
 
        msg = mempool_alloc(pool->pool, GFP_NOFS);
index ca59e66c9787303805519f2bf325cc5d5817ff55..42119c05e82c023b777298e9f41b6a6ebbade0cb 100644 (file)
@@ -140,10 +140,9 @@ void ceph_osdc_release_request(struct kref *kref)
        if (req->r_request)
                ceph_msg_put(req->r_request);
        if (req->r_con_filling_msg) {
-               dout("release_request revoking pages %p from con %p\n",
+               dout("%s revoking pages %p from con %p\n", __func__,
                     req->r_pages, req->r_con_filling_msg);
-               ceph_con_revoke_message(req->r_con_filling_msg,
-                                     req->r_reply);
+               ceph_msg_revoke_incoming(req->r_reply);
                req->r_con_filling_msg->ops->put(req->r_con_filling_msg);
        }
        if (req->r_reply)
@@ -214,10 +213,13 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc,
        kref_init(&req->r_kref);
        init_completion(&req->r_completion);
        init_completion(&req->r_safe_completion);
+       rb_init_node(&req->r_node);
        INIT_LIST_HEAD(&req->r_unsafe_item);
        INIT_LIST_HEAD(&req->r_linger_item);
        INIT_LIST_HEAD(&req->r_linger_osd);
        INIT_LIST_HEAD(&req->r_req_lru_item);
+       INIT_LIST_HEAD(&req->r_osd_item);
+
        req->r_flags = flags;
 
        WARN_ON((flags & (CEPH_OSD_FLAG_READ|CEPH_OSD_FLAG_WRITE)) == 0);
@@ -243,6 +245,7 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc,
                }
                ceph_pagelist_init(req->r_trail);
        }
+
        /* create request message; allow space for oid */
        msg_size += MAX_OBJ_NAME_SIZE;
        if (snapc)
@@ -256,7 +259,6 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc,
                return NULL;
        }
 
-       msg->hdr.type = cpu_to_le16(CEPH_MSG_OSD_OP);
        memset(msg->front.iov_base, 0, msg->front.iov_len);
 
        req->r_request = msg;
@@ -624,7 +626,7 @@ static void osd_reset(struct ceph_connection *con)
 /*
  * Track open sessions with osds.
  */
-static struct ceph_osd *create_osd(struct ceph_osd_client *osdc)
+static struct ceph_osd *create_osd(struct ceph_osd_client *osdc, int onum)
 {
        struct ceph_osd *osd;
 
@@ -634,15 +636,13 @@ static struct ceph_osd *create_osd(struct ceph_osd_client *osdc)
 
        atomic_set(&osd->o_ref, 1);
        osd->o_osdc = osdc;
+       osd->o_osd = onum;
        INIT_LIST_HEAD(&osd->o_requests);
        INIT_LIST_HEAD(&osd->o_linger_requests);
        INIT_LIST_HEAD(&osd->o_osd_lru);
        osd->o_incarnation = 1;
 
-       ceph_con_init(osdc->client->msgr, &osd->o_con);
-       osd->o_con.private = osd;
-       osd->o_con.ops = &osd_con_ops;
-       osd->o_con.peer_name.type = CEPH_ENTITY_TYPE_OSD;
+       ceph_con_init(&osd->o_con, osd, &osd_con_ops, &osdc->client->msgr);
 
        INIT_LIST_HEAD(&osd->o_keepalive_item);
        return osd;
@@ -688,7 +688,7 @@ static void __remove_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd)
 
 static void remove_all_osds(struct ceph_osd_client *osdc)
 {
-       dout("__remove_old_osds %p\n", osdc);
+       dout("%s %p\n", __func__, osdc);
        mutex_lock(&osdc->request_mutex);
        while (!RB_EMPTY_ROOT(&osdc->osds)) {
                struct ceph_osd *osd = rb_entry(rb_first(&osdc->osds),
@@ -752,7 +752,8 @@ static int __reset_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd)
                ret = -EAGAIN;
        } else {
                ceph_con_close(&osd->o_con);
-               ceph_con_open(&osd->o_con, &osdc->osdmap->osd_addr[osd->o_osd]);
+               ceph_con_open(&osd->o_con, CEPH_ENTITY_TYPE_OSD, osd->o_osd,
+                             &osdc->osdmap->osd_addr[osd->o_osd]);
                osd->o_incarnation++;
        }
        return ret;
@@ -853,7 +854,7 @@ static void __unregister_request(struct ceph_osd_client *osdc,
 
        if (req->r_osd) {
                /* make sure the original request isn't in flight. */
-               ceph_con_revoke(&req->r_osd->o_con, req->r_request);
+               ceph_msg_revoke(req->r_request);
 
                list_del_init(&req->r_osd_item);
                if (list_empty(&req->r_osd->o_requests) &&
@@ -880,7 +881,7 @@ static void __unregister_request(struct ceph_osd_client *osdc,
 static void __cancel_request(struct ceph_osd_request *req)
 {
        if (req->r_sent && req->r_osd) {
-               ceph_con_revoke(&req->r_osd->o_con, req->r_request);
+               ceph_msg_revoke(req->r_request);
                req->r_sent = 0;
        }
 }
@@ -890,7 +891,9 @@ static void __register_linger_request(struct ceph_osd_client *osdc,
 {
        dout("__register_linger_request %p\n", req);
        list_add_tail(&req->r_linger_item, &osdc->req_linger);
-       list_add_tail(&req->r_linger_osd, &req->r_osd->o_linger_requests);
+       if (req->r_osd)
+               list_add_tail(&req->r_linger_osd,
+                             &req->r_osd->o_linger_requests);
 }
 
 static void __unregister_linger_request(struct ceph_osd_client *osdc,
@@ -998,18 +1001,18 @@ static int __map_request(struct ceph_osd_client *osdc,
        req->r_osd = __lookup_osd(osdc, o);
        if (!req->r_osd && o >= 0) {
                err = -ENOMEM;
-               req->r_osd = create_osd(osdc);
+               req->r_osd = create_osd(osdc, o);
                if (!req->r_osd) {
                        list_move(&req->r_req_lru_item, &osdc->req_notarget);
                        goto out;
                }
 
                dout("map_request osd %p is osd%d\n", req->r_osd, o);
-               req->r_osd->o_osd = o;
-               req->r_osd->o_con.peer_name.num = cpu_to_le64(o);
                __insert_osd(osdc, req->r_osd);
 
-               ceph_con_open(&req->r_osd->o_con, &osdc->osdmap->osd_addr[o]);
+               ceph_con_open(&req->r_osd->o_con,
+                             CEPH_ENTITY_TYPE_OSD, o,
+                             &osdc->osdmap->osd_addr[o]);
        }
 
        if (req->r_osd) {
@@ -1304,8 +1307,9 @@ static void kick_requests(struct ceph_osd_client *osdc, int force_resend)
 
        dout("kick_requests %s\n", force_resend ? " (force resend)" : "");
        mutex_lock(&osdc->request_mutex);
-       for (p = rb_first(&osdc->requests); p; p = rb_next(p)) {
+       for (p = rb_first(&osdc->requests); p; ) {
                req = rb_entry(p, struct ceph_osd_request, r_node);
+               p = rb_next(p);
                err = __map_request(osdc, req, force_resend);
                if (err < 0)
                        continue;  /* error */
@@ -1313,10 +1317,23 @@ static void kick_requests(struct ceph_osd_client *osdc, int force_resend)
                        dout("%p tid %llu maps to no osd\n", req, req->r_tid);
                        needmap++;  /* request a newer map */
                } else if (err > 0) {
-                       dout("%p tid %llu requeued on osd%d\n", req, req->r_tid,
-                            req->r_osd ? req->r_osd->o_osd : -1);
-                       if (!req->r_linger)
+                       if (!req->r_linger) {
+                               dout("%p tid %llu requeued on osd%d\n", req,
+                                    req->r_tid,
+                                    req->r_osd ? req->r_osd->o_osd : -1);
                                req->r_flags |= CEPH_OSD_FLAG_RETRY;
+                       }
+               }
+               if (req->r_linger && list_empty(&req->r_linger_item)) {
+                       /*
+                        * register as a linger so that we will
+                        * re-submit below and get a new tid
+                        */
+                       dout("%p tid %llu restart on osd%d\n",
+                            req, req->r_tid,
+                            req->r_osd ? req->r_osd->o_osd : -1);
+                       __register_linger_request(osdc, req);
+                       __unregister_request(osdc, req);
                }
        }
 
@@ -1391,7 +1408,7 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg)
                             epoch, maplen);
                        newmap = osdmap_apply_incremental(&p, next,
                                                          osdc->osdmap,
-                                                         osdc->client->msgr);
+                                                         &osdc->client->msgr);
                        if (IS_ERR(newmap)) {
                                err = PTR_ERR(newmap);
                                goto bad;
@@ -1839,11 +1856,12 @@ int ceph_osdc_init(struct ceph_osd_client *osdc, struct ceph_client *client)
        if (!osdc->req_mempool)
                goto out;
 
-       err = ceph_msgpool_init(&osdc->msgpool_op, OSD_OP_FRONT_LEN, 10, true,
+       err = ceph_msgpool_init(&osdc->msgpool_op, CEPH_MSG_OSD_OP,
+                               OSD_OP_FRONT_LEN, 10, true,
                                "osd_op");
        if (err < 0)
                goto out_mempool;
-       err = ceph_msgpool_init(&osdc->msgpool_op_reply,
+       err = ceph_msgpool_init(&osdc->msgpool_op_reply, CEPH_MSG_OSD_OPREPLY,
                                OSD_OPREPLY_FRONT_LEN, 10, true,
                                "osd_op_reply");
        if (err < 0)
@@ -2019,15 +2037,15 @@ static struct ceph_msg *get_reply(struct ceph_connection *con,
        if (!req) {
                *skip = 1;
                m = NULL;
-               pr_info("get_reply unknown tid %llu from osd%d\n", tid,
-                       osd->o_osd);
+               dout("get_reply unknown tid %llu from osd%d\n", tid,
+                    osd->o_osd);
                goto out;
        }
 
        if (req->r_con_filling_msg) {
-               dout("get_reply revoking msg %p from old con %p\n",
+               dout("%s revoking msg %p from old con %p\n", __func__,
                     req->r_reply, req->r_con_filling_msg);
-               ceph_con_revoke_message(req->r_con_filling_msg, req->r_reply);
+               ceph_msg_revoke_incoming(req->r_reply);
                req->r_con_filling_msg->ops->put(req->r_con_filling_msg);
                req->r_con_filling_msg = NULL;
        }
@@ -2080,6 +2098,7 @@ static struct ceph_msg *alloc_msg(struct ceph_connection *con,
        int type = le16_to_cpu(hdr->type);
        int front = le32_to_cpu(hdr->front_len);
 
+       *skip = 0;
        switch (type) {
        case CEPH_MSG_OSD_MAP:
        case CEPH_MSG_WATCH_NOTIFY:
index 81e3b84a77efdecb6c44603e7784a083fe94b980..3124b71a888362910f96efec9bca0104f84dba5d 100644 (file)
@@ -135,6 +135,21 @@ bad:
        return -EINVAL;
 }
 
+static int skip_name_map(void **p, void *end)
+{
+        int len;
+        ceph_decode_32_safe(p, end, len ,bad);
+        while (len--) {
+                int strlen;
+                *p += sizeof(u32);
+                ceph_decode_32_safe(p, end, strlen, bad);
+                *p += strlen;
+}
+        return 0;
+bad:
+        return -EINVAL;
+}
+
 static struct crush_map *crush_decode(void *pbyval, void *end)
 {
        struct crush_map *c;
@@ -143,6 +158,7 @@ static struct crush_map *crush_decode(void *pbyval, void *end)
        void **p = &pbyval;
        void *start = pbyval;
        u32 magic;
+       u32 num_name_maps;
 
        dout("crush_decode %p to %p len %d\n", *p, end, (int)(end - *p));
 
@@ -150,6 +166,11 @@ static struct crush_map *crush_decode(void *pbyval, void *end)
        if (c == NULL)
                return ERR_PTR(-ENOMEM);
 
+        /* set tunables to default values */
+        c->choose_local_tries = 2;
+        c->choose_local_fallback_tries = 5;
+        c->choose_total_tries = 19;
+
        ceph_decode_need(p, end, 4*sizeof(u32), bad);
        magic = ceph_decode_32(p);
        if (magic != CRUSH_MAGIC) {
@@ -297,7 +318,25 @@ static struct crush_map *crush_decode(void *pbyval, void *end)
        }
 
        /* ignore trailing name maps. */
+        for (num_name_maps = 0; num_name_maps < 3; num_name_maps++) {
+                err = skip_name_map(p, end);
+                if (err < 0)
+                        goto done;
+        }
+
+        /* tunables */
+        ceph_decode_need(p, end, 3*sizeof(u32), done);
+        c->choose_local_tries = ceph_decode_32(p);
+        c->choose_local_fallback_tries =  ceph_decode_32(p);
+        c->choose_total_tries = ceph_decode_32(p);
+        dout("crush decode tunable choose_local_tries = %d",
+             c->choose_local_tries);
+        dout("crush decode tunable choose_local_fallback_tries = %d",
+             c->choose_local_fallback_tries);
+        dout("crush decode tunable choose_total_tries = %d",
+             c->choose_total_tries);
 
+done:
        dout("crush_decode success\n");
        return c;
 
@@ -488,15 +527,16 @@ static int __decode_pool_names(void **p, void *end, struct ceph_osdmap *map)
                ceph_decode_32_safe(p, end, pool, bad);
                ceph_decode_32_safe(p, end, len, bad);
                dout("  pool %d len %d\n", pool, len);
+               ceph_decode_need(p, end, len, bad);
                pi = __lookup_pg_pool(&map->pg_pools, pool);
                if (pi) {
+                       char *name = kstrndup(*p, len, GFP_NOFS);
+
+                       if (!name)
+                               return -ENOMEM;
                        kfree(pi->name);
-                       pi->name = kmalloc(len + 1, GFP_NOFS);
-                       if (pi->name) {
-                               memcpy(pi->name, *p, len);
-                               pi->name[len] = '\0';
-                               dout("  name is %s\n", pi->name);
-                       }
+                       pi->name = name;
+                       dout("  name is %s\n", pi->name);
                }
                *p += len;
        }
@@ -666,6 +706,9 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end)
                ceph_decode_need(p, end, sizeof(u32) + sizeof(u64), bad);
                ceph_decode_copy(p, &pgid, sizeof(pgid));
                n = ceph_decode_32(p);
+               err = -EINVAL;
+               if (n > (UINT_MAX - sizeof(*pg)) / sizeof(u32))
+                       goto bad;
                ceph_decode_need(p, end, n * sizeof(u32), bad);
                err = -ENOMEM;
                pg = kmalloc(sizeof(*pg) + n*sizeof(u32), GFP_NOFS);
@@ -889,6 +932,10 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
                        (void) __remove_pg_mapping(&map->pg_temp, pgid);
 
                        /* insert */
+                       if (pglen > (UINT_MAX - sizeof(*pg)) / sizeof(u32)) {
+                               err = -EINVAL;
+                               goto bad;
+                       }
                        pg = kmalloc(sizeof(*pg) + sizeof(u32)*pglen, GFP_NOFS);
                        if (!pg) {
                                err = -ENOMEM;
index 0ebaea16632fc348f6a48789831b737c7c51b707..ce1bccb08de5ad406c9d858eadf0cdff73e44471 100644 (file)
@@ -1106,11 +1106,23 @@ void netdev_state_change(struct net_device *dev)
 }
 EXPORT_SYMBOL(netdev_state_change);
 
-int netdev_bonding_change(struct net_device *dev, unsigned long event)
+/**
+ *     netdev_notify_peers - notify network peers about existence of @dev
+ *     @dev: network device
+ *
+ * Generate traffic such that interested network peers are aware of
+ * @dev, such as by generating a gratuitous ARP. This may be used when
+ * a device wants to inform the rest of the network about some sort of
+ * reconfiguration such as a failover event or virtual machine
+ * migration.
+ */
+void netdev_notify_peers(struct net_device *dev)
 {
-       return call_netdevice_notifiers(event, dev);
+       rtnl_lock();
+       call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, dev);
+       rtnl_unlock();
 }
-EXPORT_SYMBOL(netdev_bonding_change);
+EXPORT_SYMBOL(netdev_notify_peers);
 
 /**
  *     dev_load        - load a network module
@@ -1172,6 +1184,7 @@ static int __dev_open(struct net_device *dev)
                net_dmaengine_get();
                dev_set_rx_mode(dev);
                dev_activate(dev);
+               add_device_randomness(dev->dev_addr, dev->addr_len);
        }
 
        return ret;
@@ -2133,6 +2146,9 @@ netdev_features_t netif_skb_features(struct sk_buff *skb)
        __be16 protocol = skb->protocol;
        netdev_features_t features = skb->dev->features;
 
+       if (skb_shinfo(skb)->gso_segs > skb->dev->gso_max_segs)
+               features &= ~NETIF_F_GSO_MASK;
+
        if (protocol == htons(ETH_P_8021Q)) {
                struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
                protocol = veh->h_vlan_encapsulated_proto;
@@ -3155,6 +3171,23 @@ void netdev_rx_handler_unregister(struct net_device *dev)
 }
 EXPORT_SYMBOL_GPL(netdev_rx_handler_unregister);
 
+/*
+ * Limit the use of PFMEMALLOC reserves to those protocols that implement
+ * the special handling of PFMEMALLOC skbs.
+ */
+static bool skb_pfmemalloc_protocol(struct sk_buff *skb)
+{
+       switch (skb->protocol) {
+       case __constant_htons(ETH_P_ARP):
+       case __constant_htons(ETH_P_IP):
+       case __constant_htons(ETH_P_IPV6):
+       case __constant_htons(ETH_P_8021Q):
+               return true;
+       default:
+               return false;
+       }
+}
+
 static int __netif_receive_skb(struct sk_buff *skb)
 {
        struct packet_type *ptype, *pt_prev;
@@ -3164,14 +3197,27 @@ static int __netif_receive_skb(struct sk_buff *skb)
        bool deliver_exact = false;
        int ret = NET_RX_DROP;
        __be16 type;
+       unsigned long pflags = current->flags;
 
        net_timestamp_check(!netdev_tstamp_prequeue, skb);
 
        trace_netif_receive_skb(skb);
 
+       /*
+        * PFMEMALLOC skbs are special, they should
+        * - be delivered to SOCK_MEMALLOC sockets only
+        * - stay away from userspace
+        * - have bounded memory usage
+        *
+        * Use PF_MEMALLOC as this saves us from propagating the allocation
+        * context down to all allocation sites.
+        */
+       if (sk_memalloc_socks() && skb_pfmemalloc(skb))
+               current->flags |= PF_MEMALLOC;
+
        /* if we've gotten here through NAPI, check netpoll */
        if (netpoll_receive_skb(skb))
-               return NET_RX_DROP;
+               goto out;
 
        orig_dev = skb->dev;
 
@@ -3191,7 +3237,7 @@ another_round:
        if (skb->protocol == cpu_to_be16(ETH_P_8021Q)) {
                skb = vlan_untag(skb);
                if (unlikely(!skb))
-                       goto out;
+                       goto unlock;
        }
 
 #ifdef CONFIG_NET_CLS_ACT
@@ -3201,6 +3247,9 @@ another_round:
        }
 #endif
 
+       if (sk_memalloc_socks() && skb_pfmemalloc(skb))
+               goto skip_taps;
+
        list_for_each_entry_rcu(ptype, &ptype_all, list) {
                if (!ptype->dev || ptype->dev == skb->dev) {
                        if (pt_prev)
@@ -3209,13 +3258,18 @@ another_round:
                }
        }
 
+skip_taps:
 #ifdef CONFIG_NET_CLS_ACT
        skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
        if (!skb)
-               goto out;
+               goto unlock;
 ncls:
 #endif
 
+       if (sk_memalloc_socks() && skb_pfmemalloc(skb)
+                               && !skb_pfmemalloc_protocol(skb))
+               goto drop;
+
        rx_handler = rcu_dereference(skb->dev->rx_handler);
        if (vlan_tx_tag_present(skb)) {
                if (pt_prev) {
@@ -3225,7 +3279,7 @@ ncls:
                if (vlan_do_receive(&skb, !rx_handler))
                        goto another_round;
                else if (unlikely(!skb))
-                       goto out;
+                       goto unlock;
        }
 
        if (rx_handler) {
@@ -3235,7 +3289,7 @@ ncls:
                }
                switch (rx_handler(&skb)) {
                case RX_HANDLER_CONSUMED:
-                       goto out;
+                       goto unlock;
                case RX_HANDLER_ANOTHER:
                        goto another_round;
                case RX_HANDLER_EXACT:
@@ -3268,6 +3322,7 @@ ncls:
                else
                        ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
        } else {
+drop:
                atomic_long_inc(&skb->dev->rx_dropped);
                kfree_skb(skb);
                /* Jamal, now you will not able to escape explaining
@@ -3276,8 +3331,10 @@ ncls:
                ret = NET_RX_DROP;
        }
 
-out:
+unlock:
        rcu_read_unlock();
+out:
+       tsk_restore_flags(current, pflags, PF_MEMALLOC);
        return ret;
 }
 
@@ -4801,6 +4858,7 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
        err = ops->ndo_set_mac_address(dev, sa);
        if (!err)
                call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
+       add_device_randomness(dev->dev_addr, dev->addr_len);
        return err;
 }
 EXPORT_SYMBOL(dev_set_mac_address);
@@ -5175,12 +5233,12 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
  */
 static int dev_new_index(struct net *net)
 {
-       static int ifindex;
+       int ifindex = net->ifindex;
        for (;;) {
                if (++ifindex <= 0)
                        ifindex = 1;
                if (!__dev_get_by_index(net, ifindex))
-                       return ifindex;
+                       return net->ifindex = ifindex;
        }
 }
 
@@ -5533,7 +5591,12 @@ int register_netdevice(struct net_device *dev)
                }
        }
 
-       dev->ifindex = dev_new_index(net);
+       ret = -EBUSY;
+       if (!dev->ifindex)
+               dev->ifindex = dev_new_index(net);
+       else if (__dev_get_by_index(net, dev->ifindex))
+               goto err_uninit;
+
        if (dev->iflink == -1)
                dev->iflink = dev->ifindex;
 
@@ -5579,6 +5642,7 @@ int register_netdevice(struct net_device *dev)
        dev_init_scheduler(dev);
        dev_hold(dev);
        list_netdevice(dev);
+       add_device_randomness(dev->dev_addr, dev->addr_len);
 
        /* Notify protocols, that a new device appeared. */
        ret = call_netdevice_notifiers(NETDEV_REGISTER, dev);
@@ -5942,6 +6006,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
        dev_net_set(dev, &init_net);
 
        dev->gso_max_size = GSO_MAX_SIZE;
+       dev->gso_max_segs = GSO_MAX_SEGS;
 
        INIT_LIST_HEAD(&dev->napi_list);
        INIT_LIST_HEAD(&dev->unreg_list);
index d4ce2dc712e34b7b1cb974c5e938313f58e9a8aa..907efd27ec77bcf5f3f214058dce27fc77e873bd 100644 (file)
@@ -83,6 +83,14 @@ int sk_filter(struct sock *sk, struct sk_buff *skb)
        int err;
        struct sk_filter *filter;
 
+       /*
+        * If the skb was allocated from pfmemalloc reserves, only
+        * allow SOCK_MEMALLOC sockets to use it as this socket is
+        * helping free memory
+        */
+       if (skb_pfmemalloc(skb) && !sock_flag(sk, SOCK_MEMALLOC))
+               return -ENOMEM;
+
        err = security_sock_rcv_skb(sk, skb);
        if (err)
                return err;
index 5ff949dc954fa46d0f4787be2a3646205b69f408..34d975b0f2770e0792dbcf879fdecf0742c0728a 100644 (file)
@@ -618,7 +618,7 @@ int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id,
                       long expires, u32 error)
 {
        struct rta_cacheinfo ci = {
-               .rta_lastuse = jiffies_to_clock_t(jiffies - dst->lastuse),
+               .rta_lastuse = jiffies_delta_to_clock_t(jiffies - dst->lastuse),
                .rta_used = dst->__use,
                .rta_clntref = atomic_read(&(dst->__refcnt)),
                .rta_error = error,
@@ -1381,6 +1381,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
                        goto errout;
                send_addr_notify = 1;
                modified = 1;
+               add_device_randomness(dev->dev_addr, dev->addr_len);
        }
 
        if (tb[IFLA_MTU]) {
@@ -1811,8 +1812,6 @@ replay:
                        return -ENODEV;
                }
 
-               if (ifm->ifi_index)
-                       return -EOPNOTSUPP;
                if (tb[IFLA_MAP] || tb[IFLA_MASTER] || tb[IFLA_PROTINFO])
                        return -EOPNOTSUPP;
 
@@ -1838,10 +1837,14 @@ replay:
                        return PTR_ERR(dest_net);
 
                dev = rtnl_create_link(net, dest_net, ifname, ops, tb);
-
-               if (IS_ERR(dev))
+               if (IS_ERR(dev)) {
                        err = PTR_ERR(dev);
-               else if (ops->newlink)
+                       goto out;
+               }
+
+               dev->ifindex = ifm->ifi_index;
+
+               if (ops->newlink)
                        err = ops->newlink(net, dev, tb, data);
                else
                        err = register_netdevice(dev);
index 368f65c15e4f92aa184bac5612b400d1e868c345..fe00d12081671a22c65d05d290069d7358dda1d6 100644 (file)
@@ -145,6 +145,43 @@ static void skb_under_panic(struct sk_buff *skb, int sz, void *here)
        BUG();
 }
 
+
+/*
+ * kmalloc_reserve is a wrapper around kmalloc_node_track_caller that tells
+ * the caller if emergency pfmemalloc reserves are being used. If it is and
+ * the socket is later found to be SOCK_MEMALLOC then PFMEMALLOC reserves
+ * may be used. Otherwise, the packet data may be discarded until enough
+ * memory is free
+ */
+#define kmalloc_reserve(size, gfp, node, pfmemalloc) \
+        __kmalloc_reserve(size, gfp, node, _RET_IP_, pfmemalloc)
+void *__kmalloc_reserve(size_t size, gfp_t flags, int node, unsigned long ip,
+                        bool *pfmemalloc)
+{
+       void *obj;
+       bool ret_pfmemalloc = false;
+
+       /*
+        * Try a regular allocation, when that fails and we're not entitled
+        * to the reserves, fail.
+        */
+       obj = kmalloc_node_track_caller(size,
+                                       flags | __GFP_NOMEMALLOC | __GFP_NOWARN,
+                                       node);
+       if (obj || !(gfp_pfmemalloc_allowed(flags)))
+               goto out;
+
+       /* Try again but now we are using pfmemalloc reserves */
+       ret_pfmemalloc = true;
+       obj = kmalloc_node_track_caller(size, flags, node);
+
+out:
+       if (pfmemalloc)
+               *pfmemalloc = ret_pfmemalloc;
+
+       return obj;
+}
+
 /*     Allocate a new skbuff. We do this ourselves so we can fill in a few
  *     'private' fields and also do memory statistics to find all the
  *     [BEEP] leaks.
@@ -155,8 +192,10 @@ static void skb_under_panic(struct sk_buff *skb, int sz, void *here)
  *     __alloc_skb     -       allocate a network buffer
  *     @size: size to allocate
  *     @gfp_mask: allocation mask
- *     @fclone: allocate from fclone cache instead of head cache
- *             and allocate a cloned (child) skb
+ *     @flags: If SKB_ALLOC_FCLONE is set, allocate from fclone cache
+ *             instead of head cache and allocate a cloned (child) skb.
+ *             If SKB_ALLOC_RX is set, __GFP_MEMALLOC will be used for
+ *             allocations in case the data is required for writeback
  *     @node: numa node to allocate memory on
  *
  *     Allocate a new &sk_buff. The returned buffer has no headroom and a
@@ -167,14 +206,19 @@ static void skb_under_panic(struct sk_buff *skb, int sz, void *here)
  *     %GFP_ATOMIC.
  */
 struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
-                           int fclone, int node)
+                           int flags, int node)
 {
        struct kmem_cache *cache;
        struct skb_shared_info *shinfo;
        struct sk_buff *skb;
        u8 *data;
+       bool pfmemalloc;
 
-       cache = fclone ? skbuff_fclone_cache : skbuff_head_cache;
+       cache = (flags & SKB_ALLOC_FCLONE)
+               ? skbuff_fclone_cache : skbuff_head_cache;
+
+       if (sk_memalloc_socks() && (flags & SKB_ALLOC_RX))
+               gfp_mask |= __GFP_MEMALLOC;
 
        /* Get the HEAD */
        skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node);
@@ -189,7 +233,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
         */
        size = SKB_DATA_ALIGN(size);
        size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
-       data = kmalloc_node_track_caller(size, gfp_mask, node);
+       data = kmalloc_reserve(size, gfp_mask, node, &pfmemalloc);
        if (!data)
                goto nodata;
        /* kmalloc(size) might give us more room than requested.
@@ -207,6 +251,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
        memset(skb, 0, offsetof(struct sk_buff, tail));
        /* Account for allocated memory : skb + skb->head */
        skb->truesize = SKB_TRUESIZE(size);
+       skb->pfmemalloc = pfmemalloc;
        atomic_set(&skb->users, 1);
        skb->head = data;
        skb->data = data;
@@ -222,7 +267,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
        atomic_set(&shinfo->dataref, 1);
        kmemcheck_annotate_variable(shinfo->destructor_arg);
 
-       if (fclone) {
+       if (flags & SKB_ALLOC_FCLONE) {
                struct sk_buff *child = skb + 1;
                atomic_t *fclone_ref = (atomic_t *) (child + 1);
 
@@ -232,6 +277,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
                atomic_set(fclone_ref, 1);
 
                child->fclone = SKB_FCLONE_UNAVAILABLE;
+               child->pfmemalloc = pfmemalloc;
        }
 out:
        return skb;
@@ -302,14 +348,7 @@ static DEFINE_PER_CPU(struct netdev_alloc_cache, netdev_alloc_cache);
 
 #define NETDEV_PAGECNT_BIAS (PAGE_SIZE / SMP_CACHE_BYTES)
 
-/**
- * netdev_alloc_frag - allocate a page fragment
- * @fragsz: fragment size
- *
- * Allocates a frag from a page for receive buffer.
- * Uses GFP_ATOMIC allocations.
- */
-void *netdev_alloc_frag(unsigned int fragsz)
+static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask)
 {
        struct netdev_alloc_cache *nc;
        void *data = NULL;
@@ -319,7 +358,7 @@ void *netdev_alloc_frag(unsigned int fragsz)
        nc = &__get_cpu_var(netdev_alloc_cache);
        if (unlikely(!nc->page)) {
 refill:
-               nc->page = alloc_page(GFP_ATOMIC | __GFP_COLD);
+               nc->page = alloc_page(gfp_mask);
                if (unlikely(!nc->page))
                        goto end;
 recycle:
@@ -343,6 +382,18 @@ end:
        local_irq_restore(flags);
        return data;
 }
+
+/**
+ * netdev_alloc_frag - allocate a page fragment
+ * @fragsz: fragment size
+ *
+ * Allocates a frag from a page for receive buffer.
+ * Uses GFP_ATOMIC allocations.
+ */
+void *netdev_alloc_frag(unsigned int fragsz)
+{
+       return __netdev_alloc_frag(fragsz, GFP_ATOMIC | __GFP_COLD);
+}
 EXPORT_SYMBOL(netdev_alloc_frag);
 
 /**
@@ -366,7 +417,12 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
                              SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 
        if (fragsz <= PAGE_SIZE && !(gfp_mask & (__GFP_WAIT | GFP_DMA))) {
-               void *data = netdev_alloc_frag(fragsz);
+               void *data;
+
+               if (sk_memalloc_socks())
+                       gfp_mask |= __GFP_MEMALLOC;
+
+               data = __netdev_alloc_frag(fragsz, gfp_mask);
 
                if (likely(data)) {
                        skb = build_skb(data, fragsz);
@@ -374,7 +430,8 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
                                put_page(virt_to_head_page(data));
                }
        } else {
-               skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, 0, NUMA_NO_NODE);
+               skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask,
+                                 SKB_ALLOC_RX, NUMA_NO_NODE);
        }
        if (likely(skb)) {
                skb_reserve(skb, NET_SKB_PAD);
@@ -656,6 +713,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
 #if IS_ENABLED(CONFIG_IP_VS)
        new->ipvs_property      = old->ipvs_property;
 #endif
+       new->pfmemalloc         = old->pfmemalloc;
        new->protocol           = old->protocol;
        new->mark               = old->mark;
        new->skb_iif            = old->skb_iif;
@@ -814,6 +872,9 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
                n->fclone = SKB_FCLONE_CLONE;
                atomic_inc(fclone_ref);
        } else {
+               if (skb_pfmemalloc(skb))
+                       gfp_mask |= __GFP_MEMALLOC;
+
                n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
                if (!n)
                        return NULL;
@@ -850,6 +911,13 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type;
 }
 
+static inline int skb_alloc_rx_flag(const struct sk_buff *skb)
+{
+       if (skb_pfmemalloc(skb))
+               return SKB_ALLOC_RX;
+       return 0;
+}
+
 /**
  *     skb_copy        -       create private copy of an sk_buff
  *     @skb: buffer to copy
@@ -871,7 +939,8 @@ struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask)
 {
        int headerlen = skb_headroom(skb);
        unsigned int size = skb_end_offset(skb) + skb->data_len;
-       struct sk_buff *n = alloc_skb(size, gfp_mask);
+       struct sk_buff *n = __alloc_skb(size, gfp_mask,
+                                       skb_alloc_rx_flag(skb), NUMA_NO_NODE);
 
        if (!n)
                return NULL;
@@ -906,7 +975,8 @@ EXPORT_SYMBOL(skb_copy);
 struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask)
 {
        unsigned int size = skb_headlen(skb) + headroom;
-       struct sk_buff *n = alloc_skb(size, gfp_mask);
+       struct sk_buff *n = __alloc_skb(size, gfp_mask,
+                                       skb_alloc_rx_flag(skb), NUMA_NO_NODE);
 
        if (!n)
                goto out;
@@ -979,8 +1049,10 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
 
        size = SKB_DATA_ALIGN(size);
 
-       data = kmalloc(size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
-                      gfp_mask);
+       if (skb_pfmemalloc(skb))
+               gfp_mask |= __GFP_MEMALLOC;
+       data = kmalloc_reserve(size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
+                              gfp_mask, NUMA_NO_NODE, NULL);
        if (!data)
                goto nodata;
        size = SKB_WITH_OVERHEAD(ksize(data));
@@ -1092,8 +1164,9 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
        /*
         *      Allocate the copy buffer
         */
-       struct sk_buff *n = alloc_skb(newheadroom + skb->len + newtailroom,
-                                     gfp_mask);
+       struct sk_buff *n = __alloc_skb(newheadroom + skb->len + newtailroom,
+                                       gfp_mask, skb_alloc_rx_flag(skb),
+                                       NUMA_NO_NODE);
        int oldheadroom = skb_headroom(skb);
        int head_copy_len, head_copy_off;
        int off;
@@ -2775,8 +2848,9 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
                        skb_release_head_state(nskb);
                        __skb_push(nskb, doffset);
                } else {
-                       nskb = alloc_skb(hsize + doffset + headroom,
-                                        GFP_ATOMIC);
+                       nskb = __alloc_skb(hsize + doffset + headroom,
+                                          GFP_ATOMIC, skb_alloc_rx_flag(skb),
+                                          NUMA_NO_NODE);
 
                        if (unlikely(!nskb))
                                goto err;
index 2676a88f533e60d969b41f3ddf85313b6bd43a71..8f67ced8d6a808689255435dd412df132138af65 100644 (file)
 static DEFINE_MUTEX(proto_list_mutex);
 static LIST_HEAD(proto_list);
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
 int mem_cgroup_sockets_init(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
 {
        struct proto *proto;
@@ -271,6 +271,61 @@ __u32 sysctl_rmem_default __read_mostly = SK_RMEM_MAX;
 int sysctl_optmem_max __read_mostly = sizeof(unsigned long)*(2*UIO_MAXIOV+512);
 EXPORT_SYMBOL(sysctl_optmem_max);
 
+struct static_key memalloc_socks = STATIC_KEY_INIT_FALSE;
+EXPORT_SYMBOL_GPL(memalloc_socks);
+
+/**
+ * sk_set_memalloc - sets %SOCK_MEMALLOC
+ * @sk: socket to set it on
+ *
+ * Set %SOCK_MEMALLOC on a socket for access to emergency reserves.
+ * It's the responsibility of the admin to adjust min_free_kbytes
+ * to meet the requirements
+ */
+void sk_set_memalloc(struct sock *sk)
+{
+       sock_set_flag(sk, SOCK_MEMALLOC);
+       sk->sk_allocation |= __GFP_MEMALLOC;
+       static_key_slow_inc(&memalloc_socks);
+}
+EXPORT_SYMBOL_GPL(sk_set_memalloc);
+
+void sk_clear_memalloc(struct sock *sk)
+{
+       sock_reset_flag(sk, SOCK_MEMALLOC);
+       sk->sk_allocation &= ~__GFP_MEMALLOC;
+       static_key_slow_dec(&memalloc_socks);
+
+       /*
+        * SOCK_MEMALLOC is allowed to ignore rmem limits to ensure forward
+        * progress of swapping. However, if SOCK_MEMALLOC is cleared while
+        * it has rmem allocations there is a risk that the user of the
+        * socket cannot make forward progress due to exceeding the rmem
+        * limits. By rights, sk_clear_memalloc() should only be called
+        * on sockets being torn down but warn and reset the accounting if
+        * that assumption breaks.
+        */
+       if (WARN_ON(sk->sk_forward_alloc))
+               sk_mem_reclaim(sk);
+}
+EXPORT_SYMBOL_GPL(sk_clear_memalloc);
+
+int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb)
+{
+       int ret;
+       unsigned long pflags = current->flags;
+
+       /* these should have been dropped before queueing */
+       BUG_ON(!sock_flag(sk, SOCK_MEMALLOC));
+
+       current->flags |= PF_MEMALLOC;
+       ret = sk->sk_backlog_rcv(sk, skb);
+       tsk_restore_flags(current, pflags, PF_MEMALLOC);
+
+       return ret;
+}
+EXPORT_SYMBOL(__sk_backlog_rcv);
+
 #if defined(CONFIG_CGROUPS)
 #if !defined(CONFIG_NET_CLS_CGROUP)
 int net_cls_subsys_id = -1;
@@ -353,7 +408,7 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
        if (err)
                return err;
 
-       if (!sk_rmem_schedule(sk, skb->truesize)) {
+       if (!sk_rmem_schedule(sk, skb, skb->truesize)) {
                atomic_inc(&sk->sk_drops);
                return -ENOBUFS;
        }
@@ -1403,6 +1458,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
                } else {
                        sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM;
                        sk->sk_gso_max_size = dst->dev->gso_max_size;
+                       sk->sk_gso_max_segs = dst->dev->gso_max_segs;
                }
        }
 }
index 85a3604c87c8d2cac6cac4d7049d9ba75877bf66..c855e8d0738f75a08a1d44a35a02ff870132510f 100644 (file)
@@ -961,7 +961,7 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowidn *o
                .saddr = oldflp->saddr,
                .flowidn_scope = RT_SCOPE_UNIVERSE,
                .flowidn_mark = oldflp->flowidn_mark,
-               .flowidn_iif = init_net.loopback_dev->ifindex,
+               .flowidn_iif = LOOPBACK_IFINDEX,
                .flowidn_oif = oldflp->flowidn_oif,
        };
        struct dn_route *rt = NULL;
@@ -979,7 +979,7 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowidn *o
                       "dn_route_output_slow: dst=%04x src=%04x mark=%d"
                       " iif=%d oif=%d\n", le16_to_cpu(oldflp->daddr),
                       le16_to_cpu(oldflp->saddr),
-                      oldflp->flowidn_mark, init_net.loopback_dev->ifindex,
+                      oldflp->flowidn_mark, LOOPBACK_IFINDEX,
                       oldflp->flowidn_oif);
 
        /* If we have an output interface, verify its a DECnet device */
@@ -1042,7 +1042,7 @@ source_ok:
                        if (!fld.daddr)
                                goto out;
                }
-               fld.flowidn_oif = init_net.loopback_dev->ifindex;
+               fld.flowidn_oif = LOOPBACK_IFINDEX;
                res.type = RTN_LOCAL;
                goto make_route;
        }
index ae2ccf2890e438147bac5d3791f0f6927f720536..15ca63ec604ee8e01c1f79103c7fe39265a8831c 100644 (file)
@@ -49,7 +49,7 @@ obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o
 obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o
 obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o
 obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o
-obj-$(CONFIG_CGROUP_MEM_RES_CTLR_KMEM) += tcp_memcontrol.o
+obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o
 obj-$(CONFIG_NETLABEL) += cipso_ipv4.o
 
 obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
index fe4582ca969a4ff85c862f8fd96d3b6ec3d03a4d..6681ccf5c3eeae5bbbca030cf2d09cf826ad335c 100644 (file)
@@ -1364,7 +1364,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
        if (*(u8 *)iph != 0x45)
                goto out_unlock;
 
-       if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
+       if (unlikely(ip_fast_csum((u8 *)iph, 5)))
                goto out_unlock;
 
        id = ntohl(*(__be32 *)&iph->id);
@@ -1380,7 +1380,6 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
                iph2 = ip_hdr(p);
 
                if ((iph->protocol ^ iph2->protocol) |
-                   (iph->tos ^ iph2->tos) |
                    ((__force u32)iph->saddr ^ (__force u32)iph2->saddr) |
                    ((__force u32)iph->daddr ^ (__force u32)iph2->daddr)) {
                        NAPI_GRO_CB(p)->same_flow = 0;
@@ -1390,6 +1389,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
                /* All fields must match except length and checksum. */
                NAPI_GRO_CB(p)->flush |=
                        (iph->ttl ^ iph2->ttl) |
+                       (iph->tos ^ iph2->tos) |
                        ((u16)(ntohs(iph2->id) + NAPI_GRO_CB(p)->count) ^ id);
 
                NAPI_GRO_CB(p)->flush |= flush;
index 44bf82e3aef7d6d4b5afc39a357b7da6d85a44e7..adf273f8ad2eb28a658321c68967bb2e6ccf25a0 100644 (file)
@@ -94,25 +94,22 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
        [IFA_LABEL]             = { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
 };
 
-/* inet_addr_hash's shifting is dependent upon this IN4_ADDR_HSIZE
- * value.  So if you change this define, make appropriate changes to
- * inet_addr_hash as well.
- */
-#define IN4_ADDR_HSIZE 256
+#define IN4_ADDR_HSIZE_SHIFT   8
+#define IN4_ADDR_HSIZE         (1U << IN4_ADDR_HSIZE_SHIFT)
+
 static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
 static DEFINE_SPINLOCK(inet_addr_hash_lock);
 
-static inline unsigned int inet_addr_hash(struct net *net, __be32 addr)
+static u32 inet_addr_hash(struct net *net, __be32 addr)
 {
-       u32 val = (__force u32) addr ^ hash_ptr(net, 8);
+       u32 val = (__force u32) addr ^ net_hash_mix(net);
 
-       return ((val ^ (val >> 8) ^ (val >> 16) ^ (val >> 24)) &
-               (IN4_ADDR_HSIZE - 1));
+       return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
 }
 
 static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
 {
-       unsigned int hash = inet_addr_hash(net, ifa->ifa_local);
+       u32 hash = inet_addr_hash(net, ifa->ifa_local);
 
        spin_lock(&inet_addr_hash_lock);
        hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
@@ -136,18 +133,18 @@ static void inet_hash_remove(struct in_ifaddr *ifa)
  */
 struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
 {
-       unsigned int hash = inet_addr_hash(net, addr);
+       u32 hash = inet_addr_hash(net, addr);
        struct net_device *result = NULL;
        struct in_ifaddr *ifa;
        struct hlist_node *node;
 
        rcu_read_lock();
        hlist_for_each_entry_rcu(ifa, node, &inet_addr_lst[hash], hash) {
-               struct net_device *dev = ifa->ifa_dev->dev;
-
-               if (!net_eq(dev_net(dev), net))
-                       continue;
                if (ifa->ifa_local == addr) {
+                       struct net_device *dev = ifa->ifa_dev->dev;
+
+                       if (!net_eq(dev_net(dev), net))
+                               continue;
                        result = dev;
                        break;
                }
@@ -182,10 +179,10 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
 static void devinet_sysctl_register(struct in_device *idev);
 static void devinet_sysctl_unregister(struct in_device *idev);
 #else
-static inline void devinet_sysctl_register(struct in_device *idev)
+static void devinet_sysctl_register(struct in_device *idev)
 {
 }
-static inline void devinet_sysctl_unregister(struct in_device *idev)
+static void devinet_sysctl_unregister(struct in_device *idev)
 {
 }
 #endif
@@ -205,7 +202,7 @@ static void inet_rcu_free_ifa(struct rcu_head *head)
        kfree(ifa);
 }
 
-static inline void inet_free_ifa(struct in_ifaddr *ifa)
+static void inet_free_ifa(struct in_ifaddr *ifa)
 {
        call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
 }
@@ -659,7 +656,7 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg
  *     Determine a default network mask, based on the IP address.
  */
 
-static inline int inet_abc_len(__be32 addr)
+static int inet_abc_len(__be32 addr)
 {
        int rc = -1;    /* Something else, probably a multicast. */
 
@@ -1124,7 +1121,7 @@ skip:
        }
 }
 
-static inline bool inetdev_valid_mtu(unsigned int mtu)
+static bool inetdev_valid_mtu(unsigned int mtu)
 {
        return mtu >= 68;
 }
@@ -1239,7 +1236,7 @@ static struct notifier_block ip_netdev_notifier = {
        .notifier_call = inetdev_event,
 };
 
-static inline size_t inet_nlmsg_size(void)
+static size_t inet_nlmsg_size(void)
 {
        return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
               + nla_total_size(4) /* IFA_ADDRESS */
index c43ae3fba7921e590859cea407ba9b9e030b16be..7f073a38c87d88a89f2c11d3cd56b67a66d194b2 100644 (file)
@@ -218,7 +218,7 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb)
        scope = RT_SCOPE_UNIVERSE;
        if (!ipv4_is_zeronet(ip_hdr(skb)->saddr)) {
                fl4.flowi4_oif = 0;
-               fl4.flowi4_iif = net->loopback_dev->ifindex;
+               fl4.flowi4_iif = LOOPBACK_IFINDEX;
                fl4.daddr = ip_hdr(skb)->saddr;
                fl4.saddr = 0;
                fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
index f0cdb30921c0bdb5b1639e15d05908dfde6586f9..4587d344046d657157a6cccd62ef6f7dfa4648fc 100644 (file)
@@ -1550,7 +1550,8 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp,
                 * state.directly.
                 */
                if (pref_mismatch) {
-                       int mp = KEYLENGTH - fls(pref_mismatch);
+                       /* fls(x) = __fls(x) + 1 */
+                       int mp = KEYLENGTH - __fls(pref_mismatch) - 1;
 
                        if (tkey_extract_bits(cn->key, mp, cn->pos - mp) != 0)
                                goto backtrace;
@@ -1655,7 +1656,12 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
        if (!l)
                return -ESRCH;
 
-       fa_head = get_fa_head(l, plen);
+       li = find_leaf_info(l, plen);
+
+       if (!li)
+               return -ESRCH;
+
+       fa_head = &li->falh;
        fa = fib_find_alias(fa_head, tos, 0);
 
        if (!fa)
@@ -1691,9 +1697,6 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
        rtmsg_fib(RTM_DELROUTE, htonl(key), fa, plen, tb->tb_id,
                  &cfg->fc_nlinfo, 0);
 
-       l = fib_find_node(t, key);
-       li = find_leaf_info(l, plen);
-
        list_del_rcu(&fa->fa_list);
 
        if (!plen)
index 6699f23e6f55b0012cc50b74030f415362efbed1..0b5580c69f2d46b5d2c49bcd0bcbb220439544ff 100644 (file)
@@ -2435,6 +2435,8 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v)
                struct ip_mc_list *im = (struct ip_mc_list *)v;
                struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq);
                char   *querier;
+               long delta;
+
 #ifdef CONFIG_IP_MULTICAST
                querier = IGMP_V1_SEEN(state->in_dev) ? "V1" :
                          IGMP_V2_SEEN(state->in_dev) ? "V2" :
@@ -2448,11 +2450,12 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v)
                                   state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier);
                }
 
+               delta = im->timer.expires - jiffies;
                seq_printf(seq,
                           "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n",
                           im->multiaddr, im->users,
-                          im->tm_running, im->tm_running ?
-                          jiffies_to_clock_t(im->timer.expires-jiffies) : 0,
+                          im->tm_running,
+                          im->tm_running ? jiffies_delta_to_clock_t(delta) : 0,
                           im->reporter);
        }
        return 0;
index 8eec8f4a05360d24897719495100ea100f0c69b2..3a57570c8ee5ecf8570cb9fb714664d2a262d211 100644 (file)
@@ -1798,7 +1798,7 @@ static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb)
                .flowi4_oif = (rt_is_output_route(rt) ?
                               skb->dev->ifindex : 0),
                .flowi4_iif = (rt_is_output_route(rt) ?
-                              net->loopback_dev->ifindex :
+                              LOOPBACK_IFINDEX :
                               skb->dev->ifindex),
                .flowi4_mark = skb->mark,
        };
index 31371be8174be1d8da1a50f11dd6c48409b29b74..c30130062cd6515f31d7497eaa6a403d2b1d629d 100644 (file)
@@ -85,7 +85,7 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par)
                        return ipv4_is_local_multicast(iph->daddr) ^ invert;
                flow.flowi4_iif = 0;
        } else {
-               flow.flowi4_iif = dev_net(par->in)->loopback_dev->ifindex;
+               flow.flowi4_iif = LOOPBACK_IFINDEX;
        }
 
        flow.daddr = iph->saddr;
index c035251beb070bb7f4220324a90e0ffeee8e103c..c58137391a3dd2a8b317deb16aa0420b37c93c80 100644 (file)
@@ -70,7 +70,6 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
-#include <linux/bootmem.h>
 #include <linux/string.h>
 #include <linux/socket.h>
 #include <linux/sockios.h>
@@ -80,7 +79,6 @@
 #include <linux/netdevice.h>
 #include <linux/proc_fs.h>
 #include <linux/init.h>
-#include <linux/workqueue.h>
 #include <linux/skbuff.h>
 #include <linux/inetdevice.h>
 #include <linux/igmp.h>
 #include <linux/mroute.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/random.h>
-#include <linux/jhash.h>
 #include <linux/rcupdate.h>
 #include <linux/times.h>
 #include <linux/slab.h>
-#include <linux/prefetch.h>
 #include <net/dst.h>
 #include <net/net_namespace.h>
 #include <net/protocol.h>
@@ -1591,11 +1587,14 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
        if (ipv4_is_zeronet(daddr))
                goto martian_destination;
 
-       if (likely(!IN_DEV_ROUTE_LOCALNET(in_dev))) {
-               if (ipv4_is_loopback(daddr))
+       /* Following code try to avoid calling IN_DEV_NET_ROUTE_LOCALNET(),
+        * and call it once if daddr or/and saddr are loopback addresses
+        */
+       if (ipv4_is_loopback(daddr)) {
+               if (!IN_DEV_NET_ROUTE_LOCALNET(in_dev, net))
                        goto martian_destination;
-
-               if (ipv4_is_loopback(saddr))
+       } else if (ipv4_is_loopback(saddr)) {
+               if (!IN_DEV_NET_ROUTE_LOCALNET(in_dev, net))
                        goto martian_source;
        }
 
@@ -1620,7 +1619,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
 
        if (res.type == RTN_LOCAL) {
                err = fib_validate_source(skb, saddr, daddr, tos,
-                                         net->loopback_dev->ifindex,
+                                         LOOPBACK_IFINDEX,
                                          dev, in_dev, &itag);
                if (err < 0)
                        goto martian_source_keep_err;
@@ -1896,7 +1895,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
 
        orig_oif = fl4->flowi4_oif;
 
-       fl4->flowi4_iif = net->loopback_dev->ifindex;
+       fl4->flowi4_iif = LOOPBACK_IFINDEX;
        fl4->flowi4_tos = tos & IPTOS_RT_MASK;
        fl4->flowi4_scope = ((tos & RTO_ONLINK) ?
                         RT_SCOPE_LINK : RT_SCOPE_UNIVERSE);
@@ -1985,7 +1984,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
                if (!fl4->daddr)
                        fl4->daddr = fl4->saddr = htonl(INADDR_LOOPBACK);
                dev_out = net->loopback_dev;
-               fl4->flowi4_oif = net->loopback_dev->ifindex;
+               fl4->flowi4_oif = LOOPBACK_IFINDEX;
                res.type = RTN_LOCAL;
                flags |= RTCF_LOCAL;
                goto make_route;
index 4b6487a68279a888f28e0ae600395c313dd36fac..1b5ce96707a38124c9ae3e11b261995ce19b6668 100644 (file)
@@ -184,7 +184,7 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write,
        int ret;
        unsigned long vec[3];
        struct net *net = current->nsproxy->net_ns;
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
        struct mem_cgroup *memcg;
 #endif
 
@@ -203,7 +203,7 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write,
        if (ret)
                return ret;
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
        rcu_read_lock();
        memcg = mem_cgroup_from_task(current);
 
index e7e6eeae49c0123d392fa16c7687c333b30b79b8..2109ff4a1dafd489fbe0e2240075432df4517374 100644 (file)
@@ -811,7 +811,9 @@ static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now,
                           old_size_goal + mss_now > xmit_size_goal)) {
                        xmit_size_goal = old_size_goal;
                } else {
-                       tp->xmit_size_goal_segs = xmit_size_goal / mss_now;
+                       tp->xmit_size_goal_segs =
+                               min_t(u16, xmit_size_goal / mss_now,
+                                     sk->sk_gso_max_segs);
                        xmit_size_goal = tp->xmit_size_goal_segs * mss_now;
                }
        }
index 4d4db16e336ea63c2b581079e5295a4a74924aab..1432cdb0644c2b16f893de842bcd3b9fdd3c5538 100644 (file)
@@ -291,7 +291,8 @@ bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight)
        left = tp->snd_cwnd - in_flight;
        if (sk_can_gso(sk) &&
            left * sysctl_tcp_tso_win_divisor < tp->snd_cwnd &&
-           left * tp->mss_cache < sk->sk_gso_max_size)
+           left * tp->mss_cache < sk->sk_gso_max_size &&
+           left < sk->sk_gso_max_segs)
                return true;
        return left <= tcp_max_tso_deferred_mss(tp);
 }
index 9be30b039ae3ca7493d5fa37c3556da82ddf4229..fa2c2c2cac2d1a81557be468cf799fee5db7e49c 100644 (file)
@@ -237,7 +237,11 @@ static inline void TCP_ECN_check_ce(struct tcp_sock *tp, const struct sk_buff *s
                        tcp_enter_quickack_mode((struct sock *)tp);
                break;
        case INET_ECN_CE:
-               tp->ecn_flags |= TCP_ECN_DEMAND_CWR;
+               if (!(tp->ecn_flags & TCP_ECN_DEMAND_CWR)) {
+                       /* Better not delay acks, sender can have a very low cwnd */
+                       tcp_enter_quickack_mode((struct sock *)tp);
+                       tp->ecn_flags |= TCP_ECN_DEMAND_CWR;
+               }
                /* fallinto */
        default:
                tp->ecn_flags |= TCP_ECN_SEEN;
@@ -4351,19 +4355,20 @@ static void tcp_ofo_queue(struct sock *sk)
 static bool tcp_prune_ofo_queue(struct sock *sk);
 static int tcp_prune_queue(struct sock *sk);
 
-static int tcp_try_rmem_schedule(struct sock *sk, unsigned int size)
+static int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb,
+                                unsigned int size)
 {
        if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
-           !sk_rmem_schedule(sk, size)) {
+           !sk_rmem_schedule(sk, skb, size)) {
 
                if (tcp_prune_queue(sk) < 0)
                        return -1;
 
-               if (!sk_rmem_schedule(sk, size)) {
+               if (!sk_rmem_schedule(sk, skb, size)) {
                        if (!tcp_prune_ofo_queue(sk))
                                return -1;
 
-                       if (!sk_rmem_schedule(sk, size))
+                       if (!sk_rmem_schedule(sk, skb, size))
                                return -1;
                }
        }
@@ -4418,7 +4423,7 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
 
        TCP_ECN_check_ce(tp, skb);
 
-       if (unlikely(tcp_try_rmem_schedule(sk, skb->truesize))) {
+       if (unlikely(tcp_try_rmem_schedule(sk, skb, skb->truesize))) {
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPOFODROP);
                __kfree_skb(skb);
                return;
@@ -4552,17 +4557,17 @@ static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int
 
 int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
 {
-       struct sk_buff *skb;
+       struct sk_buff *skb = NULL;
        struct tcphdr *th;
        bool fragstolen;
 
-       if (tcp_try_rmem_schedule(sk, size + sizeof(*th)))
-               goto err;
-
        skb = alloc_skb(size + sizeof(*th), sk->sk_allocation);
        if (!skb)
                goto err;
 
+       if (tcp_try_rmem_schedule(sk, skb, size + sizeof(*th)))
+               goto err_free;
+
        th = (struct tcphdr *)skb_put(skb, sizeof(*th));
        skb_reset_transport_header(skb);
        memset(th, 0, sizeof(*th));
@@ -4633,7 +4638,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
                if (eaten <= 0) {
 queue_and_out:
                        if (eaten < 0 &&
-                           tcp_try_rmem_schedule(sk, skb->truesize))
+                           tcp_try_rmem_schedule(sk, skb, skb->truesize))
                                goto drop;
 
                        eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen);
index 7f91e5ac827794161bef2da2584a936680fd3daf..c660d2c19a2b8be9dfb450f3cb577c25922b24db 100644 (file)
@@ -2385,7 +2385,7 @@ static void get_openreq4(const struct sock *sk, const struct request_sock *req,
                         struct seq_file *f, int i, int uid, int *len)
 {
        const struct inet_request_sock *ireq = inet_rsk(req);
-       int ttd = req->expires - jiffies;
+       long delta = req->expires - jiffies;
 
        seq_printf(f, "%4d: %08X:%04X %08X:%04X"
                " %02X %08X:%08X %02X:%08lX %08X %5d %8d %u %d %pK%n",
@@ -2397,7 +2397,7 @@ static void get_openreq4(const struct sock *sk, const struct request_sock *req,
                TCP_SYN_RECV,
                0, 0, /* could print option size, but that is af dependent. */
                1,    /* timers active (only the expire timer) */
-               jiffies_to_clock_t(ttd),
+               jiffies_delta_to_clock_t(delta),
                req->retrans,
                uid,
                0,  /* non standard timer */
@@ -2448,7 +2448,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len)
                tp->write_seq - tp->snd_una,
                rx_queue,
                timer_active,
-               jiffies_to_clock_t(timer_expires - jiffies),
+               jiffies_delta_to_clock_t(timer_expires - jiffies),
                icsk->icsk_retransmits,
                sock_i_uid(sk),
                icsk->icsk_probes_out,
@@ -2467,10 +2467,7 @@ static void get_timewait4_sock(const struct inet_timewait_sock *tw,
 {
        __be32 dest, src;
        __u16 destp, srcp;
-       int ttd = tw->tw_ttd - jiffies;
-
-       if (ttd < 0)
-               ttd = 0;
+       long delta = tw->tw_ttd - jiffies;
 
        dest  = tw->tw_daddr;
        src   = tw->tw_rcv_saddr;
@@ -2480,7 +2477,7 @@ static void get_timewait4_sock(const struct inet_timewait_sock *tw,
        seq_printf(f, "%4d: %08X:%04X %08X:%04X"
                " %02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK%n",
                i, src, srcp, dest, destp, tw->tw_substate, 0, 0,
-               3, jiffies_to_clock_t(ttd), 0, 0, 0, 0,
+               3, jiffies_delta_to_clock_t(delta), 0, 0, 0, 0,
                atomic_read(&tw->tw_refcnt), tw, len);
 }
 
@@ -2633,7 +2630,7 @@ struct proto tcp_prot = {
        .compat_setsockopt      = compat_tcp_setsockopt,
        .compat_getsockopt      = compat_tcp_getsockopt,
 #endif
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
        .init_cgroup            = tcp_init_cgroup,
        .destroy_cgroup         = tcp_destroy_cgroup,
        .proto_cgroup           = tcp_proto_cgroup,
index 33cd065cfbd855ff56bcd22e05a9a2775739fbe2..a7b3ec9b6c3ee33a9bc8730c45f8f3d85b034196 100644 (file)
@@ -1522,21 +1522,21 @@ static void tcp_cwnd_validate(struct sock *sk)
  * when we would be allowed to send the split-due-to-Nagle skb fully.
  */
 static unsigned int tcp_mss_split_point(const struct sock *sk, const struct sk_buff *skb,
-                                       unsigned int mss_now, unsigned int cwnd)
+                                       unsigned int mss_now, unsigned int max_segs)
 {
        const struct tcp_sock *tp = tcp_sk(sk);
-       u32 needed, window, cwnd_len;
+       u32 needed, window, max_len;
 
        window = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq;
-       cwnd_len = mss_now * cwnd;
+       max_len = mss_now * max_segs;
 
-       if (likely(cwnd_len <= window && skb != tcp_write_queue_tail(sk)))
-               return cwnd_len;
+       if (likely(max_len <= window && skb != tcp_write_queue_tail(sk)))
+               return max_len;
 
        needed = min(skb->len, window);
 
-       if (cwnd_len <= needed)
-               return cwnd_len;
+       if (max_len <= needed)
+               return max_len;
 
        return needed - needed % mss_now;
 }
@@ -1765,7 +1765,8 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb)
        limit = min(send_win, cong_win);
 
        /* If a full-sized TSO skb can be sent, do it. */
-       if (limit >= sk->sk_gso_max_size)
+       if (limit >= min_t(unsigned int, sk->sk_gso_max_size,
+                          sk->sk_gso_max_segs * tp->mss_cache))
                goto send_now;
 
        /* Middle in queue won't get any more data, full sendable already? */
@@ -1999,7 +2000,9 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                limit = mss_now;
                if (tso_segs > 1 && !tcp_urg_mode(tp))
                        limit = tcp_mss_split_point(sk, skb, mss_now,
-                                                   cwnd_quota);
+                                                   min_t(unsigned int,
+                                                         cwnd_quota,
+                                                         sk->sk_gso_max_segs));
 
                if (skb->len > limit &&
                    unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
@@ -2045,7 +2048,8 @@ void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
        if (unlikely(sk->sk_state == TCP_CLOSE))
                return;
 
-       if (tcp_write_xmit(sk, cur_mss, nonagle, 0, GFP_ATOMIC))
+       if (tcp_write_xmit(sk, cur_mss, nonagle, 0,
+                          sk_gfp_atomic(sk, GFP_ATOMIC)))
                tcp_check_probe_timer(sk);
 }
 
@@ -2666,7 +2670,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
 
        if (cvp != NULL && cvp->s_data_constant && cvp->s_data_desired)
                s_data_desired = cvp->s_data_desired;
-       skb = alloc_skb(MAX_TCP_HEADER + 15 + s_data_desired, GFP_ATOMIC);
+       skb = alloc_skb(MAX_TCP_HEADER + 15 + s_data_desired,
+                       sk_gfp_atomic(sk, GFP_ATOMIC));
        if (unlikely(!skb)) {
                dst_release(dst);
                return NULL;
@@ -3064,7 +3069,7 @@ void tcp_send_ack(struct sock *sk)
         * tcp_transmit_skb() will set the ownership to this
         * sock.
         */
-       buff = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC);
+       buff = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC));
        if (buff == NULL) {
                inet_csk_schedule_ack(sk);
                inet_csk(sk)->icsk_ack.ato = TCP_ATO_MIN;
@@ -3079,7 +3084,7 @@ void tcp_send_ack(struct sock *sk)
 
        /* Send it off, this clears delayed acks for us. */
        TCP_SKB_CB(buff)->when = tcp_time_stamp;
-       tcp_transmit_skb(sk, buff, 0, GFP_ATOMIC);
+       tcp_transmit_skb(sk, buff, 0, sk_gfp_atomic(sk, GFP_ATOMIC));
 }
 
 /* This routine sends a packet with an out of date sequence
@@ -3099,7 +3104,7 @@ static int tcp_xmit_probe_skb(struct sock *sk, int urgent)
        struct sk_buff *skb;
 
        /* We don't queue it, tcp_transmit_skb() sets ownership. */
-       skb = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC);
+       skb = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC));
        if (skb == NULL)
                return -1;
 
index 5728695b54492dc2d275458b8db786f410e94002..4f7fe7270e3703226de121041d1e5e96a5b127df 100644 (file)
@@ -201,6 +201,22 @@ config IPV6_TUNNEL
 
          If unsure, say N.
 
+config IPV6_GRE
+       tristate "IPv6: GRE tunnel"
+       select IPV6_TUNNEL
+       ---help---
+         Tunneling means encapsulating data of one protocol type within
+         another protocol and sending it over a channel that understands the
+         encapsulating protocol. This particular tunneling driver implements
+         GRE (Generic Routing Encapsulation) and at this time allows
+         encapsulating of IPv4 or IPv6 over existing IPv6 infrastructure.
+         This driver is useful if the other endpoint is a Cisco router: Cisco
+         likes GRE much better than the other Linux tunneling driver ("IP
+         tunneling" above). In addition, GRE allows multicast redistribution
+         through the tunnel.
+
+         Saying M here will produce a module called ip6_gre. If unsure, say N.
+
 config IPV6_MULTIPLE_TABLES
        bool "IPv6: Multiple Routing Tables"
        depends on EXPERIMENTAL
index 686934acfac18eac215a17b8d4d01ea5ad03c860..b6d3f79151e28251f3b3b5a062869fa9acb64526 100644 (file)
@@ -36,6 +36,7 @@ obj-$(CONFIG_NETFILTER)       += netfilter/
 
 obj-$(CONFIG_IPV6_SIT) += sit.o
 obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
+obj-$(CONFIG_IPV6_GRE) += ip6_gre.o
 
 obj-y += addrconf_core.o exthdrs_core.o
 
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
new file mode 100644 (file)
index 0000000..a84ad5d
--- /dev/null
@@ -0,0 +1,1790 @@
+/*
+ *     GRE over IPv6 protocol decoder.
+ *
+ *     Authors: Dmitry Kozlov (xeb@mail.ru)
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version
+ *     2 of the License, or (at your option) any later version.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/capability.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/in.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/if_arp.h>
+#include <linux/mroute.h>
+#include <linux/init.h>
+#include <linux/in6.h>
+#include <linux/inetdevice.h>
+#include <linux/igmp.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/hash.h>
+#include <linux/if_tunnel.h>
+#include <linux/ip6_tunnel.h>
+
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/protocol.h>
+#include <net/addrconf.h>
+#include <net/arp.h>
+#include <net/checksum.h>
+#include <net/dsfield.h>
+#include <net/inet_ecn.h>
+#include <net/xfrm.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+#include <net/rtnetlink.h>
+
+#include <net/ipv6.h>
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+#include <net/ip6_tunnel.h>
+
+
+#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK)
+#define IPV6_TCLASS_SHIFT 20
+
+#define HASH_SIZE_SHIFT  5
+#define HASH_SIZE (1 << HASH_SIZE_SHIFT)
+
+static int ip6gre_net_id __read_mostly;
+struct ip6gre_net {
+       struct ip6_tnl __rcu *tunnels[4][HASH_SIZE];
+
+       struct net_device *fb_tunnel_dev;
+};
+
+static struct rtnl_link_ops ip6gre_link_ops __read_mostly;
+static int ip6gre_tunnel_init(struct net_device *dev);
+static void ip6gre_tunnel_setup(struct net_device *dev);
+static void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t);
+static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu);
+
+/* Tunnel hash table */
+
+/*
+   4 hash tables:
+
+   3: (remote,local)
+   2: (remote,*)
+   1: (*,local)
+   0: (*,*)
+
+   We require exact key match i.e. if a key is present in packet
+   it will match only tunnel with the same key; if it is not present,
+   it will match only keyless tunnel.
+
+   All keysless packets, if not matched configured keyless tunnels
+   will match fallback tunnel.
+ */
+
+#define HASH_KEY(key) (((__force u32)key^((__force u32)key>>4))&(HASH_SIZE - 1))
+static u32 HASH_ADDR(const struct in6_addr *addr)
+{
+       u32 hash = ipv6_addr_hash(addr);
+
+       return hash_32(hash, HASH_SIZE_SHIFT);
+}
+
+#define tunnels_r_l    tunnels[3]
+#define tunnels_r      tunnels[2]
+#define tunnels_l      tunnels[1]
+#define tunnels_wc     tunnels[0]
+/*
+ * Locking : hash tables are protected by RCU and RTNL
+ */
+
+#define for_each_ip_tunnel_rcu(start) \
+       for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
+
+/* often modified stats are per cpu, other are shared (netdev->stats) */
+struct pcpu_tstats {
+       u64     rx_packets;
+       u64     rx_bytes;
+       u64     tx_packets;
+       u64     tx_bytes;
+       struct u64_stats_sync   syncp;
+};
+
+static struct rtnl_link_stats64 *ip6gre_get_stats64(struct net_device *dev,
+               struct rtnl_link_stats64 *tot)
+{
+       int i;
+
+       for_each_possible_cpu(i) {
+               const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i);
+               u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
+               unsigned int start;
+
+               do {
+                       start = u64_stats_fetch_begin_bh(&tstats->syncp);
+                       rx_packets = tstats->rx_packets;
+                       tx_packets = tstats->tx_packets;
+                       rx_bytes = tstats->rx_bytes;
+                       tx_bytes = tstats->tx_bytes;
+               } while (u64_stats_fetch_retry_bh(&tstats->syncp, start));
+
+               tot->rx_packets += rx_packets;
+               tot->tx_packets += tx_packets;
+               tot->rx_bytes   += rx_bytes;
+               tot->tx_bytes   += tx_bytes;
+       }
+
+       tot->multicast = dev->stats.multicast;
+       tot->rx_crc_errors = dev->stats.rx_crc_errors;
+       tot->rx_fifo_errors = dev->stats.rx_fifo_errors;
+       tot->rx_length_errors = dev->stats.rx_length_errors;
+       tot->rx_errors = dev->stats.rx_errors;
+       tot->tx_fifo_errors = dev->stats.tx_fifo_errors;
+       tot->tx_carrier_errors = dev->stats.tx_carrier_errors;
+       tot->tx_dropped = dev->stats.tx_dropped;
+       tot->tx_aborted_errors = dev->stats.tx_aborted_errors;
+       tot->tx_errors = dev->stats.tx_errors;
+
+       return tot;
+}
+
+/* Given src, dst and key, find appropriate for input tunnel. */
+
+static struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev,
+               const struct in6_addr *remote, const struct in6_addr *local,
+               __be32 key, __be16 gre_proto)
+{
+       struct net *net = dev_net(dev);
+       int link = dev->ifindex;
+       unsigned int h0 = HASH_ADDR(remote);
+       unsigned int h1 = HASH_KEY(key);
+       struct ip6_tnl *t, *cand = NULL;
+       struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+       int dev_type = (gre_proto == htons(ETH_P_TEB)) ?
+                      ARPHRD_ETHER : ARPHRD_IP6GRE;
+       int score, cand_score = 4;
+
+       for_each_ip_tunnel_rcu(ign->tunnels_r_l[h0 ^ h1]) {
+               if (!ipv6_addr_equal(local, &t->parms.laddr) ||
+                   !ipv6_addr_equal(remote, &t->parms.raddr) ||
+                   key != t->parms.i_key ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (t->dev->type != ARPHRD_IP6GRE &&
+                   t->dev->type != dev_type)
+                       continue;
+
+               score = 0;
+               if (t->parms.link != link)
+                       score |= 1;
+               if (t->dev->type != dev_type)
+                       score |= 2;
+               if (score == 0)
+                       return t;
+
+               if (score < cand_score) {
+                       cand = t;
+                       cand_score = score;
+               }
+       }
+
+       for_each_ip_tunnel_rcu(ign->tunnels_r[h0 ^ h1]) {
+               if (!ipv6_addr_equal(remote, &t->parms.raddr) ||
+                   key != t->parms.i_key ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (t->dev->type != ARPHRD_IP6GRE &&
+                   t->dev->type != dev_type)
+                       continue;
+
+               score = 0;
+               if (t->parms.link != link)
+                       score |= 1;
+               if (t->dev->type != dev_type)
+                       score |= 2;
+               if (score == 0)
+                       return t;
+
+               if (score < cand_score) {
+                       cand = t;
+                       cand_score = score;
+               }
+       }
+
+       for_each_ip_tunnel_rcu(ign->tunnels_l[h1]) {
+               if ((!ipv6_addr_equal(local, &t->parms.laddr) &&
+                         (!ipv6_addr_equal(local, &t->parms.raddr) ||
+                                !ipv6_addr_is_multicast(local))) ||
+                   key != t->parms.i_key ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (t->dev->type != ARPHRD_IP6GRE &&
+                   t->dev->type != dev_type)
+                       continue;
+
+               score = 0;
+               if (t->parms.link != link)
+                       score |= 1;
+               if (t->dev->type != dev_type)
+                       score |= 2;
+               if (score == 0)
+                       return t;
+
+               if (score < cand_score) {
+                       cand = t;
+                       cand_score = score;
+               }
+       }
+
+       for_each_ip_tunnel_rcu(ign->tunnels_wc[h1]) {
+               if (t->parms.i_key != key ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (t->dev->type != ARPHRD_IP6GRE &&
+                   t->dev->type != dev_type)
+                       continue;
+
+               score = 0;
+               if (t->parms.link != link)
+                       score |= 1;
+               if (t->dev->type != dev_type)
+                       score |= 2;
+               if (score == 0)
+                       return t;
+
+               if (score < cand_score) {
+                       cand = t;
+                       cand_score = score;
+               }
+       }
+
+       if (cand != NULL)
+               return cand;
+
+       dev = ign->fb_tunnel_dev;
+       if (dev->flags & IFF_UP)
+               return netdev_priv(dev);
+
+       return NULL;
+}
+
+static struct ip6_tnl __rcu **__ip6gre_bucket(struct ip6gre_net *ign,
+               const struct __ip6_tnl_parm *p)
+{
+       const struct in6_addr *remote = &p->raddr;
+       const struct in6_addr *local = &p->laddr;
+       unsigned int h = HASH_KEY(p->i_key);
+       int prio = 0;
+
+       if (!ipv6_addr_any(local))
+               prio |= 1;
+       if (!ipv6_addr_any(remote) && !ipv6_addr_is_multicast(remote)) {
+               prio |= 2;
+               h ^= HASH_ADDR(remote);
+       }
+
+       return &ign->tunnels[prio][h];
+}
+
+static inline struct ip6_tnl __rcu **ip6gre_bucket(struct ip6gre_net *ign,
+               const struct ip6_tnl *t)
+{
+       return __ip6gre_bucket(ign, &t->parms);
+}
+
+static void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t)
+{
+       struct ip6_tnl __rcu **tp = ip6gre_bucket(ign, t);
+
+       rcu_assign_pointer(t->next, rtnl_dereference(*tp));
+       rcu_assign_pointer(*tp, t);
+}
+
+static void ip6gre_tunnel_unlink(struct ip6gre_net *ign, struct ip6_tnl *t)
+{
+       struct ip6_tnl __rcu **tp;
+       struct ip6_tnl *iter;
+
+       for (tp = ip6gre_bucket(ign, t);
+            (iter = rtnl_dereference(*tp)) != NULL;
+            tp = &iter->next) {
+               if (t == iter) {
+                       rcu_assign_pointer(*tp, t->next);
+                       break;
+               }
+       }
+}
+
+static struct ip6_tnl *ip6gre_tunnel_find(struct net *net,
+                                          const struct __ip6_tnl_parm *parms,
+                                          int type)
+{
+       const struct in6_addr *remote = &parms->raddr;
+       const struct in6_addr *local = &parms->laddr;
+       __be32 key = parms->i_key;
+       int link = parms->link;
+       struct ip6_tnl *t;
+       struct ip6_tnl __rcu **tp;
+       struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+
+       for (tp = __ip6gre_bucket(ign, parms);
+            (t = rtnl_dereference(*tp)) != NULL;
+            tp = &t->next)
+               if (ipv6_addr_equal(local, &t->parms.laddr) &&
+                   ipv6_addr_equal(remote, &t->parms.raddr) &&
+                   key == t->parms.i_key &&
+                   link == t->parms.link &&
+                   type == t->dev->type)
+                       break;
+
+       return t;
+}
+
+static struct ip6_tnl *ip6gre_tunnel_locate(struct net *net,
+               const struct __ip6_tnl_parm *parms, int create)
+{
+       struct ip6_tnl *t, *nt;
+       struct net_device *dev;
+       char name[IFNAMSIZ];
+       struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+
+       t = ip6gre_tunnel_find(net, parms, ARPHRD_IP6GRE);
+       if (t || !create)
+               return t;
+
+       if (parms->name[0])
+               strlcpy(name, parms->name, IFNAMSIZ);
+       else
+               strcpy(name, "ip6gre%d");
+
+       dev = alloc_netdev(sizeof(*t), name, ip6gre_tunnel_setup);
+       if (!dev)
+               return NULL;
+
+       dev_net_set(dev, net);
+
+       nt = netdev_priv(dev);
+       nt->parms = *parms;
+       dev->rtnl_link_ops = &ip6gre_link_ops;
+
+       nt->dev = dev;
+       ip6gre_tnl_link_config(nt, 1);
+
+       if (register_netdevice(dev) < 0)
+               goto failed_free;
+
+       /* Can use a lockless transmit, unless we generate output sequences */
+       if (!(nt->parms.o_flags & GRE_SEQ))
+               dev->features |= NETIF_F_LLTX;
+
+       dev_hold(dev);
+       ip6gre_tunnel_link(ign, nt);
+       return nt;
+
+failed_free:
+       free_netdev(dev);
+       return NULL;
+}
+
+static void ip6gre_tunnel_uninit(struct net_device *dev)
+{
+       struct net *net = dev_net(dev);
+       struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+
+       ip6gre_tunnel_unlink(ign, netdev_priv(dev));
+       dev_put(dev);
+}
+
+
+static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+               u8 type, u8 code, int offset, __be32 info)
+{
+       const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)skb->data;
+       __be16 *p = (__be16 *)(ipv6h + 1);
+       int grehlen = sizeof(ipv6h) + 4;
+       struct ip6_tnl *t;
+       __be16 flags;
+
+       flags = p[0];
+       if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
+               if (flags&(GRE_VERSION|GRE_ROUTING))
+                       return;
+               if (flags&GRE_KEY) {
+                       grehlen += 4;
+                       if (flags&GRE_CSUM)
+                               grehlen += 4;
+               }
+       }
+
+       /* If only 8 bytes returned, keyed message will be dropped here */
+       if (skb_headlen(skb) < grehlen)
+               return;
+
+       rcu_read_lock();
+
+       t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr,
+                               flags & GRE_KEY ?
+                               *(((__be32 *)p) + (grehlen / 4) - 1) : 0,
+                               p[1]);
+       if (t == NULL)
+               goto out;
+
+       switch (type) {
+               __u32 teli;
+               struct ipv6_tlv_tnl_enc_lim *tel;
+               __u32 mtu;
+       case ICMPV6_DEST_UNREACH:
+               net_warn_ratelimited("%s: Path to destination invalid or inactive!\n",
+                                    t->parms.name);
+               break;
+       case ICMPV6_TIME_EXCEED:
+               if (code == ICMPV6_EXC_HOPLIMIT) {
+                       net_warn_ratelimited("%s: Too small hop limit or routing loop in tunnel!\n",
+                                            t->parms.name);
+               }
+               break;
+       case ICMPV6_PARAMPROB:
+               teli = 0;
+               if (code == ICMPV6_HDR_FIELD)
+                       teli = ip6_tnl_parse_tlv_enc_lim(skb, skb->data);
+
+               if (teli && teli == info - 2) {
+                       tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli];
+                       if (tel->encap_limit == 0) {
+                               net_warn_ratelimited("%s: Too small encapsulation limit or routing loop in tunnel!\n",
+                                                    t->parms.name);
+                       }
+               } else {
+                       net_warn_ratelimited("%s: Recipient unable to parse tunneled packet!\n",
+                                            t->parms.name);
+               }
+               break;
+       case ICMPV6_PKT_TOOBIG:
+               mtu = info - offset;
+               if (mtu < IPV6_MIN_MTU)
+                       mtu = IPV6_MIN_MTU;
+               t->dev->mtu = mtu;
+               break;
+       }
+
+       if (time_before(jiffies, t->err_time + IP6TUNNEL_ERR_TIMEO))
+               t->err_count++;
+       else
+               t->err_count = 1;
+       t->err_time = jiffies;
+out:
+       rcu_read_unlock();
+}
+
+static inline void ip6gre_ecn_decapsulate_ipv4(const struct ip6_tnl *t,
+               const struct ipv6hdr *ipv6h, struct sk_buff *skb)
+{
+       __u8 dsfield = ipv6_get_dsfield(ipv6h) & ~INET_ECN_MASK;
+
+       if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)
+               ipv4_change_dsfield(ip_hdr(skb), INET_ECN_MASK, dsfield);
+
+       if (INET_ECN_is_ce(dsfield))
+               IP_ECN_set_ce(ip_hdr(skb));
+}
+
+static inline void ip6gre_ecn_decapsulate_ipv6(const struct ip6_tnl *t,
+               const struct ipv6hdr *ipv6h, struct sk_buff *skb)
+{
+       if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)
+               ipv6_copy_dscp(ipv6_get_dsfield(ipv6h), ipv6_hdr(skb));
+
+       if (INET_ECN_is_ce(ipv6_get_dsfield(ipv6h)))
+               IP6_ECN_set_ce(ipv6_hdr(skb));
+}
+
+static int ip6gre_rcv(struct sk_buff *skb)
+{
+       const struct ipv6hdr *ipv6h;
+       u8     *h;
+       __be16    flags;
+       __sum16   csum = 0;
+       __be32 key = 0;
+       u32    seqno = 0;
+       struct ip6_tnl *tunnel;
+       int    offset = 4;
+       __be16 gre_proto;
+
+       if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
+               goto drop_nolock;
+
+       ipv6h = ipv6_hdr(skb);
+       h = skb->data;
+       flags = *(__be16 *)h;
+
+       if (flags&(GRE_CSUM|GRE_KEY|GRE_ROUTING|GRE_SEQ|GRE_VERSION)) {
+               /* - Version must be 0.
+                  - We do not support routing headers.
+                */
+               if (flags&(GRE_VERSION|GRE_ROUTING))
+                       goto drop_nolock;
+
+               if (flags&GRE_CSUM) {
+                       switch (skb->ip_summed) {
+                       case CHECKSUM_COMPLETE:
+                               csum = csum_fold(skb->csum);
+                               if (!csum)
+                                       break;
+                               /* fall through */
+                       case CHECKSUM_NONE:
+                               skb->csum = 0;
+                               csum = __skb_checksum_complete(skb);
+                               skb->ip_summed = CHECKSUM_COMPLETE;
+                       }
+                       offset += 4;
+               }
+               if (flags&GRE_KEY) {
+                       key = *(__be32 *)(h + offset);
+                       offset += 4;
+               }
+               if (flags&GRE_SEQ) {
+                       seqno = ntohl(*(__be32 *)(h + offset));
+                       offset += 4;
+               }
+       }
+
+       gre_proto = *(__be16 *)(h + 2);
+
+       rcu_read_lock();
+       tunnel = ip6gre_tunnel_lookup(skb->dev,
+                                         &ipv6h->saddr, &ipv6h->daddr, key,
+                                         gre_proto);
+       if (tunnel) {
+               struct pcpu_tstats *tstats;
+
+               if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
+                       goto drop;
+
+               if (!ip6_tnl_rcv_ctl(tunnel, &ipv6h->daddr, &ipv6h->saddr)) {
+                       tunnel->dev->stats.rx_dropped++;
+                       goto drop;
+               }
+
+               secpath_reset(skb);
+
+               skb->protocol = gre_proto;
+               /* WCCP version 1 and 2 protocol decoding.
+                * - Change protocol to IP
+                * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
+                */
+               if (flags == 0 && gre_proto == htons(ETH_P_WCCP)) {
+                       skb->protocol = htons(ETH_P_IP);
+                       if ((*(h + offset) & 0xF0) != 0x40)
+                               offset += 4;
+               }
+
+               skb->mac_header = skb->network_header;
+               __pskb_pull(skb, offset);
+               skb_postpull_rcsum(skb, skb_transport_header(skb), offset);
+               skb->pkt_type = PACKET_HOST;
+
+               if (((flags&GRE_CSUM) && csum) ||
+                   (!(flags&GRE_CSUM) && tunnel->parms.i_flags&GRE_CSUM)) {
+                       tunnel->dev->stats.rx_crc_errors++;
+                       tunnel->dev->stats.rx_errors++;
+                       goto drop;
+               }
+               if (tunnel->parms.i_flags&GRE_SEQ) {
+                       if (!(flags&GRE_SEQ) ||
+                           (tunnel->i_seqno &&
+                                       (s32)(seqno - tunnel->i_seqno) < 0)) {
+                               tunnel->dev->stats.rx_fifo_errors++;
+                               tunnel->dev->stats.rx_errors++;
+                               goto drop;
+                       }
+                       tunnel->i_seqno = seqno + 1;
+               }
+
+               /* Warning: All skb pointers will be invalidated! */
+               if (tunnel->dev->type == ARPHRD_ETHER) {
+                       if (!pskb_may_pull(skb, ETH_HLEN)) {
+                               tunnel->dev->stats.rx_length_errors++;
+                               tunnel->dev->stats.rx_errors++;
+                               goto drop;
+                       }
+
+                       ipv6h = ipv6_hdr(skb);
+                       skb->protocol = eth_type_trans(skb, tunnel->dev);
+                       skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
+               }
+
+               tstats = this_cpu_ptr(tunnel->dev->tstats);
+               u64_stats_update_begin(&tstats->syncp);
+               tstats->rx_packets++;
+               tstats->rx_bytes += skb->len;
+               u64_stats_update_end(&tstats->syncp);
+
+               __skb_tunnel_rx(skb, tunnel->dev);
+
+               skb_reset_network_header(skb);
+               if (skb->protocol == htons(ETH_P_IP))
+                       ip6gre_ecn_decapsulate_ipv4(tunnel, ipv6h, skb);
+               else if (skb->protocol == htons(ETH_P_IPV6))
+                       ip6gre_ecn_decapsulate_ipv6(tunnel, ipv6h, skb);
+
+               netif_rx(skb);
+
+               rcu_read_unlock();
+               return 0;
+       }
+       icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
+
+drop:
+       rcu_read_unlock();
+drop_nolock:
+       kfree_skb(skb);
+       return 0;
+}
+
+struct ipv6_tel_txoption {
+       struct ipv6_txoptions ops;
+       __u8 dst_opt[8];
+};
+
+static void init_tel_txopt(struct ipv6_tel_txoption *opt, __u8 encap_limit)
+{
+       memset(opt, 0, sizeof(struct ipv6_tel_txoption));
+
+       opt->dst_opt[2] = IPV6_TLV_TNL_ENCAP_LIMIT;
+       opt->dst_opt[3] = 1;
+       opt->dst_opt[4] = encap_limit;
+       opt->dst_opt[5] = IPV6_TLV_PADN;
+       opt->dst_opt[6] = 1;
+
+       opt->ops.dst0opt = (struct ipv6_opt_hdr *) opt->dst_opt;
+       opt->ops.opt_nflen = 8;
+}
+
+static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
+                        struct net_device *dev,
+                        __u8 dsfield,
+                        struct flowi6 *fl6,
+                        int encap_limit,
+                        __u32 *pmtu)
+{
+       struct net *net = dev_net(dev);
+       struct ip6_tnl *tunnel = netdev_priv(dev);
+       struct net_device *tdev;    /* Device to other host */
+       struct ipv6hdr  *ipv6h;     /* Our new IP header */
+       unsigned int max_headroom;  /* The extra header space needed */
+       int    gre_hlen;
+       struct ipv6_tel_txoption opt;
+       int    mtu;
+       struct dst_entry *dst = NULL, *ndst = NULL;
+       struct net_device_stats *stats = &tunnel->dev->stats;
+       int err = -1;
+       u8 proto;
+       int pkt_len;
+       struct sk_buff *new_skb;
+
+       if (dev->type == ARPHRD_ETHER)
+               IPCB(skb)->flags = 0;
+
+       if (dev->header_ops && dev->type == ARPHRD_IP6GRE) {
+               gre_hlen = 0;
+               ipv6h = (struct ipv6hdr *)skb->data;
+               fl6->daddr = ipv6h->daddr;
+       } else {
+               gre_hlen = tunnel->hlen;
+               fl6->daddr = tunnel->parms.raddr;
+       }
+
+       if (!fl6->flowi6_mark)
+               dst = ip6_tnl_dst_check(tunnel);
+
+       if (!dst) {
+               ndst = ip6_route_output(net, NULL, fl6);
+
+               if (ndst->error)
+                       goto tx_err_link_failure;
+               ndst = xfrm_lookup(net, ndst, flowi6_to_flowi(fl6), NULL, 0);
+               if (IS_ERR(ndst)) {
+                       err = PTR_ERR(ndst);
+                       ndst = NULL;
+                       goto tx_err_link_failure;
+               }
+               dst = ndst;
+       }
+
+       tdev = dst->dev;
+
+       if (tdev == dev) {
+               stats->collisions++;
+               net_warn_ratelimited("%s: Local routing loop detected!\n",
+                                    tunnel->parms.name);
+               goto tx_err_dst_release;
+       }
+
+       mtu = dst_mtu(dst) - sizeof(*ipv6h);
+       if (encap_limit >= 0) {
+               max_headroom += 8;
+               mtu -= 8;
+       }
+       if (mtu < IPV6_MIN_MTU)
+               mtu = IPV6_MIN_MTU;
+       if (skb_dst(skb))
+               skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
+       if (skb->len > mtu) {
+               *pmtu = mtu;
+               err = -EMSGSIZE;
+               goto tx_err_dst_release;
+       }
+
+       if (tunnel->err_count > 0) {
+               if (time_before(jiffies,
+                               tunnel->err_time + IP6TUNNEL_ERR_TIMEO)) {
+                       tunnel->err_count--;
+
+                       dst_link_failure(skb);
+               } else
+                       tunnel->err_count = 0;
+       }
+
+       max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen + dst->header_len;
+
+       if (skb_headroom(skb) < max_headroom || skb_shared(skb) ||
+           (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
+               new_skb = skb_realloc_headroom(skb, max_headroom);
+               if (max_headroom > dev->needed_headroom)
+                       dev->needed_headroom = max_headroom;
+               if (!new_skb)
+                       goto tx_err_dst_release;
+
+               if (skb->sk)
+                       skb_set_owner_w(new_skb, skb->sk);
+               consume_skb(skb);
+               skb = new_skb;
+       }
+
+       skb_dst_drop(skb);
+
+       if (fl6->flowi6_mark) {
+               skb_dst_set(skb, dst);
+               ndst = NULL;
+       } else {
+               skb_dst_set_noref(skb, dst);
+       }
+
+       skb->transport_header = skb->network_header;
+
+       proto = NEXTHDR_GRE;
+       if (encap_limit >= 0) {
+               init_tel_txopt(&opt, encap_limit);
+               ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL);
+       }
+
+       skb_push(skb, gre_hlen);
+       skb_reset_network_header(skb);
+
+       /*
+        *      Push down and install the IP header.
+        */
+       ipv6h = ipv6_hdr(skb);
+       *(__be32 *)ipv6h = fl6->flowlabel | htonl(0x60000000);
+       dsfield = INET_ECN_encapsulate(0, dsfield);
+       ipv6_change_dsfield(ipv6h, ~INET_ECN_MASK, dsfield);
+       ipv6h->hop_limit = tunnel->parms.hop_limit;
+       ipv6h->nexthdr = proto;
+       ipv6h->saddr = fl6->saddr;
+       ipv6h->daddr = fl6->daddr;
+
+       ((__be16 *)(ipv6h + 1))[0] = tunnel->parms.o_flags;
+       ((__be16 *)(ipv6h + 1))[1] = (dev->type == ARPHRD_ETHER) ?
+                                  htons(ETH_P_TEB) : skb->protocol;
+
+       if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) {
+               __be32 *ptr = (__be32 *)(((u8 *)ipv6h) + tunnel->hlen - 4);
+
+               if (tunnel->parms.o_flags&GRE_SEQ) {
+                       ++tunnel->o_seqno;
+                       *ptr = htonl(tunnel->o_seqno);
+                       ptr--;
+               }
+               if (tunnel->parms.o_flags&GRE_KEY) {
+                       *ptr = tunnel->parms.o_key;
+                       ptr--;
+               }
+               if (tunnel->parms.o_flags&GRE_CSUM) {
+                       *ptr = 0;
+                       *(__sum16 *)ptr = ip_compute_csum((void *)(ipv6h+1),
+                               skb->len - sizeof(struct ipv6hdr));
+               }
+       }
+
+       nf_reset(skb);
+       pkt_len = skb->len;
+       err = ip6_local_out(skb);
+
+       if (net_xmit_eval(err) == 0) {
+               struct pcpu_tstats *tstats = this_cpu_ptr(tunnel->dev->tstats);
+
+               tstats->tx_bytes += pkt_len;
+               tstats->tx_packets++;
+       } else {
+               stats->tx_errors++;
+               stats->tx_aborted_errors++;
+       }
+
+       if (ndst)
+               ip6_tnl_dst_store(tunnel, ndst);
+
+       return 0;
+tx_err_link_failure:
+       stats->tx_carrier_errors++;
+       dst_link_failure(skb);
+tx_err_dst_release:
+       dst_release(ndst);
+       return err;
+}
+
+static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ip6_tnl *t = netdev_priv(dev);
+       const struct iphdr  *iph = ip_hdr(skb);
+       int encap_limit = -1;
+       struct flowi6 fl6;
+       __u8 dsfield;
+       __u32 mtu;
+       int err;
+
+       if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
+               encap_limit = t->parms.encap_limit;
+
+       memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
+       fl6.flowi6_proto = IPPROTO_IPIP;
+
+       dsfield = ipv4_get_dsfield(iph);
+
+       if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
+               fl6.flowlabel |= htonl((__u32)iph->tos << IPV6_TCLASS_SHIFT)
+                                         & IPV6_TCLASS_MASK;
+       if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
+               fl6.flowi6_mark = skb->mark;
+
+       err = ip6gre_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu);
+       if (err != 0) {
+               /* XXX: send ICMP error even if DF is not set. */
+               if (err == -EMSGSIZE)
+                       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+                                 htonl(mtu));
+               return -1;
+       }
+
+       return 0;
+}
+
+static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ip6_tnl *t = netdev_priv(dev);
+       struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+       int encap_limit = -1;
+       __u16 offset;
+       struct flowi6 fl6;
+       __u8 dsfield;
+       __u32 mtu;
+       int err;
+
+       if (ipv6_addr_equal(&t->parms.raddr, &ipv6h->saddr))
+               return -1;
+
+       offset = ip6_tnl_parse_tlv_enc_lim(skb, skb_network_header(skb));
+       if (offset > 0) {
+               struct ipv6_tlv_tnl_enc_lim *tel;
+               tel = (struct ipv6_tlv_tnl_enc_lim *)&skb_network_header(skb)[offset];
+               if (tel->encap_limit == 0) {
+                       icmpv6_send(skb, ICMPV6_PARAMPROB,
+                                   ICMPV6_HDR_FIELD, offset + 2);
+                       return -1;
+               }
+               encap_limit = tel->encap_limit - 1;
+       } else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
+               encap_limit = t->parms.encap_limit;
+
+       memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
+       fl6.flowi6_proto = IPPROTO_IPV6;
+
+       dsfield = ipv6_get_dsfield(ipv6h);
+       if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
+               fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_TCLASS_MASK);
+       if (t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
+               fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_FLOWLABEL_MASK);
+       if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
+               fl6.flowi6_mark = skb->mark;
+
+       err = ip6gre_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu);
+       if (err != 0) {
+               if (err == -EMSGSIZE)
+                       icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * ip6_tnl_addr_conflict - compare packet addresses to tunnel's own
+ *   @t: the outgoing tunnel device
+ *   @hdr: IPv6 header from the incoming packet
+ *
+ * Description:
+ *   Avoid trivial tunneling loop by checking that tunnel exit-point
+ *   doesn't match source of incoming packet.
+ *
+ * Return:
+ *   1 if conflict,
+ *   0 else
+ **/
+
+static inline bool ip6gre_tnl_addr_conflict(const struct ip6_tnl *t,
+       const struct ipv6hdr *hdr)
+{
+       return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr);
+}
+
+static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ip6_tnl *t = netdev_priv(dev);
+       int encap_limit = -1;
+       struct flowi6 fl6;
+       __u32 mtu;
+       int err;
+
+       if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
+               encap_limit = t->parms.encap_limit;
+
+       memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
+       fl6.flowi6_proto = skb->protocol;
+
+       err = ip6gre_xmit2(skb, dev, 0, &fl6, encap_limit, &mtu);
+
+       return err;
+}
+
+static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
+       struct net_device *dev)
+{
+       struct ip6_tnl *t = netdev_priv(dev);
+       struct net_device_stats *stats = &t->dev->stats;
+       int ret;
+
+       if (!ip6_tnl_xmit_ctl(t))
+               return -1;
+
+       switch (skb->protocol) {
+       case htons(ETH_P_IP):
+               ret = ip6gre_xmit_ipv4(skb, dev);
+               break;
+       case htons(ETH_P_IPV6):
+               ret = ip6gre_xmit_ipv6(skb, dev);
+               break;
+       default:
+               ret = ip6gre_xmit_other(skb, dev);
+               break;
+       }
+
+       if (ret < 0)
+               goto tx_err;
+
+       return NETDEV_TX_OK;
+
+tx_err:
+       stats->tx_errors++;
+       stats->tx_dropped++;
+       kfree_skb(skb);
+       return NETDEV_TX_OK;
+}
+
+static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
+{
+       struct net_device *dev = t->dev;
+       struct __ip6_tnl_parm *p = &t->parms;
+       struct flowi6 *fl6 = &t->fl.u.ip6;
+       int addend = sizeof(struct ipv6hdr) + 4;
+
+       if (dev->type != ARPHRD_ETHER) {
+               memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr));
+               memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr));
+       }
+
+       /* Set up flowi template */
+       fl6->saddr = p->laddr;
+       fl6->daddr = p->raddr;
+       fl6->flowi6_oif = p->link;
+       fl6->flowlabel = 0;
+
+       if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS))
+               fl6->flowlabel |= IPV6_TCLASS_MASK & p->flowinfo;
+       if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL))
+               fl6->flowlabel |= IPV6_FLOWLABEL_MASK & p->flowinfo;
+
+       p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV|IP6_TNL_F_CAP_PER_PACKET);
+       p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr);
+
+       if (p->flags&IP6_TNL_F_CAP_XMIT &&
+                       p->flags&IP6_TNL_F_CAP_RCV && dev->type != ARPHRD_ETHER)
+               dev->flags |= IFF_POINTOPOINT;
+       else
+               dev->flags &= ~IFF_POINTOPOINT;
+
+       dev->iflink = p->link;
+
+       /* Precalculate GRE options length */
+       if (t->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) {
+               if (t->parms.o_flags&GRE_CSUM)
+                       addend += 4;
+               if (t->parms.o_flags&GRE_KEY)
+                       addend += 4;
+               if (t->parms.o_flags&GRE_SEQ)
+                       addend += 4;
+       }
+
+       if (p->flags & IP6_TNL_F_CAP_XMIT) {
+               int strict = (ipv6_addr_type(&p->raddr) &
+                             (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL));
+
+               struct rt6_info *rt = rt6_lookup(dev_net(dev),
+                                                &p->raddr, &p->laddr,
+                                                p->link, strict);
+
+               if (rt == NULL)
+                       return;
+
+               if (rt->dst.dev) {
+                       dev->hard_header_len = rt->dst.dev->hard_header_len + addend;
+
+                       if (set_mtu) {
+                               dev->mtu = rt->dst.dev->mtu - addend;
+                               if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
+                                       dev->mtu -= 8;
+
+                               if (dev->mtu < IPV6_MIN_MTU)
+                                       dev->mtu = IPV6_MIN_MTU;
+                       }
+               }
+               dst_release(&rt->dst);
+       }
+
+       t->hlen = addend;
+}
+
+static int ip6gre_tnl_change(struct ip6_tnl *t,
+       const struct __ip6_tnl_parm *p, int set_mtu)
+{
+       t->parms.laddr = p->laddr;
+       t->parms.raddr = p->raddr;
+       t->parms.flags = p->flags;
+       t->parms.hop_limit = p->hop_limit;
+       t->parms.encap_limit = p->encap_limit;
+       t->parms.flowinfo = p->flowinfo;
+       t->parms.link = p->link;
+       t->parms.proto = p->proto;
+       t->parms.i_key = p->i_key;
+       t->parms.o_key = p->o_key;
+       t->parms.i_flags = p->i_flags;
+       t->parms.o_flags = p->o_flags;
+       ip6_tnl_dst_reset(t);
+       ip6gre_tnl_link_config(t, set_mtu);
+       return 0;
+}
+
+static void ip6gre_tnl_parm_from_user(struct __ip6_tnl_parm *p,
+       const struct ip6_tnl_parm2 *u)
+{
+       p->laddr = u->laddr;
+       p->raddr = u->raddr;
+       p->flags = u->flags;
+       p->hop_limit = u->hop_limit;
+       p->encap_limit = u->encap_limit;
+       p->flowinfo = u->flowinfo;
+       p->link = u->link;
+       p->i_key = u->i_key;
+       p->o_key = u->o_key;
+       p->i_flags = u->i_flags;
+       p->o_flags = u->o_flags;
+       memcpy(p->name, u->name, sizeof(u->name));
+}
+
+static void ip6gre_tnl_parm_to_user(struct ip6_tnl_parm2 *u,
+       const struct __ip6_tnl_parm *p)
+{
+       u->proto = IPPROTO_GRE;
+       u->laddr = p->laddr;
+       u->raddr = p->raddr;
+       u->flags = p->flags;
+       u->hop_limit = p->hop_limit;
+       u->encap_limit = p->encap_limit;
+       u->flowinfo = p->flowinfo;
+       u->link = p->link;
+       u->i_key = p->i_key;
+       u->o_key = p->o_key;
+       u->i_flags = p->i_flags;
+       u->o_flags = p->o_flags;
+       memcpy(u->name, p->name, sizeof(u->name));
+}
+
+static int ip6gre_tunnel_ioctl(struct net_device *dev,
+       struct ifreq *ifr, int cmd)
+{
+       int err = 0;
+       struct ip6_tnl_parm2 p;
+       struct __ip6_tnl_parm p1;
+       struct ip6_tnl *t;
+       struct net *net = dev_net(dev);
+       struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+
+       switch (cmd) {
+       case SIOCGETTUNNEL:
+               t = NULL;
+               if (dev == ign->fb_tunnel_dev) {
+                       if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
+                               err = -EFAULT;
+                               break;
+                       }
+                       ip6gre_tnl_parm_from_user(&p1, &p);
+                       t = ip6gre_tunnel_locate(net, &p1, 0);
+               }
+               if (t == NULL)
+                       t = netdev_priv(dev);
+               ip6gre_tnl_parm_to_user(&p, &t->parms);
+               if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
+                       err = -EFAULT;
+               break;
+
+       case SIOCADDTUNNEL:
+       case SIOCCHGTUNNEL:
+               err = -EPERM;
+               if (!capable(CAP_NET_ADMIN))
+                       goto done;
+
+               err = -EFAULT;
+               if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
+                       goto done;
+
+               err = -EINVAL;
+               if ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING))
+                       goto done;
+
+               if (!(p.i_flags&GRE_KEY))
+                       p.i_key = 0;
+               if (!(p.o_flags&GRE_KEY))
+                       p.o_key = 0;
+
+               ip6gre_tnl_parm_from_user(&p1, &p);
+               t = ip6gre_tunnel_locate(net, &p1, cmd == SIOCADDTUNNEL);
+
+               if (dev != ign->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
+                       if (t != NULL) {
+                               if (t->dev != dev) {
+                                       err = -EEXIST;
+                                       break;
+                               }
+                       } else {
+                               t = netdev_priv(dev);
+
+                               ip6gre_tunnel_unlink(ign, t);
+                               synchronize_net();
+                               ip6gre_tnl_change(t, &p1, 1);
+                               ip6gre_tunnel_link(ign, t);
+                               netdev_state_change(dev);
+                       }
+               }
+
+               if (t) {
+                       err = 0;
+
+                       ip6gre_tnl_parm_to_user(&p, &t->parms);
+                       if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
+                               err = -EFAULT;
+               } else
+                       err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
+               break;
+
+       case SIOCDELTUNNEL:
+               err = -EPERM;
+               if (!capable(CAP_NET_ADMIN))
+                       goto done;
+
+               if (dev == ign->fb_tunnel_dev) {
+                       err = -EFAULT;
+                       if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
+                               goto done;
+                       err = -ENOENT;
+                       ip6gre_tnl_parm_from_user(&p1, &p);
+                       t = ip6gre_tunnel_locate(net, &p1, 0);
+                       if (t == NULL)
+                               goto done;
+                       err = -EPERM;
+                       if (t == netdev_priv(ign->fb_tunnel_dev))
+                               goto done;
+                       dev = t->dev;
+               }
+               unregister_netdevice(dev);
+               err = 0;
+               break;
+
+       default:
+               err = -EINVAL;
+       }
+
+done:
+       return err;
+}
+
+static int ip6gre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct ip6_tnl *tunnel = netdev_priv(dev);
+       if (new_mtu < 68 ||
+           new_mtu > 0xFFF8 - dev->hard_header_len - tunnel->hlen)
+               return -EINVAL;
+       dev->mtu = new_mtu;
+       return 0;
+}
+
+static int ip6gre_header(struct sk_buff *skb, struct net_device *dev,
+                       unsigned short type,
+                       const void *daddr, const void *saddr, unsigned int len)
+{
+       struct ip6_tnl *t = netdev_priv(dev);
+       struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb_push(skb, t->hlen);
+       __be16 *p = (__be16 *)(ipv6h+1);
+
+       *(__be32 *)ipv6h = t->fl.u.ip6.flowlabel | htonl(0x60000000);
+       ipv6h->hop_limit = t->parms.hop_limit;
+       ipv6h->nexthdr = NEXTHDR_GRE;
+       ipv6h->saddr = t->parms.laddr;
+       ipv6h->daddr = t->parms.raddr;
+
+       p[0]            = t->parms.o_flags;
+       p[1]            = htons(type);
+
+       /*
+        *      Set the source hardware address.
+        */
+
+       if (saddr)
+               memcpy(&ipv6h->saddr, saddr, sizeof(struct in6_addr));
+       if (daddr)
+               memcpy(&ipv6h->daddr, daddr, sizeof(struct in6_addr));
+       if (!ipv6_addr_any(&ipv6h->daddr))
+               return t->hlen;
+
+       return -t->hlen;
+}
+
+static int ip6gre_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+{
+       const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)skb_mac_header(skb);
+       memcpy(haddr, &ipv6h->saddr, sizeof(struct in6_addr));
+       return sizeof(struct in6_addr);
+}
+
+static const struct header_ops ip6gre_header_ops = {
+       .create = ip6gre_header,
+       .parse  = ip6gre_header_parse,
+};
+
+static const struct net_device_ops ip6gre_netdev_ops = {
+       .ndo_init               = ip6gre_tunnel_init,
+       .ndo_uninit             = ip6gre_tunnel_uninit,
+       .ndo_start_xmit         = ip6gre_tunnel_xmit,
+       .ndo_do_ioctl           = ip6gre_tunnel_ioctl,
+       .ndo_change_mtu         = ip6gre_tunnel_change_mtu,
+       .ndo_get_stats64        = ip6gre_get_stats64,
+};
+
+static void ip6gre_dev_free(struct net_device *dev)
+{
+       free_percpu(dev->tstats);
+       free_netdev(dev);
+}
+
+static void ip6gre_tunnel_setup(struct net_device *dev)
+{
+       struct ip6_tnl *t;
+
+       dev->netdev_ops = &ip6gre_netdev_ops;
+       dev->destructor = ip6gre_dev_free;
+
+       dev->type = ARPHRD_IP6GRE;
+       dev->hard_header_len = LL_MAX_HEADER + sizeof(struct ipv6hdr) + 4;
+       dev->mtu = ETH_DATA_LEN - sizeof(struct ipv6hdr) - 4;
+       t = netdev_priv(dev);
+       if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
+               dev->mtu -= 8;
+       dev->flags |= IFF_NOARP;
+       dev->iflink = 0;
+       dev->addr_len = sizeof(struct in6_addr);
+       dev->features |= NETIF_F_NETNS_LOCAL;
+       dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+}
+
+static int ip6gre_tunnel_init(struct net_device *dev)
+{
+       struct ip6_tnl *tunnel;
+
+       tunnel = netdev_priv(dev);
+
+       tunnel->dev = dev;
+       strcpy(tunnel->parms.name, dev->name);
+
+       memcpy(dev->dev_addr, &tunnel->parms.laddr, sizeof(struct in6_addr));
+       memcpy(dev->broadcast, &tunnel->parms.raddr, sizeof(struct in6_addr));
+
+       if (ipv6_addr_any(&tunnel->parms.raddr))
+               dev->header_ops = &ip6gre_header_ops;
+
+       dev->tstats = alloc_percpu(struct pcpu_tstats);
+       if (!dev->tstats)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void ip6gre_fb_tunnel_init(struct net_device *dev)
+{
+       struct ip6_tnl *tunnel = netdev_priv(dev);
+
+       tunnel->dev = dev;
+       strcpy(tunnel->parms.name, dev->name);
+
+       tunnel->hlen            = sizeof(struct ipv6hdr) + 4;
+
+       dev_hold(dev);
+}
+
+
+static struct inet6_protocol ip6gre_protocol __read_mostly = {
+       .handler     = ip6gre_rcv,
+       .err_handler = ip6gre_err,
+       .flags       = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
+};
+
+static void ip6gre_destroy_tunnels(struct ip6gre_net *ign,
+       struct list_head *head)
+{
+       int prio;
+
+       for (prio = 0; prio < 4; prio++) {
+               int h;
+               for (h = 0; h < HASH_SIZE; h++) {
+                       struct ip6_tnl *t;
+
+                       t = rtnl_dereference(ign->tunnels[prio][h]);
+
+                       while (t != NULL) {
+                               unregister_netdevice_queue(t->dev, head);
+                               t = rtnl_dereference(t->next);
+                       }
+               }
+       }
+}
+
+static int __net_init ip6gre_init_net(struct net *net)
+{
+       struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+       int err;
+
+       ign->fb_tunnel_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6gre0",
+                                          ip6gre_tunnel_setup);
+       if (!ign->fb_tunnel_dev) {
+               err = -ENOMEM;
+               goto err_alloc_dev;
+       }
+       dev_net_set(ign->fb_tunnel_dev, net);
+
+       ip6gre_fb_tunnel_init(ign->fb_tunnel_dev);
+       ign->fb_tunnel_dev->rtnl_link_ops = &ip6gre_link_ops;
+
+       err = register_netdev(ign->fb_tunnel_dev);
+       if (err)
+               goto err_reg_dev;
+
+       rcu_assign_pointer(ign->tunnels_wc[0],
+                          netdev_priv(ign->fb_tunnel_dev));
+       return 0;
+
+err_reg_dev:
+       ip6gre_dev_free(ign->fb_tunnel_dev);
+err_alloc_dev:
+       return err;
+}
+
+static void __net_exit ip6gre_exit_net(struct net *net)
+{
+       struct ip6gre_net *ign;
+       LIST_HEAD(list);
+
+       ign = net_generic(net, ip6gre_net_id);
+       rtnl_lock();
+       ip6gre_destroy_tunnels(ign, &list);
+       unregister_netdevice_many(&list);
+       rtnl_unlock();
+}
+
+static struct pernet_operations ip6gre_net_ops = {
+       .init = ip6gre_init_net,
+       .exit = ip6gre_exit_net,
+       .id   = &ip6gre_net_id,
+       .size = sizeof(struct ip6gre_net),
+};
+
+static int ip6gre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+       __be16 flags;
+
+       if (!data)
+               return 0;
+
+       flags = 0;
+       if (data[IFLA_GRE_IFLAGS])
+               flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]);
+       if (data[IFLA_GRE_OFLAGS])
+               flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]);
+       if (flags & (GRE_VERSION|GRE_ROUTING))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int ip6gre_tap_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+       struct in6_addr daddr;
+
+       if (tb[IFLA_ADDRESS]) {
+               if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
+                       return -EINVAL;
+               if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
+                       return -EADDRNOTAVAIL;
+       }
+
+       if (!data)
+               goto out;
+
+       if (data[IFLA_GRE_REMOTE]) {
+               nla_memcpy(&daddr, data[IFLA_GRE_REMOTE], sizeof(struct in6_addr));
+               if (ipv6_addr_any(&daddr))
+                       return -EINVAL;
+       }
+
+out:
+       return ip6gre_tunnel_validate(tb, data);
+}
+
+
+static void ip6gre_netlink_parms(struct nlattr *data[],
+                               struct __ip6_tnl_parm *parms)
+{
+       memset(parms, 0, sizeof(*parms));
+
+       if (!data)
+               return;
+
+       if (data[IFLA_GRE_LINK])
+               parms->link = nla_get_u32(data[IFLA_GRE_LINK]);
+
+       if (data[IFLA_GRE_IFLAGS])
+               parms->i_flags = nla_get_be16(data[IFLA_GRE_IFLAGS]);
+
+       if (data[IFLA_GRE_OFLAGS])
+               parms->o_flags = nla_get_be16(data[IFLA_GRE_OFLAGS]);
+
+       if (data[IFLA_GRE_IKEY])
+               parms->i_key = nla_get_be32(data[IFLA_GRE_IKEY]);
+
+       if (data[IFLA_GRE_OKEY])
+               parms->o_key = nla_get_be32(data[IFLA_GRE_OKEY]);
+
+       if (data[IFLA_GRE_LOCAL])
+               nla_memcpy(&parms->laddr, data[IFLA_GRE_LOCAL], sizeof(struct in6_addr));
+
+       if (data[IFLA_GRE_REMOTE])
+               nla_memcpy(&parms->raddr, data[IFLA_GRE_REMOTE], sizeof(struct in6_addr));
+
+       if (data[IFLA_GRE_TTL])
+               parms->hop_limit = nla_get_u8(data[IFLA_GRE_TTL]);
+
+       if (data[IFLA_GRE_ENCAP_LIMIT])
+               parms->encap_limit = nla_get_u8(data[IFLA_GRE_ENCAP_LIMIT]);
+
+       if (data[IFLA_GRE_FLOWINFO])
+               parms->flowinfo = nla_get_u32(data[IFLA_GRE_FLOWINFO]);
+
+       if (data[IFLA_GRE_FLAGS])
+               parms->flags = nla_get_u32(data[IFLA_GRE_FLAGS]);
+}
+
+static int ip6gre_tap_init(struct net_device *dev)
+{
+       struct ip6_tnl *tunnel;
+
+       tunnel = netdev_priv(dev);
+
+       tunnel->dev = dev;
+       strcpy(tunnel->parms.name, dev->name);
+
+       ip6gre_tnl_link_config(tunnel, 1);
+
+       dev->tstats = alloc_percpu(struct pcpu_tstats);
+       if (!dev->tstats)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static const struct net_device_ops ip6gre_tap_netdev_ops = {
+       .ndo_init = ip6gre_tap_init,
+       .ndo_uninit = ip6gre_tunnel_uninit,
+       .ndo_start_xmit = ip6gre_tunnel_xmit,
+       .ndo_set_mac_address = eth_mac_addr,
+       .ndo_validate_addr = eth_validate_addr,
+       .ndo_change_mtu = ip6gre_tunnel_change_mtu,
+       .ndo_get_stats64 = ip6gre_get_stats64,
+};
+
+static void ip6gre_tap_setup(struct net_device *dev)
+{
+
+       ether_setup(dev);
+
+       dev->netdev_ops = &ip6gre_tap_netdev_ops;
+       dev->destructor = ip6gre_dev_free;
+
+       dev->iflink = 0;
+       dev->features |= NETIF_F_NETNS_LOCAL;
+}
+
+static int ip6gre_newlink(struct net *src_net, struct net_device *dev,
+       struct nlattr *tb[], struct nlattr *data[])
+{
+       struct ip6_tnl *nt;
+       struct net *net = dev_net(dev);
+       struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+       int err;
+
+       nt = netdev_priv(dev);
+       ip6gre_netlink_parms(data, &nt->parms);
+
+       if (ip6gre_tunnel_find(net, &nt->parms, dev->type))
+               return -EEXIST;
+
+       if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS])
+               eth_hw_addr_random(dev);
+
+       nt->dev = dev;
+       ip6gre_tnl_link_config(nt, !tb[IFLA_MTU]);
+
+       /* Can use a lockless transmit, unless we generate output sequences */
+       if (!(nt->parms.o_flags & GRE_SEQ))
+               dev->features |= NETIF_F_LLTX;
+
+       err = register_netdevice(dev);
+       if (err)
+               goto out;
+
+       dev_hold(dev);
+       ip6gre_tunnel_link(ign, nt);
+
+out:
+       return err;
+}
+
+static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],
+                           struct nlattr *data[])
+{
+       struct ip6_tnl *t, *nt;
+       struct net *net = dev_net(dev);
+       struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+       struct __ip6_tnl_parm p;
+
+       if (dev == ign->fb_tunnel_dev)
+               return -EINVAL;
+
+       nt = netdev_priv(dev);
+       ip6gre_netlink_parms(data, &p);
+
+       t = ip6gre_tunnel_locate(net, &p, 0);
+
+       if (t) {
+               if (t->dev != dev)
+                       return -EEXIST;
+       } else {
+               t = nt;
+
+               ip6gre_tunnel_unlink(ign, t);
+               ip6gre_tnl_change(t, &p, !tb[IFLA_MTU]);
+               ip6gre_tunnel_link(ign, t);
+               netdev_state_change(dev);
+       }
+
+       return 0;
+}
+
+static size_t ip6gre_get_size(const struct net_device *dev)
+{
+       return
+               /* IFLA_GRE_LINK */
+               nla_total_size(4) +
+               /* IFLA_GRE_IFLAGS */
+               nla_total_size(2) +
+               /* IFLA_GRE_OFLAGS */
+               nla_total_size(2) +
+               /* IFLA_GRE_IKEY */
+               nla_total_size(4) +
+               /* IFLA_GRE_OKEY */
+               nla_total_size(4) +
+               /* IFLA_GRE_LOCAL */
+               nla_total_size(4) +
+               /* IFLA_GRE_REMOTE */
+               nla_total_size(4) +
+               /* IFLA_GRE_TTL */
+               nla_total_size(1) +
+               /* IFLA_GRE_TOS */
+               nla_total_size(1) +
+               /* IFLA_GRE_ENCAP_LIMIT */
+               nla_total_size(1) +
+               /* IFLA_GRE_FLOWINFO */
+               nla_total_size(4) +
+               /* IFLA_GRE_FLAGS */
+               nla_total_size(4) +
+               0;
+}
+
+static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+       struct ip6_tnl *t = netdev_priv(dev);
+       struct __ip6_tnl_parm *p = &t->parms;
+
+       if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) ||
+           nla_put_be16(skb, IFLA_GRE_IFLAGS, p->i_flags) ||
+           nla_put_be16(skb, IFLA_GRE_OFLAGS, p->o_flags) ||
+           nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) ||
+           nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) ||
+           nla_put(skb, IFLA_GRE_LOCAL, sizeof(struct in6_addr), &p->raddr) ||
+           nla_put(skb, IFLA_GRE_REMOTE, sizeof(struct in6_addr), &p->laddr) ||
+           nla_put_u8(skb, IFLA_GRE_TTL, p->hop_limit) ||
+           /*nla_put_u8(skb, IFLA_GRE_TOS, t->priority) ||*/
+           nla_put_u8(skb, IFLA_GRE_ENCAP_LIMIT, p->encap_limit) ||
+           nla_put_be32(skb, IFLA_GRE_FLOWINFO, p->flowinfo) ||
+           nla_put_u32(skb, IFLA_GRE_FLAGS, p->flags))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -EMSGSIZE;
+}
+
+static const struct nla_policy ip6gre_policy[IFLA_GRE_MAX + 1] = {
+       [IFLA_GRE_LINK]        = { .type = NLA_U32 },
+       [IFLA_GRE_IFLAGS]      = { .type = NLA_U16 },
+       [IFLA_GRE_OFLAGS]      = { .type = NLA_U16 },
+       [IFLA_GRE_IKEY]        = { .type = NLA_U32 },
+       [IFLA_GRE_OKEY]        = { .type = NLA_U32 },
+       [IFLA_GRE_LOCAL]       = { .len = FIELD_SIZEOF(struct ipv6hdr, saddr) },
+       [IFLA_GRE_REMOTE]      = { .len = FIELD_SIZEOF(struct ipv6hdr, daddr) },
+       [IFLA_GRE_TTL]         = { .type = NLA_U8 },
+       [IFLA_GRE_ENCAP_LIMIT] = { .type = NLA_U8 },
+       [IFLA_GRE_FLOWINFO]    = { .type = NLA_U32 },
+       [IFLA_GRE_FLAGS]       = { .type = NLA_U32 },
+};
+
+static struct rtnl_link_ops ip6gre_link_ops __read_mostly = {
+       .kind           = "ip6gre",
+       .maxtype        = IFLA_GRE_MAX,
+       .policy         = ip6gre_policy,
+       .priv_size      = sizeof(struct ip6_tnl),
+       .setup          = ip6gre_tunnel_setup,
+       .validate       = ip6gre_tunnel_validate,
+       .newlink        = ip6gre_newlink,
+       .changelink     = ip6gre_changelink,
+       .get_size       = ip6gre_get_size,
+       .fill_info      = ip6gre_fill_info,
+};
+
+static struct rtnl_link_ops ip6gre_tap_ops __read_mostly = {
+       .kind           = "ip6gretap",
+       .maxtype        = IFLA_GRE_MAX,
+       .policy         = ip6gre_policy,
+       .priv_size      = sizeof(struct ip6_tnl),
+       .setup          = ip6gre_tap_setup,
+       .validate       = ip6gre_tap_validate,
+       .newlink        = ip6gre_newlink,
+       .changelink     = ip6gre_changelink,
+       .get_size       = ip6gre_get_size,
+       .fill_info      = ip6gre_fill_info,
+};
+
+/*
+ *     And now the modules code and kernel interface.
+ */
+
+static int __init ip6gre_init(void)
+{
+       int err;
+
+       pr_info("GRE over IPv6 tunneling driver\n");
+
+       err = register_pernet_device(&ip6gre_net_ops);
+       if (err < 0)
+               return err;
+
+       err = inet6_add_protocol(&ip6gre_protocol, IPPROTO_GRE);
+       if (err < 0) {
+               pr_info("%s: can't add protocol\n", __func__);
+               goto add_proto_failed;
+       }
+
+       err = rtnl_link_register(&ip6gre_link_ops);
+       if (err < 0)
+               goto rtnl_link_failed;
+
+       err = rtnl_link_register(&ip6gre_tap_ops);
+       if (err < 0)
+               goto tap_ops_failed;
+
+out:
+       return err;
+
+tap_ops_failed:
+       rtnl_link_unregister(&ip6gre_link_ops);
+rtnl_link_failed:
+       inet6_del_protocol(&ip6gre_protocol, IPPROTO_GRE);
+add_proto_failed:
+       unregister_pernet_device(&ip6gre_net_ops);
+       goto out;
+}
+
+static void __exit ip6gre_fini(void)
+{
+       rtnl_link_unregister(&ip6gre_tap_ops);
+       rtnl_link_unregister(&ip6gre_link_ops);
+       inet6_del_protocol(&ip6gre_protocol, IPPROTO_GRE);
+       unregister_pernet_device(&ip6gre_net_ops);
+}
+
+module_init(ip6gre_init);
+module_exit(ip6gre_fini);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
+MODULE_DESCRIPTION("GRE over IPv6 tunneling device");
+MODULE_ALIAS_RTNL_LINK("ip6gre");
+MODULE_ALIAS_NETDEV("ip6gre0");
index 9a1d5fe6aef8f229e22c8b2e8f3cef1663be09ce..33d2a0e6712de084077727298d9e2353f39eb9c0 100644 (file)
@@ -126,7 +126,7 @@ static struct net_device_stats *ip6_get_stats(struct net_device *dev)
  * Locking : hash tables are protected by RCU and RTNL
  */
 
-static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t)
+struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t)
 {
        struct dst_entry *dst = t->dst_cache;
 
@@ -139,20 +139,23 @@ static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t)
 
        return dst;
 }
+EXPORT_SYMBOL_GPL(ip6_tnl_dst_check);
 
-static inline void ip6_tnl_dst_reset(struct ip6_tnl *t)
+void ip6_tnl_dst_reset(struct ip6_tnl *t)
 {
        dst_release(t->dst_cache);
        t->dst_cache = NULL;
 }
+EXPORT_SYMBOL_GPL(ip6_tnl_dst_reset);
 
-static inline void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst)
+void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst)
 {
        struct rt6_info *rt = (struct rt6_info *) dst;
        t->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0;
        dst_release(t->dst_cache);
        t->dst_cache = dst;
 }
+EXPORT_SYMBOL_GPL(ip6_tnl_dst_store);
 
 /**
  * ip6_tnl_lookup - fetch tunnel matching the end-point addresses
@@ -200,7 +203,7 @@ ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_
  **/
 
 static struct ip6_tnl __rcu **
-ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct ip6_tnl_parm *p)
+ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct __ip6_tnl_parm *p)
 {
        const struct in6_addr *remote = &p->raddr;
        const struct in6_addr *local = &p->laddr;
@@ -267,7 +270,7 @@ static void ip6_dev_free(struct net_device *dev)
  *   created tunnel or NULL
  **/
 
-static struct ip6_tnl *ip6_tnl_create(struct net *net, struct ip6_tnl_parm *p)
+static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p)
 {
        struct net_device *dev;
        struct ip6_tnl *t;
@@ -322,7 +325,7 @@ failed:
  **/
 
 static struct ip6_tnl *ip6_tnl_locate(struct net *net,
-               struct ip6_tnl_parm *p, int create)
+               struct __ip6_tnl_parm *p, int create)
 {
        const struct in6_addr *remote = &p->raddr;
        const struct in6_addr *local = &p->laddr;
@@ -374,8 +377,7 @@ ip6_tnl_dev_uninit(struct net_device *dev)
  *   else index to encapsulation limit
  **/
 
-static __u16
-parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw)
+__u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw)
 {
        const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) raw;
        __u8 nexthdr = ipv6h->nexthdr;
@@ -425,6 +427,7 @@ parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw)
        }
        return 0;
 }
+EXPORT_SYMBOL(ip6_tnl_parse_tlv_enc_lim);
 
 /**
  * ip6_tnl_err - tunnel error handler
@@ -480,7 +483,7 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt,
        case ICMPV6_PARAMPROB:
                teli = 0;
                if ((*code) == ICMPV6_HDR_FIELD)
-                       teli = parse_tlv_tnl_enc_lim(skb, skb->data);
+                       teli = ip6_tnl_parse_tlv_enc_lim(skb, skb->data);
 
                if (teli && teli == *info - 2) {
                        tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli];
@@ -693,11 +696,11 @@ static void ip6ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t,
                IP6_ECN_set_ce(ipv6_hdr(skb));
 }
 
-static __u32 ip6_tnl_get_cap(struct ip6_tnl *t,
+__u32 ip6_tnl_get_cap(struct ip6_tnl *t,
                             const struct in6_addr *laddr,
                             const struct in6_addr *raddr)
 {
-       struct ip6_tnl_parm *p = &t->parms;
+       struct __ip6_tnl_parm *p = &t->parms;
        int ltype = ipv6_addr_type(laddr);
        int rtype = ipv6_addr_type(raddr);
        __u32 flags = 0;
@@ -715,13 +718,14 @@ static __u32 ip6_tnl_get_cap(struct ip6_tnl *t,
        }
        return flags;
 }
+EXPORT_SYMBOL(ip6_tnl_get_cap);
 
 /* called with rcu_read_lock() */
-static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t,
+int ip6_tnl_rcv_ctl(struct ip6_tnl *t,
                                  const struct in6_addr *laddr,
                                  const struct in6_addr *raddr)
 {
-       struct ip6_tnl_parm *p = &t->parms;
+       struct __ip6_tnl_parm *p = &t->parms;
        int ret = 0;
        struct net *net = dev_net(t->dev);
 
@@ -740,6 +744,7 @@ static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t,
        }
        return ret;
 }
+EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl);
 
 /**
  * ip6_tnl_rcv - decapsulate IPv6 packet and retransmit it locally
@@ -859,9 +864,9 @@ ip6_tnl_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr)
        return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr);
 }
 
-static inline int ip6_tnl_xmit_ctl(struct ip6_tnl *t)
+int ip6_tnl_xmit_ctl(struct ip6_tnl *t)
 {
-       struct ip6_tnl_parm *p = &t->parms;
+       struct __ip6_tnl_parm *p = &t->parms;
        int ret = 0;
        struct net *net = dev_net(t->dev);
 
@@ -885,6 +890,8 @@ static inline int ip6_tnl_xmit_ctl(struct ip6_tnl *t)
        }
        return ret;
 }
+EXPORT_SYMBOL_GPL(ip6_tnl_xmit_ctl);
+
 /**
  * ip6_tnl_xmit2 - encapsulate packet and send
  *   @skb: the outgoing socket buffer
@@ -1085,7 +1092,7 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
            !ip6_tnl_xmit_ctl(t) || ip6_tnl_addr_conflict(t, ipv6h))
                return -1;
 
-       offset = parse_tlv_tnl_enc_lim(skb, skb_network_header(skb));
+       offset = ip6_tnl_parse_tlv_enc_lim(skb, skb_network_header(skb));
        if (offset > 0) {
                struct ipv6_tlv_tnl_enc_lim *tel;
                tel = (struct ipv6_tlv_tnl_enc_lim *)&skb_network_header(skb)[offset];
@@ -1152,7 +1159,7 @@ tx_err:
 static void ip6_tnl_link_config(struct ip6_tnl *t)
 {
        struct net_device *dev = t->dev;
-       struct ip6_tnl_parm *p = &t->parms;
+       struct __ip6_tnl_parm *p = &t->parms;
        struct flowi6 *fl6 = &t->fl.u.ip6;
 
        memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr));
@@ -1215,7 +1222,7 @@ static void ip6_tnl_link_config(struct ip6_tnl *t)
  **/
 
 static int
-ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p)
+ip6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p)
 {
        t->parms.laddr = p->laddr;
        t->parms.raddr = p->raddr;
@@ -1230,6 +1237,34 @@ ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p)
        return 0;
 }
 
+static void
+ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u)
+{
+       p->laddr = u->laddr;
+       p->raddr = u->raddr;
+       p->flags = u->flags;
+       p->hop_limit = u->hop_limit;
+       p->encap_limit = u->encap_limit;
+       p->flowinfo = u->flowinfo;
+       p->link = u->link;
+       p->proto = u->proto;
+       memcpy(p->name, u->name, sizeof(u->name));
+}
+
+static void
+ip6_tnl_parm_to_user(struct ip6_tnl_parm *u, const struct __ip6_tnl_parm *p)
+{
+       u->laddr = p->laddr;
+       u->raddr = p->raddr;
+       u->flags = p->flags;
+       u->hop_limit = p->hop_limit;
+       u->encap_limit = p->encap_limit;
+       u->flowinfo = p->flowinfo;
+       u->link = p->link;
+       u->proto = p->proto;
+       memcpy(u->name, p->name, sizeof(u->name));
+}
+
 /**
  * ip6_tnl_ioctl - configure ipv6 tunnels from userspace
  *   @dev: virtual device associated with tunnel
@@ -1263,6 +1298,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
        int err = 0;
        struct ip6_tnl_parm p;
+       struct __ip6_tnl_parm p1;
        struct ip6_tnl *t = NULL;
        struct net *net = dev_net(dev);
        struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
@@ -1274,11 +1310,12 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                                err = -EFAULT;
                                break;
                        }
-                       t = ip6_tnl_locate(net, &p, 0);
+                       ip6_tnl_parm_from_user(&p1, &p);
+                       t = ip6_tnl_locate(net, &p1, 0);
                }
                if (t == NULL)
                        t = netdev_priv(dev);
-               memcpy(&p, &t->parms, sizeof (p));
+               ip6_tnl_parm_to_user(&p, &t->parms);
                if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) {
                        err = -EFAULT;
                }
@@ -1295,7 +1332,8 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                if (p.proto != IPPROTO_IPV6 && p.proto != IPPROTO_IPIP &&
                    p.proto != 0)
                        break;
-               t = ip6_tnl_locate(net, &p, cmd == SIOCADDTUNNEL);
+               ip6_tnl_parm_from_user(&p1, &p);
+               t = ip6_tnl_locate(net, &p1, cmd == SIOCADDTUNNEL);
                if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) {
                        if (t != NULL) {
                                if (t->dev != dev) {
@@ -1307,13 +1345,14 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 
                        ip6_tnl_unlink(ip6n, t);
                        synchronize_net();
-                       err = ip6_tnl_change(t, &p);
+                       err = ip6_tnl_change(t, &p1);
                        ip6_tnl_link(ip6n, t);
                        netdev_state_change(dev);
                }
                if (t) {
                        err = 0;
-                       if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof (p)))
+                       ip6_tnl_parm_to_user(&p, &t->parms);
+                       if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
                                err = -EFAULT;
 
                } else
@@ -1329,7 +1368,9 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                        if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p)))
                                break;
                        err = -ENOENT;
-                       if ((t = ip6_tnl_locate(net, &p, 0)) == NULL)
+                       ip6_tnl_parm_from_user(&p1, &p);
+                       t = ip6_tnl_locate(net, &p1, 0);
+                       if (t == NULL)
                                break;
                        err = -EPERM;
                        if (t->dev == ip6n->fb_tnl_dev)
index 8e80fd279100d7d18d111eff1be73f10fdd6eeaf..0ddf2d132e7f0840bcf327d4d0149fe2136fe943 100644 (file)
@@ -965,7 +965,7 @@ struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
 {
        int flags = 0;
 
-       fl6->flowi6_iif = net->loopback_dev->ifindex;
+       fl6->flowi6_iif = LOOPBACK_IFINDEX;
 
        if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
                flags |= RT6_LOOKUP_F_IFACE;
index 221224e72507cf462021c8ff330e355f067e7efb..aa41b0e6b1637b2d3e0ed5a7febae50e99e5b979 100644 (file)
@@ -1299,7 +1299,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        /* Clone pktoptions received with SYN */
        newnp->pktoptions = NULL;
        if (treq->pktopts != NULL) {
-               newnp->pktoptions = skb_clone(treq->pktopts, GFP_ATOMIC);
+               newnp->pktoptions = skb_clone(treq->pktopts,
+                                             sk_gfp_atomic(sk, GFP_ATOMIC));
                consume_skb(treq->pktopts);
                treq->pktopts = NULL;
                if (newnp->pktoptions)
@@ -1349,7 +1350,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                 * across. Shucks.
                 */
                tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newnp->daddr,
-                              AF_INET6, key->key, key->keylen, GFP_ATOMIC);
+                              AF_INET6, key->key, key->keylen,
+                              sk_gfp_atomic(sk, GFP_ATOMIC));
        }
 #endif
 
@@ -1442,7 +1444,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
                                               --ANK (980728)
         */
        if (np->rxopt.all)
-               opt_skb = skb_clone(skb, GFP_ATOMIC);
+               opt_skb = skb_clone(skb, sk_gfp_atomic(sk, GFP_ATOMIC));
 
        if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
                sock_rps_save_rxhash(sk, skb);
@@ -1873,7 +1875,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
                   tp->write_seq-tp->snd_una,
                   (sp->sk_state == TCP_LISTEN) ? sp->sk_ack_backlog : (tp->rcv_nxt - tp->copied_seq),
                   timer_active,
-                  jiffies_to_clock_t(timer_expires - jiffies),
+                  jiffies_delta_to_clock_t(timer_expires - jiffies),
                   icsk->icsk_retransmits,
                   sock_i_uid(sp),
                   icsk->icsk_probes_out,
@@ -1893,10 +1895,7 @@ static void get_timewait6_sock(struct seq_file *seq,
        const struct in6_addr *dest, *src;
        __u16 destp, srcp;
        const struct inet6_timewait_sock *tw6 = inet6_twsk((struct sock *)tw);
-       int ttd = tw->tw_ttd - jiffies;
-
-       if (ttd < 0)
-               ttd = 0;
+       long delta = tw->tw_ttd - jiffies;
 
        dest = &tw6->tw_v6_daddr;
        src  = &tw6->tw_v6_rcv_saddr;
@@ -1912,7 +1911,7 @@ static void get_timewait6_sock(struct seq_file *seq,
                   dest->s6_addr32[0], dest->s6_addr32[1],
                   dest->s6_addr32[2], dest->s6_addr32[3], destp,
                   tw->tw_substate, 0, 0,
-                  3, jiffies_to_clock_t(ttd), 0, 0, 0, 0,
+                  3, jiffies_delta_to_clock_t(delta), 0, 0, 0, 0,
                   atomic_read(&tw->tw_refcnt), tw);
 }
 
@@ -2015,7 +2014,7 @@ struct proto tcpv6_prot = {
        .compat_setsockopt      = compat_tcp_setsockopt,
        .compat_getsockopt      = compat_tcp_getsockopt,
 #endif
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
        .proto_cgroup           = tcp_proto_cgroup,
 #endif
 };
index 856dcf49ce751e3a59b9a8d43fe243317bd87703..0e2f83e71277f37176226c7c41e5fd03a9cb8abb 100644 (file)
@@ -627,6 +627,7 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
 
        del_timer_sync(&sdata->u.mesh.housekeeping_timer);
        del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
+       del_timer_sync(&sdata->u.mesh.mesh_path_timer);
        /*
         * If the timer fired while we waited for it, it will have
         * requeued the work. Now the work will be running again
@@ -639,6 +640,8 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
        local->fif_other_bss--;
        atomic_dec(&local->iff_allmultis);
        ieee80211_configure_filter(local);
+
+       sdata->u.mesh.timers_running = 0;
 }
 
 static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
index cef0c9e79aba5ff4657938478dea6e829e10b164..a4a5acdbaa4dd3ac5e2fb8c5f0ff1d1b97553d6a 100644 (file)
@@ -1430,6 +1430,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        del_timer_sync(&sdata->u.mgd.bcn_mon_timer);
        del_timer_sync(&sdata->u.mgd.timer);
        del_timer_sync(&sdata->u.mgd.chswitch_timer);
+
+       sdata->u.mgd.timers_running = 0;
 }
 
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
index bcaee5d1283915efdbc8c63f4365c7ed42d486dc..839dd9737989ec78bbb979c814953b1bdd0187a8 100644 (file)
@@ -299,7 +299,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
        if (local->scan_req != local->int_scan_req)
                cfg80211_scan_done(local->scan_req, aborted);
        local->scan_req = NULL;
-       local->scan_sdata = NULL;
+       rcu_assign_pointer(local->scan_sdata, NULL);
 
        local->scanning = 0;
        local->scan_channel = NULL;
@@ -984,7 +984,6 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
                        kfree(local->sched_scan_ies.ie[i]);
 
                drv_sched_scan_stop(local, sdata);
-               rcu_assign_pointer(local->sched_scan_sdata, NULL);
        }
 out:
        mutex_unlock(&local->mtx);
index 0060e3b396b7b41fb0a5791966a39222cce8c04d..cc55b35f80e5acd045c37fc20bb37d98ba3d258c 100644 (file)
@@ -14,3 +14,11 @@ config PACKET
          be called af_packet.
 
          If unsure, say Y.
+
+config PACKET_DIAG
+       tristate "Packet: sockets monitoring interface"
+       depends on PACKET
+       default n
+       ---help---
+         Support for PF_PACKET sockets monitoring interface used by the ss tool.
+         If unsure, say Y.
index 81183eabfdec5cee58148d85df0877fc0e3a3d45..9df61347a3c3e98938c7b11a83195a22eb5d755c 100644 (file)
@@ -3,3 +3,5 @@
 #
 
 obj-$(CONFIG_PACKET) += af_packet.o
+obj-$(CONFIG_PACKET_DIAG) += af_packet_diag.o
+af_packet_diag-y += diag.o
index ceaca7c134a011b659bc439dff6de58269b532b4..8a1605ae40299df7a52ffdca96b9d429d9a3f9bd 100644 (file)
@@ -93,6 +93,8 @@
 #include <net/inet_common.h>
 #endif
 
+#include "internal.h"
+
 /*
    Assumptions:
    - if device has no dev->hard_header routine, it adds and removes ll header
@@ -146,14 +148,6 @@ dev->hard_header == NULL (ll header is added by device, we cannot control it)
 
 /* Private packet socket structures. */
 
-struct packet_mclist {
-       struct packet_mclist    *next;
-       int                     ifindex;
-       int                     count;
-       unsigned short          type;
-       unsigned short          alen;
-       unsigned char           addr[MAX_ADDR_LEN];
-};
 /* identical to struct packet_mreq except it has
  * a longer address field.
  */
@@ -175,63 +169,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
 #define BLK_PLUS_PRIV(sz_of_priv) \
        (BLK_HDR_LEN + ALIGN((sz_of_priv), V3_ALIGNMENT))
 
-/* kbdq - kernel block descriptor queue */
-struct tpacket_kbdq_core {
-       struct pgv      *pkbdq;
-       unsigned int    feature_req_word;
-       unsigned int    hdrlen;
-       unsigned char   reset_pending_on_curr_blk;
-       unsigned char   delete_blk_timer;
-       unsigned short  kactive_blk_num;
-       unsigned short  blk_sizeof_priv;
-
-       /* last_kactive_blk_num:
-        * trick to see if user-space has caught up
-        * in order to avoid refreshing timer when every single pkt arrives.
-        */
-       unsigned short  last_kactive_blk_num;
-
-       char            *pkblk_start;
-       char            *pkblk_end;
-       int             kblk_size;
-       unsigned int    knum_blocks;
-       uint64_t        knxt_seq_num;
-       char            *prev;
-       char            *nxt_offset;
-       struct sk_buff  *skb;
-
-       atomic_t        blk_fill_in_prog;
-
-       /* Default is set to 8ms */
-#define DEFAULT_PRB_RETIRE_TOV (8)
-
-       unsigned short  retire_blk_tov;
-       unsigned short  version;
-       unsigned long   tov_in_jiffies;
-
-       /* timer to retire an outstanding block */
-       struct timer_list retire_blk_timer;
-};
-
 #define PGV_FROM_VMALLOC 1
-struct pgv {
-       char *buffer;
-};
-
-struct packet_ring_buffer {
-       struct pgv              *pg_vec;
-       unsigned int            head;
-       unsigned int            frames_per_block;
-       unsigned int            frame_size;
-       unsigned int            frame_max;
-
-       unsigned int            pg_vec_order;
-       unsigned int            pg_vec_pages;
-       unsigned int            pg_vec_len;
-
-       struct tpacket_kbdq_core        prb_bdqc;
-       atomic_t                pending;
-};
 
 #define BLOCK_STATUS(x)        ((x)->hdr.bh1.block_status)
 #define BLOCK_NUM_PKTS(x)      ((x)->hdr.bh1.num_pkts)
@@ -269,34 +207,6 @@ static void prb_fill_vlan_info(struct tpacket_kbdq_core *,
                struct tpacket3_hdr *);
 static void packet_flush_mclist(struct sock *sk);
 
-struct packet_fanout;
-struct packet_sock {
-       /* struct sock has to be the first member of packet_sock */
-       struct sock             sk;
-       struct packet_fanout    *fanout;
-       struct tpacket_stats    stats;
-       union  tpacket_stats_u  stats_u;
-       struct packet_ring_buffer       rx_ring;
-       struct packet_ring_buffer       tx_ring;
-       int                     copy_thresh;
-       spinlock_t              bind_lock;
-       struct mutex            pg_vec_lock;
-       unsigned int            running:1,      /* prot_hook is attached*/
-                               auxdata:1,
-                               origdev:1,
-                               has_vnet_hdr:1;
-       int                     ifindex;        /* bound device         */
-       __be16                  num;
-       struct packet_mclist    *mclist;
-       atomic_t                mapped;
-       enum tpacket_versions   tp_version;
-       unsigned int            tp_hdrlen;
-       unsigned int            tp_reserve;
-       unsigned int            tp_loss:1;
-       unsigned int            tp_tstamp;
-       struct packet_type      prot_hook ____cacheline_aligned_in_smp;
-};
-
 #define PACKET_FANOUT_MAX      256
 
 struct packet_fanout {
@@ -334,11 +244,6 @@ struct packet_skb_cb {
        (((x)->kactive_blk_num < ((x)->knum_blocks-1)) ? \
        ((x)->kactive_blk_num+1) : 0)
 
-static struct packet_sock *pkt_sk(struct sock *sk)
-{
-       return (struct packet_sock *)sk;
-}
-
 static void __fanout_unlink(struct sock *sk, struct packet_sock *po);
 static void __fanout_link(struct sock *sk, struct packet_sock *po);
 
diff --git a/net/packet/diag.c b/net/packet/diag.c
new file mode 100644 (file)
index 0000000..3dda4ec
--- /dev/null
@@ -0,0 +1,176 @@
+#include <linux/module.h>
+#include <linux/sock_diag.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/packet_diag.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "internal.h"
+
+static int pdiag_put_info(const struct packet_sock *po, struct sk_buff *nlskb)
+{
+       struct packet_diag_info pinfo;
+
+       pinfo.pdi_index = po->ifindex;
+       pinfo.pdi_version = po->tp_version;
+       pinfo.pdi_reserve = po->tp_reserve;
+       pinfo.pdi_copy_thresh = po->copy_thresh;
+       pinfo.pdi_tstamp = po->tp_tstamp;
+
+       pinfo.pdi_flags = 0;
+       if (po->running)
+               pinfo.pdi_flags |= PDI_RUNNING;
+       if (po->auxdata)
+               pinfo.pdi_flags |= PDI_AUXDATA;
+       if (po->origdev)
+               pinfo.pdi_flags |= PDI_ORIGDEV;
+       if (po->has_vnet_hdr)
+               pinfo.pdi_flags |= PDI_VNETHDR;
+       if (po->tp_loss)
+               pinfo.pdi_flags |= PDI_LOSS;
+
+       return nla_put(nlskb, PACKET_DIAG_INFO, sizeof(pinfo), &pinfo);
+}
+
+static int pdiag_put_mclist(const struct packet_sock *po, struct sk_buff *nlskb)
+{
+       struct nlattr *mca;
+       struct packet_mclist *ml;
+
+       mca = nla_nest_start(nlskb, PACKET_DIAG_MCLIST);
+       if (!mca)
+               return -EMSGSIZE;
+
+       rtnl_lock();
+       for (ml = po->mclist; ml; ml = ml->next) {
+               struct packet_diag_mclist *dml;
+
+               dml = nla_reserve_nohdr(nlskb, sizeof(*dml));
+               if (!dml) {
+                       rtnl_unlock();
+                       nla_nest_cancel(nlskb, mca);
+                       return -EMSGSIZE;
+               }
+
+               dml->pdmc_index = ml->ifindex;
+               dml->pdmc_type = ml->type;
+               dml->pdmc_alen = ml->alen;
+               dml->pdmc_count = ml->count;
+               BUILD_BUG_ON(sizeof(dml->pdmc_addr) != sizeof(ml->addr));
+               memcpy(dml->pdmc_addr, ml->addr, sizeof(ml->addr));
+       }
+
+       rtnl_unlock();
+       nla_nest_end(nlskb, mca);
+
+       return 0;
+}
+
+static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct packet_diag_req *req,
+               u32 pid, u32 seq, u32 flags, int sk_ino)
+{
+       struct nlmsghdr *nlh;
+       struct packet_diag_msg *rp;
+       const struct packet_sock *po = pkt_sk(sk);
+
+       nlh = nlmsg_put(skb, pid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rp), flags);
+       if (!nlh)
+               return -EMSGSIZE;
+
+       rp = nlmsg_data(nlh);
+       rp->pdiag_family = AF_PACKET;
+       rp->pdiag_type = sk->sk_type;
+       rp->pdiag_num = ntohs(po->num);
+       rp->pdiag_ino = sk_ino;
+       sock_diag_save_cookie(sk, rp->pdiag_cookie);
+
+       if ((req->pdiag_show & PACKET_SHOW_INFO) &&
+                       pdiag_put_info(po, skb))
+               goto out_nlmsg_trim;
+
+       if ((req->pdiag_show & PACKET_SHOW_MCLIST) &&
+                       pdiag_put_mclist(po, skb))
+               goto out_nlmsg_trim;
+
+       return nlmsg_end(skb, nlh);
+
+out_nlmsg_trim:
+       nlmsg_cancel(skb, nlh);
+       return -EMSGSIZE;
+}
+
+static int packet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       int num = 0, s_num = cb->args[0];
+       struct packet_diag_req *req;
+       struct net *net;
+       struct sock *sk;
+       struct hlist_node *node;
+
+       net = sock_net(skb->sk);
+       req = nlmsg_data(cb->nlh);
+
+       rcu_read_lock();
+       sk_for_each_rcu(sk, node, &net->packet.sklist) {
+               if (!net_eq(sock_net(sk), net))
+                       continue;
+               if (num < s_num)
+                       goto next;
+
+               if (sk_diag_fill(sk, skb, req, NETLINK_CB(cb->skb).pid,
+                                       cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                                       sock_i_ino(sk)) < 0)
+                       goto done;
+next:
+               num++;
+       }
+done:
+       rcu_read_unlock();
+       cb->args[0] = num;
+
+       return skb->len;
+}
+
+static int packet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
+{
+       int hdrlen = sizeof(struct packet_diag_req);
+       struct net *net = sock_net(skb->sk);
+       struct packet_diag_req *req;
+
+       if (nlmsg_len(h) < hdrlen)
+               return -EINVAL;
+
+       req = nlmsg_data(h);
+       /* Make it possible to support protocol filtering later */
+       if (req->sdiag_protocol)
+               return -EINVAL;
+
+       if (h->nlmsg_flags & NLM_F_DUMP) {
+               struct netlink_dump_control c = {
+                       .dump = packet_diag_dump,
+               };
+               return netlink_dump_start(net->diag_nlsk, skb, h, &c);
+       } else
+               return -EOPNOTSUPP;
+}
+
+static const struct sock_diag_handler packet_diag_handler = {
+       .family = AF_PACKET,
+       .dump = packet_diag_handler_dump,
+};
+
+static int __init packet_diag_init(void)
+{
+       return sock_diag_register(&packet_diag_handler);
+}
+
+static void __exit packet_diag_exit(void)
+{
+       sock_diag_unregister(&packet_diag_handler);
+}
+
+module_init(packet_diag_init);
+module_exit(packet_diag_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 17 /* AF_PACKET */);
diff --git a/net/packet/internal.h b/net/packet/internal.h
new file mode 100644 (file)
index 0000000..2c5fca2
--- /dev/null
@@ -0,0 +1,103 @@
+#ifndef __PACKET_INTERNAL_H__
+#define __PACKET_INTERNAL_H__
+
+struct packet_mclist {
+       struct packet_mclist    *next;
+       int                     ifindex;
+       int                     count;
+       unsigned short          type;
+       unsigned short          alen;
+       unsigned char           addr[MAX_ADDR_LEN];
+};
+
+/* kbdq - kernel block descriptor queue */
+struct tpacket_kbdq_core {
+       struct pgv      *pkbdq;
+       unsigned int    feature_req_word;
+       unsigned int    hdrlen;
+       unsigned char   reset_pending_on_curr_blk;
+       unsigned char   delete_blk_timer;
+       unsigned short  kactive_blk_num;
+       unsigned short  blk_sizeof_priv;
+
+       /* last_kactive_blk_num:
+        * trick to see if user-space has caught up
+        * in order to avoid refreshing timer when every single pkt arrives.
+        */
+       unsigned short  last_kactive_blk_num;
+
+       char            *pkblk_start;
+       char            *pkblk_end;
+       int             kblk_size;
+       unsigned int    knum_blocks;
+       uint64_t        knxt_seq_num;
+       char            *prev;
+       char            *nxt_offset;
+       struct sk_buff  *skb;
+
+       atomic_t        blk_fill_in_prog;
+
+       /* Default is set to 8ms */
+#define DEFAULT_PRB_RETIRE_TOV (8)
+
+       unsigned short  retire_blk_tov;
+       unsigned short  version;
+       unsigned long   tov_in_jiffies;
+
+       /* timer to retire an outstanding block */
+       struct timer_list retire_blk_timer;
+};
+
+struct pgv {
+       char *buffer;
+};
+
+struct packet_ring_buffer {
+       struct pgv              *pg_vec;
+       unsigned int            head;
+       unsigned int            frames_per_block;
+       unsigned int            frame_size;
+       unsigned int            frame_max;
+
+       unsigned int            pg_vec_order;
+       unsigned int            pg_vec_pages;
+       unsigned int            pg_vec_len;
+
+       struct tpacket_kbdq_core        prb_bdqc;
+       atomic_t                pending;
+};
+
+struct packet_fanout;
+struct packet_sock {
+       /* struct sock has to be the first member of packet_sock */
+       struct sock             sk;
+       struct packet_fanout    *fanout;
+       struct tpacket_stats    stats;
+       union  tpacket_stats_u  stats_u;
+       struct packet_ring_buffer       rx_ring;
+       struct packet_ring_buffer       tx_ring;
+       int                     copy_thresh;
+       spinlock_t              bind_lock;
+       struct mutex            pg_vec_lock;
+       unsigned int            running:1,      /* prot_hook is attached*/
+                               auxdata:1,
+                               origdev:1,
+                               has_vnet_hdr:1;
+       int                     ifindex;        /* bound device         */
+       __be16                  num;
+       struct packet_mclist    *mclist;
+       atomic_t                mapped;
+       enum tpacket_versions   tp_version;
+       unsigned int            tp_hdrlen;
+       unsigned int            tp_reserve;
+       unsigned int            tp_loss:1;
+       unsigned int            tp_tstamp;
+       struct packet_type      prot_hook ____cacheline_aligned_in_smp;
+};
+
+static struct packet_sock *pkt_sk(struct sock *sk)
+{
+       return (struct packet_sock *)sk;
+}
+
+#endif
index f10fb8256442014afbba6b85a23a3544ee0ec9e4..05d60859d8e3d1c2eb700ff7d1d63d81d9f98894 100644 (file)
@@ -67,6 +67,9 @@ static int tcf_gact_init(struct nlattr *nla, struct nlattr *est,
        struct tcf_common *pc;
        int ret = 0;
        int err;
+#ifdef CONFIG_GACT_PROB
+       struct tc_gact_p *p_parm = NULL;
+#endif
 
        if (nla == NULL)
                return -EINVAL;
@@ -82,6 +85,12 @@ static int tcf_gact_init(struct nlattr *nla, struct nlattr *est,
 #ifndef CONFIG_GACT_PROB
        if (tb[TCA_GACT_PROB] != NULL)
                return -EOPNOTSUPP;
+#else
+       if (tb[TCA_GACT_PROB]) {
+               p_parm = nla_data(tb[TCA_GACT_PROB]);
+               if (p_parm->ptype >= MAX_RAND)
+                       return -EINVAL;
+       }
 #endif
 
        pc = tcf_hash_check(parm->index, a, bind, &gact_hash_info);
@@ -103,8 +112,7 @@ static int tcf_gact_init(struct nlattr *nla, struct nlattr *est,
        spin_lock_bh(&gact->tcf_lock);
        gact->tcf_action = parm->action;
 #ifdef CONFIG_GACT_PROB
-       if (tb[TCA_GACT_PROB] != NULL) {
-               struct tc_gact_p *p_parm = nla_data(tb[TCA_GACT_PROB]);
+       if (p_parm) {
                gact->tcfg_paction = p_parm->paction;
                gact->tcfg_pval    = p_parm->pval;
                gact->tcfg_ptype   = p_parm->ptype;
@@ -133,7 +141,7 @@ static int tcf_gact(struct sk_buff *skb, const struct tc_action *a,
 
        spin_lock(&gact->tcf_lock);
 #ifdef CONFIG_GACT_PROB
-       if (gact->tcfg_ptype && gact_rand[gact->tcfg_ptype] != NULL)
+       if (gact->tcfg_ptype)
                action = gact_rand[gact->tcfg_ptype](gact);
        else
                action = gact->tcf_action;
index 511323e89cecb221f9650d9d35e13c20d5d54b29..6c4d5fe53ce80ab534b34431f246a0ec4b48e055 100644 (file)
@@ -324,24 +324,6 @@ void netif_carrier_off(struct net_device *dev)
 }
 EXPORT_SYMBOL(netif_carrier_off);
 
-/**
- *     netif_notify_peers - notify network peers about existence of @dev
- *     @dev: network device
- *
- * Generate traffic such that interested network peers are aware of
- * @dev, such as by generating a gratuitous ARP. This may be used when
- * a device wants to inform the rest of the network about some sort of
- * reconfiguration such as a failover event or virtual machine
- * migration.
- */
-void netif_notify_peers(struct net_device *dev)
-{
-       rtnl_lock();
-       call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, dev);
-       rtnl_unlock();
-}
-EXPORT_SYMBOL(netif_notify_peers);
-
 /* "NOOP" scheduler: the best scheduler, recommended for all interfaces
    under all circumstances. It is difficult to invent anything faster or
    cheaper.
index ebaef3ed6065bee6d49880cde701ee49b26585e4..b1ef3bc301a5ad424fb041c0f6f37c010098bcc3 100644 (file)
@@ -82,6 +82,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
                                          sctp_scope_t scope,
                                          gfp_t gfp)
 {
+       struct net *net = sock_net(sk);
        struct sctp_sock *sp;
        int i;
        sctp_paramhdr_t *p;
@@ -124,7 +125,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
         * socket values.
         */
        asoc->max_retrans = sp->assocparams.sasoc_asocmaxrxt;
-       asoc->pf_retrans  = sctp_pf_retrans;
+       asoc->pf_retrans  = net->sctp.pf_retrans;
 
        asoc->rto_initial = msecs_to_jiffies(sp->rtoinfo.srto_initial);
        asoc->rto_max = msecs_to_jiffies(sp->rtoinfo.srto_max);
@@ -175,7 +176,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
        asoc->timeouts[SCTP_EVENT_TIMEOUT_HEARTBEAT] = 0;
        asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] = asoc->sackdelay;
        asoc->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] =
-               min_t(unsigned long, sp->autoclose, sctp_max_autoclose) * HZ;
+               min_t(unsigned long, sp->autoclose, net->sctp.max_autoclose) * HZ;
 
        /* Initializes the timers */
        for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i)
@@ -281,7 +282,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
         * and will revert old behavior.
         */
        asoc->peer.asconf_capable = 0;
-       if (sctp_addip_noauth)
+       if (net->sctp.addip_noauth)
                asoc->peer.asconf_capable = 1;
        asoc->asconf_addr_del_pending = NULL;
        asoc->src_out_of_asoc_ok = 0;
@@ -641,6 +642,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
                                           const gfp_t gfp,
                                           const int peer_state)
 {
+       struct net *net = sock_net(asoc->base.sk);
        struct sctp_transport *peer;
        struct sctp_sock *sp;
        unsigned short port;
@@ -674,7 +676,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
                return peer;
        }
 
-       peer = sctp_transport_new(addr, gfp);
+       peer = sctp_transport_new(net, addr, gfp);
        if (!peer)
                return NULL;
 
@@ -1089,13 +1091,15 @@ out:
 
 /* Is this the association we are looking for? */
 struct sctp_transport *sctp_assoc_is_match(struct sctp_association *asoc,
+                                          struct net *net,
                                           const union sctp_addr *laddr,
                                           const union sctp_addr *paddr)
 {
        struct sctp_transport *transport;
 
        if ((htons(asoc->base.bind_addr.port) == laddr->v4.sin_port) &&
-           (htons(asoc->peer.port) == paddr->v4.sin_port)) {
+           (htons(asoc->peer.port) == paddr->v4.sin_port) &&
+           net_eq(sock_net(asoc->base.sk), net)) {
                transport = sctp_assoc_lookup_paddr(asoc, paddr);
                if (!transport)
                        goto out;
@@ -1116,6 +1120,7 @@ static void sctp_assoc_bh_rcv(struct work_struct *work)
        struct sctp_association *asoc =
                container_of(work, struct sctp_association,
                             base.inqueue.immediate);
+       struct net *net = sock_net(asoc->base.sk);
        struct sctp_endpoint *ep;
        struct sctp_chunk *chunk;
        struct sctp_inq *inqueue;
@@ -1148,13 +1153,13 @@ static void sctp_assoc_bh_rcv(struct work_struct *work)
                if (sctp_chunk_is_data(chunk))
                        asoc->peer.last_data_from = chunk->transport;
                else
-                       SCTP_INC_STATS(SCTP_MIB_INCTRLCHUNKS);
+                       SCTP_INC_STATS(net, SCTP_MIB_INCTRLCHUNKS);
 
                if (chunk->transport)
                        chunk->transport->last_time_heard = jiffies;
 
                /* Run through the state machine. */
-               error = sctp_do_sm(SCTP_EVENT_T_CHUNK, subtype,
+               error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype,
                                   state, ep, asoc, chunk, GFP_ATOMIC);
 
                /* Check to see if the association is freed in response to
@@ -1414,6 +1419,7 @@ void sctp_assoc_sync_pmtu(struct sock *sk, struct sctp_association *asoc)
 /* Should we send a SACK to update our peer? */
 static inline int sctp_peer_needs_update(struct sctp_association *asoc)
 {
+       struct net *net = sock_net(asoc->base.sk);
        switch (asoc->state) {
        case SCTP_STATE_ESTABLISHED:
        case SCTP_STATE_SHUTDOWN_PENDING:
@@ -1421,7 +1427,7 @@ static inline int sctp_peer_needs_update(struct sctp_association *asoc)
        case SCTP_STATE_SHUTDOWN_SENT:
                if ((asoc->rwnd > asoc->a_rwnd) &&
                    ((asoc->rwnd - asoc->a_rwnd) >= max_t(__u32,
-                          (asoc->base.sk->sk_rcvbuf >> sctp_rwnd_upd_shift),
+                          (asoc->base.sk->sk_rcvbuf >> net->sctp.rwnd_upd_shift),
                           asoc->pathmtu)))
                        return 1;
                break;
@@ -1542,7 +1548,8 @@ int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *asoc,
        if (asoc->peer.ipv6_address)
                flags |= SCTP_ADDR6_PEERSUPP;
 
-       return sctp_bind_addr_copy(&asoc->base.bind_addr,
+       return sctp_bind_addr_copy(sock_net(asoc->base.sk),
+                                  &asoc->base.bind_addr,
                                   &asoc->ep->base.bind_addr,
                                   scope, gfp, flags);
 }
index bf812048cf6f7a244c547e0cd31a731351abfab3..aaa6c121ecce447e373ac47cfc5fffcc5ac36e23 100644 (file)
@@ -392,13 +392,14 @@ nomem:
  */
 int sctp_auth_asoc_init_active_key(struct sctp_association *asoc, gfp_t gfp)
 {
+       struct net *net = sock_net(asoc->base.sk);
        struct sctp_auth_bytes  *secret;
        struct sctp_shared_key *ep_key;
 
        /* If we don't support AUTH, or peer is not capable
         * we don't need to do anything.
         */
-       if (!sctp_auth_enable || !asoc->peer.auth_capable)
+       if (!net->sctp.auth_enable || !asoc->peer.auth_capable)
                return 0;
 
        /* If the key_id is non-zero and we couldn't find an
@@ -445,11 +446,12 @@ struct sctp_shared_key *sctp_auth_get_shkey(
  */
 int sctp_auth_init_hmacs(struct sctp_endpoint *ep, gfp_t gfp)
 {
+       struct net *net = sock_net(ep->base.sk);
        struct crypto_hash *tfm = NULL;
        __u16   id;
 
        /* if the transforms are already allocted, we are done */
-       if (!sctp_auth_enable) {
+       if (!net->sctp.auth_enable) {
                ep->auth_hmacs = NULL;
                return 0;
        }
@@ -674,7 +676,12 @@ static int __sctp_auth_cid(sctp_cid_t chunk, struct sctp_chunks_param *param)
 /* Check if peer requested that this chunk is authenticated */
 int sctp_auth_send_cid(sctp_cid_t chunk, const struct sctp_association *asoc)
 {
-       if (!sctp_auth_enable || !asoc || !asoc->peer.auth_capable)
+       struct net  *net;
+       if (!asoc)
+               return 0;
+
+       net = sock_net(asoc->base.sk);
+       if (!net->sctp.auth_enable || !asoc->peer.auth_capable)
                return 0;
 
        return __sctp_auth_cid(chunk, asoc->peer.peer_chunks);
@@ -683,7 +690,12 @@ int sctp_auth_send_cid(sctp_cid_t chunk, const struct sctp_association *asoc)
 /* Check if we requested that peer authenticate this chunk. */
 int sctp_auth_recv_cid(sctp_cid_t chunk, const struct sctp_association *asoc)
 {
-       if (!sctp_auth_enable || !asoc)
+       struct net *net;
+       if (!asoc)
+               return 0;
+
+       net = sock_net(asoc->base.sk);
+       if (!net->sctp.auth_enable);
                return 0;
 
        return __sctp_auth_cid(chunk,
index 4ece451c8d27d59ba2a73fba1baf6cb8ca19fe4e..d886b3bf84f5a1823e208d3c784db05a6c88938a 100644 (file)
@@ -52,8 +52,8 @@
 #include <net/sctp/sm.h>
 
 /* Forward declarations for internal helpers. */
-static int sctp_copy_one_addr(struct sctp_bind_addr *, union sctp_addr *,
-                             sctp_scope_t scope, gfp_t gfp,
+static int sctp_copy_one_addr(struct net *, struct sctp_bind_addr *,
+                             union sctp_addr *, sctp_scope_t scope, gfp_t gfp,
                              int flags);
 static void sctp_bind_addr_clean(struct sctp_bind_addr *);
 
@@ -62,7 +62,7 @@ static void sctp_bind_addr_clean(struct sctp_bind_addr *);
 /* Copy 'src' to 'dest' taking 'scope' into account.  Omit addresses
  * in 'src' which have a broader scope than 'scope'.
  */
-int sctp_bind_addr_copy(struct sctp_bind_addr *dest,
+int sctp_bind_addr_copy(struct net *net, struct sctp_bind_addr *dest,
                        const struct sctp_bind_addr *src,
                        sctp_scope_t scope, gfp_t gfp,
                        int flags)
@@ -75,7 +75,7 @@ int sctp_bind_addr_copy(struct sctp_bind_addr *dest,
 
        /* Extract the addresses which are relevant for this scope.  */
        list_for_each_entry(addr, &src->address_list, list) {
-               error = sctp_copy_one_addr(dest, &addr->a, scope,
+               error = sctp_copy_one_addr(net, dest, &addr->a, scope,
                                           gfp, flags);
                if (error < 0)
                        goto out;
@@ -87,7 +87,7 @@ int sctp_bind_addr_copy(struct sctp_bind_addr *dest,
         */
        if (list_empty(&dest->address_list) && (SCTP_SCOPE_GLOBAL == scope)) {
                list_for_each_entry(addr, &src->address_list, list) {
-                       error = sctp_copy_one_addr(dest, &addr->a,
+                       error = sctp_copy_one_addr(net, dest, &addr->a,
                                                   SCTP_SCOPE_LINK, gfp,
                                                   flags);
                        if (error < 0)
@@ -448,7 +448,7 @@ union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr       *bp,
 }
 
 /* Copy out addresses from the global local address list. */
-static int sctp_copy_one_addr(struct sctp_bind_addr *dest,
+static int sctp_copy_one_addr(struct net *net, struct sctp_bind_addr *dest,
                              union sctp_addr *addr,
                              sctp_scope_t scope, gfp_t gfp,
                              int flags)
@@ -456,8 +456,8 @@ static int sctp_copy_one_addr(struct sctp_bind_addr *dest,
        int error = 0;
 
        if (sctp_is_any(NULL, addr)) {
-               error = sctp_copy_local_addr_list(dest, scope, gfp, flags);
-       } else if (sctp_in_scope(addr, scope)) {
+               error = sctp_copy_local_addr_list(net, dest, scope, gfp, flags);
+       } else if (sctp_in_scope(net, addr, scope)) {
                /* Now that the address is in scope, check to see if
                 * the address type is supported by local sock as
                 * well as the remote peer.
@@ -494,7 +494,7 @@ int sctp_is_any(struct sock *sk, const union sctp_addr *addr)
 }
 
 /* Is 'addr' valid for 'scope'?  */
-int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope)
+int sctp_in_scope(struct net *net, const union sctp_addr *addr, sctp_scope_t scope)
 {
        sctp_scope_t addr_scope = sctp_scope(addr);
 
@@ -512,7 +512,7 @@ int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope)
         * Address scoping can be selectively controlled via sysctl
         * option
         */
-       switch (sctp_scope_policy) {
+       switch (net->sctp.scope_policy) {
        case SCTP_SCOPE_POLICY_DISABLE:
                return 1;
        case SCTP_SCOPE_POLICY_ENABLE:
index 6c8556459a751b3e2faa6b0442b804396ff6de7e..7c2df9c33df37a588c426e7945cd71233edd4577 100644 (file)
@@ -257,7 +257,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
        offset = 0;
 
        if ((whole > 1) || (whole && over))
-               SCTP_INC_STATS_USER(SCTP_MIB_FRAGUSRMSGS);
+               SCTP_INC_STATS_USER(sock_net(asoc->base.sk), SCTP_MIB_FRAGUSRMSGS);
 
        /* Create chunks for all the full sized DATA chunks. */
        for (i=0, len=first_len; i < whole; i++) {
index 68a385d7c3bdaaab2ebed8c7f4bec94dc53d4c79..1859e2bc83d113d1a14d01f904475b01099e0626 100644 (file)
@@ -65,6 +65,7 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
                                                struct sock *sk,
                                                gfp_t gfp)
 {
+       struct net *net = sock_net(sk);
        struct sctp_hmac_algo_param *auth_hmacs = NULL;
        struct sctp_chunks_param *auth_chunks = NULL;
        struct sctp_shared_key *null_key;
@@ -74,7 +75,7 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
        if (!ep->digest)
                return NULL;
 
-       if (sctp_auth_enable) {
+       if (net->sctp.auth_enable) {
                /* Allocate space for HMACS and CHUNKS authentication
                 * variables.  There are arrays that we encode directly
                 * into parameters to make the rest of the operations easier.
@@ -106,7 +107,7 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
                /* If the Add-IP functionality is enabled, we must
                 * authenticate, ASCONF and ASCONF-ACK chunks
                 */
-               if (sctp_addip_enable) {
+               if (net->sctp.addip_enable) {
                        auth_chunks->chunks[0] = SCTP_CID_ASCONF;
                        auth_chunks->chunks[1] = SCTP_CID_ASCONF_ACK;
                        auth_chunks->param_hdr.length =
@@ -140,14 +141,14 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
        INIT_LIST_HEAD(&ep->asocs);
 
        /* Use SCTP specific send buffer space queues.  */
-       ep->sndbuf_policy = sctp_sndbuf_policy;
+       ep->sndbuf_policy = net->sctp.sndbuf_policy;
 
        sk->sk_data_ready = sctp_data_ready;
        sk->sk_write_space = sctp_write_space;
        sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
 
        /* Get the receive buffer policy for this endpoint */
-       ep->rcvbuf_policy = sctp_rcvbuf_policy;
+       ep->rcvbuf_policy = net->sctp.rcvbuf_policy;
 
        /* Initialize the secret key used with cookie. */
        get_random_bytes(&ep->secret_key[0], SCTP_SECRET_SIZE);
@@ -302,11 +303,13 @@ void sctp_endpoint_put(struct sctp_endpoint *ep)
 
 /* Is this the endpoint we are looking for?  */
 struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep,
+                                              struct net *net,
                                               const union sctp_addr *laddr)
 {
        struct sctp_endpoint *retval = NULL;
 
-       if (htons(ep->base.bind_addr.port) == laddr->v4.sin_port) {
+       if ((htons(ep->base.bind_addr.port) == laddr->v4.sin_port) &&
+           net_eq(sock_net(ep->base.sk), net)) {
                if (sctp_bind_addr_match(&ep->base.bind_addr, laddr,
                                         sctp_sk(ep->base.sk)))
                        retval = ep;
@@ -343,7 +346,8 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc(
 
        rport = ntohs(paddr->v4.sin_port);
 
-       hash = sctp_assoc_hashfn(ep->base.bind_addr.port, rport);
+       hash = sctp_assoc_hashfn(sock_net(ep->base.sk), ep->base.bind_addr.port,
+                                rport);
        head = &sctp_assoc_hashtable[hash];
        read_lock(&head->lock);
        sctp_for_each_hentry(epb, node, &head->chain) {
@@ -386,13 +390,14 @@ int sctp_endpoint_is_peeled_off(struct sctp_endpoint *ep,
 {
        struct sctp_sockaddr_entry *addr;
        struct sctp_bind_addr *bp;
+       struct net *net = sock_net(ep->base.sk);
 
        bp = &ep->base.bind_addr;
        /* This function is called with the socket lock held,
         * so the address_list can not change.
         */
        list_for_each_entry(addr, &bp->address_list, list) {
-               if (sctp_has_association(&addr->a, paddr))
+               if (sctp_has_association(net, &addr->a, paddr))
                        return 1;
        }
 
@@ -409,6 +414,7 @@ static void sctp_endpoint_bh_rcv(struct work_struct *work)
                             base.inqueue.immediate);
        struct sctp_association *asoc;
        struct sock *sk;
+       struct net *net;
        struct sctp_transport *transport;
        struct sctp_chunk *chunk;
        struct sctp_inq *inqueue;
@@ -423,6 +429,7 @@ static void sctp_endpoint_bh_rcv(struct work_struct *work)
        asoc = NULL;
        inqueue = &ep->base.inqueue;
        sk = ep->base.sk;
+       net = sock_net(sk);
 
        while (NULL != (chunk = sctp_inq_pop(inqueue))) {
                subtype = SCTP_ST_CHUNK(chunk->chunk_hdr->type);
@@ -474,12 +481,12 @@ normal:
                if (asoc && sctp_chunk_is_data(chunk))
                        asoc->peer.last_data_from = chunk->transport;
                else
-                       SCTP_INC_STATS(SCTP_MIB_INCTRLCHUNKS);
+                       SCTP_INC_STATS(sock_net(ep->base.sk), SCTP_MIB_INCTRLCHUNKS);
 
                if (chunk->transport)
                        chunk->transport->last_time_heard = jiffies;
 
-               error = sctp_do_sm(SCTP_EVENT_T_CHUNK, subtype, state,
+               error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype, state,
                                   ep, asoc, chunk, GFP_ATOMIC);
 
                if (error && chunk)
index e64d5210ed130610402b261218360adc5295102c..25dfe7380479e9598e5732a753349e324aea463a 100644 (file)
 
 /* Forward declarations for internal helpers. */
 static int sctp_rcv_ootb(struct sk_buff *);
-static struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb,
+static struct sctp_association *__sctp_rcv_lookup(struct net *net,
+                                     struct sk_buff *skb,
                                      const union sctp_addr *laddr,
                                      const union sctp_addr *paddr,
                                      struct sctp_transport **transportp);
-static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr);
+static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net,
+                                               const union sctp_addr *laddr);
 static struct sctp_association *__sctp_lookup_association(
+                                       struct net *net,
                                        const union sctp_addr *local,
                                        const union sctp_addr *peer,
                                        struct sctp_transport **pt);
@@ -80,7 +83,7 @@ static int sctp_add_backlog(struct sock *sk, struct sk_buff *skb);
 
 
 /* Calculate the SCTP checksum of an SCTP packet.  */
-static inline int sctp_rcv_checksum(struct sk_buff *skb)
+static inline int sctp_rcv_checksum(struct net *net, struct sk_buff *skb)
 {
        struct sctphdr *sh = sctp_hdr(skb);
        __le32 cmp = sh->checksum;
@@ -96,7 +99,7 @@ static inline int sctp_rcv_checksum(struct sk_buff *skb)
 
        if (val != cmp) {
                /* CRC failure, dump it. */
-               SCTP_INC_STATS_BH(SCTP_MIB_CHECKSUMERRORS);
+               SCTP_INC_STATS_BH(net, SCTP_MIB_CHECKSUMERRORS);
                return -1;
        }
        return 0;
@@ -129,11 +132,12 @@ int sctp_rcv(struct sk_buff *skb)
        union sctp_addr dest;
        int family;
        struct sctp_af *af;
+       struct net *net = dev_net(skb->dev);
 
        if (skb->pkt_type!=PACKET_HOST)
                goto discard_it;
 
-       SCTP_INC_STATS_BH(SCTP_MIB_INSCTPPACKS);
+       SCTP_INC_STATS_BH(net, SCTP_MIB_INSCTPPACKS);
 
        if (skb_linearize(skb))
                goto discard_it;
@@ -145,7 +149,7 @@ int sctp_rcv(struct sk_buff *skb)
        if (skb->len < sizeof(struct sctphdr))
                goto discard_it;
        if (!sctp_checksum_disable && !skb_csum_unnecessary(skb) &&
-                 sctp_rcv_checksum(skb) < 0)
+                 sctp_rcv_checksum(net, skb) < 0)
                goto discard_it;
 
        skb_pull(skb, sizeof(struct sctphdr));
@@ -178,10 +182,10 @@ int sctp_rcv(struct sk_buff *skb)
            !af->addr_valid(&dest, NULL, skb))
                goto discard_it;
 
-       asoc = __sctp_rcv_lookup(skb, &src, &dest, &transport);
+       asoc = __sctp_rcv_lookup(net, skb, &src, &dest, &transport);
 
        if (!asoc)
-               ep = __sctp_rcv_lookup_endpoint(&dest);
+               ep = __sctp_rcv_lookup_endpoint(net, &dest);
 
        /* Retrieve the common input handling substructure. */
        rcvr = asoc ? &asoc->base : &ep->base;
@@ -200,7 +204,7 @@ int sctp_rcv(struct sk_buff *skb)
                        sctp_endpoint_put(ep);
                        ep = NULL;
                }
-               sk = sctp_get_ctl_sock();
+               sk = net->sctp.ctl_sock;
                ep = sctp_sk(sk)->ep;
                sctp_endpoint_hold(ep);
                rcvr = &ep->base;
@@ -216,7 +220,7 @@ int sctp_rcv(struct sk_buff *skb)
         */
        if (!asoc) {
                if (sctp_rcv_ootb(skb)) {
-                       SCTP_INC_STATS_BH(SCTP_MIB_OUTOFBLUES);
+                       SCTP_INC_STATS_BH(net, SCTP_MIB_OUTOFBLUES);
                        goto discard_release;
                }
        }
@@ -272,9 +276,9 @@ int sctp_rcv(struct sk_buff *skb)
                        skb = NULL; /* sctp_chunk_free already freed the skb */
                        goto discard_release;
                }
-               SCTP_INC_STATS_BH(SCTP_MIB_IN_PKT_BACKLOG);
+               SCTP_INC_STATS_BH(net, SCTP_MIB_IN_PKT_BACKLOG);
        } else {
-               SCTP_INC_STATS_BH(SCTP_MIB_IN_PKT_SOFTIRQ);
+               SCTP_INC_STATS_BH(net, SCTP_MIB_IN_PKT_SOFTIRQ);
                sctp_inq_push(&chunk->rcvr->inqueue, chunk);
        }
 
@@ -289,7 +293,7 @@ int sctp_rcv(struct sk_buff *skb)
        return 0;
 
 discard_it:
-       SCTP_INC_STATS_BH(SCTP_MIB_IN_PKT_DISCARDS);
+       SCTP_INC_STATS_BH(net, SCTP_MIB_IN_PKT_DISCARDS);
        kfree_skb(skb);
        return 0;
 
@@ -462,11 +466,13 @@ void sctp_icmp_proto_unreachable(struct sock *sk,
                }
                        
        } else {
+               struct net *net = sock_net(sk);
+
                if (timer_pending(&t->proto_unreach_timer) &&
                    del_timer(&t->proto_unreach_timer))
                        sctp_association_put(asoc);
 
-               sctp_do_sm(SCTP_EVENT_T_OTHER,
+               sctp_do_sm(net, SCTP_EVENT_T_OTHER,
                           SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH),
                           asoc->state, asoc->ep, asoc, t,
                           GFP_ATOMIC);
@@ -474,7 +480,7 @@ void sctp_icmp_proto_unreachable(struct sock *sk,
 }
 
 /* Common lookup code for icmp/icmpv6 error handler. */
-struct sock *sctp_err_lookup(int family, struct sk_buff *skb,
+struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb,
                             struct sctphdr *sctphdr,
                             struct sctp_association **app,
                             struct sctp_transport **tpp)
@@ -503,7 +509,7 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb,
        /* Look for an association that matches the incoming ICMP error
         * packet.
         */
-       asoc = __sctp_lookup_association(&saddr, &daddr, &transport);
+       asoc = __sctp_lookup_association(net, &saddr, &daddr, &transport);
        if (!asoc)
                return NULL;
 
@@ -539,7 +545,7 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb,
         * servers this needs to be solved differently.
         */
        if (sock_owned_by_user(sk))
-               NET_INC_STATS_BH(&init_net, LINUX_MIB_LOCKDROPPEDICMPS);
+               NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS);
 
        *app = asoc;
        *tpp = transport;
@@ -586,9 +592,10 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
        struct inet_sock *inet;
        sk_buff_data_t saveip, savesctp;
        int err;
+       struct net *net = dev_net(skb->dev);
 
        if (skb->len < ihlen + 8) {
-               ICMP_INC_STATS_BH(&init_net, ICMP_MIB_INERRORS);
+               ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
                return;
        }
 
@@ -597,12 +604,12 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
        savesctp = skb->transport_header;
        skb_reset_network_header(skb);
        skb_set_transport_header(skb, ihlen);
-       sk = sctp_err_lookup(AF_INET, skb, sctp_hdr(skb), &asoc, &transport);
+       sk = sctp_err_lookup(net, AF_INET, skb, sctp_hdr(skb), &asoc, &transport);
        /* Put back, the original values. */
        skb->network_header = saveip;
        skb->transport_header = savesctp;
        if (!sk) {
-               ICMP_INC_STATS_BH(&init_net, ICMP_MIB_INERRORS);
+               ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
                return;
        }
        /* Warning:  The sock lock is held.  Remember to call
@@ -723,12 +730,13 @@ discard:
 /* Insert endpoint into the hash table.  */
 static void __sctp_hash_endpoint(struct sctp_endpoint *ep)
 {
+       struct net *net = sock_net(ep->base.sk);
        struct sctp_ep_common *epb;
        struct sctp_hashbucket *head;
 
        epb = &ep->base;
 
-       epb->hashent = sctp_ep_hashfn(epb->bind_addr.port);
+       epb->hashent = sctp_ep_hashfn(net, epb->bind_addr.port);
        head = &sctp_ep_hashtable[epb->hashent];
 
        sctp_write_lock(&head->lock);
@@ -747,12 +755,13 @@ void sctp_hash_endpoint(struct sctp_endpoint *ep)
 /* Remove endpoint from the hash table.  */
 static void __sctp_unhash_endpoint(struct sctp_endpoint *ep)
 {
+       struct net *net = sock_net(ep->base.sk);
        struct sctp_hashbucket *head;
        struct sctp_ep_common *epb;
 
        epb = &ep->base;
 
-       epb->hashent = sctp_ep_hashfn(epb->bind_addr.port);
+       epb->hashent = sctp_ep_hashfn(net, epb->bind_addr.port);
 
        head = &sctp_ep_hashtable[epb->hashent];
 
@@ -770,7 +779,8 @@ void sctp_unhash_endpoint(struct sctp_endpoint *ep)
 }
 
 /* Look up an endpoint. */
-static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr)
+static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net,
+                                               const union sctp_addr *laddr)
 {
        struct sctp_hashbucket *head;
        struct sctp_ep_common *epb;
@@ -778,16 +788,16 @@ static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *l
        struct hlist_node *node;
        int hash;
 
-       hash = sctp_ep_hashfn(ntohs(laddr->v4.sin_port));
+       hash = sctp_ep_hashfn(net, ntohs(laddr->v4.sin_port));
        head = &sctp_ep_hashtable[hash];
        read_lock(&head->lock);
        sctp_for_each_hentry(epb, node, &head->chain) {
                ep = sctp_ep(epb);
-               if (sctp_endpoint_is_match(ep, laddr))
+               if (sctp_endpoint_is_match(ep, net, laddr))
                        goto hit;
        }
 
-       ep = sctp_sk((sctp_get_ctl_sock()))->ep;
+       ep = sctp_sk(net->sctp.ctl_sock)->ep;
 
 hit:
        sctp_endpoint_hold(ep);
@@ -798,13 +808,15 @@ hit:
 /* Insert association into the hash table.  */
 static void __sctp_hash_established(struct sctp_association *asoc)
 {
+       struct net *net = sock_net(asoc->base.sk);
        struct sctp_ep_common *epb;
        struct sctp_hashbucket *head;
 
        epb = &asoc->base;
 
        /* Calculate which chain this entry will belong to. */
-       epb->hashent = sctp_assoc_hashfn(epb->bind_addr.port, asoc->peer.port);
+       epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port,
+                                        asoc->peer.port);
 
        head = &sctp_assoc_hashtable[epb->hashent];
 
@@ -827,12 +839,13 @@ void sctp_hash_established(struct sctp_association *asoc)
 /* Remove association from the hash table.  */
 static void __sctp_unhash_established(struct sctp_association *asoc)
 {
+       struct net *net = sock_net(asoc->base.sk);
        struct sctp_hashbucket *head;
        struct sctp_ep_common *epb;
 
        epb = &asoc->base;
 
-       epb->hashent = sctp_assoc_hashfn(epb->bind_addr.port,
+       epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port,
                                         asoc->peer.port);
 
        head = &sctp_assoc_hashtable[epb->hashent];
@@ -855,6 +868,7 @@ void sctp_unhash_established(struct sctp_association *asoc)
 
 /* Look up an association. */
 static struct sctp_association *__sctp_lookup_association(
+                                       struct net *net,
                                        const union sctp_addr *local,
                                        const union sctp_addr *peer,
                                        struct sctp_transport **pt)
@@ -869,12 +883,13 @@ static struct sctp_association *__sctp_lookup_association(
        /* Optimize here for direct hit, only listening connections can
         * have wildcards anyways.
         */
-       hash = sctp_assoc_hashfn(ntohs(local->v4.sin_port), ntohs(peer->v4.sin_port));
+       hash = sctp_assoc_hashfn(net, ntohs(local->v4.sin_port),
+                                ntohs(peer->v4.sin_port));
        head = &sctp_assoc_hashtable[hash];
        read_lock(&head->lock);
        sctp_for_each_hentry(epb, node, &head->chain) {
                asoc = sctp_assoc(epb);
-               transport = sctp_assoc_is_match(asoc, local, peer);
+               transport = sctp_assoc_is_match(asoc, net, local, peer);
                if (transport)
                        goto hit;
        }
@@ -892,27 +907,29 @@ hit:
 
 /* Look up an association. BH-safe. */
 SCTP_STATIC
-struct sctp_association *sctp_lookup_association(const union sctp_addr *laddr,
+struct sctp_association *sctp_lookup_association(struct net *net,
+                                                const union sctp_addr *laddr,
                                                 const union sctp_addr *paddr,
                                            struct sctp_transport **transportp)
 {
        struct sctp_association *asoc;
 
        sctp_local_bh_disable();
-       asoc = __sctp_lookup_association(laddr, paddr, transportp);
+       asoc = __sctp_lookup_association(net, laddr, paddr, transportp);
        sctp_local_bh_enable();
 
        return asoc;
 }
 
 /* Is there an association matching the given local and peer addresses? */
-int sctp_has_association(const union sctp_addr *laddr,
+int sctp_has_association(struct net *net,
+                        const union sctp_addr *laddr,
                         const union sctp_addr *paddr)
 {
        struct sctp_association *asoc;
        struct sctp_transport *transport;
 
-       if ((asoc = sctp_lookup_association(laddr, paddr, &transport))) {
+       if ((asoc = sctp_lookup_association(net, laddr, paddr, &transport))) {
                sctp_association_put(asoc);
                return 1;
        }
@@ -938,7 +955,8 @@ int sctp_has_association(const union sctp_addr *laddr,
  * in certain circumstances.
  *
  */
-static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb,
+static struct sctp_association *__sctp_rcv_init_lookup(struct net *net,
+       struct sk_buff *skb,
        const union sctp_addr *laddr, struct sctp_transport **transportp)
 {
        struct sctp_association *asoc;
@@ -978,7 +996,7 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb,
 
                af->from_addr_param(paddr, params.addr, sh->source, 0);
 
-               asoc = __sctp_lookup_association(laddr, paddr, &transport);
+               asoc = __sctp_lookup_association(net, laddr, paddr, &transport);
                if (asoc)
                        return asoc;
        }
@@ -1001,6 +1019,7 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb,
  * subsequent ASCONF Chunks. If found, proceed to rule D4.
  */
 static struct sctp_association *__sctp_rcv_asconf_lookup(
+                                       struct net *net,
                                        sctp_chunkhdr_t *ch,
                                        const union sctp_addr *laddr,
                                        __be16 peer_port,
@@ -1020,7 +1039,7 @@ static struct sctp_association *__sctp_rcv_asconf_lookup(
 
        af->from_addr_param(&paddr, param, peer_port, 0);
 
-       return __sctp_lookup_association(laddr, &paddr, transportp);
+       return __sctp_lookup_association(net, laddr, &paddr, transportp);
 }
 
 
@@ -1033,7 +1052,8 @@ static struct sctp_association *__sctp_rcv_asconf_lookup(
 * This means that any chunks that can help us identify the association need
 * to be looked at to find this association.
 */
-static struct sctp_association *__sctp_rcv_walk_lookup(struct sk_buff *skb,
+static struct sctp_association *__sctp_rcv_walk_lookup(struct net *net,
+                                     struct sk_buff *skb,
                                      const union sctp_addr *laddr,
                                      struct sctp_transport **transportp)
 {
@@ -1074,8 +1094,9 @@ static struct sctp_association *__sctp_rcv_walk_lookup(struct sk_buff *skb,
                            break;
 
                    case SCTP_CID_ASCONF:
-                           if (have_auth || sctp_addip_noauth)
-                                   asoc = __sctp_rcv_asconf_lookup(ch, laddr,
+                           if (have_auth || net->sctp.addip_noauth)
+                                   asoc = __sctp_rcv_asconf_lookup(
+                                                       net, ch, laddr,
                                                        sctp_hdr(skb)->source,
                                                        transportp);
                    default:
@@ -1098,7 +1119,8 @@ static struct sctp_association *__sctp_rcv_walk_lookup(struct sk_buff *skb,
  * include looking inside of INIT/INIT-ACK chunks or after the AUTH
  * chunks.
  */
-static struct sctp_association *__sctp_rcv_lookup_harder(struct sk_buff *skb,
+static struct sctp_association *__sctp_rcv_lookup_harder(struct net *net,
+                                     struct sk_buff *skb,
                                      const union sctp_addr *laddr,
                                      struct sctp_transport **transportp)
 {
@@ -1118,11 +1140,11 @@ static struct sctp_association *__sctp_rcv_lookup_harder(struct sk_buff *skb,
        switch (ch->type) {
        case SCTP_CID_INIT:
        case SCTP_CID_INIT_ACK:
-               return __sctp_rcv_init_lookup(skb, laddr, transportp);
+               return __sctp_rcv_init_lookup(net, skb, laddr, transportp);
                break;
 
        default:
-               return __sctp_rcv_walk_lookup(skb, laddr, transportp);
+               return __sctp_rcv_walk_lookup(net, skb, laddr, transportp);
                break;
        }
 
@@ -1131,21 +1153,22 @@ static struct sctp_association *__sctp_rcv_lookup_harder(struct sk_buff *skb,
 }
 
 /* Lookup an association for an inbound skb. */
-static struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb,
+static struct sctp_association *__sctp_rcv_lookup(struct net *net,
+                                     struct sk_buff *skb,
                                      const union sctp_addr *paddr,
                                      const union sctp_addr *laddr,
                                      struct sctp_transport **transportp)
 {
        struct sctp_association *asoc;
 
-       asoc = __sctp_lookup_association(laddr, paddr, transportp);
+       asoc = __sctp_lookup_association(net, laddr, paddr, transportp);
 
        /* Further lookup for INIT/INIT-ACK packets.
         * SCTP Implementors Guide, 2.18 Handling of address
         * parameters within the INIT or INIT-ACK.
         */
        if (!asoc)
-               asoc = __sctp_rcv_lookup_harder(skb, laddr, transportp);
+               asoc = __sctp_rcv_lookup_harder(net, skb, laddr, transportp);
 
        return asoc;
 }
index ed7139ea7978dc664f6dfbff33977cf31bbc4325..ea14cb44529528124e2bdd24988a59f1cacc2569 100644 (file)
@@ -99,6 +99,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,
        struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
        struct sctp_sockaddr_entry *addr = NULL;
        struct sctp_sockaddr_entry *temp;
+       struct net *net = dev_net(ifa->idev->dev);
        int found = 0;
 
        switch (ev) {
@@ -110,27 +111,27 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,
                        addr->a.v6.sin6_addr = ifa->addr;
                        addr->a.v6.sin6_scope_id = ifa->idev->dev->ifindex;
                        addr->valid = 1;
-                       spin_lock_bh(&sctp_local_addr_lock);
-                       list_add_tail_rcu(&addr->list, &sctp_local_addr_list);
-                       sctp_addr_wq_mgmt(addr, SCTP_ADDR_NEW);
-                       spin_unlock_bh(&sctp_local_addr_lock);
+                       spin_lock_bh(&net->sctp.local_addr_lock);
+                       list_add_tail_rcu(&addr->list, &net->sctp.local_addr_list);
+                       sctp_addr_wq_mgmt(net, addr, SCTP_ADDR_NEW);
+                       spin_unlock_bh(&net->sctp.local_addr_lock);
                }
                break;
        case NETDEV_DOWN:
-               spin_lock_bh(&sctp_local_addr_lock);
+               spin_lock_bh(&net->sctp.local_addr_lock);
                list_for_each_entry_safe(addr, temp,
-                                       &sctp_local_addr_list, list) {
+                                       &net->sctp.local_addr_list, list) {
                        if (addr->a.sa.sa_family == AF_INET6 &&
                                        ipv6_addr_equal(&addr->a.v6.sin6_addr,
                                                &ifa->addr)) {
-                               sctp_addr_wq_mgmt(addr, SCTP_ADDR_DEL);
+                               sctp_addr_wq_mgmt(net, addr, SCTP_ADDR_DEL);
                                found = 1;
                                addr->valid = 0;
                                list_del_rcu(&addr->list);
                                break;
                        }
                }
-               spin_unlock_bh(&sctp_local_addr_lock);
+               spin_unlock_bh(&net->sctp.local_addr_lock);
                if (found)
                        kfree_rcu(addr, rcu);
                break;
@@ -154,6 +155,7 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        struct ipv6_pinfo *np;
        sk_buff_data_t saveip, savesctp;
        int err;
+       struct net *net = dev_net(skb->dev);
 
        idev = in6_dev_get(skb->dev);
 
@@ -162,12 +164,12 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        savesctp = skb->transport_header;
        skb_reset_network_header(skb);
        skb_set_transport_header(skb, offset);
-       sk = sctp_err_lookup(AF_INET6, skb, sctp_hdr(skb), &asoc, &transport);
+       sk = sctp_err_lookup(net, AF_INET6, skb, sctp_hdr(skb), &asoc, &transport);
        /* Put back, the original pointers. */
        skb->network_header   = saveip;
        skb->transport_header = savesctp;
        if (!sk) {
-               ICMP6_INC_STATS_BH(dev_net(skb->dev), idev, ICMP6_MIB_INERRORS);
+               ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_INERRORS);
                goto out;
        }
 
@@ -241,7 +243,7 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
                          __func__, skb, skb->len,
                          &fl6.saddr, &fl6.daddr);
 
-       SCTP_INC_STATS(SCTP_MIB_OUTSCTPPACKS);
+       SCTP_INC_STATS(sock_net(sk), SCTP_MIB_OUTSCTPPACKS);
 
        if (!(transport->param_flags & SPP_PMTUD_ENABLE))
                skb->local_df = 1;
@@ -580,7 +582,7 @@ static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp)
        if (!(type & IPV6_ADDR_UNICAST))
                return 0;
 
-       return ipv6_chk_addr(&init_net, in6, NULL, 0);
+       return ipv6_chk_addr(sock_net(&sp->inet.sk), in6, NULL, 0);
 }
 
 /* This function checks if the address is a valid address to be used for
@@ -857,14 +859,14 @@ static int sctp_inet6_bind_verify(struct sctp_sock *opt, union sctp_addr *addr)
                struct net_device *dev;
 
                if (type & IPV6_ADDR_LINKLOCAL) {
+                       struct net *net;
                        if (!addr->v6.sin6_scope_id)
                                return 0;
+                       net = sock_net(&opt->inet.sk);
                        rcu_read_lock();
-                       dev = dev_get_by_index_rcu(&init_net,
-                                                  addr->v6.sin6_scope_id);
+                       dev = dev_get_by_index_rcu(net, addr->v6.sin6_scope_id);
                        if (!dev ||
-                           !ipv6_chk_addr(&init_net, &addr->v6.sin6_addr,
-                                          dev, 0)) {
+                           !ipv6_chk_addr(net, &addr->v6.sin6_addr, dev, 0)) {
                                rcu_read_unlock();
                                return 0;
                        }
@@ -897,7 +899,7 @@ static int sctp_inet6_send_verify(struct sctp_sock *opt, union sctp_addr *addr)
                        if (!addr->v6.sin6_scope_id)
                                return 0;
                        rcu_read_lock();
-                       dev = dev_get_by_index_rcu(&init_net,
+                       dev = dev_get_by_index_rcu(sock_net(&opt->inet.sk),
                                                   addr->v6.sin6_scope_id);
                        rcu_read_unlock();
                        if (!dev)
index 8ef8e7d9eb61bbf74b6c023f4e33ee360085f84c..fe012c44f8dff15e4882165e2718b11f919cf260 100644 (file)
@@ -129,20 +129,20 @@ static const struct file_operations sctp_objcnt_ops = {
 };
 
 /* Initialize the objcount in the proc filesystem.  */
-void sctp_dbg_objcnt_init(void)
+void sctp_dbg_objcnt_init(struct net *net)
 {
        struct proc_dir_entry *ent;
 
        ent = proc_create("sctp_dbg_objcnt", 0,
-                         proc_net_sctp, &sctp_objcnt_ops);
+                         net->sctp.proc_net_sctp, &sctp_objcnt_ops);
        if (!ent)
                pr_warn("sctp_dbg_objcnt: Unable to create /proc entry.\n");
 }
 
 /* Cleanup the objcount entry in the proc filesystem.  */
-void sctp_dbg_objcnt_exit(void)
+void sctp_dbg_objcnt_exit(struct net *net)
 {
-       remove_proc_entry("sctp_dbg_objcnt", proc_net_sctp);
+       remove_proc_entry("sctp_dbg_objcnt", net->sctp.proc_net_sctp);
 }
 
 
index 838e18b4d7ea62cfd4d57e9d64a86630a0100f67..0c6359bb973c58c1ba77fa3edf599c822df3535b 100644 (file)
@@ -597,7 +597,7 @@ out:
        return err;
 no_route:
        kfree_skb(nskb);
-       IP_INC_STATS_BH(&init_net, IPSTATS_MIB_OUTNOROUTES);
+       IP_INC_STATS_BH(sock_net(asoc->base.sk), IPSTATS_MIB_OUTNOROUTES);
 
        /* FIXME: Returning the 'err' will effect all the associations
         * associated with a socket, although only one of the paths of the
index e7aa177c9522a232c1f1b58c6e5a40df2db03a29..072bf6ae3c26da20baa92d07b9c6248b86db95b2 100644 (file)
@@ -299,6 +299,7 @@ void sctp_outq_free(struct sctp_outq *q)
 /* Put a new chunk in an sctp_outq.  */
 int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk)
 {
+       struct net *net = sock_net(q->asoc->base.sk);
        int error = 0;
 
        SCTP_DEBUG_PRINTK("sctp_outq_tail(%p, %p[%s])\n",
@@ -337,15 +338,15 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk)
 
                        sctp_outq_tail_data(q, chunk);
                        if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
-                               SCTP_INC_STATS(SCTP_MIB_OUTUNORDERCHUNKS);
+                               SCTP_INC_STATS(net, SCTP_MIB_OUTUNORDERCHUNKS);
                        else
-                               SCTP_INC_STATS(SCTP_MIB_OUTORDERCHUNKS);
+                               SCTP_INC_STATS(net, SCTP_MIB_OUTORDERCHUNKS);
                        q->empty = 0;
                        break;
                }
        } else {
                list_add_tail(&chunk->list, &q->control_chunk_list);
-               SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+               SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS);
        }
 
        if (error < 0)
@@ -478,11 +479,12 @@ void sctp_retransmit_mark(struct sctp_outq *q,
 void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
                     sctp_retransmit_reason_t reason)
 {
+       struct net *net = sock_net(q->asoc->base.sk);
        int error = 0;
 
        switch(reason) {
        case SCTP_RTXR_T3_RTX:
-               SCTP_INC_STATS(SCTP_MIB_T3_RETRANSMITS);
+               SCTP_INC_STATS(net, SCTP_MIB_T3_RETRANSMITS);
                sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_T3_RTX);
                /* Update the retran path if the T3-rtx timer has expired for
                 * the current retran path.
@@ -493,15 +495,15 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
                        transport->asoc->unack_data;
                break;
        case SCTP_RTXR_FAST_RTX:
-               SCTP_INC_STATS(SCTP_MIB_FAST_RETRANSMITS);
+               SCTP_INC_STATS(net, SCTP_MIB_FAST_RETRANSMITS);
                sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_FAST_RTX);
                q->fast_rtx = 1;
                break;
        case SCTP_RTXR_PMTUD:
-               SCTP_INC_STATS(SCTP_MIB_PMTUD_RETRANSMITS);
+               SCTP_INC_STATS(net, SCTP_MIB_PMTUD_RETRANSMITS);
                break;
        case SCTP_RTXR_T1_RTX:
-               SCTP_INC_STATS(SCTP_MIB_T1_RETRANSMITS);
+               SCTP_INC_STATS(net, SCTP_MIB_T1_RETRANSMITS);
                transport->asoc->init_retries++;
                break;
        default:
@@ -1914,6 +1916,6 @@ static void sctp_generate_fwdtsn(struct sctp_outq *q, __u32 ctsn)
 
        if (ftsn_chunk) {
                list_add_tail(&ftsn_chunk->list, &q->control_chunk_list);
-               SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+               SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_OUTCTRLCHUNKS);
        }
 }
index 534c7eae9d15f9b0371b7cd2963f74e7cbf18e7c..794bb14decdea60ec58e68a59bc5a6b2feda4a01 100644 (file)
@@ -57,7 +57,7 @@
 
 #define DECLARE_PRIMITIVE(name) \
 /* This is called in the code as sctp_primitive_ ## name.  */ \
-int sctp_primitive_ ## name(struct sctp_association *asoc, \
+int sctp_primitive_ ## name(struct net *net, struct sctp_association *asoc, \
                            void *arg) { \
        int error = 0; \
        sctp_event_t event_type; sctp_subtype_t subtype; \
@@ -69,7 +69,7 @@ int sctp_primitive_ ## name(struct sctp_association *asoc, \
        state = asoc ? asoc->state : SCTP_STATE_CLOSED; \
        ep = asoc ? asoc->ep : NULL; \
        \
-       error = sctp_do_sm(event_type, subtype, state, ep, asoc, \
+       error = sctp_do_sm(net, event_type, subtype, state, ep, asoc,   \
                           arg, GFP_KERNEL); \
        return error; \
 }
index 1e2eee88c3ea4750c093e44b3c3080f8ad3551fd..3e62ee55228fa60f547c9a01d7b6fe4e60ef5685 100644 (file)
@@ -80,11 +80,12 @@ static const struct snmp_mib sctp_snmp_list[] = {
 /* Display sctp snmp mib statistics(/proc/net/sctp/snmp). */
 static int sctp_snmp_seq_show(struct seq_file *seq, void *v)
 {
+       struct net *net = seq->private;
        int i;
 
        for (i = 0; sctp_snmp_list[i].name != NULL; i++)
                seq_printf(seq, "%-32s\t%ld\n", sctp_snmp_list[i].name,
-                          snmp_fold_field((void __percpu **)sctp_statistics,
+                          snmp_fold_field((void __percpu **)net->sctp.sctp_statistics,
                                      sctp_snmp_list[i].entry));
 
        return 0;
@@ -93,7 +94,7 @@ static int sctp_snmp_seq_show(struct seq_file *seq, void *v)
 /* Initialize the seq file operations for 'snmp' object. */
 static int sctp_snmp_seq_open(struct inode *inode, struct file *file)
 {
-       return single_open(file, sctp_snmp_seq_show, NULL);
+       return single_open_net(inode, file, sctp_snmp_seq_show);
 }
 
 static const struct file_operations sctp_snmp_seq_fops = {
@@ -105,11 +106,12 @@ static const struct file_operations sctp_snmp_seq_fops = {
 };
 
 /* Set up the proc fs entry for 'snmp' object. */
-int __init sctp_snmp_proc_init(void)
+int __net_init sctp_snmp_proc_init(struct net *net)
 {
        struct proc_dir_entry *p;
 
-       p = proc_create("snmp", S_IRUGO, proc_net_sctp, &sctp_snmp_seq_fops);
+       p = proc_create("snmp", S_IRUGO, net->sctp.proc_net_sctp,
+                       &sctp_snmp_seq_fops);
        if (!p)
                return -ENOMEM;
 
@@ -117,9 +119,9 @@ int __init sctp_snmp_proc_init(void)
 }
 
 /* Cleanup the proc fs entry for 'snmp' object. */
-void sctp_snmp_proc_exit(void)
+void sctp_snmp_proc_exit(struct net *net)
 {
-       remove_proc_entry("snmp", proc_net_sctp);
+       remove_proc_entry("snmp", net->sctp.proc_net_sctp);
 }
 
 /* Dump local addresses of an association/endpoint. */
@@ -197,6 +199,7 @@ static void * sctp_eps_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 /* Display sctp endpoints (/proc/net/sctp/eps). */
 static int sctp_eps_seq_show(struct seq_file *seq, void *v)
 {
+       struct seq_net_private *priv = seq->private;
        struct sctp_hashbucket *head;
        struct sctp_ep_common *epb;
        struct sctp_endpoint *ep;
@@ -213,6 +216,8 @@ static int sctp_eps_seq_show(struct seq_file *seq, void *v)
        sctp_for_each_hentry(epb, node, &head->chain) {
                ep = sctp_ep(epb);
                sk = epb->sk;
+               if (!net_eq(sock_net(sk), priv->net))
+                       continue;
                seq_printf(seq, "%8pK %8pK %-3d %-3d %-4d %-5d %5d %5lu ", ep, sk,
                           sctp_sk(sk)->type, sk->sk_state, hash,
                           epb->bind_addr.port,
@@ -238,7 +243,8 @@ static const struct seq_operations sctp_eps_ops = {
 /* Initialize the seq file operations for 'eps' object. */
 static int sctp_eps_seq_open(struct inode *inode, struct file *file)
 {
-       return seq_open(file, &sctp_eps_ops);
+       return seq_open_net(inode, file, &sctp_eps_ops,
+                           sizeof(struct seq_net_private));
 }
 
 static const struct file_operations sctp_eps_seq_fops = {
@@ -249,11 +255,12 @@ static const struct file_operations sctp_eps_seq_fops = {
 };
 
 /* Set up the proc fs entry for 'eps' object. */
-int __init sctp_eps_proc_init(void)
+int __net_init sctp_eps_proc_init(struct net *net)
 {
        struct proc_dir_entry *p;
 
-       p = proc_create("eps", S_IRUGO, proc_net_sctp, &sctp_eps_seq_fops);
+       p = proc_create("eps", S_IRUGO, net->sctp.proc_net_sctp,
+                       &sctp_eps_seq_fops);
        if (!p)
                return -ENOMEM;
 
@@ -261,9 +268,9 @@ int __init sctp_eps_proc_init(void)
 }
 
 /* Cleanup the proc fs entry for 'eps' object. */
-void sctp_eps_proc_exit(void)
+void sctp_eps_proc_exit(struct net *net)
 {
-       remove_proc_entry("eps", proc_net_sctp);
+       remove_proc_entry("eps", net->sctp.proc_net_sctp);
 }
 
 
@@ -300,6 +307,7 @@ static void * sctp_assocs_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 /* Display sctp associations (/proc/net/sctp/assocs). */
 static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
 {
+       struct seq_net_private *priv = seq->private;
        struct sctp_hashbucket *head;
        struct sctp_ep_common *epb;
        struct sctp_association *assoc;
@@ -316,6 +324,8 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
        sctp_for_each_hentry(epb, node, &head->chain) {
                assoc = sctp_assoc(epb);
                sk = epb->sk;
+               if (!net_eq(sock_net(sk), priv->net))
+                       continue;
                seq_printf(seq,
                           "%8pK %8pK %-3d %-3d %-2d %-4d "
                           "%4d %8d %8d %7d %5lu %-5d %5d ",
@@ -354,7 +364,8 @@ static const struct seq_operations sctp_assoc_ops = {
 /* Initialize the seq file operations for 'assocs' object. */
 static int sctp_assocs_seq_open(struct inode *inode, struct file *file)
 {
-       return seq_open(file, &sctp_assoc_ops);
+       return seq_open_net(inode, file, &sctp_assoc_ops,
+                           sizeof(struct seq_net_private));
 }
 
 static const struct file_operations sctp_assocs_seq_fops = {
@@ -365,11 +376,11 @@ static const struct file_operations sctp_assocs_seq_fops = {
 };
 
 /* Set up the proc fs entry for 'assocs' object. */
-int __init sctp_assocs_proc_init(void)
+int __net_init sctp_assocs_proc_init(struct net *net)
 {
        struct proc_dir_entry *p;
 
-       p = proc_create("assocs", S_IRUGO, proc_net_sctp,
+       p = proc_create("assocs", S_IRUGO, net->sctp.proc_net_sctp,
                        &sctp_assocs_seq_fops);
        if (!p)
                return -ENOMEM;
@@ -378,9 +389,9 @@ int __init sctp_assocs_proc_init(void)
 }
 
 /* Cleanup the proc fs entry for 'assocs' object. */
-void sctp_assocs_proc_exit(void)
+void sctp_assocs_proc_exit(struct net *net)
 {
-       remove_proc_entry("assocs", proc_net_sctp);
+       remove_proc_entry("assocs", net->sctp.proc_net_sctp);
 }
 
 static void *sctp_remaddr_seq_start(struct seq_file *seq, loff_t *pos)
@@ -412,6 +423,7 @@ static void sctp_remaddr_seq_stop(struct seq_file *seq, void *v)
 
 static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
 {
+       struct seq_net_private *priv = seq->private;
        struct sctp_hashbucket *head;
        struct sctp_ep_common *epb;
        struct sctp_association *assoc;
@@ -426,6 +438,8 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
        sctp_local_bh_disable();
        read_lock(&head->lock);
        sctp_for_each_hentry(epb, node, &head->chain) {
+               if (!net_eq(sock_net(epb->sk), priv->net))
+                       continue;
                assoc = sctp_assoc(epb);
                list_for_each_entry(tsp, &assoc->peer.transport_addr_list,
                                        transports) {
@@ -489,14 +503,15 @@ static const struct seq_operations sctp_remaddr_ops = {
 };
 
 /* Cleanup the proc fs entry for 'remaddr' object. */
-void sctp_remaddr_proc_exit(void)
+void sctp_remaddr_proc_exit(struct net *net)
 {
-       remove_proc_entry("remaddr", proc_net_sctp);
+       remove_proc_entry("remaddr", net->sctp.proc_net_sctp);
 }
 
 static int sctp_remaddr_seq_open(struct inode *inode, struct file *file)
 {
-       return seq_open(file, &sctp_remaddr_ops);
+       return seq_open_net(inode, file, &sctp_remaddr_ops,
+                           sizeof(struct seq_net_private));
 }
 
 static const struct file_operations sctp_remaddr_seq_fops = {
@@ -506,11 +521,12 @@ static const struct file_operations sctp_remaddr_seq_fops = {
        .release = seq_release,
 };
 
-int __init sctp_remaddr_proc_init(void)
+int __net_init sctp_remaddr_proc_init(struct net *net)
 {
        struct proc_dir_entry *p;
 
-       p = proc_create("remaddr", S_IRUGO, proc_net_sctp, &sctp_remaddr_seq_fops);
+       p = proc_create("remaddr", S_IRUGO, net->sctp.proc_net_sctp,
+                       &sctp_remaddr_seq_fops);
        if (!p)
                return -ENOMEM;
        return 0;
index 1f89c4e696457fc02948066052713cac4d2f46fc..2d518425d5984bf954c6ebba3d7db0abb7ef5dc7 100644 (file)
 
 /* Global data structures. */
 struct sctp_globals sctp_globals __read_mostly;
-DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics) __read_mostly;
-
-#ifdef CONFIG_PROC_FS
-struct proc_dir_entry  *proc_net_sctp;
-#endif
 
 struct idr sctp_assocs_id;
 DEFINE_SPINLOCK(sctp_assocs_id_lock);
 
-/* This is the global socket data structure used for responding to
- * the Out-of-the-blue (OOTB) packets.  A control sock will be created
- * for this socket at the initialization time.
- */
-static struct sock *sctp_ctl_sock;
-
 static struct sctp_pf *sctp_pf_inet6_specific;
 static struct sctp_pf *sctp_pf_inet_specific;
 static struct sctp_af *sctp_af_v4_specific;
@@ -96,74 +85,54 @@ long sysctl_sctp_mem[3];
 int sysctl_sctp_rmem[3];
 int sysctl_sctp_wmem[3];
 
-/* Return the address of the control sock. */
-struct sock *sctp_get_ctl_sock(void)
-{
-       return sctp_ctl_sock;
-}
-
 /* Set up the proc fs entry for the SCTP protocol. */
-static __init int sctp_proc_init(void)
+static __net_init int sctp_proc_init(struct net *net)
 {
-       if (percpu_counter_init(&sctp_sockets_allocated, 0))
-               goto out_nomem;
 #ifdef CONFIG_PROC_FS
-       if (!proc_net_sctp) {
-               proc_net_sctp = proc_mkdir("sctp", init_net.proc_net);
-               if (!proc_net_sctp)
-                       goto out_free_percpu;
-       }
-
-       if (sctp_snmp_proc_init())
+       net->sctp.proc_net_sctp = proc_net_mkdir(net, "sctp", net->proc_net);
+       if (!net->sctp.proc_net_sctp)
+               goto out_proc_net_sctp;
+       if (sctp_snmp_proc_init(net))
                goto out_snmp_proc_init;
-       if (sctp_eps_proc_init())
+       if (sctp_eps_proc_init(net))
                goto out_eps_proc_init;
-       if (sctp_assocs_proc_init())
+       if (sctp_assocs_proc_init(net))
                goto out_assocs_proc_init;
-       if (sctp_remaddr_proc_init())
+       if (sctp_remaddr_proc_init(net))
                goto out_remaddr_proc_init;
 
        return 0;
 
 out_remaddr_proc_init:
-       sctp_assocs_proc_exit();
+       sctp_assocs_proc_exit(net);
 out_assocs_proc_init:
-       sctp_eps_proc_exit();
+       sctp_eps_proc_exit(net);
 out_eps_proc_init:
-       sctp_snmp_proc_exit();
+       sctp_snmp_proc_exit(net);
 out_snmp_proc_init:
-       if (proc_net_sctp) {
-               proc_net_sctp = NULL;
-               remove_proc_entry("sctp", init_net.proc_net);
-       }
-out_free_percpu:
-       percpu_counter_destroy(&sctp_sockets_allocated);
-#else
-       return 0;
-#endif /* CONFIG_PROC_FS */
-
-out_nomem:
+       remove_proc_entry("sctp", net->proc_net);
+       net->sctp.proc_net_sctp = NULL;
+out_proc_net_sctp:
        return -ENOMEM;
+#endif /* CONFIG_PROC_FS */
+       return 0;
 }
 
 /* Clean up the proc fs entry for the SCTP protocol.
  * Note: Do not make this __exit as it is used in the init error
  * path.
  */
-static void sctp_proc_exit(void)
+static void sctp_proc_exit(struct net *net)
 {
 #ifdef CONFIG_PROC_FS
-       sctp_snmp_proc_exit();
-       sctp_eps_proc_exit();
-       sctp_assocs_proc_exit();
-       sctp_remaddr_proc_exit();
-
-       if (proc_net_sctp) {
-               proc_net_sctp = NULL;
-               remove_proc_entry("sctp", init_net.proc_net);
-       }
+       sctp_snmp_proc_exit(net);
+       sctp_eps_proc_exit(net);
+       sctp_assocs_proc_exit(net);
+       sctp_remaddr_proc_exit(net);
+
+       remove_proc_entry("sctp", net->proc_net);
+       net->sctp.proc_net_sctp = NULL;
 #endif
-       percpu_counter_destroy(&sctp_sockets_allocated);
 }
 
 /* Private helper to extract ipv4 address and stash them in
@@ -201,29 +170,29 @@ static void sctp_v4_copy_addrlist(struct list_head *addrlist,
 /* Extract our IP addresses from the system and stash them in the
  * protocol structure.
  */
-static void sctp_get_local_addr_list(void)
+static void sctp_get_local_addr_list(struct net *net)
 {
        struct net_device *dev;
        struct list_head *pos;
        struct sctp_af *af;
 
        rcu_read_lock();
-       for_each_netdev_rcu(&init_net, dev) {
+       for_each_netdev_rcu(net, dev) {
                __list_for_each(pos, &sctp_address_families) {
                        af = list_entry(pos, struct sctp_af, list);
-                       af->copy_addrlist(&sctp_local_addr_list, dev);
+                       af->copy_addrlist(&net->sctp.local_addr_list, dev);
                }
        }
        rcu_read_unlock();
 }
 
 /* Free the existing local addresses.  */
-static void sctp_free_local_addr_list(void)
+static void sctp_free_local_addr_list(struct net *net)
 {
        struct sctp_sockaddr_entry *addr;
        struct list_head *pos, *temp;
 
-       list_for_each_safe(pos, temp, &sctp_local_addr_list) {
+       list_for_each_safe(pos, temp, &net->sctp.local_addr_list) {
                addr = list_entry(pos, struct sctp_sockaddr_entry, list);
                list_del(pos);
                kfree(addr);
@@ -231,17 +200,17 @@ static void sctp_free_local_addr_list(void)
 }
 
 /* Copy the local addresses which are valid for 'scope' into 'bp'.  */
-int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope,
-                             gfp_t gfp, int copy_flags)
+int sctp_copy_local_addr_list(struct net *net, struct sctp_bind_addr *bp,
+                             sctp_scope_t scope, gfp_t gfp, int copy_flags)
 {
        struct sctp_sockaddr_entry *addr;
        int error = 0;
 
        rcu_read_lock();
-       list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) {
+       list_for_each_entry_rcu(addr, &net->sctp.local_addr_list, list) {
                if (!addr->valid)
                        continue;
-               if (sctp_in_scope(&addr->a, scope)) {
+               if (sctp_in_scope(net, &addr->a, scope)) {
                        /* Now that the address is in scope, check to see if
                         * the address type is really supported by the local
                         * sock as well as the remote peer.
@@ -397,7 +366,8 @@ static int sctp_v4_addr_valid(union sctp_addr *addr,
 /* Should this be available for binding?   */
 static int sctp_v4_available(union sctp_addr *addr, struct sctp_sock *sp)
 {
-       int ret = inet_addr_type(&init_net, addr->v4.sin_addr.s_addr);
+       struct net *net = sock_net(&sp->inet.sk);
+       int ret = inet_addr_type(net, addr->v4.sin_addr.s_addr);
 
 
        if (addr->v4.sin_addr.s_addr != htonl(INADDR_ANY) &&
@@ -484,7 +454,7 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
        SCTP_DEBUG_PRINTK("%s: DST:%pI4, SRC:%pI4 - ",
                          __func__, &fl4->daddr, &fl4->saddr);
 
-       rt = ip_route_output_key(&init_net, fl4);
+       rt = ip_route_output_key(sock_net(sk), fl4);
        if (!IS_ERR(rt))
                dst = &rt->dst;
 
@@ -530,7 +500,7 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
                    (AF_INET == laddr->a.sa.sa_family)) {
                        fl4->saddr = laddr->a.v4.sin_addr.s_addr;
                        fl4->fl4_sport = laddr->a.v4.sin_port;
-                       rt = ip_route_output_key(&init_net, fl4);
+                       rt = ip_route_output_key(sock_net(sk), fl4);
                        if (!IS_ERR(rt)) {
                                dst = &rt->dst;
                                goto out_unlock;
@@ -627,14 +597,15 @@ static void sctp_v4_ecn_capable(struct sock *sk)
 
 void sctp_addr_wq_timeout_handler(unsigned long arg)
 {
+       struct net *net = (struct net *)arg;
        struct sctp_sockaddr_entry *addrw, *temp;
        struct sctp_sock *sp;
 
-       spin_lock_bh(&sctp_addr_wq_lock);
+       spin_lock_bh(&net->sctp.addr_wq_lock);
 
-       list_for_each_entry_safe(addrw, temp, &sctp_addr_waitq, list) {
+       list_for_each_entry_safe(addrw, temp, &net->sctp.addr_waitq, list) {
                SCTP_DEBUG_PRINTK_IPADDR("sctp_addrwq_timo_handler: the first ent in wq %p is ",
-                   " for cmd %d at entry %p\n", &sctp_addr_waitq, &addrw->a, addrw->state,
+                   " for cmd %d at entry %p\n", &net->sctp.addr_waitq, &addrw->a, addrw->state,
                    addrw);
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -648,7 +619,7 @@ void sctp_addr_wq_timeout_handler(unsigned long arg)
                                goto free_next;
 
                        in6 = (struct in6_addr *)&addrw->a.v6.sin6_addr;
-                       if (ipv6_chk_addr(&init_net, in6, NULL, 0) == 0 &&
+                       if (ipv6_chk_addr(net, in6, NULL, 0) == 0 &&
                            addrw->state == SCTP_ADDR_NEW) {
                                unsigned long timeo_val;
 
@@ -656,12 +627,12 @@ void sctp_addr_wq_timeout_handler(unsigned long arg)
                                    SCTP_ADDRESS_TICK_DELAY);
                                timeo_val = jiffies;
                                timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY);
-                               mod_timer(&sctp_addr_wq_timer, timeo_val);
+                               mod_timer(&net->sctp.addr_wq_timer, timeo_val);
                                break;
                        }
                }
 #endif
-               list_for_each_entry(sp, &sctp_auto_asconf_splist, auto_asconf_list) {
+               list_for_each_entry(sp, &net->sctp.auto_asconf_splist, auto_asconf_list) {
                        struct sock *sk;
 
                        sk = sctp_opt2sk(sp);
@@ -679,31 +650,32 @@ free_next:
                list_del(&addrw->list);
                kfree(addrw);
        }
-       spin_unlock_bh(&sctp_addr_wq_lock);
+       spin_unlock_bh(&net->sctp.addr_wq_lock);
 }
 
-static void sctp_free_addr_wq(void)
+static void sctp_free_addr_wq(struct net *net)
 {
        struct sctp_sockaddr_entry *addrw;
        struct sctp_sockaddr_entry *temp;
 
-       spin_lock_bh(&sctp_addr_wq_lock);
-       del_timer(&sctp_addr_wq_timer);
-       list_for_each_entry_safe(addrw, temp, &sctp_addr_waitq, list) {
+       spin_lock_bh(&net->sctp.addr_wq_lock);
+       del_timer(&net->sctp.addr_wq_timer);
+       list_for_each_entry_safe(addrw, temp, &net->sctp.addr_waitq, list) {
                list_del(&addrw->list);
                kfree(addrw);
        }
-       spin_unlock_bh(&sctp_addr_wq_lock);
+       spin_unlock_bh(&net->sctp.addr_wq_lock);
 }
 
 /* lookup the entry for the same address in the addr_waitq
  * sctp_addr_wq MUST be locked
  */
-static struct sctp_sockaddr_entry *sctp_addr_wq_lookup(struct sctp_sockaddr_entry *addr)
+static struct sctp_sockaddr_entry *sctp_addr_wq_lookup(struct net *net,
+                                       struct sctp_sockaddr_entry *addr)
 {
        struct sctp_sockaddr_entry *addrw;
 
-       list_for_each_entry(addrw, &sctp_addr_waitq, list) {
+       list_for_each_entry(addrw, &net->sctp.addr_waitq, list) {
                if (addrw->a.sa.sa_family != addr->a.sa.sa_family)
                        continue;
                if (addrw->a.sa.sa_family == AF_INET) {
@@ -719,7 +691,7 @@ static struct sctp_sockaddr_entry *sctp_addr_wq_lookup(struct sctp_sockaddr_entr
        return NULL;
 }
 
-void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *addr, int cmd)
+void sctp_addr_wq_mgmt(struct net *net, struct sctp_sockaddr_entry *addr, int cmd)
 {
        struct sctp_sockaddr_entry *addrw;
        unsigned long timeo_val;
@@ -730,38 +702,38 @@ void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *addr, int cmd)
         * new address after a couple of addition and deletion of that address
         */
 
-       spin_lock_bh(&sctp_addr_wq_lock);
+       spin_lock_bh(&net->sctp.addr_wq_lock);
        /* Offsets existing events in addr_wq */
-       addrw = sctp_addr_wq_lookup(addr);
+       addrw = sctp_addr_wq_lookup(net, addr);
        if (addrw) {
                if (addrw->state != cmd) {
                        SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt offsets existing entry for %d ",
                            " in wq %p\n", addrw->state, &addrw->a,
-                           &sctp_addr_waitq);
+                           &net->sctp.addr_waitq);
                        list_del(&addrw->list);
                        kfree(addrw);
                }
-               spin_unlock_bh(&sctp_addr_wq_lock);
+               spin_unlock_bh(&net->sctp.addr_wq_lock);
                return;
        }
 
        /* OK, we have to add the new address to the wait queue */
        addrw = kmemdup(addr, sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC);
        if (addrw == NULL) {
-               spin_unlock_bh(&sctp_addr_wq_lock);
+               spin_unlock_bh(&net->sctp.addr_wq_lock);
                return;
        }
        addrw->state = cmd;
-       list_add_tail(&addrw->list, &sctp_addr_waitq);
+       list_add_tail(&addrw->list, &net->sctp.addr_waitq);
        SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt add new entry for cmd:%d ",
-           " in wq %p\n", addrw->state, &addrw->a, &sctp_addr_waitq);
+           " in wq %p\n", addrw->state, &addrw->a, &net->sctp.addr_waitq);
 
-       if (!timer_pending(&sctp_addr_wq_timer)) {
+       if (!timer_pending(&net->sctp.addr_wq_timer)) {
                timeo_val = jiffies;
                timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY);
-               mod_timer(&sctp_addr_wq_timer, timeo_val);
+               mod_timer(&net->sctp.addr_wq_timer, timeo_val);
        }
-       spin_unlock_bh(&sctp_addr_wq_lock);
+       spin_unlock_bh(&net->sctp.addr_wq_lock);
 }
 
 /* Event handler for inet address addition/deletion events.
@@ -776,11 +748,9 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
        struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
        struct sctp_sockaddr_entry *addr = NULL;
        struct sctp_sockaddr_entry *temp;
+       struct net *net = dev_net(ifa->ifa_dev->dev);
        int found = 0;
 
-       if (!net_eq(dev_net(ifa->ifa_dev->dev), &init_net))
-               return NOTIFY_DONE;
-
        switch (ev) {
        case NETDEV_UP:
                addr = kmalloc(sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC);
@@ -789,27 +759,27 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
                        addr->a.v4.sin_port = 0;
                        addr->a.v4.sin_addr.s_addr = ifa->ifa_local;
                        addr->valid = 1;
-                       spin_lock_bh(&sctp_local_addr_lock);
-                       list_add_tail_rcu(&addr->list, &sctp_local_addr_list);
-                       sctp_addr_wq_mgmt(addr, SCTP_ADDR_NEW);
-                       spin_unlock_bh(&sctp_local_addr_lock);
+                       spin_lock_bh(&net->sctp.local_addr_lock);
+                       list_add_tail_rcu(&addr->list, &net->sctp.local_addr_list);
+                       sctp_addr_wq_mgmt(net, addr, SCTP_ADDR_NEW);
+                       spin_unlock_bh(&net->sctp.local_addr_lock);
                }
                break;
        case NETDEV_DOWN:
-               spin_lock_bh(&sctp_local_addr_lock);
+               spin_lock_bh(&net->sctp.local_addr_lock);
                list_for_each_entry_safe(addr, temp,
-                                       &sctp_local_addr_list, list) {
+                                       &net->sctp.local_addr_list, list) {
                        if (addr->a.sa.sa_family == AF_INET &&
                                        addr->a.v4.sin_addr.s_addr ==
                                        ifa->ifa_local) {
-                               sctp_addr_wq_mgmt(addr, SCTP_ADDR_DEL);
+                               sctp_addr_wq_mgmt(net, addr, SCTP_ADDR_DEL);
                                found = 1;
                                addr->valid = 0;
                                list_del_rcu(&addr->list);
                                break;
                        }
                }
-               spin_unlock_bh(&sctp_local_addr_lock);
+               spin_unlock_bh(&net->sctp.local_addr_lock);
                if (found)
                        kfree_rcu(addr, rcu);
                break;
@@ -822,7 +792,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
  * Initialize the control inode/socket with a control endpoint data
  * structure.  This endpoint is reserved exclusively for the OOTB processing.
  */
-static int sctp_ctl_sock_init(void)
+static int sctp_ctl_sock_init(struct net *net)
 {
        int err;
        sa_family_t family = PF_INET;
@@ -830,14 +800,14 @@ static int sctp_ctl_sock_init(void)
        if (sctp_get_pf_specific(PF_INET6))
                family = PF_INET6;
 
-       err = inet_ctl_sock_create(&sctp_ctl_sock, family,
-                                  SOCK_SEQPACKET, IPPROTO_SCTP, &init_net);
+       err = inet_ctl_sock_create(&net->sctp.ctl_sock, family,
+                                  SOCK_SEQPACKET, IPPROTO_SCTP, net);
 
        /* If IPv6 socket could not be created, try the IPv4 socket */
        if (err < 0 && family == PF_INET6)
-               err = inet_ctl_sock_create(&sctp_ctl_sock, AF_INET,
+               err = inet_ctl_sock_create(&net->sctp.ctl_sock, AF_INET,
                                           SOCK_SEQPACKET, IPPROTO_SCTP,
-                                          &init_net);
+                                          net);
 
        if (err < 0) {
                pr_err("Failed to create the SCTP control socket\n");
@@ -990,7 +960,7 @@ static inline int sctp_v4_xmit(struct sk_buff *skb,
        inet->pmtudisc = transport->param_flags & SPP_PMTUD_ENABLE ?
                         IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
 
-       SCTP_INC_STATS(SCTP_MIB_OUTSCTPPACKS);
+       SCTP_INC_STATS(sock_net(&inet->sk), SCTP_MIB_OUTSCTPPACKS);
        return ip_queue_xmit(skb, &transport->fl);
 }
 
@@ -1063,6 +1033,7 @@ static const struct net_protocol sctp_protocol = {
        .handler     = sctp_rcv,
        .err_handler = sctp_v4_err,
        .no_policy   = 1,
+       .netns_ok    = 1,
 };
 
 /* IPv4 address related functions.  */
@@ -1130,16 +1101,16 @@ int sctp_register_pf(struct sctp_pf *pf, sa_family_t family)
        return 1;
 }
 
-static inline int init_sctp_mibs(void)
+static inline int init_sctp_mibs(struct net *net)
 {
-       return snmp_mib_init((void __percpu **)sctp_statistics,
+       return snmp_mib_init((void __percpu **)net->sctp.sctp_statistics,
                             sizeof(struct sctp_mib),
                             __alignof__(struct sctp_mib));
 }
 
-static inline void cleanup_sctp_mibs(void)
+static inline void cleanup_sctp_mibs(struct net *net)
 {
-       snmp_mib_free((void __percpu **)sctp_statistics);
+       snmp_mib_free((void __percpu **)net->sctp.sctp_statistics);
 }
 
 static void sctp_v4_pf_init(void)
@@ -1194,6 +1165,143 @@ static void sctp_v4_del_protocol(void)
        unregister_inetaddr_notifier(&sctp_inetaddr_notifier);
 }
 
+static int sctp_net_init(struct net *net)
+{
+       int status;
+
+       /*
+        * 14. Suggested SCTP Protocol Parameter Values
+        */
+       /* The following protocol parameters are RECOMMENDED:  */
+       /* RTO.Initial              - 3  seconds */
+       net->sctp.rto_initial                   = SCTP_RTO_INITIAL;
+       /* RTO.Min                  - 1  second */
+       net->sctp.rto_min                       = SCTP_RTO_MIN;
+       /* RTO.Max                 -  60 seconds */
+       net->sctp.rto_max                       = SCTP_RTO_MAX;
+       /* RTO.Alpha                - 1/8 */
+       net->sctp.rto_alpha                     = SCTP_RTO_ALPHA;
+       /* RTO.Beta                 - 1/4 */
+       net->sctp.rto_beta                      = SCTP_RTO_BETA;
+
+       /* Valid.Cookie.Life        - 60  seconds */
+       net->sctp.valid_cookie_life             = SCTP_DEFAULT_COOKIE_LIFE;
+
+       /* Whether Cookie Preservative is enabled(1) or not(0) */
+       net->sctp.cookie_preserve_enable        = 1;
+
+       /* Max.Burst                - 4 */
+       net->sctp.max_burst                     = SCTP_DEFAULT_MAX_BURST;
+
+       /* Association.Max.Retrans  - 10 attempts
+        * Path.Max.Retrans         - 5  attempts (per destination address)
+        * Max.Init.Retransmits     - 8  attempts
+        */
+       net->sctp.max_retrans_association       = 10;
+       net->sctp.max_retrans_path              = 5;
+       net->sctp.max_retrans_init              = 8;
+
+       /* Sendbuffer growth        - do per-socket accounting */
+       net->sctp.sndbuf_policy                 = 0;
+
+       /* Rcvbuffer growth         - do per-socket accounting */
+       net->sctp.rcvbuf_policy                 = 0;
+
+       /* HB.interval              - 30 seconds */
+       net->sctp.hb_interval                   = SCTP_DEFAULT_TIMEOUT_HEARTBEAT;
+
+       /* delayed SACK timeout */
+       net->sctp.sack_timeout                  = SCTP_DEFAULT_TIMEOUT_SACK;
+
+       /* Disable ADDIP by default. */
+       net->sctp.addip_enable = 0;
+       net->sctp.addip_noauth = 0;
+       net->sctp.default_auto_asconf = 0;
+
+       /* Enable PR-SCTP by default. */
+       net->sctp.prsctp_enable = 1;
+
+       /* Disable AUTH by default. */
+       net->sctp.auth_enable = 0;
+
+       /* Set SCOPE policy to enabled */
+       net->sctp.scope_policy = SCTP_SCOPE_POLICY_ENABLE;
+
+       /* Set the default rwnd update threshold */
+       net->sctp.rwnd_upd_shift = SCTP_DEFAULT_RWND_SHIFT;
+
+       /* Initialize maximum autoclose timeout. */
+       net->sctp.max_autoclose         = INT_MAX / HZ;
+
+       status = sctp_sysctl_net_register(net);
+       if (status)
+               goto err_sysctl_register;
+
+       /* Allocate and initialise sctp mibs.  */
+       status = init_sctp_mibs(net);
+       if (status)
+               goto err_init_mibs;
+
+       /* Initialize proc fs directory.  */
+       status = sctp_proc_init(net);
+       if (status)
+               goto err_init_proc;
+
+       sctp_dbg_objcnt_init(net);
+
+       /* Initialize the control inode/socket for handling OOTB packets.  */
+       if ((status = sctp_ctl_sock_init(net))) {
+               pr_err("Failed to initialize the SCTP control sock\n");
+               goto err_ctl_sock_init;
+       }
+
+       /* Initialize the local address list. */
+       INIT_LIST_HEAD(&net->sctp.local_addr_list);
+       spin_lock_init(&net->sctp.local_addr_lock);
+       sctp_get_local_addr_list(net);
+
+       /* Initialize the address event list */
+       INIT_LIST_HEAD(&net->sctp.addr_waitq);
+       INIT_LIST_HEAD(&net->sctp.auto_asconf_splist);
+       spin_lock_init(&net->sctp.addr_wq_lock);
+       net->sctp.addr_wq_timer.expires = 0;
+       setup_timer(&net->sctp.addr_wq_timer, sctp_addr_wq_timeout_handler,
+                   (unsigned long)net);
+
+       return 0;
+
+err_ctl_sock_init:
+       sctp_dbg_objcnt_exit(net);
+       sctp_proc_exit(net);
+err_init_proc:
+       cleanup_sctp_mibs(net);
+err_init_mibs:
+       sctp_sysctl_net_unregister(net);
+err_sysctl_register:
+       return status;
+}
+
+static void sctp_net_exit(struct net *net)
+{
+       /* Free the local address list */
+       sctp_free_addr_wq(net);
+       sctp_free_local_addr_list(net);
+
+       /* Free the control endpoint.  */
+       inet_ctl_sock_destroy(net->sctp.ctl_sock);
+
+       sctp_dbg_objcnt_exit(net);
+
+       sctp_proc_exit(net);
+       cleanup_sctp_mibs(net);
+       sctp_sysctl_net_unregister(net);
+}
+
+static struct pernet_operations sctp_net_ops = {
+       .init = sctp_net_init,
+       .exit = sctp_net_exit,
+};
+
 /* Initialize the universe into something sensible.  */
 SCTP_STATIC __init int sctp_init(void)
 {
@@ -1224,62 +1332,9 @@ SCTP_STATIC __init int sctp_init(void)
        if (!sctp_chunk_cachep)
                goto err_chunk_cachep;
 
-       /* Allocate and initialise sctp mibs.  */
-       status = init_sctp_mibs();
+       status = percpu_counter_init(&sctp_sockets_allocated, 0);
        if (status)
-               goto err_init_mibs;
-
-       /* Initialize proc fs directory.  */
-       status = sctp_proc_init();
-       if (status)
-               goto err_init_proc;
-
-       /* Initialize object count debugging.  */
-       sctp_dbg_objcnt_init();
-
-       /*
-        * 14. Suggested SCTP Protocol Parameter Values
-        */
-       /* The following protocol parameters are RECOMMENDED:  */
-       /* RTO.Initial              - 3  seconds */
-       sctp_rto_initial                = SCTP_RTO_INITIAL;
-       /* RTO.Min                  - 1  second */
-       sctp_rto_min                    = SCTP_RTO_MIN;
-       /* RTO.Max                 -  60 seconds */
-       sctp_rto_max                    = SCTP_RTO_MAX;
-       /* RTO.Alpha                - 1/8 */
-       sctp_rto_alpha                  = SCTP_RTO_ALPHA;
-       /* RTO.Beta                 - 1/4 */
-       sctp_rto_beta                   = SCTP_RTO_BETA;
-
-       /* Valid.Cookie.Life        - 60  seconds */
-       sctp_valid_cookie_life          = SCTP_DEFAULT_COOKIE_LIFE;
-
-       /* Whether Cookie Preservative is enabled(1) or not(0) */
-       sctp_cookie_preserve_enable     = 1;
-
-       /* Max.Burst                - 4 */
-       sctp_max_burst                  = SCTP_DEFAULT_MAX_BURST;
-
-       /* Association.Max.Retrans  - 10 attempts
-        * Path.Max.Retrans         - 5  attempts (per destination address)
-        * Max.Init.Retransmits     - 8  attempts
-        */
-       sctp_max_retrans_association    = 10;
-       sctp_max_retrans_path           = 5;
-       sctp_max_retrans_init           = 8;
-
-       /* Sendbuffer growth        - do per-socket accounting */
-       sctp_sndbuf_policy              = 0;
-
-       /* Rcvbuffer growth         - do per-socket accounting */
-       sctp_rcvbuf_policy              = 0;
-
-       /* HB.interval              - 30 seconds */
-       sctp_hb_interval                = SCTP_DEFAULT_TIMEOUT_HEARTBEAT;
-
-       /* delayed SACK timeout */
-       sctp_sack_timeout               = SCTP_DEFAULT_TIMEOUT_SACK;
+               goto err_percpu_counter_init;
 
        /* Implementation specific variables. */
 
@@ -1287,9 +1342,6 @@ SCTP_STATIC __init int sctp_init(void)
        sctp_max_instreams              = SCTP_DEFAULT_INSTREAMS;
        sctp_max_outstreams             = SCTP_DEFAULT_OUTSTREAMS;
 
-       /* Initialize maximum autoclose timeout. */
-       sctp_max_autoclose              = INT_MAX / HZ;
-
        /* Initialize handle used for association ids. */
        idr_init(&sctp_assocs_id);
 
@@ -1376,41 +1428,12 @@ SCTP_STATIC __init int sctp_init(void)
        pr_info("Hash tables configured (established %d bind %d)\n",
                sctp_assoc_hashsize, sctp_port_hashsize);
 
-       /* Disable ADDIP by default. */
-       sctp_addip_enable = 0;
-       sctp_addip_noauth = 0;
-       sctp_default_auto_asconf = 0;
-
-       /* Enable PR-SCTP by default. */
-       sctp_prsctp_enable = 1;
-
-       /* Disable AUTH by default. */
-       sctp_auth_enable = 0;
-
-       /* Set SCOPE policy to enabled */
-       sctp_scope_policy = SCTP_SCOPE_POLICY_ENABLE;
-
-       /* Set the default rwnd update threshold */
-       sctp_rwnd_upd_shift             = SCTP_DEFAULT_RWND_SHIFT;
-
        sctp_sysctl_register();
 
        INIT_LIST_HEAD(&sctp_address_families);
        sctp_v4_pf_init();
        sctp_v6_pf_init();
 
-       /* Initialize the local address list. */
-       INIT_LIST_HEAD(&sctp_local_addr_list);
-       spin_lock_init(&sctp_local_addr_lock);
-       sctp_get_local_addr_list();
-
-       /* Initialize the address event list */
-       INIT_LIST_HEAD(&sctp_addr_waitq);
-       INIT_LIST_HEAD(&sctp_auto_asconf_splist);
-       spin_lock_init(&sctp_addr_wq_lock);
-       sctp_addr_wq_timer.expires = 0;
-       setup_timer(&sctp_addr_wq_timer, sctp_addr_wq_timeout_handler, 0);
-
        status = sctp_v4_protosw_init();
 
        if (status)
@@ -1420,11 +1443,9 @@ SCTP_STATIC __init int sctp_init(void)
        if (status)
                goto err_v6_protosw_init;
 
-       /* Initialize the control inode/socket for handling OOTB packets.  */
-       if ((status = sctp_ctl_sock_init())) {
-               pr_err("Failed to initialize the SCTP control sock\n");
-               goto err_ctl_sock_init;
-       }
+       status = register_pernet_subsys(&sctp_net_ops);
+       if (status)
+               goto err_register_pernet_subsys;
 
        status = sctp_v4_add_protocol();
        if (status)
@@ -1441,13 +1462,12 @@ out:
 err_v6_add_protocol:
        sctp_v4_del_protocol();
 err_add_protocol:
-       inet_ctl_sock_destroy(sctp_ctl_sock);
-err_ctl_sock_init:
+       unregister_pernet_subsys(&sctp_net_ops);
+err_register_pernet_subsys:
        sctp_v6_protosw_exit();
 err_v6_protosw_init:
        sctp_v4_protosw_exit();
 err_protosw_init:
-       sctp_free_local_addr_list();
        sctp_v4_pf_exit();
        sctp_v6_pf_exit();
        sctp_sysctl_unregister();
@@ -1461,11 +1481,8 @@ err_ehash_alloc:
                   get_order(sctp_assoc_hashsize *
                             sizeof(struct sctp_hashbucket)));
 err_ahash_alloc:
-       sctp_dbg_objcnt_exit();
-       sctp_proc_exit();
-err_init_proc:
-       cleanup_sctp_mibs();
-err_init_mibs:
+       percpu_counter_destroy(&sctp_sockets_allocated);
+err_percpu_counter_init:
        kmem_cache_destroy(sctp_chunk_cachep);
 err_chunk_cachep:
        kmem_cache_destroy(sctp_bucket_cachep);
@@ -1482,18 +1499,13 @@ SCTP_STATIC __exit void sctp_exit(void)
        /* Unregister with inet6/inet layers. */
        sctp_v6_del_protocol();
        sctp_v4_del_protocol();
-       sctp_free_addr_wq();
 
-       /* Free the control endpoint.  */
-       inet_ctl_sock_destroy(sctp_ctl_sock);
+       unregister_pernet_subsys(&sctp_net_ops);
 
        /* Free protosw registrations */
        sctp_v6_protosw_exit();
        sctp_v4_protosw_exit();
 
-       /* Free the local address list.  */
-       sctp_free_local_addr_list();
-
        /* Unregister with socket layer. */
        sctp_v6_pf_exit();
        sctp_v4_pf_exit();
@@ -1508,9 +1520,7 @@ SCTP_STATIC __exit void sctp_exit(void)
                   get_order(sctp_port_hashsize *
                             sizeof(struct sctp_bind_hashbucket)));
 
-       sctp_dbg_objcnt_exit();
-       sctp_proc_exit();
-       cleanup_sctp_mibs();
+       percpu_counter_destroy(&sctp_sockets_allocated);
 
        rcu_barrier(); /* Wait for completion of call_rcu()'s */
 
index 479a70ef6ff8abe2a59a16ddc672f6638c7b3369..fbe1636309a75ac054de225fe4d1cf245a3923d2 100644 (file)
@@ -198,6 +198,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
                             const struct sctp_bind_addr *bp,
                             gfp_t gfp, int vparam_len)
 {
+       struct net *net = sock_net(asoc->base.sk);
        sctp_inithdr_t init;
        union sctp_params addrs;
        size_t chunksize;
@@ -237,7 +238,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
        chunksize += WORD_ROUND(SCTP_SAT_LEN(num_types));
        chunksize += sizeof(ecap_param);
 
-       if (sctp_prsctp_enable)
+       if (net->sctp.prsctp_enable)
                chunksize += sizeof(prsctp_param);
 
        /* ADDIP: Section 4.2.7:
@@ -245,7 +246,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
         *  the ASCONF,the ASCONF-ACK, and the AUTH  chunks in its INIT and
         *  INIT-ACK parameters.
         */
-       if (sctp_addip_enable) {
+       if (net->sctp.addip_enable) {
                extensions[num_ext] = SCTP_CID_ASCONF;
                extensions[num_ext+1] = SCTP_CID_ASCONF_ACK;
                num_ext += 2;
@@ -257,7 +258,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
        chunksize += vparam_len;
 
        /* Account for AUTH related parameters */
-       if (sctp_auth_enable) {
+       if (net->sctp.auth_enable) {
                /* Add random parameter length*/
                chunksize += sizeof(asoc->c.auth_random);
 
@@ -331,7 +332,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
                sctp_addto_param(retval, num_ext, extensions);
        }
 
-       if (sctp_prsctp_enable)
+       if (net->sctp.prsctp_enable)
                sctp_addto_chunk(retval, sizeof(prsctp_param), &prsctp_param);
 
        if (sp->adaptation_ind) {
@@ -342,7 +343,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
        }
 
        /* Add SCTP-AUTH chunks to the parameter list */
-       if (sctp_auth_enable) {
+       if (net->sctp.auth_enable) {
                sctp_addto_chunk(retval, sizeof(asoc->c.auth_random),
                                 asoc->c.auth_random);
                if (auth_hmacs)
@@ -1940,7 +1941,7 @@ static int sctp_process_hn_param(const struct sctp_association *asoc,
        return 0;
 }
 
-static int sctp_verify_ext_param(union sctp_params param)
+static int sctp_verify_ext_param(struct net *net, union sctp_params param)
 {
        __u16 num_ext = ntohs(param.p->length) - sizeof(sctp_paramhdr_t);
        int have_auth = 0;
@@ -1964,10 +1965,10 @@ static int sctp_verify_ext_param(union sctp_params param)
         * only if ADD-IP is turned on and we are not backward-compatible
         * mode.
         */
-       if (sctp_addip_noauth)
+       if (net->sctp.addip_noauth)
                return 1;
 
-       if (sctp_addip_enable && !have_auth && have_asconf)
+       if (net->sctp.addip_enable && !have_auth && have_asconf)
                return 0;
 
        return 1;
@@ -1976,13 +1977,14 @@ static int sctp_verify_ext_param(union sctp_params param)
 static void sctp_process_ext_param(struct sctp_association *asoc,
                                    union sctp_params param)
 {
+       struct net *net = sock_net(asoc->base.sk);
        __u16 num_ext = ntohs(param.p->length) - sizeof(sctp_paramhdr_t);
        int i;
 
        for (i = 0; i < num_ext; i++) {
                switch (param.ext->chunks[i]) {
                    case SCTP_CID_FWD_TSN:
-                           if (sctp_prsctp_enable &&
+                           if (net->sctp.prsctp_enable &&
                                !asoc->peer.prsctp_capable)
                                    asoc->peer.prsctp_capable = 1;
                            break;
@@ -1990,12 +1992,12 @@ static void sctp_process_ext_param(struct sctp_association *asoc,
                            /* if the peer reports AUTH, assume that he
                             * supports AUTH.
                             */
-                           if (sctp_auth_enable)
+                           if (net->sctp.auth_enable)
                                    asoc->peer.auth_capable = 1;
                            break;
                    case SCTP_CID_ASCONF:
                    case SCTP_CID_ASCONF_ACK:
-                           if (sctp_addip_enable)
+                           if (net->sctp.addip_enable)
                                    asoc->peer.asconf_capable = 1;
                            break;
                    default:
@@ -2081,7 +2083,8 @@ static sctp_ierror_t sctp_process_unk_param(const struct sctp_association *asoc,
  *     SCTP_IERROR_ERROR - stop processing, trigger an ERROR
  *     SCTP_IERROR_NO_ERROR - continue with the chunk
  */
-static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc,
+static sctp_ierror_t sctp_verify_param(struct net *net,
+                                       const struct sctp_association *asoc,
                                        union sctp_params param,
                                        sctp_cid_t cid,
                                        struct sctp_chunk *chunk,
@@ -2110,12 +2113,12 @@ static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc,
                break;
 
        case SCTP_PARAM_SUPPORTED_EXT:
-               if (!sctp_verify_ext_param(param))
+               if (!sctp_verify_ext_param(net, param))
                        return SCTP_IERROR_ABORT;
                break;
 
        case SCTP_PARAM_SET_PRIMARY:
-               if (sctp_addip_enable)
+               if (net->sctp.addip_enable)
                        break;
                goto fallthrough;
 
@@ -2126,12 +2129,12 @@ static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc,
                break;
 
        case SCTP_PARAM_FWD_TSN_SUPPORT:
-               if (sctp_prsctp_enable)
+               if (net->sctp.prsctp_enable)
                        break;
                goto fallthrough;
 
        case SCTP_PARAM_RANDOM:
-               if (!sctp_auth_enable)
+               if (!net->sctp.auth_enable)
                        goto fallthrough;
 
                /* SCTP-AUTH: Secion 6.1
@@ -2148,7 +2151,7 @@ static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc,
                break;
 
        case SCTP_PARAM_CHUNKS:
-               if (!sctp_auth_enable)
+               if (!net->sctp.auth_enable)
                        goto fallthrough;
 
                /* SCTP-AUTH: Section 3.2
@@ -2164,7 +2167,7 @@ static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc,
                break;
 
        case SCTP_PARAM_HMAC_ALGO:
-               if (!sctp_auth_enable)
+               if (!net->sctp.auth_enable)
                        goto fallthrough;
 
                hmacs = (struct sctp_hmac_algo_param *)param.p;
@@ -2198,7 +2201,7 @@ fallthrough:
 }
 
 /* Verify the INIT packet before we process it.  */
-int sctp_verify_init(const struct sctp_association *asoc,
+int sctp_verify_init(struct net *net, const struct sctp_association *asoc,
                     sctp_cid_t cid,
                     sctp_init_chunk_t *peer_init,
                     struct sctp_chunk *chunk,
@@ -2245,7 +2248,7 @@ int sctp_verify_init(const struct sctp_association *asoc,
        /* Verify all the variable length parameters */
        sctp_walk_params(param, peer_init, init_hdr.params) {
 
-               result = sctp_verify_param(asoc, param, cid, chunk, errp);
+               result = sctp_verify_param(net, asoc, param, cid, chunk, errp);
                switch (result) {
                    case SCTP_IERROR_ABORT:
                    case SCTP_IERROR_NOMEM:
@@ -2270,6 +2273,7 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk,
                      const union sctp_addr *peer_addr,
                      sctp_init_chunk_t *peer_init, gfp_t gfp)
 {
+       struct net *net = sock_net(asoc->base.sk);
        union sctp_params param;
        struct sctp_transport *transport;
        struct list_head *pos, *temp;
@@ -2326,7 +2330,7 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk,
         * also give us an option to silently ignore the packet, which
         * is what we'll do here.
         */
-       if (!sctp_addip_noauth &&
+       if (!net->sctp.addip_noauth &&
             (asoc->peer.asconf_capable && !asoc->peer.auth_capable)) {
                asoc->peer.addip_disabled_mask |= (SCTP_PARAM_ADD_IP |
                                                  SCTP_PARAM_DEL_IP |
@@ -2466,6 +2470,7 @@ static int sctp_process_param(struct sctp_association *asoc,
                              const union sctp_addr *peer_addr,
                              gfp_t gfp)
 {
+       struct net *net = sock_net(asoc->base.sk);
        union sctp_addr addr;
        int i;
        __u16 sat;
@@ -2494,13 +2499,13 @@ do_addr_param:
                af = sctp_get_af_specific(param_type2af(param.p->type));
                af->from_addr_param(&addr, param.addr, htons(asoc->peer.port), 0);
                scope = sctp_scope(peer_addr);
-               if (sctp_in_scope(&addr, scope))
+               if (sctp_in_scope(net, &addr, scope))
                        if (!sctp_assoc_add_peer(asoc, &addr, gfp, SCTP_UNCONFIRMED))
                                return 0;
                break;
 
        case SCTP_PARAM_COOKIE_PRESERVATIVE:
-               if (!sctp_cookie_preserve_enable)
+               if (!net->sctp.cookie_preserve_enable)
                        break;
 
                stale = ntohl(param.life->lifespan_increment);
@@ -2580,7 +2585,7 @@ do_addr_param:
                break;
 
        case SCTP_PARAM_SET_PRIMARY:
-               if (!sctp_addip_enable)
+               if (!net->sctp.addip_enable)
                        goto fall_through;
 
                addr_param = param.v + sizeof(sctp_addip_param_t);
@@ -2607,7 +2612,7 @@ do_addr_param:
                break;
 
        case SCTP_PARAM_FWD_TSN_SUPPORT:
-               if (sctp_prsctp_enable) {
+               if (net->sctp.prsctp_enable) {
                        asoc->peer.prsctp_capable = 1;
                        break;
                }
@@ -2615,7 +2620,7 @@ do_addr_param:
                goto fall_through;
 
        case SCTP_PARAM_RANDOM:
-               if (!sctp_auth_enable)
+               if (!net->sctp.auth_enable)
                        goto fall_through;
 
                /* Save peer's random parameter */
@@ -2628,7 +2633,7 @@ do_addr_param:
                break;
 
        case SCTP_PARAM_HMAC_ALGO:
-               if (!sctp_auth_enable)
+               if (!net->sctp.auth_enable)
                        goto fall_through;
 
                /* Save peer's HMAC list */
@@ -2644,7 +2649,7 @@ do_addr_param:
                break;
 
        case SCTP_PARAM_CHUNKS:
-               if (!sctp_auth_enable)
+               if (!net->sctp.auth_enable)
                        goto fall_through;
 
                asoc->peer.peer_chunks = kmemdup(param.p,
index fe99628e1257bd1173dadfa1826a0f2d5f35c7f6..bcfebb91559d1d9e9a0b6472986f626021a3f358 100644 (file)
@@ -251,6 +251,7 @@ void sctp_generate_t3_rtx_event(unsigned long peer)
        int error;
        struct sctp_transport *transport = (struct sctp_transport *) peer;
        struct sctp_association *asoc = transport->asoc;
+       struct net *net = sock_net(asoc->base.sk);
 
        /* Check whether a task is in the sock.  */
 
@@ -271,7 +272,7 @@ void sctp_generate_t3_rtx_event(unsigned long peer)
                goto out_unlock;
 
        /* Run through the state machine.  */
-       error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT,
+       error = sctp_do_sm(net, SCTP_EVENT_T_TIMEOUT,
                           SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_T3_RTX),
                           asoc->state,
                           asoc->ep, asoc,
@@ -291,6 +292,7 @@ out_unlock:
 static void sctp_generate_timeout_event(struct sctp_association *asoc,
                                        sctp_event_timeout_t timeout_type)
 {
+       struct net *net = sock_net(asoc->base.sk);
        int error = 0;
 
        sctp_bh_lock_sock(asoc->base.sk);
@@ -312,7 +314,7 @@ static void sctp_generate_timeout_event(struct sctp_association *asoc,
                goto out_unlock;
 
        /* Run through the state machine.  */
-       error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT,
+       error = sctp_do_sm(net, SCTP_EVENT_T_TIMEOUT,
                           SCTP_ST_TIMEOUT(timeout_type),
                           asoc->state, asoc->ep, asoc,
                           (void *)timeout_type, GFP_ATOMIC);
@@ -371,6 +373,7 @@ void sctp_generate_heartbeat_event(unsigned long data)
        int error = 0;
        struct sctp_transport *transport = (struct sctp_transport *) data;
        struct sctp_association *asoc = transport->asoc;
+       struct net *net = sock_net(asoc->base.sk);
 
        sctp_bh_lock_sock(asoc->base.sk);
        if (sock_owned_by_user(asoc->base.sk)) {
@@ -388,7 +391,7 @@ void sctp_generate_heartbeat_event(unsigned long data)
        if (transport->dead)
                goto out_unlock;
 
-       error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT,
+       error = sctp_do_sm(net, SCTP_EVENT_T_TIMEOUT,
                           SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_HEARTBEAT),
                           asoc->state, asoc->ep, asoc,
                           transport, GFP_ATOMIC);
@@ -408,6 +411,7 @@ void sctp_generate_proto_unreach_event(unsigned long data)
 {
        struct sctp_transport *transport = (struct sctp_transport *) data;
        struct sctp_association *asoc = transport->asoc;
+       struct net *net = sock_net(asoc->base.sk);
        
        sctp_bh_lock_sock(asoc->base.sk);
        if (sock_owned_by_user(asoc->base.sk)) {
@@ -426,7 +430,7 @@ void sctp_generate_proto_unreach_event(unsigned long data)
        if (asoc->base.dead)
                goto out_unlock;
 
-       sctp_do_sm(SCTP_EVENT_T_OTHER,
+       sctp_do_sm(net, SCTP_EVENT_T_OTHER,
                   SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH),
                   asoc->state, asoc->ep, asoc, transport, GFP_ATOMIC);
 
@@ -753,8 +757,10 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds,
        int err = 0;
 
        if (sctp_outq_sack(&asoc->outqueue, sackh)) {
+               struct net *net = sock_net(asoc->base.sk);
+
                /* There are no more TSNs awaiting SACK.  */
-               err = sctp_do_sm(SCTP_EVENT_T_OTHER,
+               err = sctp_do_sm(net, SCTP_EVENT_T_OTHER,
                                 SCTP_ST_OTHER(SCTP_EVENT_NO_PENDING_TSN),
                                 asoc->state, asoc->ep, asoc, NULL,
                                 GFP_ATOMIC);
@@ -1042,6 +1048,8 @@ static int sctp_cmd_send_msg(struct sctp_association *asoc,
  */
 static void sctp_cmd_send_asconf(struct sctp_association *asoc)
 {
+       struct net *net = sock_net(asoc->base.sk);
+
        /* Send the next asconf chunk from the addip chunk
         * queue.
         */
@@ -1053,7 +1061,7 @@ static void sctp_cmd_send_asconf(struct sctp_association *asoc)
 
                /* Hold the chunk until an ASCONF_ACK is received. */
                sctp_chunk_hold(asconf);
-               if (sctp_primitive_ASCONF(asoc, asconf))
+               if (sctp_primitive_ASCONF(net, asoc, asconf))
                        sctp_chunk_free(asconf);
                else
                        asoc->addip_last_asconf = asconf;
@@ -1089,7 +1097,7 @@ static void sctp_cmd_send_asconf(struct sctp_association *asoc)
  * If you want to understand all of lksctp, this is a
  * good place to start.
  */
-int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype,
+int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype,
               sctp_state_t state,
               struct sctp_endpoint *ep,
               struct sctp_association *asoc,
@@ -1110,12 +1118,12 @@ int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype,
        /* Look up the state function, run it, and then process the
         * side effects.  These three steps are the heart of lksctp.
         */
-       state_fn = sctp_sm_lookup_event(event_type, state, subtype);
+       state_fn = sctp_sm_lookup_event(net, event_type, state, subtype);
 
        sctp_init_cmd_seq(&commands);
 
        DEBUG_PRE;
-       status = (*state_fn->fn)(ep, asoc, subtype, event_arg, &commands);
+       status = (*state_fn->fn)(net, ep, asoc, subtype, event_arg, &commands);
        DEBUG_POST;
 
        error = sctp_side_effects(event_type, subtype, state,
index 9fca103573508aa6f0880455e0e2c2d66dbb6150..094813b6c3c3cb99dddf407eeb93a2397a676cae 100644 (file)
@@ -66,7 +66,8 @@
 #include <net/sctp/sm.h>
 #include <net/sctp/structs.h>
 
-static struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep,
+static struct sctp_packet *sctp_abort_pkt_new(struct net *net,
+                                 const struct sctp_endpoint *ep,
                                  const struct sctp_association *asoc,
                                  struct sctp_chunk *chunk,
                                  const void *payload,
@@ -74,36 +75,43 @@ static struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep,
 static int sctp_eat_data(const struct sctp_association *asoc,
                         struct sctp_chunk *chunk,
                         sctp_cmd_seq_t *commands);
-static struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc,
+static struct sctp_packet *sctp_ootb_pkt_new(struct net *net,
+                                            const struct sctp_association *asoc,
                                             const struct sctp_chunk *chunk);
-static void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep,
+static void sctp_send_stale_cookie_err(struct net *net,
+                                      const struct sctp_endpoint *ep,
                                       const struct sctp_association *asoc,
                                       const struct sctp_chunk *chunk,
                                       sctp_cmd_seq_t *commands,
                                       struct sctp_chunk *err_chunk);
-static sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep,
+static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net,
+                                                const struct sctp_endpoint *ep,
                                                 const struct sctp_association *asoc,
                                                 const sctp_subtype_t type,
                                                 void *arg,
                                                 sctp_cmd_seq_t *commands);
-static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep,
+static sctp_disposition_t sctp_sf_shut_8_4_5(struct net *net,
+                                            const struct sctp_endpoint *ep,
                                             const struct sctp_association *asoc,
                                             const sctp_subtype_t type,
                                             void *arg,
                                             sctp_cmd_seq_t *commands);
-static sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep,
+static sctp_disposition_t sctp_sf_tabort_8_4_8(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
                                        sctp_cmd_seq_t *commands);
 static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk);
 
-static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands,
+static sctp_disposition_t sctp_stop_t1_and_abort(struct net *net,
+                                          sctp_cmd_seq_t *commands,
                                           __be16 error, int sk_err,
                                           const struct sctp_association *asoc,
                                           struct sctp_transport *transport);
 
 static sctp_disposition_t sctp_sf_abort_violation(
+                                    struct net *net,
                                     const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     void *arg,
@@ -112,6 +120,7 @@ static sctp_disposition_t sctp_sf_abort_violation(
                                     const size_t paylen);
 
 static sctp_disposition_t sctp_sf_violation_chunklen(
+                                    struct net *net,
                                     const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     const sctp_subtype_t type,
@@ -119,6 +128,7 @@ static sctp_disposition_t sctp_sf_violation_chunklen(
                                     sctp_cmd_seq_t *commands);
 
 static sctp_disposition_t sctp_sf_violation_paramlen(
+                                    struct net *net,
                                     const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     const sctp_subtype_t type,
@@ -126,6 +136,7 @@ static sctp_disposition_t sctp_sf_violation_paramlen(
                                     sctp_cmd_seq_t *commands);
 
 static sctp_disposition_t sctp_sf_violation_ctsn(
+                                    struct net *net,
                                     const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     const sctp_subtype_t type,
@@ -133,18 +144,21 @@ static sctp_disposition_t sctp_sf_violation_ctsn(
                                     sctp_cmd_seq_t *commands);
 
 static sctp_disposition_t sctp_sf_violation_chunk(
+                                    struct net *net,
                                     const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     const sctp_subtype_t type,
                                     void *arg,
                                     sctp_cmd_seq_t *commands);
 
-static sctp_ierror_t sctp_sf_authenticate(const struct sctp_endpoint *ep,
+static sctp_ierror_t sctp_sf_authenticate(struct net *net,
+                                   const struct sctp_endpoint *ep,
                                    const struct sctp_association *asoc,
                                    const sctp_subtype_t type,
                                    struct sctp_chunk *chunk);
 
-static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep,
+static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -204,7 +218,8 @@ sctp_chunk_length_valid(struct sctp_chunk *chunk,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_4_C(struct net *net,
+                                 const struct sctp_endpoint *ep,
                                  const struct sctp_association *asoc,
                                  const sctp_subtype_t type,
                                  void *arg,
@@ -214,7 +229,7 @@ sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep,
        struct sctp_ulpevent *ev;
 
        if (!sctp_vtag_verify_either(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* RFC 2960 6.10 Bundling
         *
@@ -222,11 +237,11 @@ sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep,
         * SHUTDOWN COMPLETE with any other chunks.
         */
        if (!chunk->singleton)
-               return sctp_sf_violation_chunk(ep, asoc, type, arg, commands);
+               return sctp_sf_violation_chunk(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the SHUTDOWN_COMPLETE chunk has a valid length. */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        /* RFC 2960 10.2 SCTP-to-ULP
@@ -259,8 +274,8 @@ sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep,
        sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
                        SCTP_STATE(SCTP_STATE_CLOSED));
 
-       SCTP_INC_STATS(SCTP_MIB_SHUTDOWNS);
-       SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
+       SCTP_INC_STATS(net, SCTP_MIB_SHUTDOWNS);
+       SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
 
        sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
 
@@ -289,7 +304,8 @@ sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_5_1B_init(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -313,21 +329,21 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
         * with an INIT chunk that is bundled with other chunks.
         */
        if (!chunk->singleton)
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* If the packet is an OOTB packet which is temporarily on the
         * control endpoint, respond with an ABORT.
         */
-       if (ep == sctp_sk((sctp_get_ctl_sock()))->ep) {
-               SCTP_INC_STATS(SCTP_MIB_OUTOFBLUES);
-               return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
+       if (ep == sctp_sk(net->sctp.ctl_sock)->ep) {
+               SCTP_INC_STATS(net, SCTP_MIB_OUTOFBLUES);
+               return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands);
        }
 
        /* 3.1 A packet containing an INIT chunk MUST have a zero Verification
         * Tag.
         */
        if (chunk->sctp_hdr->vtag != 0)
-               return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
+               return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the INIT chunk has a valid length.
         * Normally, this would cause an ABORT with a Protocol Violation
@@ -335,7 +351,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
         * just discard the packet.
         */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_init_chunk_t)))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* If the INIT is coming toward a closing socket, we'll send back
         * and ABORT.  Essentially, this catches the race of INIT being
@@ -344,18 +360,18 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
         * can treat this OOTB
         */
        if (sctp_sstate(ep->base.sk, CLOSING))
-               return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
+               return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands);
 
        /* Verify the INIT chunk before processing it. */
        err_chunk = NULL;
-       if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
+       if (!sctp_verify_init(net, asoc, chunk->chunk_hdr->type,
                              (sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
                              &err_chunk)) {
                /* This chunk contains fatal error. It is to be discarded.
                 * Send an ABORT, with causes if there is any.
                 */
                if (err_chunk) {
-                       packet = sctp_abort_pkt_new(ep, asoc, arg,
+                       packet = sctp_abort_pkt_new(net, ep, asoc, arg,
                                        (__u8 *)(err_chunk->chunk_hdr) +
                                        sizeof(sctp_chunkhdr_t),
                                        ntohs(err_chunk->chunk_hdr->length) -
@@ -366,13 +382,13 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
                        if (packet) {
                                sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
                                                SCTP_PACKET(packet));
-                               SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+                               SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS);
                                return SCTP_DISPOSITION_CONSUME;
                        } else {
                                return SCTP_DISPOSITION_NOMEM;
                        }
                } else {
-                       return sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
+                       return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg,
                                                    commands);
                }
        }
@@ -484,7 +500,8 @@ nomem:
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_5_1C_ack(struct net *net,
+                                      const struct sctp_endpoint *ep,
                                       const struct sctp_association *asoc,
                                       const sctp_subtype_t type,
                                       void *arg,
@@ -496,25 +513,25 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
        struct sctp_packet *packet;
 
        if (!sctp_vtag_verify(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* 6.10 Bundling
         * An endpoint MUST NOT bundle INIT, INIT ACK or
         * SHUTDOWN COMPLETE with any other chunks.
         */
        if (!chunk->singleton)
-               return sctp_sf_violation_chunk(ep, asoc, type, arg, commands);
+               return sctp_sf_violation_chunk(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the INIT-ACK chunk has a valid length */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_initack_chunk_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
        /* Grab the INIT header.  */
        chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data;
 
        /* Verify the INIT chunk before processing it. */
        err_chunk = NULL;
-       if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
+       if (!sctp_verify_init(net, asoc, chunk->chunk_hdr->type,
                              (sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
                              &err_chunk)) {
 
@@ -526,7 +543,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
                 * the association.
                 */
                if (err_chunk) {
-                       packet = sctp_abort_pkt_new(ep, asoc, arg,
+                       packet = sctp_abort_pkt_new(net, ep, asoc, arg,
                                        (__u8 *)(err_chunk->chunk_hdr) +
                                        sizeof(sctp_chunkhdr_t),
                                        ntohs(err_chunk->chunk_hdr->length) -
@@ -537,7 +554,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
                        if (packet) {
                                sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
                                                SCTP_PACKET(packet));
-                               SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+                               SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS);
                                error = SCTP_ERROR_INV_PARAM;
                        }
                }
@@ -554,10 +571,10 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
                 * was malformed.
                 */
                if (sctp_auth_recv_cid(SCTP_CID_ABORT, asoc))
-                       return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+                       return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
-               SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
-               return sctp_stop_t1_and_abort(commands, error, ECONNREFUSED,
+               SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
+               return sctp_stop_t1_and_abort(net, commands, error, ECONNREFUSED,
                                                asoc, chunk->transport);
        }
 
@@ -633,7 +650,8 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_5_1D_ce(struct net *net,
+                                     const struct sctp_endpoint *ep,
                                      const struct sctp_association *asoc,
                                      const sctp_subtype_t type, void *arg,
                                      sctp_cmd_seq_t *commands)
@@ -650,9 +668,9 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep,
        /* If the packet is an OOTB packet which is temporarily on the
         * control endpoint, respond with an ABORT.
         */
-       if (ep == sctp_sk((sctp_get_ctl_sock()))->ep) {
-               SCTP_INC_STATS(SCTP_MIB_OUTOFBLUES);
-               return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
+       if (ep == sctp_sk(net->sctp.ctl_sock)->ep) {
+               SCTP_INC_STATS(net, SCTP_MIB_OUTOFBLUES);
+               return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands);
        }
 
        /* Make sure that the COOKIE_ECHO chunk has a valid length.
@@ -661,7 +679,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep,
         * in sctp_unpack_cookie().
         */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t)))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* If the endpoint is not listening or if the number of associations
         * on the TCP-style socket exceed the max backlog, respond with an
@@ -670,7 +688,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep,
        sk = ep->base.sk;
        if (!sctp_sstate(sk, LISTENING) ||
            (sctp_style(sk, TCP) && sk_acceptq_is_full(sk)))
-               return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
+               return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands);
 
        /* "Decode" the chunk.  We have no optional parameters so we
         * are in good shape.
@@ -703,13 +721,13 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep,
                        goto nomem;
 
                case -SCTP_IERROR_STALE_COOKIE:
-                       sctp_send_stale_cookie_err(ep, asoc, chunk, commands,
+                       sctp_send_stale_cookie_err(net, ep, asoc, chunk, commands,
                                                   err_chk_p);
-                       return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+                       return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
                case -SCTP_IERROR_BAD_SIG:
                default:
-                       return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+                       return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
                }
        }
 
@@ -756,14 +774,14 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep,
                skb_pull(chunk->auth_chunk, sizeof(sctp_chunkhdr_t));
                auth.transport = chunk->transport;
 
-               ret = sctp_sf_authenticate(ep, new_asoc, type, &auth);
+               ret = sctp_sf_authenticate(net, ep, new_asoc, type, &auth);
 
                /* We can now safely free the auth_chunk clone */
                kfree_skb(chunk->auth_chunk);
 
                if (ret != SCTP_IERROR_NO_ERROR) {
                        sctp_association_free(new_asoc);
-                       return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+                       return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
                }
        }
 
@@ -804,8 +822,8 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep,
        sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc));
        sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
                        SCTP_STATE(SCTP_STATE_ESTABLISHED));
-       SCTP_INC_STATS(SCTP_MIB_CURRESTAB);
-       SCTP_INC_STATS(SCTP_MIB_PASSIVEESTABS);
+       SCTP_INC_STATS(net, SCTP_MIB_CURRESTAB);
+       SCTP_INC_STATS(net, SCTP_MIB_PASSIVEESTABS);
        sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL());
 
        if (new_asoc->autoclose)
@@ -856,7 +874,8 @@ nomem:
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_do_5_1E_ca(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_5_1E_ca(struct net *net,
+                                     const struct sctp_endpoint *ep,
                                      const struct sctp_association *asoc,
                                      const sctp_subtype_t type, void *arg,
                                      sctp_cmd_seq_t *commands)
@@ -865,13 +884,13 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(const struct sctp_endpoint *ep,
        struct sctp_ulpevent *ev;
 
        if (!sctp_vtag_verify(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* Verify that the chunk length for the COOKIE-ACK is OK.
         * If we don't do this, any bundled chunks may be junked.
         */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        /* Reset init error count upon receipt of COOKIE-ACK,
@@ -892,8 +911,8 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(const struct sctp_endpoint *ep,
                        SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE));
        sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
                        SCTP_STATE(SCTP_STATE_ESTABLISHED));
-       SCTP_INC_STATS(SCTP_MIB_CURRESTAB);
-       SCTP_INC_STATS(SCTP_MIB_ACTIVEESTABS);
+       SCTP_INC_STATS(net, SCTP_MIB_CURRESTAB);
+       SCTP_INC_STATS(net, SCTP_MIB_ACTIVEESTABS);
        sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL());
        if (asoc->autoclose)
                sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
@@ -958,7 +977,8 @@ static sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep,
 }
 
 /* Generate a HEARTBEAT packet on the given transport.  */
-sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_sendbeat_8_3(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -972,8 +992,8 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep,
                /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
                sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                SCTP_PERR(SCTP_ERROR_NO_ERROR));
-               SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
-               SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
+               SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
+               SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
                return SCTP_DISPOSITION_DELETE_TCB;
        }
 
@@ -1028,7 +1048,8 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_beat_8_3(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_beat_8_3(struct net *net,
+                                   const struct sctp_endpoint *ep,
                                    const struct sctp_association *asoc,
                                    const sctp_subtype_t type,
                                    void *arg,
@@ -1039,11 +1060,11 @@ sctp_disposition_t sctp_sf_beat_8_3(const struct sctp_endpoint *ep,
        size_t paylen = 0;
 
        if (!sctp_vtag_verify(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the HEARTBEAT chunk has a valid length. */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_heartbeat_chunk_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        /* 8.3 The receiver of the HEARTBEAT should immediately
@@ -1095,7 +1116,8 @@ nomem:
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_backbeat_8_3(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -1108,12 +1130,12 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep,
        unsigned long max_interval;
 
        if (!sctp_vtag_verify(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the HEARTBEAT-ACK chunk has a valid length.  */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t) +
                                            sizeof(sctp_sender_hb_info_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data;
@@ -1171,7 +1193,7 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep,
 /* Helper function to send out an abort for the restart
  * condition.
  */
-static int sctp_sf_send_restart_abort(union sctp_addr *ssa,
+static int sctp_sf_send_restart_abort(struct net *net, union sctp_addr *ssa,
                                      struct sctp_chunk *init,
                                      sctp_cmd_seq_t *commands)
 {
@@ -1197,18 +1219,18 @@ static int sctp_sf_send_restart_abort(union sctp_addr *ssa,
        errhdr->length = htons(len);
 
        /* Assign to the control socket. */
-       ep = sctp_sk((sctp_get_ctl_sock()))->ep;
+       ep = sctp_sk(net->sctp.ctl_sock)->ep;
 
        /* Association is NULL since this may be a restart attack and we
         * want to send back the attacker's vtag.
         */
-       pkt = sctp_abort_pkt_new(ep, NULL, init, errhdr, len);
+       pkt = sctp_abort_pkt_new(net, ep, NULL, init, errhdr, len);
 
        if (!pkt)
                goto out;
        sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(pkt));
 
-       SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+       SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS);
 
        /* Discard the rest of the inbound packet. */
        sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL());
@@ -1240,6 +1262,7 @@ static int sctp_sf_check_restart_addrs(const struct sctp_association *new_asoc,
                                       struct sctp_chunk *init,
                                       sctp_cmd_seq_t *commands)
 {
+       struct net *net = sock_net(new_asoc->base.sk);
        struct sctp_transport *new_addr;
        int ret = 1;
 
@@ -1258,7 +1281,7 @@ static int sctp_sf_check_restart_addrs(const struct sctp_association *new_asoc,
                            transports) {
                if (!list_has_sctp_addr(&asoc->peer.transport_addr_list,
                                        &new_addr->ipaddr)) {
-                       sctp_sf_send_restart_abort(&new_addr->ipaddr, init,
+                       sctp_sf_send_restart_abort(net, &new_addr->ipaddr, init,
                                                   commands);
                        ret = 0;
                        break;
@@ -1358,6 +1381,7 @@ static char sctp_tietags_compare(struct sctp_association *new_asoc,
  * chunk handling.
  */
 static sctp_disposition_t sctp_sf_do_unexpected_init(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -1382,20 +1406,20 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
         * with an INIT chunk that is bundled with other chunks.
         */
        if (!chunk->singleton)
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* 3.1 A packet containing an INIT chunk MUST have a zero Verification
         * Tag.
         */
        if (chunk->sctp_hdr->vtag != 0)
-               return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
+               return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the INIT chunk has a valid length.
         * In this case, we generate a protocol violation since we have
         * an association established.
         */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_init_chunk_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
        /* Grab the INIT header.  */
        chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data;
@@ -1405,14 +1429,14 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
 
        /* Verify the INIT chunk before processing it. */
        err_chunk = NULL;
-       if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
+       if (!sctp_verify_init(net, asoc, chunk->chunk_hdr->type,
                              (sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
                              &err_chunk)) {
                /* This chunk contains fatal error. It is to be discarded.
                 * Send an ABORT, with causes if there is any.
                 */
                if (err_chunk) {
-                       packet = sctp_abort_pkt_new(ep, asoc, arg,
+                       packet = sctp_abort_pkt_new(net, ep, asoc, arg,
                                        (__u8 *)(err_chunk->chunk_hdr) +
                                        sizeof(sctp_chunkhdr_t),
                                        ntohs(err_chunk->chunk_hdr->length) -
@@ -1421,14 +1445,14 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
                        if (packet) {
                                sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
                                                SCTP_PACKET(packet));
-                               SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+                               SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS);
                                retval = SCTP_DISPOSITION_CONSUME;
                        } else {
                                retval = SCTP_DISPOSITION_NOMEM;
                        }
                        goto cleanup;
                } else {
-                       return sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
+                       return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg,
                                                    commands);
                }
        }
@@ -1570,7 +1594,8 @@ cleanup:
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_do_5_2_1_siminit(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_5_2_1_siminit(struct net *net,
+                                   const struct sctp_endpoint *ep,
                                    const struct sctp_association *asoc,
                                    const sctp_subtype_t type,
                                    void *arg,
@@ -1579,7 +1604,7 @@ sctp_disposition_t sctp_sf_do_5_2_1_siminit(const struct sctp_endpoint *ep,
        /* Call helper to do the real work for both simulataneous and
         * duplicate INIT chunk handling.
         */
-       return sctp_sf_do_unexpected_init(ep, asoc, type, arg, commands);
+       return sctp_sf_do_unexpected_init(net, ep, asoc, type, arg, commands);
 }
 
 /*
@@ -1623,7 +1648,8 @@ sctp_disposition_t sctp_sf_do_5_2_1_siminit(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_do_5_2_2_dupinit(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_5_2_2_dupinit(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -1632,7 +1658,7 @@ sctp_disposition_t sctp_sf_do_5_2_2_dupinit(const struct sctp_endpoint *ep,
        /* Call helper to do the real work for both simulataneous and
         * duplicate INIT chunk handling.
         */
-       return sctp_sf_do_unexpected_init(ep, asoc, type, arg, commands);
+       return sctp_sf_do_unexpected_init(net, ep, asoc, type, arg, commands);
 }
 
 
@@ -1645,7 +1671,8 @@ sctp_disposition_t sctp_sf_do_5_2_2_dupinit(const struct sctp_endpoint *ep,
  * An unexpected INIT ACK usually indicates the processing of an old or
  * duplicated INIT chunk.
 */
-sctp_disposition_t sctp_sf_do_5_2_3_initack(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_5_2_3_initack(struct net *net,
+                                           const struct sctp_endpoint *ep,
                                            const struct sctp_association *asoc,
                                            const sctp_subtype_t type,
                                            void *arg, sctp_cmd_seq_t *commands)
@@ -1653,10 +1680,10 @@ sctp_disposition_t sctp_sf_do_5_2_3_initack(const struct sctp_endpoint *ep,
        /* Per the above section, we'll discard the chunk if we have an
         * endpoint.  If this is an OOTB INIT-ACK, treat it as such.
         */
-       if (ep == sctp_sk((sctp_get_ctl_sock()))->ep)
-               return sctp_sf_ootb(ep, asoc, type, arg, commands);
+       if (ep == sctp_sk(net->sctp.ctl_sock)->ep)
+               return sctp_sf_ootb(net, ep, asoc, type, arg, commands);
        else
-               return sctp_sf_discard_chunk(ep, asoc, type, arg, commands);
+               return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands);
 }
 
 /* Unexpected COOKIE-ECHO handler for peer restart (Table 2, action 'A')
@@ -1664,7 +1691,8 @@ sctp_disposition_t sctp_sf_do_5_2_3_initack(const struct sctp_endpoint *ep,
  * Section 5.2.4
  *  A)  In this case, the peer may have restarted.
  */
-static sctp_disposition_t sctp_sf_do_dupcook_a(const struct sctp_endpoint *ep,
+static sctp_disposition_t sctp_sf_do_dupcook_a(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        struct sctp_chunk *chunk,
                                        sctp_cmd_seq_t *commands,
@@ -1700,7 +1728,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const struct sctp_endpoint *ep,
         * its peer.
        */
        if (sctp_state(asoc, SHUTDOWN_ACK_SENT)) {
-               disposition = sctp_sf_do_9_2_reshutack(ep, asoc,
+               disposition = sctp_sf_do_9_2_reshutack(net, ep, asoc,
                                SCTP_ST_CHUNK(chunk->chunk_hdr->type),
                                chunk, commands);
                if (SCTP_DISPOSITION_NOMEM == disposition)
@@ -1763,7 +1791,8 @@ nomem:
  *      after responding to the local endpoint's INIT
  */
 /* This case represents an initialization collision.  */
-static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep,
+static sctp_disposition_t sctp_sf_do_dupcook_b(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        struct sctp_chunk *chunk,
                                        sctp_cmd_seq_t *commands,
@@ -1784,7 +1813,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep,
        sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc));
        sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
                        SCTP_STATE(SCTP_STATE_ESTABLISHED));
-       SCTP_INC_STATS(SCTP_MIB_CURRESTAB);
+       SCTP_INC_STATS(net, SCTP_MIB_CURRESTAB);
        sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL());
 
        repl = sctp_make_cookie_ack(new_asoc, chunk);
@@ -1833,7 +1862,8 @@ nomem:
  *     but a new tag of its own.
  */
 /* This case represents an initialization collision.  */
-static sctp_disposition_t sctp_sf_do_dupcook_c(const struct sctp_endpoint *ep,
+static sctp_disposition_t sctp_sf_do_dupcook_c(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        struct sctp_chunk *chunk,
                                        sctp_cmd_seq_t *commands,
@@ -1854,7 +1884,8 @@ static sctp_disposition_t sctp_sf_do_dupcook_c(const struct sctp_endpoint *ep,
  *    enter the ESTABLISHED state, if it has not already done so.
  */
 /* This case represents an initialization collision.  */
-static sctp_disposition_t sctp_sf_do_dupcook_d(const struct sctp_endpoint *ep,
+static sctp_disposition_t sctp_sf_do_dupcook_d(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        struct sctp_chunk *chunk,
                                        sctp_cmd_seq_t *commands,
@@ -1876,7 +1907,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_d(const struct sctp_endpoint *ep,
                                SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE));
                sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
                                SCTP_STATE(SCTP_STATE_ESTABLISHED));
-               SCTP_INC_STATS(SCTP_MIB_CURRESTAB);
+               SCTP_INC_STATS(net, SCTP_MIB_CURRESTAB);
                sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START,
                                SCTP_NULL());
 
@@ -1948,7 +1979,8 @@ nomem:
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_5_2_4_dupcook(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -1967,7 +1999,7 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const struct sctp_endpoint *ep,
         * done later.
         */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        /* "Decode" the chunk.  We have no optional parameters so we
@@ -2001,12 +2033,12 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const struct sctp_endpoint *ep,
                        goto nomem;
 
                case -SCTP_IERROR_STALE_COOKIE:
-                       sctp_send_stale_cookie_err(ep, asoc, chunk, commands,
+                       sctp_send_stale_cookie_err(net, ep, asoc, chunk, commands,
                                                   err_chk_p);
-                       return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+                       return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
                case -SCTP_IERROR_BAD_SIG:
                default:
-                       return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+                       return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
                }
        }
 
@@ -2017,27 +2049,27 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const struct sctp_endpoint *ep,
 
        switch (action) {
        case 'A': /* Association restart. */
-               retval = sctp_sf_do_dupcook_a(ep, asoc, chunk, commands,
+               retval = sctp_sf_do_dupcook_a(net, ep, asoc, chunk, commands,
                                              new_asoc);
                break;
 
        case 'B': /* Collision case B. */
-               retval = sctp_sf_do_dupcook_b(ep, asoc, chunk, commands,
+               retval = sctp_sf_do_dupcook_b(net, ep, asoc, chunk, commands,
                                              new_asoc);
                break;
 
        case 'C': /* Collision case C. */
-               retval = sctp_sf_do_dupcook_c(ep, asoc, chunk, commands,
+               retval = sctp_sf_do_dupcook_c(net, ep, asoc, chunk, commands,
                                              new_asoc);
                break;
 
        case 'D': /* Collision case D. */
-               retval = sctp_sf_do_dupcook_d(ep, asoc, chunk, commands,
+               retval = sctp_sf_do_dupcook_d(net, ep, asoc, chunk, commands,
                                              new_asoc);
                break;
 
        default: /* Discard packet for all others. */
-               retval = sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               retval = sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
                break;
        }
 
@@ -2063,6 +2095,7 @@ nomem:
  * See sctp_sf_do_9_1_abort().
  */
 sctp_disposition_t sctp_sf_shutdown_pending_abort(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -2072,7 +2105,7 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort(
        struct sctp_chunk *chunk = arg;
 
        if (!sctp_vtag_verify_either(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the ABORT chunk has a valid length.
         * Since this is an ABORT chunk, we have to discard it
@@ -2085,7 +2118,7 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort(
         * packet.
         */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t)))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* ADD-IP: Special case for ABORT chunks
         * F4)  One special consideration is that ABORT Chunks arriving
@@ -2094,9 +2127,9 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort(
         */
        if (SCTP_ADDR_DEL ==
                    sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest))
-               return sctp_sf_discard_chunk(ep, asoc, type, arg, commands);
+               return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands);
 
-       return __sctp_sf_do_9_1_abort(ep, asoc, type, arg, commands);
+       return __sctp_sf_do_9_1_abort(net, ep, asoc, type, arg, commands);
 }
 
 /*
@@ -2104,7 +2137,8 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort(
  *
  * See sctp_sf_do_9_1_abort().
  */
-sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_shutdown_sent_abort(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -2113,7 +2147,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep,
        struct sctp_chunk *chunk = arg;
 
        if (!sctp_vtag_verify_either(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the ABORT chunk has a valid length.
         * Since this is an ABORT chunk, we have to discard it
@@ -2126,7 +2160,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep,
         * packet.
         */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t)))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* ADD-IP: Special case for ABORT chunks
         * F4)  One special consideration is that ABORT Chunks arriving
@@ -2135,7 +2169,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep,
         */
        if (SCTP_ADDR_DEL ==
                    sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest))
-               return sctp_sf_discard_chunk(ep, asoc, type, arg, commands);
+               return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands);
 
        /* Stop the T2-shutdown timer. */
        sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
@@ -2145,7 +2179,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep,
        sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
                        SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
 
-       return __sctp_sf_do_9_1_abort(ep, asoc, type, arg, commands);
+       return __sctp_sf_do_9_1_abort(net, ep, asoc, type, arg, commands);
 }
 
 /*
@@ -2154,6 +2188,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep,
  * See sctp_sf_do_9_1_abort().
  */
 sctp_disposition_t sctp_sf_shutdown_ack_sent_abort(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -2163,7 +2198,7 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_abort(
        /* The same T2 timer, so we should be able to use
         * common function with the SHUTDOWN-SENT state.
         */
-       return sctp_sf_shutdown_sent_abort(ep, asoc, type, arg, commands);
+       return sctp_sf_shutdown_sent_abort(net, ep, asoc, type, arg, commands);
 }
 
 /*
@@ -2180,7 +2215,8 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_abort(
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_cookie_echoed_err(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -2190,13 +2226,13 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep,
        sctp_errhdr_t *err;
 
        if (!sctp_vtag_verify(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the ERROR chunk has a valid length.
         * The parameter walking depends on this as well.
         */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_operr_chunk_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        /* Process the error here */
@@ -2206,7 +2242,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep,
         */
        sctp_walk_errors(err, chunk->chunk_hdr) {
                if (SCTP_ERROR_STALE_COOKIE == err->cause)
-                       return sctp_sf_do_5_2_6_stale(ep, asoc, type,
+                       return sctp_sf_do_5_2_6_stale(net, ep, asoc, type,
                                                        arg, commands);
        }
 
@@ -2215,7 +2251,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep,
         * we are discarding the packet, there should be no adverse
         * affects.
         */
-       return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+       return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 }
 
 /*
@@ -2243,7 +2279,8 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-static sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep,
+static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net,
+                                                const struct sctp_endpoint *ep,
                                                 const struct sctp_association *asoc,
                                                 const sctp_subtype_t type,
                                                 void *arg,
@@ -2365,7 +2402,8 @@ nomem:
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_9_1_abort(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -2374,7 +2412,7 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep,
        struct sctp_chunk *chunk = arg;
 
        if (!sctp_vtag_verify_either(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the ABORT chunk has a valid length.
         * Since this is an ABORT chunk, we have to discard it
@@ -2387,7 +2425,7 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep,
         * packet.
         */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t)))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* ADD-IP: Special case for ABORT chunks
         * F4)  One special consideration is that ABORT Chunks arriving
@@ -2396,12 +2434,13 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep,
         */
        if (SCTP_ADDR_DEL ==
                    sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest))
-               return sctp_sf_discard_chunk(ep, asoc, type, arg, commands);
+               return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands);
 
-       return __sctp_sf_do_9_1_abort(ep, asoc, type, arg, commands);
+       return __sctp_sf_do_9_1_abort(net, ep, asoc, type, arg, commands);
 }
 
-static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep,
+static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -2418,7 +2457,7 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep,
                sctp_errhdr_t *err;
                sctp_walk_errors(err, chunk->chunk_hdr);
                if ((void *)err != (void *)chunk->chunk_end)
-                       return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+                       return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
                error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
        }
@@ -2426,8 +2465,8 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep,
        sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ECONNRESET));
        /* ASSOC_FAILED will DELETE_TCB. */
        sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_PERR(error));
-       SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
-       SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
+       SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
+       SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
 
        return SCTP_DISPOSITION_ABORT;
 }
@@ -2437,7 +2476,8 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep,
  *
  * See sctp_sf_do_9_1_abort() above.
  */
-sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_cookie_wait_abort(struct net *net,
+                                    const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     const sctp_subtype_t type,
                                     void *arg,
@@ -2448,7 +2488,7 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep,
        __be16 error = SCTP_ERROR_NO_ERROR;
 
        if (!sctp_vtag_verify_either(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the ABORT chunk has a valid length.
         * Since this is an ABORT chunk, we have to discard it
@@ -2461,27 +2501,28 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep,
         * packet.
         */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t)))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* See if we have an error cause code in the chunk.  */
        len = ntohs(chunk->chunk_hdr->length);
        if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr))
                error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
 
-       return sctp_stop_t1_and_abort(commands, error, ECONNREFUSED, asoc,
+       return sctp_stop_t1_and_abort(net, commands, error, ECONNREFUSED, asoc,
                                      chunk->transport);
 }
 
 /*
  * Process an incoming ICMP as an ABORT.  (COOKIE-WAIT state)
  */
-sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
                                        sctp_cmd_seq_t *commands)
 {
-       return sctp_stop_t1_and_abort(commands, SCTP_ERROR_NO_ERROR,
+       return sctp_stop_t1_and_abort(net, commands, SCTP_ERROR_NO_ERROR,
                                      ENOPROTOOPT, asoc,
                                      (struct sctp_transport *)arg);
 }
@@ -2489,7 +2530,8 @@ sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(const struct sctp_endpoint *ep
 /*
  * Process an ABORT.  (COOKIE-ECHOED state)
  */
-sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_cookie_echoed_abort(struct net *net,
+                                              const struct sctp_endpoint *ep,
                                               const struct sctp_association *asoc,
                                               const sctp_subtype_t type,
                                               void *arg,
@@ -2498,7 +2540,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep,
        /* There is a single T1 timer, so we should be able to use
         * common function with the COOKIE-WAIT state.
         */
-       return sctp_sf_cookie_wait_abort(ep, asoc, type, arg, commands);
+       return sctp_sf_cookie_wait_abort(net, ep, asoc, type, arg, commands);
 }
 
 /*
@@ -2506,7 +2548,8 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep,
  *
  * This is common code called by several sctp_sf_*_abort() functions above.
  */
-static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands,
+static sctp_disposition_t sctp_stop_t1_and_abort(struct net *net,
+                                          sctp_cmd_seq_t *commands,
                                           __be16 error, int sk_err,
                                           const struct sctp_association *asoc,
                                           struct sctp_transport *transport)
@@ -2514,7 +2557,7 @@ static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands,
        SCTP_DEBUG_PRINTK("ABORT received (INIT).\n");
        sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
                        SCTP_STATE(SCTP_STATE_CLOSED));
-       SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
+       SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
        sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
                        SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
        sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(sk_err));
@@ -2557,7 +2600,8 @@ static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_9_2_shutdown(struct net *net,
+                                          const struct sctp_endpoint *ep,
                                           const struct sctp_association *asoc,
                                           const sctp_subtype_t type,
                                           void *arg,
@@ -2570,12 +2614,12 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep,
        __u32 ctsn;
 
        if (!sctp_vtag_verify(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the SHUTDOWN chunk has a valid length. */
        if (!sctp_chunk_length_valid(chunk,
                                      sizeof(struct sctp_shutdown_chunk_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        /* Convert the elaborate header.  */
@@ -2595,7 +2639,7 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep,
         * sender with an ABORT.
         */
        if (!TSN_lt(ctsn, asoc->next_tsn))
-               return sctp_sf_violation_ctsn(ep, asoc, type, arg, commands);
+               return sctp_sf_violation_ctsn(net, ep, asoc, type, arg, commands);
 
        /* API 5.3.1.5 SCTP_SHUTDOWN_EVENT
         * When a peer sends a SHUTDOWN, SCTP delivers this notification to
@@ -2619,7 +2663,7 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep,
        disposition = SCTP_DISPOSITION_CONSUME;
 
        if (sctp_outq_is_empty(&asoc->outqueue)) {
-               disposition = sctp_sf_do_9_2_shutdown_ack(ep, asoc, type,
+               disposition = sctp_sf_do_9_2_shutdown_ack(net, ep, asoc, type,
                                                          arg, commands);
        }
 
@@ -2645,7 +2689,8 @@ out:
  * The Cumulative TSN Ack of the received SHUTDOWN chunk
  * MUST be processed.
  */
-sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(struct net *net,
+                                          const struct sctp_endpoint *ep,
                                           const struct sctp_association *asoc,
                                           const sctp_subtype_t type,
                                           void *arg,
@@ -2656,12 +2701,12 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(const struct sctp_endpoint *ep,
        __u32 ctsn;
 
        if (!sctp_vtag_verify(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the SHUTDOWN chunk has a valid length. */
        if (!sctp_chunk_length_valid(chunk,
                                      sizeof(struct sctp_shutdown_chunk_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        sdh = (sctp_shutdownhdr_t *)chunk->skb->data;
@@ -2678,7 +2723,7 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(const struct sctp_endpoint *ep,
         * sender with an ABORT.
         */
        if (!TSN_lt(ctsn, asoc->next_tsn))
-               return sctp_sf_violation_ctsn(ep, asoc, type, arg, commands);
+               return sctp_sf_violation_ctsn(net, ep, asoc, type, arg, commands);
 
        /* verify, by checking the Cumulative TSN Ack field of the
         * chunk, that all its outstanding DATA chunks have been
@@ -2697,7 +2742,8 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(const struct sctp_endpoint *ep,
  * that belong to this association, it should discard the INIT chunk and
  * retransmit the SHUTDOWN ACK chunk.
  */
-sctp_disposition_t sctp_sf_do_9_2_reshutack(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_9_2_reshutack(struct net *net,
+                                   const struct sctp_endpoint *ep,
                                    const struct sctp_association *asoc,
                                    const sctp_subtype_t type,
                                    void *arg,
@@ -2708,7 +2754,7 @@ sctp_disposition_t sctp_sf_do_9_2_reshutack(const struct sctp_endpoint *ep,
 
        /* Make sure that the chunk has a valid length */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        /* Since we are not going to really process this INIT, there
@@ -2760,7 +2806,8 @@ nomem:
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_do_ecn_cwr(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_ecn_cwr(struct net *net,
+                                     const struct sctp_endpoint *ep,
                                      const struct sctp_association *asoc,
                                      const sctp_subtype_t type,
                                      void *arg,
@@ -2771,10 +2818,10 @@ sctp_disposition_t sctp_sf_do_ecn_cwr(const struct sctp_endpoint *ep,
        u32 lowest_tsn;
 
        if (!sctp_vtag_verify(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_ecne_chunk_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        cwr = (sctp_cwrhdr_t *) chunk->skb->data;
@@ -2815,7 +2862,8 @@ sctp_disposition_t sctp_sf_do_ecn_cwr(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_do_ecne(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_ecne(struct net *net,
+                                  const struct sctp_endpoint *ep,
                                   const struct sctp_association *asoc,
                                   const sctp_subtype_t type,
                                   void *arg,
@@ -2825,10 +2873,10 @@ sctp_disposition_t sctp_sf_do_ecne(const struct sctp_endpoint *ep,
        struct sctp_chunk *chunk = arg;
 
        if (!sctp_vtag_verify(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_ecne_chunk_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        ecne = (sctp_ecnehdr_t *) chunk->skb->data;
@@ -2871,7 +2919,8 @@ sctp_disposition_t sctp_sf_do_ecne(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_eat_data_6_2(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -2884,11 +2933,11 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep,
        if (!sctp_vtag_verify(chunk, asoc)) {
                sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
                                SCTP_NULL());
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
        }
 
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_data_chunk_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        error = sctp_eat_data(asoc, chunk, commands );
@@ -2897,16 +2946,16 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep,
                break;
        case SCTP_IERROR_HIGH_TSN:
        case SCTP_IERROR_BAD_STREAM:
-               SCTP_INC_STATS(SCTP_MIB_IN_DATA_CHUNK_DISCARDS);
+               SCTP_INC_STATS(net, SCTP_MIB_IN_DATA_CHUNK_DISCARDS);
                goto discard_noforce;
        case SCTP_IERROR_DUP_TSN:
        case SCTP_IERROR_IGNORE_TSN:
-               SCTP_INC_STATS(SCTP_MIB_IN_DATA_CHUNK_DISCARDS);
+               SCTP_INC_STATS(net, SCTP_MIB_IN_DATA_CHUNK_DISCARDS);
                goto discard_force;
        case SCTP_IERROR_NO_DATA:
                goto consume;
        case SCTP_IERROR_PROTO_VIOLATION:
-               return sctp_sf_abort_violation(ep, asoc, chunk, commands,
+               return sctp_sf_abort_violation(net, ep, asoc, chunk, commands,
                        (u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t));
        default:
                BUG();
@@ -2992,7 +3041,8 @@ consume:
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_eat_data_fast_4_4(struct net *net,
+                                    const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     const sctp_subtype_t type,
                                     void *arg,
@@ -3004,11 +3054,11 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep,
        if (!sctp_vtag_verify(chunk, asoc)) {
                sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
                                SCTP_NULL());
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
        }
 
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_data_chunk_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        error = sctp_eat_data(asoc, chunk, commands );
@@ -3022,7 +3072,7 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep,
        case SCTP_IERROR_NO_DATA:
                goto consume;
        case SCTP_IERROR_PROTO_VIOLATION:
-               return sctp_sf_abort_violation(ep, asoc, chunk, commands,
+               return sctp_sf_abort_violation(net, ep, asoc, chunk, commands,
                        (u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t));
        default:
                BUG();
@@ -3082,7 +3132,8 @@ consume:
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_eat_sack_6_2(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_eat_sack_6_2(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -3093,18 +3144,18 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(const struct sctp_endpoint *ep,
        __u32 ctsn;
 
        if (!sctp_vtag_verify(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the SACK chunk has a valid length. */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_sack_chunk_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        /* Pull the SACK chunk from the data buffer */
        sackh = sctp_sm_pull_sack(chunk);
        /* Was this a bogus SACK? */
        if (!sackh)
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
        chunk->subh.sack_hdr = sackh;
        ctsn = ntohl(sackh->cum_tsn_ack);
 
@@ -3125,7 +3176,7 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(const struct sctp_endpoint *ep,
         * sender with an ABORT.
         */
        if (!TSN_lt(ctsn, asoc->next_tsn))
-               return sctp_sf_violation_ctsn(ep, asoc, type, arg, commands);
+               return sctp_sf_violation_ctsn(net, ep, asoc, type, arg, commands);
 
        /* Return this SACK for further processing.  */
        sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK, SCTP_SACKH(sackh));
@@ -3154,7 +3205,8 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
 */
-static sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep,
+static sctp_disposition_t sctp_sf_tabort_8_4_8(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -3164,7 +3216,7 @@ static sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep,
        struct sctp_chunk *chunk = arg;
        struct sctp_chunk *abort;
 
-       packet = sctp_ootb_pkt_new(asoc, chunk);
+       packet = sctp_ootb_pkt_new(net, asoc, chunk);
 
        if (packet) {
                /* Make an ABORT. The T bit will be set if the asoc
@@ -3188,9 +3240,9 @@ static sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep,
                sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
                                SCTP_PACKET(packet));
 
-               SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+               SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS);
 
-               sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
                return SCTP_DISPOSITION_CONSUME;
        }
 
@@ -3205,7 +3257,8 @@ static sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
 */
-sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_operr_notify(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -3215,15 +3268,15 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep,
        sctp_errhdr_t *err;
 
        if (!sctp_vtag_verify(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the ERROR chunk has a valid length. */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_operr_chunk_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
        sctp_walk_errors(err, chunk->chunk_hdr);
        if ((void *)err != (void *)chunk->chunk_end)
-               return sctp_sf_violation_paramlen(ep, asoc, type, arg,
+               return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
                                                  (void *)err, commands);
 
        sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_OPERR,
@@ -3242,7 +3295,8 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition.
  */
-sctp_disposition_t sctp_sf_do_9_2_final(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_9_2_final(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -3253,11 +3307,11 @@ sctp_disposition_t sctp_sf_do_9_2_final(const struct sctp_endpoint *ep,
        struct sctp_ulpevent *ev;
 
        if (!sctp_vtag_verify(chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the SHUTDOWN_ACK chunk has a valid length. */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
        /* 10.2 H) SHUTDOWN COMPLETE notification
         *
@@ -3290,8 +3344,8 @@ sctp_disposition_t sctp_sf_do_9_2_final(const struct sctp_endpoint *ep,
 
        sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
                        SCTP_STATE(SCTP_STATE_CLOSED));
-       SCTP_INC_STATS(SCTP_MIB_SHUTDOWNS);
-       SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
+       SCTP_INC_STATS(net, SCTP_MIB_SHUTDOWNS);
+       SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
        sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
 
        /* ...and remove all record of the association. */
@@ -3324,7 +3378,8 @@ nomem:
  *    receiver of the OOTB packet shall discard the OOTB packet and take
  *    no further action.
  */
-sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_ootb(struct net *net,
+                               const struct sctp_endpoint *ep,
                                const struct sctp_association *asoc,
                                const sctp_subtype_t type,
                                void *arg,
@@ -3338,13 +3393,13 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep,
        int ootb_shut_ack = 0;
        int ootb_cookie_ack = 0;
 
-       SCTP_INC_STATS(SCTP_MIB_OUTOFBLUES);
+       SCTP_INC_STATS(net, SCTP_MIB_OUTOFBLUES);
 
        ch = (sctp_chunkhdr_t *) chunk->chunk_hdr;
        do {
                /* Report violation if the chunk is less then minimal */
                if (ntohs(ch->length) < sizeof(sctp_chunkhdr_t))
-                       return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+                       return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
                /* Now that we know we at least have a chunk header,
@@ -3359,7 +3414,7 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep,
                 *   sending an ABORT of its own.
                 */
                if (SCTP_CID_ABORT == ch->type)
-                       return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+                       return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
                /* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR
                 * or a COOKIE ACK the SCTP Packet should be silently
@@ -3381,18 +3436,18 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep,
                /* Report violation if chunk len overflows */
                ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
                if (ch_end > skb_tail_pointer(skb))
-                       return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+                       return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
                ch = (sctp_chunkhdr_t *) ch_end;
        } while (ch_end < skb_tail_pointer(skb));
 
        if (ootb_shut_ack)
-               return sctp_sf_shut_8_4_5(ep, asoc, type, arg, commands);
+               return sctp_sf_shut_8_4_5(net, ep, asoc, type, arg, commands);
        else if (ootb_cookie_ack)
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
        else
-               return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
+               return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, commands);
 }
 
 /*
@@ -3416,7 +3471,8 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep,
+static sctp_disposition_t sctp_sf_shut_8_4_5(struct net *net,
+                                            const struct sctp_endpoint *ep,
                                             const struct sctp_association *asoc,
                                             const sctp_subtype_t type,
                                             void *arg,
@@ -3426,7 +3482,7 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep,
        struct sctp_chunk *chunk = arg;
        struct sctp_chunk *shut;
 
-       packet = sctp_ootb_pkt_new(asoc, chunk);
+       packet = sctp_ootb_pkt_new(net, asoc, chunk);
 
        if (packet) {
                /* Make an SHUTDOWN_COMPLETE.
@@ -3450,19 +3506,19 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep,
                sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
                                SCTP_PACKET(packet));
 
-               SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+               SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS);
 
                /* If the chunk length is invalid, we don't want to process
                 * the reset of the packet.
                 */
                if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t)))
-                       return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+                       return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
                /* We need to discard the rest of the packet to prevent
                 * potential bomming attacks from additional bundled chunks.
                 * This is documented in SCTP Threats ID.
                 */
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
        }
 
        return SCTP_DISPOSITION_NOMEM;
@@ -3479,7 +3535,8 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep,
  *   chunks. --piggy ]
  *
  */
-sctp_disposition_t sctp_sf_do_8_5_1_E_sa(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_8_5_1_E_sa(struct net *net,
+                                     const struct sctp_endpoint *ep,
                                      const struct sctp_association *asoc,
                                      const sctp_subtype_t type,
                                      void *arg,
@@ -3489,7 +3546,7 @@ sctp_disposition_t sctp_sf_do_8_5_1_E_sa(const struct sctp_endpoint *ep,
 
        /* Make sure that the SHUTDOWN_ACK chunk has a valid length. */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        /* Although we do have an association in this case, it corresponds
@@ -3497,13 +3554,14 @@ sctp_disposition_t sctp_sf_do_8_5_1_E_sa(const struct sctp_endpoint *ep,
         * packet and the state function that handles OOTB SHUTDOWN_ACK is
         * called with a NULL association.
         */
-       SCTP_INC_STATS(SCTP_MIB_OUTOFBLUES);
+       SCTP_INC_STATS(net, SCTP_MIB_OUTOFBLUES);
 
-       return sctp_sf_shut_8_4_5(ep, NULL, type, arg, commands);
+       return sctp_sf_shut_8_4_5(net, ep, NULL, type, arg, commands);
 }
 
 /* ADDIP Section 4.2 Upon reception of an ASCONF Chunk.  */
-sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_asconf(struct net *net,
+                                    const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     const sctp_subtype_t type, void *arg,
                                     sctp_cmd_seq_t *commands)
@@ -3519,7 +3577,7 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep,
        if (!sctp_vtag_verify(chunk, asoc)) {
                sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
                                SCTP_NULL());
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
        }
 
        /* ADD-IP: Section 4.1.1
@@ -3528,12 +3586,12 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep,
         * is received unauthenticated it MUST be silently discarded as
         * described in [I-D.ietf-tsvwg-sctp-auth].
         */
-       if (!sctp_addip_noauth && !chunk->auth)
-               return sctp_sf_discard_chunk(ep, asoc, type, arg, commands);
+       if (!net->sctp.addip_noauth && !chunk->auth)
+               return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the ASCONF ADDIP chunk has a valid length.  */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_addip_chunk_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        hdr = (sctp_addiphdr_t *)chunk->skb->data;
@@ -3542,7 +3600,7 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep,
        addr_param = (union sctp_addr_param *)hdr->params;
        length = ntohs(addr_param->p.length);
        if (length < sizeof(sctp_paramhdr_t))
-               return sctp_sf_violation_paramlen(ep, asoc, type, arg,
+               return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
                           (void *)addr_param, commands);
 
        /* Verify the ASCONF chunk before processing it. */
@@ -3550,7 +3608,7 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep,
                            (sctp_paramhdr_t *)((void *)addr_param + length),
                            (void *)chunk->chunk_end,
                            &err_param))
-               return sctp_sf_violation_paramlen(ep, asoc, type, arg,
+               return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
                                                  (void *)err_param, commands);
 
        /* ADDIP 5.2 E1) Compare the value of the serial number to the value
@@ -3630,7 +3688,8 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep,
  * When building TLV parameters for the ASCONF Chunk that will add or
  * delete IP addresses the D0 to D13 rules should be applied:
  */
-sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net,
+                                        const struct sctp_endpoint *ep,
                                         const struct sctp_association *asoc,
                                         const sctp_subtype_t type, void *arg,
                                         sctp_cmd_seq_t *commands)
@@ -3645,7 +3704,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
        if (!sctp_vtag_verify(asconf_ack, asoc)) {
                sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
                                SCTP_NULL());
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
        }
 
        /* ADD-IP, Section 4.1.2:
@@ -3654,12 +3713,12 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
         * is received unauthenticated it MUST be silently discarded as
         * described in [I-D.ietf-tsvwg-sctp-auth].
         */
-       if (!sctp_addip_noauth && !asconf_ack->auth)
-               return sctp_sf_discard_chunk(ep, asoc, type, arg, commands);
+       if (!net->sctp.addip_noauth && !asconf_ack->auth)
+               return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the ADDIP chunk has a valid length.  */
        if (!sctp_chunk_length_valid(asconf_ack, sizeof(sctp_addip_chunk_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        addip_hdr = (sctp_addiphdr_t *)asconf_ack->skb->data;
@@ -3670,7 +3729,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
            (sctp_paramhdr_t *)addip_hdr->params,
            (void *)asconf_ack->chunk_end,
            &err_param))
-               return sctp_sf_violation_paramlen(ep, asoc, type, arg,
+               return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
                           (void *)err_param, commands);
 
        if (last_asconf) {
@@ -3705,8 +3764,8 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
                                SCTP_ERROR(ECONNABORTED));
                sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                SCTP_PERR(SCTP_ERROR_ASCONF_ACK));
-               SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
-               SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
+               SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
+               SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
                return SCTP_DISPOSITION_ABORT;
        }
 
@@ -3739,8 +3798,8 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
                                SCTP_ERROR(ECONNABORTED));
                sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                SCTP_PERR(SCTP_ERROR_ASCONF_ACK));
-               SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
-               SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
+               SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
+               SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
                return SCTP_DISPOSITION_ABORT;
        }
 
@@ -3761,7 +3820,8 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_eat_fwd_tsn(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_eat_fwd_tsn(struct net *net,
+                                      const struct sctp_endpoint *ep,
                                       const struct sctp_association *asoc,
                                       const sctp_subtype_t type,
                                       void *arg,
@@ -3776,12 +3836,12 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn(const struct sctp_endpoint *ep,
        if (!sctp_vtag_verify(chunk, asoc)) {
                sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
                                SCTP_NULL());
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
        }
 
        /* Make sure that the FORWARD_TSN chunk has valid length.  */
        if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_fwdtsn_chunk)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        fwdtsn_hdr = (struct sctp_fwdtsn_hdr *)chunk->skb->data;
@@ -3828,6 +3888,7 @@ discard_noforce:
 }
 
 sctp_disposition_t sctp_sf_eat_fwd_tsn_fast(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -3843,12 +3904,12 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn_fast(
        if (!sctp_vtag_verify(chunk, asoc)) {
                sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
                                SCTP_NULL());
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
        }
 
        /* Make sure that the FORWARD_TSN chunk has a valid length.  */
        if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_fwdtsn_chunk)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        fwdtsn_hdr = (struct sctp_fwdtsn_hdr *)chunk->skb->data;
@@ -3915,7 +3976,8 @@ gen_shutdown:
  *
  * The return value is the disposition of the chunk.
  */
-static sctp_ierror_t sctp_sf_authenticate(const struct sctp_endpoint *ep,
+static sctp_ierror_t sctp_sf_authenticate(struct net *net,
+                                   const struct sctp_endpoint *ep,
                                    const struct sctp_association *asoc,
                                    const sctp_subtype_t type,
                                    struct sctp_chunk *chunk)
@@ -3988,7 +4050,8 @@ nomem:
        return SCTP_IERROR_NOMEM;
 }
 
-sctp_disposition_t sctp_sf_eat_auth(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_eat_auth(struct net *net,
+                                   const struct sctp_endpoint *ep,
                                    const struct sctp_association *asoc,
                                    const sctp_subtype_t type,
                                    void *arg,
@@ -4001,21 +4064,21 @@ sctp_disposition_t sctp_sf_eat_auth(const struct sctp_endpoint *ep,
 
        /* Make sure that the peer has AUTH capable */
        if (!asoc->peer.auth_capable)
-               return sctp_sf_unk_chunk(ep, asoc, type, arg, commands);
+               return sctp_sf_unk_chunk(net, ep, asoc, type, arg, commands);
 
        if (!sctp_vtag_verify(chunk, asoc)) {
                sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
                                SCTP_NULL());
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
        }
 
        /* Make sure that the AUTH chunk has valid length.  */
        if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_auth_chunk)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        auth_hdr = (struct sctp_authhdr *)chunk->skb->data;
-       error = sctp_sf_authenticate(ep, asoc, type, chunk);
+       error = sctp_sf_authenticate(net, ep, asoc, type, chunk);
        switch (error) {
        case SCTP_IERROR_AUTH_BAD_HMAC:
                /* Generate the ERROR chunk and discard the rest
@@ -4032,10 +4095,10 @@ sctp_disposition_t sctp_sf_eat_auth(const struct sctp_endpoint *ep,
                /* Fall Through */
        case SCTP_IERROR_AUTH_BAD_KEYID:
        case SCTP_IERROR_BAD_SIG:
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        case SCTP_IERROR_PROTO_VIOLATION:
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        case SCTP_IERROR_NOMEM:
@@ -4084,7 +4147,8 @@ sctp_disposition_t sctp_sf_eat_auth(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_unk_chunk(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_unk_chunk(struct net *net,
+                                    const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     const sctp_subtype_t type,
                                     void *arg,
@@ -4097,20 +4161,20 @@ sctp_disposition_t sctp_sf_unk_chunk(const struct sctp_endpoint *ep,
        SCTP_DEBUG_PRINTK("Processing the unknown chunk id %d.\n", type.chunk);
 
        if (!sctp_vtag_verify(unk_chunk, asoc))
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
        /* Make sure that the chunk has a valid length.
         * Since we don't know the chunk type, we use a general
         * chunkhdr structure to make a comparison.
         */
        if (!sctp_chunk_length_valid(unk_chunk, sizeof(sctp_chunkhdr_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        switch (type.chunk & SCTP_CID_ACTION_MASK) {
        case SCTP_CID_ACTION_DISCARD:
                /* Discard the packet.  */
-               return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
                break;
        case SCTP_CID_ACTION_DISCARD_ERR:
                /* Generate an ERROR chunk as response. */
@@ -4125,7 +4189,7 @@ sctp_disposition_t sctp_sf_unk_chunk(const struct sctp_endpoint *ep,
                }
 
                /* Discard the packet.  */
-               sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+               sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
                return SCTP_DISPOSITION_CONSUME;
                break;
        case SCTP_CID_ACTION_SKIP:
@@ -4167,7 +4231,8 @@ sctp_disposition_t sctp_sf_unk_chunk(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_discard_chunk(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_discard_chunk(struct net *net,
+                                        const struct sctp_endpoint *ep,
                                         const struct sctp_association *asoc,
                                         const sctp_subtype_t type,
                                         void *arg,
@@ -4180,7 +4245,7 @@ sctp_disposition_t sctp_sf_discard_chunk(const struct sctp_endpoint *ep,
         * chunkhdr structure to make a comparison.
         */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        SCTP_DEBUG_PRINTK("Chunk %d is discarded\n", type.chunk);
@@ -4205,13 +4270,14 @@ sctp_disposition_t sctp_sf_discard_chunk(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_pdiscard(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_pdiscard(struct net *net,
+                                   const struct sctp_endpoint *ep,
                                    const struct sctp_association *asoc,
                                    const sctp_subtype_t type,
                                    void *arg,
                                    sctp_cmd_seq_t *commands)
 {
-       SCTP_INC_STATS(SCTP_MIB_IN_PKT_DISCARDS);
+       SCTP_INC_STATS(net, SCTP_MIB_IN_PKT_DISCARDS);
        sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL());
 
        return SCTP_DISPOSITION_CONSUME;
@@ -4232,7 +4298,8 @@ sctp_disposition_t sctp_sf_pdiscard(const struct sctp_endpoint *ep,
  * We simply tag the chunk as a violation.  The state machine will log
  * the violation and continue.
  */
-sctp_disposition_t sctp_sf_violation(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_violation(struct net *net,
+                                    const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     const sctp_subtype_t type,
                                     void *arg,
@@ -4242,7 +4309,7 @@ sctp_disposition_t sctp_sf_violation(const struct sctp_endpoint *ep,
 
        /* Make sure that the chunk has a valid length. */
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t)))
-               return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+               return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
        return SCTP_DISPOSITION_VIOLATION;
@@ -4252,6 +4319,7 @@ sctp_disposition_t sctp_sf_violation(const struct sctp_endpoint *ep,
  * Common function to handle a protocol violation.
  */
 static sctp_disposition_t sctp_sf_abort_violation(
+                                    struct net *net,
                                     const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     void *arg,
@@ -4302,7 +4370,7 @@ static sctp_disposition_t sctp_sf_abort_violation(
                }
 
                sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
-               SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+               SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS);
 
                if (asoc->state <= SCTP_STATE_COOKIE_ECHOED) {
                        sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
@@ -4316,10 +4384,10 @@ static sctp_disposition_t sctp_sf_abort_violation(
                                        SCTP_ERROR(ECONNABORTED));
                        sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                        SCTP_PERR(SCTP_ERROR_PROTO_VIOLATION));
-                       SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
+                       SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
                }
        } else {
-               packet = sctp_ootb_pkt_new(asoc, chunk);
+               packet = sctp_ootb_pkt_new(net, asoc, chunk);
 
                if (!packet)
                        goto nomem_pkt;
@@ -4334,13 +4402,13 @@ static sctp_disposition_t sctp_sf_abort_violation(
                sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
                        SCTP_PACKET(packet));
 
-               SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+               SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS);
        }
 
-       SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
+       SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
 
 discard:
-       sctp_sf_pdiscard(ep, asoc, SCTP_ST_CHUNK(0), arg, commands);
+       sctp_sf_pdiscard(net, ep, asoc, SCTP_ST_CHUNK(0), arg, commands);
        return SCTP_DISPOSITION_ABORT;
 
 nomem_pkt:
@@ -4369,6 +4437,7 @@ nomem:
  * Generate an  ABORT chunk and terminate the association.
  */
 static sctp_disposition_t sctp_sf_violation_chunklen(
+                                    struct net *net,
                                     const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     const sctp_subtype_t type,
@@ -4377,7 +4446,7 @@ static sctp_disposition_t sctp_sf_violation_chunklen(
 {
        static const char err_str[]="The following chunk had invalid length:";
 
-       return sctp_sf_abort_violation(ep, asoc, arg, commands, err_str,
+       return sctp_sf_abort_violation(net, ep, asoc, arg, commands, err_str,
                                        sizeof(err_str));
 }
 
@@ -4388,6 +4457,7 @@ static sctp_disposition_t sctp_sf_violation_chunklen(
  * the length is considered as invalid.
  */
 static sctp_disposition_t sctp_sf_violation_paramlen(
+                                    struct net *net,
                                     const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     const sctp_subtype_t type,
@@ -4407,17 +4477,17 @@ static sctp_disposition_t sctp_sf_violation_paramlen(
                goto nomem;
 
        sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
-       SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+       SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS);
 
        sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
                        SCTP_ERROR(ECONNABORTED));
        sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                        SCTP_PERR(SCTP_ERROR_PROTO_VIOLATION));
-       SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
-       SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
+       SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
+       SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
 
 discard:
-       sctp_sf_pdiscard(ep, asoc, SCTP_ST_CHUNK(0), arg, commands);
+       sctp_sf_pdiscard(net, ep, asoc, SCTP_ST_CHUNK(0), arg, commands);
        return SCTP_DISPOSITION_ABORT;
 nomem:
        return SCTP_DISPOSITION_NOMEM;
@@ -4430,6 +4500,7 @@ nomem:
  * error code.
  */
 static sctp_disposition_t sctp_sf_violation_ctsn(
+                                    struct net *net,
                                     const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     const sctp_subtype_t type,
@@ -4438,7 +4509,7 @@ static sctp_disposition_t sctp_sf_violation_ctsn(
 {
        static const char err_str[]="The cumulative tsn ack beyond the max tsn currently sent:";
 
-       return sctp_sf_abort_violation(ep, asoc, arg, commands, err_str,
+       return sctp_sf_abort_violation(net, ep, asoc, arg, commands, err_str,
                                        sizeof(err_str));
 }
 
@@ -4449,6 +4520,7 @@ static sctp_disposition_t sctp_sf_violation_ctsn(
  * on the path and we may not want to continue this communication.
  */
 static sctp_disposition_t sctp_sf_violation_chunk(
+                                    struct net *net,
                                     const struct sctp_endpoint *ep,
                                     const struct sctp_association *asoc,
                                     const sctp_subtype_t type,
@@ -4458,9 +4530,9 @@ static sctp_disposition_t sctp_sf_violation_chunk(
        static const char err_str[]="The following chunk violates protocol:";
 
        if (!asoc)
-               return sctp_sf_violation(ep, asoc, type, arg, commands);
+               return sctp_sf_violation(net, ep, asoc, type, arg, commands);
 
-       return sctp_sf_abort_violation(ep, asoc, arg, commands, err_str,
+       return sctp_sf_abort_violation(net, ep, asoc, arg, commands, err_str,
                                        sizeof(err_str));
 }
 /***************************************************************************
@@ -4523,7 +4595,8 @@ static sctp_disposition_t sctp_sf_violation_chunk(
  *
  * The return value is a disposition.
  */
-sctp_disposition_t sctp_sf_do_prm_asoc(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_prm_asoc(struct net *net,
+                                      const struct sctp_endpoint *ep,
                                       const struct sctp_association *asoc,
                                       const sctp_subtype_t type,
                                       void *arg,
@@ -4634,7 +4707,8 @@ nomem:
  *
  * The return value is the disposition.
  */
-sctp_disposition_t sctp_sf_do_prm_send(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_prm_send(struct net *net,
+                                      const struct sctp_endpoint *ep,
                                       const struct sctp_association *asoc,
                                       const sctp_subtype_t type,
                                       void *arg,
@@ -4673,6 +4747,7 @@ sctp_disposition_t sctp_sf_do_prm_send(const struct sctp_endpoint *ep,
  * The return value is the disposition.
  */
 sctp_disposition_t sctp_sf_do_9_2_prm_shutdown(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -4694,7 +4769,7 @@ sctp_disposition_t sctp_sf_do_9_2_prm_shutdown(
 
        disposition = SCTP_DISPOSITION_CONSUME;
        if (sctp_outq_is_empty(&asoc->outqueue)) {
-               disposition = sctp_sf_do_9_2_start_shutdown(ep, asoc, type,
+               disposition = sctp_sf_do_9_2_start_shutdown(net, ep, asoc, type,
                                                            arg, commands);
        }
        return disposition;
@@ -4728,6 +4803,7 @@ sctp_disposition_t sctp_sf_do_9_2_prm_shutdown(
  * The return value is the disposition.
  */
 sctp_disposition_t sctp_sf_do_9_1_prm_abort(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -4759,14 +4835,15 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort(
        sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                        SCTP_PERR(SCTP_ERROR_USER_ABORT));
 
-       SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
-       SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
+       SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
+       SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
 
        return retval;
 }
 
 /* We tried an illegal operation on an association which is closed.  */
-sctp_disposition_t sctp_sf_error_closed(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_error_closed(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -4779,7 +4856,8 @@ sctp_disposition_t sctp_sf_error_closed(const struct sctp_endpoint *ep,
 /* We tried an illegal operation on an association which is shutting
  * down.
  */
-sctp_disposition_t sctp_sf_error_shutdown(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_error_shutdown(struct net *net,
+                                         const struct sctp_endpoint *ep,
                                          const struct sctp_association *asoc,
                                          const sctp_subtype_t type,
                                          void *arg,
@@ -4805,6 +4883,7 @@ sctp_disposition_t sctp_sf_error_shutdown(const struct sctp_endpoint *ep,
  * (timers)
  */
 sctp_disposition_t sctp_sf_cookie_wait_prm_shutdown(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -4817,7 +4896,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_shutdown(
        sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
                        SCTP_STATE(SCTP_STATE_CLOSED));
 
-       SCTP_INC_STATS(SCTP_MIB_SHUTDOWNS);
+       SCTP_INC_STATS(net, SCTP_MIB_SHUTDOWNS);
 
        sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
 
@@ -4839,6 +4918,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_shutdown(
  * (timers)
  */
 sctp_disposition_t sctp_sf_cookie_echoed_prm_shutdown(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -4847,7 +4927,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_prm_shutdown(
        /* There is a single T1 timer, so we should be able to use
         * common function with the COOKIE-WAIT state.
         */
-       return sctp_sf_cookie_wait_prm_shutdown(ep, asoc, type, arg, commands);
+       return sctp_sf_cookie_wait_prm_shutdown(net, ep, asoc, type, arg, commands);
 }
 
 /*
@@ -4865,6 +4945,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_prm_shutdown(
  * (timers)
  */
 sctp_disposition_t sctp_sf_cookie_wait_prm_abort(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -4884,7 +4965,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(
        sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
                        SCTP_STATE(SCTP_STATE_CLOSED));
 
-       SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
+       SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
 
        /* Even if we can't send the ABORT due to low memory delete the
         * TCB.  This is a departure from our typical NOMEM handling.
@@ -4914,6 +4995,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(
  * (timers)
  */
 sctp_disposition_t sctp_sf_cookie_echoed_prm_abort(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -4923,7 +5005,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_prm_abort(
        /* There is a single T1 timer, so we should be able to use
         * common function with the COOKIE-WAIT state.
         */
-       return sctp_sf_cookie_wait_prm_abort(ep, asoc, type, arg, commands);
+       return sctp_sf_cookie_wait_prm_abort(net, ep, asoc, type, arg, commands);
 }
 
 /*
@@ -4939,6 +5021,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_prm_abort(
  * (timers)
  */
 sctp_disposition_t sctp_sf_shutdown_pending_prm_abort(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -4949,7 +5032,7 @@ sctp_disposition_t sctp_sf_shutdown_pending_prm_abort(
        sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
                        SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
 
-       return sctp_sf_do_9_1_prm_abort(ep, asoc, type, arg, commands);
+       return sctp_sf_do_9_1_prm_abort(net, ep, asoc, type, arg, commands);
 }
 
 /*
@@ -4965,6 +5048,7 @@ sctp_disposition_t sctp_sf_shutdown_pending_prm_abort(
  * (timers)
  */
 sctp_disposition_t sctp_sf_shutdown_sent_prm_abort(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -4979,7 +5063,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_prm_abort(
        sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
                        SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
 
-       return sctp_sf_do_9_1_prm_abort(ep, asoc, type, arg, commands);
+       return sctp_sf_do_9_1_prm_abort(net, ep, asoc, type, arg, commands);
 }
 
 /*
@@ -4995,6 +5079,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_prm_abort(
  * (timers)
  */
 sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -5004,7 +5089,7 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort(
        /* The same T2 timer, so we should be able to use
         * common function with the SHUTDOWN-SENT state.
         */
-       return sctp_sf_shutdown_sent_prm_abort(ep, asoc, type, arg, commands);
+       return sctp_sf_shutdown_sent_prm_abort(net, ep, asoc, type, arg, commands);
 }
 
 /*
@@ -5030,6 +5115,7 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort(
  *   association on which a heartbeat should be issued.
  */
 sctp_disposition_t sctp_sf_do_prm_requestheartbeat(
+                                       struct net *net,
                                        const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
@@ -5061,7 +5147,8 @@ sctp_disposition_t sctp_sf_do_prm_requestheartbeat(
  * When an endpoint has an ASCONF signaled change to be sent to the
  * remote endpoint it should do A1 to A9
  */
-sctp_disposition_t sctp_sf_do_prm_asconf(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_prm_asconf(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -5082,6 +5169,7 @@ sctp_disposition_t sctp_sf_do_prm_asconf(const struct sctp_endpoint *ep,
  * The return value is the disposition of the primitive.
  */
 sctp_disposition_t sctp_sf_ignore_primitive(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -5103,6 +5191,7 @@ sctp_disposition_t sctp_sf_ignore_primitive(
  * retransmit, the stack will immediately send up this notification.
  */
 sctp_disposition_t sctp_sf_do_no_pending_tsn(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -5134,6 +5223,7 @@ sctp_disposition_t sctp_sf_do_no_pending_tsn(
  * The return value is the disposition.
  */
 sctp_disposition_t sctp_sf_do_9_2_start_shutdown(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -5203,6 +5293,7 @@ nomem:
  * The return value is the disposition.
  */
 sctp_disposition_t sctp_sf_do_9_2_shutdown_ack(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -5221,11 +5312,11 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown_ack(
         */
        if (chunk) {
                if (!sctp_vtag_verify(chunk, asoc))
-                       return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+                       return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
                /* Make sure that the SHUTDOWN chunk has a valid length. */
                if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_shutdown_chunk_t)))
-                       return sctp_sf_violation_chunklen(ep, asoc, type, arg,
+                       return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                          commands);
        }
 
@@ -5273,7 +5364,8 @@ nomem:
  *
  * The return value is the disposition of the event.
  */
-sctp_disposition_t sctp_sf_ignore_other(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_ignore_other(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -5298,7 +5390,8 @@ sctp_disposition_t sctp_sf_ignore_other(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_6_3_3_rtx(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -5306,7 +5399,7 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep,
 {
        struct sctp_transport *transport = arg;
 
-       SCTP_INC_STATS(SCTP_MIB_T3_RTX_EXPIREDS);
+       SCTP_INC_STATS(net, SCTP_MIB_T3_RTX_EXPIREDS);
 
        if (asoc->overall_error_count >= asoc->max_retrans) {
                if (asoc->state == SCTP_STATE_SHUTDOWN_PENDING) {
@@ -5327,8 +5420,8 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep,
                        /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
                        sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                        SCTP_PERR(SCTP_ERROR_NO_ERROR));
-                       SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
-                       SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
+                       SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
+                       SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
                        return SCTP_DISPOSITION_DELETE_TCB;
                }
        }
@@ -5384,13 +5477,14 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep,
  * allow. However, an SCTP transmitter MUST NOT be more aggressive than
  * the following algorithms allow.
  */
-sctp_disposition_t sctp_sf_do_6_2_sack(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_do_6_2_sack(struct net *net,
+                                      const struct sctp_endpoint *ep,
                                       const struct sctp_association *asoc,
                                       const sctp_subtype_t type,
                                       void *arg,
                                       sctp_cmd_seq_t *commands)
 {
-       SCTP_INC_STATS(SCTP_MIB_DELAY_SACK_EXPIREDS);
+       SCTP_INC_STATS(net, SCTP_MIB_DELAY_SACK_EXPIREDS);
        sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE());
        return SCTP_DISPOSITION_CONSUME;
 }
@@ -5414,7 +5508,8 @@ sctp_disposition_t sctp_sf_do_6_2_sack(const struct sctp_endpoint *ep,
  * (timers, events)
  *
  */
-sctp_disposition_t sctp_sf_t1_init_timer_expire(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_t1_init_timer_expire(struct net *net,
+                                          const struct sctp_endpoint *ep,
                                           const struct sctp_association *asoc,
                                           const sctp_subtype_t type,
                                           void *arg,
@@ -5425,7 +5520,7 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(const struct sctp_endpoint *ep,
        int attempts = asoc->init_err_counter + 1;
 
        SCTP_DEBUG_PRINTK("Timer T1 expired (INIT).\n");
-       SCTP_INC_STATS(SCTP_MIB_T1_INIT_EXPIREDS);
+       SCTP_INC_STATS(net, SCTP_MIB_T1_INIT_EXPIREDS);
 
        if (attempts <= asoc->max_init_attempts) {
                bp = (struct sctp_bind_addr *) &asoc->base.bind_addr;
@@ -5475,7 +5570,8 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(const struct sctp_endpoint *ep,
  * (timers, events)
  *
  */
-sctp_disposition_t sctp_sf_t1_cookie_timer_expire(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_t1_cookie_timer_expire(struct net *net,
+                                          const struct sctp_endpoint *ep,
                                           const struct sctp_association *asoc,
                                           const sctp_subtype_t type,
                                           void *arg,
@@ -5485,7 +5581,7 @@ sctp_disposition_t sctp_sf_t1_cookie_timer_expire(const struct sctp_endpoint *ep
        int attempts = asoc->init_err_counter + 1;
 
        SCTP_DEBUG_PRINTK("Timer T1 expired (COOKIE-ECHO).\n");
-       SCTP_INC_STATS(SCTP_MIB_T1_COOKIE_EXPIREDS);
+       SCTP_INC_STATS(net, SCTP_MIB_T1_COOKIE_EXPIREDS);
 
        if (attempts <= asoc->max_init_attempts) {
                repl = sctp_make_cookie_echo(asoc, NULL);
@@ -5523,7 +5619,8 @@ sctp_disposition_t sctp_sf_t1_cookie_timer_expire(const struct sctp_endpoint *ep
  * the T2-Shutdown timer,  giving its peer ample opportunity to transmit
  * all of its queued DATA chunks that have not yet been sent.
  */
-sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_t2_timer_expire(struct net *net,
+                                          const struct sctp_endpoint *ep,
                                           const struct sctp_association *asoc,
                                           const sctp_subtype_t type,
                                           void *arg,
@@ -5532,7 +5629,7 @@ sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep,
        struct sctp_chunk *reply = NULL;
 
        SCTP_DEBUG_PRINTK("Timer T2 expired.\n");
-       SCTP_INC_STATS(SCTP_MIB_T2_SHUTDOWN_EXPIREDS);
+       SCTP_INC_STATS(net, SCTP_MIB_T2_SHUTDOWN_EXPIREDS);
 
        ((struct sctp_association *)asoc)->shutdown_retries++;
 
@@ -5542,8 +5639,8 @@ sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep,
                /* Note:  CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
                sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                SCTP_PERR(SCTP_ERROR_NO_ERROR));
-               SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
-               SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
+               SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
+               SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
                return SCTP_DISPOSITION_DELETE_TCB;
        }
 
@@ -5592,6 +5689,7 @@ nomem:
  * If the T4 RTO timer expires the endpoint should do B1 to B5
  */
 sctp_disposition_t sctp_sf_t4_timer_expire(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -5601,7 +5699,7 @@ sctp_disposition_t sctp_sf_t4_timer_expire(
        struct sctp_chunk *chunk = asoc->addip_last_asconf;
        struct sctp_transport *transport = chunk->transport;
 
-       SCTP_INC_STATS(SCTP_MIB_T4_RTO_EXPIREDS);
+       SCTP_INC_STATS(net, SCTP_MIB_T4_RTO_EXPIREDS);
 
        /* ADDIP 4.1 B1) Increment the error counters and perform path failure
         * detection on the appropriate destination address as defined in
@@ -5626,8 +5724,8 @@ sctp_disposition_t sctp_sf_t4_timer_expire(
                                SCTP_ERROR(ETIMEDOUT));
                sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                SCTP_PERR(SCTP_ERROR_NO_ERROR));
-               SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
-               SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
+               SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
+               SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
                return SCTP_DISPOSITION_ABORT;
        }
 
@@ -5662,7 +5760,8 @@ sctp_disposition_t sctp_sf_t4_timer_expire(
  * At the expiration of this timer the sender SHOULD abort the association
  * by sending an ABORT chunk.
  */
-sctp_disposition_t sctp_sf_t5_timer_expire(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_t5_timer_expire(struct net *net,
+                                          const struct sctp_endpoint *ep,
                                           const struct sctp_association *asoc,
                                           const sctp_subtype_t type,
                                           void *arg,
@@ -5671,7 +5770,7 @@ sctp_disposition_t sctp_sf_t5_timer_expire(const struct sctp_endpoint *ep,
        struct sctp_chunk *reply = NULL;
 
        SCTP_DEBUG_PRINTK("Timer T5 expired.\n");
-       SCTP_INC_STATS(SCTP_MIB_T5_SHUTDOWN_GUARD_EXPIREDS);
+       SCTP_INC_STATS(net, SCTP_MIB_T5_SHUTDOWN_GUARD_EXPIREDS);
 
        reply = sctp_make_abort(asoc, NULL, 0);
        if (!reply)
@@ -5683,8 +5782,8 @@ sctp_disposition_t sctp_sf_t5_timer_expire(const struct sctp_endpoint *ep,
        sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                        SCTP_PERR(SCTP_ERROR_NO_ERROR));
 
-       SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
-       SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
+       SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
+       SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
 
        return SCTP_DISPOSITION_DELETE_TCB;
 nomem:
@@ -5697,6 +5796,7 @@ nomem:
  * the user.  So this routine looks same as sctp_sf_do_9_2_prm_shutdown().
  */
 sctp_disposition_t sctp_sf_autoclose_timer_expire(
+       struct net *net,
        const struct sctp_endpoint *ep,
        const struct sctp_association *asoc,
        const sctp_subtype_t type,
@@ -5705,7 +5805,7 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire(
 {
        int disposition;
 
-       SCTP_INC_STATS(SCTP_MIB_AUTOCLOSE_EXPIREDS);
+       SCTP_INC_STATS(net, SCTP_MIB_AUTOCLOSE_EXPIREDS);
 
        /* From 9.2 Shutdown of an Association
         * Upon receipt of the SHUTDOWN primitive from its upper
@@ -5720,7 +5820,7 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire(
 
        disposition = SCTP_DISPOSITION_CONSUME;
        if (sctp_outq_is_empty(&asoc->outqueue)) {
-               disposition = sctp_sf_do_9_2_start_shutdown(ep, asoc, type,
+               disposition = sctp_sf_do_9_2_start_shutdown(net, ep, asoc, type,
                                                            arg, commands);
        }
        return disposition;
@@ -5738,7 +5838,8 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire(
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_not_impl(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_not_impl(struct net *net,
+                                   const struct sctp_endpoint *ep,
                                    const struct sctp_association *asoc,
                                    const sctp_subtype_t type,
                                    void *arg,
@@ -5755,7 +5856,8 @@ sctp_disposition_t sctp_sf_not_impl(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_bug(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_bug(struct net *net,
+                              const struct sctp_endpoint *ep,
                               const struct sctp_association *asoc,
                               const sctp_subtype_t type,
                               void *arg,
@@ -5775,7 +5877,8 @@ sctp_disposition_t sctp_sf_bug(const struct sctp_endpoint *ep,
  *
  * The return value is the disposition of the chunk.
  */
-sctp_disposition_t sctp_sf_timer_ignore(const struct sctp_endpoint *ep,
+sctp_disposition_t sctp_sf_timer_ignore(struct net *net,
+                                       const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const sctp_subtype_t type,
                                        void *arg,
@@ -5817,7 +5920,8 @@ static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk)
 /* Create an ABORT packet to be sent as a response, with the specified
  * error causes.
  */
-static struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep,
+static struct sctp_packet *sctp_abort_pkt_new(struct net *net,
+                                 const struct sctp_endpoint *ep,
                                  const struct sctp_association *asoc,
                                  struct sctp_chunk *chunk,
                                  const void *payload,
@@ -5826,7 +5930,7 @@ static struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep,
        struct sctp_packet *packet;
        struct sctp_chunk *abort;
 
-       packet = sctp_ootb_pkt_new(asoc, chunk);
+       packet = sctp_ootb_pkt_new(net, asoc, chunk);
 
        if (packet) {
                /* Make an ABORT.
@@ -5858,7 +5962,8 @@ static struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep,
 }
 
 /* Allocate a packet for responding in the OOTB conditions.  */
-static struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc,
+static struct sctp_packet *sctp_ootb_pkt_new(struct net *net,
+                                            const struct sctp_association *asoc,
                                             const struct sctp_chunk *chunk)
 {
        struct sctp_packet *packet;
@@ -5911,7 +6016,7 @@ static struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc
        }
 
        /* Make a transport for the bucket, Eliza... */
-       transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC);
+       transport = sctp_transport_new(net, sctp_source(chunk), GFP_ATOMIC);
        if (!transport)
                goto nomem;
 
@@ -5919,7 +6024,7 @@ static struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc
         * the source address.
         */
        sctp_transport_route(transport, (union sctp_addr *)&chunk->dest,
-                            sctp_sk(sctp_get_ctl_sock()));
+                            sctp_sk(net->sctp.ctl_sock));
 
        packet = sctp_packet_init(&transport->packet, transport, sport, dport);
        packet = sctp_packet_config(packet, vtag, 0);
@@ -5937,7 +6042,8 @@ void sctp_ootb_pkt_free(struct sctp_packet *packet)
 }
 
 /* Send a stale cookie error when a invalid COOKIE ECHO chunk is found  */
-static void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep,
+static void sctp_send_stale_cookie_err(struct net *net,
+                                      const struct sctp_endpoint *ep,
                                       const struct sctp_association *asoc,
                                       const struct sctp_chunk *chunk,
                                       sctp_cmd_seq_t *commands,
@@ -5946,7 +6052,7 @@ static void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep,
        struct sctp_packet *packet;
 
        if (err_chunk) {
-               packet = sctp_ootb_pkt_new(asoc, chunk);
+               packet = sctp_ootb_pkt_new(net, asoc, chunk);
                if (packet) {
                        struct sctp_signed_cookie *cookie;
 
@@ -5959,7 +6065,7 @@ static void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep,
                        sctp_packet_append_chunk(packet, err_chunk);
                        sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
                                        SCTP_PACKET(packet));
-                       SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+                       SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS);
                } else
                        sctp_chunk_free (err_chunk);
        }
@@ -5979,6 +6085,7 @@ static int sctp_eat_data(const struct sctp_association *asoc,
        __u32 tsn;
        struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map;
        struct sock *sk = asoc->base.sk;
+       struct net *net = sock_net(sk);
        u16 ssn;
        u16 sid;
        u8 ordered = 0;
@@ -6109,8 +6216,8 @@ static int sctp_eat_data(const struct sctp_association *asoc,
                                SCTP_ERROR(ECONNABORTED));
                sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                SCTP_PERR(SCTP_ERROR_NO_DATA));
-               SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
-               SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
+               SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
+               SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
                return SCTP_IERROR_NO_DATA;
        }
 
@@ -6120,9 +6227,9 @@ static int sctp_eat_data(const struct sctp_association *asoc,
         * if we renege and the chunk arrives again.
         */
        if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
-               SCTP_INC_STATS(SCTP_MIB_INUNORDERCHUNKS);
+               SCTP_INC_STATS(net, SCTP_MIB_INUNORDERCHUNKS);
        else {
-               SCTP_INC_STATS(SCTP_MIB_INORDERCHUNKS);
+               SCTP_INC_STATS(net, SCTP_MIB_INORDERCHUNKS);
                ordered = 1;
        }
 
index 7c211a7f90f4d065eec82baa0cb751373e7eb0be..84d98d8a5a7417bd92ea919c56e0f8033073a6c4 100644 (file)
@@ -59,7 +59,8 @@ other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES];
 static const sctp_sm_table_entry_t
 timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES];
 
-static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid,
+static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(struct net *net,
+                                                           sctp_cid_t cid,
                                                            sctp_state_t state);
 
 
@@ -82,13 +83,14 @@ static const sctp_sm_table_entry_t bug = {
        rtn;                                                            \
 })
 
-const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type,
+const sctp_sm_table_entry_t *sctp_sm_lookup_event(struct net *net,
+                                                 sctp_event_t event_type,
                                                  sctp_state_t state,
                                                  sctp_subtype_t event_subtype)
 {
        switch (event_type) {
        case SCTP_EVENT_T_CHUNK:
-               return sctp_chunk_event_lookup(event_subtype.chunk, state);
+               return sctp_chunk_event_lookup(net, event_subtype.chunk, state);
        case SCTP_EVENT_T_TIMEOUT:
                return DO_LOOKUP(SCTP_EVENT_TIMEOUT_MAX, timeout,
                                 timeout_event_table);
@@ -906,7 +908,8 @@ static const sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][S
        TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE,
 };
 
-static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid,
+static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(struct net *net,
+                                                           sctp_cid_t cid,
                                                            sctp_state_t state)
 {
        if (state > SCTP_STATE_MAX)
@@ -915,12 +918,12 @@ static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid,
        if (cid <= SCTP_CID_BASE_MAX)
                return &chunk_event_table[cid][state];
 
-       if (sctp_prsctp_enable) {
+       if (net->sctp.prsctp_enable) {
                if (cid == SCTP_CID_FWD_TSN)
                        return &prsctp_chunk_event_table[0][state];
        }
 
-       if (sctp_addip_enable) {
+       if (net->sctp.addip_enable) {
                if (cid == SCTP_CID_ASCONF)
                        return &addip_chunk_event_table[0][state];
 
@@ -928,7 +931,7 @@ static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid,
                        return &addip_chunk_event_table[1][state];
        }
 
-       if (sctp_auth_enable) {
+       if (net->sctp.auth_enable) {
                if (cid == SCTP_CID_AUTH)
                        return &auth_chunk_event_table[0][state];
        }
index 5e259817a7f34cd4a183139fe9c4bf5ee2ab6689..d37d24ff197f094d5200fd9e51a692a08112157e 100644 (file)
@@ -427,6 +427,7 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
 static int sctp_send_asconf(struct sctp_association *asoc,
                            struct sctp_chunk *chunk)
 {
+       struct net      *net = sock_net(asoc->base.sk);
        int             retval = 0;
 
        /* If there is an outstanding ASCONF chunk, queue it for later
@@ -439,7 +440,7 @@ static int sctp_send_asconf(struct sctp_association *asoc,
 
        /* Hold the chunk until an ASCONF_ACK is received. */
        sctp_chunk_hold(chunk);
-       retval = sctp_primitive_ASCONF(asoc, chunk);
+       retval = sctp_primitive_ASCONF(net, asoc, chunk);
        if (retval)
                sctp_chunk_free(chunk);
        else
@@ -515,6 +516,7 @@ static int sctp_send_asconf_add_ip(struct sock              *sk,
                                   struct sockaddr      *addrs,
                                   int                  addrcnt)
 {
+       struct net *net = sock_net(sk);
        struct sctp_sock                *sp;
        struct sctp_endpoint            *ep;
        struct sctp_association         *asoc;
@@ -529,7 +531,7 @@ static int sctp_send_asconf_add_ip(struct sock              *sk,
        int                             i;
        int                             retval = 0;
 
-       if (!sctp_addip_enable)
+       if (!net->sctp.addip_enable)
                return retval;
 
        sp = sctp_sk(sk);
@@ -717,6 +719,7 @@ static int sctp_send_asconf_del_ip(struct sock              *sk,
                                   struct sockaddr      *addrs,
                                   int                  addrcnt)
 {
+       struct net *net = sock_net(sk);
        struct sctp_sock        *sp;
        struct sctp_endpoint    *ep;
        struct sctp_association *asoc;
@@ -732,7 +735,7 @@ static int sctp_send_asconf_del_ip(struct sock              *sk,
        int                     stored = 0;
 
        chunk = NULL;
-       if (!sctp_addip_enable)
+       if (!net->sctp.addip_enable)
                return retval;
 
        sp = sctp_sk(sk);
@@ -1050,6 +1053,7 @@ static int __sctp_connect(struct sock* sk,
                          int addrs_size,
                          sctp_assoc_t *assoc_id)
 {
+       struct net *net = sock_net(sk);
        struct sctp_sock *sp;
        struct sctp_endpoint *ep;
        struct sctp_association *asoc = NULL;
@@ -1200,7 +1204,7 @@ static int __sctp_connect(struct sock* sk,
                        goto out_free;
        }
 
-       err = sctp_primitive_ASSOCIATE(asoc, NULL);
+       err = sctp_primitive_ASSOCIATE(net, asoc, NULL);
        if (err < 0) {
                goto out_free;
        }
@@ -1458,6 +1462,7 @@ SCTP_STATIC int sctp_getsockopt_connectx3(struct sock* sk, int len,
  */
 SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
 {
+       struct net *net = sock_net(sk);
        struct sctp_endpoint *ep;
        struct sctp_association *asoc;
        struct list_head *pos, *temp;
@@ -1499,9 +1504,9 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
 
                        chunk = sctp_make_abort_user(asoc, NULL, 0);
                        if (chunk)
-                               sctp_primitive_ABORT(asoc, chunk);
+                               sctp_primitive_ABORT(net, asoc, chunk);
                } else
-                       sctp_primitive_SHUTDOWN(asoc, NULL);
+                       sctp_primitive_SHUTDOWN(net, asoc, NULL);
        }
 
        /* On a TCP-style socket, block for at most linger_time if set. */
@@ -1569,6 +1574,7 @@ SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *);
 SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                             struct msghdr *msg, size_t msg_len)
 {
+       struct net *net = sock_net(sk);
        struct sctp_sock *sp;
        struct sctp_endpoint *ep;
        struct sctp_association *new_asoc=NULL, *asoc=NULL;
@@ -1714,7 +1720,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                if (sinfo_flags & SCTP_EOF) {
                        SCTP_DEBUG_PRINTK("Shutting down association: %p\n",
                                          asoc);
-                       sctp_primitive_SHUTDOWN(asoc, NULL);
+                       sctp_primitive_SHUTDOWN(net, asoc, NULL);
                        err = 0;
                        goto out_unlock;
                }
@@ -1727,7 +1733,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                        }
 
                        SCTP_DEBUG_PRINTK("Aborting association: %p\n", asoc);
-                       sctp_primitive_ABORT(asoc, chunk);
+                       sctp_primitive_ABORT(net, asoc, chunk);
                        err = 0;
                        goto out_unlock;
                }
@@ -1900,7 +1906,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
 
        /* Auto-connect, if we aren't connected already. */
        if (sctp_state(asoc, CLOSED)) {
-               err = sctp_primitive_ASSOCIATE(asoc, NULL);
+               err = sctp_primitive_ASSOCIATE(net, asoc, NULL);
                if (err < 0)
                        goto out_free;
                SCTP_DEBUG_PRINTK("We associated primitively.\n");
@@ -1928,7 +1934,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
         * works that way today.  Keep it that way or this
         * breaks.
         */
-       err = sctp_primitive_SEND(asoc, datamsg);
+       err = sctp_primitive_SEND(net, asoc, datamsg);
        /* Did the lower layer accept the chunk? */
        if (err)
                sctp_datamsg_free(datamsg);
@@ -2320,7 +2326,9 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
        int error;
 
        if (params->spp_flags & SPP_HB_DEMAND && trans) {
-               error = sctp_primitive_REQUESTHEARTBEAT (trans->asoc, trans);
+               struct net *net = sock_net(trans->asoc->base.sk);
+
+               error = sctp_primitive_REQUESTHEARTBEAT(net, trans->asoc, trans);
                if (error)
                        return error;
        }
@@ -3033,6 +3041,7 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
 static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optval,
                                             unsigned int optlen)
 {
+       struct net *net = sock_net(sk);
        struct sctp_sock        *sp;
        struct sctp_association *asoc = NULL;
        struct sctp_setpeerprim prim;
@@ -3042,7 +3051,7 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optva
 
        sp = sctp_sk(sk);
 
-       if (!sctp_addip_enable)
+       if (!net->sctp.addip_enable)
                return -EPERM;
 
        if (optlen != sizeof(struct sctp_setpeerprim))
@@ -3279,9 +3288,10 @@ static int sctp_setsockopt_auth_chunk(struct sock *sk,
                                      char __user *optval,
                                      unsigned int optlen)
 {
+       struct net *net = sock_net(sk);
        struct sctp_authchunk val;
 
-       if (!sctp_auth_enable)
+       if (!net->sctp.auth_enable)
                return -EACCES;
 
        if (optlen != sizeof(struct sctp_authchunk))
@@ -3311,11 +3321,12 @@ static int sctp_setsockopt_hmac_ident(struct sock *sk,
                                      char __user *optval,
                                      unsigned int optlen)
 {
+       struct net *net = sock_net(sk);
        struct sctp_hmacalgo *hmacs;
        u32 idents;
        int err;
 
-       if (!sctp_auth_enable)
+       if (!net->sctp.auth_enable)
                return -EACCES;
 
        if (optlen < sizeof(struct sctp_hmacalgo))
@@ -3348,11 +3359,12 @@ static int sctp_setsockopt_auth_key(struct sock *sk,
                                    char __user *optval,
                                    unsigned int optlen)
 {
+       struct net *net = sock_net(sk);
        struct sctp_authkey *authkey;
        struct sctp_association *asoc;
        int ret;
 
-       if (!sctp_auth_enable)
+       if (!net->sctp.auth_enable)
                return -EACCES;
 
        if (optlen <= sizeof(struct sctp_authkey))
@@ -3389,10 +3401,11 @@ static int sctp_setsockopt_active_key(struct sock *sk,
                                      char __user *optval,
                                      unsigned int optlen)
 {
+       struct net *net = sock_net(sk);
        struct sctp_authkeyid val;
        struct sctp_association *asoc;
 
-       if (!sctp_auth_enable)
+       if (!net->sctp.auth_enable)
                return -EACCES;
 
        if (optlen != sizeof(struct sctp_authkeyid))
@@ -3417,10 +3430,11 @@ static int sctp_setsockopt_del_key(struct sock *sk,
                                   char __user *optval,
                                   unsigned int optlen)
 {
+       struct net *net = sock_net(sk);
        struct sctp_authkeyid val;
        struct sctp_association *asoc;
 
-       if (!sctp_auth_enable)
+       if (!net->sctp.auth_enable)
                return -EACCES;
 
        if (optlen != sizeof(struct sctp_authkeyid))
@@ -3471,7 +3485,7 @@ static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval,
                sp->do_auto_asconf = 0;
        } else if (val && !sp->do_auto_asconf) {
                list_add_tail(&sp->auto_asconf_list,
-                   &sctp_auto_asconf_splist);
+                   &sock_net(sk)->sctp.auto_asconf_splist);
                sp->do_auto_asconf = 1;
        }
        return 0;
@@ -3843,6 +3857,7 @@ out:
  */
 SCTP_STATIC int sctp_init_sock(struct sock *sk)
 {
+       struct net *net = sock_net(sk);
        struct sctp_endpoint *ep;
        struct sctp_sock *sp;
 
@@ -3872,7 +3887,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
        sp->default_timetolive = 0;
 
        sp->default_rcv_context = 0;
-       sp->max_burst = sctp_max_burst;
+       sp->max_burst = net->sctp.max_burst;
 
        /* Initialize default setup parameters. These parameters
         * can be modified with the SCTP_INITMSG socket option or
@@ -3880,24 +3895,24 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
         */
        sp->initmsg.sinit_num_ostreams   = sctp_max_outstreams;
        sp->initmsg.sinit_max_instreams  = sctp_max_instreams;
-       sp->initmsg.sinit_max_attempts   = sctp_max_retrans_init;
-       sp->initmsg.sinit_max_init_timeo = sctp_rto_max;
+       sp->initmsg.sinit_max_attempts   = net->sctp.max_retrans_init;
+       sp->initmsg.sinit_max_init_timeo = net->sctp.rto_max;
 
        /* Initialize default RTO related parameters.  These parameters can
         * be modified for with the SCTP_RTOINFO socket option.
         */
-       sp->rtoinfo.srto_initial = sctp_rto_initial;
-       sp->rtoinfo.srto_max     = sctp_rto_max;
-       sp->rtoinfo.srto_min     = sctp_rto_min;
+       sp->rtoinfo.srto_initial = net->sctp.rto_initial;
+       sp->rtoinfo.srto_max     = net->sctp.rto_max;
+       sp->rtoinfo.srto_min     = net->sctp.rto_min;
 
        /* Initialize default association related parameters. These parameters
         * can be modified with the SCTP_ASSOCINFO socket option.
         */
-       sp->assocparams.sasoc_asocmaxrxt = sctp_max_retrans_association;
+       sp->assocparams.sasoc_asocmaxrxt = net->sctp.max_retrans_association;
        sp->assocparams.sasoc_number_peer_destinations = 0;
        sp->assocparams.sasoc_peer_rwnd = 0;
        sp->assocparams.sasoc_local_rwnd = 0;
-       sp->assocparams.sasoc_cookie_life = sctp_valid_cookie_life;
+       sp->assocparams.sasoc_cookie_life = net->sctp.valid_cookie_life;
 
        /* Initialize default event subscriptions. By default, all the
         * options are off.
@@ -3907,10 +3922,10 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
        /* Default Peer Address Parameters.  These defaults can
         * be modified via SCTP_PEER_ADDR_PARAMS
         */
-       sp->hbinterval  = sctp_hb_interval;
-       sp->pathmaxrxt  = sctp_max_retrans_path;
+       sp->hbinterval  = net->sctp.hb_interval;
+       sp->pathmaxrxt  = net->sctp.max_retrans_path;
        sp->pathmtu     = 0; // allow default discovery
-       sp->sackdelay   = sctp_sack_timeout;
+       sp->sackdelay   = net->sctp.sack_timeout;
        sp->sackfreq    = 2;
        sp->param_flags = SPP_HB_ENABLE |
                          SPP_PMTUD_ENABLE |
@@ -3961,10 +3976,10 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
 
        local_bh_disable();
        percpu_counter_inc(&sctp_sockets_allocated);
-       sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
-       if (sctp_default_auto_asconf) {
+       sock_prot_inuse_add(net, sk->sk_prot, 1);
+       if (net->sctp.default_auto_asconf) {
                list_add_tail(&sp->auto_asconf_list,
-                   &sctp_auto_asconf_splist);
+                   &net->sctp.auto_asconf_splist);
                sp->do_auto_asconf = 1;
        } else
                sp->do_auto_asconf = 0;
@@ -4011,6 +4026,7 @@ SCTP_STATIC void sctp_destroy_sock(struct sock *sk)
  */
 SCTP_STATIC void sctp_shutdown(struct sock *sk, int how)
 {
+       struct net *net = sock_net(sk);
        struct sctp_endpoint *ep;
        struct sctp_association *asoc;
 
@@ -4022,7 +4038,7 @@ SCTP_STATIC void sctp_shutdown(struct sock *sk, int how)
                if (!list_empty(&ep->asocs)) {
                        asoc = list_entry(ep->asocs.next,
                                          struct sctp_association, asocs);
-                       sctp_primitive_SHUTDOWN(asoc, NULL);
+                       sctp_primitive_SHUTDOWN(net, asoc, NULL);
                }
        }
 }
@@ -4653,9 +4669,10 @@ static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
        union sctp_addr temp;
        int cnt = 0;
        int addrlen;
+       struct net *net = sock_net(sk);
 
        rcu_read_lock();
-       list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) {
+       list_for_each_entry_rcu(addr, &net->sctp.local_addr_list, list) {
                if (!addr->valid)
                        continue;
 
@@ -5299,12 +5316,13 @@ static int sctp_getsockopt_maxburst(struct sock *sk, int len,
 static int sctp_getsockopt_hmac_ident(struct sock *sk, int len,
                                    char __user *optval, int __user *optlen)
 {
+       struct net *net = sock_net(sk);
        struct sctp_hmacalgo  __user *p = (void __user *)optval;
        struct sctp_hmac_algo_param *hmacs;
        __u16 data_len = 0;
        u32 num_idents;
 
-       if (!sctp_auth_enable)
+       if (!net->sctp.auth_enable)
                return -EACCES;
 
        hmacs = sctp_sk(sk)->ep->auth_hmacs_list;
@@ -5328,10 +5346,11 @@ static int sctp_getsockopt_hmac_ident(struct sock *sk, int len,
 static int sctp_getsockopt_active_key(struct sock *sk, int len,
                                    char __user *optval, int __user *optlen)
 {
+       struct net *net = sock_net(sk);
        struct sctp_authkeyid val;
        struct sctp_association *asoc;
 
-       if (!sctp_auth_enable)
+       if (!net->sctp.auth_enable)
                return -EACCES;
 
        if (len < sizeof(struct sctp_authkeyid))
@@ -5360,6 +5379,7 @@ static int sctp_getsockopt_active_key(struct sock *sk, int len,
 static int sctp_getsockopt_peer_auth_chunks(struct sock *sk, int len,
                                    char __user *optval, int __user *optlen)
 {
+       struct net *net = sock_net(sk);
        struct sctp_authchunks __user *p = (void __user *)optval;
        struct sctp_authchunks val;
        struct sctp_association *asoc;
@@ -5367,7 +5387,7 @@ static int sctp_getsockopt_peer_auth_chunks(struct sock *sk, int len,
        u32    num_chunks = 0;
        char __user *to;
 
-       if (!sctp_auth_enable)
+       if (!net->sctp.auth_enable)
                return -EACCES;
 
        if (len < sizeof(struct sctp_authchunks))
@@ -5403,6 +5423,7 @@ num:
 static int sctp_getsockopt_local_auth_chunks(struct sock *sk, int len,
                                    char __user *optval, int __user *optlen)
 {
+       struct net *net = sock_net(sk);
        struct sctp_authchunks __user *p = (void __user *)optval;
        struct sctp_authchunks val;
        struct sctp_association *asoc;
@@ -5410,7 +5431,7 @@ static int sctp_getsockopt_local_auth_chunks(struct sock *sk, int len,
        u32    num_chunks = 0;
        char __user *to;
 
-       if (!sctp_auth_enable)
+       if (!net->sctp.auth_enable)
                return -EACCES;
 
        if (len < sizeof(struct sctp_authchunks))
@@ -5769,7 +5790,7 @@ static void sctp_unhash(struct sock *sk)
  * a fastreuse flag (FIXME: NPI ipg).
  */
 static struct sctp_bind_bucket *sctp_bucket_create(
-       struct sctp_bind_hashbucket *head, unsigned short snum);
+       struct sctp_bind_hashbucket *head, struct net *, unsigned short snum);
 
 static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
 {
@@ -5799,11 +5820,12 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
                                rover = low;
                        if (inet_is_reserved_local_port(rover))
                                continue;
-                       index = sctp_phashfn(rover);
+                       index = sctp_phashfn(sock_net(sk), rover);
                        head = &sctp_port_hashtable[index];
                        sctp_spin_lock(&head->lock);
                        sctp_for_each_hentry(pp, node, &head->chain)
-                               if (pp->port == rover)
+                               if ((pp->port == rover) &&
+                                   net_eq(sock_net(sk), pp->net))
                                        goto next;
                        break;
                next:
@@ -5827,10 +5849,10 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
                 * to the port number (snum) - we detect that with the
                 * port iterator, pp being NULL.
                 */
-               head = &sctp_port_hashtable[sctp_phashfn(snum)];
+               head = &sctp_port_hashtable[sctp_phashfn(sock_net(sk), snum)];
                sctp_spin_lock(&head->lock);
                sctp_for_each_hentry(pp, node, &head->chain) {
-                       if (pp->port == snum)
+                       if ((pp->port == snum) && net_eq(pp->net, sock_net(sk)))
                                goto pp_found;
                }
        }
@@ -5881,7 +5903,7 @@ pp_found:
 pp_not_found:
        /* If there was a hash table miss, create a new port.  */
        ret = 1;
-       if (!pp && !(pp = sctp_bucket_create(head, snum)))
+       if (!pp && !(pp = sctp_bucket_create(head, sock_net(sk), snum)))
                goto fail_unlock;
 
        /* In either case (hit or miss), make sure fastreuse is 1 only
@@ -6113,7 +6135,7 @@ unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
  ********************************************************************/
 
 static struct sctp_bind_bucket *sctp_bucket_create(
-       struct sctp_bind_hashbucket *head, unsigned short snum)
+       struct sctp_bind_hashbucket *head, struct net *net, unsigned short snum)
 {
        struct sctp_bind_bucket *pp;
 
@@ -6123,6 +6145,7 @@ static struct sctp_bind_bucket *sctp_bucket_create(
                pp->port = snum;
                pp->fastreuse = 0;
                INIT_HLIST_HEAD(&pp->owner);
+               pp->net = net;
                hlist_add_head(&pp->node, &head->chain);
        }
        return pp;
@@ -6142,7 +6165,8 @@ static void sctp_bucket_destroy(struct sctp_bind_bucket *pp)
 static inline void __sctp_put_port(struct sock *sk)
 {
        struct sctp_bind_hashbucket *head =
-               &sctp_port_hashtable[sctp_phashfn(inet_sk(sk)->inet_num)];
+               &sctp_port_hashtable[sctp_phashfn(sock_net(sk),
+                                                 inet_sk(sk)->inet_num)];
        struct sctp_bind_bucket *pp;
 
        sctp_spin_lock(&head->lock);
@@ -6809,7 +6833,8 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
        newsp->hmac = NULL;
 
        /* Hook this new socket in to the bind_hash list. */
-       head = &sctp_port_hashtable[sctp_phashfn(inet_sk(oldsk)->inet_num)];
+       head = &sctp_port_hashtable[sctp_phashfn(sock_net(oldsk),
+                                                inet_sk(oldsk)->inet_num)];
        sctp_local_bh_disable();
        sctp_spin_lock(&head->lock);
        pp = sctp_sk(oldsk)->bind_hash;
index 2b2bfe933ff14413aa4970391eb25d038ff3d90a..70e3ba5cb50b319319e60c7bfa6fae69bc5c1fed 100644 (file)
@@ -63,9 +63,35 @@ extern int sysctl_sctp_rmem[3];
 extern int sysctl_sctp_wmem[3];
 
 static ctl_table sctp_table[] = {
+       {
+               .procname       = "sctp_mem",
+               .data           = &sysctl_sctp_mem,
+               .maxlen         = sizeof(sysctl_sctp_mem),
+               .mode           = 0644,
+               .proc_handler   = proc_doulongvec_minmax
+       },
+       {
+               .procname       = "sctp_rmem",
+               .data           = &sysctl_sctp_rmem,
+               .maxlen         = sizeof(sysctl_sctp_rmem),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "sctp_wmem",
+               .data           = &sysctl_sctp_wmem,
+               .maxlen         = sizeof(sysctl_sctp_wmem),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+
+       { /* sentinel */ }
+};
+
+static ctl_table sctp_net_table[] = {
        {
                .procname       = "rto_initial",
-               .data           = &sctp_rto_initial,
+               .data           = &init_net.sctp.rto_initial,
                .maxlen         = sizeof(unsigned int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec_minmax,
@@ -74,7 +100,7 @@ static ctl_table sctp_table[] = {
        },
        {
                .procname       = "rto_min",
-               .data           = &sctp_rto_min,
+               .data           = &init_net.sctp.rto_min,
                .maxlen         = sizeof(unsigned int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec_minmax,
@@ -83,7 +109,7 @@ static ctl_table sctp_table[] = {
        },
        {
                .procname       = "rto_max",
-               .data           = &sctp_rto_max,
+               .data           = &init_net.sctp.rto_max,
                .maxlen         = sizeof(unsigned int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec_minmax,
@@ -91,17 +117,22 @@ static ctl_table sctp_table[] = {
                .extra2         = &timer_max
        },
        {
-               .procname       = "valid_cookie_life",
-               .data           = &sctp_valid_cookie_life,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &one,
-               .extra2         = &timer_max
+               .procname       = "rto_alpha_exp_divisor",
+               .data           = &init_net.sctp.rto_alpha,
+               .maxlen         = sizeof(int),
+               .mode           = 0444,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "rto_beta_exp_divisor",
+               .data           = &init_net.sctp.rto_beta,
+               .maxlen         = sizeof(int),
+               .mode           = 0444,
+               .proc_handler   = proc_dointvec,
        },
        {
                .procname       = "max_burst",
-               .data           = &sctp_max_burst,
+               .data           = &init_net.sctp.max_burst,
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec_minmax,
@@ -109,31 +140,42 @@ static ctl_table sctp_table[] = {
                .extra2         = &int_max
        },
        {
-               .procname       = "association_max_retrans",
-               .data           = &sctp_max_retrans_association,
+               .procname       = "cookie_preserve_enable",
+               .data           = &init_net.sctp.cookie_preserve_enable,
                .maxlen         = sizeof(int),
                .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "valid_cookie_life",
+               .data           = &init_net.sctp.valid_cookie_life,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
                .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &one,
-               .extra2         = &int_max
+               .extra1         = &one,
+               .extra2         = &timer_max
        },
        {
-               .procname       = "sndbuf_policy",
-               .data           = &sctp_sndbuf_policy,
+               .procname       = "sack_timeout",
+               .data           = &init_net.sctp.sack_timeout,
                .maxlen         = sizeof(int),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &sack_timer_min,
+               .extra2         = &sack_timer_max,
        },
        {
-               .procname       = "rcvbuf_policy",
-               .data           = &sctp_rcvbuf_policy,
-               .maxlen         = sizeof(int),
+               .procname       = "hb_interval",
+               .data           = &init_net.sctp.hb_interval,
+               .maxlen         = sizeof(unsigned int),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &one,
+               .extra2         = &timer_max
        },
        {
-               .procname       = "path_max_retrans",
-               .data           = &sctp_max_retrans_path,
+               .procname       = "association_max_retrans",
+               .data           = &init_net.sctp.max_retrans_association,
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec_minmax,
@@ -141,17 +183,17 @@ static ctl_table sctp_table[] = {
                .extra2         = &int_max
        },
        {
-               .procname       = "pf_retrans",
-               .data           = &sctp_pf_retrans,
+               .procname       = "path_max_retrans",
+               .data           = &init_net.sctp.max_retrans_path,
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &zero,
+               .extra1         = &one,
                .extra2         = &int_max
        },
        {
                .procname       = "max_init_retransmits",
-               .data           = &sctp_max_retrans_init,
+               .data           = &init_net.sctp.max_retrans_init,
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec_minmax,
@@ -159,103 +201,66 @@ static ctl_table sctp_table[] = {
                .extra2         = &int_max
        },
        {
-               .procname       = "hb_interval",
-               .data           = &sctp_hb_interval,
-               .maxlen         = sizeof(unsigned int),
+               .procname       = "pf_retrans",
+               .data           = &init_net.sctp.pf_retrans,
+               .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &one,
-               .extra2         = &timer_max
+               .extra1         = &zero,
+               .extra2         = &int_max
        },
        {
-               .procname       = "cookie_preserve_enable",
-               .data           = &sctp_cookie_preserve_enable,
+               .procname       = "sndbuf_policy",
+               .data           = &init_net.sctp.sndbuf_policy,
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
        {
-               .procname       = "rto_alpha_exp_divisor",
-               .data           = &sctp_rto_alpha,
-               .maxlen         = sizeof(int),
-               .mode           = 0444,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "rto_beta_exp_divisor",
-               .data           = &sctp_rto_beta,
-               .maxlen         = sizeof(int),
-               .mode           = 0444,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "addip_enable",
-               .data           = &sctp_addip_enable,
+               .procname       = "rcvbuf_policy",
+               .data           = &init_net.sctp.rcvbuf_policy,
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
        {
                .procname       = "default_auto_asconf",
-               .data           = &sctp_default_auto_asconf,
+               .data           = &init_net.sctp.default_auto_asconf,
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
        {
-               .procname       = "prsctp_enable",
-               .data           = &sctp_prsctp_enable,
+               .procname       = "addip_enable",
+               .data           = &init_net.sctp.addip_enable,
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
        {
-               .procname       = "sack_timeout",
-               .data           = &sctp_sack_timeout,
+               .procname       = "addip_noauth_enable",
+               .data           = &init_net.sctp.addip_noauth,
                .maxlen         = sizeof(int),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &sack_timer_min,
-               .extra2         = &sack_timer_max,
-       },
-       {
-               .procname       = "sctp_mem",
-               .data           = &sysctl_sctp_mem,
-               .maxlen         = sizeof(sysctl_sctp_mem),
-               .mode           = 0644,
-               .proc_handler   = proc_doulongvec_minmax
-       },
-       {
-               .procname       = "sctp_rmem",
-               .data           = &sysctl_sctp_rmem,
-               .maxlen         = sizeof(sysctl_sctp_rmem),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "sctp_wmem",
-               .data           = &sysctl_sctp_wmem,
-               .maxlen         = sizeof(sysctl_sctp_wmem),
-               .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
        {
-               .procname       = "auth_enable",
-               .data           = &sctp_auth_enable,
+               .procname       = "prsctp_enable",
+               .data           = &init_net.sctp.prsctp_enable,
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
        {
-               .procname       = "addip_noauth_enable",
-               .data           = &sctp_addip_noauth,
+               .procname       = "auth_enable",
+               .data           = &init_net.sctp.auth_enable,
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
        {
                .procname       = "addr_scope_policy",
-               .data           = &sctp_scope_policy,
+               .data           = &init_net.sctp.scope_policy,
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec_minmax,
@@ -264,7 +269,7 @@ static ctl_table sctp_table[] = {
        },
        {
                .procname       = "rwnd_update_shift",
-               .data           = &sctp_rwnd_upd_shift,
+               .data           = &init_net.sctp.rwnd_upd_shift,
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = &proc_dointvec_minmax,
@@ -273,7 +278,7 @@ static ctl_table sctp_table[] = {
        },
        {
                .procname       = "max_autoclose",
-               .data           = &sctp_max_autoclose,
+               .data           = &init_net.sctp.max_autoclose,
                .maxlen         = sizeof(unsigned long),
                .mode           = 0644,
                .proc_handler   = &proc_doulongvec_minmax,
@@ -284,6 +289,27 @@ static ctl_table sctp_table[] = {
        { /* sentinel */ }
 };
 
+int sctp_sysctl_net_register(struct net *net)
+{
+       struct ctl_table *table;
+       int i;
+
+       table = kmemdup(sctp_net_table, sizeof(sctp_net_table), GFP_KERNEL);
+       if (!table)
+               return -ENOMEM;
+
+       for (i = 0; table[i].data; i++)
+               table[i].data += (char *)(&net->sctp) - (char *)&init_net.sctp;
+
+       net->sctp.sysctl_header = register_net_sysctl(net, "net/sctp", table);
+       return 0;
+}
+
+void sctp_sysctl_net_unregister(struct net *net)
+{
+       unregister_net_sysctl_table(net->sctp.sysctl_header);
+}
+
 static struct ctl_table_header * sctp_sysctl_header;
 
 /* Sysctl registration.  */
index c97472b248a2b257972cd9e4a353e89874ad87aa..953c21e4af977a752362187976e84b578bdb085c 100644 (file)
@@ -59,7 +59,8 @@
 /* 1st Level Abstractions.  */
 
 /* Initialize a new transport from provided memory.  */
-static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
+static struct sctp_transport *sctp_transport_init(struct net *net,
+                                                 struct sctp_transport *peer,
                                                  const union sctp_addr *addr,
                                                  gfp_t gfp)
 {
@@ -76,7 +77,7 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
         * given destination transport address, set RTO to the protocol
         * parameter 'RTO.Initial'.
         */
-       peer->rto = msecs_to_jiffies(sctp_rto_initial);
+       peer->rto = msecs_to_jiffies(net->sctp.rto_initial);
 
        peer->last_time_heard = jiffies;
        peer->last_time_ecne_reduced = jiffies;
@@ -86,8 +87,8 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
                            SPP_SACKDELAY_ENABLE;
 
        /* Initialize the default path max_retrans.  */
-       peer->pathmaxrxt  = sctp_max_retrans_path;
-       peer->pf_retrans  = sctp_pf_retrans;
+       peer->pathmaxrxt  = net->sctp.max_retrans_path;
+       peer->pf_retrans  = net->sctp.pf_retrans;
 
        INIT_LIST_HEAD(&peer->transmitted);
        INIT_LIST_HEAD(&peer->send_ready);
@@ -109,7 +110,8 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
 }
 
 /* Allocate and initialize a new transport.  */
-struct sctp_transport *sctp_transport_new(const union sctp_addr *addr,
+struct sctp_transport *sctp_transport_new(struct net *net,
+                                         const union sctp_addr *addr,
                                          gfp_t gfp)
 {
        struct sctp_transport *transport;
@@ -118,7 +120,7 @@ struct sctp_transport *sctp_transport_new(const union sctp_addr *addr,
        if (!transport)
                goto fail;
 
-       if (!sctp_transport_init(transport, addr, gfp))
+       if (!sctp_transport_init(net, transport, addr, gfp))
                goto fail_init;
 
        transport->malloced = 1;
@@ -316,6 +318,7 @@ void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt)
        SCTP_ASSERT(tp->rto_pending, "rto_pending not set", return);
 
        if (tp->rttvar || tp->srtt) {
+               struct net *net = sock_net(tp->asoc->base.sk);
                /* 6.3.1 C3) When a new RTT measurement R' is made, set
                 * RTTVAR <- (1 - RTO.Beta) * RTTVAR + RTO.Beta * |SRTT - R'|
                 * SRTT <- (1 - RTO.Alpha) * SRTT + RTO.Alpha * R'
@@ -327,10 +330,10 @@ void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt)
                 * For example, assuming the default value of RTO.Alpha of
                 * 1/8, rto_alpha would be expressed as 3.
                 */
-               tp->rttvar = tp->rttvar - (tp->rttvar >> sctp_rto_beta)
-                       + ((abs(tp->srtt - rtt)) >> sctp_rto_beta);
-               tp->srtt = tp->srtt - (tp->srtt >> sctp_rto_alpha)
-                       + (rtt >> sctp_rto_alpha);
+               tp->rttvar = tp->rttvar - (tp->rttvar >> net->sctp.rto_beta)
+                       + ((abs(tp->srtt - rtt)) >> net->sctp.rto_beta);
+               tp->srtt = tp->srtt - (tp->srtt >> net->sctp.rto_alpha)
+                       + (rtt >> net->sctp.rto_alpha);
        } else {
                /* 6.3.1 C2) When the first RTT measurement R is made, set
                 * SRTT <- R, RTTVAR <- R/2.
index 33d894776192205cd4b4a9573ccf70664c723b2d..10c018a5b9fee066c35c364c79cd6fef77e31580 100644 (file)
@@ -702,7 +702,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
        if (rx_count >= asoc->base.sk->sk_rcvbuf) {
 
                if ((asoc->base.sk->sk_userlocks & SOCK_RCVBUF_LOCK) ||
-                   (!sk_rmem_schedule(asoc->base.sk, chunk->skb->truesize)))
+                   (!sk_rmem_schedule(asoc->base.sk, chunk->skb,
+                                      chunk->skb->truesize)))
                        goto fail;
        }
 
index f5a6a4f4faf721af4874538093cb003f4efc202c..360d8697b95c33408d6a4913b9b1d497d27e5ee7 100644 (file)
@@ -326,7 +326,9 @@ static void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq,
  * payload was fragmented on the way and ip had to reassemble them.
  * We add the rest of skb's to the first skb's fraglist.
  */
-static struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff_head *queue, struct sk_buff *f_frag, struct sk_buff *l_frag)
+static struct sctp_ulpevent *sctp_make_reassembled_event(struct net *net,
+       struct sk_buff_head *queue, struct sk_buff *f_frag,
+       struct sk_buff *l_frag)
 {
        struct sk_buff *pos;
        struct sk_buff *new = NULL;
@@ -394,7 +396,7 @@ static struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff_head *qu
        }
 
        event = sctp_skb2event(f_frag);
-       SCTP_INC_STATS(SCTP_MIB_REASMUSRMSGS);
+       SCTP_INC_STATS(net, SCTP_MIB_REASMUSRMSGS);
 
        return event;
 }
@@ -493,7 +495,8 @@ static struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ul
                cevent = sctp_skb2event(pd_first);
                pd_point = sctp_sk(asoc->base.sk)->pd_point;
                if (pd_point && pd_point <= pd_len) {
-                       retval = sctp_make_reassembled_event(&ulpq->reasm,
+                       retval = sctp_make_reassembled_event(sock_net(asoc->base.sk),
+                                                            &ulpq->reasm,
                                                             pd_first,
                                                             pd_last);
                        if (retval)
@@ -503,7 +506,8 @@ static struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ul
 done:
        return retval;
 found:
-       retval = sctp_make_reassembled_event(&ulpq->reasm, first_frag, pos);
+       retval = sctp_make_reassembled_event(sock_net(ulpq->asoc->base.sk),
+                                            &ulpq->reasm, first_frag, pos);
        if (retval)
                retval->msg_flags |= MSG_EOR;
        goto done;
@@ -563,7 +567,8 @@ static struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq *ulpq)
         * further.
         */
 done:
-       retval = sctp_make_reassembled_event(&ulpq->reasm, first_frag, last_frag);
+       retval = sctp_make_reassembled_event(sock_net(ulpq->asoc->base.sk),
+                                       &ulpq->reasm, first_frag, last_frag);
        if (retval && is_last)
                retval->msg_flags |= MSG_EOR;
 
@@ -655,7 +660,8 @@ static struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq)
         * further.
         */
 done:
-       retval = sctp_make_reassembled_event(&ulpq->reasm, first_frag, last_frag);
+       retval = sctp_make_reassembled_event(sock_net(ulpq->asoc->base.sk),
+                                       &ulpq->reasm, first_frag, last_frag);
        return retval;
 }
 
index 9fe8857d8d596e5eb59146416396b834b195b2c1..03d03e37a7d56e8fc3a1505cffc0002baf7726d3 100644 (file)
@@ -21,6 +21,11 @@ config SUNRPC_XPRT_RDMA
 
          If unsure, say N.
 
+config SUNRPC_SWAP
+       bool
+       depends on SUNRPC
+       select NETVM
+
 config RPCSEC_GSS_KRB5
        tristate "Secure RPC: Kerberos V mechanism"
        depends on SUNRPC && CRYPTO
index 727e506cacda0f7e6d95178c783ac695d2e4b1fb..b5c067bccc4595204f21bff24bbf16631e54cad4 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/errno.h>
 #include <linux/hash.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/gss_api.h>
 #include <linux/spinlock.h>
 
 #ifdef RPC_DEBUG
@@ -122,6 +123,59 @@ rpcauth_unregister(const struct rpc_authops *ops)
 }
 EXPORT_SYMBOL_GPL(rpcauth_unregister);
 
+/**
+ * rpcauth_list_flavors - discover registered flavors and pseudoflavors
+ * @array: array to fill in
+ * @size: size of "array"
+ *
+ * Returns the number of array items filled in, or a negative errno.
+ *
+ * The returned array is not sorted by any policy.  Callers should not
+ * rely on the order of the items in the returned array.
+ */
+int
+rpcauth_list_flavors(rpc_authflavor_t *array, int size)
+{
+       rpc_authflavor_t flavor;
+       int result = 0;
+
+       spin_lock(&rpc_authflavor_lock);
+       for (flavor = 0; flavor < RPC_AUTH_MAXFLAVOR; flavor++) {
+               const struct rpc_authops *ops = auth_flavors[flavor];
+               rpc_authflavor_t pseudos[4];
+               int i, len;
+
+               if (result >= size) {
+                       result = -ENOMEM;
+                       break;
+               }
+
+               if (ops == NULL)
+                       continue;
+               if (ops->list_pseudoflavors == NULL) {
+                       array[result++] = ops->au_flavor;
+                       continue;
+               }
+               len = ops->list_pseudoflavors(pseudos, ARRAY_SIZE(pseudos));
+               if (len < 0) {
+                       result = len;
+                       break;
+               }
+               for (i = 0; i < len; i++) {
+                       if (result >= size) {
+                               result = -ENOMEM;
+                               break;
+                       }
+                       array[result++] = pseudos[i];
+               }
+       }
+       spin_unlock(&rpc_authflavor_lock);
+
+       dprintk("RPC:       %s returns %d\n", __func__, result);
+       return result;
+}
+EXPORT_SYMBOL_GPL(rpcauth_list_flavors);
+
 struct rpc_auth *
 rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
 {
index d3ad81f8da5b79551c36b17a7d53007406946699..34c522021004dc31f3ffe04b279333240ab311e1 100644 (file)
@@ -1619,6 +1619,7 @@ static const struct rpc_authops authgss_ops = {
        .crcreate       = gss_create_cred,
        .pipes_create   = gss_pipes_dentries_create,
        .pipes_destroy  = gss_pipes_dentries_destroy,
+       .list_pseudoflavors = gss_mech_list_pseudoflavors,
 };
 
 static const struct rpc_credops gss_credops = {
index 782bfe1b64650d991489d25f94ca7debf05e8ecb..b174fcd9ff4cc3167266dbc205a5b9ad2fd339ae 100644 (file)
@@ -239,14 +239,28 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
 
 EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor);
 
-int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr)
+/**
+ * gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors
+ * @array: array to fill in
+ * @size: size of "array"
+ *
+ * Returns the number of array items filled in, or a negative errno.
+ *
+ * The returned array is not sorted by any policy.  Callers should not
+ * rely on the order of the items in the returned array.
+ */
+int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr, int size)
 {
        struct gss_api_mech *pos = NULL;
        int j, i = 0;
 
        spin_lock(&registered_mechs_lock);
        list_for_each_entry(pos, &registered_mechs, gm_list) {
-               for (j=0; j < pos->gm_pf_num; j++) {
+               for (j = 0; j < pos->gm_pf_num; j++) {
+                       if (i >= size) {
+                               spin_unlock(&registered_mechs_lock);
+                               return -ENOMEM;
+                       }
                        array_ptr[i++] = pos->gm_pfs[j].pseudoflavor;
                }
        }
@@ -254,8 +268,6 @@ int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr)
        return i;
 }
 
-EXPORT_SYMBOL_GPL(gss_mech_list_pseudoflavors);
-
 u32
 gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service)
 {
index 47ad2666fdf6566f170b49e97097a285da29a231..2afd2a84dc35aa5cab139b38f82f5c5206e83728 100644 (file)
@@ -1349,8 +1349,11 @@ static int c_show(struct seq_file *m, void *p)
        if (cache_check(cd, cp, NULL))
                /* cache_check does a cache_put on failure */
                seq_printf(m, "# ");
-       else
+       else {
+               if (cache_is_expired(cd, cp))
+                       seq_printf(m, "# ");
                cache_put(cp, cd);
+       }
 
        return cd->cache_show(m, cd, cp);
 }
index 00eb859b7de5053f42d410be0de6632936a421cd..fa48c60aef2305430956ef9735b20f6a0f62c073 100644 (file)
@@ -717,6 +717,15 @@ void rpc_task_set_client(struct rpc_task *task, struct rpc_clnt *clnt)
                atomic_inc(&clnt->cl_count);
                if (clnt->cl_softrtry)
                        task->tk_flags |= RPC_TASK_SOFT;
+               if (sk_memalloc_socks()) {
+                       struct rpc_xprt *xprt;
+
+                       rcu_read_lock();
+                       xprt = rcu_dereference(clnt->cl_xprt);
+                       if (xprt->swapper)
+                               task->tk_flags |= RPC_TASK_SWAPPER;
+                       rcu_read_unlock();
+               }
                /* Add to the client's list of all tasks */
                spin_lock(&clnt->cl_lock);
                list_add_tail(&task->tk_task, &clnt->cl_tasks);
@@ -1844,12 +1853,13 @@ call_timeout(struct rpc_task *task)
                return;
        }
        if (RPC_IS_SOFT(task)) {
-               if (clnt->cl_chatty)
+               if (clnt->cl_chatty) {
                        rcu_read_lock();
                        printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
                                clnt->cl_protname,
                                rcu_dereference(clnt->cl_xprt)->servername);
                        rcu_read_unlock();
+               }
                if (task->tk_flags & RPC_TASK_TIMEOUT)
                        rpc_exit(task, -ETIMEDOUT);
                else
index 92509ffe15fcacce5de331cbb205a84c4f718a86..a70acae496e44d6c872ba7c93d4d9ce56c2f7f00 100644 (file)
@@ -251,7 +251,7 @@ static int rpcb_create_local_unix(struct net *net)
        if (IS_ERR(clnt)) {
                dprintk("RPC:       failed to create AF_LOCAL rpcbind "
                                "client (errno %ld).\n", PTR_ERR(clnt));
-               result = -PTR_ERR(clnt);
+               result = PTR_ERR(clnt);
                goto out;
        }
 
@@ -298,7 +298,7 @@ static int rpcb_create_local_net(struct net *net)
        if (IS_ERR(clnt)) {
                dprintk("RPC:       failed to create local rpcbind "
                                "client (errno %ld).\n", PTR_ERR(clnt));
-               result = -PTR_ERR(clnt);
+               result = PTR_ERR(clnt);
                goto out;
        }
 
index 994cfea2bad66f814432c2fcbb1227d0a321d34f..128494ec9a6490e29de2ce52a0ae7b2a2a0368e3 100644 (file)
@@ -300,8 +300,9 @@ EXPORT_SYMBOL_GPL(__rpc_wait_for_completion_task);
 /*
  * Make an RPC task runnable.
  *
- * Note: If the task is ASYNC, this must be called with
- * the spinlock held to protect the wait queue operation.
+ * Note: If the task is ASYNC, and is being made runnable after sitting on an
+ * rpc_wait_queue, this must be called with the queue spinlock held to protect
+ * the wait queue operation.
  */
 static void rpc_make_runnable(struct rpc_task *task)
 {
@@ -790,7 +791,9 @@ void rpc_execute(struct rpc_task *task)
 
 static void rpc_async_schedule(struct work_struct *work)
 {
+       current->flags |= PF_FSTRANS;
        __rpc_execute(container_of(work, struct rpc_task, u.tk_work));
+       current->flags &= ~PF_FSTRANS;
 }
 
 /**
@@ -812,7 +815,10 @@ static void rpc_async_schedule(struct work_struct *work)
 void *rpc_malloc(struct rpc_task *task, size_t size)
 {
        struct rpc_buffer *buf;
-       gfp_t gfp = RPC_IS_SWAPPER(task) ? GFP_ATOMIC : GFP_NOWAIT;
+       gfp_t gfp = GFP_NOWAIT;
+
+       if (RPC_IS_SWAPPER(task))
+               gfp |= __GFP_MEMALLOC;
 
        size += sizeof(struct rpc_buffer);
        if (size <= RPC_BUFFER_MAXSIZE)
@@ -886,7 +892,7 @@ static void rpc_init_task(struct rpc_task *task, const struct rpc_task_setup *ta
 static struct rpc_task *
 rpc_alloc_task(void)
 {
-       return (struct rpc_task *)mempool_alloc(rpc_task_mempool, GFP_NOFS);
+       return (struct rpc_task *)mempool_alloc(rpc_task_mempool, GFP_NOIO);
 }
 
 /*
index 0cf165580d8db04c00324fc44a94e349deb4bba4..0afba1b4b65606437d141cb21a64e6d00f3855f4 100644 (file)
@@ -128,34 +128,6 @@ xdr_terminate_string(struct xdr_buf *buf, const u32 len)
 }
 EXPORT_SYMBOL_GPL(xdr_terminate_string);
 
-void
-xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
-                unsigned int len)
-{
-       struct kvec *tail = xdr->tail;
-       u32 *p;
-
-       xdr->pages = pages;
-       xdr->page_base = base;
-       xdr->page_len = len;
-
-       p = (u32 *)xdr->head[0].iov_base + XDR_QUADLEN(xdr->head[0].iov_len);
-       tail->iov_base = p;
-       tail->iov_len = 0;
-
-       if (len & 3) {
-               unsigned int pad = 4 - (len & 3);
-
-               *p = 0;
-               tail->iov_base = (char *)p + (len & 3);
-               tail->iov_len  = pad;
-               len += pad;
-       }
-       xdr->buflen += len;
-       xdr->len += len;
-}
-EXPORT_SYMBOL_GPL(xdr_encode_pages);
-
 void
 xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
                 struct page **pages, unsigned int base, unsigned int len)
@@ -456,6 +428,16 @@ xdr_shift_buf(struct xdr_buf *buf, size_t len)
 }
 EXPORT_SYMBOL_GPL(xdr_shift_buf);
 
+/**
+ * xdr_stream_pos - Return the current offset from the start of the xdr_stream
+ * @xdr: pointer to struct xdr_stream
+ */
+unsigned int xdr_stream_pos(const struct xdr_stream *xdr)
+{
+       return (unsigned int)(XDR_QUADLEN(xdr->buf->len) - xdr->nwords) << 2;
+}
+EXPORT_SYMBOL_GPL(xdr_stream_pos);
+
 /**
  * xdr_init_encode - Initialize a struct xdr_stream for sending data.
  * @xdr: pointer to xdr_stream struct
@@ -556,13 +538,11 @@ void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int b
 EXPORT_SYMBOL_GPL(xdr_write_pages);
 
 static void xdr_set_iov(struct xdr_stream *xdr, struct kvec *iov,
-               __be32 *p, unsigned int len)
+               unsigned int len)
 {
        if (len > iov->iov_len)
                len = iov->iov_len;
-       if (p == NULL)
-               p = (__be32*)iov->iov_base;
-       xdr->p = p;
+       xdr->p = (__be32*)iov->iov_base;
        xdr->end = (__be32*)(iov->iov_base + len);
        xdr->iov = iov;
        xdr->page_ptr = NULL;
@@ -609,7 +589,7 @@ static void xdr_set_next_page(struct xdr_stream *xdr)
        newbase -= xdr->buf->page_base;
 
        if (xdr_set_page_base(xdr, newbase, PAGE_SIZE) < 0)
-               xdr_set_iov(xdr, xdr->buf->tail, NULL, xdr->buf->len);
+               xdr_set_iov(xdr, xdr->buf->tail, xdr->buf->len);
 }
 
 static bool xdr_set_next_buffer(struct xdr_stream *xdr)
@@ -618,7 +598,7 @@ static bool xdr_set_next_buffer(struct xdr_stream *xdr)
                xdr_set_next_page(xdr);
        else if (xdr->iov == xdr->buf->head) {
                if (xdr_set_page_base(xdr, 0, PAGE_SIZE) < 0)
-                       xdr_set_iov(xdr, xdr->buf->tail, NULL, xdr->buf->len);
+                       xdr_set_iov(xdr, xdr->buf->tail, xdr->buf->len);
        }
        return xdr->p != xdr->end;
 }
@@ -634,10 +614,15 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
        xdr->buf = buf;
        xdr->scratch.iov_base = NULL;
        xdr->scratch.iov_len = 0;
+       xdr->nwords = XDR_QUADLEN(buf->len);
        if (buf->head[0].iov_len != 0)
-               xdr_set_iov(xdr, buf->head, p, buf->len);
+               xdr_set_iov(xdr, buf->head, buf->len);
        else if (buf->page_len != 0)
                xdr_set_page_base(xdr, 0, buf->len);
+       if (p != NULL && p > xdr->p && xdr->end >= p) {
+               xdr->nwords -= p - xdr->p;
+               xdr->p = p;
+       }
 }
 EXPORT_SYMBOL_GPL(xdr_init_decode);
 
@@ -662,12 +647,14 @@ EXPORT_SYMBOL_GPL(xdr_init_decode_pages);
 
 static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
 {
+       unsigned int nwords = XDR_QUADLEN(nbytes);
        __be32 *p = xdr->p;
-       __be32 *q = p + XDR_QUADLEN(nbytes);
+       __be32 *q = p + nwords;
 
-       if (unlikely(q > xdr->end || q < p))
+       if (unlikely(nwords > xdr->nwords || q > xdr->end || q < p))
                return NULL;
        xdr->p = q;
+       xdr->nwords -= nwords;
        return p;
 }
 
@@ -734,6 +721,31 @@ __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
 }
 EXPORT_SYMBOL_GPL(xdr_inline_decode);
 
+static unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len)
+{
+       struct xdr_buf *buf = xdr->buf;
+       struct kvec *iov;
+       unsigned int nwords = XDR_QUADLEN(len);
+       unsigned int cur = xdr_stream_pos(xdr);
+
+       if (xdr->nwords == 0)
+               return 0;
+       if (nwords > xdr->nwords) {
+               nwords = xdr->nwords;
+               len = nwords << 2;
+       }
+       /* Realign pages to current pointer position */
+       iov  = buf->head;
+       if (iov->iov_len > cur)
+               xdr_shrink_bufhead(buf, iov->iov_len - cur);
+
+       /* Truncate page data and move it into the tail */
+       if (buf->page_len > len)
+               xdr_shrink_pagelen(buf, buf->page_len - len);
+       xdr->nwords = XDR_QUADLEN(buf->len - cur);
+       return len;
+}
+
 /**
  * xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position
  * @xdr: pointer to xdr_stream struct
@@ -742,39 +754,37 @@ EXPORT_SYMBOL_GPL(xdr_inline_decode);
  * Moves data beyond the current pointer position from the XDR head[] buffer
  * into the page list. Any data that lies beyond current position + "len"
  * bytes is moved into the XDR tail[].
+ *
+ * Returns the number of XDR encoded bytes now contained in the pages
  */
-void xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
+unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
 {
        struct xdr_buf *buf = xdr->buf;
        struct kvec *iov;
-       ssize_t shift;
+       unsigned int nwords;
        unsigned int end;
-       int padding;
+       unsigned int padding;
 
-       /* Realign pages to current pointer position */
-       iov  = buf->head;
-       shift = iov->iov_len + (char *)iov->iov_base - (char *)xdr->p;
-       if (shift > 0)
-               xdr_shrink_bufhead(buf, shift);
-
-       /* Truncate page data and move it into the tail */
-       if (buf->page_len > len)
-               xdr_shrink_pagelen(buf, buf->page_len - len);
-       padding = (XDR_QUADLEN(len) << 2) - len;
+       len = xdr_align_pages(xdr, len);
+       if (len == 0)
+               return 0;
+       nwords = XDR_QUADLEN(len);
+       padding = (nwords << 2) - len;
        xdr->iov = iov = buf->tail;
        /* Compute remaining message length.  */
-       end = iov->iov_len;
-       shift = buf->buflen - buf->len;
-       if (shift < end)
-               end -= shift;
-       else if (shift > 0)
-               end = 0;
+       end = ((xdr->nwords - nwords) << 2) + padding;
+       if (end > iov->iov_len)
+               end = iov->iov_len;
+
        /*
         * Position current pointer at beginning of tail, and
         * set remaining message length.
         */
        xdr->p = (__be32 *)((char *)iov->iov_base + padding);
        xdr->end = (__be32 *)((char *)iov->iov_base + end);
+       xdr->page_ptr = NULL;
+       xdr->nwords = XDR_QUADLEN(end - padding);
+       return len;
 }
 EXPORT_SYMBOL_GPL(xdr_read_pages);
 
@@ -790,12 +800,13 @@ EXPORT_SYMBOL_GPL(xdr_read_pages);
  */
 void xdr_enter_page(struct xdr_stream *xdr, unsigned int len)
 {
-       xdr_read_pages(xdr, len);
+       len = xdr_align_pages(xdr, len);
        /*
         * Position current pointer at beginning of tail, and
         * set remaining message length.
         */
-       xdr_set_page_base(xdr, 0, len);
+       if (len != 0)
+               xdr_set_page_base(xdr, 0, len);
 }
 EXPORT_SYMBOL_GPL(xdr_enter_page);
 
index b446e100286f7aca96f81ca10d6cb176471d55d5..06cdbff79e4af433d5a4ea3a2766d6d09a9b5a01 100644 (file)
@@ -200,6 +200,7 @@ xprt_rdma_connect_worker(struct work_struct *work)
        int rc = 0;
 
        if (!xprt->shutdown) {
+               current->flags |= PF_FSTRANS;
                xprt_clear_connected(xprt);
 
                dprintk("RPC:       %s: %sconnect\n", __func__,
@@ -212,10 +213,10 @@ xprt_rdma_connect_worker(struct work_struct *work)
 
 out:
        xprt_wake_pending_tasks(xprt, rc);
-
 out_clear:
        dprintk("RPC:       %s: exit\n", __func__);
        xprt_clear_connecting(xprt);
+       current->flags &= ~PF_FSTRANS;
 }
 
 /*
index 62d0dac8f7807f9fbfbe3fbc10315e5386197b89..400567243f84ba95e8f04d8a631a3b74539c53ea 100644 (file)
@@ -1892,6 +1892,8 @@ static void xs_local_setup_socket(struct work_struct *work)
        if (xprt->shutdown)
                goto out;
 
+       current->flags |= PF_FSTRANS;
+
        clear_bit(XPRT_CONNECTION_ABORT, &xprt->state);
        status = __sock_create(xprt->xprt_net, AF_LOCAL,
                                        SOCK_STREAM, 0, &sock, 1);
@@ -1925,7 +1927,47 @@ static void xs_local_setup_socket(struct work_struct *work)
 out:
        xprt_clear_connecting(xprt);
        xprt_wake_pending_tasks(xprt, status);
+       current->flags &= ~PF_FSTRANS;
+}
+
+#ifdef CONFIG_SUNRPC_SWAP
+static void xs_set_memalloc(struct rpc_xprt *xprt)
+{
+       struct sock_xprt *transport = container_of(xprt, struct sock_xprt,
+                       xprt);
+
+       if (xprt->swapper)
+               sk_set_memalloc(transport->inet);
+}
+
+/**
+ * xs_swapper - Tag this transport as being used for swap.
+ * @xprt: transport to tag
+ * @enable: enable/disable
+ *
+ */
+int xs_swapper(struct rpc_xprt *xprt, int enable)
+{
+       struct sock_xprt *transport = container_of(xprt, struct sock_xprt,
+                       xprt);
+       int err = 0;
+
+       if (enable) {
+               xprt->swapper++;
+               xs_set_memalloc(xprt);
+       } else if (xprt->swapper) {
+               xprt->swapper--;
+               sk_clear_memalloc(transport->inet);
+       }
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(xs_swapper);
+#else
+static void xs_set_memalloc(struct rpc_xprt *xprt)
+{
 }
+#endif
 
 static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
 {
@@ -1951,6 +1993,8 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
                transport->sock = sock;
                transport->inet = sk;
 
+               xs_set_memalloc(xprt);
+
                write_unlock_bh(&sk->sk_callback_lock);
        }
        xs_udp_do_set_buffer_size(xprt);
@@ -1967,6 +2011,8 @@ static void xs_udp_setup_socket(struct work_struct *work)
        if (xprt->shutdown)
                goto out;
 
+       current->flags |= PF_FSTRANS;
+
        /* Start by resetting any existing state */
        xs_reset_transport(transport);
        sock = xs_create_sock(xprt, transport,
@@ -1985,6 +2031,7 @@ static void xs_udp_setup_socket(struct work_struct *work)
 out:
        xprt_clear_connecting(xprt);
        xprt_wake_pending_tasks(xprt, status);
+       current->flags &= ~PF_FSTRANS;
 }
 
 /*
@@ -2075,6 +2122,8 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
        if (!xprt_bound(xprt))
                goto out;
 
+       xs_set_memalloc(xprt);
+
        /* Tell the socket layer to start connecting... */
        xprt->stat.connect_count++;
        xprt->stat.connect_start = jiffies;
@@ -2110,6 +2159,8 @@ static void xs_tcp_setup_socket(struct work_struct *work)
        if (xprt->shutdown)
                goto out;
 
+       current->flags |= PF_FSTRANS;
+
        if (!sock) {
                clear_bit(XPRT_CONNECTION_ABORT, &xprt->state);
                sock = xs_create_sock(xprt, transport,
@@ -2159,6 +2210,7 @@ static void xs_tcp_setup_socket(struct work_struct *work)
        case -EINPROGRESS:
        case -EALREADY:
                xprt_clear_connecting(xprt);
+               current->flags &= ~PF_FSTRANS;
                return;
        case -EINVAL:
                /* Happens, for instance, if the user specified a link
@@ -2171,6 +2223,7 @@ out_eagain:
 out:
        xprt_clear_connecting(xprt);
        xprt_wake_pending_tasks(xprt, status);
+       current->flags &= ~PF_FSTRANS;
 }
 
 /**
index 79981d97bc9c013904062b1bedfd1ebcae97f845..e4768c180da237c176e2e9491091a94420f4febe 100644 (file)
@@ -823,6 +823,34 @@ fail:
        return NULL;
 }
 
+static int unix_mknod(const char *sun_path, umode_t mode, struct path *res)
+{
+       struct dentry *dentry;
+       struct path path;
+       int err = 0;
+       /*
+        * Get the parent directory, calculate the hash for last
+        * component.
+        */
+       dentry = kern_path_create(AT_FDCWD, sun_path, &path, 0);
+       err = PTR_ERR(dentry);
+       if (IS_ERR(dentry))
+               return err;
+
+       /*
+        * All right, let's create it.
+        */
+       err = security_path_mknod(&path, dentry, mode, 0);
+       if (!err) {
+               err = vfs_mknod(path.dentry->d_inode, dentry, mode, 0);
+               if (!err) {
+                       res->mnt = mntget(path.mnt);
+                       res->dentry = dget(dentry);
+               }
+       }
+       done_path_create(&path, dentry);
+       return err;
+}
 
 static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
@@ -831,8 +859,6 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
        struct unix_sock *u = unix_sk(sk);
        struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;
        char *sun_path = sunaddr->sun_path;
-       struct dentry *dentry = NULL;
-       struct path path;
        int err;
        unsigned int hash;
        struct unix_address *addr;
@@ -869,43 +895,23 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
        atomic_set(&addr->refcnt, 1);
 
        if (sun_path[0]) {
-               umode_t mode;
-               err = 0;
-               /*
-                * Get the parent directory, calculate the hash for last
-                * component.
-                */
-               dentry = kern_path_create(AT_FDCWD, sun_path, &path, 0);
-               err = PTR_ERR(dentry);
-               if (IS_ERR(dentry))
-                       goto out_mknod_parent;
-
-               /*
-                * All right, let's create it.
-                */
-               mode = S_IFSOCK |
+               struct path path;
+               umode_t mode = S_IFSOCK |
                       (SOCK_INODE(sock)->i_mode & ~current_umask());
-               err = mnt_want_write(path.mnt);
-               if (err)
-                       goto out_mknod_dput;
-               err = security_path_mknod(&path, dentry, mode, 0);
-               if (err)
-                       goto out_mknod_drop_write;
-               err = vfs_mknod(path.dentry->d_inode, dentry, mode, 0);
-out_mknod_drop_write:
-               mnt_drop_write(path.mnt);
-               if (err)
-                       goto out_mknod_dput;
-               mutex_unlock(&path.dentry->d_inode->i_mutex);
-               dput(path.dentry);
-               path.dentry = dentry;
-
+               err = unix_mknod(sun_path, mode, &path);
+               if (err) {
+                       if (err == -EEXIST)
+                               err = -EADDRINUSE;
+                       unix_release_addr(addr);
+                       goto out_up;
+               }
                addr->hash = UNIX_HASH_SIZE;
-       }
-
-       spin_lock(&unix_table_lock);
-
-       if (!sun_path[0]) {
+               hash = path.dentry->d_inode->i_ino & (UNIX_HASH_SIZE-1);
+               spin_lock(&unix_table_lock);
+               u->path = path;
+               list = &unix_socket_table[hash];
+       } else {
+               spin_lock(&unix_table_lock);
                err = -EADDRINUSE;
                if (__unix_find_socket_byname(net, sunaddr, addr_len,
                                              sk->sk_type, hash)) {
@@ -914,9 +920,6 @@ out_mknod_drop_write:
                }
 
                list = &unix_socket_table[addr->hash];
-       } else {
-               list = &unix_socket_table[dentry->d_inode->i_ino & (UNIX_HASH_SIZE-1)];
-               u->path = path;
        }
 
        err = 0;
@@ -930,16 +933,6 @@ out_up:
        mutex_unlock(&u->readlock);
 out:
        return err;
-
-out_mknod_dput:
-       dput(dentry);
-       mutex_unlock(&path.dentry->d_inode->i_mutex);
-       path_put(&path);
-out_mknod_parent:
-       if (err == -EEXIST)
-               err = -EADDRINUSE;
-       unix_release_addr(addr);
-       goto out_up;
 }
 
 static void unix_state_double_lock(struct sock *sk1, struct sock *sk2)
index 2303ee73b50ad2fc40dc136c9250861e9bb890ee..2ded3c7fad063a067151595c774b9bddd14bdc9a 100644 (file)
@@ -680,6 +680,8 @@ static u32 map_regdom_flags(u32 rd_flags)
                channel_flags |= IEEE80211_CHAN_NO_IBSS;
        if (rd_flags & NL80211_RRF_DFS)
                channel_flags |= IEEE80211_CHAN_RADAR;
+       if (rd_flags & NL80211_RRF_NO_OFDM)
+               channel_flags |= IEEE80211_CHAN_NO_OFDM;
        return channel_flags;
 }
 
@@ -901,7 +903,21 @@ static void handle_channel(struct wiphy *wiphy,
        chan->max_antenna_gain = min(chan->orig_mag,
                (int) MBI_TO_DBI(power_rule->max_antenna_gain));
        chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
-       chan->max_power = min(chan->max_power, chan->max_reg_power);
+       if (chan->orig_mpwr) {
+               /*
+                * Devices that have their own custom regulatory domain
+                * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the
+                * passed country IE power settings.
+                */
+               if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+                   wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
+                   wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
+                       chan->max_power = chan->max_reg_power;
+               else
+                       chan->max_power = min(chan->orig_mpwr,
+                                             chan->max_reg_power);
+       } else
+               chan->max_power = chan->max_reg_power;
 }
 
 static void handle_band(struct wiphy *wiphy,
@@ -1885,6 +1901,7 @@ static void restore_custom_reg_settings(struct wiphy *wiphy)
                        chan->flags = chan->orig_flags;
                        chan->max_antenna_gain = chan->orig_mag;
                        chan->max_power = chan->orig_mpwr;
+                       chan->beacon_found = false;
                }
        }
 }
index c5a5165a5927a185043bdc83e8c9c4e25f549753..5ad4d2c4b83c5ca1fb58ff719c516b710ba2ef96 100644 (file)
@@ -42,13 +42,14 @@ static DEFINE_SPINLOCK(xfrm_policy_sk_bundle_lock);
 static struct dst_entry *xfrm_policy_sk_bundles;
 static DEFINE_RWLOCK(xfrm_policy_lock);
 
-static DEFINE_RWLOCK(xfrm_policy_afinfo_lock);
-static struct xfrm_policy_afinfo *xfrm_policy_afinfo[NPROTO];
+static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock);
+static struct xfrm_policy_afinfo __rcu *xfrm_policy_afinfo[NPROTO]
+                                               __read_mostly;
 
 static struct kmem_cache *xfrm_dst_cache __read_mostly;
 
 static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family);
-static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo);
+static inline void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo);
 static void xfrm_init_pmtu(struct dst_entry *dst);
 static int stale_bundle(struct dst_entry *dst);
 static int xfrm_bundle_ok(struct xfrm_dst *xdst);
@@ -2418,7 +2419,7 @@ int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
                return -EINVAL;
        if (unlikely(afinfo->family >= NPROTO))
                return -EAFNOSUPPORT;
-       write_lock_bh(&xfrm_policy_afinfo_lock);
+       spin_lock_bh(&xfrm_policy_afinfo_lock);
        if (unlikely(xfrm_policy_afinfo[afinfo->family] != NULL))
                err = -ENOBUFS;
        else {
@@ -2439,9 +2440,9 @@ int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
                        dst_ops->neigh_lookup = xfrm_neigh_lookup;
                if (likely(afinfo->garbage_collect == NULL))
                        afinfo->garbage_collect = xfrm_garbage_collect_deferred;
-               xfrm_policy_afinfo[afinfo->family] = afinfo;
+               rcu_assign_pointer(xfrm_policy_afinfo[afinfo->family], afinfo);
        }
-       write_unlock_bh(&xfrm_policy_afinfo_lock);
+       spin_unlock_bh(&xfrm_policy_afinfo_lock);
 
        rtnl_lock();
        for_each_net(net) {
@@ -2474,13 +2475,14 @@ int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo)
                return -EINVAL;
        if (unlikely(afinfo->family >= NPROTO))
                return -EAFNOSUPPORT;
-       write_lock_bh(&xfrm_policy_afinfo_lock);
+       spin_lock_bh(&xfrm_policy_afinfo_lock);
        if (likely(xfrm_policy_afinfo[afinfo->family] != NULL)) {
                if (unlikely(xfrm_policy_afinfo[afinfo->family] != afinfo))
                        err = -EINVAL;
                else {
                        struct dst_ops *dst_ops = afinfo->dst_ops;
-                       xfrm_policy_afinfo[afinfo->family] = NULL;
+                       rcu_assign_pointer(xfrm_policy_afinfo[afinfo->family],
+                                                                       NULL);
                        dst_ops->kmem_cachep = NULL;
                        dst_ops->check = NULL;
                        dst_ops->negative_advice = NULL;
@@ -2488,7 +2490,8 @@ int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo)
                        afinfo->garbage_collect = NULL;
                }
        }
-       write_unlock_bh(&xfrm_policy_afinfo_lock);
+       spin_unlock_bh(&xfrm_policy_afinfo_lock);
+       synchronize_rcu();
        return err;
 }
 EXPORT_SYMBOL(xfrm_policy_unregister_afinfo);
@@ -2497,16 +2500,16 @@ static void __net_init xfrm_dst_ops_init(struct net *net)
 {
        struct xfrm_policy_afinfo *afinfo;
 
-       read_lock_bh(&xfrm_policy_afinfo_lock);
-       afinfo = xfrm_policy_afinfo[AF_INET];
+       rcu_read_lock_bh();
+       afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET]);
        if (afinfo)
                net->xfrm.xfrm4_dst_ops = *afinfo->dst_ops;
 #if IS_ENABLED(CONFIG_IPV6)
-       afinfo = xfrm_policy_afinfo[AF_INET6];
+       afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET6]);
        if (afinfo)
                net->xfrm.xfrm6_dst_ops = *afinfo->dst_ops;
 #endif
-       read_unlock_bh(&xfrm_policy_afinfo_lock);
+       rcu_read_unlock_bh();
 }
 
 static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family)
@@ -2514,16 +2517,16 @@ static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family)
        struct xfrm_policy_afinfo *afinfo;
        if (unlikely(family >= NPROTO))
                return NULL;
-       read_lock(&xfrm_policy_afinfo_lock);
-       afinfo = xfrm_policy_afinfo[family];
+       rcu_read_lock();
+       afinfo = rcu_dereference(xfrm_policy_afinfo[family]);
        if (unlikely(!afinfo))
-               read_unlock(&xfrm_policy_afinfo_lock);
+               rcu_read_unlock();
        return afinfo;
 }
 
-static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo)
+static inline void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo)
 {
-       read_unlock(&xfrm_policy_afinfo_lock);
+       rcu_read_unlock();
 }
 
 static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
index 5b228f97d4b3308a950ae857828e04e90df86848..87cd0e4d42829a19797ae3e2371c47723fd40fb7 100644 (file)
@@ -415,8 +415,17 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me)
        if (x->lft.hard_add_expires_seconds) {
                long tmo = x->lft.hard_add_expires_seconds +
                        x->curlft.add_time - now;
-               if (tmo <= 0)
-                       goto expired;
+               if (tmo <= 0) {
+                       if (x->xflags & XFRM_SOFT_EXPIRE) {
+                               /* enter hard expire without soft expire first?!
+                                * setting a new date could trigger this.
+                                * workarbound: fix x->curflt.add_time by below:
+                                */
+                               x->curlft.add_time = now - x->saved_tmo - 1;
+                               tmo = x->lft.hard_add_expires_seconds - x->saved_tmo;
+                       } else
+                               goto expired;
+               }
                if (tmo < next)
                        next = tmo;
        }
@@ -433,10 +442,14 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me)
        if (x->lft.soft_add_expires_seconds) {
                long tmo = x->lft.soft_add_expires_seconds +
                        x->curlft.add_time - now;
-               if (tmo <= 0)
+               if (tmo <= 0) {
                        warn = 1;
-               else if (tmo < next)
+                       x->xflags &= ~XFRM_SOFT_EXPIRE;
+               } else if (tmo < next) {
                        next = tmo;
+                       x->xflags |= XFRM_SOFT_EXPIRE;
+                       x->saved_tmo = tmo;
+               }
        }
        if (x->lft.soft_use_expires_seconds) {
                long tmo = x->lft.soft_use_expires_seconds +
index e5bd60ff48e3d553ef3921256123519df7b812b8..913d6bdfdda3ecb44f521af79980019d5a531399 100755 (executable)
@@ -1600,13 +1600,17 @@ sub process {
 
 # Check signature styles
                if (!$in_header_lines &&
-                   $line =~ /^(\s*)($signature_tags)(\s*)(.*)/) {
+                   $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) {
                        my $space_before = $1;
                        my $sign_off = $2;
                        my $space_after = $3;
                        my $email = $4;
                        my $ucfirst_sign_off = ucfirst(lc($sign_off));
 
+                       if ($sign_off !~ /$signature_tags/) {
+                               WARN("BAD_SIGN_OFF",
+                                    "Non-standard signature: $sign_off\n" . $herecurr);
+                       }
                        if (defined $space_before && $space_before ne "") {
                                WARN("BAD_SIGN_OFF",
                                     "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr);
@@ -1848,8 +1852,8 @@ sub process {
 
                        my $pos = pos_last_openparen($rest);
                        if ($pos >= 0) {
-                               $line =~ /^\+([ \t]*)/;
-                               my $newindent = $1;
+                               $line =~ /^(\+| )([ \t]*)/;
+                               my $newindent = $2;
 
                                my $goodtabindent = $oldindent .
                                        "\t" x ($pos / 8) .
@@ -2984,6 +2988,45 @@ sub process {
                        }
                }
 
+# do {} while (0) macro tests:
+# single-statement macros do not need to be enclosed in do while (0) loop,
+# macro should not end with a semicolon
+               if ($^V && $^V ge 5.10.0 &&
+                   $realfile !~ m@/vmlinux.lds.h$@ &&
+                   $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) {
+                       my $ln = $linenr;
+                       my $cnt = $realcnt;
+                       my ($off, $dstat, $dcond, $rest);
+                       my $ctx = '';
+                       ($dstat, $dcond, $ln, $cnt, $off) =
+                               ctx_statement_block($linenr, $realcnt, 0);
+                       $ctx = $dstat;
+
+                       $dstat =~ s/\\\n.//g;
+
+                       if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) {
+                               my $stmts = $2;
+                               my $semis = $3;
+
+                               $ctx =~ s/\n*$//;
+                               my $cnt = statement_rawlines($ctx);
+                               my $herectx = $here . "\n";
+
+                               for (my $n = 0; $n < $cnt; $n++) {
+                                       $herectx .= raw_line($linenr, $n) . "\n";
+                               }
+
+                               if (($stmts =~ tr/;/;/) == 1) {
+                                       WARN("SINGLE_STATEMENT_DO_WHILE_MACRO",
+                                            "Single statement macros should not use a do {} while (0) loop\n" . "$herectx");
+                               }
+                               if (defined $semis && $semis ne "") {
+                                       WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON",
+                                            "do {} while (0) macros should not be semicolon terminated\n" . "$herectx");
+                               }
+                       }
+               }
+
 # make sure symbols are always wrapped with VMLINUX_SYMBOL() ...
 # all assignments may have only one of the following with an assignment:
 #      .
@@ -3261,6 +3304,12 @@ sub process {
                             "sizeof(& should be avoided\n" . $herecurr);
                }
 
+# check for sizeof without parenthesis
+               if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) {
+                       WARN("SIZEOF_PARENTHESIS",
+                            "sizeof $1 should be sizeof($1)\n" . $herecurr);
+               }
+
 # check for line continuations in quoted strings with odd counts of "
                if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) {
                        WARN("LINE_CONTINUATIONS",
@@ -3309,6 +3358,22 @@ sub process {
                        }
                }
 
+# check usleep_range arguments
+               if ($^V && $^V ge 5.10.0 &&
+                   defined $stat &&
+                   $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) {
+                       my $min = $1;
+                       my $max = $7;
+                       if ($min eq $max) {
+                               WARN("USLEEP_RANGE",
+                                    "usleep_range should not use min == max args; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n");
+                       } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ &&
+                                $min > $max) {
+                               WARN("USLEEP_RANGE",
+                                    "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n");
+                       }
+               }
+
 # check for new externs in .c files.
                if ($realfile =~ /\.c$/ && defined $stat &&
                    $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s)
diff --git a/scripts/coccinelle/iterators/use_after_iter.cocci b/scripts/coccinelle/iterators/use_after_iter.cocci
new file mode 100644 (file)
index 0000000..06284c5
--- /dev/null
@@ -0,0 +1,147 @@
+/// If list_for_each_entry, etc complete a traversal of the list, the iterator
+/// variable ends up pointing to an address at an offset from the list head,
+/// and not a meaningful structure.  Thus this value should not be used after
+/// the end of the iterator.
+//#False positives arise when there is a goto in the iterator and the
+//#reported reference is at the label of this goto.  Some flag tests
+//#may also cause a report to be a false positive.
+///
+// Confidence: Moderate
+// Copyright: (C) 2012 Julia Lawall, INRIA/LIP6.  GPLv2.
+// Copyright: (C) 2012 Gilles Muller, INRIA/LIP6.  GPLv2.
+// URL: http://coccinelle.lip6.fr/
+// Comments:
+// Options: -no_includes -include_headers
+
+virtual context
+virtual org
+virtual report
+
+@r exists@
+identifier c,member;
+expression E,x;
+iterator name list_for_each_entry;
+iterator name list_for_each_entry_reverse;
+iterator name list_for_each_entry_continue;
+iterator name list_for_each_entry_continue_reverse;
+iterator name list_for_each_entry_from;
+iterator name list_for_each_entry_safe;
+iterator name list_for_each_entry_safe_continue;
+iterator name list_for_each_entry_safe_from;
+iterator name list_for_each_entry_safe_reverse;
+iterator name hlist_for_each_entry;
+iterator name hlist_for_each_entry_continue;
+iterator name hlist_for_each_entry_from;
+iterator name hlist_for_each_entry_safe;
+statement S;
+position p1,p2;
+@@
+
+(
+list_for_each_entry@p1(c,...,member) { ... when != break;
+                                 when forall
+                                 when strict
+}
+|
+list_for_each_entry_reverse@p1(c,...,member) { ... when != break;
+                                 when forall
+                                 when strict
+}
+|
+list_for_each_entry_continue@p1(c,...,member) { ... when != break;
+                                 when forall
+                                 when strict
+}
+|
+list_for_each_entry_continue_reverse@p1(c,...,member) { ... when != break;
+                                 when forall
+                                 when strict
+}
+|
+list_for_each_entry_from@p1(c,...,member) { ... when != break;
+                                 when forall
+                                 when strict
+}
+|
+list_for_each_entry_safe@p1(c,...,member) { ... when != break;
+                                 when forall
+                                 when strict
+}
+|
+list_for_each_entry_safe_continue@p1(c,...,member) { ... when != break;
+                                 when forall
+                                 when strict
+}
+|
+list_for_each_entry_safe_from@p1(c,...,member) { ... when != break;
+                                 when forall
+                                 when strict
+}
+|
+list_for_each_entry_safe_reverse@p1(c,...,member) { ... when != break;
+                                 when forall
+                                 when strict
+}
+)
+...
+(
+list_for_each_entry(c,...) S
+|
+list_for_each_entry_reverse(c,...) S
+|
+list_for_each_entry_continue(c,...) S
+|
+list_for_each_entry_continue_reverse(c,...) S
+|
+list_for_each_entry_from(c,...) S
+|
+list_for_each_entry_safe(c,...) S
+|
+list_for_each_entry_safe(x,c,...) S
+|
+list_for_each_entry_safe_continue(c,...) S
+|
+list_for_each_entry_safe_continue(x,c,...) S
+|
+list_for_each_entry_safe_from(c,...) S
+|
+list_for_each_entry_safe_from(x,c,...) S
+|
+list_for_each_entry_safe_reverse(c,...) S
+|
+list_for_each_entry_safe_reverse(x,c,...) S
+|
+hlist_for_each_entry(c,...) S
+|
+hlist_for_each_entry_continue(c,...) S
+|
+hlist_for_each_entry_from(c,...) S
+|
+hlist_for_each_entry_safe(c,...) S
+|
+list_remove_head(x,c,...)
+|
+sizeof(<+...c...+>)
+|
+&c->member
+|
+c = E
+|
+*c@p2
+)
+
+@script:python depends on org@
+p1 << r.p1;
+p2 << r.p2;
+@@
+
+cocci.print_main("invalid iterator index reference",p2)
+cocci.print_secs("iterator",p1)
+
+@script:python depends on report@
+p1 << r.p1;
+p2 << r.p2;
+@@
+
+msg = "ERROR: invalid reference to the index variable of the iterator on line %s" % (p1[0].line)
+coccilib.report.print_report(p2[0], msg)
diff --git a/scripts/coccinelle/misc/irqf_oneshot.cocci b/scripts/coccinelle/misc/irqf_oneshot.cocci
new file mode 100644 (file)
index 0000000..6cfde94
--- /dev/null
@@ -0,0 +1,65 @@
+/// Make sure threaded IRQs without a primary handler are always request with
+/// IRQF_ONESHOT
+///
+//
+// Confidence: Good
+// Comments:
+// Options: --no-includes
+
+virtual patch
+virtual context
+virtual org
+virtual report
+
+@r1@
+expression irq;
+expression thread_fn;
+expression flags;
+position p;
+@@
+request_threaded_irq@p(irq, NULL, thread_fn,
+(
+flags | IRQF_ONESHOT
+|
+IRQF_ONESHOT
+)
+, ...)
+
+@depends on patch@
+expression irq;
+expression thread_fn;
+expression flags;
+position p != r1.p;
+@@
+request_threaded_irq@p(irq, NULL, thread_fn,
+(
+-0
++IRQF_ONESHOT
+|
+-flags
++flags | IRQF_ONESHOT
+)
+, ...)
+
+@depends on context@
+position p != r1.p;
+@@
+*request_threaded_irq@p(...)
+
+@match depends on report || org@
+expression irq;
+position p != r1.p;
+@@
+request_threaded_irq@p(irq, NULL, ...)
+
+@script:python depends on org@
+p << match.p;
+@@
+msg = "ERROR: Threaded IRQ with no primary handler requested without IRQF_ONESHOT"
+coccilib.org.print_todo(p[0],msg)
+
+@script:python depends on report@
+p << match.p;
+@@
+msg = "ERROR: Threaded IRQ with no primary handler requested without IRQF_ONESHOT"
+coccilib.report.print_report(p[0],msg)
index ed6653ef9702aa5320756537bb942e8a35123cb4..ee355394f4ef76eeb6d6f74dac00729d8cc942e3 100755 (executable)
@@ -1,6 +1,9 @@
 #!/bin/bash
 # Manipulate options in a .config file from the command line
 
+# If no prefix forced, use the default CONFIG_
+CONFIG_="${CONFIG_-CONFIG_}"
+
 usage() {
        cat >&2 <<EOL
 Manipulate options in a .config file from the command line.
@@ -14,6 +17,7 @@ commands:
                             Set option to "string"
        --set-val option value
                             Set option to value
+       --undefine|-u option Undefine option
        --state|-s option    Print state of option (n,y,m,undef)
 
        --enable-after|-E beforeopt option
@@ -26,10 +30,17 @@ commands:
        commands can be repeated multiple times
 
 options:
-       --file .config file to change (default .config)
+       --file config-file   .config file to change (default .config)
+       --keep-case|-k       Keep next symbols' case (dont' upper-case it)
 
 config doesn't check the validity of the .config file. This is done at next
- make time.
+make time.
+
+By default, config will upper-case the given symbol. Use --keep-case to keep
+the case of all following symbols unchanged.
+
+config uses 'CONFIG_' as the default symbol prefix. Set the environment
+variable CONFIG_ to the prefix to use. Eg.: CONFIG_="FOO_" config ...
 EOL
        exit 1
 }
@@ -40,11 +51,13 @@ checkarg() {
                usage
        fi
        case "$ARG" in
-       CONFIG_*)
-               ARG="${ARG/CONFIG_/}"
+       ${CONFIG_}*)
+               ARG="${ARG/${CONFIG_}/}"
                ;;
        esac
-       ARG="`echo $ARG | tr a-z A-Z`"
+       if [ "$MUNGE_CASE" = "yes" ] ; then
+               ARG="`echo $ARG | tr a-z A-Z`"
+       fi
 }
 
 set_var() {
@@ -61,6 +74,12 @@ set_var() {
        fi
 }
 
+undef_var() {
+       local name=$1
+
+       sed -ri "/^($name=|# $name is not set)/d" "$FN"
+}
+
 if [ "$1" = "--file" ]; then
        FN="$2"
        if [ "$FN" = "" ] ; then
@@ -75,10 +94,16 @@ if [ "$1" = "" ] ; then
        usage
 fi
 
+MUNGE_CASE=yes
 while [ "$1" != "" ] ; do
        CMD="$1"
        shift
        case "$CMD" in
+       --keep-case|-k)
+               MUNGE_CASE=no
+               shift
+               continue
+               ;;
        --refresh)
                ;;
        --*-after)
@@ -95,55 +120,58 @@ while [ "$1" != "" ] ; do
        esac
        case "$CMD" in
        --enable|-e)
-               set_var "CONFIG_$ARG" "CONFIG_$ARG=y"
+               set_var "${CONFIG_}$ARG" "${CONFIG_}$ARG=y"
                ;;
 
        --disable|-d)
-               set_var "CONFIG_$ARG" "# CONFIG_$ARG is not set"
+               set_var "${CONFIG_}$ARG" "# ${CONFIG_}$ARG is not set"
                ;;
 
        --module|-m)
-               set_var "CONFIG_$ARG" "CONFIG_$ARG=m"
+               set_var "${CONFIG_}$ARG" "${CONFIG_}$ARG=m"
                ;;
 
        --set-str)
                # sed swallows one level of escaping, so we need double-escaping
-               set_var "CONFIG_$ARG" "CONFIG_$ARG=\"${1//\"/\\\\\"}\""
+               set_var "${CONFIG_}$ARG" "${CONFIG_}$ARG=\"${1//\"/\\\\\"}\""
                shift
                ;;
 
        --set-val)
-               set_var "CONFIG_$ARG" "CONFIG_$ARG=$1"
+               set_var "${CONFIG_}$ARG" "${CONFIG_}$ARG=$1"
                shift
                ;;
+       --undefine|-u)
+               undef_var "${CONFIG_}$ARG"
+               ;;
 
        --state|-s)
-               if grep -q "# CONFIG_$ARG is not set" $FN ; then
+               if grep -q "# ${CONFIG_}$ARG is not set" $FN ; then
                        echo n
                else
-                       V="$(grep "^CONFIG_$ARG=" $FN)"
+                       V="$(grep "^${CONFIG_}$ARG=" $FN)"
                        if [ $? != 0 ] ; then
                                echo undef
                        else
-                               V="${V/#CONFIG_$ARG=/}"
+                               V="${V/#${CONFIG_}$ARG=/}"
                                V="${V/#\"/}"
                                V="${V/%\"/}"
-                               V="${V/\\\"/\"}"
+                               V="${V//\\\"/\"}"
                                echo "${V}"
                        fi
                fi
                ;;
 
        --enable-after|-E)
-               set_var "CONFIG_$B" "CONFIG_$B=y" "CONFIG_$A"
+               set_var "${CONFIG_}$B" "${CONFIG_}$B=y" "${CONFIG_}$A"
                ;;
 
        --disable-after|-D)
-               set_var "CONFIG_$B" "# CONFIG_$B is not set" "CONFIG_$A"
+               set_var "${CONFIG_}$B" "# ${CONFIG_}$B is not set" "${CONFIG_}$A"
                ;;
 
        --module-after|-M)
-               set_var "CONFIG_$B" "CONFIG_$B=m" "CONFIG_$A"
+               set_var "${CONFIG_}$B" "${CONFIG_}$B=m" "${CONFIG_}$A"
                ;;
 
        # undocumented because it ignores --file (fixme)
index ee120d441565422f0a6283b55ab5258fb61de9e7..be603c4fef624669ea2bac84e1f41311713a8065 100644 (file)
@@ -7,7 +7,6 @@ config*
 *.tab.h
 zconf.hash.c
 *.moc
-lkc_defs.h
 gconf.glade.h
 *.pot
 *.mo
index 79662658fb9158af1d7b3ee43dba0cfab0aa42cf..77d53999ffb90f51bea3f0395be08e495e3b75b8 100644 (file)
@@ -114,7 +114,7 @@ help:
        @echo  '  alldefconfig    - New config with all symbols set to default'
        @echo  '  randconfig      - New config with random answer to all options'
        @echo  '  listnewconfig   - List new options'
-       @echo  '  oldnoconfig     - Same as silentoldconfig but set new symbols to n (unset)'
+       @echo  '  oldnoconfig     - Same as silentoldconfig but sets new symbols to their default value'
 
 # lxdialog stuff
 check-lxdialog  := $(srctree)/$(src)/lxdialog/check-lxdialog.sh
@@ -234,12 +234,12 @@ $(obj)/.tmp_qtcheck:
                if [ -f $$d/include/qconfig.h ]; then dir=$$d; break; fi; \
              done; \
              if [ -z "$$dir" ]; then \
-               echo "*"; \
-               echo "* Unable to find any QT installation. Please make sure that"; \
-               echo "* the QT4 or QT3 development package is correctly installed and"; \
-               echo "* either qmake can be found or install pkg-config or set"; \
-               echo "* the QTDIR environment variable to the correct location."; \
-               echo "*"; \
+               echo >&2 "*"; \
+               echo >&2 "* Unable to find any QT installation. Please make sure that"; \
+               echo >&2 "* the QT4 or QT3 development package is correctly installed and"; \
+               echo >&2 "* either qmake can be found or install pkg-config or set"; \
+               echo >&2 "* the QTDIR environment variable to the correct location."; \
+               echo >&2 "*"; \
                false; \
              fi; \
              libpath=$$dir/lib; lib=qt; osdir=""; \
@@ -260,8 +260,8 @@ $(obj)/.tmp_qtcheck:
        else \
          cflags="\$$(shell pkg-config QtCore QtGui Qt3Support --cflags)"; \
          libs="\$$(shell pkg-config QtCore QtGui Qt3Support --libs)"; \
-         binpath="\$$(shell pkg-config QtCore --variable=prefix)"; \
-         moc="$$binpath/bin/moc"; \
+         moc="\$$(shell pkg-config QtCore --variable=moc_location)"; \
+         [ -n "$$moc" ] || moc="\$$(shell pkg-config QtCore --variable=prefix)/bin/moc"; \
        fi; \
        echo "KC_QT_CFLAGS=$$cflags" > $@; \
        echo "KC_QT_LIBS=$$libs" >> $@; \
@@ -279,17 +279,17 @@ $(obj)/.tmp_gtkcheck:
                if `pkg-config --atleast-version=2.0.0 gtk+-2.0`; then                  \
                        touch $@;                                                               \
                else                                                                    \
-                       echo "*";                                                       \
-                       echo "* GTK+ is present but version >= 2.0.0 is required.";     \
-                       echo "*";                                                       \
+                       echo >&2 "*";                                                   \
+                       echo >&2 "* GTK+ is present but version >= 2.0.0 is required."; \
+                       echo >&2 "*";                                                   \
                        false;                                                          \
                fi                                                                      \
        else                                                                            \
-               echo "*";                                                               \
-               echo "* Unable to find the GTK+ installation. Please make sure that";   \
-               echo "* the GTK+ 2.0 development package is correctly installed...";    \
-               echo "* You need gtk+-2.0, glib-2.0 and libglade-2.0.";                 \
-               echo "*";                                                               \
+               echo >&2 "*";                                                           \
+               echo >&2 "* Unable to find the GTK+ installation. Please make sure that";       \
+               echo >&2 "* the GTK+ 2.0 development package is correctly installed...";        \
+               echo >&2 "* You need gtk+-2.0, glib-2.0 and libglade-2.0.";             \
+               echo >&2 "*";                                                           \
                false;                                                                  \
        fi
 endif
@@ -298,8 +298,11 @@ $(obj)/zconf.tab.o: $(obj)/zconf.lex.c $(obj)/zconf.hash.c
 
 $(obj)/qconf.o: $(obj)/qconf.moc
 
-$(obj)/%.moc: $(src)/%.h
-       $(KC_QT_MOC) -i $< -o $@
+quiet_cmd_moc = MOC     $@
+      cmd_moc = $(KC_QT_MOC) -i $< -o $@
+
+$(obj)/%.moc: $(src)/%.h $(obj)/.tmp_qtcheck
+       $(call cmd,moc)
 
 # Extract gconf menu items for I18N support
 $(obj)/gconf.glade.h: $(obj)/gconf.glade
index 52577f052bc12d06503aa9d6459eaa578fb0fd4b..13ddf1126c2a05d82eaa8171c75ee8949d4092cc 100644 (file)
@@ -182,10 +182,66 @@ static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p)
        return 0;
 }
 
+#define LINE_GROWTH 16
+static int add_byte(int c, char **lineptr, size_t slen, size_t *n)
+{
+       char *nline;
+       size_t new_size = slen + 1;
+       if (new_size > *n) {
+               new_size += LINE_GROWTH - 1;
+               new_size *= 2;
+               nline = realloc(*lineptr, new_size);
+               if (!nline)
+                       return -1;
+
+               *lineptr = nline;
+               *n = new_size;
+       }
+
+       (*lineptr)[slen] = c;
+
+       return 0;
+}
+
+static ssize_t compat_getline(char **lineptr, size_t *n, FILE *stream)
+{
+       char *line = *lineptr;
+       size_t slen = 0;
+
+       for (;;) {
+               int c = getc(stream);
+
+               switch (c) {
+               case '\n':
+                       if (add_byte(c, &line, slen, n) < 0)
+                               goto e_out;
+                       slen++;
+                       /* fall through */
+               case EOF:
+                       if (add_byte('\0', &line, slen, n) < 0)
+                               goto e_out;
+                       *lineptr = line;
+                       if (slen == 0)
+                               return -1;
+                       return slen;
+               default:
+                       if (add_byte(c, &line, slen, n) < 0)
+                               goto e_out;
+                       slen++;
+               }
+       }
+
+e_out:
+       line[slen-1] = '\0';
+       *lineptr = line;
+       return -1;
+}
+
 int conf_read_simple(const char *name, int def)
 {
        FILE *in = NULL;
-       char line[1024];
+       char   *line = NULL;
+       size_t  line_asize = 0;
        char *p, *p2;
        struct symbol *sym;
        int i, def_flags;
@@ -247,7 +303,7 @@ load:
                }
        }
 
-       while (fgets(line, sizeof(line), in)) {
+       while (compat_getline(&line, &line_asize, in) != -1) {
                conf_lineno++;
                sym = NULL;
                if (line[0] == '#') {
@@ -335,6 +391,7 @@ setsym:
                        cs->def[def].tri = EXPR_OR(cs->def[def].tri, sym->def[def].tri);
                }
        }
+       free(line);
        fclose(in);
 
        if (modules_sym)
index 82cc3a85e7f885e2c7ab52aa99a6f76242899aa5..e3b12c010417c8e2886c1f57b07379cd483f7878 100644 (file)
@@ -4,7 +4,7 @@
 # What library to link
 ldflags()
 {
-       for ext in so a dylib ; do
+       for ext in so a dll.a dylib ; do
                for lib in ncursesw ncurses curses ; do
                        $cc -print-file-name=lib${lib}.${ext} | grep -q /
                        if [ $? -eq 0 ]; then
@@ -19,12 +19,12 @@ ldflags()
 # Where is ncurses.h?
 ccflags()
 {
-       if [ -f /usr/include/ncurses/ncurses.h ]; then
+       if [ -f /usr/include/ncursesw/curses.h ]; then
+               echo '-I/usr/include/ncursesw -DCURSES_LOC="<ncursesw/curses.h>"'
+       elif [ -f /usr/include/ncurses/ncurses.h ]; then
                echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"'
        elif [ -f /usr/include/ncurses/curses.h ]; then
                echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"'
-       elif [ -f /usr/include/ncursesw/curses.h ]; then
-               echo '-I/usr/include/ncursesw -DCURSES_LOC="<ncursesw/curses.h>"'
        elif [ -f /usr/include/ncurses.h ]; then
                echo '-DCURSES_LOC="<ncurses.h>"'
        else
index 154c2dd245b77536de60fd344d46e49dfbe41520..4e5de60a0c0d35765d1cfb8f67a06c852c892e8a 100644 (file)
@@ -129,6 +129,7 @@ do_resize:
                case 'e':
                case 'X':
                case 'x':
+               case 'q':
                        delwin(box);
                        delwin(dialog);
                        return 0;
@@ -190,6 +191,7 @@ do_resize:
                        break;
                case 'B':       /* Previous page */
                case 'b':
+               case 'u':
                case KEY_PPAGE:
                        if (begin_reached)
                                break;
@@ -214,6 +216,7 @@ do_resize:
                        break;
                case KEY_NPAGE: /* Next page */
                case ' ':
+               case 'd':
                        if (end_reached)
                                break;
 
index f606738d421d99c29f7321236e3eebcd2ac6a760..f584a281bb4c94d240b135ba6943fe75a433b3ec 100644 (file)
@@ -105,10 +105,10 @@ static const char mconf_readme[] = N_(
 "Text Box    (Help Window)\n"
 "--------\n"
 "o  Use the cursor keys to scroll up/down/left/right.  The VI editor\n"
-"   keys h,j,k,l function here as do <SPACE BAR> and <B> for those\n"
-"   who are familiar with less and lynx.\n"
+"   keys h,j,k,l function here as do <u>, <d>, <SPACE BAR> and <B> for \n"
+"   those who are familiar with less and lynx.\n"
 "\n"
-"o  Press <E>, <X>, <Enter> or <Esc><Esc> to exit.\n"
+"o  Press <E>, <X>, <q>, <Enter> or <Esc><Esc> to exit.\n"
 "\n"
 "\n"
 "Alternate Configuration Files\n"
index 8c0eb65978c95aecedb73a7e71c6c4d788fe2255..1704a8562a5dc40ae09c23cd355958814b6976a8 100644 (file)
@@ -83,10 +83,10 @@ static const char nconf_readme[] = N_(
 "Text Box    (Help Window)\n"
 "--------\n"
 "o  Use the cursor keys to scroll up/down/left/right.  The VI editor\n"
-"   keys h,j,k,l function here as do <SPACE BAR> for those\n"
-"   who are familiar with less and lynx.\n"
+"   keys h,j,k,l function here as do <u>, <d> and <SPACE BAR> for\n"
+"   those who are familiar with less and lynx.\n"
 "\n"
-"o  Press <Enter>, <F1>, <F5>, <F7> or <Esc> to exit.\n"
+"o  Press <Enter>, <F1>, <F5>, <F9>, <q> or <Esc> to exit.\n"
 "\n"
 "\n"
 "Alternate Configuration Files\n"
@@ -1503,7 +1503,11 @@ int main(int ac, char **av)
        }
 
        notimeout(stdscr, FALSE);
+#if NCURSES_REENTRANT
+       set_escdelay(1);
+#else
        ESCDELAY = 1;
+#endif
 
        /* set btns menu */
        curses_menu = new_menu(curses_menu_items);
index 3b18dd839668b295cd3d06b1c4cac3d4c941ddf3..379003c7a2b4d8cef60e25548fcb9567ca3656c5 100644 (file)
@@ -604,9 +604,11 @@ void show_scroll_win(WINDOW *main_window,
                switch (res) {
                case KEY_NPAGE:
                case ' ':
+               case 'd':
                        start_y += text_lines-2;
                        break;
                case KEY_PPAGE:
+               case 'u':
                        start_y -= text_lines+2;
                        break;
                case KEY_HOME:
@@ -632,10 +634,10 @@ void show_scroll_win(WINDOW *main_window,
                        start_x++;
                        break;
                }
-               if (res == 10 || res == 27 || res == 'q'
-                   || res == KEY_F(F_BACK) || res == KEY_F(F_EXIT)) {
+               if (res == 10 || res == 27 || res == 'q' ||
+                       res == KEY_F(F_HELP) || res == KEY_F(F_BACK) ||
+                       res == KEY_F(F_EXIT))
                        break;
-               }
                if (start_y < 0)
                        start_y = 0;
                if (start_y >= total_lines-text_lines)
index bccf07ddd0b67a0e2f761fd5721f274a49bf9bbb..2fbbbc1ddea02ad573315ef264fc5c79064a1f5e 100644 (file)
 use strict;
 use Getopt::Long;
 
+# set the environment variable LOCALMODCONFIG_DEBUG to get
+# debug output.
+my $debugprint = 0;
+$debugprint = 1 if (defined($ENV{LOCALMODCONFIG_DEBUG}));
+
+sub dprint {
+    return if (!$debugprint);
+    print STDERR @_;
+}
+
 my $config = ".config";
 
 my $uname = `uname -r`;
@@ -113,6 +123,10 @@ sub find_config {
 
 find_config;
 
+# Read in the entire config file into config_file
+my @config_file = <CIN>;
+close CIN;
+
 # Parse options
 my $localmodconfig = 0;
 my $localyesconfig = 0;
@@ -186,6 +200,7 @@ sub read_kconfig {
            $state = "NEW";
            $config = $2;
 
+           # Add depends for 'if' nesting
            for (my $i = 0; $i < $iflevel; $i++) {
                if ($i) {
                    $depends{$config} .= " " . $ifdeps[$i];
@@ -204,10 +219,11 @@ sub read_kconfig {
 
        # Get the configs that select this config
        } elsif ($state ne "NONE" && /^\s*select\s+(\S+)/) {
-           if (defined($selects{$1})) {
-               $selects{$1} .= " " . $config;
+           my $conf = $1;
+           if (defined($selects{$conf})) {
+               $selects{$conf} .= " " . $config;
            } else {
-               $selects{$1} = $config;
+               $selects{$conf} = $config;
            }
 
        # configs without prompts must be selected
@@ -250,6 +266,7 @@ if ($kconfig) {
     read_kconfig($kconfig);
 }
 
+# Makefiles can use variables to define their dependencies
 sub convert_vars {
     my ($line, %vars) = @_;
 
@@ -293,6 +310,7 @@ foreach my $makefile (@makefiles) {
 
        my $objs;
 
+       # Convert variables in a line (could define configs)
        $_ = convert_vars($_, %make_vars);
 
        # collect objects after obj-$(CONFIG_FOO_BAR)
@@ -373,13 +391,15 @@ while (<LIN>) {
 close (LIN);
 
 # add to the configs hash all configs that are needed to enable
-# a loaded module.
+# a loaded module. This is a direct obj-${CONFIG_FOO} += bar.o
+# where we know we need bar.o so we add FOO to the list.
 my %configs;
 foreach my $module (keys(%modules)) {
     if (defined($objects{$module})) {
        my @arr = @{$objects{$module}};
        foreach my $conf (@arr) {
            $configs{$conf} = $module;
+           dprint "$conf added by direct ($module)\n";
        }
     } else {
        # Most likely, someone has a custom (binary?) module loaded.
@@ -387,9 +407,24 @@ foreach my $module (keys(%modules)) {
     }
 }
 
+# Read the current config, and see what is enabled. We want to
+# ignore configs that we would not enable anyway.
+
+my %orig_configs;
 my $valid = "A-Za-z_0-9";
+
+foreach my $line (@config_file) {
+    $_ = $line;
+
+    if (/(CONFIG_[$valid]*)=(m|y)/) {
+       $orig_configs{$1} = $2;
+    }
+}
+
 my $repeat = 1;
 
+my $depconfig;
+
 #
 # Note, we do not care about operands (like: &&, ||, !) we want to add any
 # config that is in the depend list of another config. This script does
@@ -398,7 +433,7 @@ my $repeat = 1;
 # to keep on. If A was on in the original config, B would not have been
 # and B would not be turned on by this script.
 #
-sub parse_config_dep_select
+sub parse_config_depends
 {
     my ($p) = @_;
 
@@ -409,10 +444,16 @@ sub parse_config_dep_select
 
            $p =~ s/^[^$valid]*[$valid]+//;
 
+           # We only need to process if the depend config is a module
+           if (!defined($orig_configs{$conf}) || !$orig_configs{conf} eq "m") {
+               next;
+           }
+
            if (!defined($configs{$conf})) {
                # We must make sure that this config has its
                # dependencies met.
                $repeat = 1; # do again
+               dprint "$conf selected by depend $depconfig\n";
                $configs{$conf} = 1;
            }
        } else {
@@ -421,31 +462,132 @@ sub parse_config_dep_select
     }
 }
 
-while ($repeat) {
-    $repeat = 0;
+# Select is treated a bit differently than depends. We call this
+# when a config has no prompt and requires another config to be
+# selected. We use to just select all configs that selected this
+# config, but found that that can balloon into enabling hundreds
+# of configs that we do not care about.
+#
+# The idea is we look at all the configs that select it. If one
+# is already in our list of configs to enable, then there's nothing
+# else to do. If there isn't, we pick the first config that was
+# enabled in the orignal config and use that.
+sub parse_config_selects
+{
+    my ($config, $p) = @_;
 
-    foreach my $config (keys %configs) {
-       $config =~ s/^CONFIG_//;
+    my $next_config;
+
+    while ($p =~ /[$valid]/) {
 
-       if (defined($depends{$config})) {
-           # This config has dependencies. Make sure they are also included
-           parse_config_dep_select $depends{$config};
+       if ($p =~ /^[^$valid]*([$valid]+)/) {
+           my $conf = "CONFIG_" . $1;
+
+           $p =~ s/^[^$valid]*[$valid]+//;
+
+           # Make sure that this config exists in the current .config file
+           if (!defined($orig_configs{$conf})) {
+               dprint "$conf not set for $config select\n";
+               next;
+           }
+
+           # Check if something other than a module selects this config
+           if (defined($orig_configs{$conf}) && $orig_configs{$conf} ne "m") {
+               dprint "$conf (non module) selects config, we are good\n";
+               # we are good with this
+               return;
+           }
+           if (defined($configs{$conf})) {
+               dprint "$conf selects $config so we are good\n";
+               # A set config selects this config, we are good
+               return;
+           }
+           # Set this config to be selected
+           if (!defined($next_config)) {
+               $next_config = $conf;
+           }
+       } else {
+           die "this should never happen";
        }
+    }
 
-       if (defined($prompts{$config}) || !defined($selects{$config})) {
-           next;
+    # If no possible config selected this, then something happened.
+    if (!defined($next_config)) {
+       print STDERR "WARNING: $config is required, but nothing in the\n";
+       print STDERR "  current config selects it.\n";
+       return;
+    }
+
+    # If we are here, then we found no config that is set and
+    # selects this config. Repeat.
+    $repeat = 1;
+    # Make this config need to be selected
+    $configs{$next_config} = 1;
+    dprint "$next_config selected by select $config\n";
+}
+
+my %process_selects;
+
+# loop through all configs, select their dependencies.
+sub loop_depend {
+    $repeat = 1;
+
+    while ($repeat) {
+       $repeat = 0;
+
+      forloop:
+       foreach my $config (keys %configs) {
+
+           # If this config is not a module, we do not need to process it
+           if (defined($orig_configs{$config}) && $orig_configs{$config} ne "m") {
+               next forloop;
+           }
+
+           $config =~ s/^CONFIG_//;
+           $depconfig = $config;
+
+           if (defined($depends{$config})) {
+               # This config has dependencies. Make sure they are also included
+               parse_config_depends $depends{$config};
+           }
+
+           # If the config has no prompt, then we need to check if a config
+           # that is enabled selected it. Or if we need to enable one.
+           if (!defined($prompts{$config}) && defined($selects{$config})) {
+               $process_selects{$config} = 1;
+           }
        }
+    }
+}
+
+sub loop_select {
+
+    foreach my $config (keys %process_selects) {
+       $config =~ s/^CONFIG_//;
+
+       dprint "Process select $config\n";
 
        # config has no prompt and must be selected.
-       parse_config_dep_select $selects{$config};
+       parse_config_selects $config, $selects{$config};
     }
 }
 
+while ($repeat) {
+    # Get the first set of configs and their dependencies.
+    loop_depend;
+
+    $repeat = 0;
+
+    # Now we need to see if we have to check selects;
+    loop_select;
+}          
+
 my %setconfigs;
 
 # Finally, read the .config file and turn off any module enabled that
 # we could not find a reason to keep enabled.
-while(<CIN>) {
+foreach my $line (@config_file) {
+    $_ = $line;
 
     if (/CONFIG_IKCONFIG/) {
        if (/# CONFIG_IKCONFIG is not set/) {
@@ -473,7 +615,6 @@ while(<CIN>) {
     }
     print;
 }
-close(CIN);
 
 # Integrity check, make sure all modules that we want enabled do
 # indeed have their configs set.
index cd9c6c6bb4c9d9dba4f9c80fc5edbb67170beef8..4629038c9e5acb677499ace431355a818ee4b3d6 100644 (file)
@@ -210,8 +210,8 @@ if [ -n "${CONFIG_KALLSYMS}" ]; then
        mksysmap ${kallsyms_vmlinux} .tmp_System.map
 
        if ! cmp -s System.map .tmp_System.map; then
-               echo Inconsistent kallsyms data
-               echo echo Try "make KALLSYMS_EXTRA_PASS=1" as a workaround
+               echo >&2 Inconsistent kallsyms data
+               echo >&2 echo Try "make KALLSYMS_EXTRA_PASS=1" as a workaround
                cleanup
                exit 1
        fi
index c95fdda584147de330a53ef49b165304f6c8625c..acb86507828aaba1304594588959fbfc2804c528 100644 (file)
@@ -92,7 +92,7 @@ rm -rf "$tmpdir" "$fwdir" "$kernel_headers_dir" "$libc_headers_dir"
 mkdir -m 755 -p "$tmpdir/DEBIAN"
 mkdir -p  "$tmpdir/lib" "$tmpdir/boot" "$tmpdir/usr/share/doc/$packagename"
 mkdir -m 755 -p "$fwdir/DEBIAN"
-mkdir -p "$fwdir/lib" "$fwdir/usr/share/doc/$fwpackagename"
+mkdir -p "$fwdir/lib/firmware/$version/" "$fwdir/usr/share/doc/$fwpackagename"
 mkdir -m 755 -p "$libc_headers_dir/DEBIAN"
 mkdir -p "$libc_headers_dir/usr/share/doc/$libc_headers_packagename"
 mkdir -m 755 -p "$kernel_headers_dir/DEBIAN"
@@ -243,7 +243,7 @@ EOF
 fi
 
 # Build header package
-(cd $srctree; find . -name Makefile -o -name Kconfig\* -o -name \*.pl > "$objtree/debian/hdrsrcfiles")
+(cd $srctree; find . -name Makefile\* -o -name Kconfig\* -o -name \*.pl > "$objtree/debian/hdrsrcfiles")
 (cd $srctree; find arch/$SRCARCH/include include scripts -type f >> "$objtree/debian/hdrsrcfiles")
 (cd $objtree; find arch/$SRCARCH/include .config Module.symvers include scripts -type f >> "$objtree/debian/hdrobjfiles")
 destdir=$kernel_headers_dir/usr/src/linux-headers-$version
@@ -267,7 +267,8 @@ EOF
 
 # Do we have firmware? Move it out of the way and build it into a package.
 if [ -e "$tmpdir/lib/firmware" ]; then
-       mv "$tmpdir/lib/firmware" "$fwdir/lib/"
+       mv "$tmpdir/lib/firmware"/* "$fwdir/lib/firmware/$version/"
+       rmdir "$tmpdir/lib/firmware"
 
        cat <<EOF >> debian/control
 
index 1ca9ceb95eb6413161f0844ca491ba4e1260f711..6acf834491050237548163230a2b89e7e53b9b61 100644 (file)
@@ -247,6 +247,7 @@ do_file(char const *const fname)
        case EM_X86_64:
                custom_sort = sort_x86_table;
                break;
+       case EM_S390:
        case EM_MIPS:
                break;
        }  /* end switch */
index cf7b12fee573174c8aaf00ee75aa9d21c056d61d..cff8faad73d15dab529f71943e5c5544b2ad9217 100755 (executable)
@@ -153,7 +153,8 @@ exuberant()
        --regex-c++='/CLEARPAGEFLAG_NOOP\(([^,)]*).*/ClearPage\1/'      \
        --regex-c++='/__CLEARPAGEFLAG_NOOP\(([^,)]*).*/__ClearPage\1/'  \
        --regex-c++='/TESTCLEARFLAG_FALSE\(([^,)]*).*/TestClearPage\1/' \
-       --regex-c++='/__TESTCLEARFLAG_FALSE\(([^,)]*).*/__TestClearPage\1/'
+       --regex-c++='/__TESTCLEARFLAG_FALSE\(([^,)]*).*/__TestClearPage\1/' \
+       --regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/'
 
        all_kconfigs | xargs $1 -a                              \
        --langdef=kconfig --language-force=kconfig              \
@@ -195,7 +196,8 @@ emacs()
        --regex='/CLEARPAGEFLAG_NOOP\(([^,)]*).*/ClearPage\1/'  \
        --regex='/__CLEARPAGEFLAG_NOOP\(([^,)]*).*/__ClearPage\1/' \
        --regex='/TESTCLEARFLAG_FALSE\(([^,)]*).*/TestClearPage\1/' \
-       --regex='/__TESTCLEARFLAG_FALSE\(([^,)]*).*/__TestClearPage\1/'
+       --regex='/__TESTCLEARFLAG_FALSE\(([^,)]*).*/__TestClearPage\1/' \
+       --regex='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/'
 
        all_kconfigs | xargs $1 -a                              \
        --regex='/^[ \t]*\(\(menu\)*config\)[ \t]+\([a-zA-Z0-9_]+\)/\3/'
index 68d82daed257db766c57bed57d1768866207d253..4d3fab47e643855db9c0816e6d10efa71ebb749e 100644 (file)
@@ -274,7 +274,7 @@ static struct avc_node *avc_alloc_node(void)
 {
        struct avc_node *node;
 
-       node = kmem_cache_zalloc(avc_node_cachep, GFP_ATOMIC);
+       node = kmem_cache_zalloc(avc_node_cachep, GFP_ATOMIC|__GFP_NOMEMALLOC);
        if (!node)
                goto out;
 
index 94c45a1531a4c1199535b38a5eb0041075b29ce4..6c77f63c759198ead061712fb3bad1166c4ace2a 100644 (file)
@@ -2791,11 +2791,16 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
 
                        /* We strip a nul only if it is at the end, otherwise the
                         * context contains a nul and we should audit that */
-                       str = value;
-                       if (str[size - 1] == '\0')
-                               audit_size = size - 1;
-                       else
-                               audit_size = size;
+                       if (value) {
+                               str = value;
+                               if (str[size - 1] == '\0')
+                                       audit_size = size - 1;
+                               else
+                                       audit_size = size;
+                       } else {
+                               str = "";
+                               audit_size = 0;
+                       }
                        ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR);
                        audit_log_format(ab, "op=setxattr invalid_context=");
                        audit_log_n_untrustedstring(ab, value, audit_size);
@@ -3180,6 +3185,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
        case F_GETFL:
        case F_GETOWN:
        case F_GETSIG:
+       case F_GETOWNER_UIDS:
                /* Just check FD__USE permission */
                err = file_has_perm(cred, file, 0);
                break;
index d31e6d957c21a47733ca1d73e8a43ef7d3fae22c..b1b768e4049af3304d457451f06c8fa834909b89 100644 (file)
@@ -323,11 +323,11 @@ static int smk_parse_long_rule(const char *data, struct smack_rule *rule,
        int datalen;
        int rc = -1;
 
-       /*
-        * This is probably inefficient, but safe.
-        */
+       /* This is inefficient */
        datalen = strlen(data);
-       subject = kzalloc(datalen, GFP_KERNEL);
+
+       /* Our first element can be 64 + \0 with no spaces */
+       subject = kzalloc(datalen + 1, GFP_KERNEL);
        if (subject == NULL)
                return -1;
        object = kzalloc(datalen, GFP_KERNEL);
index 768167925409ecb9be45b3c2ac82c885685da9bc..30e027ecf4da7383c7e6045f0b9a9f9905ca63b4 100644 (file)
@@ -68,6 +68,7 @@ void __snd_printk(unsigned int level, const char *path, int line,
 {
        va_list args;
 #ifdef CONFIG_SND_VERBOSE_PRINTK
+       int kern_level;
        struct va_format vaf;
        char verbose_fmt[] = KERN_DEFAULT "ALSA %s:%d %pV";
 #endif
@@ -81,12 +82,16 @@ void __snd_printk(unsigned int level, const char *path, int line,
 #ifdef CONFIG_SND_VERBOSE_PRINTK
        vaf.fmt = format;
        vaf.va = &args;
-       if (format[0] == '<' && format[2] == '>') {
-               memcpy(verbose_fmt, format, 3);
-               vaf.fmt = format + 3;
+
+       kern_level = printk_get_level(format);
+       if (kern_level) {
+               const char *end_of_header = printk_skip_level(format);
+               memcpy(verbose_fmt, format, end_of_header - format);
+               vaf.fmt = end_of_header;
        } else if (level)
-               memcpy(verbose_fmt, KERN_DEBUG, 3);
+               memcpy(verbose_fmt, KERN_DEBUG, sizeof(KERN_DEBUG) - 1);
        printk(verbose_fmt, sanity_file_name(path), line, &vaf);
+
 #else
        vprintk(format, args);
 #endif
index 1cff331a228ef87831760b6635025c23f94937e5..4608c2ca43f84601643be8a6cdba6451d3953a22 100644 (file)
@@ -554,6 +554,7 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
        spin_lock_init(&mpu->output_lock);
        spin_lock_init(&mpu->timer_lock);
        mpu->hardware = hardware;
+       mpu->irq = -1;
        if (! (info_flags & MPU401_INFO_INTEGRATED)) {
                int res_size = hardware == MPU401_HW_PC98II ? 4 : 2;
                mpu->res = request_region(port, res_size, "MPU401 UART");
index 7eca25fae4137947ebf933451e2d2964d2bd9d40..d14edb7d6484a1963cd9ae0c4b61750a349bfbf0 100644 (file)
@@ -71,6 +71,9 @@ static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val)
        u16 l;
        u8 data;
 
+       if (tea->ops->write_val)
+               return tea->ops->write_val(tea, val);
+
        tea->ops->set_direction(tea, 1);
        udelay(16);
 
@@ -94,6 +97,9 @@ static u32 snd_tea575x_read(struct snd_tea575x *tea)
        u16 l, rdata;
        u32 data = 0;
 
+       if (tea->ops->read_val)
+               return tea->ops->read_val(tea);
+
        tea->ops->set_direction(tea, 0);
        tea->ops->set_pins(tea, 0);
        udelay(16);
@@ -197,6 +203,8 @@ static int vidioc_g_tuner(struct file *file, void *priv,
        strcpy(v->name, "FM");
        v->type = V4L2_TUNER_RADIO;
        v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+       if (!tea->cannot_read_data)
+               v->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED;
        v->rangelow = FREQ_LO;
        v->rangehigh = FREQ_HI;
        v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
@@ -305,7 +313,7 @@ static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
        }
        tea->val &= ~TEA575X_BIT_SEARCH;
        snd_tea575x_set_freq(tea);
-       return -EAGAIN;
+       return -ENODATA;
 }
 
 static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -377,7 +385,6 @@ int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner)
        strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name));
        tea->vd.lock = &tea->mutex;
        tea->vd.v4l2_dev = tea->v4l2_dev;
-       tea->vd.ctrl_handler = &tea->ctrl_handler;
        tea->fops = tea575x_fops;
        tea->fops.owner = owner;
        tea->vd.fops = &tea->fops;
@@ -386,29 +393,33 @@ int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner)
        if (tea->cannot_read_data)
                v4l2_disable_ioctl(&tea->vd, VIDIOC_S_HW_FREQ_SEEK);
 
-       v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
-       v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
-       retval = tea->ctrl_handler.error;
-       if (retval) {
-               v4l2_err(tea->v4l2_dev, "can't initialize controls\n");
-               v4l2_ctrl_handler_free(&tea->ctrl_handler);
-               return retval;
-       }
-
-       if (tea->ext_init) {
-               retval = tea->ext_init(tea);
+       if (!tea->cannot_mute) {
+               tea->vd.ctrl_handler = &tea->ctrl_handler;
+               v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+               v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops,
+                                 V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+               retval = tea->ctrl_handler.error;
                if (retval) {
+                       v4l2_err(tea->v4l2_dev, "can't initialize controls\n");
                        v4l2_ctrl_handler_free(&tea->ctrl_handler);
                        return retval;
                }
-       }
 
-       v4l2_ctrl_handler_setup(&tea->ctrl_handler);
+               if (tea->ext_init) {
+                       retval = tea->ext_init(tea);
+                       if (retval) {
+                               v4l2_ctrl_handler_free(&tea->ctrl_handler);
+                               return retval;
+                       }
+               }
+
+               v4l2_ctrl_handler_setup(&tea->ctrl_handler);
+       }
 
        retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->radio_nr);
        if (retval) {
                v4l2_err(tea->v4l2_dev, "can't register video device!\n");
-               v4l2_ctrl_handler_free(&tea->ctrl_handler);
+               v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
                return retval;
        }
 
@@ -418,7 +429,7 @@ int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner)
 void snd_tea575x_exit(struct snd_tea575x *tea)
 {
        video_unregister_device(&tea->vd);
-       v4l2_ctrl_handler_free(&tea->ctrl_handler);
+       v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
 }
 
 static int __init alsa_tea575x_module_init(void)
index 1d47be8170b59928f758f13bad7106c3959f79e8..b3b4f15e45baa850f0698828589b31673ccbeb6c 100644 (file)
@@ -612,10 +612,10 @@ static int snd_es1688_capture_close(struct snd_pcm_substream *substream)
 
 static int snd_es1688_free(struct snd_es1688 *chip)
 {
-       if (chip->res_port) {
+       if (chip->hardware != ES1688_HW_UNDEF)
                snd_es1688_init(chip, 0);
+       if (chip->res_port)
                release_and_free_resource(chip->res_port);
-       }
        if (chip->irq >= 0)
                free_irq(chip->irq, (void *) chip);
        if (chip->dma8 >= 0) {
@@ -657,19 +657,27 @@ int snd_es1688_create(struct snd_card *card,
                return -ENOMEM;
        chip->irq = -1;
        chip->dma8 = -1;
+       chip->hardware = ES1688_HW_UNDEF;
        
-       if ((chip->res_port = request_region(port + 4, 12, "ES1688")) == NULL) {
+       chip->res_port = request_region(port + 4, 12, "ES1688");
+       if (chip->res_port == NULL) {
                snd_printk(KERN_ERR "es1688: can't grab port 0x%lx\n", port + 4);
-               return -EBUSY;
+               err = -EBUSY;
+               goto exit;
        }
-       if (request_irq(irq, snd_es1688_interrupt, 0, "ES1688", (void *) chip)) {
+
+       err = request_irq(irq, snd_es1688_interrupt, 0, "ES1688", (void *) chip);
+       if (err < 0) {
                snd_printk(KERN_ERR "es1688: can't grab IRQ %d\n", irq);
-               return -EBUSY;
+               goto exit;
        }
+
        chip->irq = irq;
-       if (request_dma(dma8, "ES1688")) {
+       err = request_dma(dma8, "ES1688");
+
+       if (err < 0) {
                snd_printk(KERN_ERR "es1688: can't grab DMA8 %d\n", dma8);
-               return -EBUSY;
+               goto exit;
        }
        chip->dma8 = dma8;
 
@@ -685,14 +693,18 @@ int snd_es1688_create(struct snd_card *card,
 
        err = snd_es1688_probe(chip);
        if (err < 0)
-               return err;
+               goto exit;
 
        err = snd_es1688_init(chip, 1);
        if (err < 0)
-               return err;
+               goto exit;
 
        /* Register device */
-       return snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+exit:
+       if (err)
+               snd_es1688_free(chip);
+       return err;
 }
 
 static struct snd_pcm_ops snd_es1688_playback_ops = {
index 641408dc28c07f8356107290a1a4b21a4c92dbc3..69b928449789e22afcaef39a67dea3323bc5e5e7 100644 (file)
@@ -877,6 +877,8 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
        struct hdmi_eld *eld;
        struct hdmi_spec_per_cvt *per_cvt = NULL;
 
+       hinfo->nid = 0; /* clear the leftover value */
+
        /* Validate hinfo */
        pin_idx = hinfo_to_pin_index(spec, hinfo);
        if (snd_BUG_ON(pin_idx < 0))
@@ -1161,9 +1163,9 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
        return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
 }
 
-static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
-                                            struct hda_codec *codec,
-                                            struct snd_pcm_substream *substream)
+static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
+                         struct hda_codec *codec,
+                         struct snd_pcm_substream *substream)
 {
        struct hdmi_spec *spec = codec->spec;
        int cvt_idx, pin_idx;
@@ -1171,8 +1173,6 @@ static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
        struct hdmi_spec_per_pin *per_pin;
        int pinctl;
 
-       snd_hda_codec_cleanup_stream(codec, hinfo->nid);
-
        if (hinfo->nid) {
                cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid);
                if (snd_BUG_ON(cvt_idx < 0))
@@ -1195,14 +1195,13 @@ static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
                                    pinctl & ~PIN_OUT);
                snd_hda_spdif_ctls_unassign(codec, pin_idx);
        }
-
        return 0;
 }
 
 static const struct hda_pcm_ops generic_ops = {
        .open = hdmi_pcm_open,
+       .close = hdmi_pcm_close,
        .prepare = generic_hdmi_playback_pcm_prepare,
-       .cleanup = generic_hdmi_playback_pcm_cleanup,
 };
 
 static int generic_hdmi_build_pcms(struct hda_codec *codec)
@@ -1221,6 +1220,7 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
                pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
                pstr->substreams = 1;
                pstr->ops = generic_ops;
+               pstr->nid = 1; /* FIXME: just for avoiding a debug WARNING */
                /* other pstr fields are set in open */
        }
 
index f141395dfee67e1ed8ad51aed904c8d8c83abb36..344b221d2102ffd9d4568961d7a0d2b5f8490e2a 100644 (file)
@@ -203,6 +203,7 @@ struct alc_spec {
        unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
        unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
        unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
+       unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
 
        /* auto-mute control */
        int automute_mode;
@@ -4323,7 +4324,8 @@ static int alc_parse_auto_config(struct hda_codec *codec,
                return 0; /* can't find valid BIOS pin config */
        }
 
-       if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
+       if (!spec->no_primary_hp &&
+           cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
            cfg->line_outs <= cfg->hp_outs) {
                /* use HP as primary out */
                cfg->speaker_outs = cfg->line_outs;
@@ -5050,6 +5052,7 @@ enum {
        ALC889_FIXUP_MBP_VREF,
        ALC889_FIXUP_IMAC91_VREF,
        ALC882_FIXUP_INV_DMIC,
+       ALC882_FIXUP_NO_PRIMARY_HP,
 };
 
 static void alc889_fixup_coef(struct hda_codec *codec,
@@ -5171,6 +5174,17 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec,
        spec->keep_vref_in_automute = 1;
 }
 
+/* Don't take HP output as primary
+ * strangely, the speaker output doesn't work on VAIO Z through DAC 0x05
+ */
+static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
+                                      const struct alc_fixup *fix, int action)
+{
+       struct alc_spec *spec = codec->spec;
+       if (action == ALC_FIXUP_ACT_PRE_PROBE)
+               spec->no_primary_hp = 1;
+}
+
 static const struct alc_fixup alc882_fixups[] = {
        [ALC882_FIXUP_ABIT_AW9D_MAX] = {
                .type = ALC_FIXUP_PINS,
@@ -5357,6 +5371,10 @@ static const struct alc_fixup alc882_fixups[] = {
                .type = ALC_FIXUP_FUNC,
                .v.func = alc_fixup_inv_dmic_0x12,
        },
+       [ALC882_FIXUP_NO_PRIMARY_HP] = {
+               .type = ALC_FIXUP_FUNC,
+               .v.func = alc882_fixup_no_primary_hp,
+       },
 };
 
 static const struct snd_pci_quirk alc882_fixup_tbl[] = {
@@ -5391,6 +5409,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC),
        SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601),
        SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT),
+       SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP),
 
        /* All Apple entries are in codec SSIDs */
        SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF),
@@ -5432,6 +5451,7 @@ static const struct alc_model_fixup alc882_fixup_models[] = {
        {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"},
        {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"},
        {.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"},
+       {.id = ALC882_FIXUP_NO_PRIMARY_HP, .name = "no-primary-hp"},
        {}
 };
 
index a1596a3b171c67683d900ebe9ec7da27f3236a97..94040ccf8e8fbd954833a6302ed37ab60a34f1ed 100644 (file)
@@ -101,6 +101,8 @@ enum {
        STAC_92HD83XXX_HP_cNB11_INTQUAD,
        STAC_HP_DV7_4000,
        STAC_HP_ZEPHYR,
+       STAC_92HD83XXX_HP_LED,
+       STAC_92HD83XXX_HP_INV_LED,
        STAC_92HD83XXX_MODELS
 };
 
@@ -1675,6 +1677,8 @@ static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
        [STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad",
        [STAC_HP_DV7_4000] = "hp-dv7-4000",
        [STAC_HP_ZEPHYR] = "hp-zephyr",
+       [STAC_92HD83XXX_HP_LED] = "hp-led",
+       [STAC_92HD83XXX_HP_INV_LED] = "hp-inv-led",
 };
 
 static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
@@ -1729,6 +1733,8 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
                          "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561,
                          "HP", STAC_HP_ZEPHYR),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3660,
+                         "HP Mini", STAC_92HD83XXX_HP_LED),
        {} /* terminator */
 };
 
@@ -4414,7 +4420,12 @@ static int stac92xx_init(struct hda_codec *codec)
        snd_hda_jack_report_sync(codec);
 
        /* sync mute LED */
-       snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+       if (spec->gpio_led) {
+               if (spec->vmaster_mute.hook)
+                       snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+               else /* the very first init call doesn't have vmaster yet */
+                       stac92xx_update_led_status(codec, false);
+       }
 
        /* sync the power-map */
        if (spec->num_pwrs)
@@ -5507,6 +5518,7 @@ static void stac92hd8x_fill_auto_spec(struct hda_codec *codec)
 static int patch_stac92hd83xxx(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec;
+       int default_polarity = -1; /* no default cfg */
        int err;
 
        spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
@@ -5555,9 +5567,15 @@ again:
        case STAC_HP_ZEPHYR:
                spec->init = stac92hd83xxx_hp_zephyr_init;
                break;
+       case STAC_92HD83XXX_HP_LED:
+               default_polarity = 0;
+               break;
+       case STAC_92HD83XXX_HP_INV_LED:
+               default_polarity = 1;
+               break;
        }
 
-       if (find_mute_led_cfg(codec, -1/*no default cfg*/))
+       if (find_mute_led_cfg(codec, default_polarity))
                snd_printd("mute LED gpio %d polarity %d\n",
                                spec->gpio_led,
                                spec->gpio_led_polarity);
index 90645560ed3981c5648bc35b0907ecd829a1e62c..80d90cb42853bd50abfac7349361142e71185a7c 100644 (file)
@@ -3226,7 +3226,7 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
        int imux_is_smixer;
-       unsigned int parm;
+       unsigned int parm, parm2;
        /* MUX6 (1eh) = stereo mixer */
        imux_is_smixer =
        snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
@@ -3249,7 +3249,7 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
        parm = AC_PWRST_D3;
        set_pin_power_state(codec, 0x27, &parm);
        update_power_state(codec, 0x1a, parm);
-       update_power_state(codec, 0xb, parm);
+       parm2 = parm; /* for pin 0x0b */
 
        /* PW2 (26h), AOW2 (ah) */
        parm = AC_PWRST_D3;
@@ -3264,6 +3264,9 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
        if (!spec->hp_independent_mode) /* check for redirected HP */
                set_pin_power_state(codec, 0x28, &parm);
        update_power_state(codec, 0x8, parm);
+       if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3)
+               parm = parm2;
+       update_power_state(codec, 0xb, parm);
        /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
        update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
 
index 0ff1e70b7770da5edac83ddafc7a7ac39f2fbd7c..c084c549942ecef39ffa52788524125b2c8ef1b6 100644 (file)
@@ -653,7 +653,7 @@ int twl6040_get_hs_step_size(struct snd_soc_codec *codec)
 {
        struct twl6040 *twl6040 = codec->control_data;
 
-       if (twl6040_get_revid(twl6040) < TWL6040_REV_ES1_2)
+       if (twl6040_get_revid(twl6040) < TWL6040_REV_ES1_3)
                /* For ES under ES_1.3 HS step is 2 mV */
                return 2;
        else
index 7e96249536b4e092ac52115efa547bacc8f86cc7..37711a5d0d6b174b015e646effad1082adea481a 100644 (file)
@@ -23,14 +23,14 @@ static int do_mod_firmware_load(const char *fn, char **fp)
        if (l <= 0 || l > 131072)
        {
                printk(KERN_INFO "Invalid firmware '%s'\n", fn);
-               filp_close(filp, current->files);
+               filp_close(filp, NULL);
                return 0;
        }
        dp = vmalloc(l);
        if (dp == NULL)
        {
                printk(KERN_INFO "Out of memory loading '%s'.\n", fn);
-               filp_close(filp, current->files);
+               filp_close(filp, NULL);
                return 0;
        }
        pos = 0;
@@ -38,10 +38,10 @@ static int do_mod_firmware_load(const char *fn, char **fp)
        {
                printk(KERN_INFO "Failed to read '%s'.\n", fn);
                vfree(dp);
-               filp_close(filp, current->files);
+               filp_close(filp, NULL);
                return 0;
        }
-       filp_close(filp, current->files);
+       filp_close(filp, NULL);
        *fp = dp;
        return (int) l;
 }
index 379baad3d5ad548265c06b6e97ae490f4d2a2ed2..5e634a2eb2829461e9efea055a042384e3714b29 100644 (file)
@@ -111,7 +111,8 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
                return 0;
 
        /* If a clock source can't tell us whether it's valid, we assume it is */
-       if (!uac2_control_is_readable(cs_desc->bmControls, UAC2_CS_CONTROL_CLOCK_VALID))
+       if (!uac2_control_is_readable(cs_desc->bmControls,
+                                     UAC2_CS_CONTROL_CLOCK_VALID - 1))
                return 1;
 
        err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
diff --git a/tools/lib/traceevent/.gitignore b/tools/lib/traceevent/.gitignore
new file mode 100644 (file)
index 0000000..35f56be
--- /dev/null
@@ -0,0 +1 @@
+TRACEEVENT-CFLAGS
index 46c2f6b7b123c388d056ae6c324cdde76edef182..14131cb0522d3e7983fc4efc730bb5f30e6ee8ef 100644 (file)
@@ -207,7 +207,7 @@ libtraceevent.so: $(PEVENT_LIB_OBJS)
 libtraceevent.a: $(PEVENT_LIB_OBJS)
        $(Q)$(do_build_static_lib)
 
-$(PEVENT_LIB_OBJS): %.o: $(src)/%.c
+$(PEVENT_LIB_OBJS): %.o: $(src)/%.c TRACEEVENT-CFLAGS
        $(Q)$(do_fpic_compile)
 
 define make_version.h
@@ -272,6 +272,16 @@ ifneq ($(dep_includes),)
  include $(dep_includes)
 endif
 
+### Detect environment changes
+TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):$(ARCH):$(CROSS_COMPILE)
+
+TRACEEVENT-CFLAGS: force
+       @FLAGS='$(TRACK_CFLAGS)'; \
+           if test x"$$FLAGS" != x"`cat TRACEEVENT-CFLAGS 2>/dev/null`" ; then \
+               echo 1>&2 "    * new build flags or cross compiler"; \
+               echo "$$FLAGS" >TRACEEVENT-CFLAGS; \
+            fi
+
 tags:  force
        $(RM) tags
        find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px \
@@ -297,7 +307,7 @@ install: install_lib
 
 clean:
        $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d
-       $(RM) tags TAGS
+       $(RM) TRACEEVENT-CFLAGS tags TAGS
 
 endif # skip-makefile
 
index 75d74e5db8d552372aa0b7c19861c76458784f5e..77f124fe57ad54d8e45858f4e4429f6b63f42273 100644 (file)
@@ -354,6 +354,7 @@ LIB_OBJS += $(OUTPUT)util/usage.o
 LIB_OBJS += $(OUTPUT)util/wrapper.o
 LIB_OBJS += $(OUTPUT)util/sigchain.o
 LIB_OBJS += $(OUTPUT)util/symbol.o
+LIB_OBJS += $(OUTPUT)util/dso-test-data.o
 LIB_OBJS += $(OUTPUT)util/color.o
 LIB_OBJS += $(OUTPUT)util/pager.o
 LIB_OBJS += $(OUTPUT)util/header.o
@@ -803,6 +804,9 @@ $(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
 $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
 
+$(OUTPUT)util/parse-events.o: util/parse-events.c $(OUTPUT)PERF-CFLAGS
+       $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-redundant-decls $<
+
 $(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
 
index 5ce30305462b1775fbfc80f9569e29328d25edd3..d909eb74a0eb74bc46ec8a9e6cb9d8acca5da7f2 100644 (file)
@@ -1141,6 +1141,10 @@ static struct test {
                .desc = "Test perf pmu format parsing",
                .func = test__perf_pmu,
        },
+       {
+               .desc = "Test dso data interface",
+               .func = dso__test_data,
+       },
        {
                .func = NULL,
        },
index e3cab5f088f8def4264a0ec65dba4f85b0b0f954..35e86c6df713d7af91bc50f98953d55f08cc4162 100644 (file)
@@ -125,7 +125,7 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
        /*
         * We can't annotate with just /proc/kallsyms
         */
-       if (map->dso->symtab_type == SYMTAB__KALLSYMS) {
+       if (map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) {
                pr_err("Can't annotate %s: No vmlinux file was found in the "
                       "path\n", sym->name);
                sleep(1);
index 482f0517b61e20f61a18933133717ce53fc0e1c1..413bd62eedb14ab0a2a023b6ef8f654235e59cda 100644 (file)
@@ -978,8 +978,8 @@ static int hist_browser__dump(struct hist_browser *browser)
        fp = fopen(filename, "w");
        if (fp == NULL) {
                char bf[64];
-               strerror_r(errno, bf, sizeof(bf));
-               ui_helpline__fpush("Couldn't write to %s: %s", filename, bf);
+               const char *err = strerror_r(errno, bf, sizeof(bf));
+               ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
                return -1;
        }
 
index 8069dfb5ba7774bd89afd8e78b36f3faea15e907..3a282c0057d2266fa2adcdb00122bf3bfdb69c27 100644 (file)
@@ -426,7 +426,18 @@ int symbol__alloc_hist(struct symbol *sym)
 {
        struct annotation *notes = symbol__annotation(sym);
        const size_t size = symbol__size(sym);
-       size_t sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64));
+       size_t sizeof_sym_hist;
+
+       /* Check for overflow when calculating sizeof_sym_hist */
+       if (size > (SIZE_MAX - sizeof(struct sym_hist)) / sizeof(u64))
+               return -1;
+
+       sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64));
+
+       /* Check for overflow in zalloc argument */
+       if (sizeof_sym_hist > (SIZE_MAX - sizeof(*notes->src))
+                               / symbol_conf.nr_events)
+               return -1;
 
        notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist);
        if (notes->src == NULL)
@@ -777,7 +788,7 @@ fallback:
                free_filename = false;
        }
 
-       if (dso->symtab_type == SYMTAB__KALLSYMS) {
+       if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) {
                char bf[BUILD_ID_SIZE * 2 + 16] = " with build id ";
                char *build_id_msg = NULL;
 
diff --git a/tools/perf/util/dso-test-data.c b/tools/perf/util/dso-test-data.c
new file mode 100644 (file)
index 0000000..541cdc7
--- /dev/null
@@ -0,0 +1,153 @@
+#include "util.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "symbol.h"
+
+#define TEST_ASSERT_VAL(text, cond) \
+do { \
+       if (!(cond)) { \
+               pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \
+               return -1; \
+       } \
+} while (0)
+
+static char *test_file(int size)
+{
+       static char buf_templ[] = "/tmp/test-XXXXXX";
+       char *templ = buf_templ;
+       int fd, i;
+       unsigned char *buf;
+
+       fd = mkostemp(templ, O_CREAT|O_WRONLY|O_TRUNC);
+
+       buf = malloc(size);
+       if (!buf) {
+               close(fd);
+               return NULL;
+       }
+
+       for (i = 0; i < size; i++)
+               buf[i] = (unsigned char) ((int) i % 10);
+
+       if (size != write(fd, buf, size))
+               templ = NULL;
+
+       close(fd);
+       return templ;
+}
+
+#define TEST_FILE_SIZE (DSO__DATA_CACHE_SIZE * 20)
+
+struct test_data_offset {
+       off_t offset;
+       u8 data[10];
+       int size;
+};
+
+struct test_data_offset offsets[] = {
+       /* Fill first cache page. */
+       {
+               .offset = 10,
+               .data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
+               .size   = 10,
+       },
+       /* Read first cache page. */
+       {
+               .offset = 10,
+               .data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
+               .size   = 10,
+       },
+       /* Fill cache boundary pages. */
+       {
+               .offset = DSO__DATA_CACHE_SIZE - DSO__DATA_CACHE_SIZE % 10,
+               .data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
+               .size   = 10,
+       },
+       /* Read cache boundary pages. */
+       {
+               .offset = DSO__DATA_CACHE_SIZE - DSO__DATA_CACHE_SIZE % 10,
+               .data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
+               .size   = 10,
+       },
+       /* Fill final cache page. */
+       {
+               .offset = TEST_FILE_SIZE - 10,
+               .data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
+               .size   = 10,
+       },
+       /* Read final cache page. */
+       {
+               .offset = TEST_FILE_SIZE - 10,
+               .data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
+               .size   = 10,
+       },
+       /* Read final cache page. */
+       {
+               .offset = TEST_FILE_SIZE - 3,
+               .data   = { 7, 8, 9, 0, 0, 0, 0, 0, 0, 0 },
+               .size   = 3,
+       },
+};
+
+int dso__test_data(void)
+{
+       struct machine machine;
+       struct dso *dso;
+       char *file = test_file(TEST_FILE_SIZE);
+       size_t i;
+
+       TEST_ASSERT_VAL("No test file", file);
+
+       memset(&machine, 0, sizeof(machine));
+
+       dso = dso__new((const char *)file);
+
+       /* Basic 10 bytes tests. */
+       for (i = 0; i < ARRAY_SIZE(offsets); i++) {
+               struct test_data_offset *data = &offsets[i];
+               ssize_t size;
+               u8 buf[10];
+
+               memset(buf, 0, 10);
+               size = dso__data_read_offset(dso, &machine, data->offset,
+                                    buf, 10);
+
+               TEST_ASSERT_VAL("Wrong size", size == data->size);
+               TEST_ASSERT_VAL("Wrong data", !memcmp(buf, data->data, 10));
+       }
+
+       /* Read cross multiple cache pages. */
+       {
+               ssize_t size;
+               int c;
+               u8 *buf;
+
+               buf = malloc(TEST_FILE_SIZE);
+               TEST_ASSERT_VAL("ENOMEM\n", buf);
+
+               /* First iteration to fill caches, second one to read them. */
+               for (c = 0; c < 2; c++) {
+                       memset(buf, 0, TEST_FILE_SIZE);
+                       size = dso__data_read_offset(dso, &machine, 10,
+                                                    buf, TEST_FILE_SIZE);
+
+                       TEST_ASSERT_VAL("Wrong size",
+                               size == (TEST_FILE_SIZE - 10));
+
+                       for (i = 0; i < (size_t)size; i++)
+                               TEST_ASSERT_VAL("Wrong data",
+                                       buf[i] == (i % 10));
+               }
+
+               free(buf);
+       }
+
+       dso__delete(dso);
+       unlink(file);
+       return 0;
+}
index f74e9560350eb3dccfe7cc47c6a13e6256f91bb4..3edfd3483816dfa88ac49dc966d0c2a4db2b5923 100644 (file)
@@ -214,7 +214,7 @@ int perf_evlist__add_tracepoints(struct perf_evlist *evlist,
                attrs[i].type          = PERF_TYPE_TRACEPOINT;
                attrs[i].config        = err;
                attrs[i].sample_type   = (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
-                                         PERF_SAMPLE_CPU);
+                                         PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD);
                attrs[i].sample_period = 1;
        }
 
index 5a47aba46759274f7f542e533bae4a62749d565a..3a6d20443330b13d74b2b69b4c1d0e39cd5bb678 100644 (file)
@@ -1212,6 +1212,12 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
                                attr.exclude_user,
                                attr.exclude_kernel);
 
+               fprintf(fp, ", excl_host = %d, excl_guest = %d",
+                               attr.exclude_host,
+                               attr.exclude_guest);
+
+               fprintf(fp, ", precise_ip = %d", attr.precise_ip);
+
                if (nr)
                        fprintf(fp, ", id = {");
 
index 514e2a4b367d6d53838d9ac726a063b0ba3bf5e8..f247ef2789a4d77ffe4ee1e9c9d981d065775c3f 100644 (file)
@@ -708,7 +708,7 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
        bool printed = false;
        struct rb_node *node;
        int i = 0;
-       int ret;
+       int ret = 0;
 
        /*
         * If have one single callchain root, don't bother printing
@@ -747,8 +747,11 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
                root = &cnode->rb_root;
        }
 
-       return __callchain__fprintf_graph(fp, root, total_samples,
+       ret += __callchain__fprintf_graph(fp, root, total_samples,
                                          1, 1, left_margin);
+       ret += fprintf(fp, "\n");
+
+       return ret;
 }
 
 static size_t __callchain__fprintf_flat(FILE *fp,
index a1f4e3669142630aa641d1a34b0f53673b6b67b0..cc33486ad9e25b80e8486a4c60fce41d03a60b15 100644 (file)
@@ -7,6 +7,8 @@
 #include <stdio.h>
 #include <unistd.h>
 #include "map.h"
+#include "thread.h"
+#include "strlist.h"
 
 const char *map_type__name[MAP__NR_TYPES] = {
        [MAP__FUNCTION] = "Functions",
@@ -585,7 +587,21 @@ int machine__init(struct machine *self, const char *root_dir, pid_t pid)
        self->kmaps.machine = self;
        self->pid           = pid;
        self->root_dir      = strdup(root_dir);
-       return self->root_dir == NULL ? -ENOMEM : 0;
+       if (self->root_dir == NULL)
+               return -ENOMEM;
+
+       if (pid != HOST_KERNEL_ID) {
+               struct thread *thread = machine__findnew_thread(self, pid);
+               char comm[64];
+
+               if (thread == NULL)
+                       return -ENOMEM;
+
+               snprintf(comm, sizeof(comm), "[guest/%d]", pid);
+               thread__set_comm(thread, comm);
+       }
+
+       return 0;
 }
 
 static void dsos__delete(struct list_head *self)
@@ -680,7 +696,15 @@ struct machine *machines__findnew(struct rb_root *self, pid_t pid)
            (symbol_conf.guestmount)) {
                sprintf(path, "%s/%d", symbol_conf.guestmount, pid);
                if (access(path, R_OK)) {
-                       pr_err("Can't access file %s\n", path);
+                       static struct strlist *seen;
+
+                       if (!seen)
+                               seen = strlist__new(true, NULL);
+
+                       if (!strlist__has_entry(seen, path)) {
+                               pr_err("Can't access file %s\n", path);
+                               strlist__add(seen, path);
+                       }
                        machine = NULL;
                        goto out;
                }
@@ -714,3 +738,16 @@ char *machine__mmap_name(struct machine *self, char *bf, size_t size)
 
        return bf;
 }
+
+void machines__set_id_hdr_size(struct rb_root *machines, u16 id_hdr_size)
+{
+       struct rb_node *node;
+       struct machine *machine;
+
+       for (node = rb_first(machines); node; node = rb_next(node)) {
+               machine = rb_entry(node, struct machine, rb_node);
+               machine->id_hdr_size = id_hdr_size;
+       }
+
+       return;
+}
index c14c665d9a259c8c5c9aed791a32f2096eff6bb0..03a1e9b08b21a81f4cbdcc7834eb6f6bb7e6c8d1 100644 (file)
@@ -151,6 +151,7 @@ struct machine *machines__add(struct rb_root *self, pid_t pid,
 struct machine *machines__find_host(struct rb_root *self);
 struct machine *machines__find(struct rb_root *self, pid_t pid);
 struct machine *machines__findnew(struct rb_root *self, pid_t pid);
+void machines__set_id_hdr_size(struct rb_root *self, u16 id_hdr_size);
 char *machine__mmap_name(struct machine *self, char *bf, size_t size);
 int machine__init(struct machine *self, const char *root_dir, pid_t pid);
 void machine__exit(struct machine *self);
index 1aa721d7c10f33d84bbd63f8761ed67f18fbe86c..74a5af4d33ec5fc4720639c7634d37cf94e54a92 100644 (file)
@@ -377,6 +377,7 @@ static int add_tracepoint(struct list_head **list, int *idx,
        attr.sample_type |= PERF_SAMPLE_RAW;
        attr.sample_type |= PERF_SAMPLE_TIME;
        attr.sample_type |= PERF_SAMPLE_CPU;
+       attr.sample_type |= PERF_SAMPLE_PERIOD;
        attr.sample_period = 1;
 
        snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name);
@@ -489,6 +490,7 @@ int parse_events_add_breakpoint(struct list_head **list, int *idx,
                attr.bp_len = HW_BREAKPOINT_LEN_4;
 
        attr.type = PERF_TYPE_BREAKPOINT;
+       attr.sample_period = 1;
 
        return add_event(list, idx, &attr, NULL);
 }
index 8e485592ca200c1712d1c959296230f9dce50d33..8e4f0755d2aa8364d81fa714888d622d61f6a2db 100644 (file)
@@ -87,6 +87,7 @@ void perf_session__update_sample_type(struct perf_session *self)
        self->sample_id_all = perf_evlist__sample_id_all(self->evlist);
        self->id_hdr_size = perf_evlist__id_hdr_size(self->evlist);
        self->host_machine.id_hdr_size = self->id_hdr_size;
+       machines__set_id_hdr_size(&self->machines, self->id_hdr_size);
 }
 
 int perf_session__create_kernel_maps(struct perf_session *self)
@@ -918,7 +919,9 @@ static struct machine *
 {
        const u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
 
-       if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) {
+       if (perf_guest &&
+           ((cpumode == PERF_RECORD_MISC_GUEST_KERNEL) ||
+            (cpumode == PERF_RECORD_MISC_GUEST_USER))) {
                u32 pid;
 
                if (event->header.type == PERF_RECORD_MMAP)
index 50958bbeb26aa0c0d19f92deb830649cd43d5100..fdad4eeeb429c1fce9ef90125aab3e4a288c6066 100644 (file)
@@ -29,6 +29,7 @@
 #define NT_GNU_BUILD_ID 3
 #endif
 
+static void dso_cache__free(struct rb_root *root);
 static bool dso__build_id_equal(const struct dso *dso, u8 *build_id);
 static int elf_read_build_id(Elf *elf, void *bf, size_t size);
 static void dsos__add(struct list_head *head, struct dso *dso);
@@ -48,6 +49,31 @@ struct symbol_conf symbol_conf = {
        .symfs            = "",
 };
 
+static enum dso_binary_type binary_type_symtab[] = {
+       DSO_BINARY_TYPE__KALLSYMS,
+       DSO_BINARY_TYPE__GUEST_KALLSYMS,
+       DSO_BINARY_TYPE__JAVA_JIT,
+       DSO_BINARY_TYPE__DEBUGLINK,
+       DSO_BINARY_TYPE__BUILD_ID_CACHE,
+       DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
+       DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
+       DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
+       DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
+       DSO_BINARY_TYPE__GUEST_KMODULE,
+       DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,
+       DSO_BINARY_TYPE__NOT_FOUND,
+};
+
+#define DSO_BINARY_TYPE__SYMTAB_CNT sizeof(binary_type_symtab)
+
+static enum dso_binary_type binary_type_data[] = {
+       DSO_BINARY_TYPE__BUILD_ID_CACHE,
+       DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
+       DSO_BINARY_TYPE__NOT_FOUND,
+};
+
+#define DSO_BINARY_TYPE__DATA_CNT sizeof(binary_type_data)
+
 int dso__name_len(const struct dso *dso)
 {
        if (!dso)
@@ -318,7 +344,9 @@ struct dso *dso__new(const char *name)
                dso__set_short_name(dso, dso->name);
                for (i = 0; i < MAP__NR_TYPES; ++i)
                        dso->symbols[i] = dso->symbol_names[i] = RB_ROOT;
-               dso->symtab_type = SYMTAB__NOT_FOUND;
+               dso->cache = RB_ROOT;
+               dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND;
+               dso->data_type   = DSO_BINARY_TYPE__NOT_FOUND;
                dso->loaded = 0;
                dso->sorted_by_name = 0;
                dso->has_build_id = 0;
@@ -352,6 +380,7 @@ void dso__delete(struct dso *dso)
                free((char *)dso->short_name);
        if (dso->lname_alloc)
                free(dso->long_name);
+       dso_cache__free(&dso->cache);
        free(dso);
 }
 
@@ -806,9 +835,9 @@ int dso__load_kallsyms(struct dso *dso, const char *filename,
        symbols__fixup_end(&dso->symbols[map->type]);
 
        if (dso->kernel == DSO_TYPE_GUEST_KERNEL)
-               dso->symtab_type = SYMTAB__GUEST_KALLSYMS;
+               dso->symtab_type = DSO_BINARY_TYPE__GUEST_KALLSYMS;
        else
-               dso->symtab_type = SYMTAB__KALLSYMS;
+               dso->symtab_type = DSO_BINARY_TYPE__KALLSYMS;
 
        return dso__split_kallsyms(dso, map, filter);
 }
@@ -1660,32 +1689,110 @@ out:
 char dso__symtab_origin(const struct dso *dso)
 {
        static const char origin[] = {
-               [SYMTAB__KALLSYMS]            = 'k',
-               [SYMTAB__JAVA_JIT]            = 'j',
-               [SYMTAB__DEBUGLINK]           = 'l',
-               [SYMTAB__BUILD_ID_CACHE]      = 'B',
-               [SYMTAB__FEDORA_DEBUGINFO]    = 'f',
-               [SYMTAB__UBUNTU_DEBUGINFO]    = 'u',
-               [SYMTAB__BUILDID_DEBUGINFO]   = 'b',
-               [SYMTAB__SYSTEM_PATH_DSO]     = 'd',
-               [SYMTAB__SYSTEM_PATH_KMODULE] = 'K',
-               [SYMTAB__GUEST_KALLSYMS]      =  'g',
-               [SYMTAB__GUEST_KMODULE]       =  'G',
+               [DSO_BINARY_TYPE__KALLSYMS]             = 'k',
+               [DSO_BINARY_TYPE__JAVA_JIT]             = 'j',
+               [DSO_BINARY_TYPE__DEBUGLINK]            = 'l',
+               [DSO_BINARY_TYPE__BUILD_ID_CACHE]       = 'B',
+               [DSO_BINARY_TYPE__FEDORA_DEBUGINFO]     = 'f',
+               [DSO_BINARY_TYPE__UBUNTU_DEBUGINFO]     = 'u',
+               [DSO_BINARY_TYPE__BUILDID_DEBUGINFO]    = 'b',
+               [DSO_BINARY_TYPE__SYSTEM_PATH_DSO]      = 'd',
+               [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE]  = 'K',
+               [DSO_BINARY_TYPE__GUEST_KALLSYMS]       = 'g',
+               [DSO_BINARY_TYPE__GUEST_KMODULE]        = 'G',
        };
 
-       if (dso == NULL || dso->symtab_type == SYMTAB__NOT_FOUND)
+       if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND)
                return '!';
        return origin[dso->symtab_type];
 }
 
+int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,
+                         char *root_dir, char *file, size_t size)
+{
+       char build_id_hex[BUILD_ID_SIZE * 2 + 1];
+       int ret = 0;
+
+       switch (type) {
+       case DSO_BINARY_TYPE__DEBUGLINK: {
+               char *debuglink;
+
+               strncpy(file, dso->long_name, size);
+               debuglink = file + dso->long_name_len;
+               while (debuglink != file && *debuglink != '/')
+                       debuglink--;
+               if (*debuglink == '/')
+                       debuglink++;
+               filename__read_debuglink(dso->long_name, debuglink,
+                                        size - (debuglink - file));
+               }
+               break;
+       case DSO_BINARY_TYPE__BUILD_ID_CACHE:
+               /* skip the locally configured cache if a symfs is given */
+               if (symbol_conf.symfs[0] ||
+                   (dso__build_id_filename(dso, file, size) == NULL))
+                       ret = -1;
+               break;
+
+       case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:
+               snprintf(file, size, "%s/usr/lib/debug%s.debug",
+                        symbol_conf.symfs, dso->long_name);
+               break;
+
+       case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:
+               snprintf(file, size, "%s/usr/lib/debug%s",
+                        symbol_conf.symfs, dso->long_name);
+               break;
+
+       case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:
+               if (!dso->has_build_id) {
+                       ret = -1;
+                       break;
+               }
+
+               build_id__sprintf(dso->build_id,
+                                 sizeof(dso->build_id),
+                                 build_id_hex);
+               snprintf(file, size,
+                        "%s/usr/lib/debug/.build-id/%.2s/%s.debug",
+                        symbol_conf.symfs, build_id_hex, build_id_hex + 2);
+               break;
+
+       case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:
+               snprintf(file, size, "%s%s",
+                        symbol_conf.symfs, dso->long_name);
+               break;
+
+       case DSO_BINARY_TYPE__GUEST_KMODULE:
+               snprintf(file, size, "%s%s%s", symbol_conf.symfs,
+                        root_dir, dso->long_name);
+               break;
+
+       case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
+               snprintf(file, size, "%s%s", symbol_conf.symfs,
+                        dso->long_name);
+               break;
+
+       default:
+       case DSO_BINARY_TYPE__KALLSYMS:
+       case DSO_BINARY_TYPE__GUEST_KALLSYMS:
+       case DSO_BINARY_TYPE__JAVA_JIT:
+       case DSO_BINARY_TYPE__NOT_FOUND:
+               ret = -1;
+               break;
+       }
+
+       return ret;
+}
+
 int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
 {
-       int size = PATH_MAX;
        char *name;
        int ret = -1;
        int fd;
+       u_int i;
        struct machine *machine;
-       const char *root_dir;
+       char *root_dir = (char *) "";
        int want_symtab;
 
        dso__set_loaded(dso, map->type);
@@ -1700,7 +1807,7 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
        else
                machine = NULL;
 
-       name = malloc(size);
+       name = malloc(PATH_MAX);
        if (!name)
                return -1;
 
@@ -1719,81 +1826,27 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
                }
 
                ret = dso__load_perf_map(dso, map, filter);
-               dso->symtab_type = ret > 0 ? SYMTAB__JAVA_JIT :
-                                             SYMTAB__NOT_FOUND;
+               dso->symtab_type = ret > 0 ? DSO_BINARY_TYPE__JAVA_JIT :
+                                            DSO_BINARY_TYPE__NOT_FOUND;
                return ret;
        }
 
+       if (machine)
+               root_dir = machine->root_dir;
+
        /* Iterate over candidate debug images.
         * On the first pass, only load images if they have a full symtab.
         * Failing that, do a second pass where we accept .dynsym also
         */
        want_symtab = 1;
 restart:
-       for (dso->symtab_type = SYMTAB__DEBUGLINK;
-            dso->symtab_type != SYMTAB__NOT_FOUND;
-            dso->symtab_type++) {
-               switch (dso->symtab_type) {
-               case SYMTAB__DEBUGLINK: {
-                       char *debuglink;
-                       strncpy(name, dso->long_name, size);
-                       debuglink = name + dso->long_name_len;
-                       while (debuglink != name && *debuglink != '/')
-                               debuglink--;
-                       if (*debuglink == '/')
-                               debuglink++;
-                       filename__read_debuglink(dso->long_name, debuglink,
-                                                size - (debuglink - name));
-                       }
-                       break;
-               case SYMTAB__BUILD_ID_CACHE:
-                       /* skip the locally configured cache if a symfs is given */
-                       if (symbol_conf.symfs[0] ||
-                           (dso__build_id_filename(dso, name, size) == NULL)) {
-                               continue;
-                       }
-                       break;
-               case SYMTAB__FEDORA_DEBUGINFO:
-                       snprintf(name, size, "%s/usr/lib/debug%s.debug",
-                                symbol_conf.symfs, dso->long_name);
-                       break;
-               case SYMTAB__UBUNTU_DEBUGINFO:
-                       snprintf(name, size, "%s/usr/lib/debug%s",
-                                symbol_conf.symfs, dso->long_name);
-                       break;
-               case SYMTAB__BUILDID_DEBUGINFO: {
-                       char build_id_hex[BUILD_ID_SIZE * 2 + 1];
+       for (i = 0; i < DSO_BINARY_TYPE__SYMTAB_CNT; i++) {
 
-                       if (!dso->has_build_id)
-                               continue;
+               dso->symtab_type = binary_type_symtab[i];
 
-                       build_id__sprintf(dso->build_id,
-                                         sizeof(dso->build_id),
-                                         build_id_hex);
-                       snprintf(name, size,
-                                "%s/usr/lib/debug/.build-id/%.2s/%s.debug",
-                                symbol_conf.symfs, build_id_hex, build_id_hex + 2);
-                       }
-                       break;
-               case SYMTAB__SYSTEM_PATH_DSO:
-                       snprintf(name, size, "%s%s",
-                            symbol_conf.symfs, dso->long_name);
-                       break;
-               case SYMTAB__GUEST_KMODULE:
-                       if (map->groups && machine)
-                               root_dir = machine->root_dir;
-                       else
-                               root_dir = "";
-                       snprintf(name, size, "%s%s%s", symbol_conf.symfs,
-                                root_dir, dso->long_name);
-                       break;
-
-               case SYMTAB__SYSTEM_PATH_KMODULE:
-                       snprintf(name, size, "%s%s", symbol_conf.symfs,
-                                dso->long_name);
-                       break;
-               default:;
-               }
+               if (dso__binary_type_file(dso, dso->symtab_type,
+                                         root_dir, name, PATH_MAX))
+                       continue;
 
                /* Name is now the name of the next image to try */
                fd = open(name, O_RDONLY);
@@ -2010,9 +2063,9 @@ struct map *machine__new_module(struct machine *machine, u64 start,
                return NULL;
 
        if (machine__is_host(machine))
-               dso->symtab_type = SYMTAB__SYSTEM_PATH_KMODULE;
+               dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE;
        else
-               dso->symtab_type = SYMTAB__GUEST_KMODULE;
+               dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE;
        map_groups__insert(&machine->kmaps, map);
        return map;
 }
@@ -2564,8 +2617,15 @@ int machine__create_kernel_maps(struct machine *machine)
            __machine__create_kernel_maps(machine, kernel) < 0)
                return -1;
 
-       if (symbol_conf.use_modules && machine__create_modules(machine) < 0)
-               pr_debug("Problems creating module maps, continuing anyway...\n");
+       if (symbol_conf.use_modules && machine__create_modules(machine) < 0) {
+               if (machine__is_host(machine))
+                       pr_debug("Problems creating module maps, "
+                                "continuing anyway...\n");
+               else
+                       pr_debug("Problems creating module maps for guest %d, "
+                                "continuing anyway...\n", machine->pid);
+       }
+
        /*
         * Now that we have all the maps created, just set the ->end of them:
         */
@@ -2905,3 +2965,218 @@ struct map *dso__new_map(const char *name)
 
        return map;
 }
+
+static int open_dso(struct dso *dso, struct machine *machine)
+{
+       char *root_dir = (char *) "";
+       char *name;
+       int fd;
+
+       name = malloc(PATH_MAX);
+       if (!name)
+               return -ENOMEM;
+
+       if (machine)
+               root_dir = machine->root_dir;
+
+       if (dso__binary_type_file(dso, dso->data_type,
+                                 root_dir, name, PATH_MAX)) {
+               free(name);
+               return -EINVAL;
+       }
+
+       fd = open(name, O_RDONLY);
+       free(name);
+       return fd;
+}
+
+int dso__data_fd(struct dso *dso, struct machine *machine)
+{
+       int i = 0;
+
+       if (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND)
+               return open_dso(dso, machine);
+
+       do {
+               int fd;
+
+               dso->data_type = binary_type_data[i++];
+
+               fd = open_dso(dso, machine);
+               if (fd >= 0)
+                       return fd;
+
+       } while (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND);
+
+       return -EINVAL;
+}
+
+static void
+dso_cache__free(struct rb_root *root)
+{
+       struct rb_node *next = rb_first(root);
+
+       while (next) {
+               struct dso_cache *cache;
+
+               cache = rb_entry(next, struct dso_cache, rb_node);
+               next = rb_next(&cache->rb_node);
+               rb_erase(&cache->rb_node, root);
+               free(cache);
+       }
+}
+
+static struct dso_cache*
+dso_cache__find(struct rb_root *root, u64 offset)
+{
+       struct rb_node **p = &root->rb_node;
+       struct rb_node *parent = NULL;
+       struct dso_cache *cache;
+
+       while (*p != NULL) {
+               u64 end;
+
+               parent = *p;
+               cache = rb_entry(parent, struct dso_cache, rb_node);
+               end = cache->offset + DSO__DATA_CACHE_SIZE;
+
+               if (offset < cache->offset)
+                       p = &(*p)->rb_left;
+               else if (offset >= end)
+                       p = &(*p)->rb_right;
+               else
+                       return cache;
+       }
+       return NULL;
+}
+
+static void
+dso_cache__insert(struct rb_root *root, struct dso_cache *new)
+{
+       struct rb_node **p = &root->rb_node;
+       struct rb_node *parent = NULL;
+       struct dso_cache *cache;
+       u64 offset = new->offset;
+
+       while (*p != NULL) {
+               u64 end;
+
+               parent = *p;
+               cache = rb_entry(parent, struct dso_cache, rb_node);
+               end = cache->offset + DSO__DATA_CACHE_SIZE;
+
+               if (offset < cache->offset)
+                       p = &(*p)->rb_left;
+               else if (offset >= end)
+                       p = &(*p)->rb_right;
+       }
+
+       rb_link_node(&new->rb_node, parent, p);
+       rb_insert_color(&new->rb_node, root);
+}
+
+static ssize_t
+dso_cache__memcpy(struct dso_cache *cache, u64 offset,
+                 u8 *data, u64 size)
+{
+       u64 cache_offset = offset - cache->offset;
+       u64 cache_size   = min(cache->size - cache_offset, size);
+
+       memcpy(data, cache->data + cache_offset, cache_size);
+       return cache_size;
+}
+
+static ssize_t
+dso_cache__read(struct dso *dso, struct machine *machine,
+                u64 offset, u8 *data, ssize_t size)
+{
+       struct dso_cache *cache;
+       ssize_t ret;
+       int fd;
+
+       fd = dso__data_fd(dso, machine);
+       if (fd < 0)
+               return -1;
+
+       do {
+               u64 cache_offset;
+
+               ret = -ENOMEM;
+
+               cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE);
+               if (!cache)
+                       break;
+
+               cache_offset = offset & DSO__DATA_CACHE_MASK;
+               ret = -EINVAL;
+
+               if (-1 == lseek(fd, cache_offset, SEEK_SET))
+                       break;
+
+               ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE);
+               if (ret <= 0)
+                       break;
+
+               cache->offset = cache_offset;
+               cache->size   = ret;
+               dso_cache__insert(&dso->cache, cache);
+
+               ret = dso_cache__memcpy(cache, offset, data, size);
+
+       } while (0);
+
+       if (ret <= 0)
+               free(cache);
+
+       close(fd);
+       return ret;
+}
+
+static ssize_t dso_cache_read(struct dso *dso, struct machine *machine,
+                             u64 offset, u8 *data, ssize_t size)
+{
+       struct dso_cache *cache;
+
+       cache = dso_cache__find(&dso->cache, offset);
+       if (cache)
+               return dso_cache__memcpy(cache, offset, data, size);
+       else
+               return dso_cache__read(dso, machine, offset, data, size);
+}
+
+ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
+                             u64 offset, u8 *data, ssize_t size)
+{
+       ssize_t r = 0;
+       u8 *p = data;
+
+       do {
+               ssize_t ret;
+
+               ret = dso_cache_read(dso, machine, offset, p, size);
+               if (ret < 0)
+                       return ret;
+
+               /* Reached EOF, return what we have. */
+               if (!ret)
+                       break;
+
+               BUG_ON(ret > size);
+
+               r      += ret;
+               p      += ret;
+               offset += ret;
+               size   -= ret;
+
+       } while (size);
+
+       return r;
+}
+
+ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
+                           struct machine *machine, u64 addr,
+                           u8 *data, ssize_t size)
+{
+       u64 offset = map->map_ip(map, addr);
+       return dso__data_read_offset(dso, machine, offset, data, size);
+}
index a884b99017f085c0dceb47e7b766dab8527dc7df..1fe733a1e21f2acb31282c9df562b34b0a03346f 100644 (file)
@@ -155,6 +155,21 @@ struct addr_location {
        s32           cpu;
 };
 
+enum dso_binary_type {
+       DSO_BINARY_TYPE__KALLSYMS = 0,
+       DSO_BINARY_TYPE__GUEST_KALLSYMS,
+       DSO_BINARY_TYPE__JAVA_JIT,
+       DSO_BINARY_TYPE__DEBUGLINK,
+       DSO_BINARY_TYPE__BUILD_ID_CACHE,
+       DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
+       DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
+       DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
+       DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
+       DSO_BINARY_TYPE__GUEST_KMODULE,
+       DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,
+       DSO_BINARY_TYPE__NOT_FOUND,
+};
+
 enum dso_kernel_type {
        DSO_TYPE_USER = 0,
        DSO_TYPE_KERNEL,
@@ -167,19 +182,31 @@ enum dso_swap_type {
        DSO_SWAP__YES,
 };
 
+#define DSO__DATA_CACHE_SIZE 4096
+#define DSO__DATA_CACHE_MASK ~(DSO__DATA_CACHE_SIZE - 1)
+
+struct dso_cache {
+       struct rb_node  rb_node;
+       u64 offset;
+       u64 size;
+       char data[0];
+};
+
 struct dso {
        struct list_head node;
        struct rb_root   symbols[MAP__NR_TYPES];
        struct rb_root   symbol_names[MAP__NR_TYPES];
+       struct rb_root   cache;
        enum dso_kernel_type    kernel;
        enum dso_swap_type      needs_swap;
+       enum dso_binary_type    symtab_type;
+       enum dso_binary_type    data_type;
        u8               adjust_symbols:1;
        u8               has_build_id:1;
        u8               hit:1;
        u8               annotate_warned:1;
        u8               sname_alloc:1;
        u8               lname_alloc:1;
-       unsigned char    symtab_type;
        u8               sorted_by_name;
        u8               loaded;
        u8               build_id[BUILD_ID_SIZE];
@@ -253,21 +280,6 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso,
                                    enum map_type type, FILE *fp);
 size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp);
 
-enum symtab_type {
-       SYMTAB__KALLSYMS = 0,
-       SYMTAB__GUEST_KALLSYMS,
-       SYMTAB__JAVA_JIT,
-       SYMTAB__DEBUGLINK,
-       SYMTAB__BUILD_ID_CACHE,
-       SYMTAB__FEDORA_DEBUGINFO,
-       SYMTAB__UBUNTU_DEBUGINFO,
-       SYMTAB__BUILDID_DEBUGINFO,
-       SYMTAB__SYSTEM_PATH_DSO,
-       SYMTAB__GUEST_KMODULE,
-       SYMTAB__SYSTEM_PATH_KMODULE,
-       SYMTAB__NOT_FOUND,
-};
-
 char dso__symtab_origin(const struct dso *dso);
 void dso__set_long_name(struct dso *dso, char *name);
 void dso__set_build_id(struct dso *dso, void *build_id);
@@ -304,4 +316,14 @@ bool symbol_type__is_a(char symbol_type, enum map_type map_type);
 
 size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp);
 
+int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,
+                         char *root_dir, char *file, size_t size);
+
+int dso__data_fd(struct dso *dso, struct machine *machine);
+ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
+                             u64 offset, u8 *data, ssize_t size);
+ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
+                           struct machine *machine, u64 addr,
+                           u8 *data, ssize_t size);
+int dso__test_data(void);
 #endif /* __PERF_SYMBOL */
index 1064d5b148ad9dce074bd828c6267c61de2cf376..3f59c496e64c9035311389fb73e9468fb47e7889 100644 (file)
@@ -110,8 +110,17 @@ int perf_target__strerror(struct perf_target *target, int errnum,
        int idx;
        const char *msg;
 
+       BUG_ON(buflen > 0);
+
        if (errnum >= 0) {
-               strerror_r(errnum, buf, buflen);
+               const char *err = strerror_r(errnum, buf, buflen);
+
+               if (err != buf) {
+                       size_t len = strlen(err);
+                       char *c = mempcpy(buf, err, min(buflen - 1, len));
+                       *c = '\0';
+               }
+
                return 0;
        }
 
diff --git a/tools/testing/fault-injection/failcmd.sh b/tools/testing/fault-injection/failcmd.sh
new file mode 100644 (file)
index 0000000..78a9ed7
--- /dev/null
@@ -0,0 +1,219 @@
+#!/bin/bash
+#
+# NAME
+#      failcmd.sh - run a command with injecting slab/page allocation failures
+#
+# SYNOPSIS
+#      failcmd.sh --help
+#      failcmd.sh [<options>] command [arguments]
+#
+# DESCRIPTION
+#      Run command with injecting slab/page allocation failures by fault
+#      injection.
+#
+#      NOTE: you need to run this script as root.
+#
+
+usage()
+{
+       cat >&2 <<EOF
+Usage: $0 [options] command [arguments]
+
+OPTIONS
+       -p percent
+       --probability=percent
+               likelihood of failure injection, in percent.
+               Default value is 1
+
+       -t value
+       --times=value
+               specifies how many times failures may happen at most.
+               Default value is 1
+
+       --oom-kill-allocating-task=value
+               set /proc/sys/vm/oom_kill_allocating_task to specified value
+               before running the command.
+               Default value is 1
+
+       -h, --help
+               Display a usage message and exit
+
+       --interval=value, --space=value, --verbose=value, --task-filter=value,
+       --stacktrace-depth=value, --require-start=value, --require-end=value,
+       --reject-start=value, --reject-end=value, --ignore-gfp-wait=value
+               See Documentation/fault-injection/fault-injection.txt for more
+               information
+
+       failslab options:
+       --cache-filter=value
+
+       fail_page_alloc options:
+       --ignore-gfp-highmem=value, --min-order=value
+
+ENVIRONMENT
+       FAILCMD_TYPE
+               The following values for FAILCMD_TYPE are recognized:
+
+               failslab
+                       inject slab allocation failures
+               fail_page_alloc
+                       inject page allocation failures
+
+               If FAILCMD_TYPE is not defined, then failslab is used.
+EOF
+}
+
+if [ $UID != 0 ]; then
+       echo must be run as root >&2
+       exit 1
+fi
+
+DEBUGFS=`mount -t debugfs | head -1 | awk '{ print $3}'`
+
+if [ ! -d "$DEBUGFS" ]; then
+       echo debugfs is not mounted >&2
+       exit 1
+fi
+
+FAILCMD_TYPE=${FAILCMD_TYPE:-failslab}
+FAULTATTR=$DEBUGFS/$FAILCMD_TYPE
+
+if [ ! -d $FAULTATTR ]; then
+       echo $FAILCMD_TYPE is not available >&2
+       exit 1
+fi
+
+LONGOPTS=probability:,interval:,times:,space:,verbose:,task-filter:
+LONGOPTS=$LONGOPTS,stacktrace-depth:,require-start:,require-end:
+LONGOPTS=$LONGOPTS,reject-start:,reject-end:,oom-kill-allocating-task:,help
+
+if [ $FAILCMD_TYPE = failslab ]; then
+       LONGOPTS=$LONGOPTS,ignore-gfp-wait:,cache-filter:
+elif [ $FAILCMD_TYPE = fail_page_alloc ]; then
+       LONGOPTS=$LONGOPTS,ignore-gfp-wait:,ignore-gfp-highmem:,min-order:
+fi
+
+TEMP=`getopt -o p:i:t:s:v:h --long $LONGOPTS -n 'failcmd.sh' -- "$@"`
+
+if [ $? != 0 ]; then
+       usage
+       exit 1
+fi
+
+eval set -- "$TEMP"
+
+fault_attr_default()
+{
+       echo N > $FAULTATTR/task-filter
+       echo 0 > $FAULTATTR/probability
+       echo 1 > $FAULTATTR/times
+}
+
+fault_attr_default
+
+oom_kill_allocating_task_saved=`cat /proc/sys/vm/oom_kill_allocating_task`
+
+restore_values()
+{
+       fault_attr_default
+       echo $oom_kill_allocating_task_saved \
+               > /proc/sys/vm/oom_kill_allocating_task
+}
+
+#
+# Default options
+#
+declare -i oom_kill_allocating_task=1
+declare task_filter=Y
+declare -i probability=1
+declare -i times=1
+
+while true; do
+       case "$1" in
+       -p|--probability)
+               probability=$2
+               shift 2
+               ;;
+       -i|--interval)
+               echo $2 > $FAULTATTR/interval
+               shift 2
+               ;;
+       -t|--times)
+               times=$2
+               shift 2
+               ;;
+       -s|--space)
+               echo $2 > $FAULTATTR/space
+               shift 2
+               ;;
+       -v|--verbose)
+               echo $2 > $FAULTATTR/verbose
+               shift 2
+               ;;
+       --task-filter)
+               task_filter=$2
+               shift 2
+               ;;
+       --stacktrace-depth)
+               echo $2 > $FAULTATTR/stacktrace-depth
+               shift 2
+               ;;
+       --require-start)
+               echo $2 > $FAULTATTR/require-start
+               shift 2
+               ;;
+       --require-end)
+               echo $2 > $FAULTATTR/require-end
+               shift 2
+               ;;
+       --reject-start)
+               echo $2 > $FAULTATTR/reject-start
+               shift 2
+               ;;
+       --reject-end)
+               echo $2 > $FAULTATTR/reject-end
+               shift 2
+               ;;
+       --oom-kill-allocating-task)
+               oom_kill_allocating_task=$2
+               shift 2
+               ;;
+       --ignore-gfp-wait)
+               echo $2 > $FAULTATTR/ignore-gfp-wait
+               shift 2
+               ;;
+       --cache-filter)
+               echo $2 > $FAULTATTR/cache_filter
+               shift 2
+               ;;
+       --ignore-gfp-highmem)
+               echo $2 > $FAULTATTR/ignore-gfp-highmem
+               shift 2
+               ;;
+       --min-order)
+               echo $2 > $FAULTATTR/min-order
+               shift 2
+               ;;
+       -h|--help)
+               usage
+               exit 0
+               shift
+               ;;
+       --)
+               shift
+               break
+               ;;
+       esac
+done
+
+[ -z "$1" ] && exit 0
+
+echo $oom_kill_allocating_task > /proc/sys/vm/oom_kill_allocating_task
+echo $task_filter > $FAULTATTR/task-filter
+echo $probability > $FAULTATTR/probability
+echo $times > $FAULTATTR/times
+
+trap "restore_values" SIGINT SIGTERM EXIT
+
+cmd="echo 1 > /proc/self/make-it-fail && exec $@"
+bash -c "$cmd"
index 292b13ad03f505f25790f6fb43a8b9c8b822c3c6..52b7959cd513dc358e57376528abcf41b36f4f6d 100755 (executable)
@@ -52,6 +52,7 @@ my %default = (
     "STOP_AFTER_SUCCESS"       => 10,
     "STOP_AFTER_FAILURE"       => 60,
     "STOP_TEST_AFTER"          => 600,
+    "MAX_MONITOR_WAIT"         => 1800,
 
 # required, and we will ask users if they don't have them but we keep the default
 # value something that is common.
@@ -77,6 +78,11 @@ my $output_config;
 my $test_type;
 my $build_type;
 my $build_options;
+my $final_post_ktest;
+my $pre_ktest;
+my $post_ktest;
+my $pre_test;
+my $post_test;
 my $pre_build;
 my $post_build;
 my $pre_build_die;
@@ -93,6 +99,7 @@ my $reboot_on_success;
 my $die_on_failure;
 my $powercycle_after_reboot;
 my $poweroff_after_halt;
+my $max_monitor_wait;
 my $ssh_exec;
 my $scp_to_target;
 my $scp_to_target_install;
@@ -101,6 +108,7 @@ my $grub_menu;
 my $grub_number;
 my $target;
 my $make;
+my $pre_install;
 my $post_install;
 my $no_install;
 my $noclean;
@@ -167,6 +175,7 @@ my $bisect_check;
 
 my $config_bisect;
 my $config_bisect_type;
+my $config_bisect_check;
 
 my $patchcheck_type;
 my $patchcheck_start;
@@ -182,6 +191,9 @@ my $newconfig = 0;
 my %entered_configs;
 my %config_help;
 my %variable;
+
+# force_config is the list of configs that we force enabled (or disabled)
+# in a .config file. The MIN_CONFIG and ADD_CONFIG configs.
 my %force_config;
 
 # do not force reboots on config problems
@@ -197,6 +209,10 @@ my %option_map = (
     "OUTPUT_DIR"               => \$outputdir,
     "BUILD_DIR"                        => \$builddir,
     "TEST_TYPE"                        => \$test_type,
+    "PRE_KTEST"                        => \$pre_ktest,
+    "POST_KTEST"               => \$post_ktest,
+    "PRE_TEST"                 => \$pre_test,
+    "POST_TEST"                        => \$post_test,
     "BUILD_TYPE"               => \$build_type,
     "BUILD_OPTIONS"            => \$build_options,
     "PRE_BUILD"                        => \$pre_build,
@@ -216,6 +232,7 @@ my %option_map = (
     "ADD_CONFIG"               => \$addconfig,
     "REBOOT_TYPE"              => \$reboot_type,
     "GRUB_MENU"                        => \$grub_menu,
+    "PRE_INSTALL"              => \$pre_install,
     "POST_INSTALL"             => \$post_install,
     "NO_INSTALL"               => \$no_install,
     "REBOOT_SCRIPT"            => \$reboot_script,
@@ -228,6 +245,7 @@ my %option_map = (
     "POWER_OFF"                        => \$power_off,
     "POWERCYCLE_AFTER_REBOOT"  => \$powercycle_after_reboot,
     "POWEROFF_AFTER_HALT"      => \$poweroff_after_halt,
+    "MAX_MONITOR_WAIT"         => \$max_monitor_wait,
     "SLEEP_TIME"               => \$sleep_time,
     "BISECT_SLEEP_TIME"                => \$bisect_sleep_time,
     "PATCHCHECK_SLEEP_TIME"    => \$patchcheck_sleep_time,
@@ -272,6 +290,7 @@ my %option_map = (
 
     "CONFIG_BISECT"            => \$config_bisect,
     "CONFIG_BISECT_TYPE"       => \$config_bisect_type,
+    "CONFIG_BISECT_CHECK"      => \$config_bisect_check,
 
     "PATCHCHECK_TYPE"          => \$patchcheck_type,
     "PATCHCHECK_START"         => \$patchcheck_start,
@@ -604,6 +623,10 @@ sub process_compare {
        return $lval eq $rval;
     } elsif ($cmp eq "!=") {
        return $lval ne $rval;
+    } elsif ($cmp eq "=~") {
+       return $lval =~ m/$rval/;
+    } elsif ($cmp eq "!~") {
+       return $lval !~ m/$rval/;
     }
 
     my $statement = "$lval $cmp $rval";
@@ -659,7 +682,7 @@ sub process_expression {
        }
     }
 
-    if ($val =~ /(.*)(==|\!=|>=|<=|>|<)(.*)/) {
+    if ($val =~ /(.*)(==|\!=|>=|<=|>|<|=~|\!~)(.*)/) {
        my $ret = process_compare($1, $2, $3);
        if ($ret < 0) {
            die "$name: $.: Unable to process comparison\n";
@@ -1117,7 +1140,11 @@ sub reboot {
     }
 
     if (defined($time)) {
-       wait_for_monitor($time, $reboot_success_line);
+       if (wait_for_monitor($time, $reboot_success_line)) {
+           # reboot got stuck?
+           doprint "Reboot did not finish. Forcing power cycle\n";
+           run_command "$power_cycle";
+       }
        end_monitor;
     }
 }
@@ -1212,6 +1239,11 @@ sub wait_for_monitor {
     my $full_line = "";
     my $line;
     my $booted = 0;
+    my $start_time = time;
+    my $skip_call_trace = 0;
+    my $bug = 0;
+    my $bug_ignored = 0;
+    my $now;
 
     doprint "** Wait for monitor to settle down **\n";
 
@@ -1227,11 +1259,39 @@ sub wait_for_monitor {
            $booted = 1;
        }
 
+       if ($full_line =~ /\[ backtrace testing \]/) {
+           $skip_call_trace = 1;
+       }
+
+       if ($full_line =~ /call trace:/i) {
+           if (!$bug && !$skip_call_trace) {
+               if ($ignore_errors) {
+                   $bug_ignored = 1;
+               } else {
+                   $bug = 1;
+               }
+           }
+       }
+
+       if ($full_line =~ /\[ end of backtrace testing \]/) {
+           $skip_call_trace = 0;
+       }
+
+       if ($full_line =~ /Kernel panic -/) {
+           $bug = 1;
+       }
+
        if ($line =~ /\n/) {
            $full_line = "";
        }
+       $now = time;
+       if ($now - $start_time >= $max_monitor_wait) {
+           doprint "Exiting monitor flush due to hitting MAX_MONITOR_WAIT\n";
+           return 1;
+       }
     }
     print "** Monitor flushed **\n";
+    return $bug;
 }
 
 sub save_logs {
@@ -1273,6 +1333,10 @@ sub save_logs {
 
 sub fail {
 
+       if (defined($post_test)) {
+               run_command $post_test;
+       }
+
        if ($die_on_failure) {
                dodie @_;
        }
@@ -1656,6 +1720,12 @@ sub install {
 
     return if ($no_install);
 
+    if (defined($pre_install)) {
+       my $cp_pre_install = eval_kernel_version $pre_install;
+       run_command "$cp_pre_install" or
+           dodie "Failed to run pre install";
+    }
+
     my $cp_target = eval_kernel_version $target_image;
 
     run_scp_install "$outputdir/$build_target", "$cp_target" or
@@ -1814,6 +1884,7 @@ sub make_oldconfig {
 sub load_force_config {
     my ($config) = @_;
 
+    doprint "Loading force configs from $config\n";
     open(IN, $config) or
        dodie "failed to read $config";
     while (<IN>) {
@@ -1937,6 +2008,10 @@ sub halt {
 sub success {
     my ($i) = @_;
 
+    if (defined($post_test)) {
+       run_command $post_test;
+    }
+
     $successes++;
 
     my $name = "";
@@ -2003,6 +2078,7 @@ sub do_run_test {
     my $line;
     my $full_line;
     my $bug = 0;
+    my $bug_ignored = 0;
 
     wait_for_monitor 1;
 
@@ -2027,7 +2103,11 @@ sub do_run_test {
            doprint $line;
 
            if ($full_line =~ /call trace:/i) {
-               $bug = 1;
+               if ($ignore_errors) {
+                   $bug_ignored = 1;
+               } else {
+                   $bug = 1;
+               }
            }
 
            if ($full_line =~ /Kernel panic -/) {
@@ -2040,6 +2120,10 @@ sub do_run_test {
        }
     } while (!$child_done && !$bug);
 
+    if (!$bug && $bug_ignored) {
+       doprint "WARNING: Call Trace detected but ignored due to IGNORE_ERRORS=1\n";
+    }
+
     if ($bug) {
        my $failure_start = time;
        my $now;
@@ -2362,9 +2446,24 @@ sub bisect {
     success $i;
 }
 
+# config_ignore holds the configs that were set (or unset) for
+# a good config and we will ignore these configs for the rest
+# of a config bisect. These configs stay as they were.
 my %config_ignore;
+
+# config_set holds what all configs were set as.
 my %config_set;
 
+# config_off holds the set of configs that the bad config had disabled.
+# We need to record them and set them in the .config when running
+# oldnoconfig, because oldnoconfig does not turn off new symbols, but
+# instead just keeps the defaults.
+my %config_off;
+
+# config_off_tmp holds a set of configs to turn off for now
+my @config_off_tmp;
+
+# config_list is the set of configs that are being tested
 my %config_list;
 my %null_config;
 
@@ -2443,12 +2542,21 @@ sub create_config {
        }
     }
 
+    # turn off configs to keep off
+    foreach my $config (keys %config_off) {
+       print OUT "# $config is not set\n";
+    }
+
+    # turn off configs that should be off for now
+    foreach my $config (@config_off_tmp) {
+       print OUT "# $config is not set\n";
+    }
+
     foreach my $config (keys %config_ignore) {
        print OUT "$config_ignore{$config}\n";
     }
     close(OUT);
 
-#    exit;
     make_oldconfig;
 }
 
@@ -2525,6 +2633,13 @@ sub run_config_bisect {
     do {
        my @tophalf = @start_list[0 .. $half];
 
+       # keep the bottom half off
+       if ($half < $#start_list) {
+           @config_off_tmp = @start_list[$half + 1 .. $#start_list];
+       } else {
+           @config_off_tmp = ();
+       }
+
        create_config @tophalf;
        read_current_config \%current_config;
 
@@ -2541,7 +2656,11 @@ sub run_config_bisect {
        if (!$found) {
            # try the other half
            doprint "Top half produced no set configs, trying bottom half\n";
+
+           # keep the top half off
+           @config_off_tmp = @tophalf;
            @tophalf = @start_list[$half + 1 .. $#start_list];
+
            create_config @tophalf;
            read_current_config \%current_config;
            foreach my $config (@tophalf) {
@@ -2679,6 +2798,10 @@ sub config_bisect {
                $added_configs{$2} = $1;
                $config_list{$2} = $1;
            }
+       } elsif (/^# ((CONFIG\S*).*)/) {
+           # Keep these configs disabled
+           $config_set{$2} = $1;
+           $config_off{$2} = $1;
        }
     }
     close(IN);
@@ -2701,6 +2824,8 @@ sub config_bisect {
     my %config_test;
     my $once = 0;
 
+    @config_off_tmp = ();
+
     # Sometimes kconfig does weird things. We must make sure
     # that the config we autocreate has everything we need
     # to test, otherwise we may miss testing configs, or
@@ -2719,6 +2844,18 @@ sub config_bisect {
        }
     }
     my $ret;
+
+    if (defined($config_bisect_check) && $config_bisect_check) {
+       doprint " Checking to make sure bad config with min config fails\n";
+       create_config keys %config_list;
+       $ret = run_config_bisect_test $config_bisect_type;
+       if ($ret) {
+           doprint " FAILED! Bad config with min config boots fine\n";
+           return -1;
+       }
+       doprint " Bad config with min config fails as expected\n";
+    }
+
     do {
        $ret = run_config_bisect;
     } while (!$ret);
@@ -3510,6 +3647,8 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) {
 
     $iteration = $i;
 
+    undef %force_config;
+
     my $makecmd = set_test_option("MAKE_CMD", $i);
 
     # Load all the options into their mapped variable names
@@ -3519,6 +3658,18 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) {
 
     $start_minconfig_defined = 1;
 
+    # The first test may override the PRE_KTEST option
+    if (defined($pre_ktest) && $i == 1) {
+       doprint "\n";
+       run_command $pre_ktest;
+    }
+
+    # Any test can override the POST_KTEST option
+    # The last test takes precedence.
+    if (defined($post_ktest)) {
+       $final_post_ktest = $post_ktest;
+    }
+
     if (!defined($start_minconfig)) {
        $start_minconfig_defined = 0;
        $start_minconfig = $minconfig;
@@ -3573,6 +3724,10 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) {
     doprint "\n\n";
     doprint "RUNNING TEST $i of $opt{NUM_TESTS} with option $test_type $run_type$installme\n\n";
 
+    if (defined($pre_test)) {
+       run_command $pre_test;
+    }
+
     unlink $dmesg;
     unlink $buildlog;
     unlink $testlog;
@@ -3638,6 +3793,10 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) {
     success $i;
 }
 
+if (defined($final_post_ktest)) {
+    run_command $final_post_ktest;
+}
+
 if ($opt{"POWEROFF_ON_SUCCESS"}) {
     halt;
 } elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot && $reboot_success) {
index cf362b3d1ec959322af01e650b4eb0912084e5e9..de28a0a3b8fc90b6070ac557cf755fbaa366e32a 100644 (file)
 # DEFAULTS
 # DEFAULTS SKIP
 
+# If you want to execute some command before the first test runs
+# you can set this option. Note, it can be set as a default option
+# or an option in the first test case. All other test cases will
+# ignore it. If both the default and first test have this option
+# set, then the first test will take precedence.
+#
+# default (undefined)
+#PRE_KTEST = ${SSH} ~/set_up_test
+
+# If you want to execute some command after all the tests have
+# completed, you can set this option. Note, it can be set as a
+# default or any test case can override it. If multiple test cases
+# set this option, then the last test case that set it will take
+# precedence
+#
+# default (undefined)
+#POST_KTEST = ${SSH} ~/dismantle_test
+
 # The default test type (default test)
 # The test types may be:
 #   build   - only build the kernel, do nothing else
 # (default "")
 #BUILD_OPTIONS = -j20
 
+# If you need to do some special handling before installing
+# you can add a script with this option.
+# The environment variable KERNEL_VERSION will be set to the
+# kernel version that is used.
+#
+# default (undefined)
+#PRE_INSTALL = ssh user@target rm -rf '/lib/modules/*-test*'
+
 # If you need an initrd, you can add a script or code here to install
 # it. The environment variable KERNEL_VERSION will be set to the
 # kernel version that is used. Remember to add the initrd line
 # (default 0)
 #NO_INSTALL = 1
 
+# If there is a command that you want to run before the individual test
+# case executes, then you can set this option
+#
+# default (undefined)
+#PRE_TEST = ${SSH} reboot_to_special_kernel
+
+# If there is a command you want to run after the individual test case
+# completes, then you can set this option.
+#
+# default (undefined)
+#POST_TEST = cd ${BUILD_DIR}; git reset --hard
+
 # If there is a script that you require to run before the build is done
 # you can specify it with PRE_BUILD.
 #
 # (default 60)
 #BISECT_SLEEP_TIME = 60
 
+# The max wait time (in seconds) for waiting for the console to finish.
+# If for some reason, the console is outputting content without
+# ever finishing, this will cause ktest to get stuck. This
+# option is the max time ktest will wait for the monitor (console)
+# to settle down before continuing.
+# (default 1800)
+#MAX_MONITOR_WAIT
+
 # The time in between patch checks to sleep (in seconds)
 # (default 60)
 #PATCHCHECK_SLEEP_TIME = 60
 #  can specify it with CONFIG_BISECT_GOOD. Otherwise
 #  the MIN_CONFIG is the base.
 #
+# CONFIG_BISECT_CHECK (optional)
+#  Set this to 1 if you want to confirm that the config ktest
+#  generates (the bad config with the min config) is still bad.
+#  It may be that the min config fixes what broke the bad config
+#  and the test will not return a result.
+#
 # Example:
 #   TEST_START
 #   TEST_TYPE = config_bisect
index a4162e15c25f89f32862a1f4fb2630c32f8c1c60..85baf11e2acd7d11aa4990a0f7f53f8d28689a20 100644 (file)
@@ -1,4 +1,4 @@
-TARGETS = breakpoints kcmp mqueue vm
+TARGETS = breakpoints kcmp mqueue vm cpu-hotplug memory-hotplug
 
 all:
        for TARGET in $(TARGETS); do \
diff --git a/tools/testing/selftests/cpu-hotplug/Makefile b/tools/testing/selftests/cpu-hotplug/Makefile
new file mode 100644 (file)
index 0000000..7c9c20f
--- /dev/null
@@ -0,0 +1,6 @@
+all:
+
+run_tests:
+       ./on-off-test.sh
+
+clean:
diff --git a/tools/testing/selftests/cpu-hotplug/on-off-test.sh b/tools/testing/selftests/cpu-hotplug/on-off-test.sh
new file mode 100644 (file)
index 0000000..bdde7cf
--- /dev/null
@@ -0,0 +1,221 @@
+#!/bin/bash
+
+SYSFS=
+
+prerequisite()
+{
+       msg="skip all tests:"
+
+       if [ $UID != 0 ]; then
+               echo $msg must be run as root >&2
+               exit 0
+       fi
+
+       SYSFS=`mount -t sysfs | head -1 | awk '{ print $3 }'`
+
+       if [ ! -d "$SYSFS" ]; then
+               echo $msg sysfs is not mounted >&2
+               exit 0
+       fi
+
+       if ! ls $SYSFS/devices/system/cpu/cpu* > /dev/null 2>&1; then
+               echo $msg cpu hotplug is not supported >&2
+               exit 0
+       fi
+}
+
+#
+# list all hot-pluggable CPUs
+#
+hotpluggable_cpus()
+{
+       local state=${1:-.\*}
+
+       for cpu in $SYSFS/devices/system/cpu/cpu*; do
+               if [ -f $cpu/online ] && grep -q $state $cpu/online; then
+                       echo ${cpu##/*/cpu}
+               fi
+       done
+}
+
+hotplaggable_offline_cpus()
+{
+       hotpluggable_cpus 0
+}
+
+hotpluggable_online_cpus()
+{
+       hotpluggable_cpus 1
+}
+
+cpu_is_online()
+{
+       grep -q 1 $SYSFS/devices/system/cpu/cpu$1/online
+}
+
+cpu_is_offline()
+{
+       grep -q 0 $SYSFS/devices/system/cpu/cpu$1/online
+}
+
+online_cpu()
+{
+       echo 1 > $SYSFS/devices/system/cpu/cpu$1/online
+}
+
+offline_cpu()
+{
+       echo 0 > $SYSFS/devices/system/cpu/cpu$1/online
+}
+
+online_cpu_expect_success()
+{
+       local cpu=$1
+
+       if ! online_cpu $cpu; then
+               echo $FUNCNAME $cpu: unexpected fail >&2
+       elif ! cpu_is_online $cpu; then
+               echo $FUNCNAME $cpu: unexpected offline >&2
+       fi
+}
+
+online_cpu_expect_fail()
+{
+       local cpu=$1
+
+       if online_cpu $cpu 2> /dev/null; then
+               echo $FUNCNAME $cpu: unexpected success >&2
+       elif ! cpu_is_offline $cpu; then
+               echo $FUNCNAME $cpu: unexpected online >&2
+       fi
+}
+
+offline_cpu_expect_success()
+{
+       local cpu=$1
+
+       if ! offline_cpu $cpu; then
+               echo $FUNCNAME $cpu: unexpected fail >&2
+       elif ! cpu_is_offline $cpu; then
+               echo $FUNCNAME $cpu: unexpected offline >&2
+       fi
+}
+
+offline_cpu_expect_fail()
+{
+       local cpu=$1
+
+       if offline_cpu $cpu 2> /dev/null; then
+               echo $FUNCNAME $cpu: unexpected success >&2
+       elif ! cpu_is_online $cpu; then
+               echo $FUNCNAME $cpu: unexpected offline >&2
+       fi
+}
+
+error=-12
+priority=0
+
+while getopts e:hp: opt; do
+       case $opt in
+       e)
+               error=$OPTARG
+               ;;
+       h)
+               echo "Usage $0 [ -e errno ] [ -p notifier-priority ]"
+               exit
+               ;;
+       p)
+               priority=$OPTARG
+               ;;
+       esac
+done
+
+if ! [ "$error" -ge -4095 -a "$error" -lt 0 ]; then
+       echo "error code must be -4095 <= errno < 0" >&2
+       exit 1
+fi
+
+prerequisite
+
+#
+# Online all hot-pluggable CPUs
+#
+for cpu in `hotplaggable_offline_cpus`; do
+       online_cpu_expect_success $cpu
+done
+
+#
+# Offline all hot-pluggable CPUs
+#
+for cpu in `hotpluggable_online_cpus`; do
+       offline_cpu_expect_success $cpu
+done
+
+#
+# Online all hot-pluggable CPUs again
+#
+for cpu in `hotplaggable_offline_cpus`; do
+       online_cpu_expect_success $cpu
+done
+
+#
+# Test with cpu notifier error injection
+#
+
+DEBUGFS=`mount -t debugfs | head -1 | awk '{ print $3 }'`
+NOTIFIER_ERR_INJECT_DIR=$DEBUGFS/notifier-error-inject/cpu
+
+prerequisite_extra()
+{
+       msg="skip extra tests:"
+
+       /sbin/modprobe -q -r cpu-notifier-error-inject
+       /sbin/modprobe -q cpu-notifier-error-inject priority=$priority
+
+       if [ ! -d "$DEBUGFS" ]; then
+               echo $msg debugfs is not mounted >&2
+               exit 0
+       fi
+
+       if [ ! -d $NOTIFIER_ERR_INJECT_DIR ]; then
+               echo $msg cpu-notifier-error-inject module is not available >&2
+               exit 0
+       fi
+}
+
+prerequisite_extra
+
+#
+# Offline all hot-pluggable CPUs
+#
+echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/CPU_DOWN_PREPARE/error
+for cpu in `hotpluggable_online_cpus`; do
+       offline_cpu_expect_success $cpu
+done
+
+#
+# Test CPU hot-add error handling (offline => online)
+#
+echo $error > $NOTIFIER_ERR_INJECT_DIR/actions/CPU_UP_PREPARE/error
+for cpu in `hotplaggable_offline_cpus`; do
+       online_cpu_expect_fail $cpu
+done
+
+#
+# Online all hot-pluggable CPUs
+#
+echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/CPU_UP_PREPARE/error
+for cpu in `hotplaggable_offline_cpus`; do
+       online_cpu_expect_success $cpu
+done
+
+#
+# Test CPU hot-remove error handling (online => offline)
+#
+echo $error > $NOTIFIER_ERR_INJECT_DIR/actions/CPU_DOWN_PREPARE/error
+for cpu in `hotpluggable_online_cpus`; do
+       offline_cpu_expect_fail $cpu
+done
+
+echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/CPU_DOWN_PREPARE/error
+/sbin/modprobe -q -r cpu-notifier-error-inject
diff --git a/tools/testing/selftests/memory-hotplug/Makefile b/tools/testing/selftests/memory-hotplug/Makefile
new file mode 100644 (file)
index 0000000..7c9c20f
--- /dev/null
@@ -0,0 +1,6 @@
+all:
+
+run_tests:
+       ./on-off-test.sh
+
+clean:
diff --git a/tools/testing/selftests/memory-hotplug/on-off-test.sh b/tools/testing/selftests/memory-hotplug/on-off-test.sh
new file mode 100644 (file)
index 0000000..a2816f6
--- /dev/null
@@ -0,0 +1,230 @@
+#!/bin/bash
+
+SYSFS=
+
+prerequisite()
+{
+       msg="skip all tests:"
+
+       if [ $UID != 0 ]; then
+               echo $msg must be run as root >&2
+               exit 0
+       fi
+
+       SYSFS=`mount -t sysfs | head -1 | awk '{ print $3 }'`
+
+       if [ ! -d "$SYSFS" ]; then
+               echo $msg sysfs is not mounted >&2
+               exit 0
+       fi
+
+       if ! ls $SYSFS/devices/system/memory/memory* > /dev/null 2>&1; then
+               echo $msg memory hotplug is not supported >&2
+               exit 0
+       fi
+}
+
+#
+# list all hot-pluggable memory
+#
+hotpluggable_memory()
+{
+       local state=${1:-.\*}
+
+       for memory in $SYSFS/devices/system/memory/memory*; do
+               if grep -q 1 $memory/removable &&
+                  grep -q $state $memory/state; then
+                       echo ${memory##/*/memory}
+               fi
+       done
+}
+
+hotplaggable_offline_memory()
+{
+       hotpluggable_memory offline
+}
+
+hotpluggable_online_memory()
+{
+       hotpluggable_memory online
+}
+
+memory_is_online()
+{
+       grep -q online $SYSFS/devices/system/memory/memory$1/state
+}
+
+memory_is_offline()
+{
+       grep -q offline $SYSFS/devices/system/memory/memory$1/state
+}
+
+online_memory()
+{
+       echo online > $SYSFS/devices/system/memory/memory$1/state
+}
+
+offline_memory()
+{
+       echo offline > $SYSFS/devices/system/memory/memory$1/state
+}
+
+online_memory_expect_success()
+{
+       local memory=$1
+
+       if ! online_memory $memory; then
+               echo $FUNCNAME $memory: unexpected fail >&2
+       elif ! memory_is_online $memory; then
+               echo $FUNCNAME $memory: unexpected offline >&2
+       fi
+}
+
+online_memory_expect_fail()
+{
+       local memory=$1
+
+       if online_memory $memory 2> /dev/null; then
+               echo $FUNCNAME $memory: unexpected success >&2
+       elif ! memory_is_offline $memory; then
+               echo $FUNCNAME $memory: unexpected online >&2
+       fi
+}
+
+offline_memory_expect_success()
+{
+       local memory=$1
+
+       if ! offline_memory $memory; then
+               echo $FUNCNAME $memory: unexpected fail >&2
+       elif ! memory_is_offline $memory; then
+               echo $FUNCNAME $memory: unexpected offline >&2
+       fi
+}
+
+offline_memory_expect_fail()
+{
+       local memory=$1
+
+       if offline_memory $memory 2> /dev/null; then
+               echo $FUNCNAME $memory: unexpected success >&2
+       elif ! memory_is_online $memory; then
+               echo $FUNCNAME $memory: unexpected offline >&2
+       fi
+}
+
+error=-12
+priority=0
+ratio=10
+
+while getopts e:hp:r: opt; do
+       case $opt in
+       e)
+               error=$OPTARG
+               ;;
+       h)
+               echo "Usage $0 [ -e errno ] [ -p notifier-priority ] [ -r percent-of-memory-to-offline ]"
+               exit
+               ;;
+       p)
+               priority=$OPTARG
+               ;;
+       r)
+               ratio=$OPTARG
+               ;;
+       esac
+done
+
+if ! [ "$error" -ge -4095 -a "$error" -lt 0 ]; then
+       echo "error code must be -4095 <= errno < 0" >&2
+       exit 1
+fi
+
+prerequisite
+
+#
+# Online all hot-pluggable memory
+#
+for memory in `hotplaggable_offline_memory`; do
+       online_memory_expect_success $memory
+done
+
+#
+# Offline $ratio percent of hot-pluggable memory
+#
+for memory in `hotpluggable_online_memory`; do
+       if [ $((RANDOM % 100)) -lt $ratio ]; then
+               offline_memory_expect_success $memory
+       fi
+done
+
+#
+# Online all hot-pluggable memory again
+#
+for memory in `hotplaggable_offline_memory`; do
+       online_memory_expect_success $memory
+done
+
+#
+# Test with memory notifier error injection
+#
+
+DEBUGFS=`mount -t debugfs | head -1 | awk '{ print $3 }'`
+NOTIFIER_ERR_INJECT_DIR=$DEBUGFS/notifier-error-inject/memory
+
+prerequisite_extra()
+{
+       msg="skip extra tests:"
+
+       /sbin/modprobe -q -r memory-notifier-error-inject
+       /sbin/modprobe -q memory-notifier-error-inject priority=$priority
+
+       if [ ! -d "$DEBUGFS" ]; then
+               echo $msg debugfs is not mounted >&2
+               exit 0
+       fi
+
+       if [ ! -d $NOTIFIER_ERR_INJECT_DIR ]; then
+               echo $msg memory-notifier-error-inject module is not available >&2
+               exit 0
+       fi
+}
+
+prerequisite_extra
+
+#
+# Offline $ratio percent of hot-pluggable memory
+#
+echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_OFFLINE/error
+for memory in `hotpluggable_online_memory`; do
+       if [ $((RANDOM % 100)) -lt $ratio ]; then
+               offline_memory_expect_success $memory
+       fi
+done
+
+#
+# Test memory hot-add error handling (offline => online)
+#
+echo $error > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_ONLINE/error
+for memory in `hotplaggable_offline_memory`; do
+       online_memory_expect_fail $memory
+done
+
+#
+# Online all hot-pluggable memory
+#
+echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_ONLINE/error
+for memory in `hotplaggable_offline_memory`; do
+       online_memory_expect_success $memory
+done
+
+#
+# Test memory hot-remove error handling (online => offline)
+#
+echo $error > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_OFFLINE/error
+for memory in `hotpluggable_online_memory`; do
+       offline_memory_expect_fail $memory
+done
+
+echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_OFFLINE/error
+/sbin/modprobe -q -r memory-notifier-error-inject
index 164cbcf61106f5d65412961c46f548383e8263ab..808d5a9d5dcfb062a5dc9d9fc10246cb6e7e00d2 100644 (file)
@@ -437,34 +437,34 @@ static void slab_stats(struct slabinfo *s)
        printf("Fastpath             %8lu %8lu %3lu %3lu\n",
                s->alloc_fastpath, s->free_fastpath,
                s->alloc_fastpath * 100 / total_alloc,
-               s->free_fastpath * 100 / total_free);
+               total_free ? s->free_fastpath * 100 / total_free : 0);
        printf("Slowpath             %8lu %8lu %3lu %3lu\n",
                total_alloc - s->alloc_fastpath, s->free_slowpath,
                (total_alloc - s->alloc_fastpath) * 100 / total_alloc,
-               s->free_slowpath * 100 / total_free);
+               total_free ? s->free_slowpath * 100 / total_free : 0);
        printf("Page Alloc           %8lu %8lu %3lu %3lu\n",
                s->alloc_slab, s->free_slab,
                s->alloc_slab * 100 / total_alloc,
-               s->free_slab * 100 / total_free);
+               total_free ? s->free_slab * 100 / total_free : 0);
        printf("Add partial          %8lu %8lu %3lu %3lu\n",
                s->deactivate_to_head + s->deactivate_to_tail,
                s->free_add_partial,
                (s->deactivate_to_head + s->deactivate_to_tail) * 100 / total_alloc,
-               s->free_add_partial * 100 / total_free);
+               total_free ? s->free_add_partial * 100 / total_free : 0);
        printf("Remove partial       %8lu %8lu %3lu %3lu\n",
                s->alloc_from_partial, s->free_remove_partial,
                s->alloc_from_partial * 100 / total_alloc,
-               s->free_remove_partial * 100 / total_free);
+               total_free ? s->free_remove_partial * 100 / total_free : 0);
 
        printf("Cpu partial list     %8lu %8lu %3lu %3lu\n",
                s->cpu_partial_alloc, s->cpu_partial_free,
                s->cpu_partial_alloc * 100 / total_alloc,
-               s->cpu_partial_free * 100 / total_free);
+               total_free ? s->cpu_partial_free * 100 / total_free : 0);
 
        printf("RemoteObj/SlabFrozen %8lu %8lu %3lu %3lu\n",
                s->deactivate_remote_frees, s->free_frozen,
                s->deactivate_remote_frees * 100 / total_alloc,
-               s->free_frozen * 100 / total_free);
+               total_free ? s->free_frozen * 100 / total_free : 0);
 
        printf("Total                %8lu %8lu\n\n", total_alloc, total_free);